Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Current develop

### Added (new features/APIs/variables/...)
- [[PR 1362]](https://github.com/parthenon-hpc-lab/parthenon/pull/1362) Add support for completely custom "user" output
- [[PR 1344]](https://github.com/parthenon-hpc-lab/parthenon/pull/1344) Add option to communicate single layer of ghosts, only communicate required two-level composite boundaries


Expand Down
31 changes: 31 additions & 0 deletions doc/sphinx/src/outputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,37 @@ The following is a minimal example to plot a 1D and 2D histogram from the output
plt.pcolormesh(x,y,z,)
plt.show()

User
----

Parthenon provides a simple callback for downstream codes to enroll their own
output routines.
In the input file, include a ``<parthenon/output*>`` block and specify
``file_type = my_user_type`` where the latter is an arbitrary string.
Output frequency is controlld via ``dt`` or ``dn`` as for other output types.
Using this callback requires a downstream code to implement and enroll a derived
``OutputType`` object with the ``my_user_type`` string name.

.. code:: c++

pman.app_input->RegisterUserOutput("my_user_type", std::make_shared<MyOutput>());

where ``MyOutput`` has to minimally implement ``WriteOutputFile``

.. code:: c++

class MyOutput : public OutputType {
public:
explicit MyOutput() : OutputType({}) {}
void WriteOutputFile(Mesh *pm, ParameterInput *pin, SimTime *tm,
const SignalHandler::OutputSignal signal) override;
};
Comment thread
Yurlungur marked this conversation as resolved.

A minimal implementation is demonstrated in the
`particle_leapfrog <https://github.com/parthenon-hpc-lab/parthenon/blob/develop/example/particle_leapfrog/particle_leapfrog.cpp>`__
example.


Ascent (optional)
-----------------

Expand Down
11 changes: 10 additions & 1 deletion example/particle_leapfrog/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
//========================================================================================
// Parthenon performance portable AMR framework
// Copyright(C) 2021-2026 The Parthenon collaboration
// Licensed under the 3-clause BSD License, see LICENSE file for details
//========================================================================================
// (C) (or copyright) 2020-2023. Triad National Security, LLC. All rights reserved.
//
// This program was produced under U.S. Government contract 89233218CNA000001 for Los
Expand All @@ -11,8 +15,9 @@
// the public, perform publicly and display publicly, and to permit others to do so.
//========================================================================================

#include "parthenon_manager.hpp"
#include <memory>

#include "parthenon_manager.hpp"
#include "particle_leapfrog.hpp"

int main(int argc, char *argv[]) {
Expand All @@ -24,6 +29,10 @@ int main(int argc, char *argv[]) {
pman.app_input->ProcessPackages = particles_leapfrog::ProcessPackages;
pman.app_input->ProblemGenerator = particles_leapfrog::ProblemGenerator;

// Create user output and enroll
pman.app_input->RegisterUserOutput(
"particle_user_output", std::make_shared<particles_leapfrog::ParticleUserOutput>());

// call ParthenonInit to initialize MPI and Kokkos, parse the input deck, and set up
auto manager_status = pman.ParthenonInitEnv(argc, argv);
if (manager_status == ParthenonStatus::complete) {
Expand Down
32 changes: 31 additions & 1 deletion example/particle_leapfrog/particle_leapfrog.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//========================================================================================
// Parthenon performance portable AMR framework
// Copyright(C) 2021-2024 The Parthenon collaboration
// Copyright(C) 2021-2026 The Parthenon collaboration
// Licensed under the 3-clause BSD License, see LICENSE file for details
//========================================================================================
// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved.
Expand Down Expand Up @@ -47,6 +47,36 @@ Packages_t ProcessPackages(std::unique_ptr<ParameterInput> &pin) {
return packages;
}

// *************************************************//
// Example (very simple) on how to enroll a custom output function that follows
// all the standard logic (i.e., time, cycle or signal based) and can implement
// any kind of logic/output.
// It is highly recommend to look at the other default Parthenon internal OutputTypes
// for more detailed code on typical output boilerplate.
// *************************************************//
void ParticleUserOutput::WriteOutputFile(Mesh *pm, ParameterInput *pin, SimTime *tm,
const SignalHandler::OutputSignal signal) {
// For simplicitly, only rank 0 writes the current cycle.
// Typically writing is a parallel operation.
if (Globals::my_rank == 0) {
std::ofstream outfile("user_output." + std::to_string(output_params.file_number));
if (outfile.is_open()) {
outfile << "cycle = " << tm->ncycle;
outfile.close();
}
}

// Advance file ids and times.
// This has to be done on all ranks (so no rank filter) for consistency.
if (signal == SignalHandler::OutputSignal::none) {
// After file has been opened with the current number, already advance output
// parameters so that for restarts the file is not immediatly overwritten again.
// Only applies to default time-based data dumps, so that writing "now" and "final"
// outputs does not change the desired output numbering.
UpdateNextOutput_(pm, tm);
}
}

// *************************************************//
// define the "physics" package particles_package, *//
// which includes defining various functions that *//
Expand Down
13 changes: 12 additions & 1 deletion example/particle_leapfrog/particle_leapfrog.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//========================================================================================
// Parthenon performance portable AMR framework
// Copyright(C) 2021 The Parthenon collaboration
// Copyright(C) 2021-2026 The Parthenon collaboration
// Licensed under the 3-clause BSD License, see LICENSE file for details
//========================================================================================
// (C) (or copyright) 2020-2021. Triad National Security, LLC. All rights reserved.
Expand Down Expand Up @@ -45,6 +45,17 @@ class ParticleDriver : public EvolutionDriver {
void ProblemGenerator(MeshBlock *pmb, ParameterInput *pin);
Packages_t ProcessPackages(std::unique_ptr<ParameterInput> &pin);

//----------------------------------------------------------------------------------------
//! \class ParticleUserOutput
// \brief derived OutputType class for User enrolled outputs

class ParticleUserOutput : public OutputType {
public:
explicit ParticleUserOutput() : OutputType({}) {}
void WriteOutputFile(Mesh *pm, ParameterInput *pin, SimTime *tm,
const SignalHandler::OutputSignal signal) override;
};

namespace Particles {

std::shared_ptr<StateDescriptor> Initialize(ParameterInput *pin);
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ add_library(parthenon
interface/meshblock_data.hpp
interface/metadata.cpp
interface/metadata.hpp
interface/outputs.hpp
interface/packages.hpp
interface/params.cpp
interface/params.hpp
Expand Down Expand Up @@ -202,7 +203,6 @@ add_library(parthenon
outputs/outputs.hpp
outputs/outputs_package.cpp
outputs/outputs_package.hpp
outputs/output_parameters.hpp
outputs/parthenon_hdf5.cpp
outputs/parthenon_hdf5_attributes.cpp
outputs/parthenon_hdf5_attributes_read.cpp
Expand Down
26 changes: 26 additions & 0 deletions src/application_input.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
//========================================================================================
// Parthenon performance portable AMR framework
// Copyright(C) 2020-2026 The Parthenon collaboration
// Licensed under the 3-clause BSD License, see LICENSE file for details
//========================================================================================
// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved.
//
// This program was produced under U.S. Government contract 89233218CNA000001 for Los
Expand All @@ -11,6 +15,7 @@
// the public, perform publicly and display publicly, and to permit others to do so.
//========================================================================================

#include <memory>
#include <string>

#include "application_input.hpp"
Expand Down Expand Up @@ -103,5 +108,26 @@ SBValFunc ApplicationInput::GetSwarmBoundaryCondition(BoundaryFace face,
}
return swarm_boundary_conditions_[face].at(name);
}
void ApplicationInput::RegisterUserOutput(const std::string &name,
std::shared_ptr<OutputType> output_type) {
PARTHENON_REQUIRE_THROWS(!(name == "hst" || name == "rst" || name == "hdf5" ||
name == "corehdf5" || name == "histogram" ||
name == "ascent"),
"Trying to enroll UserOutput with name in conflict with "
"internal Parthenon file_type.");
if (user_outputs_.count(name) > 0) {
PARTHENON_THROW("User OutputType " + name + " already registered.");
}
user_outputs_[name] = output_type;
}

// Returns pointer to user output (if it exists) or nullptr if not found.
std::shared_ptr<OutputType> ApplicationInput::GetUserOutput(const std::string &name) {
if (user_outputs_.count(name) == 0) {
return nullptr;
} else {
return user_outputs_[name];
}
}

} // namespace parthenon
12 changes: 12 additions & 0 deletions src/application_input.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
//========================================================================================
// Parthenon performance portable AMR framework
// Copyright(C) 2020-2026 The Parthenon collaboration
// Licensed under the 3-clause BSD License, see LICENSE file for details
//========================================================================================
// (C) (or copyright) 2020-2024. Triad National Security, LLC. All rights reserved.
//
// This program was produced under U.S. Government contract 89233218CNA000001 for Los
Expand All @@ -22,6 +26,7 @@
#include "basic_types.hpp"
#include "bvals/boundary_conditions.hpp"
#include "defs.hpp"
#include "interface/outputs.hpp"
#include "parthenon_arrays.hpp"

namespace parthenon {
Expand Down Expand Up @@ -93,9 +98,16 @@ class ApplicationInput {
BValFunc GetBoundaryCondition(BoundaryFace face, const std::string &name) const;
SBValFunc GetSwarmBoundaryCondition(BoundaryFace face, const std::string &name) const;

// Custom user outputs
void RegisterUserOutput(const std::string &name,
std::shared_ptr<OutputType> output_type);
std::shared_ptr<OutputType> GetUserOutput(const std::string &name);

private:
Dictionary<BValFunc> boundary_conditions_[BOUNDARY_NFACES];
Dictionary<SBValFunc> swarm_boundary_conditions_[BOUNDARY_NFACES];

Dictionary<std::shared_ptr<OutputType>> user_outputs_;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be a dictionary of factories rather than storing OutputTypes?

Dictionary<std::function<std::shared_ptr<OutputType>(OutputParameters)>> user_output_factories_;

This way the user outputs could be generated newly for each input block. I think of use cases like the multiple history files generated in the advection example

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about the factory and the decided for the full derived class approach because I thought it's more flexible, e.g., when a user want to keep some state between outputs (e.g., thinking about some ADIOS2 streaming based output).

Similarly, with the current approach multiple output of the same type are still possible, aren't they?

As far as I can tell,

<parthenon/output1>
file_type = particle_user_output
dt = 2.0
variables = bla, blub

<parthenon/output2>
file_type = particle_user_output
dn = 1
variables = mycyclevar
myothervar = foobar

should work out of the box as WriteOutputFile also contains pin, additional custom parameters could be parsed and used.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In your example when you call ptype->WriteOutputFile(pm, pin, tm, signal); won't it only ever write the output2 file, regardless ? Because it is registered as

pnew_type = papp_in->GetUserOutput(op.file_type);
      if (pnew_type) {
        // Update empty OutputParams op with actual ones
        pnew_type->output_params = op;
      } else {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes, good point.
Do you think it'd be an issue if I add the option for the object to clone itself (before assigning the OutputParameters (as it'd be a smaller refactor compared to writing a factory with functions)?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the factory would be clearer downstream, but cloning should get the same resulting behavior.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to double check: Do you imagine the factory living in Parthenon with the downstream code just providing shared ptr to the WriteOutputFile implementation or do you imagine the downstream codes write factories that are called by Parthenon to create output objects (or something completely else)?
I think having control over the object in the downstream code would be useful as it allows for more flexibility than a "simple" callback function.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking it would be something like

pman.app_input->RegisterUserOutput(
      "particle_user_output", 
[](OutputParameter op){
   return std::make_shared<particles_leapfrog::ParticleUserOutput>(op);
}
);

, which I don't think would preclude allowing the downstream code to own the object, since the factory doesn't have to always make a new object. You could even throw from the factory function if the output type gets used in multiple input blocks

};

} // namespace parthenon
Expand Down
6 changes: 4 additions & 2 deletions src/driver/driver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ class Driver {
Driver(ParameterInput *pin, ApplicationInput *app_in, Mesh *pm)
: pinput(pin), app_input(app_in), pmesh(pm), mbcnt_prev(), time_LBandAMR() {}
virtual DriverStatus Execute() = 0;
void InitializeOutputs() { pouts = std::make_unique<Outputs>(pmesh, pinput); }
void InitializeOutputs() {
pouts = std::make_unique<Outputs>(pmesh, pinput, app_input);
}
void DumpInputParameters();

ParameterInput *pinput;
Expand Down Expand Up @@ -121,7 +123,7 @@ class EvolutionDriver : public Driver {
const auto nout_mesh = pinput->GetOrAddInteger("parthenon/time", "ncycle_out_mesh", 0,
"cadence of outputs describing mesh");
tm = SimTime(start_time, tstop, nmax, ncycle, nout, nout_mesh, dt);
pouts = std::make_unique<Outputs>(pmesh, pinput, &tm);
pouts = std::make_unique<Outputs>(pmesh, pinput, app_in, &tm);

output_before_amr = pinput->GetOrAddBoolean(
"parthenon/time", "output_before_amr", false,
Expand Down
54 changes: 48 additions & 6 deletions src/outputs/output_parameters.hpp → src/interface/outputs.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//========================================================================================
// Parthenon performance portable AMR framework
// Copyright(C) 2020-2025 The Parthenon collaboration
// Copyright(C) 2020-2026 The Parthenon collaboration
// Licensed under the 3-clause BSD License, see LICENSE file for details
//========================================================================================
// Athena++ astrophysical MHD code
Expand All @@ -18,17 +18,24 @@
// license in this material to reproduce, prepare derivative works, distribute copies to
// the public, perform publicly and display publicly, and to permit others to do so.
//========================================================================================

#ifndef OUTPUTS_OUTPUT_PARAMETERS_HPP_
#define OUTPUTS_OUTPUT_PARAMETERS_HPP_
#ifndef INTERFACE_OUTPUTS_HPP_
#define INTERFACE_OUTPUTS_HPP_
//! \file outputs.hpp
// \brief provides base classes to handle ALL types of data output

#include <map>
#include <set>
#include <string>
#include <vector>

#include "basic_types.hpp"

namespace parthenon {

// forward declarations
class Mesh;
class ParameterInput;

// JMM: I designed this for HDF5 but in pinciple this switching could
// also work for other output types... Any output type that is capable
// of outputting a full dump can do this.
Expand Down Expand Up @@ -74,6 +81,41 @@ struct OutputParameters {
int file_number = 0;
};

} // namespace parthenon
//----------------------------------------------------------------------------------------
// \brief abstract base class for different output types (modes/formats). Each OutputType
// is designed to be a node in a singly linked list created & stored in the Outputs class

class OutputType {
public:
// mark single parameter constructors as "explicit" to prevent them from acting as
// implicit conversion functions: for f(OutputType arg), prevent f(anOutputParameters)
explicit OutputType(OutputParameters oparams);

// rule of five:
virtual ~OutputType() = default;
// copied)
OutputType(const OutputType &copy_other) = default;
OutputType &operator=(const OutputType &copy_other) = default;
// move constructor and assignment operator
OutputType(OutputType &&) = default;
OutputType &operator=(OutputType &&) = default;

#endif // OUTPUTS_OUTPUT_PARAMETERS_HPP_
// data
OutputParameters output_params; // control data read from <output> block

// following pure virtual function must be implemented in all derived classes
virtual void WriteOutputFile(Mesh *pm, ParameterInput *pin, SimTime *tm,
const SignalHandler::OutputSignal signal) = 0;
virtual void WriteContainer(SimTime &tm, Mesh *pm, ParameterInput *pin, bool flag) {
return;
}

protected:
int num_vars_; // number of variables in output

// Update book-keeping such as next output time to next output
void UpdateNextOutput_(Mesh *pm, SimTime *tm);
};

} // namespace parthenon
#endif // INTERFACE_OUTPUTS_HPP_
2 changes: 1 addition & 1 deletion src/interface/state_descriptor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
#include "basic_types.hpp"
#include "bvals/boundary_conditions.hpp"
#include "interface/metadata.hpp"
#include "interface/outputs.hpp"
#include "interface/params.hpp"
#include "interface/sparse_pool.hpp"
#include "interface/var_id.hpp"
#include "outputs/output_parameters.hpp"
#include "pack/scratch_variables.hpp"
#include "parameter_input.hpp"
#include "prolong_restrict/prolong_restrict.hpp"
Expand Down
2 changes: 1 addition & 1 deletion src/outputs/output_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
#include "basic_types.hpp"
#include "defs.hpp"
#include "interface/metadata.hpp"
#include "interface/outputs.hpp"
#include "interface/variable.hpp"
#include "kokkos_abstraction.hpp"
#include "mesh/domain.hpp"
#include "mesh/mesh.hpp"
#include "mesh/meshblock.hpp"
#include "outputs/output_parameters.hpp"
#include "utils/error_checking.hpp"

namespace parthenon {
Expand Down
Loading
Loading