diff --git a/docs/source/run/parameters.rst b/docs/source/run/parameters.rst index 501e7b6ea1..d3bd9555b5 100644 --- a/docs/source/run/parameters.rst +++ b/docs/source/run/parameters.rst @@ -1056,9 +1056,14 @@ There are different types of diagnostics in HiPACE++. The standard diagnostics a in-situ diagnostics allow for fast analysis of large beams or the plasma particles. Please make sure to always clear or rename the output folder before running a new simulation to avoid mixing data from different runs. -* ``diagnostic.output_period`` (`integer`) optional (default `0`) +* ``diagnostic.output_period`` (`integer` or `string`) optional (default `0`) Output period for standard beam and field diagnostics. Field or beam specific diagnostics can overwrite this parameter. - No output is given for ``diagnostic.output_period = 0``. + Diagnostic output is written to file when the current time step is a multiple of the + output period. For ``diagnostic.output_period = 0`` no output is given, + while ``diagnostic.output_period = 1`` always produces output. The output period can also + be a function of ``current_step`` and ``current_time`` to have finer control of when output + should be written. Examples of how to use this can be found + `here `__. * ``hipace.output_folder`` (`string`) optional (default ``"diags"``) Set the output path of diagnostic data. By default all types of diagnostics will output @@ -1075,9 +1080,10 @@ Please make sure to always clear or rename the output folder before running a ne Beam diagnostics ^^^^^^^^^^^^^^^^ -* ``diagnostic.beam_output_period`` (`integer`) optional (default `0`) +* ``diagnostic.beam_output_period`` (`integer` or `string`) optional (default `0`) Output period for the beam. No output is given for ``diagnostic.beam_output_period = 0``. If ``diagnostic.output_period`` is defined, that value is used as the default for this. + See the documentation of ``diagnostic.output_period`` for more details. * ``diagnostic.beam_data`` (`string`) optional (default `all`) Names of the beams written to file, separated by a space. The beam names need to be ``all``, @@ -1100,9 +1106,10 @@ Field diagnostics If ```` is equal to ``lev0 lev1 lev2 laser_diag``, the default for this parameter becomes ``level_0 level_1 level_2 laser`` respectively. -* ``.output_period`` (`integer`) optional (default `0`) +* ``.output_period`` (`integer` or `string`) optional (default `0`) Output period for fields. No output is given for ``.output_period = 0``. If ``diagnostic.output_period`` is defined, that value is used as the default for this. + See the documentation of ``diagnostic.output_period`` for more details. * `` or diagnostic.diag_type`` (`string`) Type of field output. Available options are `xyz`, `xz`, `yz` and `xy_integrated`. @@ -1218,8 +1225,9 @@ Usage example: ir.time, ir.zeta # get metadata needed for plotting -* `` or beams.insitu_period`` (`int`) optional (default ``0``) +* `` or beams.insitu_period`` (`integer` or `string`) optional (default ``0``) Period of the beam in-situ diagnostics. `0` means no beam in-situ diagnostics. + See the documentation of ``diagnostic.output_period`` for more details. * `` or beams.insitu_file_prefix`` (`string`) optional (default ``"/insitu"``) Path of the beam in-situ output. Must not be the same as `hipace.file_prefix`. @@ -1228,8 +1236,9 @@ Usage example: Maximum radius ``.insitu_radius`` :math:`= \sqrt{x^2 + y^2}` within which particles are used for the calculation of the insitu diagnostics. -* `` or plasmas.insitu_period`` (`int`) optional (default ``0``) +* `` or plasmas.insitu_period`` (`integer` or `string`) optional (default ``0``) Period of the plasma in-situ diagnostics. `0` means no plasma in-situ diagnostics. + See the documentation of ``diagnostic.output_period`` for more details. * `` or plasmas.insitu_file_prefix`` (`string`) optional (default ``"/insitu"``) Path of the plasma in-situ output. Must not be the same as `hipace.file_prefix`. @@ -1238,17 +1247,19 @@ Usage example: Maximum radius ``.insitu_radius`` :math:`= \sqrt{x^2 + y^2}` within which particles are used for the calculation of the insitu diagnostics. -* ``fields.insitu_period`` (`int`) optional (default ``0``) +* ``fields.insitu_period`` (`integer` or `string`) optional (default ``0``) Period of the field in-situ diagnostics. `0` means no field in-situ diagnostics. + See the documentation of ``diagnostic.output_period`` for more details. * ``fields.insitu_file_prefix`` (`string`) optional (default ``"/insitu"``) Path of the field in-situ output. Must not be the same as `hipace.file_prefix`. -* ``lasers.insitu_period`` (`int`) optional (default ``0``) +* ``lasers.insitu_period`` (`integer` or `string`) optional (default ``0``) Period of the laser in-situ diagnostics. `0` means no laser in-situ diagnostics. * ``lasers.insitu_file_prefix`` (`string`) optional (default ``"/insitu"``) Path of the laser in-situ output. Must not be the same as `hipace.file_prefix`. + See the documentation of ``diagnostic.output_period`` for more details. Additional physics ------------------ diff --git a/src/diagnostics/Diagnostic.H b/src/diagnostics/Diagnostic.H index 1e97234ee8..14872468ae 100644 --- a/src/diagnostics/Diagnostic.H +++ b/src/diagnostics/Diagnostic.H @@ -46,7 +46,7 @@ struct FieldDiagnosticData bool m_has_field; /**< if there is field output to write */ /** Number of iterations between consecutive output dumps. * Default is 0, meaning no output */ - int m_output_period = 0; + utils::DiagPeriod m_output_period = "0"; }; @@ -80,9 +80,8 @@ public: int output_step, int max_step, amrex::Real output_time, amrex::Real max_time) { - return (fd.m_nfields > 0) && - utils::doDiagnostics(fd.m_output_period, output_step, max_step, - output_time, max_time); + return (fd.m_nfields > 0) && + fd.m_output_period.doDiagnostics(output_step, max_step, output_time, max_time); } /** \brief determines if any field diagnostic has any output on in this time step @@ -114,8 +113,7 @@ public: amrex::Real output_time, amrex::Real max_time) const { return m_output_beam_names.size() > 0 && - utils::doDiagnostics(m_beam_output_period, output_step, max_step, - output_time, max_time); + m_beam_output_period.doDiagnostics(output_step, max_step, output_time, max_time); } /** \brief determines if any field or beam diagnostic has any output on in this time step @@ -173,7 +171,7 @@ private: amrex::Vector m_output_beam_names; /**< Component names to Write to output file */ /** Number of iterations between consecutive output dumps. * Default is 0, meaning no output */ - int m_beam_output_period = 0; + utils::DiagPeriod m_beam_output_period = "0"; amrex::Vector m_field_data; /**< individual field diag data */ bool m_initialized = false; /**< if this object is fully initialized */ }; diff --git a/src/diagnostics/Diagnostic.cpp b/src/diagnostics/Diagnostic.cpp index e8f331a0bb..a41ddef91a 100644 --- a/src/diagnostics/Diagnostic.cpp +++ b/src/diagnostics/Diagnostic.cpp @@ -76,16 +76,18 @@ Diagnostic::ReadParameters (int nlev, bool use_laser) AMREX_ALWAYS_ASSERT_WITH_MESSAGE( fd.m_diag_coarsen.min() >= 1, "Coarsening ratio must be >= 1"); - queryWithParser(pph, "output_period", fd.m_output_period); - queryWithParserAlt(pp, "output_period", fd.m_output_period, ppd); + queryWithParser(pph, "output_period", fd.m_output_period.m_func_str); + queryWithParserAlt(pp, "output_period", fd.m_output_period.m_func_str, ppd); + fd.m_output_period.compile(); } - if (queryWithParser(pph, "output_period", m_beam_output_period)) { + if (queryWithParser(pph, "output_period", m_beam_output_period.m_func_str)) { amrex::Print() << "WARNING: 'hipace.output_period' is deprecated! " "Use 'diagnostic.output_period' instead!\n"; } - queryWithParser(ppd, "output_period", m_beam_output_period); - queryWithParser(ppd, "beam_output_period", m_beam_output_period); + queryWithParser(ppd, "output_period", m_beam_output_period.m_func_str); + queryWithParser(ppd, "beam_output_period", m_beam_output_period.m_func_str); + m_beam_output_period.compile(); } bool diff --git a/src/fields/Fields.H b/src/fields/Fields.H index e9d980a0b2..51c9b8179c 100644 --- a/src/fields/Fields.H +++ b/src/fields/Fields.H @@ -13,6 +13,7 @@ #include "diagnostics/Diagnostic.H" #include "laser/MultiLaser.H" #include "utils/GPUUtil.H" +#include "utils/IOUtil.H" #include #include @@ -475,7 +476,7 @@ private: static constexpr int m_insitu_nrp = 10; /** How often the insitu field diagnostics should be computed and written * Default is 0, meaning no output */ - int m_insitu_period {0}; + utils::DiagPeriod m_insitu_period {"0"}; /** All per-slice real field properties */ amrex::Vector m_insitu_rdata; /** Sum of all per-slice real field properties */ diff --git a/src/fields/Fields.cpp b/src/fields/Fields.cpp index 4eb0208938..ae9cc83977 100644 --- a/src/fields/Fields.cpp +++ b/src/fields/Fields.cpp @@ -34,7 +34,8 @@ Fields::ReadParameters (const int nlev) amrex::ParmParse ppf("fields"); DeprecatedInput("fields", "do_dirichlet_poisson", "poisson_solver", ""); - queryWithParser(ppf, "insitu_period", m_insitu_period); + queryWithParser(ppf, "insitu_period", m_insitu_period.m_func_str); + m_insitu_period.compile(); m_insitu_file_prefix = Hipace::m_output_folder + "/insitu"; const bool set_file_prefix = queryWithParser(ppf, "insitu_file_prefix", m_insitu_file_prefix); if (set_file_prefix) { @@ -246,7 +247,7 @@ Fields::AllocData ( "'FFTDirichletQuick', 'FFTPeriodic' or 'MGDirichlet'"); } - if (lev == 0 && m_insitu_period > 0) { + if (lev == 0 && m_insitu_period.isNonZero()) { #ifdef HIPACE_USE_OPENPMD AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_insitu_file_prefix != Hipace::GetInstance().m_openpmd_writer.m_file_prefix, @@ -1355,7 +1356,7 @@ void Fields::InSituComputeDiags (int step, amrex::Real time, int islice, const amrex::Geometry& geom3D, int max_step, amrex::Real max_time) { - if (!utils::doDiagnostics(m_insitu_period, step, max_step, time, max_time)) return; + if (!m_insitu_period.doDiagnostics(step, max_step, time, max_time)) return; HIPACE_PROFILE("Fields::InSituComputeDiags()"); using namespace amrex::literals; @@ -1416,7 +1417,7 @@ void Fields::InSituWriteToFile (int step, amrex::Real time, const amrex::Geometry& geom3D, int max_step, amrex::Real max_time) { - if (!utils::doDiagnostics(m_insitu_period, step, max_step, time, max_time)) return; + if (!m_insitu_period.doDiagnostics(step, max_step, time, max_time)) return; HIPACE_PROFILE("Fields::InSituWriteToFile()"); #ifdef HIPACE_USE_OPENPMD diff --git a/src/laser/MultiLaser.H b/src/laser/MultiLaser.H index e4a7aa158d..e8f2fadd19 100644 --- a/src/laser/MultiLaser.H +++ b/src/laser/MultiLaser.H @@ -13,6 +13,7 @@ #include "Laser.H" #include "mg_solver/HpMultiGrid.H" #include "fields/fft_poisson_solver/fft/AnyFFT.H" +#include "utils/IOUtil.H" #include #include @@ -244,7 +245,7 @@ private: static constexpr int m_insitu_ncp = 1; /** How often the insitu laser diagnostics should be computed and written * Default is 0, meaning no output */ - int m_insitu_period {0}; + utils::DiagPeriod m_insitu_period {"0"}; /** All per-slice real laser properties */ amrex::Vector m_insitu_rdata; /** Sum of all per-slice real laser properties */ diff --git a/src/laser/MultiLaser.cpp b/src/laser/MultiLaser.cpp index 900e0420ff..94d0b40a2f 100644 --- a/src/laser/MultiLaser.cpp +++ b/src/laser/MultiLaser.cpp @@ -63,7 +63,8 @@ MultiLaser::ReadParameters () "if necessary, 'lasers.MG_tolerance_rel = 1e-7'. " "If for some reason you need to use the FFT-based solver plase open an issue on Github"); - queryWithParser(pp, "insitu_period", m_insitu_period); + queryWithParser(pp, "insitu_period", m_insitu_period.m_func_str); + m_insitu_period.compile(); m_insitu_file_prefix = Hipace::m_output_folder + "/insitu"; const bool set_file_prefix = queryWithParser(pp, "insitu_file_prefix", m_insitu_file_prefix); if (set_file_prefix) { @@ -193,7 +194,7 @@ MultiLaser::InitData () m_rhs_mg.resize(amrex::grow(m_slice_box, amrex::IntVect{1, 1, 0}), 2, amrex::The_Arena()); } - if (m_insitu_period > 0) { + if (m_insitu_period.isNonZero()) { #ifdef HIPACE_USE_OPENPMD AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_insitu_file_prefix != Hipace::GetInstance().m_openpmd_writer.m_file_prefix, @@ -1055,7 +1056,7 @@ MultiLaser::InSituComputeDiags (int step, amrex::Real time, int islice, int max_step, amrex::Real max_time) { if (!UseLaser(islice)) return; - if (!utils::doDiagnostics(m_insitu_period, step, max_step, time, max_time)) return; + if (!m_insitu_period.doDiagnostics(step, max_step, time, max_time)) return; HIPACE_PROFILE("MultiLaser::InSituComputeDiags()"); using namespace amrex::literals; @@ -1144,7 +1145,7 @@ void MultiLaser::InSituWriteToFile (int step, amrex::Real time, int max_step, amrex::Real max_time) { if (!m_use_laser) return; - if (!utils::doDiagnostics(m_insitu_period, step, max_step, time, max_time)) return; + if (!m_insitu_period.doDiagnostics(step, max_step, time, max_time)) return; HIPACE_PROFILE("MultiLaser::InSituWriteToFile()"); #ifdef HIPACE_USE_OPENPMD diff --git a/src/particles/beam/BeamParticleContainer.H b/src/particles/beam/BeamParticleContainer.H index c1371e3355..62b9575ed9 100644 --- a/src/particles/beam/BeamParticleContainer.H +++ b/src/particles/beam/BeamParticleContainer.H @@ -12,6 +12,7 @@ #include "particles/profiles/GetInitialDensity.H" #include "particles/profiles/GetInitialMomentum.H" #include "utils/Parser.H" +#include "utils/IOUtil.H" #include "particles/sorting/BoxSort.H" #include #include @@ -229,7 +230,7 @@ public: bool m_initialize_on_cpu {false}; /** How often the insitu beam diagnostics should be computed and written * Default is 0, meaning no output */ - int m_insitu_period {0}; + utils::DiagPeriod m_insitu_period {"0"}; /** Whether external fields should be used for this beam */ bool m_use_external_fields = false; /** External field functions for Ex Ey Ez Bx By Bz */ diff --git a/src/particles/beam/BeamParticleContainer.cpp b/src/particles/beam/BeamParticleContainer.cpp index 70d4f43b30..7f74fba361 100644 --- a/src/particles/beam/BeamParticleContainer.cpp +++ b/src/particles/beam/BeamParticleContainer.cpp @@ -59,7 +59,8 @@ BeamParticleContainer::ReadParameters () queryWithParser(pp, "do_z_push", m_do_z_push); queryWithParserAlt(pp, "do_push", m_do_push, pp_alt); queryWithParserAlt(pp, "do_radiation_reaction", m_do_radiation_reaction, pp_alt); - queryWithParserAlt(pp, "insitu_period", m_insitu_period, pp_alt); + queryWithParserAlt(pp, "insitu_period", m_insitu_period.m_func_str, pp_alt); + m_insitu_period.compile(); m_insitu_file_prefix = Hipace::m_output_folder + "/insitu"; const bool set_file_prefix = queryWithParserAlt(pp, "insitu_file_prefix", m_insitu_file_prefix, pp_alt); @@ -306,7 +307,7 @@ BeamParticleContainer::InitData (const amrex::Geometry& geom) << "' will be initialized with no particles!\n"; } - if (m_insitu_period > 0) { + if (m_insitu_period.isNonZero()) { #ifdef HIPACE_USE_OPENPMD AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_insitu_file_prefix != Hipace::GetInstance().m_openpmd_writer.m_file_prefix, diff --git a/src/particles/beam/MultiBeam.cpp b/src/particles/beam/MultiBeam.cpp index c2da13272a..6a8fd56ab7 100644 --- a/src/particles/beam/MultiBeam.cpp +++ b/src/particles/beam/MultiBeam.cpp @@ -96,7 +96,7 @@ MultiBeam::InSituComputeDiags (int step, int islice, amrex::Real max_time) { for (auto& beam : m_all_beams) { - if (utils::doDiagnostics(beam.m_insitu_period, step, max_step, physical_time, max_time)) { + if (beam.m_insitu_period.doDiagnostics(step, max_step, physical_time, max_time)) { beam.InSituComputeDiags(islice); } } @@ -107,7 +107,7 @@ MultiBeam::InSituWriteToFile (int step, amrex::Real time, const amrex::Geometry& int max_step, amrex::Real max_time) { for (auto& beam : m_all_beams) { - if (utils::doDiagnostics(beam.m_insitu_period, step, max_step, time, max_time)) { + if (beam.m_insitu_period.doDiagnostics(step, max_step, time, max_time)) { beam.InSituWriteToFile(step, time, geom); } } diff --git a/src/particles/plasma/MultiPlasma.cpp b/src/particles/plasma/MultiPlasma.cpp index b3ca731f7e..fa689dc88b 100644 --- a/src/particles/plasma/MultiPlasma.cpp +++ b/src/particles/plasma/MultiPlasma.cpp @@ -183,8 +183,7 @@ MultiPlasma::InSituComputeDiags (int step, int islice, int max_step, amrex::Real physical_time, amrex::Real max_time) { for (auto& plasma : m_all_plasmas) { - if (utils::doDiagnostics(plasma.m_insitu_period, step, - max_step, physical_time, max_time)) { + if (plasma.m_insitu_period.doDiagnostics(step, max_step, physical_time, max_time)) { plasma.InSituComputeDiags(islice); } } @@ -195,8 +194,7 @@ MultiPlasma::InSituWriteToFile (int step, amrex::Real time, const amrex::Geometr int max_step, amrex::Real max_time) { for (auto& plasma : m_all_plasmas) { - if (utils::doDiagnostics(plasma.m_insitu_period, step, - max_step, time, max_time)) { + if (plasma.m_insitu_period.doDiagnostics(step, max_step, time, max_time)) { plasma.InSituWriteToFile(step, time, geom); } } diff --git a/src/particles/plasma/PlasmaParticleContainer.H b/src/particles/plasma/PlasmaParticleContainer.H index ca942aab7b..cfd61aef13 100644 --- a/src/particles/plasma/PlasmaParticleContainer.H +++ b/src/particles/plasma/PlasmaParticleContainer.H @@ -12,6 +12,7 @@ #include "fields/Fields.H" #include "utils/Parser.H" #include "utils/GPUUtil.H" +#include "utils/IOUtil.H" #include "particles/profiles/GetInitialDensity.H" #include #include @@ -304,7 +305,7 @@ public: /** How often the insitu plasma diagnostics should be computed and written * Default is 0, meaning no output */ - int m_insitu_period {0}; + utils::DiagPeriod m_insitu_period {"0"}; private: std::string m_name; /**< name of the species */ int m_nslices; /**< number of z slices of the domain */ diff --git a/src/particles/plasma/PlasmaParticleContainer.cpp b/src/particles/plasma/PlasmaParticleContainer.cpp index a009e06d31..0470d4f4e1 100644 --- a/src/particles/plasma/PlasmaParticleContainer.cpp +++ b/src/particles/plasma/PlasmaParticleContainer.cpp @@ -166,7 +166,8 @@ PlasmaParticleContainer::ReadParameters () {Hipace::m_depos_order_xy % 2, Hipace::m_depos_order_xy % 2}; queryWithParserAlt(pp, "reorder_idx_type", idx_array, pp_alt); m_reorder_idx_type = amrex::IntVect(idx_array[0], idx_array[1], 0); - queryWithParserAlt(pp, "insitu_period", m_insitu_period, pp_alt); + queryWithParserAlt(pp, "insitu_period", m_insitu_period.m_func_str, pp_alt); + m_insitu_period.compile(); m_insitu_file_prefix = Hipace::m_output_folder + "/insitu"; const bool set_file_prefix = queryWithParserAlt(pp, "insitu_file_prefix", m_insitu_file_prefix, pp_alt); @@ -281,7 +282,7 @@ PlasmaParticleContainer::InitData (const amrex::Vector& geom3d) InitParticles(m_u_std, m_u_mean, m_radius, m_hollow_core_radius); - if (m_insitu_period > 0) { + if (m_insitu_period.isNonZero()) { #ifdef HIPACE_USE_OPENPMD AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_insitu_file_prefix != Hipace::GetInstance().m_openpmd_writer.m_file_prefix, diff --git a/src/utils/IOUtil.H b/src/utils/IOUtil.H index b3fa89838d..0f662bdea4 100644 --- a/src/utils/IOUtil.H +++ b/src/utils/IOUtil.H @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -46,16 +47,27 @@ namespace utils std::vector getReversedVec ( const amrex::Real* v ); - /** \brief - * returns whether output should be writen to file - * \param[in] output_period period of the output - * \param[in] output_step current step - * \param[in] max_step maximum step of simulation - * \param[in] output_time physical time of the current step - * \param[in] max_time maximum simulation time - */ - bool doDiagnostics (int output_period, int output_step, int max_step, - amrex::Real output_time, amrex::Real max_time); + struct DiagPeriod { + std::string m_func_str; + amrex::Parser m_parser; + amrex::ParserExecutor<2> m_exe; + + DiagPeriod (const char * str) : m_func_str{str} {} + + void compile (); + + bool isNonZero () const { return m_func_str != "0"; } + + /** \brief + * returns whether output should be writen to file + * \param[in] output_step current step + * \param[in] max_step maximum step of simulation + * \param[in] output_time physical time of the current step + * \param[in] max_time maximum simulation time + */ + bool doDiagnostics (int output_step, int max_step, + amrex::Real output_time, amrex::Real max_time) const; + }; struct format_time { double seconds = 0; diff --git a/src/utils/IOUtil.cpp b/src/utils/IOUtil.cpp index fd8c5607d7..b4014b75ea 100644 --- a/src/utils/IOUtil.cpp +++ b/src/utils/IOUtil.cpp @@ -6,6 +6,7 @@ * License: BSD-3-Clause-LBNL */ #include "IOUtil.H" +#include "Parser.H" #include #include @@ -71,10 +72,16 @@ utils::getReversedVec ( const amrex::Real* v ) return u; } +void +utils::DiagPeriod::compile () { + m_exe = makeFunctionWithParser<2>(m_func_str, m_parser, {"current_step", "current_time"}); +} + bool -utils::doDiagnostics (int output_period, int output_step, int max_step, - amrex::Real output_time, amrex::Real max_time) +utils::DiagPeriod::doDiagnostics (int output_step, int max_step, + amrex::Real output_time, amrex::Real max_time) const { + const auto output_period = static_cast(m_exe(output_step, output_time)); return output_period > 0 && ( (output_time == max_time) || (output_step == max_step) ||