From d91090fdea5a35c0f580af77e69cca9195589a30 Mon Sep 17 00:00:00 2001 From: haykh Date: Tue, 6 Jan 2026 16:06:36 -0500 Subject: [PATCH 01/21] cooling -> radiative drag + param overhaul --- input.example.toml | 14 +++-- src/engines/engine_printer.cpp | 7 ++- src/engines/srpic.hpp | 64 +++++++++++------------ src/framework/containers/particles.cpp | 4 +- src/framework/containers/particles.h | 6 +-- src/framework/containers/species.h | 16 +++--- src/framework/parameters.cpp | 72 ++++++++++++++++++-------- src/framework/tests/parameters.cpp | 24 ++++++--- src/framework/tests/particles.cpp | 37 +++++++------ src/global/enums.h | 53 ++++++++++++------- src/global/tests/enums.cpp | 2 - src/kernels/particle_pusher_sr.hpp | 38 +++++--------- src/kernels/tests/ext_force.cpp | 2 +- src/kernels/tests/gca_pusher.cpp | 2 +- src/kernels/tests/prtl_bc.cpp | 2 +- src/kernels/tests/pusher.cpp | 4 +- 16 files changed, 199 insertions(+), 148 deletions(-) diff --git a/input.example.toml b/input.example.toml index 9a4aedee..f14d60ed 100644 --- a/input.example.toml +++ b/input.example.toml @@ -254,7 +254,14 @@ # Radiation reaction limit gamma-factor for synchrotron # @type: float [> 0.0] # @default: 1.0 - # @note: [required] if one of the species has `cooling = "synchrotron"` + # @note: [required] if one of the species has `radiative_drag = "synchrotron"` + gamma_rad = "" + + [algorithms.compton] + # Radiation reaction limit gamma-factor for Compton drag + # @type: float [> 0.0] + # @default: 1.0 + # @note: [required] if one of the species has `radiative_drag = "compton"` gamma_rad = "" # Stencil coefficients for the field solver [notation as in Blinne+ (2018)] @@ -366,8 +373,9 @@ # Radiation reaction to use for the species # @type: string # @default: "None" - # @enum: "None", "Synchrotron" - cooling = "" + # @enum: "None", "Synchrotron", "Compton" + # @note: Can also be coma-separated combination, e.g., "Synchrotron,Compton" + radiative_drag = "" # Parameters for specific problem generators and setups [setup] diff --git a/src/engines/engine_printer.cpp b/src/engines/engine_printer.cpp index 34df741c..4ab9207d 100644 --- a/src/engines/engine_printer.cpp +++ b/src/engines/engine_printer.cpp @@ -401,7 +401,12 @@ namespace ntt { if (species.mass() != 0.0) { add_param(report, 6, "GCA", "%s", species.use_gca() ? "ON" : "OFF"); } - add_param(report, 6, "Cooling", "%s", species.cooling().to_string()); + add_param( + report, + 6, + "Radiative drag", + "%s", + RadiativeDrag::to_string(species.radiative_drag_flags()).c_str()); add_param(report, 6, "# of real-value payloads", "%d", species.npld_r()); add_param(report, 6, "# of integer-value payloads", "%d", species.npld_i()); } diff --git a/src/engines/srpic.hpp b/src/engines/srpic.hpp index fc95e6cf..76d72158 100644 --- a/src/engines/srpic.hpp +++ b/src/engines/srpic.hpp @@ -338,7 +338,7 @@ namespace ntt { } else { raise::Fatal("Invalid particle pusher", HERE); } - const auto cooling = species.cooling(); + const auto radiative_drag_flags = species.radiative_drag_flags(); // coefficients to be forwarded to the dispatcher // gca @@ -349,29 +349,32 @@ namespace ntt { const auto gca_eovrb_max = has_gca ? m_params.template get( "algorithms.gca.e_ovr_b_max") : ZERO; - // cooling - const auto has_synchrotron = (cooling == Cooling::SYNCHROTRON); - const auto has_compton = (cooling == Cooling::COMPTON); - const auto sync_grad = has_synchrotron - ? m_params.template get( + // radiative drag + const auto has_synchrotron = (radiative_drag_flags & + RadiativeDrag::SYNCHROTRON); + const auto has_compton = (radiative_drag_flags & RadiativeDrag::COMPTON); + const auto sync_grad = has_synchrotron + ? m_params.template get( "algorithms.synchrotron.gamma_rad") - : ZERO; - const auto sync_coeff = has_synchrotron - ? (real_t)(0.1) * dt * + : ZERO; + const auto sync_coeff = has_synchrotron + ? (real_t)(0.1) * dt * m_params.template get( "scales.omegaB0") / (SQR(sync_grad) * species.mass()) - : ZERO; - const auto comp_grad = has_compton ? m_params.template get( - "algorithms.compton.gamma_rad") - : ZERO; - const auto comp_coeff = has_compton ? (real_t)(0.1) * dt * - m_params.template get( - "scales.omegaB0") / - (SQR(comp_grad) * species.mass()) - : ZERO; + : ZERO; + const auto compton_grad = has_compton + ? m_params.template get( + "algorithms.compton.gamma_rad") + : ZERO; + const auto compton_coeff = has_compton + ? (real_t)(0.1) * dt * + m_params.template get( + "scales.omegaB0") / + (SQR(compton_grad) * species.mass()) + : ZERO; // toggle to indicate whether pgen defines the external force - bool has_extforce = false; + bool has_extforce = false; if constexpr (traits::pgen::HasExtForce) { has_extforce = true; // toggle to indicate whether the ext force applies to current species @@ -383,13 +386,6 @@ namespace ntt { } } - kernel::sr::CoolingTags cooling_tags = 0; - if (cooling == Cooling::SYNCHROTRON) { - cooling_tags = kernel::sr::Cooling::Synchrotron; - } - if (cooling == Cooling::COMPTON) { - cooling_tags = kernel::sr::Cooling::Compton; - } // clang-format off if (not has_atmosphere and not has_extforce) { Kokkos::parallel_for( @@ -397,7 +393,7 @@ namespace ntt { species.rangeActiveParticles(), kernel::sr::Pusher_kernel( pusher, has_gca, false, - cooling_tags, + radiative_drag_flags, domain.fields.em, species.index(), species.i1, species.i2, species.i3, @@ -412,7 +408,7 @@ namespace ntt { domain.mesh.n_active(in::x2), domain.mesh.n_active(in::x3), domain.mesh.prtl_bc(), - gca_larmor_max, gca_eovrb_max, sync_coeff, comp_coeff + gca_larmor_max, gca_eovrb_max, sync_coeff, compton_coeff )); } else if (has_atmosphere and not has_extforce) { const auto force = @@ -426,7 +422,7 @@ namespace ntt { species.rangeActiveParticles(), kernel::sr::Pusher_kernel( pusher, has_gca, false, - cooling_tags, + radiative_drag_flags, domain.fields.em, species.index(), species.i1, species.i2, species.i3, @@ -442,7 +438,7 @@ namespace ntt { domain.mesh.n_active(in::x2), domain.mesh.n_active(in::x3), domain.mesh.prtl_bc(), - gca_larmor_max, gca_eovrb_max, sync_coeff, comp_coeff + gca_larmor_max, gca_eovrb_max, sync_coeff, compton_coeff )); } else if (not has_atmosphere and has_extforce) { if constexpr (traits::pgen::HasExtForce) { @@ -455,7 +451,7 @@ namespace ntt { species.rangeActiveParticles(), kernel::sr::Pusher_kernel( pusher, has_gca, true, - cooling_tags, + radiative_drag_flags, domain.fields.em, species.index(), species.i1, species.i2, species.i3, @@ -471,7 +467,7 @@ namespace ntt { domain.mesh.n_active(in::x2), domain.mesh.n_active(in::x3), domain.mesh.prtl_bc(), - gca_larmor_max, gca_eovrb_max, sync_coeff, comp_coeff + gca_larmor_max, gca_eovrb_max, sync_coeff, compton_coeff )); } else { raise::Error("External force not implemented", HERE); @@ -487,7 +483,7 @@ namespace ntt { species.rangeActiveParticles(), kernel::sr::Pusher_kernel( pusher, has_gca, true, - cooling_tags, + radiative_drag_flags, domain.fields.em, species.index(), species.i1, species.i2, species.i3, @@ -503,7 +499,7 @@ namespace ntt { domain.mesh.n_active(in::x2), domain.mesh.n_active(in::x3), domain.mesh.prtl_bc(), - gca_larmor_max, gca_eovrb_max, sync_coeff, comp_coeff + gca_larmor_max, gca_eovrb_max, sync_coeff, compton_coeff )); } else { raise::Error("External force not implemented", HERE); diff --git a/src/framework/containers/particles.cpp b/src/framework/containers/particles.cpp index e9e51622..2a254cd9 100644 --- a/src/framework/containers/particles.cpp +++ b/src/framework/containers/particles.cpp @@ -24,7 +24,7 @@ namespace ntt { const PrtlPusher& pusher, bool use_tracking, bool use_gca, - const Cooling& cooling, + RadiativeDragFlags radiative_drag_flags, unsigned short npld_r, unsigned short npld_i) : ParticleSpecies(index, @@ -35,7 +35,7 @@ namespace ntt { pusher, use_tracking, use_gca, - cooling, + radiative_drag_flags, npld_r, npld_i) { diff --git a/src/framework/containers/particles.h b/src/framework/containers/particles.h index 144ca611..66e34cdf 100644 --- a/src/framework/containers/particles.h +++ b/src/framework/containers/particles.h @@ -86,7 +86,7 @@ namespace ntt { * @param pusher The pusher assigned for the species * @param use_tracking Use particle tracking for the species * @param use_gca Use hybrid GCA pusher for the species - * @param cooling The cooling mechanism assigned for the species + * @param radiative_drag_flags The radiative drag mechanism(s) assigned for the species * @param npld_r The number of real-valued payloads for the species * @param npld_i The number of integer-valued payloads for the species */ @@ -98,7 +98,7 @@ namespace ntt { const PrtlPusher& pusher, bool use_gca, bool use_tracking, - const Cooling& cooling, + RadiativeDragFlags radiative_drag_flags, unsigned short npld_r = 0, unsigned short npld_i = 0); @@ -116,7 +116,7 @@ namespace ntt { spec.pusher(), spec.use_tracking(), spec.use_gca(), - spec.cooling(), + spec.radiative_drag_flags(), spec.npld_r(), spec.npld_i()) {} diff --git a/src/framework/containers/species.h b/src/framework/containers/species.h index baf02487..ebc1c701 100644 --- a/src/framework/containers/species.h +++ b/src/framework/containers/species.h @@ -39,8 +39,8 @@ namespace ntt { // Use byrid gca pusher for the species const bool m_use_gca; - // Cooling drag mechanism assigned for the species - const Cooling m_cooling; + // Radiative drag mechanism(s) assigned for the species + const RadiativeDragFlags m_radiative_drag_flags; // Number of payloads for the species const unsigned short m_npld_r; @@ -56,7 +56,7 @@ namespace ntt { , m_pusher { PrtlPusher::INVALID } , m_use_tracking { false } , m_use_gca { false } - , m_cooling { Cooling::INVALID } + , m_radiative_drag_flags { RadiativeDrag::NONE } , m_npld_r { 0 } , m_npld_i { 0 } {} @@ -71,7 +71,7 @@ namespace ntt { * @param pusher The pusher assigned for the species. * @param use_tracking Use particle tracking for the species. * @param use_gca Use hybrid GCA pusher for the species. - * @param cooling The cooling mechanism assigned for the species. + * @param radiative_drag_flags The radiative drag mechanism(s) assigned for the species. * @param npld_r The number of real-valued payloads for the species * @param npld_i The number of integer-valued payloads for the species */ @@ -83,7 +83,7 @@ namespace ntt { const PrtlPusher& pusher, bool use_tracking, bool use_gca, - const Cooling& cooling, + RadiativeDragFlags radiative_drag_flags, unsigned short npld_r = 0, unsigned short npld_i = 0) : m_index { index } @@ -94,7 +94,7 @@ namespace ntt { , m_pusher { pusher } , m_use_tracking { use_tracking } , m_use_gca { use_gca } - , m_cooling { cooling } + , m_radiative_drag_flags { radiative_drag_flags } , m_npld_r { npld_r } , m_npld_i { npld_i } { if (use_tracking) { @@ -159,8 +159,8 @@ namespace ntt { } [[nodiscard]] - auto cooling() const -> Cooling { - return m_cooling; + auto radiative_drag_flags() const -> RadiativeDragFlags { + return m_radiative_drag_flags; } [[nodiscard]] diff --git a/src/framework/parameters.cpp b/src/framework/parameters.cpp index f7ef82fd..44af6407 100644 --- a/src/framework/parameters.cpp +++ b/src/framework/parameters.cpp @@ -47,6 +47,34 @@ namespace ntt { return { dx0, V0 }; } + /* + * Auxiliary functions + */ + auto getRadiativeDragFlags( + const std::string& radiative_drag_str) -> RadiativeDragFlags { + if (fmt::toLower(radiative_drag_str) == "none") { + return RadiativeDrag::NONE; + } else { + // separate comas + RadiativeDragFlags flags = RadiativeDrag::NONE; + std::string token; + std::istringstream tokenStream(radiative_drag_str); + while (std::getline(tokenStream, token, ',')) { + const auto token_lower = fmt::toLower(token); + if (token_lower == "synchrotron") { + flags |= RadiativeDrag::SYNCHROTRON; + } else if (token_lower == "compton") { + flags |= RadiativeDrag::COMPTON; + } else { + raise::Error( + fmt::format("Invalid radiative_drag value: %s", radiative_drag_str), + HERE); + } + } + return flags; + } + } + /* * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * Parameters that must not be changed during the checkpoint restart @@ -216,9 +244,11 @@ namespace ntt { npayloads_int += 2; #endif } - const auto cooling = toml::find_or(sp, "cooling", std::string("None")); - raise::ErrorIf((fmt::toLower(cooling) != "none") && is_massless, - "cooling is only applicable to massive particles", + const auto radiative_drag_str = toml::find_or(sp, + "radiative_drag", + std::string("none")); + raise::ErrorIf((fmt::toLower(radiative_drag_str) != "none") && is_massless, + "radiative drag is only applicable to massive particles", HERE); raise::ErrorIf((fmt::toLower(pusher) == "photon") && !is_massless, "photon pusher is only applicable to massless particles", @@ -232,27 +262,27 @@ namespace ntt { use_gca = true; pusher = pusher.substr(0, pusher.find(',')); } - const auto pusher_enum = PrtlPusher::pick(pusher.c_str()); - const auto cooling_enum = Cooling::pick(cooling.c_str()); - if (use_gca) { - raise::ErrorIf(engine_enum != SimEngine::SRPIC, - "GCA pushers are only supported for SRPIC", - HERE); - promiseToDefine("algorithms.gca.e_ovr_b_max"); - promiseToDefine("algorithms.gca.larmor_max"); - } - if (cooling_enum == Cooling::SYNCHROTRON) { + const auto pusher_enum = PrtlPusher::pick(pusher.c_str()); + const auto radiative_drag_flags = getRadiativeDragFlags(radiative_drag_str); + if (radiative_drag_flags & RadiativeDrag::SYNCHROTRON) { raise::ErrorIf(engine_enum != SimEngine::SRPIC, - "Synchrotron cooling is only supported for SRPIC", + "Synchrotron radiative drag is only supported for SRPIC", HERE); promiseToDefine("algorithms.synchrotron.gamma_rad"); } - - if (cooling_enum == Cooling::COMPTON) { + if (radiative_drag_flags & RadiativeDrag::COMPTON) { + raise::ErrorIf( + engine_enum != SimEngine::SRPIC, + "Inverse Compton radiative drag is only supported for SRPIC", + HERE); + promiseToDefine("algorithms.compton.gamma_rad"); + } + if (use_gca) { raise::ErrorIf(engine_enum != SimEngine::SRPIC, - "Inverse Compton cooling is only supported for SRPIC", + "GCA pushers are only supported for SRPIC", HERE); - promiseToDefine("algorithms.compton.gamma_rad"); + promiseToDefine("algorithms.gca.e_ovr_b_max"); + promiseToDefine("algorithms.gca.larmor_max"); } species.emplace_back(ParticleSpecies(idx, @@ -263,7 +293,7 @@ namespace ntt { pusher_enum, use_tracking, use_gca, - cooling_enum, + radiative_drag_flags, npayloads_real, npayloads_int)); idx += 1; @@ -550,7 +580,7 @@ namespace ntt { particle_species.pusher(), particle_species.use_tracking(), particle_species.use_gca(), - particle_species.cooling(), + particle_species.radiative_drag_flags(), particle_species.npld_r(), particle_species.npld_i()); idxM1++; @@ -1018,7 +1048,7 @@ namespace ntt { toml::find_or(toml_data, "algorithms", "gca", "larmor_max", ZERO)); } - // cooling + // radiative drag parameters if (isPromised("algorithms.synchrotron.gamma_rad")) { set("algorithms.synchrotron.gamma_rad", toml::find_or(toml_data, diff --git a/src/framework/tests/parameters.cpp b/src/framework/tests/parameters.cpp index 78a3294b..9c597212 100644 --- a/src/framework/tests/parameters.cpp +++ b/src/framework/tests/parameters.cpp @@ -138,6 +138,9 @@ const auto sph_2d = u8R"( [algorithms.synchrotron] gamma_rad = 50.0 + [algorithms.compton] + gamma_rad = 20.0 + [particles] ppc0 = 25.0 use_weights = true @@ -151,7 +154,7 @@ const auto sph_2d = u8R"( maxnpart = 1e2 pusher = "boris,gca" n_payloads_real = 3 - cooling = "synchrotron" + radiative_drag = "synchrotron,compton" [[particles.species]] label = "e+" @@ -159,7 +162,7 @@ const auto sph_2d = u8R"( charge = 1.0 maxnpart = 1e2 pusher = "boris,gca" - cooling = "synchrotron" + radiative_drag = "synchrotron" n_payloads_int = 2 [[particles.species]] @@ -458,6 +461,10 @@ auto main(int argc, char* argv[]) -> int { (real_t)50.0, "algorithms.synchrotron.gamma_rad"); + assert_equal(params_sph_2d.get("algorithms.compton.gamma_rad"), + (real_t)20.0, + "algorithms.compton.gamma_rad"); + const auto species = params_sph_2d.get>( "particles.species"); assert_equal(species[0].label(), "e-", "species[0].label"); @@ -469,9 +476,10 @@ auto main(int argc, char* argv[]) -> int { "species[0].pusher"); assert_equal(species[0].use_gca(), true, "species[0].use_gca"); assert_equal(species[0].npld_r(), 3, "species[0].npld_r"); - assert_equal(species[0].cooling(), - Cooling::SYNCHROTRON, - "species[0].cooling"); + assert_equal(species[0].radiative_drag_flags(), + RadiativeDrag::SYNCHROTRON | + RadiativeDrag::COMPTON, + "species[0].radiative_drag_flags"); assert_equal(species[1].label(), "e+", "species[1].label"); assert_equal(species[1].mass(), 1.0f, "species[1].mass"); @@ -482,9 +490,9 @@ auto main(int argc, char* argv[]) -> int { "species[1].pusher"); assert_equal(species[1].use_gca(), true, "species[1].use_gca"); assert_equal(species[1].npld_r(), 0, "species[1].npld_r"); - assert_equal(species[1].cooling(), - Cooling::SYNCHROTRON, - "species[1].cooling"); + assert_equal(species[1].radiative_drag_flags(), + RadiativeDrag::SYNCHROTRON, + "species[1].radiative_drag_flags"); assert_equal(species[1].npld_i(), 2, "species[1].npld_i"); assert_equal(species[1].use_tracking(), false, "species[1].tracking"); diff --git a/src/framework/tests/particles.cpp b/src/framework/tests/particles.cpp index ab3aa0e0..9592a66e 100644 --- a/src/framework/tests/particles.cpp +++ b/src/framework/tests/particles.cpp @@ -9,16 +9,16 @@ #include template -void testParticles(int index, - const std::string& label, - float m, - float ch, - std::size_t maxnpart, - const ntt::PrtlPusher& pusher, - bool use_tracking, - const ntt::Cooling& cooling, - unsigned short npld_r = 0, - unsigned short npld_i = 0) { +void testParticles(int index, + const std::string& label, + float m, + float ch, + std::size_t maxnpart, + const ntt::PrtlPusher& pusher, + bool use_tracking, + ntt::RadiativeDragFlags radiative_drag_flags, + unsigned short npld_r = 0, + unsigned short npld_i = 0) { using namespace ntt; auto p = Particles(index, label, @@ -28,7 +28,7 @@ void testParticles(int index, pusher, use_tracking, false, - cooling, + radiative_drag_flags, npld_r, npld_i); raise::ErrorIf(p.index() != index, "Index mismatch", HERE); @@ -37,7 +37,9 @@ void testParticles(int index, raise::ErrorIf(p.charge() != ch, "Charge mismatch", HERE); raise::ErrorIf(p.maxnpart() != maxnpart, "Max number of particles mismatch", HERE); raise::ErrorIf(p.pusher() != pusher, "Pusher mismatch", HERE); - raise::ErrorIf(p.cooling() != cooling, "Cooling mismatch", HERE); + raise::ErrorIf(p.radiative_drag_flags() != radiative_drag_flags, + "Radiative drag mismatch", + HERE); raise::ErrorIf(p.npart() != 0, "Number of particles mismatch", HERE); raise::ErrorIf(p.i1.extent(0) != maxnpart, "i1 incorrectly allocated", HERE); @@ -117,7 +119,7 @@ auto main(int argc, char** argv) -> int { 100, PrtlPusher::BORIS, false, - Cooling::SYNCHROTRON); + RadiativeDrag::SYNCHROTRON); testParticles(2, "p+", 100.0, @@ -125,7 +127,8 @@ auto main(int argc, char** argv) -> int { 1000, PrtlPusher::VAY, true, - Cooling::SYNCHROTRON, + RadiativeDrag::SYNCHROTRON | + RadiativeDrag::COMPTON, 2, 1); testParticles(3, @@ -135,7 +138,7 @@ auto main(int argc, char** argv) -> int { 100, PrtlPusher::PHOTON, false, - Cooling::NONE, + RadiativeDrag::NONE, 5); testParticles(4, "e+", @@ -144,7 +147,7 @@ auto main(int argc, char** argv) -> int { 100, PrtlPusher::BORIS, true, - Cooling::NONE, + RadiativeDrag::NONE, 2, 3); testParticles(5, @@ -154,7 +157,7 @@ auto main(int argc, char** argv) -> int { 100, PrtlPusher::BORIS, false, - Cooling::NONE, + RadiativeDrag::NONE, 1, 2); } catch (const std::exception& e) { diff --git a/src/global/enums.h b/src/global/enums.h index 4b11604d..f1108a8e 100644 --- a/src/global/enums.h +++ b/src/global/enums.h @@ -11,11 +11,13 @@ * - enum ntt::FldsBC // periodic, match, fixed, atmosphere, * custom, horizon, axis, conductor, sync * - enum ntt::PrtlPusher // boris, vay, photon, none - * - enum ntt::Cooling // compton, synchrotron, none * - enum ntt::FldsID // e, dive, d, divd, b, h, j, * a, t, rho, charge, n, nppc, v, custom * - enum ntt::StatsID // b^2, e^2, exb, j.e, t, rho, * charge, n, npart + * + * - enum ntt::RadiativeDrag // compton, synchrotron, none + * * @namespaces: * - ntt:: * @note Enums of the same type can be compared with each other and with strings @@ -33,10 +35,10 @@ * example: PrtlPusher::pick("vay") [returns PrtlPusher(PrtlPusher::VAY)] * @note * To get the total number of enum instances, use the total variable - * example: Cooling::total == 2 + * example: SimEngine::total == 2 * @note * To iterate over all enum instances, use the variants array - * example: for (Cooling c : Cooling::variants) { ... } + * example: for (auto s : SimEngine::variants) { ... } */ #ifndef GLOBAL_ENUMS_H @@ -259,23 +261,6 @@ namespace ntt { static constexpr std::size_t total = sizeof(variants) / sizeof(variants[0]); }; - struct Cooling : public enums_hidden::BaseEnum { - static constexpr const char* label = "cooling"; - - enum type : uint8_t { - INVALID = 0, - SYNCHROTRON = 1, - COMPTON = 2, - NONE = 3, - }; - - constexpr Cooling(uint8_t c) : enums_hidden::BaseEnum { c } {} - - static constexpr type variants[] = { SYNCHROTRON, COMPTON, NONE }; - static constexpr const char* lookup[] = { "synchrotron", "compton", "none" }; - static constexpr std::size_t total = sizeof(variants) / sizeof(variants[0]); - }; - struct FldsID : public enums_hidden::BaseEnum { static constexpr const char* label = "out_flds"; @@ -337,6 +322,34 @@ namespace ntt { static constexpr std::size_t total = sizeof(variants) / sizeof(variants[0]); }; + namespace RadiativeDrag { + enum RadiativeDragFlags_ { + NONE = 0, + SYNCHROTRON = 1 << 0, + COMPTON = 1 << 1, + }; + + inline auto to_string(int flags) -> std::string { + if (flags == NONE) { + return "none"; + } else { + std::string result = ""; + if (flags & SYNCHROTRON) { + result += "synchrotron"; + } + if (flags & COMPTON) { + if (!result.empty()) { + result += ","; + } + result += "compton"; + } + return result; + } + } + } // namespace RadiativeDrag + + typedef int RadiativeDragFlags; + } // namespace ntt #endif // GLOBAL_ENUMS_H diff --git a/src/global/tests/enums.cpp b/src/global/tests/enums.cpp index c70572be..55ed63a7 100644 --- a/src/global/tests/enums.cpp +++ b/src/global/tests/enums.cpp @@ -65,7 +65,6 @@ auto main() -> int { "atmosphere", "custom", "horizon", "axis", "conductor", "sync" }; enum_str_t all_particle_pushers = { "boris", "vay", "photon", "none" }; - enum_str_t all_coolings = { "synchrotron", "compton", "none" }; enum_str_t all_out_flds = { "e", "dive", "d", "divd", "b", "h", "j", "a", "t", "rho", @@ -80,7 +79,6 @@ auto main() -> int { checkEnum(all_particle_bcs); checkEnum(all_fields_bcs); checkEnum(all_particle_pushers); - checkEnum(all_coolings); checkEnum(all_out_flds); checkEnum(all_out_stats); diff --git a/src/kernels/particle_pusher_sr.hpp b/src/kernels/particle_pusher_sr.hpp index 9f9a48a8..9b5672c9 100644 --- a/src/kernels/particle_pusher_sr.hpp +++ b/src/kernels/particle_pusher_sr.hpp @@ -48,16 +48,6 @@ namespace kernel::sr { using namespace ntt; - namespace Cooling { - enum CoolingTags_ { - None = 0, - Synchrotron = 1 << 0, - Compton = 1 << 1, - }; - } // namespace Cooling - - typedef int CoolingTags; - struct NoForce_t { NoForce_t() {} }; @@ -198,10 +188,10 @@ namespace kernel::sr { static constexpr auto ExtForce = not std::is_same::value; private: - const PrtlPusher::type pusher; - const bool GCA; - const bool ext_force; - const CoolingTags cooling; + const PrtlPusher::type pusher; + const bool GCA; + const bool ext_force; + const RadiativeDragFlags radiative_drag_flags; const randacc_ndfield_t EB; const spidx_t sp; @@ -229,14 +219,14 @@ namespace kernel::sr { bool is_axis_i2min { false }, is_axis_i2max { false }; // gca parameters const real_t gca_larmor, gca_EovrB_sqr; - // radiative cooling parameters + // radiative drag parameters const real_t coeff_sync, coeff_comp; public: Pusher_kernel(const PrtlPusher::type& pusher, bool GCA, bool ext_force, - CoolingTags cooling, + RadiativeDragFlags radiative_drag_flags, const randacc_ndfield_t& EB, spidx_t sp, array_t& i1, @@ -272,7 +262,7 @@ namespace kernel::sr { : pusher { pusher } , GCA { GCA } , ext_force { ext_force } - , cooling { cooling } + , radiative_drag_flags { radiative_drag_flags } , EB { EB } , sp { sp } , i1 { i1 } @@ -342,7 +332,7 @@ namespace kernel::sr { Pusher_kernel(const PrtlPusher::type& pusher, bool GCA, bool ext_force, - CoolingTags cooling, + RadiativeDragFlags radiative_drag_flags, const ndfield_t& EB, spidx_t sp, array_t& i1, @@ -377,7 +367,7 @@ namespace kernel::sr { : Pusher_kernel(pusher, GCA, ext_force, - cooling, + radiative_drag_flags, EB, sp, i1, @@ -502,8 +492,8 @@ namespace kernel::sr { metric.template transform_xyz(xp_Cd, ei, ei_Cart); metric.template transform_xyz(xp_Cd, bi, bi_Cart); - if (cooling != 0) { - // backup fields & velocities to use later in cooling + if (radiative_drag_flags != RadiativeDrag::NONE) { + // backup fields & velocities to use later in radiative drag ei_Cart_rad[0] = ei_Cart[0]; ei_Cart_rad[1] = ei_Cart[1]; ei_Cart_rad[2] = ei_Cart[2]; @@ -573,8 +563,8 @@ namespace kernel::sr { ux3(p) += HALF * dt * force_Cart[2]; } } - // cooling - if (cooling & Cooling::Synchrotron) { + // radiative drag + if (radiative_drag_flags & RadiativeDrag::SYNCHROTRON) { if (!is_gca) { u_prime[0] = HALF * (u_prime[0] + ux1(p)); u_prime[1] = HALF * (u_prime[1] + ux2(p)); @@ -582,7 +572,7 @@ namespace kernel::sr { synchrotronDrag(p, u_prime, ei_Cart_rad, bi_Cart_rad); } } - if (cooling & Cooling::Compton) { + if (radiative_drag_flags & RadiativeDrag::COMPTON) { if (!is_gca) { u_prime[0] = HALF * (u_prime[0] + ux1(p)); u_prime[1] = HALF * (u_prime[1] + ux2(p)); diff --git a/src/kernels/tests/ext_force.cpp b/src/kernels/tests/ext_force.cpp index 916f7b11..21d57b24 100644 --- a/src/kernels/tests/ext_force.cpp +++ b/src/kernels/tests/ext_force.cpp @@ -184,7 +184,7 @@ void testPusher(const std::vector& res) { "pusher", CreateRangePolicy({0}, {2}), kernel::sr::Pusher_kernel, decltype(force)>(PrtlPusher::BORIS, - false, true, kernel::sr::Cooling::None, + false, true, RadiativeDrag::NONE, emfield, sp, i1, i2, i3, diff --git a/src/kernels/tests/gca_pusher.cpp b/src/kernels/tests/gca_pusher.cpp index 7a2e2d9a..405aa193 100644 --- a/src/kernels/tests/gca_pusher.cpp +++ b/src/kernels/tests/gca_pusher.cpp @@ -160,7 +160,7 @@ void testPusher(const std::vector& res) { "pusher", CreateRangePolicy({0}, {2}), kernel::sr::Pusher_kernel>(PrtlPusher::BORIS, - true, false, kernel::sr::Cooling::None, + true, false, RadiativeDrag::NONE, emfield, sp, i1, i2, i3, diff --git a/src/kernels/tests/prtl_bc.cpp b/src/kernels/tests/prtl_bc.cpp index f75c90bc..aea08943 100644 --- a/src/kernels/tests/prtl_bc.cpp +++ b/src/kernels/tests/prtl_bc.cpp @@ -246,7 +246,7 @@ void testPeriodicBC(const std::vector& res, Kokkos::parallel_for( "pusher", CreateRangePolicy({ 0 }, { 2 }), kernel::sr::Pusher_kernel(PrtlPusher::BORIS, - NoGCA, NoExtForce, kernel::sr::Cooling::None, + NoGCA, NoExtForce, RadiativeDrag::NONE, emfield, sp_idx, i1, i2, i3, diff --git a/src/kernels/tests/pusher.cpp b/src/kernels/tests/pusher.cpp index ddbc1be8..8c783efe 100644 --- a/src/kernels/tests/pusher.cpp +++ b/src/kernels/tests/pusher.cpp @@ -162,7 +162,7 @@ void testPusher(const std::vector& res) { "pusher", CreateRangePolicy({0}, {1}), kernel::sr::Pusher_kernel>(PrtlPusher::BORIS, - false, false, kernel::sr::Cooling::None, + false, false, RadiativeDrag::NONE, emfield, sp, i1, i2, i3, @@ -181,7 +181,7 @@ void testPusher(const std::vector& res) { "pusher", CreateRangePolicy({1}, {2}), kernel::sr::Pusher_kernel>(PrtlPusher::VAY, - false, false, kernel::sr::Cooling::None, + false, false, RadiativeDrag::NONE, emfield, sp, i1, i2, i3, From 6d9eb8b84128bedee23a06c1f2db92267240d4ed Mon Sep 17 00:00:00 2001 From: haykh Date: Thu, 8 Jan 2026 14:32:15 -0500 Subject: [PATCH 02/21] parameters reader refactor (CPUTEST) --- src/archetypes/problem_generator.h | 2 +- src/archetypes/utils.h | 2 +- src/engines/engine.hpp | 2 +- src/engines/grpic.hpp | 2 +- src/engines/srpic.hpp | 2 +- src/framework/CMakeLists.txt | 4 +- src/framework/domain/checkpoint.cpp | 2 +- src/framework/domain/metadomain.h | 2 +- src/framework/domain/output.cpp | 2 +- src/framework/domain/stats.cpp | 2 +- src/framework/parameters/grid.cpp | 289 ++++++++++++++ src/framework/parameters/grid.h | 34 ++ src/framework/{ => parameters}/parameters.cpp | 367 +----------------- src/framework/{ => parameters}/parameters.h | 8 +- src/framework/parameters/particles.cpp | 135 +++++++ src/framework/parameters/particles.h | 24 ++ src/framework/simulation.h | 2 +- src/framework/tests/parameters.cpp | 2 +- 18 files changed, 517 insertions(+), 366 deletions(-) create mode 100644 src/framework/parameters/grid.cpp create mode 100644 src/framework/parameters/grid.h rename src/framework/{ => parameters}/parameters.cpp (67%) rename src/framework/{ => parameters}/parameters.h (89%) create mode 100644 src/framework/parameters/particles.cpp create mode 100644 src/framework/parameters/particles.h diff --git a/src/archetypes/problem_generator.h b/src/archetypes/problem_generator.h index efcec90a..0f5e3b88 100644 --- a/src/archetypes/problem_generator.h +++ b/src/archetypes/problem_generator.h @@ -25,7 +25,7 @@ #include "arch/traits.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" namespace arch { using namespace ntt; diff --git a/src/archetypes/utils.h b/src/archetypes/utils.h index b3eb24ae..bbb4f8fb 100644 --- a/src/archetypes/utils.h +++ b/src/archetypes/utils.h @@ -17,7 +17,7 @@ #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" #include "framework/domain/domain.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include diff --git a/src/engines/engine.hpp b/src/engines/engine.hpp index 3117b082..e67b90c1 100644 --- a/src/engines/engine.hpp +++ b/src/engines/engine.hpp @@ -28,7 +28,7 @@ #include "framework/containers/species.h" #include "framework/domain/metadomain.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "pgen.hpp" diff --git a/src/engines/grpic.hpp b/src/engines/grpic.hpp index 275c70b5..637deb5a 100644 --- a/src/engines/grpic.hpp +++ b/src/engines/grpic.hpp @@ -22,7 +22,7 @@ #include "utils/toml.h" #include "framework/domain/domain.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "engines/engine.hpp" #include "kernels/ampere_gr.hpp" diff --git a/src/engines/srpic.hpp b/src/engines/srpic.hpp index 76d72158..55b41ab9 100644 --- a/src/engines/srpic.hpp +++ b/src/engines/srpic.hpp @@ -27,7 +27,7 @@ #include "archetypes/particle_injector.h" #include "archetypes/spatial_dist.h" #include "framework/domain/domain.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "engines/engine.hpp" #include "kernels/ampere_mink.hpp" diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index 01496787..8ce63d0a 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -36,8 +36,10 @@ set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(SOURCES - ${SRC_DIR}/parameters.cpp ${SRC_DIR}/simulation.cpp + ${SRC_DIR}/parameters/parameters.cpp + ${SRC_DIR}/parameters/particles.cpp + ${SRC_DIR}/parameters/grid.cpp ${SRC_DIR}/domain/grid.cpp ${SRC_DIR}/domain/metadomain.cpp ${SRC_DIR}/domain/communications.cpp diff --git a/src/framework/domain/checkpoint.cpp b/src/framework/domain/checkpoint.cpp index 6e10eda2..693cafc1 100644 --- a/src/framework/domain/checkpoint.cpp +++ b/src/framework/domain/checkpoint.cpp @@ -8,7 +8,7 @@ #include "utils/log.h" #include "framework/domain/metadomain.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "framework/specialization_registry.h" namespace ntt { diff --git a/src/framework/domain/metadomain.h b/src/framework/domain/metadomain.h index bd6ec864..611f658c 100644 --- a/src/framework/domain/metadomain.h +++ b/src/framework/domain/metadomain.h @@ -24,7 +24,7 @@ #include "framework/containers/species.h" #include "framework/domain/domain.h" #include "framework/domain/mesh.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "output/stats.h" #if defined(MPI_ENABLED) diff --git a/src/framework/domain/output.cpp b/src/framework/domain/output.cpp index b4ff2123..5cced2d9 100644 --- a/src/framework/domain/output.cpp +++ b/src/framework/domain/output.cpp @@ -9,7 +9,7 @@ #include "framework/containers/particles.h" #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "framework/specialization_registry.h" #include "kernels/divergences.hpp" diff --git a/src/framework/domain/stats.cpp b/src/framework/domain/stats.cpp index 83d65956..811b3511 100644 --- a/src/framework/domain/stats.cpp +++ b/src/framework/domain/stats.cpp @@ -9,7 +9,7 @@ #include "framework/containers/particles.h" #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "framework/specialization_registry.h" #include "kernels/reduced_stats.hpp" diff --git a/src/framework/parameters/grid.cpp b/src/framework/parameters/grid.cpp new file mode 100644 index 00000000..9a0163b9 --- /dev/null +++ b/src/framework/parameters/grid.cpp @@ -0,0 +1,289 @@ +#include "framework/parameters/grid.h" + +#include "defaults.h" +#include "global.h" + +#include "utils/error.h" +#include "utils/formatting.h" +#include "utils/numeric.h" +#include "utils/toml.h" + +#include "framework/parameters/parameters.h" + +#include +#include +#include +#include + +namespace ntt { + namespace params { + + auto GetGridParams( + const toml::value& toml_data) -> std::tuple, Dimension> { + const auto res = toml::find>(toml_data, + "grid", + "resolution"); + raise::ErrorIf(res.size() < 1 || res.size() > 3, + "invalid `grid.resolution`", + HERE); + const auto dim = static_cast(res.size()); + return { res, dim }; + } + + auto GetMetricParams(const SimEngine& engine_enum, + Dimension dim, + const toml::value& toml_data) + -> std::tuple> { + const auto metric_enum = Metric::pick( + fmt::toLower(toml::find(toml_data, "grid", "metric", "metric")) + .c_str()); + std::map additional_params; + std::string coord; + if (metric_enum == Metric::Minkowski) { + raise::ErrorIf(engine_enum != SimEngine::SRPIC, + "minkowski metric is only supported for SRPIC", + HERE); + coord = "cart"; + } else if (metric_enum == Metric::QKerr_Schild or + metric_enum == Metric::QSpherical) { + // quasi-spherical geometry + raise::ErrorIf(dim == Dim::_1D, + "not enough dimensions for qspherical geometry", + HERE); + raise::ErrorIf(dim == Dim::_3D, + "3D not implemented for qspherical geometry", + HERE); + coord = "qsph"; + additional_params["qsph_r0"] = toml::find_or(toml_data, + "grid", + "metric", + "qsph_r0", + defaults::qsph::r0); + additional_params["qsph_h"] = toml::find_or(toml_data, + "grid", + "metric", + "qsph_h", + defaults::qsph::h); + } else { + // spherical geometry + raise::ErrorIf(dim == Dim::_1D, + "not enough dimensions for spherical geometry", + HERE); + raise::ErrorIf(dim == Dim::_3D, + "3D not implemented for spherical geometry", + HERE); + coord = "sph"; + } + if ((engine_enum == SimEngine::GRPIC) && + (metric_enum != Metric::Kerr_Schild_0)) { + const auto ks_a = toml::find_or(toml_data, + "grid", + "metric", + "ks_a", + defaults::ks::a); + additional_params["ks_a"] = ks_a; + additional_params["ks_rh"] = ONE + math::sqrt(ONE - SQR(ks_a)); + } + const auto coord_enum = Coord::pick(coord.c_str()); + return { metric_enum, coord_enum, additional_params }; + } + + auto GetBoundaryConditions(SimulationParams* params, + const SimEngine& engine_enum, + Dimension dim, + const Coord& coord_enum, + const toml::value& toml_data) + -> std::tuple, boundaries_t> { + auto flds_bc = toml::find>>( + toml_data, + "grid", + "boundaries", + "fields"); + { + raise::ErrorIf(flds_bc.size() < 1 || flds_bc.size() > 3, + "invalid `grid.boundaries.fields`", + HERE); + params->promiseToDefine("grid.boundaries.fields"); + auto atm_defined = false; + for (const auto& bcs : flds_bc) { + for (const auto& bc : bcs) { + if (fmt::toLower(bc) == "match") { + params->promiseToDefine("grid.boundaries.match.ds"); + } + if (fmt::toLower(bc) == "atmosphere") { + raise::ErrorIf(atm_defined, + "ATMOSPHERE is only allowed in one direction", + HERE); + atm_defined = true; + params->promiseToDefine("grid.boundaries.atmosphere.temperature"); + params->promiseToDefine("grid.boundaries.atmosphere.density"); + params->promiseToDefine("grid.boundaries.atmosphere.height"); + params->promiseToDefine("grid.boundaries.atmosphere.ds"); + params->promiseToDefine("grid.boundaries.atmosphere.species"); + params->promiseToDefine("grid.boundaries.atmosphere.g"); + } + } + } + } + + auto prtl_bc = toml::find>>( + toml_data, + "grid", + "boundaries", + "particles"); + { + raise::ErrorIf(prtl_bc.size() < 1 || prtl_bc.size() > 3, + "invalid `grid.boundaries.particles`", + HERE); + params->promiseToDefine("grid.boundaries.particles"); + auto atm_defined = false; + for (const auto& bcs : prtl_bc) { + for (const auto& bc : bcs) { + if (fmt::toLower(bc) == "absorb") { + params->promiseToDefine("grid.boundaries.absorb.ds"); + } + if (fmt::toLower(bc) == "atmosphere") { + raise::ErrorIf(atm_defined, + "ATMOSPHERE is only allowed in one direction", + HERE); + atm_defined = true; + params->promiseToDefine("grid.boundaries.atmosphere.temperature"); + params->promiseToDefine("grid.boundaries.atmosphere.density"); + params->promiseToDefine("grid.boundaries.atmosphere.height"); + params->promiseToDefine("grid.boundaries.atmosphere.ds"); + params->promiseToDefine("grid.boundaries.atmosphere.species"); + params->promiseToDefine("grid.boundaries.atmosphere.g"); + } + } + } + } + std::vector> flds_bc_enum; + std::vector> prtl_bc_enum; + if (coord_enum == Coord::Cart) { + raise::ErrorIf(flds_bc.size() != (std::size_t)dim, + "invalid `grid.boundaries.fields`", + HERE); + raise::ErrorIf(prtl_bc.size() != (std::size_t)dim, + "invalid `grid.boundaries.particles`", + HERE); + for (auto d { 0u }; d < (dim_t)dim; ++d) { + flds_bc_enum.push_back({}); + prtl_bc_enum.push_back({}); + const auto fbc = flds_bc[d]; + const auto pbc = prtl_bc[d]; + raise::ErrorIf(fbc.size() < 1 || fbc.size() > 2, + "invalid `grid.boundaries.fields`", + HERE); + raise::ErrorIf(pbc.size() < 1 || pbc.size() > 2, + "invalid `grid.boundaries.particles`", + HERE); + auto fbc_enum = FldsBC::pick(fmt::toLower(fbc[0]).c_str()); + auto pbc_enum = PrtlBC::pick(fmt::toLower(pbc[0]).c_str()); + if (fbc.size() == 1) { + raise::ErrorIf(fbc_enum != FldsBC::PERIODIC, + "invalid `grid.boundaries.fields`", + HERE); + flds_bc_enum.back().push_back(FldsBC(FldsBC::PERIODIC)); + flds_bc_enum.back().push_back(FldsBC(FldsBC::PERIODIC)); + } else { + raise::ErrorIf(fbc_enum == FldsBC::PERIODIC, + "invalid `grid.boundaries.fields`", + HERE); + flds_bc_enum.back().push_back(fbc_enum); + auto fbc_enum = FldsBC::pick(fmt::toLower(fbc[1]).c_str()); + raise::ErrorIf(fbc_enum == FldsBC::PERIODIC, + "invalid `grid.boundaries.fields`", + HERE); + flds_bc_enum.back().push_back(fbc_enum); + } + if (pbc.size() == 1) { + raise::ErrorIf(pbc_enum != PrtlBC::PERIODIC, + "invalid `grid.boundaries.particles`", + HERE); + prtl_bc_enum.back().push_back(PrtlBC(PrtlBC::PERIODIC)); + prtl_bc_enum.back().push_back(PrtlBC(PrtlBC::PERIODIC)); + } else { + raise::ErrorIf(pbc_enum == PrtlBC::PERIODIC, + "invalid `grid.boundaries.particles`", + HERE); + prtl_bc_enum.back().push_back(pbc_enum); + auto pbc_enum = PrtlBC::pick(fmt::toLower(pbc[1]).c_str()); + raise::ErrorIf(pbc_enum == PrtlBC::PERIODIC, + "invalid `grid.boundaries.particles`", + HERE); + prtl_bc_enum.back().push_back(pbc_enum); + } + } + } else { + raise::ErrorIf(flds_bc.size() > 1, "invalid `grid.boundaries.fields`", HERE); + raise::ErrorIf(prtl_bc.size() > 1, + "invalid `grid.boundaries.particles`", + HERE); + if (engine_enum == SimEngine::SRPIC) { + raise::ErrorIf(flds_bc[0].size() != 2, + "invalid `grid.boundaries.fields`", + HERE); + flds_bc_enum.push_back( + { FldsBC::pick(fmt::toLower(flds_bc[0][0]).c_str()), + FldsBC::pick(fmt::toLower(flds_bc[0][1]).c_str()) }); + flds_bc_enum.push_back({ FldsBC::AXIS, FldsBC::AXIS }); + if (dim == Dim::_3D) { + flds_bc_enum.push_back({ FldsBC::PERIODIC, FldsBC::PERIODIC }); + } + raise::ErrorIf(prtl_bc[0].size() != 2, + "invalid `grid.boundaries.particles`", + HERE); + prtl_bc_enum.push_back( + { PrtlBC::pick(fmt::toLower(prtl_bc[0][0]).c_str()), + PrtlBC::pick(fmt::toLower(prtl_bc[0][1]).c_str()) }); + prtl_bc_enum.push_back({ PrtlBC::AXIS, PrtlBC::AXIS }); + if (dim == Dim::_3D) { + prtl_bc_enum.push_back({ PrtlBC::PERIODIC, PrtlBC::PERIODIC }); + } + } else { + raise::ErrorIf(flds_bc[0].size() != 1, + "invalid `grid.boundaries.fields`", + HERE); + raise::ErrorIf(prtl_bc[0].size() != 1, + "invalid `grid.boundaries.particles`", + HERE); + flds_bc_enum.push_back( + { FldsBC::HORIZON, FldsBC::pick(fmt::toLower(flds_bc[0][0]).c_str()) }); + flds_bc_enum.push_back({ FldsBC::AXIS, FldsBC::AXIS }); + if (dim == Dim::_3D) { + flds_bc_enum.push_back({ FldsBC::PERIODIC, FldsBC::PERIODIC }); + } + prtl_bc_enum.push_back( + { PrtlBC::HORIZON, PrtlBC::pick(fmt::toLower(prtl_bc[0][0]).c_str()) }); + prtl_bc_enum.push_back({ PrtlBC::AXIS, PrtlBC::AXIS }); + if (dim == Dim::_3D) { + prtl_bc_enum.push_back({ PrtlBC::PERIODIC, PrtlBC::PERIODIC }); + } + } + } + + raise::ErrorIf(flds_bc_enum.size() != (std::size_t)dim, + "invalid inferred `grid.boundaries.fields`", + HERE); + raise::ErrorIf(prtl_bc_enum.size() != (std::size_t)dim, + "invalid inferred `grid.boundaries.particles`", + HERE); + boundaries_t flds_bc_pairwise; + boundaries_t prtl_bc_pairwise; + for (auto d { 0u }; d < (dim_t)dim; ++d) { + raise::ErrorIf( + flds_bc_enum[d].size() != 2, + fmt::format("invalid inferred `grid.boundaries.fields[%d]`", d), + HERE); + raise::ErrorIf( + prtl_bc_enum[d].size() != 2, + fmt::format("invalid inferred `grid.boundaries.particles[%d]`", d), + HERE); + flds_bc_pairwise.push_back({ flds_bc_enum[d][0], flds_bc_enum[d][1] }); + prtl_bc_pairwise.push_back({ prtl_bc_enum[d][0], prtl_bc_enum[d][1] }); + } + return { flds_bc_pairwise, prtl_bc_pairwise }; + } + + } // namespace params +} // namespace ntt diff --git a/src/framework/parameters/grid.h b/src/framework/parameters/grid.h new file mode 100644 index 00000000..fd01dfd5 --- /dev/null +++ b/src/framework/parameters/grid.h @@ -0,0 +1,34 @@ +#ifndef FRAMEWORK_PARAMETERS_GRID_H +#define FRAMEWORK_PARAMETERS_GRID_H + +#include "enums.h" +#include "global.h" + +#include "utils/toml.h" + +#include "framework/parameters/parameters.h" + +#include +#include +#include + +namespace ntt { + namespace params { + + auto GetGridParams( + const toml::value&) -> std::tuple, Dimension>; + + auto GetMetricParams(const SimEngine&, Dimension, const toml::value&) + -> std::tuple>; + + auto GetBoundaryConditions( + SimulationParams* params, + const SimEngine&, + Dimension, + const Coord&, + const toml::value&) -> std::tuple, boundaries_t>; + + } // namespace params +} // namespace ntt + +#endif // FRAMEWORK_PARAMETERS_GRID_H diff --git a/src/framework/parameters.cpp b/src/framework/parameters/parameters.cpp similarity index 67% rename from src/framework/parameters.cpp rename to src/framework/parameters/parameters.cpp index 44af6407..50a21651 100644 --- a/src/framework/parameters.cpp +++ b/src/framework/parameters/parameters.cpp @@ -1,4 +1,4 @@ -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "defaults.h" #include "enums.h" @@ -18,6 +18,8 @@ #include "metrics/spherical.h" #include "framework/containers/species.h" +#include "framework/parameters/grid.h" +#include "framework/parameters/particles.h" #if defined(MPI_ENABLED) #include @@ -47,34 +49,6 @@ namespace ntt { return { dx0, V0 }; } - /* - * Auxiliary functions - */ - auto getRadiativeDragFlags( - const std::string& radiative_drag_str) -> RadiativeDragFlags { - if (fmt::toLower(radiative_drag_str) == "none") { - return RadiativeDrag::NONE; - } else { - // separate comas - RadiativeDragFlags flags = RadiativeDrag::NONE; - std::string token; - std::istringstream tokenStream(radiative_drag_str); - while (std::getline(tokenStream, token, ',')) { - const auto token_lower = fmt::toLower(token); - if (token_lower == "synchrotron") { - flags |= RadiativeDrag::SYNCHROTRON; - } else if (token_lower == "compton") { - flags |= RadiativeDrag::COMPTON; - } else { - raise::Error( - fmt::format("Invalid radiative_drag value: %s", radiative_drag_str), - HERE); - } - } - return flags; - } - } - /* * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * Parameters that must not be changed during the checkpoint restart @@ -108,14 +82,8 @@ namespace ntt { promiseToDefine("simulation.domain.decomposition"); /* [grid] --------------------------------------------------------------- */ - const auto res = toml::find>(toml_data, - "grid", - "resolution"); - raise::ErrorIf(res.size() < 1 || res.size() > 3, - "invalid `grid.resolution`", - HERE); + const auto [res, dim] = params::GetGridParams(toml_data); set("grid.resolution", res); - const auto dim = static_cast(res.size()); set("grid.dim", dim); if (decomposition.size() > dim) { @@ -136,52 +104,13 @@ namespace ntt { promiseToDefine("grid.extent"); /* [grid.metric] -------------------------------------------------------- */ - const auto metric_enum = Metric::pick( - fmt::toLower(toml::find(toml_data, "grid", "metric", "metric")) - .c_str()); + const auto [metric_enum, coord_enum, additional_params] = + params::GetMetricParams(engine_enum, dim, toml_data); promiseToDefine("grid.metric.metric"); - std::string coord; - if (metric_enum == Metric::Minkowski) { - raise::ErrorIf(engine_enum != SimEngine::SRPIC, - "minkowski metric is only supported for SRPIC", - HERE); - coord = "cart"; - } else if (metric_enum == Metric::QKerr_Schild or - metric_enum == Metric::QSpherical) { - // quasi-spherical geometry - raise::ErrorIf(dim == Dim::_1D, - "not enough dimensions for qspherical geometry", - HERE); - raise::ErrorIf(dim == Dim::_3D, - "3D not implemented for qspherical geometry", - HERE); - coord = "qsph"; - set("grid.metric.qsph_r0", - toml::find_or(toml_data, "grid", "metric", "qsph_r0", defaults::qsph::r0)); - set("grid.metric.qsph_h", - toml::find_or(toml_data, "grid", "metric", "qsph_h", defaults::qsph::h)); - } else { - // spherical geometry - raise::ErrorIf(dim == Dim::_1D, - "not enough dimensions for spherical geometry", - HERE); - raise::ErrorIf(dim == Dim::_3D, - "3D not implemented for spherical geometry", - HERE); - coord = "sph"; - } - if ((engine_enum == SimEngine::GRPIC) && - (metric_enum != Metric::Kerr_Schild_0)) { - const auto ks_a = toml::find_or(toml_data, - "grid", - "metric", - "ks_a", - defaults::ks::a); - set("grid.metric.ks_a", ks_a); - set("grid.metric.ks_rh", ONE + math::sqrt(ONE - SQR(ks_a))); - } - const auto coord_enum = Coord::pick(coord.c_str()); set("grid.metric.coord", coord_enum); + for (const auto& [key, value] : additional_params) { + set("grid.metric." + key, value); + } /* [scales] ------------------------------------------------------------- */ const auto larmor0 = toml::find(toml_data, "scales", "larmor0"); @@ -216,86 +145,7 @@ namespace ntt { spidx_t idx = 1; for (const auto& sp : species_tab) { - const auto label = toml::find_or(sp, - "label", - "s" + std::to_string(idx)); - const auto mass = toml::find(sp, "mass"); - const auto charge = toml::find(sp, "charge"); - raise::ErrorIf((charge != 0.0f) && (mass == 0.0f), - "mass of the charged species must be non-zero", - HERE); - const auto is_massless = (mass == 0.0f) && (charge == 0.0f); - const auto def_pusher = (is_massless ? defaults::ph_pusher - : defaults::em_pusher); - const auto maxnpart_real = toml::find(sp, "maxnpart"); - const auto maxnpart = static_cast(maxnpart_real); - auto pusher = toml::find_or(sp, "pusher", std::string(def_pusher)); - const auto npayloads_real = toml::find_or(sp, - "n_payloads_real", - static_cast(0)); - const auto use_tracking = toml::find_or(sp, "tracking", false); - auto npayloads_int = toml::find_or(sp, - "n_payloads_int", - static_cast(0)); - if (use_tracking) { -#if !defined(MPI_ENABLED) - npayloads_int += 1; -#else - npayloads_int += 2; -#endif - } - const auto radiative_drag_str = toml::find_or(sp, - "radiative_drag", - std::string("none")); - raise::ErrorIf((fmt::toLower(radiative_drag_str) != "none") && is_massless, - "radiative drag is only applicable to massive particles", - HERE); - raise::ErrorIf((fmt::toLower(pusher) == "photon") && !is_massless, - "photon pusher is only applicable to massless particles", - HERE); - bool use_gca = false; - if (pusher.find(',') != std::string::npos) { - raise::ErrorIf(fmt::toLower(pusher.substr(pusher.find(',') + 1, - pusher.size())) != "gca", - "invalid pusher syntax", - HERE); - use_gca = true; - pusher = pusher.substr(0, pusher.find(',')); - } - const auto pusher_enum = PrtlPusher::pick(pusher.c_str()); - const auto radiative_drag_flags = getRadiativeDragFlags(radiative_drag_str); - if (radiative_drag_flags & RadiativeDrag::SYNCHROTRON) { - raise::ErrorIf(engine_enum != SimEngine::SRPIC, - "Synchrotron radiative drag is only supported for SRPIC", - HERE); - promiseToDefine("algorithms.synchrotron.gamma_rad"); - } - if (radiative_drag_flags & RadiativeDrag::COMPTON) { - raise::ErrorIf( - engine_enum != SimEngine::SRPIC, - "Inverse Compton radiative drag is only supported for SRPIC", - HERE); - promiseToDefine("algorithms.compton.gamma_rad"); - } - if (use_gca) { - raise::ErrorIf(engine_enum != SimEngine::SRPIC, - "GCA pushers are only supported for SRPIC", - HERE); - promiseToDefine("algorithms.gca.e_ovr_b_max"); - promiseToDefine("algorithms.gca.larmor_max"); - } - - species.emplace_back(ParticleSpecies(idx, - label, - mass, - charge, - maxnpart, - pusher_enum, - use_tracking, - use_gca, - radiative_drag_flags, - npayloads_real, - npayloads_int)); + species.emplace_back(params::GetParticleSpecies(this, engine_enum, idx, sp)); idx += 1; } set("particles.species", species); @@ -390,69 +240,13 @@ namespace ntt { toml::find(toml_data, "simulation", "runtime")); /* [grid.boundaraies] --------------------------------------------------- */ - auto flds_bc = toml::find>>( - toml_data, - "grid", - "boundaries", - "fields"); - { - raise::ErrorIf(flds_bc.size() < 1 || flds_bc.size() > 3, - "invalid `grid.boundaries.fields`", - HERE); - promiseToDefine("grid.boundaries.fields"); - auto atm_defined = false; - for (const auto& bcs : flds_bc) { - for (const auto& bc : bcs) { - if (fmt::toLower(bc) == "match") { - promiseToDefine("grid.boundaries.match.ds"); - } - if (fmt::toLower(bc) == "atmosphere") { - raise::ErrorIf(atm_defined, - "ATMOSPHERE is only allowed in one direction", - HERE); - atm_defined = true; - promiseToDefine("grid.boundaries.atmosphere.temperature"); - promiseToDefine("grid.boundaries.atmosphere.density"); - promiseToDefine("grid.boundaries.atmosphere.height"); - promiseToDefine("grid.boundaries.atmosphere.ds"); - promiseToDefine("grid.boundaries.atmosphere.species"); - promiseToDefine("grid.boundaries.atmosphere.g"); - } - } - } - } - - auto prtl_bc = toml::find>>( - toml_data, - "grid", - "boundaries", - "particles"); - { - raise::ErrorIf(prtl_bc.size() < 1 || prtl_bc.size() > 3, - "invalid `grid.boundaries.particles`", - HERE); - promiseToDefine("grid.boundaries.particles"); - auto atm_defined = false; - for (const auto& bcs : prtl_bc) { - for (const auto& bc : bcs) { - if (fmt::toLower(bc) == "absorb") { - promiseToDefine("grid.boundaries.absorb.ds"); - } - if (fmt::toLower(bc) == "atmosphere") { - raise::ErrorIf(atm_defined, - "ATMOSPHERE is only allowed in one direction", - HERE); - atm_defined = true; - promiseToDefine("grid.boundaries.atmosphere.temperature"); - promiseToDefine("grid.boundaries.atmosphere.density"); - promiseToDefine("grid.boundaries.atmosphere.height"); - promiseToDefine("grid.boundaries.atmosphere.ds"); - promiseToDefine("grid.boundaries.atmosphere.species"); - promiseToDefine("grid.boundaries.atmosphere.g"); - } - } - } - } + const auto [flds_bc, prtl_bc] = params::GetBoundaryConditions(this, + engine_enum, + dim, + coord_enum, + toml_data); + set("grid.boundaries.fields", flds_bc); + set("grid.boundaries.particles", prtl_bc); /* [algorithms] --------------------------------------------------------- */ set("algorithms.current_filters", @@ -588,7 +382,6 @@ namespace ntt { set("particles.species", new_species); /* [output] ------------------------------------------------------------- */ - // fields set("output.format", toml::find_or(toml_data, "output", "format", defaults::output::format)); set("output.interval", @@ -799,132 +592,6 @@ namespace ntt { toml::find_or(toml_data, "diagnostics", "log_level", defaults::diag::log_level)); /* inferred variables --------------------------------------------------- */ - // fields/particle boundaries - std::vector> flds_bc_enum; - std::vector> prtl_bc_enum; - if (coord_enum == Coord::Cart) { - raise::ErrorIf(flds_bc.size() != (std::size_t)dim, - "invalid `grid.boundaries.fields`", - HERE); - raise::ErrorIf(prtl_bc.size() != (std::size_t)dim, - "invalid `grid.boundaries.particles`", - HERE); - for (auto d { 0u }; d < (dim_t)dim; ++d) { - flds_bc_enum.push_back({}); - prtl_bc_enum.push_back({}); - const auto fbc = flds_bc[d]; - const auto pbc = prtl_bc[d]; - raise::ErrorIf(fbc.size() < 1 || fbc.size() > 2, - "invalid `grid.boundaries.fields`", - HERE); - raise::ErrorIf(pbc.size() < 1 || pbc.size() > 2, - "invalid `grid.boundaries.particles`", - HERE); - auto fbc_enum = FldsBC::pick(fmt::toLower(fbc[0]).c_str()); - auto pbc_enum = PrtlBC::pick(fmt::toLower(pbc[0]).c_str()); - if (fbc.size() == 1) { - raise::ErrorIf(fbc_enum != FldsBC::PERIODIC, - "invalid `grid.boundaries.fields`", - HERE); - flds_bc_enum.back().push_back(FldsBC(FldsBC::PERIODIC)); - flds_bc_enum.back().push_back(FldsBC(FldsBC::PERIODIC)); - } else { - raise::ErrorIf(fbc_enum == FldsBC::PERIODIC, - "invalid `grid.boundaries.fields`", - HERE); - flds_bc_enum.back().push_back(fbc_enum); - auto fbc_enum = FldsBC::pick(fmt::toLower(fbc[1]).c_str()); - raise::ErrorIf(fbc_enum == FldsBC::PERIODIC, - "invalid `grid.boundaries.fields`", - HERE); - flds_bc_enum.back().push_back(fbc_enum); - } - if (pbc.size() == 1) { - raise::ErrorIf(pbc_enum != PrtlBC::PERIODIC, - "invalid `grid.boundaries.particles`", - HERE); - prtl_bc_enum.back().push_back(PrtlBC(PrtlBC::PERIODIC)); - prtl_bc_enum.back().push_back(PrtlBC(PrtlBC::PERIODIC)); - } else { - raise::ErrorIf(pbc_enum == PrtlBC::PERIODIC, - "invalid `grid.boundaries.particles`", - HERE); - prtl_bc_enum.back().push_back(pbc_enum); - auto pbc_enum = PrtlBC::pick(fmt::toLower(pbc[1]).c_str()); - raise::ErrorIf(pbc_enum == PrtlBC::PERIODIC, - "invalid `grid.boundaries.particles`", - HERE); - prtl_bc_enum.back().push_back(pbc_enum); - } - } - } else { - raise::ErrorIf(flds_bc.size() > 1, "invalid `grid.boundaries.fields`", HERE); - raise::ErrorIf(prtl_bc.size() > 1, "invalid `grid.boundaries.particles`", HERE); - if (engine_enum == SimEngine::SRPIC) { - raise::ErrorIf(flds_bc[0].size() != 2, - "invalid `grid.boundaries.fields`", - HERE); - flds_bc_enum.push_back( - { FldsBC::pick(fmt::toLower(flds_bc[0][0]).c_str()), - FldsBC::pick(fmt::toLower(flds_bc[0][1]).c_str()) }); - flds_bc_enum.push_back({ FldsBC::AXIS, FldsBC::AXIS }); - if (dim == Dim::_3D) { - flds_bc_enum.push_back({ FldsBC::PERIODIC, FldsBC::PERIODIC }); - } - raise::ErrorIf(prtl_bc[0].size() != 2, - "invalid `grid.boundaries.particles`", - HERE); - prtl_bc_enum.push_back( - { PrtlBC::pick(fmt::toLower(prtl_bc[0][0]).c_str()), - PrtlBC::pick(fmt::toLower(prtl_bc[0][1]).c_str()) }); - prtl_bc_enum.push_back({ PrtlBC::AXIS, PrtlBC::AXIS }); - if (dim == Dim::_3D) { - prtl_bc_enum.push_back({ PrtlBC::PERIODIC, PrtlBC::PERIODIC }); - } - } else { - raise::ErrorIf(flds_bc[0].size() != 1, - "invalid `grid.boundaries.fields`", - HERE); - raise::ErrorIf(prtl_bc[0].size() != 1, - "invalid `grid.boundaries.particles`", - HERE); - flds_bc_enum.push_back( - { FldsBC::HORIZON, FldsBC::pick(fmt::toLower(flds_bc[0][0]).c_str()) }); - flds_bc_enum.push_back({ FldsBC::AXIS, FldsBC::AXIS }); - if (dim == Dim::_3D) { - flds_bc_enum.push_back({ FldsBC::PERIODIC, FldsBC::PERIODIC }); - } - prtl_bc_enum.push_back( - { PrtlBC::HORIZON, PrtlBC::pick(fmt::toLower(prtl_bc[0][0]).c_str()) }); - prtl_bc_enum.push_back({ PrtlBC::AXIS, PrtlBC::AXIS }); - if (dim == Dim::_3D) { - prtl_bc_enum.push_back({ PrtlBC::PERIODIC, PrtlBC::PERIODIC }); - } - } - } - - raise::ErrorIf(flds_bc_enum.size() != (std::size_t)dim, - "invalid inferred `grid.boundaries.fields`", - HERE); - raise::ErrorIf(prtl_bc_enum.size() != (std::size_t)dim, - "invalid inferred `grid.boundaries.particles`", - HERE); - boundaries_t flds_bc_pairwise; - boundaries_t prtl_bc_pairwise; - for (auto d { 0u }; d < (dim_t)dim; ++d) { - raise::ErrorIf( - flds_bc_enum[d].size() != 2, - fmt::format("invalid inferred `grid.boundaries.fields[%d]`", d), - HERE); - raise::ErrorIf( - prtl_bc_enum[d].size() != 2, - fmt::format("invalid inferred `grid.boundaries.particles[%d]`", d), - HERE); - flds_bc_pairwise.push_back({ flds_bc_enum[d][0], flds_bc_enum[d][1] }); - prtl_bc_pairwise.push_back({ prtl_bc_enum[d][0], prtl_bc_enum[d][1] }); - } - set("grid.boundaries.fields", flds_bc_pairwise); - set("grid.boundaries.particles", prtl_bc_pairwise); if (isPromised("grid.boundaries.match.ds")) { if (coord_enum == Coord::Cart) { diff --git a/src/framework/parameters.h b/src/framework/parameters/parameters.h similarity index 89% rename from src/framework/parameters.h rename to src/framework/parameters/parameters.h index 0af4fa40..e2a1d75e 100644 --- a/src/framework/parameters.h +++ b/src/framework/parameters/parameters.h @@ -1,5 +1,5 @@ /** - * @file framework/parameters.h + * @file framework/parameters/parameters.h * @brief Structure for defining and holding initial simulation parameters * @implements * - ntt::SimulationParams : ntt::Parameters @@ -14,8 +14,8 @@ * @note A proper metric is used to infer the minimum cell size/volume etc. */ -#ifndef FRAMEWORK_PARAMETERS_H -#define FRAMEWORK_PARAMETERS_H +#ifndef FRAMEWORK_PARAMETERS_PARAMETERS_H +#define FRAMEWORK_PARAMETERS_PARAMETERS_H #include "utils/param_container.h" #include "utils/toml.h" @@ -62,4 +62,4 @@ namespace ntt { } // namespace ntt -#endif // FRAMEWORK_PARAMETERS_H +#endif // FRAMEWORK_PARAMETERS_PARAMETERS_H diff --git a/src/framework/parameters/particles.cpp b/src/framework/parameters/particles.cpp new file mode 100644 index 00000000..1f76d282 --- /dev/null +++ b/src/framework/parameters/particles.cpp @@ -0,0 +1,135 @@ +#include "framework/parameters/particles.h" + +#include "defaults.h" +#include "enums.h" +#include "global.h" + +#include "utils/error.h" +#include "utils/formatting.h" +#include "utils/toml.h" + +#include "framework/containers/species.h" +#include "framework/parameters/parameters.h" + +#include + +namespace ntt { + + namespace params { + + /* + * Auxiliary functions + */ + auto getRadiativeDragFlags( + const std::string& radiative_drag_str) -> RadiativeDragFlags { + if (fmt::toLower(radiative_drag_str) == "none") { + return RadiativeDrag::NONE; + } else { + // separate comas + RadiativeDragFlags flags = RadiativeDrag::NONE; + std::string token; + std::istringstream tokenStream(radiative_drag_str); + while (std::getline(tokenStream, token, ',')) { + const auto token_lower = fmt::toLower(token); + if (token_lower == "synchrotron") { + flags |= RadiativeDrag::SYNCHROTRON; + } else if (token_lower == "compton") { + flags |= RadiativeDrag::COMPTON; + } else { + raise::Error(fmt::format("Invalid radiative_drag value: %s", + radiative_drag_str), + HERE); + } + } + return flags; + } + } + + auto GetParticleSpecies(SimulationParams* params, + const SimEngine& engine_enum, + spidx_t idx, + const toml::value& sp) -> ParticleSpecies { + const auto label = toml::find_or(sp, + "label", + "s" + std::to_string(idx)); + const auto mass = toml::find(sp, "mass"); + const auto charge = toml::find(sp, "charge"); + raise::ErrorIf((charge != 0.0f) && (mass == 0.0f), + "mass of the charged species must be non-zero", + HERE); + const auto is_massless = (mass == 0.0f) && (charge == 0.0f); + const auto def_pusher = (is_massless ? defaults::ph_pusher + : defaults::em_pusher); + const auto maxnpart_real = toml::find(sp, "maxnpart"); + const auto maxnpart = static_cast(maxnpart_real); + auto pusher = toml::find_or(sp, "pusher", std::string(def_pusher)); + const auto npayloads_real = toml::find_or(sp, + "n_payloads_real", + static_cast(0)); + const auto use_tracking = toml::find_or(sp, "tracking", false); + auto npayloads_int = toml::find_or(sp, + "n_payloads_int", + static_cast(0)); + if (use_tracking) { +#if !defined(MPI_ENABLED) + npayloads_int += 1; +#else + npayloads_int += 2; +#endif + } + const auto radiative_drag_str = toml::find_or(sp, + "radiative_drag", + std::string("none")); + raise::ErrorIf((fmt::toLower(radiative_drag_str) != "none") && is_massless, + "radiative drag is only applicable to massive particles", + HERE); + raise::ErrorIf((fmt::toLower(pusher) == "photon") && !is_massless, + "photon pusher is only applicable to massless particles", + HERE); + bool use_gca = false; + if (pusher.find(',') != std::string::npos) { + raise::ErrorIf(fmt::toLower(pusher.substr(pusher.find(',') + 1, + pusher.size())) != "gca", + "invalid pusher syntax", + HERE); + use_gca = true; + pusher = pusher.substr(0, pusher.find(',')); + } + const auto pusher_enum = PrtlPusher::pick(pusher.c_str()); + const auto radiative_drag_flags = getRadiativeDragFlags(radiative_drag_str); + if (radiative_drag_flags & RadiativeDrag::SYNCHROTRON) { + raise::ErrorIf(engine_enum != SimEngine::SRPIC, + "Synchrotron radiative drag is only supported for SRPIC", + HERE); + params->promiseToDefine("algorithms.synchrotron.gamma_rad"); + } + if (radiative_drag_flags & RadiativeDrag::COMPTON) { + raise::ErrorIf( + engine_enum != SimEngine::SRPIC, + "Inverse Compton radiative drag is only supported for SRPIC", + HERE); + params->promiseToDefine("algorithms.compton.gamma_rad"); + } + if (use_gca) { + raise::ErrorIf(engine_enum != SimEngine::SRPIC, + "GCA pushers are only supported for SRPIC", + HERE); + params->promiseToDefine("algorithms.gca.e_ovr_b_max"); + params->promiseToDefine("algorithms.gca.larmor_max"); + } + return ParticleSpecies(idx, + label, + mass, + charge, + maxnpart, + pusher_enum, + use_tracking, + use_gca, + radiative_drag_flags, + npayloads_real, + npayloads_int); + } + + } // namespace params + +} // namespace ntt diff --git a/src/framework/parameters/particles.h b/src/framework/parameters/particles.h new file mode 100644 index 00000000..f5b76319 --- /dev/null +++ b/src/framework/parameters/particles.h @@ -0,0 +1,24 @@ +#ifndef FRAMEWORK_PARAMETERS_PARTICLES_H +#define FRAMEWORK_PARAMETERS_PARTICLES_H + +#include "enums.h" + +#include "utils/toml.h" + +#include "framework/containers/species.h" +#include "framework/parameters/parameters.h" + +namespace ntt { + + namespace params { + + auto GetParticleSpecies(SimulationParams*, + const SimEngine&, + spidx_t, + const toml::value&) -> ParticleSpecies; + + } // namespace params + +} // namespace ntt + +#endif diff --git a/src/framework/simulation.h b/src/framework/simulation.h index d8696532..f24c82b9 100644 --- a/src/framework/simulation.h +++ b/src/framework/simulation.h @@ -20,7 +20,7 @@ #include "utils/error.h" #include "utils/toml.h" -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" namespace ntt { diff --git a/src/framework/tests/parameters.cpp b/src/framework/tests/parameters.cpp index 9c597212..614bf505 100644 --- a/src/framework/tests/parameters.cpp +++ b/src/framework/tests/parameters.cpp @@ -1,4 +1,4 @@ -#include "framework/parameters.h" +#include "framework/parameters/parameters.h" #include "defaults.h" #include "enums.h" From e18b72fa50d74adda7c801587e9d2c5774f7f0c9 Mon Sep 17 00:00:00 2001 From: haykh Date: Thu, 8 Jan 2026 17:48:50 -0500 Subject: [PATCH 03/21] refactored params even further (CPUTEST) --- src/framework/parameters/grid.cpp | 126 +++++++++++ src/framework/parameters/grid.h | 27 +++ src/framework/parameters/output.cpp | 188 ++++++++++++++++ src/framework/parameters/output.h | 54 +++++ src/framework/parameters/parameters.cpp | 274 ++---------------------- 5 files changed, 407 insertions(+), 262 deletions(-) create mode 100644 src/framework/parameters/output.cpp create mode 100644 src/framework/parameters/output.h diff --git a/src/framework/parameters/grid.cpp b/src/framework/parameters/grid.cpp index 9a0163b9..1d58e495 100644 --- a/src/framework/parameters/grid.cpp +++ b/src/framework/parameters/grid.cpp @@ -285,5 +285,131 @@ namespace ntt { return { flds_bc_pairwise, prtl_bc_pairwise }; } + void Boundaries::read(Dimension dim, + const Coord& coord_enum, + const boundaries_t& extent_pairwise, + const toml::value& toml_data) { + if (needs_match_boundaries) { + if (coord_enum == Coord::Cart) { + auto min_extent = std::numeric_limits::max(); + for (const auto& e : extent_pairwise) { + min_extent = std::min(min_extent, e.second - e.first); + } + const auto default_ds = min_extent * defaults::bc::match::ds_frac; + try { + auto ds = toml::find(toml_data, "grid", "boundaries", "match", "ds"); + for (auto d = 0u; d < dim; ++d) { + match_ds_array.push_back({ ds, ds }); + } + } catch (...) { + try { + const auto ds = toml::find>>( + toml_data, + "grid", + "boundaries", + "match", + "ds"); + raise::ErrorIf(ds.size() != dim, + "invalid # in `grid.boundaries.match.ds`", + HERE); + for (auto d = 0u; d < dim; ++d) { + if (ds[d].size() == 1) { + match_ds_array.push_back({ ds[d][0], ds[d][0] }); + } else if (ds[d].size() == 2) { + match_ds_array.push_back({ ds[d][0], ds[d][1] }); + } else if (ds[d].size() == 0) { + match_ds_array.push_back({}); + } else { + raise::Error("invalid `grid.boundaries.match.ds`", HERE); + } + } + } catch (...) { + for (auto d = 0u; d < dim; ++d) { + match_ds_array.push_back({ default_ds, default_ds }); + } + } + } + } else { + auto r_extent = extent_pairwise[0].second - extent_pairwise[0].first; + const auto ds = toml::find_or( + toml_data, + "grid", + "boundaries", + "match", + "ds", + r_extent * defaults::bc::match::ds_frac); + match_ds_array.push_back({ ds, ds }); + } + } + + if (needs_absorb_boundaries) { + if (coord_enum == Coord::Cart) { + auto min_extent = std::numeric_limits::max(); + for (const auto& e : extent_pairwise) { + min_extent = std::min(min_extent, e.second - e.first); + } + absorb_ds = toml::find_or(toml_data, + "grid", + "boundaries", + "absorb", + "ds", + min_extent * defaults::bc::absorb::ds_frac); + } else { + auto r_extent = extent_pairwise[0].second - extent_pairwise[0].first; + absorb_ds = toml::find_or(toml_data, + "grid", + "boundaries", + "absorb", + "ds", + r_extent * defaults::bc::absorb::ds_frac); + } + } + + if (needs_atmosphere_boundaries) { + atmosphere_temperature = toml::find(toml_data, + "grid", + "boundaries", + "atmosphere", + "temperature"); + atmosphere_height = toml::find(toml_data, + "grid", + "boundaries", + "atmosphere", + "height"); + atmosphere_density = toml::find(toml_data, + "grid", + "boundaries", + "atmosphere", + "density"); + atmosphere_ds = + toml::find_or(toml_data, "grid", "boundaries", "atmosphere", "ds", ZERO); + atmosphere_g = atmosphere_temperature / atmosphere_height; + atmosphere_species = toml::find>( + toml_data, + "grid", + "boundaries", + "atmosphere", + "species"); + } + } + + void Boundaries::setParams(SimulationParams* params) const { + if (needs_match_boundaries) { + params->set("grid.boundaries.match.ds", match_ds_array); + } + if (needs_absorb_boundaries) { + params->set("grid.boundaries.absorb.ds", absorb_ds); + } + if (needs_atmosphere_boundaries) { + params->set("grid.boundaries.atmosphere.temperature", + atmosphere_temperature); + params->set("grid.boundaries.atmosphere.density", atmosphere_density); + params->set("grid.boundaries.atmosphere.height", atmosphere_height); + params->set("grid.boundaries.atmosphere.ds", atmosphere_ds); + params->set("grid.boundaries.atmosphere.g", atmosphere_g); + params->set("grid.boundaries.atmosphere.species", atmosphere_species); + } + } + } // namespace params } // namespace ntt diff --git a/src/framework/parameters/grid.h b/src/framework/parameters/grid.h index fd01dfd5..fdcc5047 100644 --- a/src/framework/parameters/grid.h +++ b/src/framework/parameters/grid.h @@ -15,6 +15,33 @@ namespace ntt { namespace params { + struct Boundaries { + const bool needs_match_boundaries; + boundaries_t match_ds_array; + + const bool needs_absorb_boundaries; + real_t absorb_ds; + + const bool needs_atmosphere_boundaries; + real_t atmosphere_temperature; + real_t atmosphere_height; + real_t atmosphere_density; + real_t atmosphere_g; + real_t atmosphere_ds; + std::pair atmosphere_species; + + Boundaries(bool needs_match, bool needs_absorb, bool needs_atmosphere) + : needs_match_boundaries { needs_match } + , needs_absorb_boundaries { needs_absorb } + , needs_atmosphere_boundaries { needs_atmosphere } {} + + void read(Dimension, + const Coord&, + const boundaries_t&, + const toml::value&); + void setParams(SimulationParams*) const; + }; + auto GetGridParams( const toml::value&) -> std::tuple, Dimension>; diff --git a/src/framework/parameters/output.cpp b/src/framework/parameters/output.cpp new file mode 100644 index 00000000..f6c15b81 --- /dev/null +++ b/src/framework/parameters/output.cpp @@ -0,0 +1,188 @@ +#include "framework/parameters/output.h" + +#include "defaults.h" +#include "global.h" + +#include "utils/error.h" +#include "utils/log.h" +#include "utils/toml.h" + +namespace ntt { + namespace params { + + void Output::read(Dimension dim, std::size_t nspec, const toml::value& toml_data) { + format = toml::find_or(toml_data, "output", "format", defaults::output::format); + global_interval = toml::find_or(toml_data, + "output", + "interval", + defaults::output::interval); + global_interval_time = toml::find_or(toml_data, + "output", + "interval_time", + -1.0); + raise::ErrorIf( + not toml::find_or(toml_data, "output", "separate_files", true), + "separate_files=false is deprecated", + HERE); + + for (const auto& category : { "fields", "particles", "spectra", "stats" }) { + const auto q_int = toml::find_or(toml_data, + "output", + category, + "interval", + 0); + const auto q_int_time = toml::find_or(toml_data, + "output", + category, + "interval_time", + -1.0); + categories[category].enable = toml::find_or(toml_data, + "output", + category, + "enable", + true); + if ((q_int == 0) and (q_int_time == -1.0)) { + categories[category].interval = global_interval; + categories[category].interval_time = global_interval_time; + } else { + categories[category].interval = q_int; + categories[category].interval_time = q_int_time; + } + } + + /* Fields --------------------------------------------------------------- */ + const auto flds_out = toml::find_or(toml_data, + "output", + "fields", + "quantities", + std::vector {}); + const auto custom_flds_out = toml::find_or(toml_data, + "output", + "fields", + "custom", + std::vector {}); + if (flds_out.size() == 0) { + raise::Warning("No fields output specified", HERE); + } + fields_quantities = flds_out; + fields_custom_quantities = custom_flds_out; + fields_mom_smooth = toml::find_or(toml_data, + "output", + "fields", + "mom_smooth", + defaults::output::mom_smooth); + try { + auto field_dwn_ = toml::find>(toml_data, + "output", + "fields", + "downsampling"); + for (auto i = 0u; i < field_dwn_.size(); ++i) { + fields_downsampling.push_back(field_dwn_[i]); + } + } catch (...) { + try { + auto field_dwn_ = toml::find(toml_data, + "output", + "fields", + "downsampling"); + for (auto i = 0u; i < dim; ++i) { + fields_downsampling.push_back(field_dwn_); + } + } catch (...) { + for (auto i = 0u; i < dim; ++i) { + fields_downsampling.push_back(1u); + } + } + } + raise::ErrorIf(fields_downsampling.size() > 3, + "invalid `output.fields.downsampling`", + HERE); + if (fields_downsampling.size() > dim) { + fields_downsampling.erase(fields_downsampling.begin() + (std::size_t)(dim), + fields_downsampling.end()); + } + for (const auto& dwn : fields_downsampling) { + raise::ErrorIf(dwn == 0, "downsampling factor must be nonzero", HERE); + } + + /* Particles ------------------------------------------------------------ */ + auto all_specs = std::vector {}; + for (auto i = 0u; i < nspec; ++i) { + all_specs.push_back(static_cast(i + 1)); + } + particles_species = toml::find_or(toml_data, + "output", + "particles", + "species", + all_specs); + particles_stride = toml::find_or(toml_data, + "output", + "particles", + "stride", + defaults::output::prtl_stride); + + /* Spectra -------------------------------------------------------------- */ + spectra_e_min = toml::find_or(toml_data, + "output", + "spectra", + "e_min", + defaults::output::spec_emin); + spectra_e_max = toml::find_or(toml_data, + "output", + "spectra", + "e_max", + defaults::output::spec_emax); + spectra_log_bins = toml::find_or(toml_data, + "output", + "spectra", + "log_bins", + defaults::output::spec_log); + spectra_n_bins = toml::find_or(toml_data, + "output", + "spectra", + "n_bins", + defaults::output::spec_nbins); + + /* Stats ---------------------------------------------------------------- */ + stats_quantities = toml::find_or(toml_data, + "output", + "stats", + "quantities", + defaults::output::stats_quantities); + stats_custom_quantities = toml::find_or(toml_data, + "output", + "stats", + "custom", + std::vector {}); + } + + void Output::setParams(SimulationParams* params) const { + params->set("output.format", format); + params->set("output.interval", global_interval); + params->set("output.interval_time", global_interval_time); + for (const auto& [category, cat_params] : categories) { + params->set("output." + category + ".enable", cat_params.enable); + params->set("output." + category + ".interval", cat_params.interval); + params->set("output." + category + ".interval_time", + cat_params.interval_time); + } + + params->set("output.fields.quantities", fields_quantities); + params->set("output.fields.custom", fields_custom_quantities); + params->set("output.fields.mom_smooth", fields_mom_smooth); + params->set("output.fields.downsampling", fields_downsampling); + + params->set("output.particles.species", particles_species); + params->set("output.particles.stride", particles_stride); + + params->set("output.spectra.e_min", spectra_e_min); + params->set("output.spectra.e_max", spectra_e_max); + params->set("output.spectra.log_bins", spectra_log_bins); + params->set("output.spectra.n_bins", spectra_n_bins); + + params->set("output.stats.quantities", stats_quantities); + params->set("output.stats.custom", stats_custom_quantities); + } + + } // namespace params +} // namespace ntt diff --git a/src/framework/parameters/output.h b/src/framework/parameters/output.h new file mode 100644 index 00000000..68e34c2f --- /dev/null +++ b/src/framework/parameters/output.h @@ -0,0 +1,54 @@ +#ifndef FRAMEWORK_PARAMETERS_OUTPUT_H +#define FRAMEWORK_PARAMETERS_OUTPUT_H + +#include "global.h" + +#include "utils/toml.h" + +#include "framework/parameters/parameters.h" + +#include +#include +#include + +namespace ntt { + namespace params { + + struct OutputCategory { + bool enable; + timestep_t interval; + simtime_t interval_time; + }; + + struct Output { + std::string format; + + timestep_t global_interval; + simtime_t global_interval_time; + + std::map categories; + + std::vector fields_quantities; + std::vector fields_custom_quantities; + unsigned short fields_mom_smooth; + std::vector fields_downsampling; + + std::vector particles_species; + npart_t particles_stride; + + real_t spectra_e_min; + real_t spectra_e_max; + bool spectra_log_bins; + std::size_t spectra_n_bins; + + std::vector stats_quantities; + std::vector stats_custom_quantities; + + void read(Dimension, std::size_t, const toml::value&); + void setParams(SimulationParams*) const; + }; + + } // namespace params +} // namespace ntt + +#endif // FRAMEWORK_PARAMETERS_OUTPUT_H diff --git a/src/framework/parameters/parameters.cpp b/src/framework/parameters/parameters.cpp index 50a21651..4c5cdc40 100644 --- a/src/framework/parameters/parameters.cpp +++ b/src/framework/parameters/parameters.cpp @@ -6,7 +6,6 @@ #include "utils/error.h" #include "utils/formatting.h" -#include "utils/log.h" #include "utils/numeric.h" #include "utils/toml.h" @@ -19,6 +18,7 @@ #include "framework/containers/species.h" #include "framework/parameters/grid.h" +#include "framework/parameters/output.h" #include "framework/parameters/particles.h" #if defined(MPI_ENABLED) @@ -382,157 +382,10 @@ namespace ntt { set("particles.species", new_species); /* [output] ------------------------------------------------------------- */ - set("output.format", - toml::find_or(toml_data, "output", "format", defaults::output::format)); - set("output.interval", - toml::find_or(toml_data, "output", "interval", defaults::output::interval)); - set("output.interval_time", - toml::find_or(toml_data, "output", "interval_time", -1.0)); - set("output.separate_files", - toml::find_or(toml_data, "output", "separate_files", true)); - - promiseToDefine("output.fields.enable"); - promiseToDefine("output.fields.interval"); - promiseToDefine("output.fields.interval_time"); - promiseToDefine("output.particles.enable"); - promiseToDefine("output.particles.interval"); - promiseToDefine("output.particles.interval_time"); - promiseToDefine("output.spectra.enable"); - promiseToDefine("output.spectra.interval"); - promiseToDefine("output.spectra.interval_time"); - promiseToDefine("output.stats.enable"); - promiseToDefine("output.stats.interval"); - promiseToDefine("output.stats.interval_time"); - - const auto flds_out = toml::find_or(toml_data, - "output", - "fields", - "quantities", - std::vector {}); - const auto custom_flds_out = toml::find_or(toml_data, - "output", - "fields", - "custom", - std::vector {}); - if (flds_out.size() == 0) { - raise::Warning("No fields output specified", HERE); - } - set("output.fields.quantities", flds_out); - set("output.fields.custom", custom_flds_out); - set("output.fields.mom_smooth", - toml::find_or(toml_data, - "output", - "fields", - "mom_smooth", - defaults::output::mom_smooth)); - std::vector field_dwn; - try { - auto field_dwn_ = toml::find>(toml_data, - "output", - "fields", - "downsampling"); - for (auto i = 0u; i < field_dwn_.size(); ++i) { - field_dwn.push_back(field_dwn_[i]); - } - } catch (...) { - try { - auto field_dwn_ = toml::find(toml_data, - "output", - "fields", - "downsampling"); - for (auto i = 0u; i < dim; ++i) { - field_dwn.push_back(field_dwn_); - } - } catch (...) { - for (auto i = 0u; i < dim; ++i) { - field_dwn.push_back(1u); - } - } - } - raise::ErrorIf(field_dwn.size() > 3, "invalid `output.fields.downsampling`", HERE); - if (field_dwn.size() > dim) { - field_dwn.erase(field_dwn.begin() + (std::size_t)(dim), field_dwn.end()); - } - for (const auto& dwn : field_dwn) { - raise::ErrorIf(dwn == 0, "downsampling factor must be nonzero", HERE); - } - set("output.fields.downsampling", field_dwn); - // particles - auto all_specs = std::vector {}; - const auto nspec = get("particles.nspec"); - for (auto i = 0u; i < nspec; ++i) { - all_specs.push_back(static_cast(i + 1)); - } - const auto prtl_out = toml::find_or(toml_data, - "output", - "particles", - "species", - all_specs); - set("output.particles.species", prtl_out); - set("output.particles.stride", - toml::find_or(toml_data, - "output", - "particles", - "stride", - defaults::output::prtl_stride)); - - // spectra - set("output.spectra.e_min", - toml::find_or(toml_data, "output", "spectra", "e_min", defaults::output::spec_emin)); - set("output.spectra.e_max", - toml::find_or(toml_data, "output", "spectra", "e_max", defaults::output::spec_emax)); - set("output.spectra.log_bins", - toml::find_or(toml_data, - "output", - "spectra", - "log_bins", - defaults::output::spec_log)); - set("output.spectra.n_bins", - toml::find_or(toml_data, - "output", - "spectra", - "n_bins", - defaults::output::spec_nbins)); - - // stats - set("output.stats.quantities", - toml::find_or(toml_data, - "output", - "stats", - "quantities", - defaults::output::stats_quantities)); - set("output.stats.custom", - toml::find_or(toml_data, - "output", - "stats", - "custom", - std::vector {})); - - // intervals - for (const auto& type : { "fields", "particles", "spectra", "stats" }) { - const auto q_int = toml::find_or(toml_data, - "output", - std::string(type), - "interval", - 0); - const auto q_int_time = toml::find_or(toml_data, - "output", - std::string(type), - "interval_time", - -1.0); - set("output." + std::string(type) + ".enable", - toml::find_or(toml_data, "output", std::string(type), "enable", true)); - if ((q_int == 0) and (q_int_time == -1.0)) { - set("output." + std::string(type) + ".interval", - get("output.interval")); - set("output." + std::string(type) + ".interval_time", - get("output.interval_time")); - } else { - set("output." + std::string(type) + ".interval", q_int); - set("output." + std::string(type) + ".interval_time", q_int_time); - } - } + params::Output output_params; + output_params.read(dim, get("particles.nspec"), toml_data); + output_params.setParams(this); /* [output.debug] ------------------------------------------------------- */ set("output.debug.as_is", @@ -544,7 +397,7 @@ namespace ntt { false); set("output.debug.ghosts", output_ghosts); if (output_ghosts) { - for (const auto& dwn : field_dwn) { + for (const auto& dwn : output_params.fields_downsampling) { raise::ErrorIf( dwn != 1, "full resolution required when outputting with ghost cells", @@ -592,116 +445,13 @@ namespace ntt { toml::find_or(toml_data, "diagnostics", "log_level", defaults::diag::log_level)); /* inferred variables --------------------------------------------------- */ - - if (isPromised("grid.boundaries.match.ds")) { - if (coord_enum == Coord::Cart) { - auto min_extent = std::numeric_limits::max(); - for (const auto& e : extent_pairwise) { - min_extent = std::min(min_extent, e.second - e.first); - } - const auto default_ds = min_extent * defaults::bc::match::ds_frac; - boundaries_t ds_array; - try { - auto ds = toml::find(toml_data, "grid", "boundaries", "match", "ds"); - for (auto d = 0u; d < dim; ++d) { - ds_array.push_back({ ds, ds }); - } - } catch (...) { - try { - const auto ds = toml::find>>( - toml_data, - "grid", - "boundaries", - "match", - "ds"); - raise::ErrorIf(ds.size() != dim, - "invalid # in `grid.boundaries.match.ds`", - HERE); - for (auto d = 0u; d < dim; ++d) { - if (ds[d].size() == 1) { - ds_array.push_back({ ds[d][0], ds[d][0] }); - } else if (ds[d].size() == 2) { - ds_array.push_back({ ds[d][0], ds[d][1] }); - } else if (ds[d].size() == 0) { - ds_array.push_back({}); - } else { - raise::Error("invalid `grid.boundaries.match.ds`", HERE); - } - } - } catch (...) { - for (auto d = 0u; d < dim; ++d) { - ds_array.push_back({ default_ds, default_ds }); - } - } - } - set("grid.boundaries.match.ds", ds_array); - } else { - auto r_extent = extent_pairwise[0].second - extent_pairwise[0].first; - const auto ds = toml::find_or( - toml_data, - "grid", - "boundaries", - "match", - "ds", - r_extent * defaults::bc::match::ds_frac); - boundaries_t ds_array { - { ds, ds } - }; - set("grid.boundaries.match.ds", ds_array); - } - } - - if (isPromised("grid.boundaries.absorb.ds")) { - if (coord_enum == Coord::Cart) { - auto min_extent = std::numeric_limits::max(); - for (const auto& e : extent_pairwise) { - min_extent = std::min(min_extent, e.second - e.first); - } - set("grid.boundaries.absorb.ds", - toml::find_or(toml_data, - "grid", - "boundaries", - "absorb", - "ds", - min_extent * defaults::bc::absorb::ds_frac)); - } else { - auto r_extent = extent_pairwise[0].second - extent_pairwise[0].first; - set("grid.boundaries.absorb.ds", - toml::find_or(toml_data, - "grid", - "boundaries", - "absorb", - "ds", - r_extent * defaults::bc::absorb::ds_frac)); - } - } - - if (isPromised("grid.boundaries.atmosphere.temperature")) { - const auto atm_T = toml::find(toml_data, - "grid", - "boundaries", - "atmosphere", - "temperature"); - const auto atm_h = toml::find(toml_data, - "grid", - "boundaries", - "atmosphere", - "height"); - set("grid.boundaries.atmosphere.temperature", atm_T); - set("grid.boundaries.atmosphere.density", - toml::find(toml_data, "grid", "boundaries", "atmosphere", "density")); - set("grid.boundaries.atmosphere.ds", - toml::find_or(toml_data, "grid", "boundaries", "atmosphere", "ds", ZERO)); - set("grid.boundaries.atmosphere.height", atm_h); - set("grid.boundaries.atmosphere.g", atm_T / atm_h); - const auto atm_species = toml::find>( - toml_data, - "grid", - "boundaries", - "atmosphere", - "species"); - set("grid.boundaries.atmosphere.species", atm_species); - } + params::Boundaries boundaries_params { + isPromised("grid.boundaries.match.ds"), + isPromised("grid.boundaries.absorb.ds"), + isPromised("grid.boundaries.atmosphere.temperature") + }; + boundaries_params.read(dim, coord_enum, extent_pairwise, toml_data); + boundaries_params.setParams(this); // gca if (isPromised("algorithms.gca.e_ovr_b_max")) { From 1802bae1a63ebee3dd51217be76f3d33d6a4d0a5 Mon Sep 17 00:00:00 2001 From: haykh Date: Fri, 9 Jan 2026 11:38:01 -0500 Subject: [PATCH 04/21] fixed cmake for params (CPUTEST) --- src/framework/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index 8ce63d0a..8eedc576 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -40,6 +40,7 @@ set(SOURCES ${SRC_DIR}/parameters/parameters.cpp ${SRC_DIR}/parameters/particles.cpp ${SRC_DIR}/parameters/grid.cpp + ${SRC_DIR}/parameters/output.cpp ${SRC_DIR}/domain/grid.cpp ${SRC_DIR}/domain/metadomain.cpp ${SRC_DIR}/domain/communications.cpp From 05674405f09c39610950e40c5213c79ae915f09e Mon Sep 17 00:00:00 2001 From: haykh Date: Fri, 9 Jan 2026 11:41:36 -0500 Subject: [PATCH 05/21] added header comments --- src/framework/parameters/grid.h | 13 +++++++++++++ src/framework/parameters/output.h | 11 +++++++++++ src/framework/parameters/particles.h | 10 ++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/framework/parameters/grid.h b/src/framework/parameters/grid.h index fdcc5047..08d75e02 100644 --- a/src/framework/parameters/grid.h +++ b/src/framework/parameters/grid.h @@ -1,3 +1,16 @@ +/** + * @file framework/parameters/grid.h + * @brief Auxiliary functions for reading in grid/box parameters + * @implements + * - ntt::params::Boundaries + * - ntt::params::GetGridParams -> (std::vector, Dimension) + * - ntt::params::GetMetricParams -> (Metric, Coord, std::map) + * - ntt::params::GetBoundaryConditions -> (boundaries_t, boundaries_t) + * @cpp: + * - grid.cpp + * @namespaces: + * - ntt::params:: + */ #ifndef FRAMEWORK_PARAMETERS_GRID_H #define FRAMEWORK_PARAMETERS_GRID_H diff --git a/src/framework/parameters/output.h b/src/framework/parameters/output.h index 68e34c2f..6039418c 100644 --- a/src/framework/parameters/output.h +++ b/src/framework/parameters/output.h @@ -1,3 +1,14 @@ +/** + * @file framework/parameters/output.h + * @brief Auxiliary functions for reading in output parameters + * @implements + * - ntt::params::Output + * - ntt::params::OutputCategory + * @cpp: + * - output.cpp + * @namespaces: + * - ntt::params:: + */ #ifndef FRAMEWORK_PARAMETERS_OUTPUT_H #define FRAMEWORK_PARAMETERS_OUTPUT_H diff --git a/src/framework/parameters/particles.h b/src/framework/parameters/particles.h index f5b76319..6ceaebcd 100644 --- a/src/framework/parameters/particles.h +++ b/src/framework/parameters/particles.h @@ -1,3 +1,13 @@ +/** + * @file framework/parameters/particles.h + * @brief Auxiliary functions for reading in particle species parameters + * @implements + * - ntt::params::GetParticleSpecies -> ParticleSpecies + * @cpp: + * - particles.cpp + * @namespaces: + * - ntt::params:: + */ #ifndef FRAMEWORK_PARAMETERS_PARTICLES_H #define FRAMEWORK_PARAMETERS_PARTICLES_H From 9b519bba41b72255f0e09ab6f882af2692b2a98b Mon Sep 17 00:00:00 2001 From: haykh Date: Fri, 9 Jan 2026 12:34:37 -0500 Subject: [PATCH 06/21] further simplified params reading for grid (CPUTEST) --- input.example.toml | 10 +- src/engines/srpic.hpp | 1 - src/framework/parameters/grid.cpp | 270 ++++++++++++++++++------ src/framework/parameters/grid.h | 24 ++- src/framework/parameters/output.cpp | 15 ++ src/framework/parameters/output.h | 3 + src/framework/parameters/parameters.cpp | 178 +--------------- 7 files changed, 255 insertions(+), 246 deletions(-) diff --git a/input.example.toml b/input.example.toml index f14d60ed..3766a55f 100644 --- a/input.example.toml +++ b/input.example.toml @@ -224,10 +224,12 @@ # @type: bool # @default: true enable = "" - # Order of the particle shape function - # @type: int - # @default: 1 - order = "" + + # @inferred: + # - order + # @brief: order of the particle shape function + # @from: compile-time definition `shape_order` + # @type: ushort [0 -> 10] [algorithms.gr] # Stepsize for numerical differentiation in GR pusher diff --git a/src/engines/srpic.hpp b/src/engines/srpic.hpp index 55b41ab9..ef632149 100644 --- a/src/engines/srpic.hpp +++ b/src/engines/srpic.hpp @@ -540,7 +540,6 @@ namespace ntt { void CurrentsDeposit(domain_t& domain) { auto scatter_cur = Kokkos::Experimental::create_scatter_view( domain.fields.cur); - auto shape_order = m_params.template get("algorithms.deposit.order"); for (auto& species : domain.species) { if ((species.pusher() == PrtlPusher::NONE) or (species.npart() == 0) or cmp::AlmostZero_host(species.charge())) { diff --git a/src/framework/parameters/grid.cpp b/src/framework/parameters/grid.cpp index 1d58e495..705bb036 100644 --- a/src/framework/parameters/grid.cpp +++ b/src/framework/parameters/grid.cpp @@ -8,6 +8,13 @@ #include "utils/numeric.h" #include "utils/toml.h" +#include "metrics/kerr_schild.h" +#include "metrics/kerr_schild_0.h" +#include "metrics/minkowski.h" +#include "metrics/qkerr_schild.h" +#include "metrics/qspherical.h" +#include "metrics/spherical.h" + #include "framework/parameters/parameters.h" #include @@ -18,74 +25,19 @@ namespace ntt { namespace params { - auto GetGridParams( - const toml::value& toml_data) -> std::tuple, Dimension> { - const auto res = toml::find>(toml_data, - "grid", - "resolution"); - raise::ErrorIf(res.size() < 1 || res.size() > 3, - "invalid `grid.resolution`", - HERE); - const auto dim = static_cast(res.size()); - return { res, dim }; - } - - auto GetMetricParams(const SimEngine& engine_enum, - Dimension dim, - const toml::value& toml_data) - -> std::tuple> { - const auto metric_enum = Metric::pick( - fmt::toLower(toml::find(toml_data, "grid", "metric", "metric")) - .c_str()); - std::map additional_params; - std::string coord; - if (metric_enum == Metric::Minkowski) { - raise::ErrorIf(engine_enum != SimEngine::SRPIC, - "minkowski metric is only supported for SRPIC", - HERE); - coord = "cart"; - } else if (metric_enum == Metric::QKerr_Schild or - metric_enum == Metric::QSpherical) { - // quasi-spherical geometry - raise::ErrorIf(dim == Dim::_1D, - "not enough dimensions for qspherical geometry", - HERE); - raise::ErrorIf(dim == Dim::_3D, - "3D not implemented for qspherical geometry", - HERE); - coord = "qsph"; - additional_params["qsph_r0"] = toml::find_or(toml_data, - "grid", - "metric", - "qsph_r0", - defaults::qsph::r0); - additional_params["qsph_h"] = toml::find_or(toml_data, - "grid", - "metric", - "qsph_h", - defaults::qsph::h); - } else { - // spherical geometry - raise::ErrorIf(dim == Dim::_1D, - "not enough dimensions for spherical geometry", - HERE); - raise::ErrorIf(dim == Dim::_3D, - "3D not implemented for spherical geometry", - HERE); - coord = "sph"; - } - if ((engine_enum == SimEngine::GRPIC) && - (metric_enum != Metric::Kerr_Schild_0)) { - const auto ks_a = toml::find_or(toml_data, - "grid", - "metric", - "ks_a", - defaults::ks::a); - additional_params["ks_a"] = ks_a; - additional_params["ks_rh"] = ONE + math::sqrt(ONE - SQR(ks_a)); + template + auto get_dx0_V0( + const std::vector& resolution, + const boundaries_t& extent, + const std::map& params) -> std::pair { + const auto metric = M(resolution, extent, params); + const auto dx0 = metric.dxMin(); + coord_t x_corner { ZERO }; + for (auto d { 0u }; d < M::Dim; ++d) { + x_corner[d] = HALF; } - const auto coord_enum = Coord::pick(coord.c_str()); - return { metric_enum, coord_enum, additional_params }; + const auto V0 = metric.sqrt_det_h(x_corner); + return { dx0, V0 }; } auto GetBoundaryConditions(SimulationParams* params, @@ -411,5 +363,189 @@ namespace ntt { } } + void Grid::read(const SimEngine& engine_enum, const toml::value& toml_data) { + /* domain decomposition ------------------------------------------------ */ + int default_ndomains = 1; +#if defined(MPI_ENABLED) + raise::ErrorIf(MPI_Comm_size(MPI_COMM_WORLD, &default_ndomains) != MPI_SUCCESS, + "MPI_Comm_size failed", + HERE); +#endif + number_of_domains = toml::find_or(toml_data, + "simulation", + "domain", + "number", + default_ndomains); + + domain_decomposition = toml::find_or>( + toml_data, + "simulation", + "domain", + "decomposition", + std::vector { -1, -1, -1 }); + + /* resolution and dimension ------------------------------------------- */ + resolution = toml::find>(toml_data, "grid", "resolution"); + raise::ErrorIf(resolution.size() < 1 || resolution.size() > 3, + "invalid `grid.resolution`", + HERE); + dim = static_cast(resolution.size()); + + if (domain_decomposition.size() > dim) { + domain_decomposition.erase(domain_decomposition.begin() + (std::size_t)(dim), + domain_decomposition.end()); + } + raise::ErrorIf(domain_decomposition.size() != dim, + "invalid `simulation.domain.decomposition`", + HERE); + + /* metric and coordinates -------------------------------------------- */ + metric_enum = Metric::pick( + fmt::toLower(toml::find(toml_data, "grid", "metric", "metric")) + .c_str()); + std::string coord; + if (metric_enum == Metric::Minkowski) { + raise::ErrorIf(engine_enum != SimEngine::SRPIC, + "minkowski metric is only supported for SRPIC", + HERE); + coord = "cart"; + } else if (metric_enum == Metric::QKerr_Schild or + metric_enum == Metric::QSpherical) { + // quasi-spherical geometry + raise::ErrorIf(dim == Dim::_1D, + "not enough dimensions for qspherical geometry", + HERE); + raise::ErrorIf(dim == Dim::_3D, + "3D not implemented for qspherical geometry", + HERE); + coord = "qsph"; + metric_params["qsph_r0"] = toml::find_or(toml_data, + "grid", + "metric", + "qsph_r0", + defaults::qsph::r0); + metric_params["qsph_h"] = toml::find_or(toml_data, + "grid", + "metric", + "qsph_h", + defaults::qsph::h); + } else { + // spherical geometry + raise::ErrorIf(dim == Dim::_1D, + "not enough dimensions for spherical geometry", + HERE); + raise::ErrorIf(dim == Dim::_3D, + "3D not implemented for spherical geometry", + HERE); + coord = "sph"; + } + if ((engine_enum == SimEngine::GRPIC) && + (metric_enum != Metric::Kerr_Schild_0)) { + const auto ks_a = toml::find_or(toml_data, + "grid", + "metric", + "ks_a", + defaults::ks::a); + metric_params["ks_a"] = ks_a; + metric_params["ks_rh"] = ONE + math::sqrt(ONE - SQR(ks_a)); + } + coord_enum = Coord::pick(coord.c_str()); + + /* extent ------------------------------------------------------------- */ + extent = toml::find>>(toml_data, + "grid", + "extent"); + + if (extent.size() > dim) { + extent.erase(extent.begin() + (std::size_t)(dim), extent.end()); + } + raise::ErrorIf(extent[0].size() != 2, "invalid `grid.extent[0]`", HERE); + if (coord_enum != Coord::Cart) { + raise::ErrorIf(extent.size() > 1, + "invalid `grid.extent` for non-cartesian geometry", + HERE); + extent.push_back({ ZERO, constant::PI }); + if (dim == Dim::_3D) { + extent.push_back({ ZERO, TWO * constant::PI }); + } + } + raise::ErrorIf(extent.size() != dim, "invalid inferred `grid.extent`", HERE); + for (auto d { 0u }; d < (dim_t)dim; ++d) { + raise::ErrorIf(extent[d].size() != 2, + fmt::format("invalid inferred `grid.extent[%d]`", d), + HERE); + extent_pairwise_.push_back({ extent[d][0], extent[d][1] }); + } + + /* metric parameters ------------------------------------------------------ */ + if (coord_enum == Coord::Qsph) { + metric_params_short_["r0"] = metric_params["qsph_r0"]; + metric_params_short_["h"] = metric_params["qsph_h"]; + } + if ((engine_enum == SimEngine::GRPIC) && + (metric_enum != Metric::Kerr_Schild_0)) { + metric_params_short_["a"] = metric_params["ks_a"]; + } + // set("grid.metric.params", params); + + std::pair dx0_V0; + if (metric_enum == Metric::Minkowski) { + if (dim == Dim::_1D) { + dx0_V0 = get_dx0_V0>(resolution, + extent_pairwise_, + metric_params_short_); + } else if (dim == Dim::_2D) { + dx0_V0 = get_dx0_V0>(resolution, + extent_pairwise_, + metric_params_short_); + } else { + dx0_V0 = get_dx0_V0>(resolution, + extent_pairwise_, + metric_params_short_); + } + } else if (metric_enum == Metric::Spherical) { + dx0_V0 = get_dx0_V0>(resolution, + extent_pairwise_, + metric_params_short_); + } else if (metric_enum == Metric::QSpherical) { + dx0_V0 = get_dx0_V0>(resolution, + extent_pairwise_, + metric_params_short_); + } else if (metric_enum == Metric::Kerr_Schild) { + dx0_V0 = get_dx0_V0>(resolution, + extent_pairwise_, + metric_params_short_); + } else if (metric_enum == Metric::Kerr_Schild_0) { + dx0_V0 = get_dx0_V0>(resolution, + extent_pairwise_, + metric_params_short_); + } else if (metric_enum == Metric::QKerr_Schild) { + dx0_V0 = get_dx0_V0>(resolution, + extent_pairwise_, + metric_params_short_); + } + auto [dx0, V0] = dx0_V0; + scale_dx0 = dx0; + scale_V0 = V0; + } + + void Grid::setParams(SimulationParams* params) const { + params->set("simulation.domain.number", number_of_domains); + params->set("simulation.domain.decomposition", domain_decomposition); + + params->set("grid.resolution", resolution); + params->set("grid.dim", dim); + params->set("grid.metric.metric", metric_enum); + params->set("grid.metric.coord", coord_enum); + for (const auto& [key, value] : metric_params) { + params->set("grid.metric." + key, value); + } + params->set("grid.metric.params", metric_params_short_); + params->set("grid.extent", extent_pairwise_); + + params->set("scales.dx0", scale_dx0); + params->set("scales.V0", scale_V0); + } + } // namespace params } // namespace ntt diff --git a/src/framework/parameters/grid.h b/src/framework/parameters/grid.h index 08d75e02..19903052 100644 --- a/src/framework/parameters/grid.h +++ b/src/framework/parameters/grid.h @@ -55,11 +55,27 @@ namespace ntt { void setParams(SimulationParams*) const; }; - auto GetGridParams( - const toml::value&) -> std::tuple, Dimension>; + struct Grid { + int number_of_domains; + std::vector domain_decomposition; - auto GetMetricParams(const SimEngine&, Dimension, const toml::value&) - -> std::tuple>; + std::vector resolution; + Dimension dim; + + std::vector> extent; + boundaries_t extent_pairwise_; + + Metric metric_enum = Metric::INVALID; + Coord coord_enum = Coord::INVALID; + std::map metric_params; + std::map metric_params_short_; + + real_t scale_dx0; + real_t scale_V0; + + void read(const SimEngine&, const toml::value&); + void setParams(SimulationParams*) const; + }; auto GetBoundaryConditions( SimulationParams* params, diff --git a/src/framework/parameters/output.cpp b/src/framework/parameters/output.cpp index f6c15b81..17fdf540 100644 --- a/src/framework/parameters/output.cpp +++ b/src/framework/parameters/output.cpp @@ -154,6 +154,18 @@ namespace ntt { "stats", "custom", std::vector {}); + + /* Debug ---------------------------------------------------------------- */ + debug_as_is = toml::find_or(toml_data, "output", "debug", "as_is", false); + debug_ghosts = toml::find_or(toml_data, "output", "debug", "ghosts", false); + if (debug_ghosts) { + for (const auto& dwn : fields_downsampling) { + raise::ErrorIf( + dwn != 1, + "full resolution required when outputting with ghost cells", + HERE); + } + } } void Output::setParams(SimulationParams* params) const { @@ -182,6 +194,9 @@ namespace ntt { params->set("output.stats.quantities", stats_quantities); params->set("output.stats.custom", stats_custom_quantities); + + params->set("output.debug.as_is", debug_as_is); + params->set("output.debug.ghosts", debug_ghosts); } } // namespace params diff --git a/src/framework/parameters/output.h b/src/framework/parameters/output.h index 6039418c..7a15baab 100644 --- a/src/framework/parameters/output.h +++ b/src/framework/parameters/output.h @@ -55,6 +55,9 @@ namespace ntt { std::vector stats_quantities; std::vector stats_custom_quantities; + bool debug_as_is; + bool debug_ghosts; + void read(Dimension, std::size_t, const toml::value&); void setParams(SimulationParams*) const; }; diff --git a/src/framework/parameters/parameters.cpp b/src/framework/parameters/parameters.cpp index 4c5cdc40..cedcd940 100644 --- a/src/framework/parameters/parameters.cpp +++ b/src/framework/parameters/parameters.cpp @@ -9,13 +9,6 @@ #include "utils/numeric.h" #include "utils/toml.h" -#include "metrics/kerr_schild.h" -#include "metrics/kerr_schild_0.h" -#include "metrics/minkowski.h" -#include "metrics/qkerr_schild.h" -#include "metrics/qspherical.h" -#include "metrics/spherical.h" - #include "framework/containers/species.h" #include "framework/parameters/grid.h" #include "framework/parameters/output.h" @@ -27,28 +20,12 @@ #include #include -#include #include #include #include namespace ntt { - template - auto get_dx0_V0( - const std::vector& resolution, - const boundaries_t& extent, - const std::map& params) -> std::pair { - const auto metric = M(resolution, extent, params); - const auto dx0 = metric.dxMin(); - coord_t x_corner { ZERO }; - for (auto d { 0u }; d < M::Dim; ++d) { - x_corner[d] = HALF; - } - const auto V0 = metric.sqrt_det_h(x_corner); - return { dx0, V0 }; - } - /* * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . * Parameters that must not be changed during the checkpoint restart @@ -60,57 +37,10 @@ namespace ntt { fmt::toLower(toml::find(toml_data, "simulation", "engine")).c_str()); set("simulation.engine", engine_enum); - int default_ndomains = 1; -#if defined(MPI_ENABLED) - raise::ErrorIf(MPI_Comm_size(MPI_COMM_WORLD, &default_ndomains) != MPI_SUCCESS, - "MPI_Comm_size failed", - HERE); -#endif - const auto ndoms = toml::find_or(toml_data, - "simulation", - "domain", - "number", - default_ndomains); - set("simulation.domain.number", (unsigned int)ndoms); - - auto decomposition = toml::find_or>( - toml_data, - "simulation", - "domain", - "decomposition", - std::vector { -1, -1, -1 }); - promiseToDefine("simulation.domain.decomposition"); - - /* [grid] --------------------------------------------------------------- */ - const auto [res, dim] = params::GetGridParams(toml_data); - set("grid.resolution", res); - set("grid.dim", dim); - - if (decomposition.size() > dim) { - decomposition.erase(decomposition.begin() + (std::size_t)(dim), - decomposition.end()); - } - raise::ErrorIf(decomposition.size() != dim, - "invalid `simulation.domain.decomposition`", - HERE); - set("simulation.domain.decomposition", decomposition); - - auto extent = toml::find>>(toml_data, - "grid", - "extent"); - raise::ErrorIf(extent.size() < 1 || extent.size() > 3, - "invalid `grid.extent`", - HERE); - promiseToDefine("grid.extent"); - - /* [grid.metric] -------------------------------------------------------- */ - const auto [metric_enum, coord_enum, additional_params] = - params::GetMetricParams(engine_enum, dim, toml_data); - promiseToDefine("grid.metric.metric"); - set("grid.metric.coord", coord_enum); - for (const auto& [key, value] : additional_params) { - set("grid.metric." + key, value); - } + /* grid and decomposition ------------------------------------------------ */ + params::Grid grid_params {}; + grid_params.read(engine_enum, toml_data); + grid_params.setParams(this); /* [scales] ------------------------------------------------------------- */ const auto larmor0 = toml::find(toml_data, "scales", "larmor0"); @@ -120,10 +50,6 @@ namespace ntt { HERE); set("scales.larmor0", larmor0); set("scales.skindepth0", skindepth0); - promiseToDefine("scales.dx0"); - promiseToDefine("scales.V0"); - promiseToDefine("scales.n0"); - promiseToDefine("scales.q0"); set("scales.sigma0", SQR(skindepth0 / larmor0)); set("scales.B0", ONE / larmor0); set("scales.omegaB0", ONE / larmor0); @@ -135,6 +61,9 @@ namespace ntt { set("particles.use_weights", toml::find_or(toml_data, "particles", "use_weights", false)); + set("scales.n0", ppc0 / get("scales.V0")); + set("scales.q0", get("scales.V0") / (ppc0 * SQR(skindepth0))); + /* [particles.species] -------------------------------------------------- */ std::vector species; const auto species_tab = toml::find_or(toml_data, @@ -149,77 +78,6 @@ namespace ntt { idx += 1; } set("particles.species", species); - - /* inferred variables --------------------------------------------------- */ - // extent - if (extent.size() > dim) { - extent.erase(extent.begin() + (std::size_t)(dim), extent.end()); - } - raise::ErrorIf(extent[0].size() != 2, "invalid `grid.extent[0]`", HERE); - if (coord_enum != Coord::Cart) { - raise::ErrorIf(extent.size() > 1, - "invalid `grid.extent` for non-cartesian geometry", - HERE); - extent.push_back({ ZERO, constant::PI }); - if (dim == Dim::_3D) { - extent.push_back({ ZERO, TWO * constant::PI }); - } - } - raise::ErrorIf(extent.size() != dim, "invalid inferred `grid.extent`", HERE); - boundaries_t extent_pairwise; - for (auto d { 0u }; d < (dim_t)dim; ++d) { - raise::ErrorIf(extent[d].size() != 2, - fmt::format("invalid inferred `grid.extent[%d]`", d), - HERE); - extent_pairwise.push_back({ extent[d][0], extent[d][1] }); - } - set("grid.extent", extent_pairwise); - - // metric, dx0, V0, n0, q0 - { - boundaries_t ext; - for (const auto& e : extent) { - ext.push_back({ e[0], e[1] }); - } - std::map params; - if (coord_enum == Coord::Qsph) { - params["r0"] = get("grid.metric.qsph_r0"); - params["h"] = get("grid.metric.qsph_h"); - } - if ((engine_enum == SimEngine::GRPIC) && - (metric_enum != Metric::Kerr_Schild_0)) { - params["a"] = get("grid.metric.ks_a"); - } - set("grid.metric.params", params); - - std::pair dx0_V0; - if (metric_enum == Metric::Minkowski) { - if (dim == Dim::_1D) { - dx0_V0 = get_dx0_V0>(res, ext, params); - } else if (dim == Dim::_2D) { - dx0_V0 = get_dx0_V0>(res, ext, params); - } else { - dx0_V0 = get_dx0_V0>(res, ext, params); - } - } else if (metric_enum == Metric::Spherical) { - dx0_V0 = get_dx0_V0>(res, ext, params); - } else if (metric_enum == Metric::QSpherical) { - dx0_V0 = get_dx0_V0>(res, ext, params); - } else if (metric_enum == Metric::Kerr_Schild) { - dx0_V0 = get_dx0_V0>(res, ext, params); - } else if (metric_enum == Metric::Kerr_Schild_0) { - dx0_V0 = get_dx0_V0>(res, ext, params); - } else if (metric_enum == Metric::QKerr_Schild) { - dx0_V0 = get_dx0_V0>(res, ext, params); - } - auto [dx0, V0] = dx0_V0; - set("scales.dx0", dx0); - set("scales.V0", V0); - set("scales.n0", ppc0 / V0); - set("scales.q0", V0 / (ppc0 * SQR(skindepth0))); - - set("grid.metric.metric", metric_enum); - } } /* @@ -258,8 +116,7 @@ namespace ntt { /* [algorithms.deposit] ------------------------------------------------- */ set("algorithms.deposit.enable", toml::find_or(toml_data, "algorithms", "deposit", "enable", true)); - set("algorithms.deposit.order", - toml::find_or(toml_data, "algorithms", "deposit", "order", 1)); + set("algorithms.deposit.order", static_cast(SHAPE_ORDER)); /* [algorithms.fieldsolver] --------------------------------------------- */ set("algorithms.fieldsolver.enable", @@ -382,29 +239,10 @@ namespace ntt { set("particles.species", new_species); /* [output] ------------------------------------------------------------- */ - params::Output output_params; output_params.read(dim, get("particles.nspec"), toml_data); output_params.setParams(this); - /* [output.debug] ------------------------------------------------------- */ - set("output.debug.as_is", - toml::find_or(toml_data, "output", "debug", "as_is", false)); - const auto output_ghosts = toml::find_or(toml_data, - "output", - "debug", - "ghosts", - false); - set("output.debug.ghosts", output_ghosts); - if (output_ghosts) { - for (const auto& dwn : output_params.fields_downsampling) { - raise::ErrorIf( - dwn != 1, - "full resolution required when outputting with ghost cells", - HERE); - } - } - /* [checkpoint] --------------------------------------------------------- */ set("checkpoint.interval", toml::find_or(toml_data, From 46dc068a800eeca40c02e7b459bfdfedfa3facbd Mon Sep 17 00:00:00 2001 From: haykh Date: Fri, 9 Jan 2026 14:46:26 -0500 Subject: [PATCH 07/21] algorithms parameter reader (CPUTEST) --- src/framework/CMakeLists.txt | 7 +- src/framework/parameters/algorithms.cpp | 181 ++++++++++++++++++++++++ src/framework/parameters/algorithms.h | 56 ++++++++ src/framework/parameters/grid.h | 2 - src/framework/parameters/parameters.cpp | 137 ++---------------- 5 files changed, 254 insertions(+), 129 deletions(-) create mode 100644 src/framework/parameters/algorithms.cpp create mode 100644 src/framework/parameters/algorithms.h diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index 8eedc576..2b1bc51c 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -4,7 +4,11 @@ # # @sources: # -# * parameters.cpp +# * parameters/parameters.cpp +# * parameters/particles.cpp +# * parameters/grid.cpp +# * parameters/output.cpp +# * parameters/algorithms.cpp # * simulation.cpp # * domain/grid.cpp # * domain/metadomain.cpp @@ -41,6 +45,7 @@ set(SOURCES ${SRC_DIR}/parameters/particles.cpp ${SRC_DIR}/parameters/grid.cpp ${SRC_DIR}/parameters/output.cpp + ${SRC_DIR}/parameters/algorithms.cpp ${SRC_DIR}/domain/grid.cpp ${SRC_DIR}/domain/metadomain.cpp ${SRC_DIR}/domain/communications.cpp diff --git a/src/framework/parameters/algorithms.cpp b/src/framework/parameters/algorithms.cpp new file mode 100644 index 00000000..030f41a6 --- /dev/null +++ b/src/framework/parameters/algorithms.cpp @@ -0,0 +1,181 @@ +#include "framework/parameters/algorithms.h" + +#include "defaults.h" +#include "global.h" + +#include "utils/numeric.h" +#include "utils/toml.h" + +#include "framework/parameters/parameters.h" + +namespace ntt { + namespace params { + + void Algorithms::read(real_t dx0, + const std::map& extra, + const toml::value& toml_data) { + CFL = toml::find_or(toml_data, "algorithms", "timestep", "CFL", defaults::cfl); + dt = CFL * dx0; + dt_correction_factor = toml::find_or(toml_data, + "algorithms", + "timestep", + "correction", + defaults::correction); + + number_of_current_filters = toml::find_or(toml_data, + "algorithms", + "current_filters", + defaults::current_filters); + + deposit_enable = toml::find_or(toml_data, "algorithms", "deposit", "enable", true); + deposit_order = static_cast(SHAPE_ORDER); + + fieldsolver_enable = toml::find_or(toml_data, + "algorithms", + "fieldsolver", + "enable", + true); + + fieldsolver_stencil_coeffs["delta_x"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "delta_x", + defaults::fieldsolver::delta_x); + + fieldsolver_stencil_coeffs["delta_y"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "delta_y", + defaults::fieldsolver::delta_y); + + fieldsolver_stencil_coeffs["delta_z"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "delta_z", + defaults::fieldsolver::delta_z); + + fieldsolver_stencil_coeffs["beta_xy"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "beta_xy", + defaults::fieldsolver::beta_xy); + + fieldsolver_stencil_coeffs["beta_yx"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "beta_yx", + defaults::fieldsolver::beta_yx); + + fieldsolver_stencil_coeffs["beta_xz"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "beta_xz", + defaults::fieldsolver::beta_xz); + + fieldsolver_stencil_coeffs["beta_zx"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "beta_zx", + defaults::fieldsolver::beta_zx); + + fieldsolver_stencil_coeffs["beta_yz"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "beta_yz", + defaults::fieldsolver::beta_yz); + + fieldsolver_stencil_coeffs["beta_zy"] = toml::find_or( + toml_data, + "algorithms", + "fieldsolver", + "beta_zy", + defaults::fieldsolver::beta_zy); + + if (extra.at("gr")) { + gr_pusher_eps = toml::find_or(toml_data, + "algorithms", + "gr", + "pusher_eps", + defaults::gr::pusher_eps); + gr_pusher_niter = toml::find_or(toml_data, + "algorithms", + "gr", + "pusher_niter", + defaults::gr::pusher_niter); + } + + if (extra.at("gca")) { + gca_e_ovr_b_max = toml::find_or(toml_data, + "algorithms", + "gca", + "e_ovr_b_max", + defaults::gca::EovrB_max); + gca_larmor_max = toml::find_or(toml_data, + "algorithms", + "gca", + "larmor_max", + ZERO); + } + + if (extra.at("synchrotron")) { + synchrotron_gamma_rad = toml::find_or(toml_data, + "algorithms", + "synchrotron", + "gamma_rad", + defaults::synchrotron::gamma_rad); + } + + if (extra.at("compton")) { + compton_gamma_rad = toml::find_or(toml_data, + "algorithms", + "compton", + "gamma_rad", + defaults::compton::gamma_rad); + } + } + + void Algorithms::setParams(const std::map& extra, + SimulationParams* params) const { + params->set("algorithms.timestep.CFL", CFL); + params->set("algorithms.timestep.dt", dt); + params->set("algorithms.timestep.correction", dt_correction_factor); + + params->set("algorithms.current_filters", number_of_current_filters); + + params->set("algorithms.deposit.enable", deposit_enable); + params->set("algorithms.deposit.order", deposit_order); + + params->set("algorithms.fieldsolver.enable", fieldsolver_enable); + for (const auto& [key, value] : fieldsolver_stencil_coeffs) { + params->set("algorithms.fieldsolver." + key, value); + } + + if (extra.at("gr")) { + params->set("algorithms.gr.pusher_eps", gr_pusher_eps); + params->set("algorithms.gr.pusher_niter", gr_pusher_niter); + } + + if (extra.at("gca")) { + params->set("algorithms.gca.e_ovr_b_max", gca_e_ovr_b_max); + params->set("algorithms.gca.larmor_max", gca_larmor_max); + } + + if (extra.at("synchrotron")) { + params->set("algorithms.synchrotron.gamma_rad", synchrotron_gamma_rad); + } + + if (extra.at("compton")) { + params->set("algorithms.compton.gamma_rad", compton_gamma_rad); + } + } + + } // namespace params +} // namespace ntt diff --git a/src/framework/parameters/algorithms.h b/src/framework/parameters/algorithms.h new file mode 100644 index 00000000..0a4d13d0 --- /dev/null +++ b/src/framework/parameters/algorithms.h @@ -0,0 +1,56 @@ +/** + * @file framework/parameters/algorithms.h + * @brief Auxiliary functions for reading in algorithms parameters + * @implements + * - ntt::params::Algorithms + * @cpp: + * - grid.cpp + * @namespaces: + * - ntt::params:: + */ + +#ifndef FRAMEWORK_PARAMETERS_ALGORITHMS_H +#define FRAMEWORK_PARAMETERS_ALGORITHMS_H + +#include "global.h" + +#include "utils/toml.h" + +#include "framework/parameters/parameters.h" + +#include +#include + +namespace ntt { + namespace params { + + struct Algorithms { + real_t CFL; + real_t dt; + real_t dt_correction_factor; + + unsigned short number_of_current_filters; + + bool deposit_enable; + unsigned short deposit_order; + + bool fieldsolver_enable; + std::map fieldsolver_stencil_coeffs; + + real_t gr_pusher_eps; + unsigned short gr_pusher_niter; + + real_t gca_e_ovr_b_max; + real_t gca_larmor_max; + + real_t synchrotron_gamma_rad; + real_t compton_gamma_rad; + + void read(real_t, const std::map&, const toml::value&); + void setParams(const std::map&, SimulationParams*) const; + }; + + } // namespace params +} // namespace ntt + +#endif // FRAMEWORK_PARAMETERS_ALGORITHMS_H diff --git a/src/framework/parameters/grid.h b/src/framework/parameters/grid.h index 19903052..b436fd9d 100644 --- a/src/framework/parameters/grid.h +++ b/src/framework/parameters/grid.h @@ -3,8 +3,6 @@ * @brief Auxiliary functions for reading in grid/box parameters * @implements * - ntt::params::Boundaries - * - ntt::params::GetGridParams -> (std::vector, Dimension) - * - ntt::params::GetMetricParams -> (Metric, Coord, std::map) * - ntt::params::GetBoundaryConditions -> (boundaries_t, boundaries_t) * @cpp: * - grid.cpp diff --git a/src/framework/parameters/parameters.cpp b/src/framework/parameters/parameters.cpp index cedcd940..48280c69 100644 --- a/src/framework/parameters/parameters.cpp +++ b/src/framework/parameters/parameters.cpp @@ -10,6 +10,7 @@ #include "utils/toml.h" #include "framework/containers/species.h" +#include "framework/parameters/algorithms.h" #include "framework/parameters/grid.h" #include "framework/parameters/output.h" #include "framework/parameters/particles.h" @@ -106,103 +107,6 @@ namespace ntt { set("grid.boundaries.fields", flds_bc); set("grid.boundaries.particles", prtl_bc); - /* [algorithms] --------------------------------------------------------- */ - set("algorithms.current_filters", - toml::find_or(toml_data, - "algorithms", - "current_filters", - defaults::current_filters)); - - /* [algorithms.deposit] ------------------------------------------------- */ - set("algorithms.deposit.enable", - toml::find_or(toml_data, "algorithms", "deposit", "enable", true)); - set("algorithms.deposit.order", static_cast(SHAPE_ORDER)); - - /* [algorithms.fieldsolver] --------------------------------------------- */ - set("algorithms.fieldsolver.enable", - toml::find_or(toml_data, "algorithms", "fieldsolver", "enable", true)); - - set("algorithms.fieldsolver.delta_x", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "delta_x", - defaults::fieldsolver::delta_x)); - set("algorithms.fieldsolver.delta_y", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "delta_y", - defaults::fieldsolver::delta_y)); - set("algorithms.fieldsolver.delta_z", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "delta_z", - defaults::fieldsolver::delta_z)); - set("algorithms.fieldsolver.beta_xy", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "beta_xy", - defaults::fieldsolver::beta_xy)); - set("algorithms.fieldsolver.beta_yx", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "beta_yx", - defaults::fieldsolver::beta_yx)); - set("algorithms.fieldsolver.beta_xz", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "beta_xz", - defaults::fieldsolver::beta_xz)); - set("algorithms.fieldsolver.beta_zx", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "beta_zx", - defaults::fieldsolver::beta_zx)); - set("algorithms.fieldsolver.beta_yz", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "beta_yz", - defaults::fieldsolver::beta_yz)); - set("algorithms.fieldsolver.beta_zy", - toml::find_or(toml_data, - "algorithms", - "fieldsolver", - "beta_zy", - defaults::fieldsolver::beta_zy)); - /* [algorithms.timestep] ------------------------------------------------ */ - set("algorithms.timestep.CFL", - toml::find_or(toml_data, "algorithms", "timestep", "CFL", defaults::cfl)); - set("algorithms.timestep.dt", - get("algorithms.timestep.CFL") * get("scales.dx0")); - set("algorithms.timestep.correction", - toml::find_or(toml_data, - "algorithms", - "timestep", - "correction", - defaults::correction)); - - /* [algorithms.gr] ------------------------------------------------------ */ - if (engine_enum == SimEngine::GRPIC) { - set("algorithms.gr.pusher_eps", - toml::find_or(toml_data, - "algorithms", - "gr", - "pusher_eps", - defaults::gr::pusher_eps)); - set("algorithms.gr.pusher_niter", - toml::find_or(toml_data, - "algorithms", - "gr", - "pusher_niter", - defaults::gr::pusher_niter)); - } /* [particles] ---------------------------------------------------------- */ set("particles.clear_interval", toml::find_or(toml_data, "particles", "clear_interval", defaults::clear_interval)); @@ -291,35 +195,16 @@ namespace ntt { boundaries_params.read(dim, coord_enum, extent_pairwise, toml_data); boundaries_params.setParams(this); - // gca - if (isPromised("algorithms.gca.e_ovr_b_max")) { - set("algorithms.gca.e_ovr_b_max", - toml::find_or(toml_data, - "algorithms", - "gca", - "e_ovr_b_max", - defaults::gca::EovrB_max)); - set("algorithms.gca.larmor_max", - toml::find_or(toml_data, "algorithms", "gca", "larmor_max", ZERO)); - } - - // radiative drag parameters - if (isPromised("algorithms.synchrotron.gamma_rad")) { - set("algorithms.synchrotron.gamma_rad", - toml::find_or(toml_data, - "algorithms", - "synchrotron", - "gamma_rad", - defaults::synchrotron::gamma_rad)); - } - if (isPromised("algorithms.compton.gamma_rad")) { - set("algorithms.compton.gamma_rad", - toml::find_or(toml_data, - "algorithms", - "compton", - "gamma_rad", - defaults::compton::gamma_rad)); - } + /* [algorithms] --------------------------------------------------------- */ + ntt::params::Algorithms alg_params {}; + std::map alg_extra_flags = { + { "gr", engine_enum == SimEngine::GRPIC }, + { "use_gca", isPromised("algorithms.gca.e_ovr_b_max") }, + { "use_synchrotron", isPromised("algorithms.synchrotron.gamma_rad") }, + { "use_compton", isPromised("algorithms.compton.gamma_rad") } + }; + alg_params.read(get("scales.dx0"), alg_extra_flags, toml_data); + alg_params.setParams(alg_extra_flags, this); // @TODO: disabling stats for non-Cartesian if (coord_enum != Coord::Cart) { From c1a6a276d7fa4370b6fe1a1ef56447cbb81968be Mon Sep 17 00:00:00 2001 From: haykh Date: Fri, 9 Jan 2026 15:10:04 -0500 Subject: [PATCH 08/21] minor bug in algorithm param (CPUTEST) --- src/framework/parameters/parameters.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/framework/parameters/parameters.cpp b/src/framework/parameters/parameters.cpp index 48280c69..be5027a6 100644 --- a/src/framework/parameters/parameters.cpp +++ b/src/framework/parameters/parameters.cpp @@ -198,10 +198,10 @@ namespace ntt { /* [algorithms] --------------------------------------------------------- */ ntt::params::Algorithms alg_params {}; std::map alg_extra_flags = { - { "gr", engine_enum == SimEngine::GRPIC }, - { "use_gca", isPromised("algorithms.gca.e_ovr_b_max") }, - { "use_synchrotron", isPromised("algorithms.synchrotron.gamma_rad") }, - { "use_compton", isPromised("algorithms.compton.gamma_rad") } + { "gr", engine_enum == SimEngine::GRPIC }, + { "gca", isPromised("algorithms.gca.e_ovr_b_max") }, + { "synchrotron", isPromised("algorithms.synchrotron.gamma_rad") }, + { "compton", isPromised("algorithms.compton.gamma_rad") } }; alg_params.read(get("scales.dx0"), alg_extra_flags, toml_data); alg_params.setParams(alg_extra_flags, this); From 29f159552ee1da1b63c3cfd97ff274a903959bd0 Mon Sep 17 00:00:00 2001 From: haykh Date: Fri, 9 Jan 2026 15:55:21 -0500 Subject: [PATCH 09/21] prtl pusher is now an bit-int (CPUTEST) --- src/engines/engine_printer.cpp | 9 ++-- src/engines/grpic.hpp | 66 +++++++++++++------------ src/engines/srpic.hpp | 26 +++------- src/framework/containers/particles.cpp | 24 +++++---- src/framework/containers/particles.h | 25 +++++----- src/framework/containers/species.h | 44 ++++++----------- src/framework/parameters/parameters.cpp | 1 - src/framework/parameters/particles.cpp | 52 +++++++++++++------ src/framework/tests/parameters.cpp | 44 ++++++++--------- src/framework/tests/particles.cpp | 31 ++++++------ src/global/enums.h | 59 +++++++++++++--------- src/global/tests/enums.cpp | 2 - src/global/utils/param_container.cpp | 5 -- src/kernels/particle_pusher_sr.hpp | 23 ++++----- src/kernels/tests/ext_force.cpp | 57 +++++++++++---------- src/kernels/tests/gca_pusher.cpp | 4 +- src/kernels/tests/prtl_bc.cpp | 5 +- src/kernels/tests/pusher.cpp | 8 +-- 18 files changed, 244 insertions(+), 241 deletions(-) diff --git a/src/engines/engine_printer.cpp b/src/engines/engine_printer.cpp index 4ab9207d..ff8c13a9 100644 --- a/src/engines/engine_printer.cpp +++ b/src/engines/engine_printer.cpp @@ -397,10 +397,11 @@ namespace ntt { add_param(report, 6, "Mass", "%.1f", species.mass()); add_param(report, 6, "Charge", "%.1f", species.charge()); add_param(report, 6, "Max #", "%d [per domain]", species.maxnpart()); - add_param(report, 6, "Pusher", "%s", species.pusher().to_string()); - if (species.mass() != 0.0) { - add_param(report, 6, "GCA", "%s", species.use_gca() ? "ON" : "OFF"); - } + add_param(report, + 6, + "Pusher", + "%s", + ParticlePusher::to_string(species.pusher()).c_str()); add_param( report, 6, diff --git a/src/engines/grpic.hpp b/src/engines/grpic.hpp index 637deb5a..3eec7c07 100644 --- a/src/engines/grpic.hpp +++ b/src/engines/grpic.hpp @@ -1082,36 +1082,40 @@ namespace ntt { "algorithms.gr.pusher_eps"); const auto niter = m_params.template get( "algorithms.gr.pusher_niter"); - // clang-format off - if (species.pusher() == PrtlPusher::PHOTON) { - auto range_policy = Kokkos::RangePolicy( - 0, - species.npart()); + if (species.pusher() == ParticlePusher::PHOTON) { + auto range_policy = + Kokkos::RangePolicy( + 0, + species.npart()); - Kokkos::parallel_for( - "ParticlePusher", - range_policy, - kernel::gr::Pusher_kernel( - domain.fields.em, - domain.fields.em0, - species.i1, species.i2, species.i3, - species.i1_prev, species.i2_prev, species.i3_prev, - species.dx1, species.dx2, species.dx3, - species.dx1_prev, species.dx2_prev, species.dx3_prev, - species.ux1, species.ux2, species.ux3, - species.phi, species.tag, - domain.mesh.metric, - coeff, dt, - domain.mesh.n_active(in::x1), - domain.mesh.n_active(in::x2), - domain.mesh.n_active(in::x3), - eps, niter, - domain.mesh.prtl_bc() - )); - } else if (species.pusher() == PrtlPusher::BORIS) { - auto range_policy = Kokkos::RangePolicy( - 0, - species.npart()); + // clang-format off + Kokkos::parallel_for( + "ParticlePusher", + range_policy, + kernel::gr::Pusher_kernel( + domain.fields.em, + domain.fields.em0, + species.i1, species.i2, species.i3, + species.i1_prev, species.i2_prev, species.i3_prev, + species.dx1, species.dx2, species.dx3, + species.dx1_prev, species.dx2_prev, species.dx3_prev, + species.ux1, species.ux2, species.ux3, + species.phi, species.tag, + domain.mesh.metric, + coeff, dt, + domain.mesh.n_active(in::x1), + domain.mesh.n_active(in::x2), + domain.mesh.n_active(in::x3), + eps, niter, + domain.mesh.prtl_bc() + )); + // clang-format on + } else if (species.pusher() == ParticlePusher::BORIS) { + auto range_policy = + Kokkos::RangePolicy( + 0, + species.npart()); + // clang-format off Kokkos::parallel_for( "ParticlePusher", range_policy, @@ -1132,12 +1136,12 @@ namespace ntt { eps, niter, domain.mesh.prtl_bc() )); - } else if (species.pusher() == PrtlPusher::NONE) { + // clang-format on + } else if (species.pusher() == ParticlePusher::NONE) { // do nothing } else { raise::Error("not implemented", HERE); } - // clang-format on } } }; diff --git a/src/engines/srpic.hpp b/src/engines/srpic.hpp index ef632149..129a0a1d 100644 --- a/src/engines/srpic.hpp +++ b/src/engines/srpic.hpp @@ -312,7 +312,7 @@ namespace ntt { } } for (auto& species : domain.species) { - if ((species.pusher() == PrtlPusher::NONE) or (species.npart() == 0)) { + if ((species.pusher() == ParticlePusher::NONE) or (species.npart() == 0)) { continue; } species.set_unsorted(); @@ -328,21 +328,11 @@ namespace ntt { // coeff = q / m (dt / 2) omegaB0 const auto coeff = q_ovr_m * HALF * dt * m_params.template get("scales.omegaB0"); - PrtlPusher::type pusher; - if (species.pusher() == PrtlPusher::PHOTON) { - pusher = PrtlPusher::PHOTON; - } else if (species.pusher() == PrtlPusher::BORIS) { - pusher = PrtlPusher::BORIS; - } else if (species.pusher() == PrtlPusher::VAY) { - pusher = PrtlPusher::VAY; - } else { - raise::Fatal("Invalid particle pusher", HERE); - } const auto radiative_drag_flags = species.radiative_drag_flags(); // coefficients to be forwarded to the dispatcher // gca - const auto has_gca = species.use_gca(); + const auto has_gca = species.pusher() & ParticlePusher::GCA; const auto gca_larmor_max = has_gca ? m_params.template get( "algorithms.gca.larmor_max") : ZERO; @@ -392,7 +382,7 @@ namespace ntt { "ParticlePusher", species.rangeActiveParticles(), kernel::sr::Pusher_kernel( - pusher, has_gca, false, + species.pusher(), false, radiative_drag_flags, domain.fields.em, species.index(), @@ -421,7 +411,7 @@ namespace ntt { "ParticlePusher", species.rangeActiveParticles(), kernel::sr::Pusher_kernel( - pusher, has_gca, false, + species.pusher(), false, radiative_drag_flags, domain.fields.em, species.index(), @@ -450,7 +440,7 @@ namespace ntt { "ParticlePusher", species.rangeActiveParticles(), kernel::sr::Pusher_kernel( - pusher, has_gca, true, + species.pusher(), true, radiative_drag_flags, domain.fields.em, species.index(), @@ -482,7 +472,7 @@ namespace ntt { "ParticlePusher", species.rangeActiveParticles(), kernel::sr::Pusher_kernel( - pusher, has_gca, true, + species.pusher(), true, radiative_drag_flags, domain.fields.em, species.index(), @@ -541,8 +531,8 @@ namespace ntt { auto scatter_cur = Kokkos::Experimental::create_scatter_view( domain.fields.cur); for (auto& species : domain.species) { - if ((species.pusher() == PrtlPusher::NONE) or (species.npart() == 0) or - cmp::AlmostZero_host(species.charge())) { + if ((species.pusher() == ParticlePusher::NONE) or + (species.npart() == 0) or cmp::AlmostZero_host(species.charge())) { continue; } logger::Checkpoint( diff --git a/src/framework/containers/particles.cpp b/src/framework/containers/particles.cpp index 2a254cd9..b33ae907 100644 --- a/src/framework/containers/particles.cpp +++ b/src/framework/containers/particles.cpp @@ -16,25 +16,23 @@ namespace ntt { template - Particles::Particles(spidx_t index, - const std::string& label, - float m, - float ch, - npart_t maxnpart, - const PrtlPusher& pusher, - bool use_tracking, - bool use_gca, - RadiativeDragFlags radiative_drag_flags, - unsigned short npld_r, - unsigned short npld_i) + Particles::Particles(spidx_t index, + const std::string& label, + float m, + float ch, + npart_t maxnpart, + ParticlePusherFlags particle_pusher_flags, + bool use_tracking, + RadiativeDragFlags radiative_drag_flags, + unsigned short npld_r, + unsigned short npld_i) : ParticleSpecies(index, label, m, ch, maxnpart, - pusher, + particle_pusher_flags, use_tracking, - use_gca, radiative_drag_flags, npld_r, npld_i) { diff --git a/src/framework/containers/particles.h b/src/framework/containers/particles.h index 66e34cdf..c0c2efcb 100644 --- a/src/framework/containers/particles.h +++ b/src/framework/containers/particles.h @@ -83,24 +83,22 @@ namespace ntt { * @param m The mass of the species * @param ch The charge of the species * @param maxnpart The maximum number of allocated particles for the species - * @param pusher The pusher assigned for the species + * @param particle_pusher_flags The pusher(s) assigned for the species * @param use_tracking Use particle tracking for the species - * @param use_gca Use hybrid GCA pusher for the species * @param radiative_drag_flags The radiative drag mechanism(s) assigned for the species * @param npld_r The number of real-valued payloads for the species * @param npld_i The number of integer-valued payloads for the species */ - Particles(spidx_t index, - const std::string& label, - float m, - float ch, - npart_t maxnpart, - const PrtlPusher& pusher, - bool use_gca, - bool use_tracking, - RadiativeDragFlags radiative_drag_flags, - unsigned short npld_r = 0, - unsigned short npld_i = 0); + Particles(spidx_t index, + const std::string& label, + float m, + float ch, + npart_t maxnpart, + ParticlePusherFlags particle_pusher_flags, + bool use_tracking, + RadiativeDragFlags radiative_drag_flags, + unsigned short npld_r = 0, + unsigned short npld_i = 0); /** * @brief Constructor for the particle container @@ -115,7 +113,6 @@ namespace ntt { spec.maxnpart(), spec.pusher(), spec.use_tracking(), - spec.use_gca(), spec.radiative_drag_flags(), spec.npld_r(), spec.npld_i()) {} diff --git a/src/framework/containers/species.h b/src/framework/containers/species.h index ebc1c701..1c44fc6a 100644 --- a/src/framework/containers/species.h +++ b/src/framework/containers/species.h @@ -31,14 +31,11 @@ namespace ntt { npart_t m_maxnpart; // Pusher assigned for the species - const PrtlPusher m_pusher; + const ParticlePusherFlags m_particle_pusher_flags; // Use particle tracking for the species const bool m_use_tracking; - // Use byrid gca pusher for the species - const bool m_use_gca; - // Radiative drag mechanism(s) assigned for the species const RadiativeDragFlags m_radiative_drag_flags; @@ -53,9 +50,8 @@ namespace ntt { , m_mass { 0.0 } , m_charge { 0.0 } , m_maxnpart { 0 } - , m_pusher { PrtlPusher::INVALID } + , m_particle_pusher_flags { ParticlePusher::NONE } , m_use_tracking { false } - , m_use_gca { false } , m_radiative_drag_flags { RadiativeDrag::NONE } , m_npld_r { 0 } , m_npld_i { 0 } {} @@ -68,32 +64,29 @@ namespace ntt { * @param m The mass of the species. * @param ch The charge of the species. * @param maxnpart The maximum number of allocated particles for the species. - * @param pusher The pusher assigned for the species. + * @param particle_pusher_flags The pusher(s) assigned for the species. * @param use_tracking Use particle tracking for the species. - * @param use_gca Use hybrid GCA pusher for the species. * @param radiative_drag_flags The radiative drag mechanism(s) assigned for the species. * @param npld_r The number of real-valued payloads for the species * @param npld_i The number of integer-valued payloads for the species */ - ParticleSpecies(spidx_t index, - const std::string& label, - float m, - float ch, - npart_t maxnpart, - const PrtlPusher& pusher, - bool use_tracking, - bool use_gca, - RadiativeDragFlags radiative_drag_flags, - unsigned short npld_r = 0, - unsigned short npld_i = 0) + ParticleSpecies(spidx_t index, + const std::string& label, + float m, + float ch, + npart_t maxnpart, + ParticlePusherFlags particle_pusher_flags, + bool use_tracking, + RadiativeDragFlags radiative_drag_flags, + unsigned short npld_r = 0, + unsigned short npld_i = 0) : m_index { index } , m_label { std::move(label) } , m_mass { m } , m_charge { ch } , m_maxnpart { maxnpart } - , m_pusher { pusher } + , m_particle_pusher_flags { particle_pusher_flags } , m_use_tracking { use_tracking } - , m_use_gca { use_gca } , m_radiative_drag_flags { radiative_drag_flags } , m_npld_r { npld_r } , m_npld_i { npld_i } { @@ -144,8 +137,8 @@ namespace ntt { } [[nodiscard]] - auto pusher() const -> PrtlPusher { - return m_pusher; + auto pusher() const -> ParticlePusherFlags { + return m_particle_pusher_flags; } [[nodiscard]] @@ -153,11 +146,6 @@ namespace ntt { return m_use_tracking; } - [[nodiscard]] - auto use_gca() const -> bool { - return m_use_gca; - } - [[nodiscard]] auto radiative_drag_flags() const -> RadiativeDragFlags { return m_radiative_drag_flags; diff --git a/src/framework/parameters/parameters.cpp b/src/framework/parameters/parameters.cpp index be5027a6..9d8d585f 100644 --- a/src/framework/parameters/parameters.cpp +++ b/src/framework/parameters/parameters.cpp @@ -134,7 +134,6 @@ namespace ntt { maxnpart, particle_species.pusher(), particle_species.use_tracking(), - particle_species.use_gca(), particle_species.radiative_drag_flags(), particle_species.npld_r(), particle_species.npld_i()); diff --git a/src/framework/parameters/particles.cpp b/src/framework/parameters/particles.cpp index 1f76d282..fd445a37 100644 --- a/src/framework/parameters/particles.cpp +++ b/src/framework/parameters/particles.cpp @@ -45,6 +45,38 @@ namespace ntt { } } + auto getPusherFlags( + const std::string& particle_pusher_str) -> ParticlePusherFlags { + if (fmt::toLower(particle_pusher_str) == "none") { + return ParticlePusher::NONE; + } else { + // separate comas + ParticlePusherFlags flags = ParticlePusher::NONE; + std::string token; + std::istringstream tokenStream(particle_pusher_str); + while (std::getline(tokenStream, token, ',')) { + const auto token_lower = fmt::toLower(token); + if (token_lower == "photon") { + flags |= ParticlePusher::PHOTON; + } else if (token_lower == "boris") { + flags |= ParticlePusher::BORIS; + } else if (token_lower == "vay") { + flags |= ParticlePusher::VAY; + } else if (token_lower == "gca") { + flags |= ParticlePusher::GCA; + } else { + raise::Error( + fmt::format("Invalid pusher value: %s", particle_pusher_str), + HERE); + } + } + if (flags & ParticlePusher::PHOTON and flags & ParticlePusher::GCA) { + raise::Error("Photon pusher cannot be used with GCA", HERE); + } + return flags; + } + } + auto GetParticleSpecies(SimulationParams* params, const SimEngine& engine_enum, spidx_t idx, @@ -62,7 +94,7 @@ namespace ntt { : defaults::em_pusher); const auto maxnpart_real = toml::find(sp, "maxnpart"); const auto maxnpart = static_cast(maxnpart_real); - auto pusher = toml::find_or(sp, "pusher", std::string(def_pusher)); + auto pusher_str = toml::find_or(sp, "pusher", std::string(def_pusher)); const auto npayloads_real = toml::find_or(sp, "n_payloads_real", static_cast(0)); @@ -83,19 +115,10 @@ namespace ntt { raise::ErrorIf((fmt::toLower(radiative_drag_str) != "none") && is_massless, "radiative drag is only applicable to massive particles", HERE); - raise::ErrorIf((fmt::toLower(pusher) == "photon") && !is_massless, + raise::ErrorIf((fmt::toLower(pusher_str) == "photon") && !is_massless, "photon pusher is only applicable to massless particles", HERE); - bool use_gca = false; - if (pusher.find(',') != std::string::npos) { - raise::ErrorIf(fmt::toLower(pusher.substr(pusher.find(',') + 1, - pusher.size())) != "gca", - "invalid pusher syntax", - HERE); - use_gca = true; - pusher = pusher.substr(0, pusher.find(',')); - } - const auto pusher_enum = PrtlPusher::pick(pusher.c_str()); + const auto particle_pusher_flags = getPusherFlags(pusher_str); const auto radiative_drag_flags = getRadiativeDragFlags(radiative_drag_str); if (radiative_drag_flags & RadiativeDrag::SYNCHROTRON) { raise::ErrorIf(engine_enum != SimEngine::SRPIC, @@ -110,7 +133,7 @@ namespace ntt { HERE); params->promiseToDefine("algorithms.compton.gamma_rad"); } - if (use_gca) { + if (particle_pusher_flags & ParticlePusher::GCA) { raise::ErrorIf(engine_enum != SimEngine::SRPIC, "GCA pushers are only supported for SRPIC", HERE); @@ -122,9 +145,8 @@ namespace ntt { mass, charge, maxnpart, - pusher_enum, + particle_pusher_flags, use_tracking, - use_gca, radiative_drag_flags, npayloads_real, npayloads_int); diff --git a/src/framework/tests/parameters.cpp b/src/framework/tests/parameters.cpp index 614bf505..acd10421 100644 --- a/src/framework/tests/parameters.cpp +++ b/src/framework/tests/parameters.cpp @@ -309,9 +309,9 @@ auto main(int argc, char* argv[]) -> int { assert_equal(species[0].mass(), 1.0f, "species[0].mass"); assert_equal(species[0].charge(), -1.0f, "species[0].charge"); assert_equal(species[0].maxnpart(), 100, "species[0].maxnpart"); - assert_equal(species[0].pusher(), - PrtlPusher::BORIS, - "species[0].pusher"); + assert_equal(species[0].pusher(), + ParticlePusher::BORIS, + "species[0].pusher"); assert_equal(species[0].npld_r(), 3, "species[0].npld_r"); assert_equal(species[0].npld_i(), 1, "species[0].npld_i"); assert_equal(species[0].use_tracking(), true, "species[0].tracking"); @@ -320,9 +320,9 @@ auto main(int argc, char* argv[]) -> int { assert_equal(species[1].mass(), 1.0f, "species[1].mass"); assert_equal(species[1].charge(), 200.0f, "species[1].charge"); assert_equal(species[1].maxnpart(), 100, "species[1].maxnpart"); - assert_equal(species[1].pusher(), - PrtlPusher::VAY, - "species[1].pusher"); + assert_equal(species[1].pusher(), + ParticlePusher::VAY, + "species[1].pusher"); assert_equal(species[1].npld_r(), 0, "species[1].npld_r"); assert_equal(params_mink_1d.get("setup.myfloat"), @@ -471,10 +471,9 @@ auto main(int argc, char* argv[]) -> int { assert_equal(species[0].mass(), 1.0f, "species[0].mass"); assert_equal(species[0].charge(), -1.0f, "species[0].charge"); assert_equal(species[0].maxnpart(), 100, "species[0].maxnpart"); - assert_equal(species[0].pusher(), - PrtlPusher::BORIS, - "species[0].pusher"); - assert_equal(species[0].use_gca(), true, "species[0].use_gca"); + assert_equal(species[0].pusher(), + ParticlePusher::BORIS | ParticlePusher::GCA, + "species[0].pusher"); assert_equal(species[0].npld_r(), 3, "species[0].npld_r"); assert_equal(species[0].radiative_drag_flags(), RadiativeDrag::SYNCHROTRON | @@ -485,10 +484,9 @@ auto main(int argc, char* argv[]) -> int { assert_equal(species[1].mass(), 1.0f, "species[1].mass"); assert_equal(species[1].charge(), 1.0f, "species[1].charge"); assert_equal(species[1].maxnpart(), 100, "species[1].maxnpart"); - assert_equal(species[1].pusher(), - PrtlPusher::BORIS, - "species[1].pusher"); - assert_equal(species[1].use_gca(), true, "species[1].use_gca"); + assert_equal(species[1].pusher(), + ParticlePusher::BORIS | ParticlePusher::GCA, + "species[1].pusher"); assert_equal(species[1].npld_r(), 0, "species[1].npld_r"); assert_equal(species[1].radiative_drag_flags(), RadiativeDrag::SYNCHROTRON, @@ -500,9 +498,9 @@ auto main(int argc, char* argv[]) -> int { assert_equal(species[2].mass(), 0.0f, "species[2].mass"); assert_equal(species[2].charge(), 0.0f, "species[2].charge"); assert_equal(species[2].maxnpart(), 100, "species[2].maxnpart"); - assert_equal(species[2].pusher(), - PrtlPusher::PHOTON, - "species[2].pusher"); + assert_equal(species[2].pusher(), + ParticlePusher::PHOTON, + "species[2].pusher"); assert_equal(species[2].npld_r(), 0, "species[2].npld_r"); } @@ -609,18 +607,18 @@ auto main(int argc, char* argv[]) -> int { assert_equal(species[0].mass(), 1.0f, "species[0].mass"); assert_equal(species[0].charge(), -1.0f, "species[0].charge"); assert_equal(species[0].maxnpart(), 100, "species[0].maxnpart"); - assert_equal(species[0].pusher(), - PrtlPusher::BORIS, - "species[0].pusher"); + assert_equal(species[0].pusher(), + ParticlePusher::BORIS, + "species[0].pusher"); assert_equal(species[0].npld_r(), 0, "species[0].npld_r"); assert_equal(species[1].label(), "e+", "species[1].label"); assert_equal(species[1].mass(), 1.0f, "species[1].mass"); assert_equal(species[1].charge(), 1.0f, "species[1].charge"); assert_equal(species[1].maxnpart(), 100, "species[1].maxnpart"); - assert_equal(species[1].pusher(), - PrtlPusher::BORIS, - "species[1].pusher"); + assert_equal(species[1].pusher(), + ParticlePusher::BORIS, + "species[1].pusher"); assert_equal(species[1].npld_r(), 0, "species[1].npld_r"); } diff --git a/src/framework/tests/particles.cpp b/src/framework/tests/particles.cpp index 9592a66e..a1015172 100644 --- a/src/framework/tests/particles.cpp +++ b/src/framework/tests/particles.cpp @@ -9,16 +9,16 @@ #include template -void testParticles(int index, - const std::string& label, - float m, - float ch, - std::size_t maxnpart, - const ntt::PrtlPusher& pusher, - bool use_tracking, - ntt::RadiativeDragFlags radiative_drag_flags, - unsigned short npld_r = 0, - unsigned short npld_i = 0) { +void testParticles(int index, + const std::string& label, + float m, + float ch, + std::size_t maxnpart, + ntt::ParticlePusherFlags pusher, + bool use_tracking, + ntt::RadiativeDragFlags radiative_drag_flags, + unsigned short npld_r = 0, + unsigned short npld_i = 0) { using namespace ntt; auto p = Particles(index, label, @@ -27,7 +27,6 @@ void testParticles(int index, maxnpart, pusher, use_tracking, - false, radiative_drag_flags, npld_r, npld_i); @@ -117,7 +116,7 @@ auto main(int argc, char** argv) -> int { 1.0, -1.0, 100, - PrtlPusher::BORIS, + ParticlePusher::BORIS, false, RadiativeDrag::SYNCHROTRON); testParticles(2, @@ -125,7 +124,7 @@ auto main(int argc, char** argv) -> int { 100.0, -1.0, 1000, - PrtlPusher::VAY, + ParticlePusher::VAY, true, RadiativeDrag::SYNCHROTRON | RadiativeDrag::COMPTON, @@ -136,7 +135,7 @@ auto main(int argc, char** argv) -> int { 0.0, 0.0, 100, - PrtlPusher::PHOTON, + ParticlePusher::PHOTON, false, RadiativeDrag::NONE, 5); @@ -145,7 +144,7 @@ auto main(int argc, char** argv) -> int { 1.0, 1.0, 100, - PrtlPusher::BORIS, + ParticlePusher::BORIS, true, RadiativeDrag::NONE, 2, @@ -155,7 +154,7 @@ auto main(int argc, char** argv) -> int { 1.0, 1.0, 100, - PrtlPusher::BORIS, + ParticlePusher::BORIS, false, RadiativeDrag::NONE, 1, diff --git a/src/global/enums.h b/src/global/enums.h index f1108a8e..d1aae86b 100644 --- a/src/global/enums.h +++ b/src/global/enums.h @@ -10,12 +10,12 @@ * reflect, horizon, axis, sync * - enum ntt::FldsBC // periodic, match, fixed, atmosphere, * custom, horizon, axis, conductor, sync - * - enum ntt::PrtlPusher // boris, vay, photon, none * - enum ntt::FldsID // e, dive, d, divd, b, h, j, * a, t, rho, charge, n, nppc, v, custom * - enum ntt::StatsID // b^2, e^2, exb, j.e, t, rho, * charge, n, npart * + * - enum ntt::ParticlePusher // photon, boris, vay, gca, none * - enum ntt::RadiativeDrag // compton, synchrotron, none * * @namespaces: @@ -29,10 +29,10 @@ * example: SimEngine(SimEngine::SRPIC).to_string() [return "srpic"] * @note * To check if a string is a valid option, use the contains() function - * example: PrtlPusher::contains("vay") == true + * example: PrtlBC::contains("periodic") == true * @note * To get the proper enum instance from a string, use the pick() function - * example: PrtlPusher::pick("vay") [returns PrtlPusher(PrtlPusher::VAY)] + * example: PrtlBC::pick("periodic") [returns PrtlBC::PERIODIC] * @note * To get the total number of enum instances, use the total variable * example: SimEngine::total == 2 @@ -242,25 +242,6 @@ namespace ntt { static constexpr std::size_t total = sizeof(variants) / sizeof(variants[0]); }; - struct PrtlPusher : public enums_hidden::BaseEnum { - static constexpr const char* label = "prtl_pusher"; - - enum type : uint8_t { - INVALID = 0, - BORIS = 1, - VAY = 2, - PHOTON = 3, - NONE = 4, - }; - - constexpr PrtlPusher(uint8_t c) - : enums_hidden::BaseEnum { c } {} - - static constexpr type variants[] = { BORIS, VAY, PHOTON, NONE }; - static constexpr const char* lookup[] = { "boris", "vay", "photon", "none" }; - static constexpr std::size_t total = sizeof(variants) / sizeof(variants[0]); - }; - struct FldsID : public enums_hidden::BaseEnum { static constexpr const char* label = "out_flds"; @@ -322,6 +303,40 @@ namespace ntt { static constexpr std::size_t total = sizeof(variants) / sizeof(variants[0]); }; + namespace ParticlePusher { + enum ParticlePusherFlags_ { + NONE = 0, + PHOTON = 1 << 0, + BORIS = 1 << 1, + VAY = 1 << 2, + GCA = 1 << 3, + }; + + inline auto to_string(int flags) -> std::string { + if (flags == NONE) { + return "none"; + } else { + std::string result = ""; + if (flags & PHOTON) { + result += "photon"; + } else if (flags & BORIS) { + result += "boris"; + } else if (flags & VAY) { + result += "vay"; + } + if (flags & GCA) { + if (!result.empty()) { + result += ","; + } + result += "gca"; + } + return result; + } + } + } // namespace ParticlePusher + + typedef int ParticlePusherFlags; + namespace RadiativeDrag { enum RadiativeDragFlags_ { NONE = 0, diff --git a/src/global/tests/enums.cpp b/src/global/tests/enums.cpp index 55ed63a7..8a28f96e 100644 --- a/src/global/tests/enums.cpp +++ b/src/global/tests/enums.cpp @@ -64,7 +64,6 @@ auto main() -> int { enum_str_t all_fields_bcs = { "periodic", "match", "fixed", "atmosphere", "custom", "horizon", "axis", "conductor", "sync" }; - enum_str_t all_particle_pushers = { "boris", "vay", "photon", "none" }; enum_str_t all_out_flds = { "e", "dive", "d", "divd", "b", "h", "j", "a", "t", "rho", @@ -78,7 +77,6 @@ auto main() -> int { checkEnum(all_simulation_engines); checkEnum(all_particle_bcs); checkEnum(all_fields_bcs); - checkEnum(all_particle_pushers); checkEnum(all_out_flds); checkEnum(all_out_stats); diff --git a/src/global/utils/param_container.cpp b/src/global/utils/param_container.cpp index 895eec18..bd8399bc 100644 --- a/src/global/utils/param_container.cpp +++ b/src/global/utils/param_container.cpp @@ -235,7 +235,6 @@ namespace prm { register_write_function(); register_write_function(); register_write_function(); - register_write_function(); register_write_function_for_pair(); register_write_function_for_pair(); @@ -254,7 +253,6 @@ namespace prm { register_write_function_for_pair(); register_write_function_for_pair(); register_write_function_for_pair(); - register_write_function_for_pair(); register_write_function_for_vector(); register_write_function_for_vector(); @@ -273,7 +271,6 @@ namespace prm { register_write_function_for_vector(); register_write_function_for_vector(); register_write_function_for_vector(); - register_write_function_for_vector(); register_write_function_for_vector_of_pair(); register_write_function_for_vector_of_pair(); @@ -292,7 +289,6 @@ namespace prm { register_write_function_for_vector_of_pair(); register_write_function_for_vector_of_pair(); register_write_function_for_vector_of_pair(); - register_write_function_for_vector_of_pair(); register_write_function_for_vector_of_vector(); register_write_function_for_vector_of_vector(); @@ -311,7 +307,6 @@ namespace prm { register_write_function_for_vector_of_vector(); register_write_function_for_vector_of_vector(); register_write_function_for_vector_of_vector(); - register_write_function_for_vector_of_vector(); register_write_function_for_dict(); register_write_function_for_dict(); diff --git a/src/kernels/particle_pusher_sr.hpp b/src/kernels/particle_pusher_sr.hpp index 9b5672c9..1c8901c6 100644 --- a/src/kernels/particle_pusher_sr.hpp +++ b/src/kernels/particle_pusher_sr.hpp @@ -188,10 +188,9 @@ namespace kernel::sr { static constexpr auto ExtForce = not std::is_same::value; private: - const PrtlPusher::type pusher; - const bool GCA; - const bool ext_force; - const RadiativeDragFlags radiative_drag_flags; + const ParticlePusherFlags pusher; + const bool ext_force; + const RadiativeDragFlags radiative_drag_flags; const randacc_ndfield_t EB; const spidx_t sp; @@ -223,8 +222,7 @@ namespace kernel::sr { const real_t coeff_sync, coeff_comp; public: - Pusher_kernel(const PrtlPusher::type& pusher, - bool GCA, + Pusher_kernel(ParticlePusherFlags pusher, bool ext_force, RadiativeDragFlags radiative_drag_flags, const randacc_ndfield_t& EB, @@ -260,7 +258,6 @@ namespace kernel::sr { real_t coeff_sync, real_t coeff_comp) : pusher { pusher } - , GCA { GCA } , ext_force { ext_force } , radiative_drag_flags { radiative_drag_flags } , EB { EB } @@ -329,8 +326,7 @@ namespace kernel::sr { } } - Pusher_kernel(const PrtlPusher::type& pusher, - bool GCA, + Pusher_kernel(ParticlePusherFlags pusher, bool ext_force, RadiativeDragFlags radiative_drag_flags, const ndfield_t& EB, @@ -365,7 +361,6 @@ namespace kernel::sr { real_t coeff_sync, real_t coeff_comp) : Pusher_kernel(pusher, - GCA, ext_force, radiative_drag_flags, EB, @@ -475,7 +470,7 @@ namespace kernel::sr { } coord_t xp_Cd { ZERO }; getPrtlPos(p, xp_Cd); - if (pusher == PrtlPusher::PHOTON) { + if (pusher == ParticlePusher::PHOTON) { posUpd(false, p, xp_Cd); return; } @@ -520,7 +515,7 @@ namespace kernel::sr { force.fx3(sp, time, ext_force, xp_Ph) }, force_Cart); } - if (GCA) { + if (pusher & ParticlePusher::GCA) { /* hybrid GCA/conventional mode --------------------------------- */ const auto E2 { NORM_SQR(ei_Cart[0], ei_Cart[1], ei_Cart[2]) }; const auto B2 { NORM_SQR(bi_Cart[0], bi_Cart[1], bi_Cart[2]) }; @@ -715,7 +710,7 @@ namespace kernel::sr { ux1(p) = upar * b0[0] + vE_Cart[0] * Gamma; ux2(p) = upar * b0[1] + vE_Cart[1] * Gamma; ux3(p) = upar * b0[2] + vE_Cart[2] * Gamma; - } else if (pusher == PrtlPusher::BORIS) { + } else if (pusher & ParticlePusher::BORIS) { real_t COEFF { coeff }; e0[0] *= COEFF; @@ -742,7 +737,7 @@ namespace kernel::sr { ux1(p) = u0[0]; ux2(p) = u0[1]; ux3(p) = u0[2]; - } else if (pusher == PrtlPusher::VAY) { + } else if (pusher & ParticlePusher::VAY) { auto COEFF { coeff }; e0[0] *= COEFF; e0[1] *= COEFF; diff --git a/src/kernels/tests/ext_force.cpp b/src/kernels/tests/ext_force.cpp index 21d57b24..781d68b1 100644 --- a/src/kernels/tests/ext_force.cpp +++ b/src/kernels/tests/ext_force.cpp @@ -183,21 +183,23 @@ void testPusher(const std::vector& res) { Kokkos::parallel_for( "pusher", CreateRangePolicy({0}, {2}), - kernel::sr::Pusher_kernel, decltype(force)>(PrtlPusher::BORIS, - false, true, RadiativeDrag::NONE, - emfield, - sp, - i1, i2, i3, - i1_prev, i2_prev, i3_prev, - dx1, dx2, dx3, - dx1_prev, dx2_prev, dx3_prev, - ux1, ux2, ux3, - phi, tag, - metric, force, - (simtime_t)time, coeff, dt, - nx1, nx2, nx3, - boundaries, - ZERO, ZERO, ZERO, ZERO)); + kernel::sr::Pusher_kernel, decltype(force)>( + ParticlePusher::BORIS, + true, RadiativeDrag::NONE, + emfield, + sp, + i1, i2, i3, + i1_prev, i2_prev, i3_prev, + dx1, dx2, dx3, + dx1_prev, dx2_prev, dx3_prev, + ux1, ux2, ux3, + phi, tag, + metric, force, + (simtime_t)time, coeff, dt, + nx1, nx2, nx3, + boundaries, + ZERO, ZERO, ZERO, ZERO)); + // clang-format on auto i1_prev_ = Kokkos::create_mirror_view(i1_prev); auto i2_prev_ = Kokkos::create_mirror_view(i2_prev); @@ -236,16 +238,18 @@ void testPusher(const std::vector& res) { i1_(1), i2_(1), i3_(1), - dx1_( 1), - dx2_( 1), - dx3_( 1), - ux1_( 1), - ux2_( 1), - ux3_( 1)); + dx1_(1), + dx2_(1), + dx3_(1), + ux1_(1), + ux2_(1), + ux3_(1)); { - const real_t ux1_expect = ux1_0 + (time + dt) * f_mag * std::sin(ONE) * std::sin(ONE); - const real_t ux2_expect = ux2_0 + (time + dt) * f_mag * std::sin(ONE) * std::cos(ONE); + const real_t ux1_expect = ux1_0 + (time + dt) * f_mag * std::sin(ONE) * + std::sin(ONE); + const real_t ux2_expect = ux2_0 + (time + dt) * f_mag * std::sin(ONE) * + std::cos(ONE); const real_t ux3_expect = ux3_0 + (time + dt) * f_mag * std::cos(ONE); check_value(t, ux1_(0), ux1_expect, eps, "Particle #1 ux1"); @@ -254,15 +258,16 @@ void testPusher(const std::vector& res) { } { - const real_t ux1_expect = -ux1_0 + (time + dt) * f_mag * std::sin(ONE) * std::sin(ONE); - const real_t ux2_expect = -ux2_0 + (time + dt) * f_mag * std::sin(ONE) * std::cos(ONE); + const real_t ux1_expect = -ux1_0 + (time + dt) * f_mag * std::sin(ONE) * + std::sin(ONE); + const real_t ux2_expect = -ux2_0 + (time + dt) * f_mag * std::sin(ONE) * + std::cos(ONE); const real_t ux3_expect = -ux3_0 + (time + dt) * f_mag * std::cos(ONE); check_value(t, ux1_(1), ux1_expect, eps, "Particle #2 ux1"); check_value(t, ux2_(1), ux2_expect, eps, "Particle #2 ux2"); check_value(t, ux3_(1), ux3_expect, eps, "Particle #2 ux3"); } - } } diff --git a/src/kernels/tests/gca_pusher.cpp b/src/kernels/tests/gca_pusher.cpp index 405aa193..eb071f3b 100644 --- a/src/kernels/tests/gca_pusher.cpp +++ b/src/kernels/tests/gca_pusher.cpp @@ -159,8 +159,8 @@ void testPusher(const std::vector& res) { Kokkos::parallel_for( "pusher", CreateRangePolicy({0}, {2}), - kernel::sr::Pusher_kernel>(PrtlPusher::BORIS, - true, false, RadiativeDrag::NONE, + kernel::sr::Pusher_kernel>(ParticlePusher::BORIS | ParticlePusher::GCA, + false, RadiativeDrag::NONE, emfield, sp, i1, i2, i3, diff --git a/src/kernels/tests/prtl_bc.cpp b/src/kernels/tests/prtl_bc.cpp index aea08943..3041b374 100644 --- a/src/kernels/tests/prtl_bc.cpp +++ b/src/kernels/tests/prtl_bc.cpp @@ -50,7 +50,6 @@ void testPeriodicBC(const std::vector& res, errorIf(res.size() != M::Dim, "res.size() != M::Dim"); errorIf(M::CoordType != Coord::Cart, "M::CoordType != Coord::Cart"); // aliases - const auto NoGCA = false; const auto NoExtForce = false; real_t sx = ZERO, sy = ZERO, sz = ZERO; @@ -245,8 +244,8 @@ void testPeriodicBC(const std::vector& res, // clang-format off Kokkos::parallel_for( "pusher", CreateRangePolicy({ 0 }, { 2 }), - kernel::sr::Pusher_kernel(PrtlPusher::BORIS, - NoGCA, NoExtForce, RadiativeDrag::NONE, + kernel::sr::Pusher_kernel(ParticlePusher::BORIS, + NoExtForce, RadiativeDrag::NONE, emfield, sp_idx, i1, i2, i3, diff --git a/src/kernels/tests/pusher.cpp b/src/kernels/tests/pusher.cpp index 8c783efe..578b38f2 100644 --- a/src/kernels/tests/pusher.cpp +++ b/src/kernels/tests/pusher.cpp @@ -161,8 +161,8 @@ void testPusher(const std::vector& res) { Kokkos::parallel_for( "pusher", CreateRangePolicy({0}, {1}), - kernel::sr::Pusher_kernel>(PrtlPusher::BORIS, - false, false, RadiativeDrag::NONE, + kernel::sr::Pusher_kernel>(ParticlePusher::BORIS, + false, RadiativeDrag::NONE, emfield, sp, i1, i2, i3, @@ -180,8 +180,8 @@ void testPusher(const std::vector& res) { Kokkos::parallel_for( "pusher", CreateRangePolicy({1}, {2}), - kernel::sr::Pusher_kernel>(PrtlPusher::VAY, - false, false, RadiativeDrag::NONE, + kernel::sr::Pusher_kernel>(ParticlePusher::VAY, + false, RadiativeDrag::NONE, emfield, sp, i1, i2, i3, From 68a6a110621a95d43d6c06535c9975418ebd9e89 Mon Sep 17 00:00:00 2001 From: haykh Date: Mon, 12 Jan 2026 10:20:56 -0500 Subject: [PATCH 10/21] minor str issue for clang --- src/framework/parameters/particles.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/framework/parameters/particles.cpp b/src/framework/parameters/particles.cpp index fd445a37..87e50d9e 100644 --- a/src/framework/parameters/particles.cpp +++ b/src/framework/parameters/particles.cpp @@ -37,7 +37,7 @@ namespace ntt { flags |= RadiativeDrag::COMPTON; } else { raise::Error(fmt::format("Invalid radiative_drag value: %s", - radiative_drag_str), + radiative_drag_str.c_str()), HERE); } } @@ -66,7 +66,7 @@ namespace ntt { flags |= ParticlePusher::GCA; } else { raise::Error( - fmt::format("Invalid pusher value: %s", particle_pusher_str), + fmt::format("Invalid pusher value: %s", particle_pusher_str.c_str()), HERE); } } From bf7ff96c026ca7e4c20164fed2c444d0edd973e0 Mon Sep 17 00:00:00 2001 From: haykh Date: Mon, 12 Jan 2026 17:41:05 -0500 Subject: [PATCH 11/21] pusher kernel sr constructor simplified (CPUTEST) --- dev/nix/kokkos.nix | 5 +- src/engines/srpic.hpp | 218 +++++++---------- src/kernels/particle_pusher_sr.hpp | 381 ++++++++++++++--------------- src/kernels/tests/ext_force.cpp | 76 +++--- src/kernels/tests/gca_pusher.cpp | 73 +++--- src/kernels/tests/prtl_bc.cpp | 58 +++-- src/kernels/tests/pusher.cpp | 91 +++---- 7 files changed, 441 insertions(+), 461 deletions(-) diff --git a/dev/nix/kokkos.nix b/dev/nix/kokkos.nix index d8ae115c..fc51647b 100644 --- a/dev/nix/kokkos.nix +++ b/dev/nix/kokkos.nix @@ -17,15 +17,16 @@ let rocprim rocminfo rocm-smi + pkgs.llvmPackages_19.clang-tools ]; "CUDA" = with pkgs.cudaPackages; [ - llvmPackages_18.clang-tools + llvmPackages_19.clang-tools cudatoolkit cuda_cudart pkgs.gcc13 ]; "NONE" = [ - pkgs.llvmPackages_18.clang-tools + pkgs.llvmPackages_19.clang-tools pkgs.gcc13 ]; }; diff --git a/src/engines/srpic.hpp b/src/engines/srpic.hpp index 129a0a1d..f31afbce 100644 --- a/src/engines/srpic.hpp +++ b/src/engines/srpic.hpp @@ -322,49 +322,63 @@ namespace ntt { species.label().c_str(), species.npart()), HERE); - const auto q_ovr_m = species.mass() > ZERO - ? species.charge() / species.mass() - : ZERO; - // coeff = q / m (dt / 2) omegaB0 - const auto coeff = q_ovr_m * HALF * dt * - m_params.template get("scales.omegaB0"); - const auto radiative_drag_flags = species.radiative_drag_flags(); - - // coefficients to be forwarded to the dispatcher - // gca - const auto has_gca = species.pusher() & ParticlePusher::GCA; - const auto gca_larmor_max = has_gca ? m_params.template get( - "algorithms.gca.larmor_max") - : ZERO; - const auto gca_eovrb_max = has_gca ? m_params.template get( - "algorithms.gca.e_ovr_b_max") - : ZERO; - // radiative drag - const auto has_synchrotron = (radiative_drag_flags & - RadiativeDrag::SYNCHROTRON); - const auto has_compton = (radiative_drag_flags & RadiativeDrag::COMPTON); - const auto sync_grad = has_synchrotron - ? m_params.template get( - "algorithms.synchrotron.gamma_rad") - : ZERO; - const auto sync_coeff = has_synchrotron - ? (real_t)(0.1) * dt * - m_params.template get( - "scales.omegaB0") / - (SQR(sync_grad) * species.mass()) - : ZERO; - const auto compton_grad = has_compton - ? m_params.template get( - "algorithms.compton.gamma_rad") - : ZERO; - const auto compton_coeff = has_compton - ? (real_t)(0.1) * dt * - m_params.template get( - "scales.omegaB0") / - (SQR(compton_grad) * species.mass()) - : ZERO; + + kernel::sr::PusherParams pusher_params {}; + pusher_params.pusher_flags = species.pusher(); + pusher_params.radiative_drag_flags = species.radiative_drag_flags(); + pusher_params.mass = species.mass(); + pusher_params.charge = species.charge(); + pusher_params.time = time; + pusher_params.dt = dt; + pusher_params.omegaB0 = m_params.template get("scales.omegaB0"); + pusher_params.ni1 = domain.mesh.n_active(in::x1); + pusher_params.ni2 = domain.mesh.n_active(in::x2); + pusher_params.ni3 = domain.mesh.n_active(in::x3); + pusher_params.boundaries = domain.mesh.prtl_bc(); + + if (species.pusher() & ParticlePusher::GCA) { + pusher_params.gca_params.set( + "larmor_max", + m_params.template get("algorithms.gca.larmor_max")); + pusher_params.gca_params.set( + "e_ovr_b_max", + m_params.template get("algorithms.gca.e_ovr_b_max")); + } + + if (species.radiative_drag_flags() & RadiativeDrag::SYNCHROTRON) { + pusher_params.radiative_drag_params.set( + "synchrotron_gamma_rad", + m_params.template get("algorithms.synchrotron.gamma_rad")); + } + + if (species.radiative_drag_flags() & RadiativeDrag::COMPTON) { + pusher_params.radiative_drag_params.set( + "compton_gamma_rad", + m_params.template get("algorithms.compton.gamma_rad")); + } + + kernel::sr::PusherArrays pusher_arrays {}; + pusher_arrays.sp = species.index(); + pusher_arrays.i1 = species.i1; + pusher_arrays.i2 = species.i2; + pusher_arrays.i3 = species.i3; + pusher_arrays.i1_prev = species.i1_prev; + pusher_arrays.i2_prev = species.i2_prev; + pusher_arrays.i3_prev = species.i3_prev; + pusher_arrays.dx1 = species.dx1; + pusher_arrays.dx2 = species.dx2; + pusher_arrays.dx3 = species.dx3; + pusher_arrays.dx1_prev = species.dx1_prev; + pusher_arrays.dx2_prev = species.dx2_prev; + pusher_arrays.dx3_prev = species.dx3_prev; + pusher_arrays.ux1 = species.ux1; + pusher_arrays.ux2 = species.ux2; + pusher_arrays.ux3 = species.ux3; + pusher_arrays.phi = species.phi; + pusher_arrays.tag = species.tag; + // toggle to indicate whether pgen defines the external force - bool has_extforce = false; + bool has_extforce = false; if constexpr (traits::pgen::HasExtForce) { has_extforce = true; // toggle to indicate whether the ext force applies to current species @@ -376,60 +390,30 @@ namespace ntt { } } - // clang-format off + pusher_params.ext_force = has_extforce; + if (not has_atmosphere and not has_extforce) { - Kokkos::parallel_for( - "ParticlePusher", - species.rangeActiveParticles(), - kernel::sr::Pusher_kernel( - species.pusher(), false, - radiative_drag_flags, - domain.fields.em, - species.index(), - species.i1, species.i2, species.i3, - species.i1_prev, species.i2_prev, species.i3_prev, - species.dx1, species.dx2, species.dx3, - species.dx1_prev, species.dx2_prev, species.dx3_prev, - species.ux1, species.ux2, species.ux3, - species.phi, species.tag, - domain.mesh.metric, - time, coeff, dt, - domain.mesh.n_active(in::x1), - domain.mesh.n_active(in::x2), - domain.mesh.n_active(in::x3), - domain.mesh.prtl_bc(), - gca_larmor_max, gca_eovrb_max, sync_coeff, compton_coeff - )); + Kokkos::parallel_for("ParticlePusher", + species.rangeActiveParticles(), + kernel::sr::Pusher_kernel(pusher_params, + pusher_arrays, + domain.fields.em, + domain.mesh.metric)); } else if (has_atmosphere and not has_extforce) { const auto force = kernel::sr::Force { - {gx1, gx2, gx3}, + { gx1, gx2, gx3 }, x_surf, ds - }; + }; Kokkos::parallel_for( "ParticlePusher", species.rangeActiveParticles(), - kernel::sr::Pusher_kernel( - species.pusher(), false, - radiative_drag_flags, - domain.fields.em, - species.index(), - species.i1, species.i2, species.i3, - species.i1_prev, species.i2_prev, species.i3_prev, - species.dx1, species.dx2, species.dx3, - species.dx1_prev, species.dx2_prev, species.dx3_prev, - species.ux1, species.ux2, species.ux3, - species.phi, species.tag, - domain.mesh.metric, - force, - time, coeff, dt, - domain.mesh.n_active(in::x1), - domain.mesh.n_active(in::x2), - domain.mesh.n_active(in::x3), - domain.mesh.prtl_bc(), - gca_larmor_max, gca_eovrb_max, sync_coeff, compton_coeff - )); + kernel::sr::Pusher_kernel(pusher_params, + pusher_arrays, + domain.fields.em, + domain.mesh.metric, + force)); } else if (not has_atmosphere and has_extforce) { if constexpr (traits::pgen::HasExtForce) { const auto force = @@ -439,26 +423,11 @@ namespace ntt { Kokkos::parallel_for( "ParticlePusher", species.rangeActiveParticles(), - kernel::sr::Pusher_kernel( - species.pusher(), true, - radiative_drag_flags, - domain.fields.em, - species.index(), - species.i1, species.i2, species.i3, - species.i1_prev, species.i2_prev, species.i3_prev, - species.dx1, species.dx2, species.dx3, - species.dx1_prev, species.dx2_prev, species.dx3_prev, - species.ux1, species.ux2, species.ux3, - species.phi, species.tag, - domain.mesh.metric, - force, - time, coeff, dt, - domain.mesh.n_active(in::x1), - domain.mesh.n_active(in::x2), - domain.mesh.n_active(in::x3), - domain.mesh.prtl_bc(), - gca_larmor_max, gca_eovrb_max, sync_coeff, compton_coeff - )); + kernel::sr::Pusher_kernel(pusher_params, + pusher_arrays, + domain.fields.em, + domain.mesh.metric, + force)); } else { raise::Error("External force not implemented", HERE); } @@ -466,36 +435,23 @@ namespace ntt { if constexpr (traits::pgen::HasExtForce) { const auto force = kernel::sr::Force { - m_pgen.ext_force, {gx1, gx2, gx3}, x_surf, ds - }; + m_pgen.ext_force, + { gx1, gx2, gx3 }, + x_surf, + ds + }; Kokkos::parallel_for( "ParticlePusher", species.rangeActiveParticles(), - kernel::sr::Pusher_kernel( - species.pusher(), true, - radiative_drag_flags, - domain.fields.em, - species.index(), - species.i1, species.i2, species.i3, - species.i1_prev, species.i2_prev, species.i3_prev, - species.dx1, species.dx2, species.dx3, - species.dx1_prev, species.dx2_prev, species.dx3_prev, - species.ux1, species.ux2, species.ux3, - species.phi, species.tag, - domain.mesh.metric, - force, - time, coeff, dt, - domain.mesh.n_active(in::x1), - domain.mesh.n_active(in::x2), - domain.mesh.n_active(in::x3), - domain.mesh.prtl_bc(), - gca_larmor_max, gca_eovrb_max, sync_coeff, compton_coeff - )); + kernel::sr::Pusher_kernel(pusher_params, + pusher_arrays, + domain.fields.em, + domain.mesh.metric, + force)); } else { raise::Error("External force not implemented", HERE); - } + } } - // clang-format on } } diff --git a/src/kernels/particle_pusher_sr.hpp b/src/kernels/particle_pusher_sr.hpp index 1c8901c6..b55c0760 100644 --- a/src/kernels/particle_pusher_sr.hpp +++ b/src/kernels/particle_pusher_sr.hpp @@ -22,6 +22,7 @@ #include "arch/traits.h" #include "utils/error.h" #include "utils/numeric.h" +#include "utils/param_container.h" #include "particle_shapes.hpp" @@ -33,7 +34,9 @@ /* Local macros */ /* -------------------------------------------------------------------------- */ #define from_Xi_to_i(XI, I) \ - { I = static_cast((XI + 1)) - 1; } + { \ + I = static_cast((XI + 1)) - 1; \ + } #define from_Xi_to_i_di(XI, I, DI) \ { \ @@ -175,6 +178,44 @@ namespace kernel::sr { } }; + struct PusherParams { + // pusher algorithm(s) assigned to the species + ParticlePusherFlags pusher_flags { ParticlePusher::NONE }; + // radiative drag force(s) enabled for the species + RadiativeDragFlags radiative_drag_flags { RadiativeDrag::NONE }; + + // external force (other than the atmosphere) + bool ext_force { false }; + + // species parameters + float mass, charge; + + // time variable + simtime_t time; + + // global constants + real_t dt, omegaB0; + + // grid parameters + int ni1, ni2, ni3; + boundaries_t boundaries; + + // parameters for the advanced features + prm::Parameters gca_params; + prm::Parameters radiative_drag_params; + }; + + struct PusherArrays { + spidx_t sp; + array_t i1, i2, i3; + array_t i1_prev, i2_prev, i3_prev; + array_t dx1, dx2, dx3; + array_t dx1_prev, dx2_prev, dx3_prev; + array_t ux1, ux2, ux3; + array_t phi; + array_t tag; + }; + /** * @tparam M Metric * @tparam F Additional force @@ -188,214 +229,151 @@ namespace kernel::sr { static constexpr auto ExtForce = not std::is_same::value; private: - const ParticlePusherFlags pusher; - const bool ext_force; + const ParticlePusherFlags pusher_flags; const RadiativeDragFlags radiative_drag_flags; + const bool ext_force; + const randacc_ndfield_t EB; - const spidx_t sp; - array_t i1, i2, i3; - array_t i1_prev, i2_prev, i3_prev; - array_t dx1, dx2, dx3; - array_t dx1_prev, dx2_prev, dx3_prev; - array_t ux1, ux2, ux3; - array_t phi; - array_t tag; - const M metric; - const F force; - - const real_t time, coeff, dt; - const int ni1, ni2, ni3; - bool is_absorb_i1min { false }, is_absorb_i1max { false }; - bool is_absorb_i2min { false }, is_absorb_i2max { false }; - bool is_absorb_i3min { false }, is_absorb_i3max { false }; - bool is_periodic_i1min { false }, is_periodic_i1max { false }; - bool is_periodic_i2min { false }, is_periodic_i2max { false }; - bool is_periodic_i3min { false }, is_periodic_i3max { false }; - bool is_reflect_i1min { false }, is_reflect_i1max { false }; - bool is_reflect_i2min { false }, is_reflect_i2max { false }; - bool is_reflect_i3min { false }, is_reflect_i3max { false }; - bool is_axis_i2min { false }, is_axis_i2max { false }; + + const spidx_t sp; + array_t i1, i2, i3; + array_t i1_prev, i2_prev, i3_prev; + array_t dx1, dx2, dx3; + array_t dx1_prev, dx2_prev, dx3_prev; + array_t ux1, ux2, ux3; + array_t phi; + array_t tag; + const M metric; + const F force; + + const simtime_t time; + const real_t coeff, dt; + + const int ni1, ni2, ni3; + bool is_absorb_i1min { false }, is_absorb_i1max { false }; + bool is_absorb_i2min { false }, is_absorb_i2max { false }; + bool is_absorb_i3min { false }, is_absorb_i3max { false }; + bool is_periodic_i1min { false }, is_periodic_i1max { false }; + bool is_periodic_i2min { false }, is_periodic_i2max { false }; + bool is_periodic_i3min { false }, is_periodic_i3max { false }; + bool is_reflect_i1min { false }, is_reflect_i1max { false }; + bool is_reflect_i2min { false }, is_reflect_i2max { false }; + bool is_reflect_i3min { false }, is_reflect_i3max { false }; + bool is_axis_i2min { false }, is_axis_i2max { false }; + // gca parameters const real_t gca_larmor, gca_EovrB_sqr; // radiative drag parameters - const real_t coeff_sync, coeff_comp; + const real_t raddrag_coeff_synchrotron, raddrag_coeff_compton; public: - Pusher_kernel(ParticlePusherFlags pusher, - bool ext_force, - RadiativeDragFlags radiative_drag_flags, + Pusher_kernel(const PusherParams& pusher_params, + PusherArrays& pusher_arrays, const randacc_ndfield_t& EB, - spidx_t sp, - array_t& i1, - array_t& i2, - array_t& i3, - array_t& i1_prev, - array_t& i2_prev, - array_t& i3_prev, - array_t& dx1, - array_t& dx2, - array_t& dx3, - array_t& dx1_prev, - array_t& dx2_prev, - array_t& dx3_prev, - array_t& ux1, - array_t& ux2, - array_t& ux3, - array_t& phi, - array_t& tag, const M& metric, - const F& force, - real_t time, - real_t coeff, - real_t dt, - int ni1, - int ni2, - int ni3, - const boundaries_t& boundaries, - real_t gca_larmor_max, - real_t gca_eovrb_max, - real_t coeff_sync, - real_t coeff_comp) - : pusher { pusher } - , ext_force { ext_force } - , radiative_drag_flags { radiative_drag_flags } + const F& force = NoForce_t {}) + : pusher_flags { pusher_params.pusher_flags } + , radiative_drag_flags { pusher_params.radiative_drag_flags } + , ext_force { pusher_params.ext_force } , EB { EB } - , sp { sp } - , i1 { i1 } - , i2 { i2 } - , i3 { i3 } - , i1_prev { i1_prev } - , i2_prev { i2_prev } - , i3_prev { i3_prev } - , dx1 { dx1 } - , dx2 { dx2 } - , dx3 { dx3 } - , dx1_prev { dx1_prev } - , dx2_prev { dx2_prev } - , dx3_prev { dx3_prev } - , ux1 { ux1 } - , ux2 { ux2 } - , ux3 { ux3 } - , phi { phi } - , tag { tag } + , sp { pusher_arrays.sp } + , i1 { pusher_arrays.i1 } + , i2 { pusher_arrays.i2 } + , i3 { pusher_arrays.i3 } + , i1_prev { pusher_arrays.i1_prev } + , i2_prev { pusher_arrays.i2_prev } + , i3_prev { pusher_arrays.i3_prev } + , dx1 { pusher_arrays.dx1 } + , dx2 { pusher_arrays.dx2 } + , dx3 { pusher_arrays.dx3 } + , dx1_prev { pusher_arrays.dx1_prev } + , dx2_prev { pusher_arrays.dx2_prev } + , dx3_prev { pusher_arrays.dx3_prev } + , ux1 { pusher_arrays.ux1 } + , ux2 { pusher_arrays.ux2 } + , ux3 { pusher_arrays.ux3 } + , phi { pusher_arrays.phi } + , tag { pusher_arrays.tag } , metric { metric } , force { force } - , time { time } - , coeff { coeff } - , dt { dt } - , ni1 { ni1 } - , ni2 { ni2 } - , ni3 { ni3 } - , gca_larmor { gca_larmor_max } - , gca_EovrB_sqr { SQR(gca_eovrb_max) } - , coeff_sync { coeff_sync } - , coeff_comp { coeff_comp } { - raise::ErrorIf(boundaries.size() < 1, "boundaries defined incorrectly", HERE); - is_absorb_i1min = (boundaries[0].first == PrtlBC::ATMOSPHERE) || - (boundaries[0].first == PrtlBC::ABSORB); - is_absorb_i1max = (boundaries[0].second == PrtlBC::ATMOSPHERE) || - (boundaries[0].second == PrtlBC::ABSORB); - is_periodic_i1min = (boundaries[0].first == PrtlBC::PERIODIC); - is_periodic_i1max = (boundaries[0].second == PrtlBC::PERIODIC); - is_reflect_i1min = (boundaries[0].first == PrtlBC::REFLECT); - is_reflect_i1max = (boundaries[0].second == PrtlBC::REFLECT); + , time { pusher_params.time } + , coeff { HALF * (pusher_params.charge / pusher_params.mass) * + pusher_params.omegaB0 * pusher_params.dt } + , dt { pusher_params.dt } + , ni1 { pusher_params.ni1 } + , ni2 { pusher_params.ni2 } + , ni3 { pusher_params.ni3 } + , gca_larmor { (pusher_flags & ParticlePusher::GCA) + ? pusher_params.gca_params.get("larmor_max") + : ZERO } + , gca_EovrB_sqr { (pusher_flags & ParticlePusher::GCA) + ? SQR(pusher_params.gca_params.get( + "e_ovr_b_max")) + : ZERO } + , raddrag_coeff_synchrotron { (pusher_params.radiative_drag_flags & + RadiativeDrag::SYNCHROTRON) + ? static_cast(0.1) * + pusher_params.dt * pusher_params.omegaB0 / + (SQR(pusher_params + .radiative_drag_params.get( + "synchrotron_gamma_rad")) * + pusher_params.mass) + : ZERO } + , raddrag_coeff_compton { + (pusher_params.radiative_drag_flags & RadiativeDrag::COMPTON) + ? static_cast(0.1) * pusher_params.dt * pusher_params.omegaB0 / + (SQR(pusher_params.radiative_drag_params.get( + "compton_gamma_rad")) * + pusher_params.mass) + : ZERO + } { + raise::ErrorIf(pusher_flags == ParticlePusher::NONE, + "No particle pusher specified", + HERE); + raise::ErrorIf(pusher_params.boundaries.size() < 1, + "pusher_params.boundaries defined incorrectly", + HERE); + is_absorb_i1min = (pusher_params.boundaries[0].first == PrtlBC::ATMOSPHERE) || + (pusher_params.boundaries[0].first == PrtlBC::ABSORB); + is_absorb_i1max = (pusher_params.boundaries[0].second == PrtlBC::ATMOSPHERE) || + (pusher_params.boundaries[0].second == PrtlBC::ABSORB); + is_periodic_i1min = (pusher_params.boundaries[0].first == PrtlBC::PERIODIC); + is_periodic_i1max = (pusher_params.boundaries[0].second == PrtlBC::PERIODIC); + is_reflect_i1min = (pusher_params.boundaries[0].first == PrtlBC::REFLECT); + is_reflect_i1max = (pusher_params.boundaries[0].second == PrtlBC::REFLECT); if constexpr ((D == Dim::_2D) || (D == Dim::_3D)) { - raise::ErrorIf(boundaries.size() < 2, "boundaries defined incorrectly", HERE); - is_absorb_i2min = (boundaries[1].first == PrtlBC::ATMOSPHERE) || - (boundaries[1].first == PrtlBC::ABSORB); - is_absorb_i2max = (boundaries[1].second == PrtlBC::ATMOSPHERE) || - (boundaries[1].second == PrtlBC::ABSORB); - is_periodic_i2min = (boundaries[1].first == PrtlBC::PERIODIC); - is_periodic_i2max = (boundaries[1].second == PrtlBC::PERIODIC); - is_reflect_i2min = (boundaries[1].first == PrtlBC::REFLECT); - is_reflect_i2max = (boundaries[1].second == PrtlBC::REFLECT); - is_axis_i2min = (boundaries[1].first == PrtlBC::AXIS); - is_axis_i2max = (boundaries[1].second == PrtlBC::AXIS); + raise::ErrorIf(pusher_params.boundaries.size() < 2, + "pusher_params.boundaries defined incorrectly", + HERE); + is_absorb_i2min = (pusher_params.boundaries[1].first == PrtlBC::ATMOSPHERE) || + (pusher_params.boundaries[1].first == PrtlBC::ABSORB); + is_absorb_i2max = (pusher_params.boundaries[1].second == + PrtlBC::ATMOSPHERE) || + (pusher_params.boundaries[1].second == PrtlBC::ABSORB); + is_periodic_i2min = (pusher_params.boundaries[1].first == PrtlBC::PERIODIC); + is_periodic_i2max = (pusher_params.boundaries[1].second == PrtlBC::PERIODIC); + is_reflect_i2min = (pusher_params.boundaries[1].first == PrtlBC::REFLECT); + is_reflect_i2max = (pusher_params.boundaries[1].second == PrtlBC::REFLECT); + is_axis_i2min = (pusher_params.boundaries[1].first == PrtlBC::AXIS); + is_axis_i2max = (pusher_params.boundaries[1].second == PrtlBC::AXIS); } if constexpr (D == Dim::_3D) { - raise::ErrorIf(boundaries.size() < 3, "boundaries defined incorrectly", HERE); - is_absorb_i3min = (boundaries[2].first == PrtlBC::ATMOSPHERE) || - (boundaries[2].first == PrtlBC::ABSORB); - is_absorb_i3max = (boundaries[2].second == PrtlBC::ATMOSPHERE) || - (boundaries[2].second == PrtlBC::ABSORB); - is_periodic_i3min = (boundaries[2].first == PrtlBC::PERIODIC); - is_periodic_i3max = (boundaries[2].second == PrtlBC::PERIODIC); - is_reflect_i3min = (boundaries[2].first == PrtlBC::REFLECT); - is_reflect_i3max = (boundaries[2].second == PrtlBC::REFLECT); + raise::ErrorIf(pusher_params.boundaries.size() < 3, + "pusher_params.boundaries defined incorrectly", + HERE); + is_absorb_i3min = (pusher_params.boundaries[2].first == PrtlBC::ATMOSPHERE) || + (pusher_params.boundaries[2].first == PrtlBC::ABSORB); + is_absorb_i3max = (pusher_params.boundaries[2].second == + PrtlBC::ATMOSPHERE) || + (pusher_params.boundaries[2].second == PrtlBC::ABSORB); + is_periodic_i3min = (pusher_params.boundaries[2].first == PrtlBC::PERIODIC); + is_periodic_i3max = (pusher_params.boundaries[2].second == PrtlBC::PERIODIC); + is_reflect_i3min = (pusher_params.boundaries[2].first == PrtlBC::REFLECT); + is_reflect_i3max = (pusher_params.boundaries[2].second == PrtlBC::REFLECT); } } - Pusher_kernel(ParticlePusherFlags pusher, - bool ext_force, - RadiativeDragFlags radiative_drag_flags, - const ndfield_t& EB, - spidx_t sp, - array_t& i1, - array_t& i2, - array_t& i3, - array_t& i1_prev, - array_t& i2_prev, - array_t& i3_prev, - array_t& dx1, - array_t& dx2, - array_t& dx3, - array_t& dx1_prev, - array_t& dx2_prev, - array_t& dx3_prev, - array_t& ux1, - array_t& ux2, - array_t& ux3, - array_t& phi, - array_t& tag, - const M& metric, - simtime_t time, - real_t coeff, - real_t dt, - int ni1, - int ni2, - int ni3, - const boundaries_t& boundaries, - real_t gca_larmor_max, - real_t gca_eovrb_max, - real_t coeff_sync, - real_t coeff_comp) - : Pusher_kernel(pusher, - ext_force, - radiative_drag_flags, - EB, - sp, - i1, - i2, - i3, - i1_prev, - i2_prev, - i3_prev, - dx1, - dx2, - dx3, - dx1_prev, - dx2_prev, - dx3_prev, - ux1, - ux2, - ux3, - phi, - tag, - metric, - NoForce_t {}, - time, - coeff, - dt, - ni1, - ni2, - ni3, - boundaries, - gca_larmor_max, - gca_eovrb_max, - coeff_sync, - coeff_comp) {} - Inline void synchrotronDrag(index_t p, vec_t& u_prime, const vec_t& e0, @@ -442,9 +420,12 @@ namespace kernel::sr { e_plus_beta_cross_b[1], e_plus_beta_cross_b[2]) - SQR(beta_dot_e) }; - ux1(p) += coeff_sync * (kappaR[0] - gamma_prime_sqr * u_prime[0] * chiR_sqr); - ux2(p) += coeff_sync * (kappaR[1] - gamma_prime_sqr * u_prime[1] * chiR_sqr); - ux3(p) += coeff_sync * (kappaR[2] - gamma_prime_sqr * u_prime[2] * chiR_sqr); + ux1(p) += raddrag_coeff_synchrotron * + (kappaR[0] - gamma_prime_sqr * u_prime[0] * chiR_sqr); + ux2(p) += raddrag_coeff_synchrotron * + (kappaR[1] - gamma_prime_sqr * u_prime[1] * chiR_sqr); + ux3(p) += raddrag_coeff_synchrotron * + (kappaR[2] - gamma_prime_sqr * u_prime[2] * chiR_sqr); } Inline void inverseComptonDrag(index_t p, vec_t& u_prime) const { @@ -456,9 +437,9 @@ namespace kernel::sr { u_prime[2] *= gamma_prime_sqr; gamma_prime_sqr = SQR(ONE / gamma_prime_sqr); - ux1(p) -= coeff_comp * gamma_prime_sqr * u_prime[0]; - ux2(p) -= coeff_comp * gamma_prime_sqr * u_prime[1]; - ux3(p) -= coeff_comp * gamma_prime_sqr * u_prime[2]; + ux1(p) -= raddrag_coeff_compton * gamma_prime_sqr * u_prime[0]; + ux2(p) -= raddrag_coeff_compton * gamma_prime_sqr * u_prime[1]; + ux3(p) -= raddrag_coeff_compton * gamma_prime_sqr * u_prime[2]; } Inline void operator()(index_t p) const { @@ -470,7 +451,7 @@ namespace kernel::sr { } coord_t xp_Cd { ZERO }; getPrtlPos(p, xp_Cd); - if (pusher == ParticlePusher::PHOTON) { + if (pusher_flags == ParticlePusher::PHOTON) { posUpd(false, p, xp_Cd); return; } @@ -515,7 +496,7 @@ namespace kernel::sr { force.fx3(sp, time, ext_force, xp_Ph) }, force_Cart); } - if (pusher & ParticlePusher::GCA) { + if (pusher_flags & ParticlePusher::GCA) { /* hybrid GCA/conventional mode --------------------------------- */ const auto E2 { NORM_SQR(ei_Cart[0], ei_Cart[1], ei_Cart[2]) }; const auto B2 { NORM_SQR(bi_Cart[0], bi_Cart[1], bi_Cart[2]) }; @@ -710,7 +691,7 @@ namespace kernel::sr { ux1(p) = upar * b0[0] + vE_Cart[0] * Gamma; ux2(p) = upar * b0[1] + vE_Cart[1] * Gamma; ux3(p) = upar * b0[2] + vE_Cart[2] * Gamma; - } else if (pusher & ParticlePusher::BORIS) { + } else if (pusher_flags & ParticlePusher::BORIS) { real_t COEFF { coeff }; e0[0] *= COEFF; @@ -737,7 +718,7 @@ namespace kernel::sr { ux1(p) = u0[0]; ux2(p) = u0[1]; ux3(p) = u0[2]; - } else if (pusher & ParticlePusher::VAY) { + } else if (pusher_flags & ParticlePusher::VAY) { auto COEFF { coeff }; e0[0] *= COEFF; e0[1] *= COEFF; diff --git a/src/kernels/tests/ext_force.cpp b/src/kernels/tests/ext_force.cpp index 781d68b1..dbd49783 100644 --- a/src/kernels/tests/ext_force.cpp +++ b/src/kernels/tests/ext_force.cpp @@ -153,18 +153,6 @@ void testPusher(const std::vector& res) { put_value(ux3, -ux3_0, 1); put_value(tag, ParticleTag::alive, 1); - // Particle boundaries - auto boundaries = boundaries_t {}; - boundaries = { - { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, - { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, - { PrtlBC::PERIODIC, PrtlBC::PERIODIC } - }; - - const spidx_t sp { 1u }; - - const real_t coeff = HALF * dt * omegaB0; - const real_t eps = std::is_same_v ? 1e-4 : 1e-6; const auto ext_force = Force { f_mag }; @@ -176,30 +164,54 @@ void testPusher(const std::vector& res) { plog::init(plog::verbose, &file_appender); PLOGD << "t,i1,i2,i3,dx1,dx2,dx3,ux1,ux2,ux3"; + kernel::sr::PusherParams pusher_params {}; + pusher_params.pusher_flags = ParticlePusher::BORIS; + pusher_params.mass = ONE; + pusher_params.charge = ONE; + pusher_params.dt = dt; + pusher_params.omegaB0 = omegaB0; + pusher_params.ni1 = nx1; + pusher_params.ni2 = nx2; + pusher_params.ni3 = nx3; + pusher_params.boundaries = { + { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, + { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, + { PrtlBC::PERIODIC, PrtlBC::PERIODIC } + }; + pusher_params.ext_force = true; + + kernel::sr::PusherArrays pusher_arrays {}; + pusher_arrays.sp = 1u; + pusher_arrays.i1 = i1; + pusher_arrays.i2 = i2; + pusher_arrays.i3 = i3; + pusher_arrays.i1_prev = i1_prev; + pusher_arrays.i2_prev = i2_prev; + pusher_arrays.i3_prev = i3_prev; + pusher_arrays.dx1 = dx1; + pusher_arrays.dx2 = dx2; + pusher_arrays.dx3 = dx3; + pusher_arrays.dx1_prev = dx1_prev; + pusher_arrays.dx2_prev = dx2_prev; + pusher_arrays.dx3_prev = dx3_prev; + pusher_arrays.ux1 = ux1; + pusher_arrays.ux2 = ux2; + pusher_arrays.ux3 = ux3; + pusher_arrays.phi = phi; + pusher_arrays.tag = tag; + for (auto t { 0u }; t < 100; ++t) { - const real_t time = t * dt; + const real_t time = t * dt; + pusher_params.time = time; - // clang-format off Kokkos::parallel_for( "pusher", - CreateRangePolicy({0}, {2}), - kernel::sr::Pusher_kernel, decltype(force)>( - ParticlePusher::BORIS, - true, RadiativeDrag::NONE, - emfield, - sp, - i1, i2, i3, - i1_prev, i2_prev, i3_prev, - dx1, dx2, dx3, - dx1_prev, dx2_prev, dx3_prev, - ux1, ux2, ux3, - phi, tag, - metric, force, - (simtime_t)time, coeff, dt, - nx1, nx2, nx3, - boundaries, - ZERO, ZERO, ZERO, ZERO)); - // clang-format on + CreateRangePolicy({ 0 }, { 2 }), + kernel::sr::Pusher_kernel, decltype(force)>(pusher_params, + pusher_arrays, + emfield, + metric, + force)); auto i1_prev_ = Kokkos::create_mirror_view(i1_prev); auto i2_prev_ = Kokkos::create_mirror_view(i2_prev); diff --git a/src/kernels/tests/gca_pusher.cpp b/src/kernels/tests/gca_pusher.cpp index eb071f3b..66c05d7f 100644 --- a/src/kernels/tests/gca_pusher.cpp +++ b/src/kernels/tests/gca_pusher.cpp @@ -140,44 +140,59 @@ void testPusher(const std::vector& res) { put_value(ux3, -ux3_0, 1); put_value(tag, ParticleTag::alive, 1); - // Particle boundaries - auto boundaries = boundaries_t {}; - boundaries = { + const real_t eps = std::is_same_v ? 1e-3 : 1e-6; + + kernel::sr::PusherParams pusher_params {}; + pusher_params.pusher_flags = ParticlePusher::BORIS | ParticlePusher::GCA; + pusher_params.mass = ONE; + pusher_params.charge = ONE; + pusher_params.dt = dt; + pusher_params.omegaB0 = omegaB0; + pusher_params.ni1 = nx1; + pusher_params.ni2 = nx2; + pusher_params.ni3 = nx3; + pusher_params.boundaries = { { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, { PrtlBC::PERIODIC, PrtlBC::PERIODIC } }; - - const spidx_t sp { 1u }; - - const real_t coeff = HALF * dt * omegaB0; - - const real_t eps = std::is_same_v ? 1e-3 : 1e-6; + pusher_params.gca_params.set("larmor_max", (real_t)10000.0); + pusher_params.gca_params.set("e_ovr_b_max", ONE); + + kernel::sr::PusherArrays pusher_arrays {}; + pusher_arrays.sp = 1u; + pusher_arrays.i1 = i1; + pusher_arrays.i2 = i2; + pusher_arrays.i3 = i3; + pusher_arrays.i1_prev = i1_prev; + pusher_arrays.i2_prev = i2_prev; + pusher_arrays.i3_prev = i3_prev; + pusher_arrays.dx1 = dx1; + pusher_arrays.dx2 = dx2; + pusher_arrays.dx3 = dx3; + pusher_arrays.dx1_prev = dx1_prev; + pusher_arrays.dx2_prev = dx2_prev; + pusher_arrays.dx3_prev = dx3_prev; + pusher_arrays.ux1 = ux1; + pusher_arrays.ux2 = ux2; + pusher_arrays.ux3 = ux3; + pusher_arrays.phi = phi; + pusher_arrays.tag = tag; for (auto t { 0u }; t < 2000; ++t) { - // clang-format off + pusher_params.time = t * dt; + Kokkos::parallel_for( "pusher", - CreateRangePolicy({0}, {2}), - kernel::sr::Pusher_kernel>(ParticlePusher::BORIS | ParticlePusher::GCA, - false, RadiativeDrag::NONE, + CreateRangePolicy({ 0 }, { 2 }), + kernel::sr::Pusher_kernel>(pusher_params, + pusher_arrays, emfield, - sp, - i1, i2, i3, - i1_prev, i2_prev, i3_prev, - dx1, dx2, dx3, - dx1_prev, dx2_prev, dx3_prev, - ux1, ux2, ux3, - phi, tag, - metric, - ZERO, coeff, dt, - nx1, nx2, nx3, - boundaries, - (real_t)10000.0, ONE, ZERO, ZERO)); - - auto ux1_ = Kokkos::create_mirror_view(ux1); - auto ux2_ = Kokkos::create_mirror_view(ux2); - auto ux3_ = Kokkos::create_mirror_view(ux3); + metric)); + + auto ux1_ = Kokkos::create_mirror_view(ux1); + auto ux2_ = Kokkos::create_mirror_view(ux2); + auto ux3_ = Kokkos::create_mirror_view(ux3); Kokkos::deep_copy(ux1_, ux1); Kokkos::deep_copy(ux2_, ux2); Kokkos::deep_copy(ux3_, ux3); diff --git a/src/kernels/tests/prtl_bc.cpp b/src/kernels/tests/prtl_bc.cpp index 3041b374..0da12749 100644 --- a/src/kernels/tests/prtl_bc.cpp +++ b/src/kernels/tests/prtl_bc.cpp @@ -229,37 +229,49 @@ void testPeriodicBC(const std::vector& res, put_value(tag, ParticleTag::alive, prtl_idx); } - // Particle boundaries - auto boundaries = boundaries_t {}; - boundaries = { + real_t time = ZERO; + const auto n_iter = 100; + + kernel::sr::PusherParams pusher_params {}; + pusher_params.pusher_flags = ParticlePusher::BORIS; + pusher_params.mass = ONE; + pusher_params.charge = ONE; + pusher_params.dt = dt; + pusher_params.omegaB0 = ONE; + pusher_params.ni1 = nx1; + pusher_params.ni2 = nx2; + pusher_params.ni3 = nx3; + pusher_params.boundaries = { { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, { PrtlBC::PERIODIC, PrtlBC::PERIODIC } }; - real_t time = ZERO; - const auto n_iter = 100; + kernel::sr::PusherArrays pusher_arrays {}; + pusher_arrays.sp = 1u; + pusher_arrays.i1 = i1; + pusher_arrays.i2 = i2; + pusher_arrays.i3 = i3; + pusher_arrays.i1_prev = i1_prev; + pusher_arrays.i2_prev = i2_prev; + pusher_arrays.i3_prev = i3_prev; + pusher_arrays.dx1 = dx1; + pusher_arrays.dx2 = dx2; + pusher_arrays.dx3 = dx3; + pusher_arrays.dx1_prev = dx1_prev; + pusher_arrays.dx2_prev = dx2_prev; + pusher_arrays.dx3_prev = dx3_prev; + pusher_arrays.ux1 = ux1; + pusher_arrays.ux2 = ux2; + pusher_arrays.ux3 = ux3; + pusher_arrays.phi = phi; + pusher_arrays.tag = tag; for (auto n { 0 }; n < n_iter; ++n) { - // clang-format off Kokkos::parallel_for( - "pusher", CreateRangePolicy({ 0 }, { 2 }), - kernel::sr::Pusher_kernel(ParticlePusher::BORIS, - NoExtForce, RadiativeDrag::NONE, - emfield, - sp_idx, - i1, i2, i3, - i1_prev, i2_prev, i3_prev, - dx1, dx2, dx3, - dx1_prev, dx2_prev, dx3_prev, - ux1, ux2, ux3, - phi, tag, - metric, - time, coeff, dt, - nx1, nx2, nx3, - boundaries, - ZERO, ZERO, ZERO, ZERO)); - // clang-format on + "pusher", + CreateRangePolicy({ 0 }, { 2 }), + kernel::sr::Pusher_kernel(pusher_params, pusher_arrays, emfield, metric)); auto i1_ = Kokkos::create_mirror_view(i1); auto i2_ = Kokkos::create_mirror_view(i2); auto i3_ = Kokkos::create_mirror_view(i3); diff --git a/src/kernels/tests/pusher.cpp b/src/kernels/tests/pusher.cpp index 578b38f2..4e90ce7a 100644 --- a/src/kernels/tests/pusher.cpp +++ b/src/kernels/tests/pusher.cpp @@ -136,17 +136,6 @@ void testPusher(const std::vector& res) { put_value(tag, ParticleTag::alive, 1); // Particle boundaries - auto boundaries = boundaries_t {}; - boundaries = { - { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, - { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, - { PrtlBC::PERIODIC, PrtlBC::PERIODIC } - }; - - const spidx_t sp { 1u }; - - const real_t coeff = HALF * dt * omegaB0; - const auto u0_dot_b = (ux1_0 * bx1 + ux2_0 * bx2 + ux3_0 * bx3) / b_mag; const auto u0_cross_b_x1 = (ux2_0 * bx3 - ux3_0 * bx2) / b_mag; const auto u0_cross_b_x2 = (ux3_0 * bx1 - ux1_0 * bx3) / b_mag; @@ -154,47 +143,62 @@ void testPusher(const std::vector& res) { const real_t eps = std::is_same_v ? 1e-2 : 1e-3; + kernel::sr::PusherParams pusher_params {}; + pusher_params.mass = ONE; + pusher_params.charge = ONE; + pusher_params.dt = dt; + pusher_params.omegaB0 = omegaB0; + pusher_params.ni1 = nx1; + pusher_params.ni2 = nx2; + pusher_params.ni3 = nx3; + pusher_params.boundaries = { + { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, + { PrtlBC::PERIODIC, PrtlBC::PERIODIC }, + { PrtlBC::PERIODIC, PrtlBC::PERIODIC } + }; + + kernel::sr::PusherArrays pusher_arrays {}; + pusher_arrays.sp = 1u; + pusher_arrays.i1 = i1; + pusher_arrays.i2 = i2; + pusher_arrays.i3 = i3; + pusher_arrays.i1_prev = i1_prev; + pusher_arrays.i2_prev = i2_prev; + pusher_arrays.i3_prev = i3_prev; + pusher_arrays.dx1 = dx1; + pusher_arrays.dx2 = dx2; + pusher_arrays.dx3 = dx3; + pusher_arrays.dx1_prev = dx1_prev; + pusher_arrays.dx2_prev = dx2_prev; + pusher_arrays.dx3_prev = dx3_prev; + pusher_arrays.ux1 = ux1; + pusher_arrays.ux2 = ux2; + pusher_arrays.ux3 = ux3; + pusher_arrays.phi = phi; + pusher_arrays.tag = tag; + for (auto t { 0u }; t < 2000; ++t) { - const real_t time = t * dt; + const real_t time = t * dt; + pusher_params.time = time; - // clang-format off + pusher_params.pusher_flags = ParticlePusher::BORIS; Kokkos::parallel_for( "pusher", - CreateRangePolicy({0}, {1}), - kernel::sr::Pusher_kernel>(ParticlePusher::BORIS, - false, RadiativeDrag::NONE, + CreateRangePolicy({ 0 }, { 1 }), + kernel::sr::Pusher_kernel>(pusher_params, + pusher_arrays, emfield, - sp, - i1, i2, i3, - i1_prev, i2_prev, i3_prev, - dx1, dx2, dx3, - dx1_prev, dx2_prev, dx3_prev, - ux1, ux2, ux3, - phi, tag, - metric, - ZERO, coeff, dt, - nx1, nx2, nx3, - boundaries, - ZERO, ZERO, ZERO, ZERO)); + metric)); + + pusher_params.pusher_flags = ParticlePusher::VAY; Kokkos::parallel_for( "pusher", - CreateRangePolicy({1}, {2}), - kernel::sr::Pusher_kernel>(ParticlePusher::VAY, - false, RadiativeDrag::NONE, + CreateRangePolicy({ 1 }, { 2 }), + kernel::sr::Pusher_kernel>(pusher_params, + pusher_arrays, emfield, - sp, - i1, i2, i3, - i1_prev, i2_prev, i3_prev, - dx1, dx2, dx3, - dx1_prev, dx2_prev, dx3_prev, - ux1, ux2, ux3, - phi, tag, - metric, - ZERO, coeff, dt, - nx1, nx2, nx3, - boundaries, - ZERO, ZERO, ZERO, ZERO)); + metric)); auto i1_prev_ = Kokkos::create_mirror_view(i1_prev); auto i2_prev_ = Kokkos::create_mirror_view(i2_prev); @@ -252,7 +256,6 @@ void testPusher(const std::vector& res) { check_value(t, ux1_(1), ux1_expect, eps, "Particle #2 ux1"); check_value(t, ux2_(1), ux2_expect, eps, "Particle #2 ux2"); check_value(t, ux3_(1), ux3_expect, eps, "Particle #2 ux3"); - } } From 727c023241c2a4766e650c4e7e30828b6cca7010 Mon Sep 17 00:00:00 2001 From: haykh Date: Mon, 19 Jan 2026 16:41:11 -0500 Subject: [PATCH 12/21] radiation drag & emission policy separated --- input.example.toml | 79 ++++-- src/engines/srpic.hpp | 5 +- src/framework/CMakeLists.txt | 2 + src/framework/containers/particles.cpp | 2 + src/framework/containers/particles.h | 7 +- src/framework/containers/species.h | 16 +- src/framework/parameters/algorithms.cpp | 24 -- src/framework/parameters/algorithms.h | 2 +- src/framework/parameters/extra.cpp | 115 +++++++++ src/framework/parameters/extra.h | 50 ++++ src/framework/parameters/parameters.cpp | 20 +- src/framework/parameters/particles.cpp | 90 +++++-- src/framework/tests/parameters.cpp | 312 +++++++++++++----------- src/framework/tests/particles.cpp | 30 ++- src/global/defaults.h | 8 +- src/global/enums.h | 27 ++ src/global/utils/numeric.h | 4 +- src/kernels/emission.hpp | 29 +++ src/kernels/particle_pusher_sr.hpp | 65 ++--- 19 files changed, 641 insertions(+), 246 deletions(-) create mode 100644 src/framework/parameters/extra.cpp create mode 100644 src/framework/parameters/extra.h create mode 100644 src/kernels/emission.hpp diff --git a/input.example.toml b/input.example.toml index 3766a55f..7d0d73f1 100644 --- a/input.example.toml +++ b/input.example.toml @@ -195,6 +195,59 @@ # @from: `scales.larmor0` # @value: `1 / larmor0` +[radiation] + [radiation.drag] + [radiation.drag.synchrotron] + # Radiation reaction limit gamma-factor for synchrotron + # @type: float [> 0.0] + # @default: 1.0 + # @note: [required] if one of the species has `radiative_drag = "synchrotron"` + gamma_rad = "" + + [radiation.drag.compton] + # Radiation reaction limit gamma-factor for Compton drag + # @type: float [> 0.0] + # @default: 1.0 + # @note: [required] if one of the species has `radiative_drag = "compton"` + gamma_rad = "" + + [radiation.emission] + [radiation.emission.synchrotron] + # Gamma-factor of a particle emitting synchrotron photons at energy `m0 c^2` in fiducial magnetic field `B0` + # @type: float [> 1.0] + # @default: 10.0 + gamma_qed = "" + # Minimum photon energy for synchrotron emission (units of `m0 c^2`) + # @type: float [> 0.0] + # @default: 1e-4 + photon_energy_min = "" + # Weights for the emitted synchrotron photons + # @type: float [> 0.0] + # @default: 1.0 + photon_weight = "" + # Index of species for the emitted photon + # @type: ushort [> 0] + # @required + photon_species = "" + + [radiation.emission.compton] + # Gamma-factor of a particle emitting inverse Compton photons at energy `m0 c^2` in fiducial magnetic field `B0` + # @type: float [> 1.0] + # @default: 10.0 + gamma_qed = "" + # Minimum photon energy for inverse Compton emission (units of `m0 c^2`) + # @type: float [> 0.0] + # @default: 1e-4 + photon_energy_min = "" + # Weights for the emitted inverse Compton photons + # @type: float [> 0.0] + # @default: 1.0 + photon_weight = "" + # Index of species for the emitted photon + # @type: ushort [> 0] + # @required + photon_species = "" + [algorithms] # Number of current smoothing passes # @type: ushort [>= 0] @@ -252,23 +305,13 @@ # @note: When `larmor_max` == 0, the limit is disabled larmor_max = "" - [algorithms.synchrotron] - # Radiation reaction limit gamma-factor for synchrotron - # @type: float [> 0.0] - # @default: 1.0 - # @note: [required] if one of the species has `radiative_drag = "synchrotron"` - gamma_rad = "" - - [algorithms.compton] - # Radiation reaction limit gamma-factor for Compton drag - # @type: float [> 0.0] - # @default: 1.0 - # @note: [required] if one of the species has `radiative_drag = "compton"` - gamma_rad = "" - # Stencil coefficients for the field solver [notation as in Blinne+ (2018)] # @note: Standard Yee solver: `delta_i = beta_ij = 0.0` [algorithms.fieldsolver] + # Enable the fieldsolver + # @type: bool + # @default: true + enable = "" # delta_x coefficient (for `F_{i +/- 3/2, j, k}`) # @type: float # @default: 0.0 @@ -377,7 +420,15 @@ # @default: "None" # @enum: "None", "Synchrotron", "Compton" # @note: Can also be coma-separated combination, e.g., "Synchrotron,Compton" + # @note: Relevant radiation.drag parameters should also be provided radiative_drag = "" + # Particle emission policy for the species + # @type: string + # @default: "None" + # @enum: "None", "Synchrotron", "Compton", "StrongFieldPP" + # @note: Only one emission mechanism allowed + # @note: Appropriate radiation drag flag will be applied automatically (unless explicitly set to "None") + emission = "" # Parameters for specific problem generators and setups [setup] diff --git a/src/engines/srpic.hpp b/src/engines/srpic.hpp index f31afbce..f78b0745 100644 --- a/src/engines/srpic.hpp +++ b/src/engines/srpic.hpp @@ -348,13 +348,14 @@ namespace ntt { if (species.radiative_drag_flags() & RadiativeDrag::SYNCHROTRON) { pusher_params.radiative_drag_params.set( "synchrotron_gamma_rad", - m_params.template get("algorithms.synchrotron.gamma_rad")); + m_params.template get( + "radiation.drag.synchrotron.gamma_rad")); } if (species.radiative_drag_flags() & RadiativeDrag::COMPTON) { pusher_params.radiative_drag_params.set( "compton_gamma_rad", - m_params.template get("algorithms.compton.gamma_rad")); + m_params.template get("radiation.drag.compton.gamma_rad")); } kernel::sr::PusherArrays pusher_arrays {}; diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index 2b1bc51c..907b04f2 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -9,6 +9,7 @@ # * parameters/grid.cpp # * parameters/output.cpp # * parameters/algorithms.cpp +# * parameters/extra.cpp # * simulation.cpp # * domain/grid.cpp # * domain/metadomain.cpp @@ -46,6 +47,7 @@ set(SOURCES ${SRC_DIR}/parameters/grid.cpp ${SRC_DIR}/parameters/output.cpp ${SRC_DIR}/parameters/algorithms.cpp + ${SRC_DIR}/parameters/extra.cpp ${SRC_DIR}/domain/grid.cpp ${SRC_DIR}/domain/metadomain.cpp ${SRC_DIR}/domain/communications.cpp diff --git a/src/framework/containers/particles.cpp b/src/framework/containers/particles.cpp index b33ae907..b1b77497 100644 --- a/src/framework/containers/particles.cpp +++ b/src/framework/containers/particles.cpp @@ -24,6 +24,7 @@ namespace ntt { ParticlePusherFlags particle_pusher_flags, bool use_tracking, RadiativeDragFlags radiative_drag_flags, + EmissionTypeFlag emission_policy_flag, unsigned short npld_r, unsigned short npld_i) : ParticleSpecies(index, @@ -34,6 +35,7 @@ namespace ntt { particle_pusher_flags, use_tracking, radiative_drag_flags, + emission_policy_flag, npld_r, npld_i) { diff --git a/src/framework/containers/particles.h b/src/framework/containers/particles.h index c0c2efcb..f265425e 100644 --- a/src/framework/containers/particles.h +++ b/src/framework/containers/particles.h @@ -86,6 +86,7 @@ namespace ntt { * @param particle_pusher_flags The pusher(s) assigned for the species * @param use_tracking Use particle tracking for the species * @param radiative_drag_flags The radiative drag mechanism(s) assigned for the species + * @param emission_policy_flag The emission policy assigned for the species * @param npld_r The number of real-valued payloads for the species * @param npld_i The number of integer-valued payloads for the species */ @@ -97,8 +98,9 @@ namespace ntt { ParticlePusherFlags particle_pusher_flags, bool use_tracking, RadiativeDragFlags radiative_drag_flags, - unsigned short npld_r = 0, - unsigned short npld_i = 0); + EmissionTypeFlag emission_policy_flag, + unsigned short npld_r, + unsigned short npld_i); /** * @brief Constructor for the particle container @@ -114,6 +116,7 @@ namespace ntt { spec.pusher(), spec.use_tracking(), spec.radiative_drag_flags(), + spec.emission_policy_flag(), spec.npld_r(), spec.npld_i()) {} diff --git a/src/framework/containers/species.h b/src/framework/containers/species.h index 1c44fc6a..5f374249 100644 --- a/src/framework/containers/species.h +++ b/src/framework/containers/species.h @@ -39,6 +39,9 @@ namespace ntt { // Radiative drag mechanism(s) assigned for the species const RadiativeDragFlags m_radiative_drag_flags; + // Emission policy assigned for the species + const EmissionTypeFlag m_emission_policy_flag; + // Number of payloads for the species const unsigned short m_npld_r; const unsigned short m_npld_i; @@ -53,6 +56,7 @@ namespace ntt { , m_particle_pusher_flags { ParticlePusher::NONE } , m_use_tracking { false } , m_radiative_drag_flags { RadiativeDrag::NONE } + , m_emission_policy_flag { EmissionType::NONE } , m_npld_r { 0 } , m_npld_i { 0 } {} @@ -67,6 +71,7 @@ namespace ntt { * @param particle_pusher_flags The pusher(s) assigned for the species. * @param use_tracking Use particle tracking for the species. * @param radiative_drag_flags The radiative drag mechanism(s) assigned for the species. + * @param emission_policy_flag The emission policy assigned for the species. * @param npld_r The number of real-valued payloads for the species * @param npld_i The number of integer-valued payloads for the species */ @@ -78,8 +83,9 @@ namespace ntt { ParticlePusherFlags particle_pusher_flags, bool use_tracking, RadiativeDragFlags radiative_drag_flags, - unsigned short npld_r = 0, - unsigned short npld_i = 0) + EmissionTypeFlag emission_policy_flag, + unsigned short npld_r, + unsigned short npld_i) : m_index { index } , m_label { std::move(label) } , m_mass { m } @@ -88,6 +94,7 @@ namespace ntt { , m_particle_pusher_flags { particle_pusher_flags } , m_use_tracking { use_tracking } , m_radiative_drag_flags { radiative_drag_flags } + , m_emission_policy_flag { emission_policy_flag } , m_npld_r { npld_r } , m_npld_i { npld_i } { if (use_tracking) { @@ -151,6 +158,11 @@ namespace ntt { return m_radiative_drag_flags; } + [[nodiscard]] + auto emission_policy_flag() const -> EmissionTypeFlag { + return m_emission_policy_flag; + } + [[nodiscard]] auto npld_r() const -> unsigned short { return m_npld_r; diff --git a/src/framework/parameters/algorithms.cpp b/src/framework/parameters/algorithms.cpp index 030f41a6..ce20136f 100644 --- a/src/framework/parameters/algorithms.cpp +++ b/src/framework/parameters/algorithms.cpp @@ -124,22 +124,6 @@ namespace ntt { "larmor_max", ZERO); } - - if (extra.at("synchrotron")) { - synchrotron_gamma_rad = toml::find_or(toml_data, - "algorithms", - "synchrotron", - "gamma_rad", - defaults::synchrotron::gamma_rad); - } - - if (extra.at("compton")) { - compton_gamma_rad = toml::find_or(toml_data, - "algorithms", - "compton", - "gamma_rad", - defaults::compton::gamma_rad); - } } void Algorithms::setParams(const std::map& extra, @@ -167,14 +151,6 @@ namespace ntt { params->set("algorithms.gca.e_ovr_b_max", gca_e_ovr_b_max); params->set("algorithms.gca.larmor_max", gca_larmor_max); } - - if (extra.at("synchrotron")) { - params->set("algorithms.synchrotron.gamma_rad", synchrotron_gamma_rad); - } - - if (extra.at("compton")) { - params->set("algorithms.compton.gamma_rad", compton_gamma_rad); - } } } // namespace params diff --git a/src/framework/parameters/algorithms.h b/src/framework/parameters/algorithms.h index 0a4d13d0..18bd2a66 100644 --- a/src/framework/parameters/algorithms.h +++ b/src/framework/parameters/algorithms.h @@ -4,7 +4,7 @@ * @implements * - ntt::params::Algorithms * @cpp: - * - grid.cpp + * - algorithms.cpp * @namespaces: * - ntt::params:: */ diff --git a/src/framework/parameters/extra.cpp b/src/framework/parameters/extra.cpp new file mode 100644 index 00000000..9a729d18 --- /dev/null +++ b/src/framework/parameters/extra.cpp @@ -0,0 +1,115 @@ +#include "framework/parameters/extra.h" + +#include "defaults.h" + +#include "utils/numeric.h" + +namespace ntt { + namespace params { + + void Extra::read(const std::map& extra, + const toml::value& toml_data) { + if (extra.at("synchrotron_drag")) { + synchrotron_gamma_rad = toml::find_or(toml_data, + "radiation", + "drag", + "synchrotron", + "gamma_rad", + defaults::synchrotron::gamma_rad); + } + + if (extra.at("compton_drag")) { + compton_gamma_rad = toml::find_or(toml_data, + "radiation", + "drag", + "compton", + "gamma_rad", + defaults::compton::gamma_rad); + } + + if (extra.at("synchrotron_emission")) { + synchrotron_gamma_qed = toml::find_or(toml_data, + "radiation", + "emission", + "synchrotron", + "gamma_qed", + defaults::synchrotron::gamma_qed); + synchrotron_energy_min = toml::find_or(toml_data, + "radiation", + "emission", + "synchrotron", + "photon_energy_min", + defaults::synchrotron::energy_min); + synchrotron_photon_weight = toml::find_or(toml_data, + "radiation", + "emission", + "synchrotron", + "photon_weight", + ONE); + synchrotron_photon_species = toml::find(toml_data, + "radiation", + "emission", + "synchrotron", + "photon_species"); + } + + if (extra.at("compton_emission")) { + compton_gamma_qed = toml::find_or(toml_data, + "radiation", + "emission", + "compton", + "gamma_qed", + defaults::compton::gamma_qed); + compton_energy_min = toml::find_or(toml_data, + "radiation", + "emission", + "compton", + "photon_energy_min", + defaults::compton::energy_min); + compton_photon_weight = toml::find_or(toml_data, + "radiation", + "emission", + "compton", + "photon_weight", + ONE); + compton_photon_species = toml::find(toml_data, + "radiation", + "emission", + "compton", + "photon_species"); + } + } + + void Extra::setParams(const std::map& extra, + SimulationParams* params) const { + if (extra.at("synchrotron_drag")) { + params->set("radiation.drag.synchrotron.gamma_rad", synchrotron_gamma_rad); + } + + if (extra.at("compton_drag")) { + params->set("radiation.drag.compton.gamma_rad", compton_gamma_rad); + } + + if (extra.at("synchrotron_emission")) { + params->set("radiation.emission.synchrotron.gamma_qed", + synchrotron_gamma_qed); + params->set("radiation.emission.synchrotron.photon_energy_min", + synchrotron_energy_min); + params->set("radiation.emission.synchrotron.photon_weight", + synchrotron_photon_weight); + params->set("radiation.emission.synchrotron.photon_species", + synchrotron_photon_species); + } + + if (extra.at("compton_emission")) { + params->set("radiation.emission.compton.gamma_qed", compton_gamma_qed); + params->set("radiation.emission.compton.photon_energy_min", + compton_energy_min); + params->set("radiation.emission.compton.photon_weight", + compton_photon_weight); + params->set("radiation.emission.compton.photon_species", + compton_photon_species); + } + } + } // namespace params +} // namespace ntt diff --git a/src/framework/parameters/extra.h b/src/framework/parameters/extra.h new file mode 100644 index 00000000..76fe9823 --- /dev/null +++ b/src/framework/parameters/extra.h @@ -0,0 +1,50 @@ +/** + * @file framework/parameters/algorithms.h + * @brief Auxiliary functions for reading in extra physics parameters + * @implements + * - ntt::params::Extra + * @cpp: + * - extra.cpp + * @namespaces: + * - ntt::params:: + */ + +#ifndef FRAMEWORK_PARAMETERS_EXTRA_H +#define FRAMEWORK_PARAMETERS_EXTRA_H + +#include "global.h" + +#include "utils/toml.h" + +#include "framework/parameters/parameters.h" + +#include +#include + +namespace ntt { + namespace params { + + struct Extra { + // radiative drag parameters + real_t synchrotron_gamma_rad; + real_t compton_gamma_rad; + + // emission parameters + real_t synchrotron_energy_min; + real_t synchrotron_gamma_qed; + real_t synchrotron_photon_weight; + spidx_t synchrotron_photon_species; + + real_t compton_energy_min; + real_t compton_gamma_qed; + real_t compton_photon_weight; + spidx_t compton_photon_species; + + void read(const std::map&, const toml::value&); + void setParams(const std::map&, SimulationParams*) const; + }; + + } // namespace params +} // namespace ntt + +#endif // FRAMEWORK_PARAMETERS_EXTRA_H diff --git a/src/framework/parameters/parameters.cpp b/src/framework/parameters/parameters.cpp index 9d8d585f..9d4440d0 100644 --- a/src/framework/parameters/parameters.cpp +++ b/src/framework/parameters/parameters.cpp @@ -11,6 +11,7 @@ #include "framework/containers/species.h" #include "framework/parameters/algorithms.h" +#include "framework/parameters/extra.h" #include "framework/parameters/grid.h" #include "framework/parameters/output.h" #include "framework/parameters/particles.h" @@ -135,6 +136,7 @@ namespace ntt { particle_species.pusher(), particle_species.use_tracking(), particle_species.radiative_drag_flags(), + particle_species.emission_policy_flag(), particle_species.npld_r(), particle_species.npld_i()); idxM1++; @@ -197,14 +199,24 @@ namespace ntt { /* [algorithms] --------------------------------------------------------- */ ntt::params::Algorithms alg_params {}; std::map alg_extra_flags = { - { "gr", engine_enum == SimEngine::GRPIC }, - { "gca", isPromised("algorithms.gca.e_ovr_b_max") }, - { "synchrotron", isPromised("algorithms.synchrotron.gamma_rad") }, - { "compton", isPromised("algorithms.compton.gamma_rad") } + { "gr", engine_enum == SimEngine::GRPIC }, + { "gca", isPromised("algorithms.gca.e_ovr_b_max") }, }; alg_params.read(get("scales.dx0"), alg_extra_flags, toml_data); alg_params.setParams(alg_extra_flags, this); + /* extra physics ------------------------------------------------------ */ + ntt::params::Extra extra_params {}; + std::map extra_extra_flags = { + { "synchrotron_drag",isPromised("radiation.drag.synchrotron.gamma_rad") }, + { "compton_drag", isPromised("radiation.drag.compton.gamma_rad") }, + { "synchrotron_emission", + isPromised("radiation.emission.synchrotron.photon_species") }, + { "compton_emission", isPromised("radiation.emission.compton.photon_species") } + }; + extra_params.read(extra_extra_flags, toml_data); + extra_params.setParams(extra_extra_flags, this); + // @TODO: disabling stats for non-Cartesian if (coord_enum != Coord::Cart) { set("output.stats.enable", false); diff --git a/src/framework/parameters/particles.cpp b/src/framework/parameters/particles.cpp index 87e50d9e..ddc22135 100644 --- a/src/framework/parameters/particles.cpp +++ b/src/framework/parameters/particles.cpp @@ -20,8 +20,8 @@ namespace ntt { /* * Auxiliary functions */ - auto getRadiativeDragFlags( - const std::string& radiative_drag_str) -> RadiativeDragFlags { + auto getRadiativeDragFlags(const std::string& radiative_drag_str) + -> RadiativeDragFlags { if (fmt::toLower(radiative_drag_str) == "none") { return RadiativeDrag::NONE; } else { @@ -45,8 +45,8 @@ namespace ntt { } } - auto getPusherFlags( - const std::string& particle_pusher_str) -> ParticlePusherFlags { + auto getPusherFlags(const std::string& particle_pusher_str) + -> ParticlePusherFlags { if (fmt::toLower(particle_pusher_str) == "none") { return ParticlePusher::NONE; } else { @@ -65,9 +65,9 @@ namespace ntt { } else if (token_lower == "gca") { flags |= ParticlePusher::GCA; } else { - raise::Error( - fmt::format("Invalid pusher value: %s", particle_pusher_str.c_str()), - HERE); + raise::Error(fmt::format("Invalid pusher value: %s", + particle_pusher_str.c_str()), + HERE); } } if (flags & ParticlePusher::PHOTON and flags & ParticlePusher::GCA) { @@ -77,6 +77,24 @@ namespace ntt { } } + auto getEmissionPolicyFlag(const std::string& emission_policy_str) + -> EmissionTypeFlag { + if (fmt::toLower(emission_policy_str) == "none") { + return EmissionType::NONE; + } else if (fmt::toLower(emission_policy_str) == "synchrotron") { + return EmissionType::SYNCHROTRON; + } else if (fmt::toLower(emission_policy_str) == "compton") { + return EmissionType::COMPTON; + } else if (fmt::toLower(emission_policy_str) == "strongfieldpp") { + return EmissionType::STRONGFIELDPP; + } else { + raise::Error(fmt::format("Invalid emission_policy value: %s", + emission_policy_str.c_str()), + HERE); + return EmissionType::NONE; + } + } + auto GetParticleSpecies(SimulationParams* params, const SimEngine& engine_enum, spidx_t idx, @@ -109,29 +127,62 @@ namespace ntt { npayloads_int += 2; #endif } - const auto radiative_drag_str = toml::find_or(sp, - "radiative_drag", - std::string("none")); + auto radiative_drag_str = toml::find_or(sp, + "radiative_drag", + std::string("default")); + + const auto radiative_drag_defaulted = (fmt::toLower(radiative_drag_str) == + "default"); + if (radiative_drag_defaulted) { + radiative_drag_str = "none"; + } + + const auto emission_policy_str = toml::find_or(sp, + "emission", + std::string("none")); raise::ErrorIf((fmt::toLower(radiative_drag_str) != "none") && is_massless, "radiative drag is only applicable to massive particles", HERE); raise::ErrorIf((fmt::toLower(pusher_str) == "photon") && !is_massless, "photon pusher is only applicable to massless particles", HERE); - const auto particle_pusher_flags = getPusherFlags(pusher_str); - const auto radiative_drag_flags = getRadiativeDragFlags(radiative_drag_str); + + auto particle_pusher_flags = getPusherFlags(pusher_str); + auto radiative_drag_flags = getRadiativeDragFlags(radiative_drag_str); + auto emission_policy_flag = getEmissionPolicyFlag(emission_policy_str); + + raise::ErrorIf((emission_policy_flag == EmissionType::STRONGFIELDPP) and + (not is_massless), + "Strong Field Pair Production emission policy is only " + "applicable to massless particles", + HERE); + raise::ErrorIf((emission_policy_flag == EmissionType::SYNCHROTRON or + emission_policy_flag == EmissionType::COMPTON) and + is_massless, + "Radiative emission policies are only applicable to " + "massive particles", + HERE); + + if (radiative_drag_defaulted) { + if (emission_policy_flag == EmissionType::SYNCHROTRON) { + radiative_drag_flags |= RadiativeDrag::SYNCHROTRON; + } else if (emission_policy_flag == EmissionType::COMPTON) { + radiative_drag_flags |= RadiativeDrag::COMPTON; + } + } + if (radiative_drag_flags & RadiativeDrag::SYNCHROTRON) { raise::ErrorIf(engine_enum != SimEngine::SRPIC, "Synchrotron radiative drag is only supported for SRPIC", HERE); - params->promiseToDefine("algorithms.synchrotron.gamma_rad"); + params->promiseToDefine("radiation.drag.synchrotron.gamma_rad"); } if (radiative_drag_flags & RadiativeDrag::COMPTON) { raise::ErrorIf( engine_enum != SimEngine::SRPIC, "Inverse Compton radiative drag is only supported for SRPIC", HERE); - params->promiseToDefine("algorithms.compton.gamma_rad"); + params->promiseToDefine("radiation.drag.compton.gamma_rad"); } if (particle_pusher_flags & ParticlePusher::GCA) { raise::ErrorIf(engine_enum != SimEngine::SRPIC, @@ -140,6 +191,16 @@ namespace ntt { params->promiseToDefine("algorithms.gca.e_ovr_b_max"); params->promiseToDefine("algorithms.gca.larmor_max"); } + + if (emission_policy_flag == EmissionType::SYNCHROTRON) { + params->promiseToDefine( + "radiation.emission.synchrotron.photon_species"); + params->promiseToDefine("radiation.drag.synchrotron.gamma_rad"); + } else if (emission_policy_flag == EmissionType::COMPTON) { + params->promiseToDefine("radiation.emission.compton.photon_species"); + params->promiseToDefine("radiation.drag.compton.gamma_rad"); + } + return ParticleSpecies(idx, label, mass, @@ -148,6 +209,7 @@ namespace ntt { particle_pusher_flags, use_tracking, radiative_drag_flags, + emission_policy_flag, npayloads_real, npayloads_int); } diff --git a/src/framework/tests/parameters.cpp b/src/framework/tests/parameters.cpp index acd10421..41f854d3 100644 --- a/src/framework/tests/parameters.cpp +++ b/src/framework/tests/parameters.cpp @@ -125,6 +125,21 @@ const auto sph_2d = u8R"( larmor0 = 0.01 skindepth0 = 0.01 +[radiation] + [radiation.drag] + [radiation.drag.synchrotron] + gamma_rad = 50.0 + + [radiation.drag.compton] + gamma_rad = 20.0 + + [radiation.emission] + [radiation.emission.synchrotron] + gamma_qed = 100.0 + photon_energy_min = 0.01 + photon_weight = 10.0 + photon_species = 3 + [algorithms] current_filters = 8 @@ -135,12 +150,6 @@ const auto sph_2d = u8R"( e_ovr_b_max = 0.95 larmor_max = 0.025 - [algorithms.synchrotron] - gamma_rad = 50.0 - - [algorithms.compton] - gamma_rad = 20.0 - [particles] ppc0 = 25.0 use_weights = true @@ -155,6 +164,7 @@ const auto sph_2d = u8R"( pusher = "boris,gca" n_payloads_real = 3 radiative_drag = "synchrotron,compton" + emission = "synchrotron" [[particles.species]] label = "e+" @@ -257,135 +267,135 @@ auto main(int argc, char* argv[]) -> int { try { using namespace ntt; - { - auto params_mink_1d = SimulationParams(); - params_mink_1d.setImmutableParams(mink_1d); - params_mink_1d.setMutableParams(mink_1d); - params_mink_1d.setSetupParams(mink_1d); - params_mink_1d.checkPromises(); - - assert_equal(params_mink_1d.get("grid.metric.metric"), - Metric::Minkowski, - "grid.metric.metric"); - // engine - assert_equal( - params_mink_1d.get("simulation.engine"), - SimEngine::SRPIC, - "simulation.engine"); - - assert_equal(params_mink_1d.get("scales.dx0"), - (real_t)0.0078125, - "scales.dx0"); - assert_equal(params_mink_1d.get("scales.V0"), - (real_t)0.0078125, - "scales.V0"); - boundaries_t fbc = { - { FldsBC::MATCH, FldsBC::MATCH } - }; - assert_equal( - params_mink_1d.get>("grid.boundaries.fields")[0].first, - fbc[0].first, - "grid.boundaries.fields[0].first"); - assert_equal( - params_mink_1d.get>("grid.boundaries.fields")[0].second, - fbc[0].second, - "grid.boundaries.fields[0].second"); - assert_equal( - params_mink_1d.get>("grid.boundaries.fields").size(), - fbc.size(), - "grid.boundaries.fields.size()"); - assert_equal( - params_mink_1d.get>("grid.boundaries.match.ds")[0].first, - (real_t)0.025, - "grid.boundaries.match.ds[0].first"); - assert_equal( - params_mink_1d.get>("grid.boundaries.match.ds")[0].second, - (real_t)0.1, - "grid.boundaries.match.ds[0].first"); - - const auto species = params_mink_1d.get>( - "particles.species"); - assert_equal(species[0].label(), "e-", "species[0].label"); - assert_equal(species[0].mass(), 1.0f, "species[0].mass"); - assert_equal(species[0].charge(), -1.0f, "species[0].charge"); - assert_equal(species[0].maxnpart(), 100, "species[0].maxnpart"); - assert_equal(species[0].pusher(), - ParticlePusher::BORIS, - "species[0].pusher"); - assert_equal(species[0].npld_r(), 3, "species[0].npld_r"); - assert_equal(species[0].npld_i(), 1, "species[0].npld_i"); - assert_equal(species[0].use_tracking(), true, "species[0].tracking"); - - assert_equal(species[1].label(), "p+", "species[1].label"); - assert_equal(species[1].mass(), 1.0f, "species[1].mass"); - assert_equal(species[1].charge(), 200.0f, "species[1].charge"); - assert_equal(species[1].maxnpart(), 100, "species[1].maxnpart"); - assert_equal(species[1].pusher(), - ParticlePusher::VAY, - "species[1].pusher"); - assert_equal(species[1].npld_r(), 0, "species[1].npld_r"); - - assert_equal(params_mink_1d.get("setup.myfloat"), - (real_t)(1e-2), - "setup.myfloat"); - assert_equal(params_mink_1d.get("setup.myint"), - (int)(123), - "setup.myint"); - assert_equal(params_mink_1d.get("setup.mybool"), - true, - "setup.mybool"); - const auto myarr = params_mink_1d.get>("setup.myarr"); - assert_equal(myarr[0], 1.0, "setup.myarr[0]"); - assert_equal(myarr[1], 2.0, "setup.myarr[1]"); - assert_equal(myarr[2], 3.0, "setup.myarr[2]"); - assert_equal(params_mink_1d.get("setup.mystr"), - "hi", - "setup.mystr"); - - const auto output_stride = params_mink_1d.get>( - "output.fields.downsampling"); - assert_equal(output_stride.size(), - 1, - "output.fields.downsampling.size()"); - assert_equal(output_stride[0], 4, "output.fields.downsampling[0]"); - - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.delta_x"), - (real_t)(1.0), - "algorithms.fieldsolver.delta_x"); - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.delta_y"), - (real_t)(2.0), - "algorithms.fieldsolver.delta_y"); - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.delta_z"), - (real_t)(3.0), - "algorithms.fieldsolver.delta_z"); - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.beta_xy"), - (real_t)(4.0), - "algorithms.fieldsolver.beta_xy"); - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.beta_yx"), - (real_t)(5.0), - "algorithms.fieldsolver.beta_yx"); - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.beta_xz"), - (real_t)(6.0), - "algorithms.fieldsolver.beta_xz"); - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.beta_zx"), - (real_t)(7.0), - "algorithms.fieldsolver.beta_zx"); - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.beta_yz"), - (real_t)(8.0), - "algorithms.fieldsolver.beta_yz"); - assert_equal( - params_mink_1d.get("algorithms.fieldsolver.beta_zy"), - (real_t)(9.0), - "algorithms.fieldsolver.beta_zy"); - } + // { + // auto params_mink_1d = SimulationParams(); + // params_mink_1d.setImmutableParams(mink_1d); + // params_mink_1d.setMutableParams(mink_1d); + // params_mink_1d.setSetupParams(mink_1d); + // params_mink_1d.checkPromises(); + // + // assert_equal(params_mink_1d.get("grid.metric.metric"), + // Metric::Minkowski, + // "grid.metric.metric"); + // // engine + // assert_equal( + // params_mink_1d.get("simulation.engine"), + // SimEngine::SRPIC, + // "simulation.engine"); + // + // assert_equal(params_mink_1d.get("scales.dx0"), + // (real_t)0.0078125, + // "scales.dx0"); + // assert_equal(params_mink_1d.get("scales.V0"), + // (real_t)0.0078125, + // "scales.V0"); + // boundaries_t fbc = { + // { FldsBC::MATCH, FldsBC::MATCH } + // }; + // assert_equal( + // params_mink_1d.get>("grid.boundaries.fields")[0].first, + // fbc[0].first, + // "grid.boundaries.fields[0].first"); + // assert_equal( + // params_mink_1d.get>("grid.boundaries.fields")[0].second, + // fbc[0].second, + // "grid.boundaries.fields[0].second"); + // assert_equal( + // params_mink_1d.get>("grid.boundaries.fields").size(), + // fbc.size(), + // "grid.boundaries.fields.size()"); + // assert_equal( + // params_mink_1d.get>("grid.boundaries.match.ds")[0].first, + // (real_t)0.025, + // "grid.boundaries.match.ds[0].first"); + // assert_equal( + // params_mink_1d.get>("grid.boundaries.match.ds")[0].second, + // (real_t)0.1, + // "grid.boundaries.match.ds[0].first"); + // + // const auto species = params_mink_1d.get>( + // "particles.species"); + // assert_equal(species[0].label(), "e-", "species[0].label"); + // assert_equal(species[0].mass(), 1.0f, "species[0].mass"); + // assert_equal(species[0].charge(), -1.0f, "species[0].charge"); + // assert_equal(species[0].maxnpart(), 100, "species[0].maxnpart"); + // assert_equal(species[0].pusher(), + // ParticlePusher::BORIS, + // "species[0].pusher"); + // assert_equal(species[0].npld_r(), 3, "species[0].npld_r"); + // assert_equal(species[0].npld_i(), 1, "species[0].npld_i"); + // assert_equal(species[0].use_tracking(), true, "species[0].tracking"); + // + // assert_equal(species[1].label(), "p+", "species[1].label"); + // assert_equal(species[1].mass(), 1.0f, "species[1].mass"); + // assert_equal(species[1].charge(), 200.0f, "species[1].charge"); + // assert_equal(species[1].maxnpart(), 100, "species[1].maxnpart"); + // assert_equal(species[1].pusher(), + // ParticlePusher::VAY, + // "species[1].pusher"); + // assert_equal(species[1].npld_r(), 0, "species[1].npld_r"); + // + // assert_equal(params_mink_1d.get("setup.myfloat"), + // (real_t)(1e-2), + // "setup.myfloat"); + // assert_equal(params_mink_1d.get("setup.myint"), + // (int)(123), + // "setup.myint"); + // assert_equal(params_mink_1d.get("setup.mybool"), + // true, + // "setup.mybool"); + // const auto myarr = params_mink_1d.get>("setup.myarr"); + // assert_equal(myarr[0], 1.0, "setup.myarr[0]"); + // assert_equal(myarr[1], 2.0, "setup.myarr[1]"); + // assert_equal(myarr[2], 3.0, "setup.myarr[2]"); + // assert_equal(params_mink_1d.get("setup.mystr"), + // "hi", + // "setup.mystr"); + // + // const auto output_stride = params_mink_1d.get>( + // "output.fields.downsampling"); + // assert_equal(output_stride.size(), + // 1, + // "output.fields.downsampling.size()"); + // assert_equal(output_stride[0], 4, "output.fields.downsampling[0]"); + // + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.delta_x"), + // (real_t)(1.0), + // "algorithms.fieldsolver.delta_x"); + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.delta_y"), + // (real_t)(2.0), + // "algorithms.fieldsolver.delta_y"); + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.delta_z"), + // (real_t)(3.0), + // "algorithms.fieldsolver.delta_z"); + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.beta_xy"), + // (real_t)(4.0), + // "algorithms.fieldsolver.beta_xy"); + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.beta_yx"), + // (real_t)(5.0), + // "algorithms.fieldsolver.beta_yx"); + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.beta_xz"), + // (real_t)(6.0), + // "algorithms.fieldsolver.beta_xz"); + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.beta_zx"), + // (real_t)(7.0), + // "algorithms.fieldsolver.beta_zx"); + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.beta_yz"), + // (real_t)(8.0), + // "algorithms.fieldsolver.beta_yz"); + // assert_equal( + // params_mink_1d.get("algorithms.fieldsolver.beta_zy"), + // (real_t)(9.0), + // "algorithms.fieldsolver.beta_zy"); + // } { auto params_sph_2d = SimulationParams(); @@ -457,13 +467,31 @@ auto main(int argc, char* argv[]) -> int { "algorithms.gca.larmor_max"); assert_equal( - params_sph_2d.get("algorithms.synchrotron.gamma_rad"), + params_sph_2d.get("radiation.drag.synchrotron.gamma_rad"), (real_t)50.0, - "algorithms.synchrotron.gamma_rad"); + "radiation.drag.synchrotron.gamma_rad"); - assert_equal(params_sph_2d.get("algorithms.compton.gamma_rad"), - (real_t)20.0, - "algorithms.compton.gamma_rad"); + assert_equal( + params_sph_2d.get("radiation.drag.compton.gamma_rad"), + (real_t)20.0, + "radiation.drag.compton.gamma_rad"); + + assert_equal(params_sph_2d.get( + "radiation.emission.synchrotron.photon_energy_min"), + (real_t)0.01, + "radiation.emission.synchrotron.photon_energy_min"); + assert_equal( + params_sph_2d.get("radiation.emission.synchrotron.gamma_qed"), + (real_t)100.0, + "radiation.emission.synchrotron.gamma_qed"); + assert_equal(params_sph_2d.get( + "radiation.emission.synchrotron.photon_weight"), + (real_t)10.0, + "radiation.emission.synchrotron.photon_weight"); + assert_equal(params_sph_2d.get( + "radiation.emission.synchrotron.photon_species"), + 3, + "radiation.emission.synchrotron.photon_species"); const auto species = params_sph_2d.get>( "particles.species"); @@ -480,6 +508,10 @@ auto main(int argc, char* argv[]) -> int { RadiativeDrag::COMPTON, "species[0].radiative_drag_flags"); + assert_equal(species[0].emission_policy_flag(), + EmissionType::SYNCHROTRON, + "species[0].emission_policy_flag"); + assert_equal(species[1].label(), "e+", "species[1].label"); assert_equal(species[1].mass(), 1.0f, "species[1].mass"); assert_equal(species[1].charge(), 1.0f, "species[1].charge"); diff --git a/src/framework/tests/particles.cpp b/src/framework/tests/particles.cpp index a1015172..cce6af28 100644 --- a/src/framework/tests/particles.cpp +++ b/src/framework/tests/particles.cpp @@ -9,16 +9,18 @@ #include template -void testParticles(int index, - const std::string& label, - float m, - float ch, - std::size_t maxnpart, - ntt::ParticlePusherFlags pusher, - bool use_tracking, - ntt::RadiativeDragFlags radiative_drag_flags, - unsigned short npld_r = 0, - unsigned short npld_i = 0) { +void testParticles( + int index, + const std::string& label, + float m, + float ch, + std::size_t maxnpart, + ntt::ParticlePusherFlags pusher, + bool use_tracking, + ntt::RadiativeDragFlags radiative_drag_flags, + ntt::EmissionTypeFlag emission_policy_flag = ntt::EmissionType::NONE, + unsigned short npld_r = 0, + unsigned short npld_i = 0) { using namespace ntt; auto p = Particles(index, label, @@ -28,6 +30,7 @@ void testParticles(int index, pusher, use_tracking, radiative_drag_flags, + emission_policy_flag, npld_r, npld_i); raise::ErrorIf(p.index() != index, "Index mismatch", HERE); @@ -39,6 +42,9 @@ void testParticles(int index, raise::ErrorIf(p.radiative_drag_flags() != radiative_drag_flags, "Radiative drag mismatch", HERE); + raise::ErrorIf(p.emission_policy_flag() != emission_policy_flag, + "Emission policy mismatch", + HERE); raise::ErrorIf(p.npart() != 0, "Number of particles mismatch", HERE); raise::ErrorIf(p.i1.extent(0) != maxnpart, "i1 incorrectly allocated", HERE); @@ -128,6 +134,7 @@ auto main(int argc, char** argv) -> int { true, RadiativeDrag::SYNCHROTRON | RadiativeDrag::COMPTON, + EmissionType::SYNCHROTRON, 2, 1); testParticles(3, @@ -138,6 +145,7 @@ auto main(int argc, char** argv) -> int { ParticlePusher::PHOTON, false, RadiativeDrag::NONE, + EmissionType::COMPTON, 5); testParticles(4, "e+", @@ -147,6 +155,7 @@ auto main(int argc, char** argv) -> int { ParticlePusher::BORIS, true, RadiativeDrag::NONE, + EmissionType::NONE, 2, 3); testParticles(5, @@ -157,6 +166,7 @@ auto main(int argc, char** argv) -> int { ParticlePusher::BORIS, false, RadiativeDrag::NONE, + EmissionType::NONE, 1, 2); } catch (const std::exception& e) { diff --git a/src/global/defaults.h b/src/global/defaults.h index 96b17f1f..dbe37ef0 100644 --- a/src/global/defaults.h +++ b/src/global/defaults.h @@ -97,11 +97,15 @@ namespace ntt::defaults { } // namespace gca namespace synchrotron { - const real_t gamma_rad = 1.0; + const real_t energy_min = 1e-3; + const real_t gamma_rad = 1.0; + const real_t gamma_qed = 10.0; } // namespace synchrotron namespace compton { - const real_t gamma_rad = 1.0; + const real_t energy_min = 1e-3; + const real_t gamma_rad = 1.0; + const real_t gamma_qed = 10.0; } // namespace compton } // namespace ntt::defaults diff --git a/src/global/enums.h b/src/global/enums.h index d1aae86b..ac05c0fb 100644 --- a/src/global/enums.h +++ b/src/global/enums.h @@ -17,6 +17,7 @@ * * - enum ntt::ParticlePusher // photon, boris, vay, gca, none * - enum ntt::RadiativeDrag // compton, synchrotron, none + * - enum ntt::EmissionType // none, synchrotron, inversecompton, strongfieldpp * * @namespaces: * - ntt:: @@ -365,6 +366,32 @@ namespace ntt { typedef int RadiativeDragFlags; + namespace EmissionType { + enum EmissionTypeFlag_ { + NONE = 0, + SYNCHROTRON = 1, + COMPTON = 2, + STRONGFIELDPP = 3, + }; + + inline auto to_string(int flags) -> std::string { + switch (flags) { + case NONE: + return "none"; + case SYNCHROTRON: + return "synchrotron"; + case COMPTON: + return "compton"; + case STRONGFIELDPP: + return "strongfieldpp"; + default: + return "unknown"; + } + } + } // namespace EmissionType + + typedef int EmissionTypeFlag; + } // namespace ntt #endif // GLOBAL_ENUMS_H diff --git a/src/global/utils/numeric.h b/src/global/utils/numeric.h index 0be0e4a0..e8495a48 100644 --- a/src/global/utils/numeric.h +++ b/src/global/utils/numeric.h @@ -26,8 +26,6 @@ #ifndef GLOBAL_UTILS_NUMERIC_H #define GLOBAL_UTILS_NUMERIC_H -#include "arch/kokkos_aliases.h" - #include #if defined(SINGLE_PRECISION) @@ -61,7 +59,7 @@ inline constexpr double ZERO = 0.0; inline constexpr double HALF = 0.5; inline constexpr double THIRD = 0.3333333333333333; inline constexpr double THREE_FOURTHS = 0.75; -inline constexpr float THREE_HALFS = 1.5; +inline constexpr double THREE_HALFS = 1.5; inline constexpr double INV_2 = 0.5; inline constexpr double INV_4 = 0.25; inline constexpr double INV_8 = 0.125; diff --git a/src/kernels/emission.hpp b/src/kernels/emission.hpp new file mode 100644 index 00000000..f6cf69f4 --- /dev/null +++ b/src/kernels/emission.hpp @@ -0,0 +1,29 @@ +#ifndef KERNELS_EMISSION_HPP +#define KERNELS_EMISSION_HPP + +#include "enums.h" +#include "global.h" + +#include "kernels/injectors.hpp" + +namespace kernel { + using namespace ntt; + + template + struct EmissionPolicy; + + template + struct EmissionPolicy {}; + + template + struct EmissionPolicy {}; + + template + struct EmissionPolicy {}; + + template + struct EmissionPolicy {}; + +} // namespace kernel + +#endif diff --git a/src/kernels/particle_pusher_sr.hpp b/src/kernels/particle_pusher_sr.hpp index b55c0760..c57f6b35 100644 --- a/src/kernels/particle_pusher_sr.hpp +++ b/src/kernels/particle_pusher_sr.hpp @@ -3,6 +3,8 @@ * @brief Particle pusher for the SR * @implements * - kernel::sr::Pusher_kernel<> + * - kernel::sr::PusherParams + * - kernel::sr::PusherArrays * @namespaces: * - kernel::sr:: * @macros: @@ -24,6 +26,7 @@ #include "utils/numeric.h" #include "utils/param_container.h" +#include "kernels/emission.hpp" #include "particle_shapes.hpp" #if defined(MPI_ENABLED) @@ -69,8 +72,8 @@ namespace kernel::sr { */ template struct Force { - static constexpr auto ExtForce = not std::is_same::value; - static_assert(ExtForce or Atm, + static constexpr auto HasExtForce = not std::is_same::value; + static_assert(HasExtForce or Atm, "Force initialized with neither PGen force nor gravity"); const F pgen_force; @@ -96,7 +99,7 @@ namespace kernel::sr { Force(const vec_t& g, real_t x_surf, real_t ds) : Force { NoForce_t {}, g, x_surf, ds } { - raise::ErrorIf(ExtForce, "External force not provided", HERE); + raise::ErrorIf(HasExtForce, "External force not provided", HERE); } Inline auto fx1(spidx_t sp, @@ -104,7 +107,7 @@ namespace kernel::sr { bool ext_force, const coord_t& x_Ph) const -> real_t { real_t f_x1 = ZERO; - if constexpr (ExtForce) { + if constexpr (HasExtForce) { if (ext_force) { f_x1 += pgen_force.fx1(sp, time, x_Ph); } @@ -130,7 +133,7 @@ namespace kernel::sr { bool ext_force, const coord_t& x_Ph) const -> real_t { real_t f_x2 = ZERO; - if constexpr (ExtForce) { + if constexpr (HasExtForce) { if (ext_force) { f_x2 += pgen_force.fx2(sp, time, x_Ph); } @@ -156,7 +159,7 @@ namespace kernel::sr { bool ext_force, const coord_t& x_Ph) const -> real_t { real_t f_x3 = ZERO; - if constexpr (ExtForce) { + if constexpr (HasExtForce) { if (ext_force) { f_x3 += pgen_force.fx3(sp, time, x_Ph); } @@ -220,13 +223,14 @@ namespace kernel::sr { * @tparam M Metric * @tparam F Additional force */ - template + template requires traits::metric::HasD && traits::metric::HasTransformXYZ && traits::metric::HasConvertXYZ && traits::metric::HasTransform_i && traits::metric::HasConvert_i struct Pusher_kernel { - static constexpr auto D = M::Dim; - static constexpr auto ExtForce = not std::is_same::value; + static constexpr auto D = M::Dim; + static constexpr auto HasExtForce = not std::is_same::value; + static constexpr auto HasEmission = (E != EmissionType::NONE); private: const ParticlePusherFlags pusher_flags; @@ -247,6 +251,8 @@ namespace kernel::sr { const M metric; const F force; + const EmissionPolicy emission_policy; + const simtime_t time; const real_t coeff, dt; @@ -268,11 +274,13 @@ namespace kernel::sr { const real_t raddrag_coeff_synchrotron, raddrag_coeff_compton; public: - Pusher_kernel(const PusherParams& pusher_params, - PusherArrays& pusher_arrays, - const randacc_ndfield_t& EB, - const M& metric, - const F& force = NoForce_t {}) + Pusher_kernel( + const PusherParams& pusher_params, + PusherArrays& pusher_arrays, + const randacc_ndfield_t& EB, + const M& metric, + const F& force = NoForce_t {}, + const EmissionPolicy& emission_policy = EmissionPolicy {}) : pusher_flags { pusher_params.pusher_flags } , radiative_drag_flags { pusher_params.radiative_drag_flags } , ext_force { pusher_params.ext_force } @@ -320,14 +328,15 @@ namespace kernel::sr { "synchrotron_gamma_rad")) * pusher_params.mass) : ZERO } - , raddrag_coeff_compton { - (pusher_params.radiative_drag_flags & RadiativeDrag::COMPTON) - ? static_cast(0.1) * pusher_params.dt * pusher_params.omegaB0 / - (SQR(pusher_params.radiative_drag_params.get( - "compton_gamma_rad")) * - pusher_params.mass) - : ZERO - } { + , raddrag_coeff_compton { (pusher_params.radiative_drag_flags & + RadiativeDrag::COMPTON) + ? static_cast(0.1) * + pusher_params.dt * pusher_params.omegaB0 / + (SQR(pusher_params.radiative_drag_params.get( + "compton_gamma_rad")) * + pusher_params.mass) + : ZERO } + , emission_policy { emission_policy } { raise::ErrorIf(pusher_flags == ParticlePusher::NONE, "No particle pusher specified", HERE); @@ -480,7 +489,7 @@ namespace kernel::sr { u_prime[1] = ux2(p); u_prime[2] = ux3(p); } - if constexpr (ExtForce) { + if constexpr (HasExtForce) { coord_t xp_Ph { ZERO }; xp_Ph[0] = metric.template convert<1, Crd::Cd, Crd::Ph>(xp_Cd[0]); if constexpr (M::PrtlDim == Dim::_2D or M::PrtlDim == Dim::_3D) { @@ -505,20 +514,20 @@ namespace kernel::sr { if (B2 > ZERO && rL < gca_larmor && (E2 / B2) < gca_EovrB_sqr) { is_gca = true; // update with GCA - if constexpr (ExtForce) { + if constexpr (HasExtForce) { velUpd(true, p, force_Cart, ei_Cart, bi_Cart); } else { velUpd(true, p, ei_Cart, bi_Cart); } } else { // update with conventional pusher - if constexpr (ExtForce) { + if constexpr (HasExtForce) { ux1(p) += HALF * dt * force_Cart[0]; ux2(p) += HALF * dt * force_Cart[1]; ux3(p) += HALF * dt * force_Cart[2]; } velUpd(false, p, ei_Cart, bi_Cart); - if constexpr (ExtForce) { + if constexpr (HasExtForce) { ux1(p) += HALF * dt * force_Cart[0]; ux2(p) += HALF * dt * force_Cart[1]; ux3(p) += HALF * dt * force_Cart[2]; @@ -527,13 +536,13 @@ namespace kernel::sr { } else { /* conventional pusher mode ------------------------------------- */ // update with conventional pusher - if constexpr (ExtForce) { + if constexpr (HasExtForce) { ux1(p) += HALF * dt * force_Cart[0]; ux2(p) += HALF * dt * force_Cart[1]; ux3(p) += HALF * dt * force_Cart[2]; } velUpd(false, p, ei_Cart, bi_Cart); - if constexpr (ExtForce) { + if constexpr (HasExtForce) { ux1(p) += HALF * dt * force_Cart[0]; ux2(p) += HALF * dt * force_Cart[1]; ux3(p) += HALF * dt * force_Cart[2]; From cf7f9ed9b731b81f60ff3e5fb4847fd283d65669 Mon Sep 17 00:00:00 2001 From: haykh Date: Mon, 19 Jan 2026 16:42:07 -0500 Subject: [PATCH 13/21] CPUTEST From 784dd5b0164e3586c544dabaf3ee8f8e69c9bb22 Mon Sep 17 00:00:00 2001 From: hayk Date: Mon, 19 Jan 2026 23:16:51 -0500 Subject: [PATCH 14/21] dependencies script (no cluster presets yet) --- dependencies.py | 1178 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1178 insertions(+) create mode 100755 dependencies.py diff --git a/dependencies.py b/dependencies.py new file mode 100755 index 00000000..6354b133 --- /dev/null +++ b/dependencies.py @@ -0,0 +1,1178 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import curses +import json +import os +from dataclasses import dataclass, field +from typing import Callable, List, Optional, Tuple + + +# ============================ +# colors: edit these +# ============================ + +# foreground colors (use curses.COLOR_* or -1 for default) +COLOR_TITLE_FG = curses.COLOR_BLUE +COLOR_TEXT_FG = curses.COLOR_WHITE +COLOR_SELECTED_FG = curses.COLOR_WHITE +COLOR_SELECTED_BG = curses.COLOR_BLACK +COLOR_HINT_FG = curses.COLOR_YELLOW +COLOR_OK_FG = curses.COLOR_GREEN +COLOR_ERR_FG = curses.COLOR_RED +COLOR_KEY_FG = curses.COLOR_MAGENTA +COLOR_DIM_FG = curses.COLOR_CYAN + +# pair IDs (must be unique small ints) +PAIR_TITLE = 1 +PAIR_TEXT = 2 +PAIR_SELECTED = 3 +PAIR_HINT = 4 +PAIR_OK = 5 +PAIR_ERR = 6 +PAIR_KEY = 7 +PAIR_DIM = 8 + + +KOKKOS_BACKENDS = ["cpu", "cuda", "hip", "sycl"] +ADIOS2_MPI_MODES = ["non-mpi", "mpi"] + +MESSAGE: str = "" + + +@dataclass +class Settings: + cluster: str = "(custom)" + write_modulefiles: bool = False + overwrite: bool = False + install_prefix: str = os.path.join(os.path.expanduser("~"), ".entity") + + apps: dict = field( + default_factory=lambda: {"Kokkos": False, "adios2": False, "nt2py": False} + ) + + # versions + kokkos_version: str = "5.0.1" + adios2_version: str = "2.11.0" + + # options + kokkos_backend: str = "cpu" + kokkos_arch: str = "" + extra_kokkos_flags: List[str] = field(default_factory=list) + adios2_mpi: str = "non-mpi" + extra_adios2_flags: List[str] = field(default_factory=list) + + module_loads: List[str] = field(default_factory=list) + + def from_json(self, json_str: str) -> None: + data = json.loads(json_str) + self.cluster = data.get("cluster", self.cluster) + self.write_modulefiles = data.get("write_modulefiles", self.write_modulefiles) + self.overwrite = data.get("overwrite", self.overwrite) + self.install_prefix = data.get("install_prefix", self.install_prefix) + self.apps = data.get("dependencies", self.apps) + versions = data.get("versions", {}) + self.kokkos_version = versions.get("Kokkos", self.kokkos_version) + self.adios2_version = versions.get("adios2", self.adios2_version) + options = data.get("options", {}) + self.kokkos_backend = options.get("kokkos_backend", self.kokkos_backend) + self.kokkos_arch = options.get("kokkos_arch", self.kokkos_arch) + self.adios2_mpi = options.get("adios2_mpi", self.adios2_mpi) + self.module_loads = data.get("module_loads", self.module_loads) + + def apps_summary(self) -> str: + chosen = [k for k, v in self.apps.items() if v] + return ", ".join(chosen) if chosen else "(none)" + + def to_json(self) -> str: + return json.dumps( + { + "cluster": self.cluster, + "write_modulefiles": self.write_modulefiles, + "overwrite": self.overwrite, + "install_prefix": self.install_prefix, + "dependencies": self.apps, + "versions": { + "Kokkos": self.kokkos_version, + "adios2": self.adios2_version, + }, + "options": { + "kokkos_backend": self.kokkos_backend, + "kokkos_arch": self.kokkos_arch, + "adios2_mpi": self.adios2_mpi, + }, + "module_loads": self.module_loads, + }, + indent=2, + ) + + +def unindent(script: str) -> str: + script_lines = script.splitlines() + min_indent = min( + (len(line) - len(line.lstrip()) for line in script_lines if line.strip()), + default=0, + ) + trimmed_lines = [line[min_indent:] for line in script_lines] + if trimmed_lines[0] == "": + trimmed_lines = trimmed_lines[1:] + if trimmed_lines[-1] == "": + trimmed_lines = trimmed_lines[:-1] + return "\n".join(trimmed_lines) + + +def InstallKokkosScriptModfile(settings: Settings) -> tuple[str, str]: + if settings.apps.get("Kokkos", False): + prefix = settings.install_prefix + version = settings.kokkos_version + backend = settings.kokkos_backend + arch = settings.kokkos_arch.strip() + modules = "\n".join( + [f"module load {module} && \\" for module in settings.module_loads] + ) + src_path = f"{prefix}/src/kokkos" + install_path = ( + f"{prefix}/kokkos/{version}/{backend}{f'_{arch}' if arch else ''}" + ) + if os.path.exists(install_path) and not settings.overwrite: + raise FileExistsError( + f"Kokkos install path {install_path} already exists and overwrite is disabled" + ) + + extra_flags = "-D ".join(settings.extra_kokkos_flags) + cxx_standard = 20 if tuple(map(int, version.split("."))) >= (5, 0, 0) else 17 + + if arch == "": + arch = "NATIVE" + arch = arch.upper() + + script = f""" + # Kokkos installation + {modules} + rm -rf {src_path} && \\ + git clone https://github.com/kokkos/kokkos.git {src_path} && \\ + cd {src_path} && \\ + git checkout {version} && \\ + cmake -B build \\ + -D CMAKE_CXX_STANDARD={cxx_standard} \\ + -D CMAKE_CXX_EXTENSIONS=OFF \\ + -D CMAKE_POSITION_INDEPENDENT_CODE=TRUE \\ + -D Kokkos_ARCH_{arch}=ON {f'-D Kokkos_ENABLE_{backend.upper()}=ON' if backend != 'cpu' else ''} \\ + -D CMAKE_INSTALL_PREFIX={install_path} {extra_flags} && \\ + cmake --build build -j $(nproc) && \\ + cmake --install build + """ + + modfile = f""" + #%Module1.0###################################################################### + ## + ## Kokkos @ {backend} @ {arch} modulefile + ## + ################################################################################# + proc ModulesHelp {{ }} {{ + puts stderr \"\\tKokkos @ {backend} @ {arch}\\n\" + }} + + module-whatis \"Sets up Kokkos @ {backend} @ {arch}\" + + conflict kokkos + {modules} + + set basedir {install_path} + prepend-path PATH $basedir/bin + setenv Kokkos_DIR $basedir + + setenv Kokkos_ARCH_{arch} ON + {f'setenv Kokkos_ENABLE_{backend.upper()} ON' if backend != 'cpu' else ''} + """ + + return (unindent(script), unindent(modfile)) + + else: + return ("""# skipping Kokkos install""", "") + + +def InstallAdios2Script(settings: Settings) -> tuple[str, str]: + if settings.apps.get("adios2", False): + prefix = settings.install_prefix + version = settings.adios2_version + mpi_mode = settings.adios2_mpi + modules = "\n".join( + [f"module load {module} && \\" for module in settings.module_loads] + ) + src_path = f"{prefix}/src/adios2" + install_path = f"{prefix}/adios2/{version}/{mpi_mode}" + if os.path.exists(install_path) and not settings.overwrite: + raise FileExistsError( + f"Adios2 install path {install_path} already exists and overwrite is disabled" + ) + + extra_flags = "-D ".join(settings.extra_adios2_flags) + cxx_standard = ( + 20 + if tuple(map(int, settings.kokkos_version.split("."))) >= (5, 0, 0) + else 17 + ) + + with_mpi = "ON" if mpi_mode == "mpi" else "OFF" + + script = f""" + # Adios2 installation + {modules} + rm -rf {src_path} && \\ + git clone https://github.com/ornladios/ADIOS2.git {src_path} && \\ + cd {src_path} && \\ + git checkout v{version} && \\ + cmake -B build \\ + -D CMAKE_CXX_STANDARD={cxx_standard} \\ + -D CMAKE_CXX_EXTENSIONS=OFF \\ + -D CMAKE_POSITION_INDEPENDENT_CODE=TRUE \\ + -D BUILD_SHARED_LIBS=ON \\ + -D ADIOS2_USE_Python=OFF \\ + -D ADIOS2_USE_Fortran=OFF \\ + -D ADIOS2_USE_ZeroMQ=OFF \\ + -D BUILD_TESTING=OFF \\ + -D ADIOS2_BUILD_EXAMPLES=OFF \\ + -D ADIOS2_USE_HDF5=OFF \\ + -D ADIOS2_USE_MPI={with_mpi} \\ + -D CMAKE_INSTALL_PREFIX={install_path} {extra_flags} && \\ + cmake --build build -j $(nproc) && \\ + cmake --install build + """ + + modfile = f""" + #%Module1.0###################################################################### + ## + ## ADIOS2 @ {mpi_mode} modulefile + ## + ################################################################################# + proc ModulesHelp {{ }} {{ + puts stderr \"\\tADIOS2 @ {mpi_mode}\\n\" + }} + + module-whatis \"Sets up ADIOS2 @ {mpi_mode}\" + + conflict adios2 + {modules} + + set basedir {install_path} + prepend-path PATH $basedir/bin + setenv ADIOS2_DIR $basedir + + setenv ADIOS2_USE_MPI {with_mpi} + """ + + return (unindent(script), unindent(modfile)) + + else: + return ("""# skipping Adios2 install""", "") + + +def InstallNt2pyScript(settings: Settings) -> str: + if settings.apps.get("nt2py", False): + prefix = settings.install_prefix + modules = "\n".join( + [f"module load {module} && \\" for module in settings.module_loads] + ) + install_path = f"{prefix}/.venv" + + script = f""" + # nt2py installation + {modules} + rm -rf {install_path} && \\ + python3 -m venv {install_path} && \\ + source {install_path}/bin/activate && \\ + pip install nt2py && \\ + deactivate + """ + return unindent(script) + else: + return """# skipping nt2py install""" + + +PRESETS = { + "rusty": {"module_loads": []}, + "stellar": {"module_loads": []}, + "perlmutter": {"module_loads": []}, + "frontier": {"module_loads": []}, + "aurora": {"module_loads": []}, +} + + +def apply_preset(s: Settings, name: str) -> None: + s.cluster = name + s.install_prefix = os.path.join(os.path.expanduser("~"), ".entity") + cluster_preset = PRESETS.get(name, {}) + s.apps["Kokkos"] = True + s.apps["adios2"] = True + s.apps["nt2py"] = True + s.module_loads = cluster_preset.get("module_loads", []) + s.extra_kokkos_flags = cluster_preset.get("extra_kokkos_flags", []) + s.extra_adios2_flags = cluster_preset.get("extra_adios2_flags", []) + + +def on_install_confirmed(settings: Settings) -> None: + global MESSAGE + os.makedirs(settings.install_prefix, exist_ok=True) + kokkos_script, kokkos_modfile = InstallKokkosScriptModfile(settings) + adios2_script, adios2_modfile = InstallAdios2Script(settings) + with open(os.path.join(settings.install_prefix, "install.sh"), "w") as f: + f.write("#!/usr/bin/env bash\n\n") + f.write(kokkos_script) + f.write("\n\n") + f.write(adios2_script) + f.write("\n") + if settings.write_modulefiles: + os.makedirs(os.path.join(settings.install_prefix, "modules"), exist_ok=True) + if kokkos_modfile != "": + kokkos_modfile_file = os.path.join( + settings.install_prefix, + "modules", + "kokkos", + settings.kokkos_backend + + ( + f"_{settings.kokkos_arch.strip()}" + if settings.kokkos_arch.strip() + else "" + ), + settings.kokkos_version, + ) + os.makedirs(os.path.dirname(kokkos_modfile_file), exist_ok=True) + if os.path.exists(kokkos_modfile_file) and not settings.overwrite: + raise FileExistsError( + f"modulefile {kokkos_modfile_file} already exists and overwrite is disabled" + ) + with open(kokkos_modfile_file, "w") as f: + f.write(kokkos_modfile) + if adios2_modfile != "": + adios2_modfile_file = os.path.join( + settings.install_prefix, + "modules", + "adios2", + settings.adios2_mpi, + settings.adios2_version, + ) + os.makedirs(os.path.dirname(adios2_modfile_file), exist_ok=True) + if os.path.exists(adios2_modfile_file) and not settings.overwrite: + raise FileExistsError( + f"modulefile {adios2_modfile_file} already exists and overwrite is disabled" + ) + with open(adios2_modfile_file, "w") as f: + f.write(adios2_modfile) + + os.chmod(os.path.join(settings.install_prefix, "install.sh"), 0o755) + MESSAGE = f"- installation script written to {os.path.join(settings.install_prefix, 'install.sh')}!\n" + MESSAGE += " please read and verify it before running.\n\n" + if settings.write_modulefiles: + MESSAGE += f"- module files have been written to {os.path.join(settings.install_prefix, 'modules')} directory.\n" + MESSAGE += f" add them to your .rc script as `module use --append {os.path.join(settings.install_prefix, 'modules')}`\n\n" + + if settings.apps.get("nt2py", False): + MESSAGE += ( + "- nt2py installed in a new virtual environment at " + f"{os.path.join(settings.install_prefix, '.venv')}.\n" + ) + MESSAGE += " activate it with `source {}/bin/activate`.\n\n".format( + os.path.join(settings.install_prefix, ".venv") + ) + + settings_json = os.path.join(settings.install_prefix, "settings.json") + with open(settings_json, "w") as f: + f.write(settings.to_json()) + return + + +@dataclass +class MenuItem: + label: str + hint: str = "" + right: Optional[Callable[[], str]] = None + on_enter: Optional[Callable[[], None]] = None + on_space: Optional[Callable[[], None]] = None + disabled: Optional[Callable[[], bool]] = None + + +class TuiExitInstall(Exception): + pass + + +class App: + def __init__(self, stdscr): + self.stdscr = stdscr + self.s = Settings() + if os.path.exists(os.path.join(self.s.install_prefix, "settings.json")): + with open(os.path.join(self.s.install_prefix, "settings.json"), "r") as f: + data = json.load(f) + self.s.from_json(json.dumps(data)) + + self.state = "mainmenu" + self.stack: List[Tuple[str, int]] = [] + self.selected = 0 + self.scroll = 0 + self.message = "use arrows or j/k" + + self.mod_sel = 0 + self.mod_scroll = 0 + + self._init_curses() + + def _init_curses(self) -> None: + curses.curs_set(0) + self.stdscr.keypad(True) + curses.noecho() + curses.cbreak() + + if curses.has_colors(): + curses.start_color() + curses.use_default_colors() + curses.init_pair(PAIR_TITLE, COLOR_TITLE_FG, -1) + curses.init_pair(PAIR_TEXT, COLOR_TEXT_FG, -1) + curses.init_pair(PAIR_SELECTED, COLOR_SELECTED_FG, COLOR_SELECTED_BG) + curses.init_pair(PAIR_HINT, COLOR_HINT_FG, -1) + curses.init_pair(PAIR_OK, COLOR_OK_FG, -1) + curses.init_pair(PAIR_ERR, COLOR_ERR_FG, -1) + curses.init_pair(PAIR_KEY, COLOR_KEY_FG, -1) + curses.init_pair(PAIR_DIM, COLOR_DIM_FG, -1) + + def cp(self, pair_id: int) -> int: + return curses.color_pair(pair_id) if curses.has_colors() else 0 + + # ----- formatting helpers ----- + + def checkbox(self, on: bool) -> str: + return "[x]" if on else "[ ]" + + def pill(self, on: bool) -> str: + return "[on]" if on else "[off]" + + def kokkos_right(self) -> str: + arch = self.s.kokkos_arch.strip() or "-" + return f"{self.s.kokkos_version} · {self.s.kokkos_backend} · {arch}" + + def adios2_right(self) -> str: + return f"{self.s.adios2_version} · {self.s.adios2_mpi}" + + # ----- nav stack ----- + + def push(self, st: str) -> None: + self.stack.append((self.state, self.selected)) + self.state = st + self.selected = 0 + self.scroll = 0 + self.message = "" + + def pop(self) -> None: + if self.stack: + self.state, self.selected = self.stack.pop() + else: + self.state, self.selected = "mainmenu", 0 + self.scroll = 0 + self.message = "" + + # ----- drawing ----- + + def add(self, y: int, x: int, s: str, attr: int = 0) -> None: + try: + self.stdscr.addstr(y, x, s, attr) + except curses.error: + pass + + def hline(self, y: int) -> None: + _, w = self.stdscr.getmaxyx() + try: + self.stdscr.hline(y, 0, curses.ACS_HLINE, max(0, w - 1)) + except curses.error: + pass + + def draw_keybar(self, y: int, x: int, pairs: List[Tuple[str, str]]) -> None: + cur_x = x + for key, action in pairs: + self.add(y, cur_x, key, self.cp(PAIR_KEY) | curses.A_BOLD) + cur_x += len(key) + self.add(y, cur_x, " ", self.cp(PAIR_DIM)) + cur_x += 1 + self.add(y, cur_x, action, self.cp(PAIR_HINT)) + cur_x += len(action) + self.add(y, cur_x, " ", self.cp(PAIR_DIM)) + cur_x += 3 + + def breadcrumb(self) -> str: + if self.state == "mainmenu": + return "mainmenu" + if self.state == "custom": + return "mainmenu › custom install" + if self.state == "dependencies": + return "mainmenu › custom install › dependencies" + if self.state == "versions": + return "mainmenu › custom install › versions" + if self.state == "options": + return "mainmenu › custom install › options" + if self.state == "cluster": + return "mainmenu › cluster-specific" + if self.state == "preset_applied": + return f"mainmenu › cluster-specific › {self.s.cluster}" + return "mainmenu" + + def draw_menu(self, title: str, prompt: str, items: List[MenuItem]) -> None: + self.stdscr.erase() + h, w = self.stdscr.getmaxyx() + + self.add(0, 2, title, self.cp(PAIR_TITLE) | curses.A_BOLD) + bc = self.breadcrumb() + self.add(0, max(2, w - 2 - len(bc)), bc, self.cp(PAIR_DIM)) + + self.draw_keybar( + 1, + 2, + [ + ("↑/↓/j/k", "move"), + ("enter", "select"), + ("space", "toggle/cycle"), + ("b", "back"), + ("q", "quit"), + ], + ) + self.hline(2) + + status1 = f"cluster: {self.s.cluster} write modulefiles: {self.pill(self.s.write_modulefiles)} module loads: {len(self.s.module_loads)}" + status2 = ( + f"prefix: {self.s.install_prefix} dependencies: {self.s.apps_summary()}" + ) + self.add(3, 2, status1[: w - 4], self.cp(PAIR_TEXT)) + self.add(4, 2, status2[: w - 4], self.cp(PAIR_TEXT)) + self.hline(5) + + self.add(6, 2, prompt[: w - 4], self.cp(PAIR_TEXT) | curses.A_BOLD) + + list_y = 8 + footer_h = 3 + view_h = max(1, h - list_y - footer_h) + n = len(items) + + if n == 0: + self.add(list_y, 2, "(empty)", self.cp(PAIR_HINT)) + else: + self.selected = max(0, min(self.selected, n - 1)) + + if self.selected < self.scroll: + self.scroll = self.selected + if self.selected >= self.scroll + view_h: + self.scroll = self.selected - view_h + 1 + self.scroll = max(0, min(self.scroll, max(0, n - view_h))) + + shown = items[self.scroll : self.scroll + view_h] + + for i, it in enumerate(shown): + idx = self.scroll + i + sel = idx == self.selected + dis = bool(it.disabled and it.disabled()) + + row_attr = ( + self.cp(PAIR_SELECTED) | curses.A_BOLD + if sel + else (self.cp(PAIR_DIM) if dis else self.cp(PAIR_TEXT)) + ) + self.add(list_y + i, 2, f" {it.label}"[: w - 4], row_attr) + + if it.right: + rt = (it.right() or "").strip() + if rt: + rt = rt[: max(0, w - 6)] + x = max(2, w - 2 - len(rt)) + rt_attr = ( + row_attr + if sel + else (self.cp(PAIR_HINT) if not dis else self.cp(PAIR_DIM)) + ) + self.add(list_y + i, x, rt, rt_attr) + + if sel and it.hint: + self.add( + list_y + i, + min(w - 4, 30), + f" {it.hint}"[: w - 4], + self.cp(PAIR_HINT), + ) + + self.hline(h - 3) + msg = self.message or "" + if msg: + is_err = msg.startswith("error") + attr = (self.cp(PAIR_ERR) if is_err else self.cp(PAIR_OK)) | curses.A_BOLD + self.add(h - 2, 2, msg[: w - 4], attr) + self.stdscr.refresh() + + # ----- modals ----- + + def input_box(self, title: str, prompt: str, initial: str) -> Optional[str]: + h, w = self.stdscr.getmaxyx() + win_h, win_w = 9, min(86, max(46, w - 6)) + top, left = max(0, (h - win_h) // 2), max(0, (w - win_w) // 2) + + win = curses.newwin(win_h, win_w, top, left) + win.keypad(True) + win.border() + + win.addstr(1, 2, title[: win_w - 4], self.cp(PAIR_TITLE) | curses.A_BOLD) + win.addstr(2, 2, prompt[: win_w - 4], self.cp(PAIR_TEXT)) + + buf = list(initial) + curses.curs_set(1) + + while True: + win.addstr(4, 2, " " * (win_w - 4), self.cp(PAIR_TEXT)) + text = "".join(buf) + if len(text) > win_w - 4: + text = text[-(win_w - 4) :] + win.addstr(4, 2, text, self.cp(PAIR_TEXT) | curses.A_BOLD) + win.addstr(6, 2, "enter=ok esc=cancel", self.cp(PAIR_DIM)) + win.refresh() + + ch = win.getch() + if ch == 27: + curses.curs_set(0) + return None + if ch in (curses.KEY_ENTER, 10, 13): + curses.curs_set(0) + return "".join(buf).strip() + if ch in (curses.KEY_BACKSPACE, 127, 8): + if buf: + buf.pop() + elif 32 <= ch <= 126: + buf.append(chr(ch)) + + def confirm_install(self) -> bool: + arch = self.s.kokkos_arch.strip() or "-" + lines = [ + f"cluster: {self.s.cluster}", + f"overwrite existing files: {self.pill(self.s.overwrite)}", + f"write modulefiles: {self.pill(self.s.write_modulefiles)}", + f"module loads: {len(self.s.module_loads)}", + f"prefix: {self.s.install_prefix}", + f"dependencies: {self.s.apps_summary()}", + f"kokkos: {self.s.kokkos_version} · {self.s.kokkos_backend} · {arch}", + f"adios2: {self.s.adios2_version} · {self.s.adios2_mpi}", + "", + "confirm install?", + ] + + h, w = self.stdscr.getmaxyx() + win_h, win_w = min(16, max(10, h - 6)), min(94, max(52, w - 6)) + top, left = max(0, (h - win_h) // 2), max(0, (w - win_w) // 2) + + win = curses.newwin(win_h, win_w, top, left) + win.keypad(True) + win.border() + win.addstr(1, 2, "confirm", self.cp(PAIR_TITLE) | curses.A_BOLD) + + y = 3 + for ln in lines[: win_h - 6]: + win.addstr(y, 2, ln[: win_w - 4], self.cp(PAIR_TEXT)) + y += 1 + + win.addstr(win_h - 3, 2, "y=yes n=no", self.cp(PAIR_DIM)) + win.refresh() + + while True: + ch = win.getch() + if ch in (ord("y"), ord("Y")): + return True + if ch in (ord("n"), ord("N"), 27): + return False + + # ----- helpers ----- + + def cycle(self, current: str, options: List[str]) -> str: + if current not in options: + return options[0] + i = options.index(current) + return options[(i + 1) % len(options)] + + # ----- module editor ----- + + def module_editor(self) -> None: + while True: + self.stdscr.erase() + h, w = self.stdscr.getmaxyx() + + self.add(0, 2, "module lines", self.cp(PAIR_TITLE) | curses.A_BOLD) + self.draw_keybar( + 1, + 2, + [ + ("↑/↓/j/k", "move"), + ("enter", "edit"), + ("a", "add"), + ("d", "delete"), + ("u/m", "reorder"), + ("b", "back"), + ], + ) + self.hline(2) + + self.add(3, 2, f"lines: {len(self.s.module_loads)}", self.cp(PAIR_TEXT)) + self.hline(4) + + lines = self.s.module_loads + n = len(lines) + list_y = 6 + view_h = max(1, h - list_y - 3) + + if n == 0: + self.add(list_y, 2, "(empty) press a to add", self.cp(PAIR_HINT)) + else: + self.mod_sel = max(0, min(self.mod_sel, n - 1)) + if self.mod_sel < self.mod_scroll: + self.mod_scroll = self.mod_sel + if self.mod_sel >= self.mod_scroll + view_h: + self.mod_scroll = self.mod_sel - view_h + 1 + self.mod_scroll = max(0, min(self.mod_scroll, max(0, n - view_h))) + + shown = lines[self.mod_scroll : self.mod_scroll + view_h] + for i, ln in enumerate(shown): + idx = self.mod_scroll + i + sel = idx == self.mod_sel + attr = ( + self.cp(PAIR_SELECTED) | curses.A_BOLD + if sel + else self.cp(PAIR_TEXT) + ) + self.add(list_y + i, 2, f" {ln}"[: w - 4], attr) + + self.hline(h - 3) + self.add( + h - 2, + 2, + "tip: example: cuda/12.9"[: w - 4], + self.cp(PAIR_HINT), + ) + self.stdscr.refresh() + + ch = self.stdscr.getch() + if ch in (ord("q"), ord("Q"), ord("b"), 8, 127): + return + + n = len(self.s.module_loads) + self.mod_sel = 0 if n == 0 else max(0, min(self.mod_sel, n - 1)) + + if ch in (curses.KEY_UP, ord("k"), ord("K")) and n: + self.mod_sel = (self.mod_sel - 1) % n + continue + if ch in (curses.KEY_DOWN, ord("j"), ord("J")) and n: + self.mod_sel = (self.mod_sel + 1) % n + continue + + if ch in (ord("a"), ord("A")): + val = self.input_box("add module line", "example: cuda/12.9", "") + if val: + self.s.module_loads.append(val) + self.mod_sel = len(self.s.module_loads) - 1 + continue + + if ch in (ord("d"), ord("D")): + if n == 0: + continue + val = self.input_box("delete line", "type 'delete' to confirm:", "") + if val == "delete": + del self.s.module_loads[self.mod_sel] + self.mod_sel = max( + 0, min(self.mod_sel, len(self.s.module_loads) - 1) + ) + continue + + if ch in (ord("u"), ord("U")): + if n >= 2 and self.mod_sel > 0: + i = self.mod_sel + self.s.module_loads[i - 1], self.s.module_loads[i] = ( + self.s.module_loads[i], + self.s.module_loads[i - 1], + ) + self.mod_sel -= 1 + continue + + if ch in (ord("m"), ord("M")): + if n >= 2 and self.mod_sel < n - 1: + i = self.mod_sel + self.s.module_loads[i + 1], self.s.module_loads[i] = ( + self.s.module_loads[i], + self.s.module_loads[i + 1], + ) + self.mod_sel += 1 + continue + + if ch in (curses.KEY_ENTER, 10, 13): + if n == 0: + continue + cur = self.s.module_loads[self.mod_sel] + val = self.input_box("edit module line", "edit the selected line:", cur) + if val is not None and val.strip(): + self.s.module_loads[self.mod_sel] = val.strip() + continue + + # ----- menus ----- + + def versions_menu(self) -> Tuple[str, str, List[MenuItem]]: + def edit_kokkos(): + val = self.input_box( + "kokkos version", "enter version/tag:", self.s.kokkos_version + ) + if val: + self.s.kokkos_version = val.strip() + + def edit_adios2(): + val = self.input_box( + "adios2 version", "enter version/tag:", self.s.adios2_version + ) + if val: + self.s.adios2_version = val.strip() + + return ( + "versions", + "set versions:", + [ + MenuItem( + "kokkos version", + "enter to edit", + right=lambda: self.s.kokkos_version, + on_enter=edit_kokkos, + ), + MenuItem( + "adios2 version", + "enter to edit", + right=lambda: self.s.adios2_version, + on_enter=edit_adios2, + ), + MenuItem("back", "return", on_enter=self.pop), + ], + ) + + def options_menu(self) -> Tuple[str, str, List[MenuItem]]: + def cycle_kokkos(): + self.s.kokkos_backend = self.cycle(self.s.kokkos_backend, KOKKOS_BACKENDS) + + def edit_kokkos_arch(): + val = self.input_box( + "kokkos arch", "enter arch text (free-form):", self.s.kokkos_arch + ) + if val is not None: + self.s.kokkos_arch = val.strip() + + def cycle_adios2(): + self.s.adios2_mpi = self.cycle(self.s.adios2_mpi, ADIOS2_MPI_MODES) + + return ( + "options", + "set build options:", + [ + MenuItem( + "kokkos backend", + "space cycles: cpu/cuda/hip/sycl", + right=lambda: self.s.kokkos_backend, + on_enter=cycle_kokkos, + on_space=cycle_kokkos, + disabled=lambda: not self.s.apps.get("Kokkos", False), + ), + MenuItem( + "kokkos arch", + "enter to edit (optional)", + right=lambda: (self.s.kokkos_arch.strip() or "-"), + on_enter=edit_kokkos_arch, + disabled=lambda: not self.s.apps.get("Kokkos", False), + ), + MenuItem( + "adios2 mpi", + "space cycles: non-mpi/mpi", + right=lambda: self.s.adios2_mpi, + on_enter=cycle_adios2, + on_space=cycle_adios2, + disabled=lambda: not self.s.apps.get("adios2", False), + ), + MenuItem("back", "return", on_enter=self.pop), + ], + ) + + def menu_main(self) -> Tuple[str, str, List[MenuItem]]: + return ( + "entity deps", + "main menu:", + [ + MenuItem( + "custom install", + "edit settings then install", + on_enter=lambda: self.push("custom"), + ), + MenuItem( + "cluster-specific", + "apply a cluster-specific preset (editable)", + on_enter=lambda: self.push("cluster"), + ), + MenuItem("exit", "", on_enter=lambda: setattr(self, "state", "exit")), + ], + ) + + def menu_custom(self) -> Tuple[str, str, List[MenuItem]]: + def toggle_write_modulefiles(): + self.s.write_modulefiles = not self.s.write_modulefiles + + def toggle_overwrite(): + self.s.overwrite = not self.s.overwrite + + def edit_prefix(): + val = self.input_box( + "install location", "enter install prefix:", self.s.install_prefix + ) + if val: + self.s.install_prefix = os.path.expanduser(val.strip()) + + def go_apps(): + self.push("dependencies") + + def go_versions(): + self.push("versions") + + def go_options(): + self.push("options") + + def do_install(): + if not self.confirm_install(): + self.message = "cancelled." + return + on_install_confirmed(self.s) + raise TuiExitInstall + + return ( + "custom install", + "settings:", + [ + MenuItem( + "overwrite existing files", + "whether to overwrite existing files", + right=lambda: "enabled" if self.s.overwrite else "disabled", + on_enter=toggle_overwrite, + on_space=toggle_overwrite, + ), + MenuItem( + "write modulefiles", + "whether to create module files", + right=lambda: "enabled" if self.s.write_modulefiles else "disabled", + on_enter=toggle_write_modulefiles, + on_space=toggle_write_modulefiles, + ), + MenuItem( + "module load lines", + "add/remove modules to load", + right=lambda: f"{len(self.s.module_loads)} entry(s)", + on_enter=self.module_editor, + ), + MenuItem( + "install location", + "root location where modules and dependencies are installed", + right=lambda: self.s.install_prefix, + on_enter=edit_prefix, + ), + MenuItem( + "dependencies to install", + "select which dependencies to install", + right=lambda: self.s.apps_summary(), + on_enter=go_apps, + ), + MenuItem( + "versions", + "edit dependency versions", + right=lambda: " · ".join( + [ + a + for (a, ae) in zip( + [ + f"kokkos {self.s.kokkos_version}", + f"adios2 {self.s.adios2_version}", + ], + [ + self.s.apps.get(app, False) + for app in ["Kokkos", "adios2"] + ], + ) + if ae + ] + ), + on_enter=go_versions, + ), + MenuItem( + "options", + "pick backends/architectures/mpi", + right=lambda: " · ".join( + [ + a + for (a, ae) in zip( + [ + f"kokkos {self.s.kokkos_backend}/{self.s.kokkos_arch.strip() or '-'}", + f"adios2 {self.s.adios2_mpi}", + ], + [ + self.s.apps.get(app, False) + for app in ["Kokkos", "adios2"] + ], + ) + if ae + ] + ), + on_enter=go_options, + ), + MenuItem("install", "", on_enter=do_install), + MenuItem("back", "", on_enter=self.pop), + ], + ) + + def menu_apps(self) -> Tuple[str, str, List[MenuItem]]: + def toggle(k: str): + self.s.apps[k] = not self.s.apps.get(k, False) + + return ( + "dependencies", + "select the dependencies:", + [ + MenuItem( + f"{self.checkbox(self.s.apps.get('Kokkos', False))} kokkos", + "", + on_enter=lambda: toggle("Kokkos"), + on_space=lambda: toggle("Kokkos"), + right=self.kokkos_right, + ), + MenuItem( + f"{self.checkbox(self.s.apps.get('adios2', False))} adios2", + "", + on_enter=lambda: toggle("adios2"), + on_space=lambda: toggle("adios2"), + right=self.adios2_right, + ), + MenuItem( + f"{self.checkbox(self.s.apps.get('nt2py', False))} nt2py", + "", + on_enter=lambda: toggle("nt2py"), + on_space=lambda: toggle("nt2py"), + ), + MenuItem("back", "", on_enter=self.pop), + ], + ) + + def menu_cluster(self) -> Tuple[str, str, List[MenuItem]]: + def choose(name: str): + apply_preset(self.s, name) + self.push("custom") + + return ( + "cluster-specific", + "pick a preset:", + [ + MenuItem("rusty", "apply preset", on_enter=lambda: choose("rusty")), + MenuItem("stellar", "apply preset", on_enter=lambda: choose("stellar")), + MenuItem( + "perlmutter", "apply preset", on_enter=lambda: choose("perlmutter") + ), + MenuItem( + "frontier", "apply preset", on_enter=lambda: choose("frontier") + ), + MenuItem("aurora", "apply preset", on_enter=lambda: choose("aurora")), + MenuItem("back", "", on_enter=self.pop), + ], + ) + + def get_menu(self) -> Tuple[str, str, List[MenuItem]]: + if self.state == "mainmenu": + return self.menu_main() + if self.state == "custom": + return self.menu_custom() + if self.state == "dependencies": + return self.menu_apps() + if self.state == "versions": + return self.versions_menu() + if self.state == "options": + return self.options_menu() + if self.state == "cluster": + return self.menu_cluster() + self.state = "mainmenu" + return self.menu_main() + + # ----- navigation ----- + + def is_disabled(self, it: MenuItem) -> bool: + return bool(it.disabled and it.disabled()) + + def move_sel(self, items: List[MenuItem], delta: int) -> None: + if not items: + return + n = len(items) + start = self.selected + for _ in range(n): + self.selected = (self.selected + delta) % n + if not self.is_disabled(items[self.selected]): + return + self.selected = start + + def activate(self, items: List[MenuItem], enter: bool) -> None: + if not items: + return + it = items[self.selected] + if self.is_disabled(it): + self.message = "error: option disabled." + return + fn = it.on_enter if enter else it.on_space + if fn: + fn() + + # ----- loop ----- + + def run(self) -> None: + while True: + if self.state == "exit": + return + + title, prompt, items = self.get_menu() + self.draw_menu(title, prompt, items) + + ch = self.stdscr.getch() + + if ch in (ord("q"), ord("Q")): + self.state = "exit" + continue + + if ch in (ord("b"), 8, 127): + self.pop() + continue + + if ch in (curses.KEY_UP, ord("k"), ord("K")): + self.move_sel(items, -1) + continue + + if ch in (curses.KEY_DOWN, ord("j"), ord("J")): + self.move_sel(items, +1) + continue + + if ch in (curses.KEY_ENTER, 10, 13): + self.activate(items, enter=True) + continue + + if ch == ord(" "): + self.activate(items, enter=False) + continue + + +def _wrapper_capture(stdscr) -> None: + app = App(stdscr) + try: + app.run() + except TuiExitInstall: + on_install_confirmed(app.s) + raise + + +if __name__ == "__main__": + try: + curses.wrapper(_wrapper_capture) + raise SystemExit(0) + except TuiExitInstall: + print(MESSAGE) + raise SystemExit(0) + except KeyboardInterrupt: + raise SystemExit(130) From f4740be889e9dfdf880f0de66c242ca378154976 Mon Sep 17 00:00:00 2001 From: haykh Date: Thu, 22 Jan 2026 17:49:45 -0500 Subject: [PATCH 15/21] concepts revised --- src/archetypes/energy_dist.h | 5 +- src/archetypes/field_setter.h | 94 +++++---- src/archetypes/particle_injector.h | 10 +- src/archetypes/problem_generator.h | 4 +- src/archetypes/spatial_dist.h | 4 +- src/archetypes/traits.h | 159 ++++++++++++++ src/engines/engine.hpp | 25 ++- src/engines/engine_init.cpp | 80 ++++--- src/engines/engine_printer.cpp | 6 +- src/engines/engine_run.cpp | 234 ++++++++++----------- src/engines/engine_traits.h | 28 --- src/engines/grpic.hpp | 4 +- src/engines/srpic.hpp | 10 +- src/engines/traits.h | 26 +++ src/entity.cpp | 22 +- src/framework/domain/domain.h | 12 +- src/framework/domain/mesh.h | 10 +- src/framework/domain/metadomain.h | 9 +- src/framework/simulation.h | 2 +- src/global/arch/traits.h | 327 +++++------------------------ src/global/tests/CMakeLists.txt | 1 + src/global/tests/traits.cpp | 152 ++++++++++++++ src/global/utils/formatting.h | 28 ++- src/kernels/ampere_gr.hpp | 11 +- src/kernels/ampere_sr.hpp | 11 +- src/kernels/aux_fields_gr.hpp | 15 +- src/kernels/currents_deposit.hpp | 11 +- src/kernels/divergences.hpp | 5 +- src/kernels/faraday_gr.hpp | 7 +- src/kernels/faraday_sr.hpp | 7 +- src/kernels/fields_bcs.hpp | 239 ++++++++++++--------- src/kernels/fields_to_phys.hpp | 5 +- src/kernels/injectors.hpp | 32 +-- src/kernels/particle_moments.hpp | 9 +- src/kernels/particle_pusher_gr.hpp | 19 +- src/kernels/particle_pusher_sr.hpp | 9 +- src/kernels/prtls_to_phys.hpp | 9 +- src/kernels/reduced_stats.hpp | 15 +- src/metrics/traits.h | 162 ++++++++++++++ 39 files changed, 1081 insertions(+), 737 deletions(-) create mode 100644 src/archetypes/traits.h delete mode 100644 src/engines/engine_traits.h create mode 100644 src/engines/traits.h create mode 100644 src/global/tests/traits.cpp create mode 100644 src/metrics/traits.h diff --git a/src/archetypes/energy_dist.h b/src/archetypes/energy_dist.h index f387922d..230e4a93 100644 --- a/src/archetypes/energy_dist.h +++ b/src/archetypes/energy_dist.h @@ -22,11 +22,12 @@ #include "global.h" #include "arch/kokkos_aliases.h" -#include "arch/traits.h" #include "utils/comparators.h" #include "utils/error.h" #include "utils/numeric.h" +#include "metrics/traits.h" + #include #include @@ -34,7 +35,7 @@ namespace arch { using namespace ntt; template - requires traits::metric::HasD + requires metric::traits::HasD struct EnergyDistribution { static constexpr auto D = M::Dim; diff --git a/src/archetypes/field_setter.h b/src/archetypes/field_setter.h index 56a24af1..d4fab7a3 100644 --- a/src/archetypes/field_setter.h +++ b/src/archetypes/field_setter.h @@ -27,36 +27,34 @@ #include "arch/traits.h" #include "utils/numeric.h" +#include "metrics/traits.h" + #include namespace arch { using namespace ntt; template - requires traits::metric::HasD && - ((S == SimEngine::SRPIC && traits::metric::HasConvert && - traits::metric::HasTransform_i) || - (S == SimEngine::GRPIC && traits::metric::HasConvert_i)) + requires metric::traits::HasD && + ((S == SimEngine::SRPIC && metric::traits::HasConvert && + metric::traits::HasTransform_i) || + (S == SimEngine::GRPIC && metric::traits::HasConvert_i)) && + (S == SimEngine::SRPIC && + (::traits::fieldsetter::HasEx1 || + ::traits::fieldsetter::HasEx2 || + ::traits::fieldsetter::HasEx3 || + ::traits::fieldsetter::HasBx1 || + ::traits::fieldsetter::HasBx2 || + ::traits::fieldsetter::HasBx3) || + (S == SimEngine::GRPIC && + (::traits::fieldsetter::HasDx1 && + ::traits::fieldsetter::HasDx2 && + ::traits::fieldsetter::HasDx3) || + (::traits::fieldsetter::HasBx1 && + ::traits::fieldsetter::HasBx2 && + ::traits::fieldsetter::HasBx3))) class SetEMFields_kernel { static constexpr Dimension D = M::Dim; - static constexpr bool defines_ex1 = traits::has_method::value; - static constexpr bool defines_ex2 = traits::has_method::value; - static constexpr bool defines_ex3 = traits::has_method::value; - static constexpr bool defines_bx1 = traits::has_method::value; - static constexpr bool defines_bx2 = traits::has_method::value; - static constexpr bool defines_bx3 = traits::has_method::value; - static constexpr bool defines_dx1 = traits::has_method::value; - static constexpr bool defines_dx2 = traits::has_method::value; - static constexpr bool defines_dx3 = traits::has_method::value; - - static_assert(defines_ex1 || defines_ex2 || defines_ex3 || defines_bx1 || - defines_bx2 || defines_bx3 || defines_dx1 || defines_dx2 || - defines_dx3, - "No field initializer defined"); - static_assert((S != SimEngine::GRPIC) || - (defines_dx1 == defines_dx2 && defines_dx2 == defines_dx3 && - defines_bx1 == defines_bx2 && defines_bx2 == defines_bx3), - "In GR mode, all components must be defined or none"); ndfield_t EM; const I finit; @@ -75,37 +73,37 @@ namespace arch { const auto i1_ = COORD(i1); coord_t x_Phys { ZERO }; if constexpr (S == SimEngine::SRPIC) { - if constexpr (defines_ex1) { + if constexpr (::traits::fieldsetter::HasEx1) { metric.template convert({ i1_ + HALF }, x_Phys); EM(i1, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( { i1_ + HALF }, finit.ex1(x_Phys)); } - if constexpr (defines_ex2) { + if constexpr (::traits::fieldsetter::HasEx2) { metric.template convert({ i1_ }, x_Phys); EM(i1, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( { i1_ }, finit.ex2(x_Phys)); } - if constexpr (defines_ex3) { + if constexpr (::traits::fieldsetter::HasEx3) { metric.template convert({ i1_ }, x_Phys); EM(i1, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( { i1_ }, finit.ex3(x_Phys)); } - if constexpr (defines_bx1) { + if constexpr (::traits::fieldsetter::HasBx1) { metric.template convert({ i1_ }, x_Phys); EM(i1, em::bx1) = metric.template transform<1, Idx::T, Idx::U>( { i1_ }, finit.bx1(x_Phys)); } - if constexpr (defines_bx2) { + if constexpr (::traits::fieldsetter::HasBx2) { metric.template convert({ i1_ + HALF }, x_Phys); EM(i1, em::bx2) = metric.template transform<2, Idx::T, Idx::U>( { i1_ + HALF }, finit.bx2(x_Phys)); } - if constexpr (defines_bx3) { + if constexpr (::traits::fieldsetter::HasBx3) { metric.template convert({ i1_ + HALF }, x_Phys); EM(i1, em::bx3) = metric.template transform<3, Idx::T, Idx::U>( { i1_ + HALF }, @@ -126,37 +124,37 @@ namespace arch { // srpic if constexpr (S == SimEngine::SRPIC) { coord_t x_Phys { ZERO }; - if constexpr (defines_ex1) { + if constexpr (::traits::fieldsetter::HasEx1) { metric.template convert({ i1_ + HALF, i2_ }, x_Phys); EM(i1, i2, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( { i1_ + HALF, i2_ }, finit.ex1(x_Phys)); } - if constexpr (defines_ex2) { + if constexpr (::traits::fieldsetter::HasEx2) { metric.template convert({ i1_, i2_ + HALF }, x_Phys); EM(i1, i2, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( { i1_, i2_ + HALF }, finit.ex2(x_Phys)); } - if constexpr (defines_ex3) { + if constexpr (::traits::fieldsetter::HasEx3) { metric.template convert({ i1_, i2_ }, x_Phys); EM(i1, i2, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( { i1_, i2_ }, finit.ex3(x_Phys)); } - if constexpr (defines_bx1) { + if constexpr (::traits::fieldsetter::HasBx1) { metric.template convert({ i1_, i2_ + HALF }, x_Phys); EM(i1, i2, em::bx1) = metric.template transform<1, Idx::T, Idx::U>( { i1_, i2_ + HALF }, finit.bx1(x_Phys)); } - if constexpr (defines_bx2) { + if constexpr (::traits::fieldsetter::HasBx2) { metric.template convert({ i1_ + HALF, i2_ }, x_Phys); EM(i1, i2, em::bx2) = metric.template transform<2, Idx::T, Idx::U>( { i1_ + HALF, i2_ }, finit.bx2(x_Phys)); } - if constexpr (defines_bx3) { + if constexpr (::traits::fieldsetter::HasBx3) { metric.template convert({ i1_ + HALF, i2_ + HALF }, x_Phys); EM(i1, i2, em::bx3) = metric.template transform<3, Idx::T, Idx::U>( @@ -165,7 +163,9 @@ namespace arch { } } else if constexpr (S == SimEngine::GRPIC) { // grpic - if constexpr (defines_dx1 && defines_dx2 && defines_dx3) { + if constexpr (::traits::fieldsetter::HasDx1 && + ::traits::fieldsetter::HasDx2 && + ::traits::fieldsetter::HasDx3) { const real_t x1_0 { metric.template convert<1, Crd::Cd, Crd::Ph>(i1_) }; const real_t x1_H { metric.template convert<1, Crd::Cd, Crd::Ph>( i1_ + HALF) }; @@ -182,7 +182,9 @@ namespace arch { EM(i1, i2, em::dx3) = finit.dx3({ x1_0, x2_0 }); } } - if constexpr (defines_bx1 && defines_bx2 && defines_bx3) { + if constexpr (::traits::fieldsetter::HasBx1 && + ::traits::fieldsetter::HasBx2 && + ::traits::fieldsetter::HasBx3) { const real_t x1_0 { metric.template convert<1, Crd::Cd, Crd::Ph>(i1_) }; const real_t x1_H { metric.template convert<1, Crd::Cd, Crd::Ph>( i1_ + HALF) }; @@ -215,28 +217,28 @@ namespace arch { coord_t x_Phys { ZERO }; if constexpr (S == SimEngine::SRPIC) { // srpic - if constexpr (defines_ex1) { + if constexpr (::traits::fieldsetter::HasEx1) { metric.template convert({ i1_ + HALF, i2_, i3_ }, x_Phys); EM(i1, i2, i3, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( { i1_ + HALF, i2_, i3_ }, finit.ex1(x_Phys)); } - if constexpr (defines_ex2) { + if constexpr (::traits::fieldsetter::HasEx2) { metric.template convert({ i1_, i2_ + HALF, i3_ }, x_Phys); EM(i1, i2, i3, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( { i1_, i2_ + HALF, i3_ }, finit.ex2(x_Phys)); } - if constexpr (defines_ex3) { + if constexpr (::traits::fieldsetter::HasEx3) { metric.template convert({ i1_, i2_, i3_ + HALF }, x_Phys); EM(i1, i2, i3, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( { i1_, i2_, i3_ + HALF }, finit.ex3(x_Phys)); } - if constexpr (defines_bx1) { + if constexpr (::traits::fieldsetter::HasBx1) { metric.template convert( { i1_, i2_ + HALF, i3_ + HALF }, x_Phys); @@ -244,7 +246,7 @@ namespace arch { { i1_, i2_ + HALF, i3_ + HALF }, finit.bx1(x_Phys)); } - if constexpr (defines_bx2) { + if constexpr (::traits::fieldsetter::HasBx2) { metric.template convert( { i1_ + HALF, i2_, i3_ + HALF }, x_Phys); @@ -252,7 +254,7 @@ namespace arch { { i1_ + HALF, i2_, i3_ + HALF }, finit.bx2(x_Phys)); } - if constexpr (defines_bx3) { + if constexpr (::traits::fieldsetter::HasBx3) { metric.template convert( { i1_ + HALF, i2_ + HALF, i3_ }, x_Phys); @@ -272,7 +274,9 @@ namespace arch { const real_t x3_H { metric.template convert<3, Crd::Cd, Crd::Ph>( i3_ + HALF) }; - if constexpr (defines_dx1 && defines_dx2 && defines_dx3) { + if constexpr (::traits::fieldsetter::HasDx1 && + ::traits::fieldsetter::HasDx2 && + ::traits::fieldsetter::HasDx3) { { // dx1 EM(i1, i2, i3, em::dx1) = finit.dx1({ x1_H, x2_0, x3_0 }); } @@ -283,7 +287,9 @@ namespace arch { EM(i1, i2, i3, em::dx3) = finit.dx3({ x1_0, x2_0, x3_H }); } } - if constexpr (defines_bx1 && defines_bx2 && defines_bx3) { + if constexpr (::traits::fieldsetter::HasBx1 && + ::traits::fieldsetter::HasBx2 && + ::traits::fieldsetter::HasBx3) { { // bx1 EM(i1, i2, i3, em::bx1) = finit.bx1({ x1_0, x2_H, x3_H }); } diff --git a/src/archetypes/particle_injector.h b/src/archetypes/particle_injector.h index 1543e265..a5239a02 100644 --- a/src/archetypes/particle_injector.h +++ b/src/archetypes/particle_injector.h @@ -22,6 +22,8 @@ #include "utils/error.h" #include "utils/numeric.h" +#include "metrics/traits.h" + #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" @@ -53,7 +55,7 @@ namespace arch { * - array_t: maximum coordinates of the region in computational coords */ template - requires traits::metric::HasD && traits::metric::HasConvert + requires metric::traits::HasD && metric::traits::HasConvert auto DeduceRegion(const Domain& domain, const boundaries_t& box) -> std::tuple, array_t> { if (not domain.mesh.Intersects(box)) { @@ -108,7 +110,7 @@ namespace arch { * - array_t: maximum coordinates of the region in computational coords */ template - requires traits::metric::HasD + requires metric::traits::HasD auto ComputeNumInject(const SimulationParams& params, const Domain& domain, real_t number_density, @@ -200,7 +202,7 @@ namespace arch { * @tparam ED2 Energy distribution type for species 2 */ template - requires traits::metric::HasD && traits::energydist::IsValid && + requires metric::traits::HasD && traits::energydist::IsValid && traits::energydist::IsValid inline void InjectUniform(const SimulationParams& params, Domain& domain, @@ -309,7 +311,7 @@ namespace arch { * @tparam SD Spatial distribution type */ template - requires traits::metric::HasD && traits::energydist::IsValid && + requires metric::traits::HasD && traits::energydist::IsValid && traits::energydist::IsValid && traits::spatialdist::IsValid inline void InjectNonUniform(const SimulationParams& params, Domain& domain, diff --git a/src/archetypes/problem_generator.h b/src/archetypes/problem_generator.h index 0f5e3b88..fc22428e 100644 --- a/src/archetypes/problem_generator.h +++ b/src/archetypes/problem_generator.h @@ -23,7 +23,7 @@ #include "enums.h" #include "global.h" -#include "arch/traits.h" +#include "metrics/traits.h" #include "framework/parameters/parameters.h" @@ -31,7 +31,7 @@ namespace arch { using namespace ntt; template - requires traits::metric::HasD + requires metric::traits::HasD and metric::traits::HasCoordType struct ProblemGenerator { static constexpr Dimension D { M::Dim }; static constexpr Coord C { M::CoordType }; diff --git a/src/archetypes/spatial_dist.h b/src/archetypes/spatial_dist.h index be42c3ba..bf8abe9c 100644 --- a/src/archetypes/spatial_dist.h +++ b/src/archetypes/spatial_dist.h @@ -23,11 +23,13 @@ #include "utils/error.h" #include "utils/numeric.h" +#include "metrics/traits.h" + namespace arch { using namespace ntt; template - requires traits::metric::HasD + requires metric::traits::HasD struct SpatialDistribution { static constexpr auto D = M::Dim; diff --git a/src/archetypes/traits.h b/src/archetypes/traits.h new file mode 100644 index 00000000..a0ca199f --- /dev/null +++ b/src/archetypes/traits.h @@ -0,0 +1,159 @@ +/** + * @file archetypes/traits.h + * @brief Defines a set of traits to check if archetype classes satisfy certain conditions + * @implements + * - arch::traits::energydist::IsValid<> - checks if energy distribution class has required operator() + * - arch::traits::spatialdist::IsValid<> - checks if spatial distribution class has required operator() + * - arch::traits::pgen::check_compatibility<> - checks if problem generator is compatible with given enums + * - arch::traits::pgen::compatible_with<> - defines compatible enums for problem generator + * - arch::traits::pgen::HasD<> - checks if problem generator has Dim static member + * - arch::traits::pgen::HasInitFlds<> - checks if problem generator has init_flds member + * - arch::traits::pgen::HasInitPrtls<> - checks if problem generator has InitPrtls method + * - arch::traits::pgen::HasExtForce<> - checks if problem generator has ext_force member + * - arch::traits::pgen::HasExtCurrent<> - checks if problem generator has ext_current member + * - arch::traits::pgen::HasAtmFields<> - checks if problem generator has AtmFields method + * - arch::traits::pgen::HasMatchFields<> - checks if problem generator has MatchFields method + * - arch::traits::pgen::HasMatchFieldsInX1<> - checks if problem generator has MatchFieldsInX1 method + * - arch::traits::pgen::HasMatchFieldsInX2<> - checks if problem generator has MatchFieldsInX2 method + * - arch::traits::pgen::HasMatchFieldsInX3<> - checks if problem generator has MatchFieldsInX3 method + * - arch::traits::pgen::HasFixFieldsConst<> - checks if problem generator has FixFieldsConst method + * - arch::traits::pgen::HasCustomPostStep<> - checks if problem generator has CustomPostStep method + * - arch::traits::pgen::HasCustomFieldOutput<> - checks if problem generator has CustomFieldOutput method + * - arch::traits::pgen::HasCustomStatOutput<> - checks if problem generator has CustomStat method + * @namespaces: + * - arch::traits:: + */ +#ifndef ARCHETYPES_TRAITS_H +#define ARCHETYPES_TRAITS_H + +#include "global.h" + +#include "arch/kokkos_aliases.h" + +namespace arch { + namespace traits { + + namespace energydist { + + template + concept IsValid = requires(const ED& edist, + const coord_t& x_Ph, + vec_t& v) { + { edist(x_Ph, v) } -> std::same_as; + }; + + } // namespace energydist + + namespace spatialdist { + + template + concept IsValid = requires(const SD& sdist, const coord_t& x_Ph) { + { sdist(x_Ph) } -> std::convertible_to; + }; + + } // namespace spatialdist + + namespace pgen { + + // checking compat for the problem generator + engine + template + struct check_compatibility { + template + static constexpr bool value(std::integer_sequence) { + return ((Is == N) || ...); + } + }; + + template + struct compatible_with { + static constexpr auto value = std::integer_sequence {}; + }; + + template + concept HasD = requires { + { PG::D } -> std::convertible_to; + }; + + template + concept HasInitFlds = requires(const PG& pgen) { pgen.init_flds; }; + + template + concept HasInitPrtls = requires(PG& pgen, D& domain) { + { pgen.InitPrtls(domain) } -> std::same_as; + }; + + template + concept HasExtForce = requires(const PG& pgen) { pgen.ext_force; }; + + template + concept HasExtCurrent = requires(const PG& pgen) { pgen.ext_current; }; + + template + concept HasAtmFields = requires(const PG& pgen, simtime_t time) { + pgen.AtmFields(time); + }; + + template + concept HasMatchFields = requires(const PG& pgen, simtime_t time) { + pgen.MatchFields(time); + }; + + template + concept HasMatchFieldsInX1 = requires(const PG& pgen, simtime_t time) { + pgen.MatchFieldsInX1(time); + }; + + template + concept HasMatchFieldsInX2 = requires(const PG& pgen, simtime_t time) { + pgen.MatchFieldsInX2(time); + }; + + template + concept HasMatchFieldsInX3 = requires(const PG& pgen, simtime_t time) { + pgen.MatchFieldsInX3(time); + }; + + template + concept HasFixFieldsConst = requires(const PG& pgen, + const bc_in& bc, + const ntt::em& comp) { + { + pgen.FixFieldsConst(bc, comp) + } -> std::convertible_to>; + }; + + template + concept HasCustomPostStep = requires(PG& pgen, + timestep_t s, + simtime_t t, + D& domain) { + { pgen.CustomPostStep(s, t, domain) } -> std::same_as; + }; + + template + concept HasCustomFieldOutput = requires(PG& pgen, + const std::string& name, + ndfield_t& buff, + index_t idx, + timestep_t step, + simtime_t time, + const D& dom) { + { + pgen.CustomFieldOutput(name, buff, idx, step, time, dom) + } -> std::same_as; + }; + + template + concept HasCustomStatOutput = requires(PG& pgen, + const std::string& name, + timestep_t s, + simtime_t t, + const D& dom) { + { pgen.CustomStat(name, s, t, dom) } -> std::convertible_to; + }; + + } // namespace pgen + } // namespace traits +} // namespace arch + +#endif // ARCHETYPES_TRAITS_H diff --git a/src/engines/engine.hpp b/src/engines/engine.hpp index e67b90c1..8e145994 100644 --- a/src/engines/engine.hpp +++ b/src/engines/engine.hpp @@ -21,11 +21,12 @@ #include "enums.h" #include "global.h" -#include "arch/traits.h" -#include "utils/error.h" #include "utils/timer.h" #include "utils/toml.h" +#include "metrics/traits.h" + +#include "archetypes/traits.h" #include "framework/containers/species.h" #include "framework/domain/metadomain.h" #include "framework/parameters/parameters.h" @@ -49,10 +50,16 @@ namespace ntt { template - concept IsCompatibleWithEngine = traits::metric::HasD; + concept IsCompatibleWithEngine = + metric::traits::HasD and + arch::traits::pgen::check_compatibility::value(user::PGen::engines) and + arch::traits::pgen::check_compatibility::value( + user::PGen::metrics) and + arch::traits::pgen::check_compatibility::value( + user::PGen::dimensions); template - requires IsCompatibleWithEngine + // requires IsCompatibleWithEngine class Engine { protected: @@ -78,12 +85,6 @@ namespace ntt { timestep_t step; public: - static constexpr bool pgen_is_ok { - traits::check_compatibility::value(user::PGen::engines) and - traits::check_compatibility::value(user::PGen::metrics) and - traits::check_compatibility::value(user::PGen::dimensions) - }; - static constexpr Dimension D { M::Dim }; Engine(const SimulationParams& params) @@ -109,9 +110,7 @@ namespace ntt { , start_step { m_params.get("checkpoint.start_step") } , start_time { m_params.get("checkpoint.start_time") } , time { start_time } - , step { start_step } { - raise::ErrorIf(not pgen_is_ok, "Problem generator is not compatible with the picked engine/metric/dimension", HERE); - } + , step { start_step } {} ~Engine() = default; diff --git a/src/engines/engine_init.cpp b/src/engines/engine_init.cpp index e822ba3f..9f4cad62 100644 --- a/src/engines/engine_init.cpp +++ b/src/engines/engine_init.cpp @@ -1,9 +1,8 @@ #include "enums.h" #include "global.h" -#include "arch/traits.h" - #include "archetypes/field_setter.h" +#include "archetypes/traits.h" #include "framework/specialization_registry.h" #include "engines/engine.hpp" @@ -15,51 +14,50 @@ namespace ntt { template - requires IsCompatibleWithEngine + // requires IsCompatibleWithEngine void Engine::init() { - if constexpr (pgen_is_ok) { - m_metadomain.InitStatsWriter(m_params, is_resuming); + m_metadomain.InitStatsWriter(m_params, is_resuming); #if defined(OUTPUT_ENABLED) - m_metadomain.InitWriter(&m_adios, m_params); - m_metadomain.InitCheckpointWriter(&m_adios, m_params); + m_metadomain.InitWriter(&m_adios, m_params); + m_metadomain.InitCheckpointWriter(&m_adios, m_params); #endif - logger::Checkpoint("Initializing Engine", HERE); - if (not is_resuming) { - // start a new simulation with initial conditions - logger::Checkpoint("Loading initial conditions", HERE); - if constexpr (traits::pgen::HasInitFlds>) { - logger::Checkpoint("Initializing fields from problem generator", HERE); - m_metadomain.runOnLocalDomains([&](auto& loc_dom) { - Kokkos::parallel_for( - "InitFields", - loc_dom.mesh.rangeActiveCells(), - arch::SetEMFields_kernel { - loc_dom.fields.em, - m_pgen.init_flds, - loc_dom.mesh.metric }); - }); - } - if constexpr (traits::pgen::HasInitPrtls, Domain>) { - logger::Checkpoint("Initializing particles from problem generator", HERE); - m_metadomain.runOnLocalDomains([&](auto& loc_dom) { - m_pgen.InitPrtls(loc_dom); - }); - } - } else { + logger::Checkpoint("Initializing Engine", HERE); + if (not is_resuming) { + // start a new simulation with initial conditions + logger::Checkpoint("Loading initial conditions", HERE); + if constexpr (arch::traits::pgen::HasInitFlds>) { + logger::Checkpoint("Initializing fields from problem generator", HERE); + m_metadomain.runOnLocalDomains([&](auto& loc_dom) { + Kokkos::parallel_for( + "InitFields", + loc_dom.mesh.rangeActiveCells(), + arch::SetEMFields_kernel { + loc_dom.fields.em, + m_pgen.init_flds, + loc_dom.mesh.metric }); + }); + } + if constexpr ( + arch::traits::pgen::HasInitPrtls, Domain>) { + logger::Checkpoint("Initializing particles from problem generator", HERE); + m_metadomain.runOnLocalDomains([&](auto& loc_dom) { + m_pgen.InitPrtls(loc_dom); + }); + } + } else { #if defined(OUTPUT_ENABLED) - // read simulation data from the checkpoint - raise::ErrorIf( - m_params.template get("checkpoint.start_step") == 0, - "Resuming simulation from a checkpoint requires a valid start_step", - HERE); - logger::Checkpoint("Resuming simulation from a checkpoint", HERE); - m_metadomain.ContinueFromCheckpoint(&m_adios, m_params); + // read simulation data from the checkpoint + raise::ErrorIf( + m_params.template get("checkpoint.start_step") == 0, + "Resuming simulation from a checkpoint requires a valid start_step", + HERE); + logger::Checkpoint("Resuming simulation from a checkpoint", HERE); + m_metadomain.ContinueFromCheckpoint(&m_adios, m_params); #else - raise::Error( - "Resuming simulation from a checkpoint requires -D output=ON", - HERE); + raise::Error( + "Resuming simulation from a checkpoint requires -D output=ON", + HERE); #endif - } } print_report(); } diff --git a/src/engines/engine_printer.cpp b/src/engines/engine_printer.cpp index ff8c13a9..d25e4b50 100644 --- a/src/engines/engine_printer.cpp +++ b/src/engines/engine_printer.cpp @@ -101,8 +101,8 @@ namespace ntt { color::RESET); } - auto bytes_to_human_readable( - std::size_t bytes) -> std::pair { + auto bytes_to_human_readable(std::size_t bytes) + -> std::pair { const std::vector units { "B", "KB", "MB", "GB", "TB" }; idx_t unit_idx = 0; auto size = static_cast(bytes); @@ -115,7 +115,7 @@ namespace ntt { } // namespace template - requires IsCompatibleWithEngine + // requires IsCompatibleWithEngine void Engine::print_report() const { const auto colored_stdout = m_params.template get( "diagnostics.colored_stdout"); diff --git a/src/engines/engine_run.cpp b/src/engines/engine_run.cpp index 7d81fe4b..9d334c63 100644 --- a/src/engines/engine_run.cpp +++ b/src/engines/engine_run.cpp @@ -1,8 +1,8 @@ #include "enums.h" -#include "arch/traits.h" #include "utils/diag.h" +#include "archetypes/traits.h" #include "framework/domain/domain.h" #include "framework/specialization_registry.h" @@ -11,130 +11,128 @@ namespace ntt { template - requires IsCompatibleWithEngine + // requires IsCompatibleWithEngine void Engine::run() { - if constexpr (pgen_is_ok) { - init(); - - auto timers = timer::Timers { - { "FieldSolver", - "CurrentFiltering", "CurrentDeposit", - "ParticlePusher", "FieldBoundaries", - "ParticleBoundaries", "Communications", - "Injector", "Custom", - "PrtlClear", "Output", - "Checkpoint" }, - []() { - Kokkos::fence(); - }, - m_params.get("diagnostics.blocking_timers") - }; - const auto diag_interval = m_params.get( - "diagnostics.interval"); - - auto time_history = pbar::DurationHistory { 1000 }; - const auto clear_interval = m_params.template get( - "particles.clear_interval"); - - // main algorithm loop - while (step < max_steps) { - // run the engine-dependent algorithm step + init(); + + auto timers = timer::Timers { + { "FieldSolver", + "CurrentFiltering", "CurrentDeposit", + "ParticlePusher", "FieldBoundaries", + "ParticleBoundaries", "Communications", + "Injector", "Custom", + "PrtlClear", "Output", + "Checkpoint" }, + []() { + Kokkos::fence(); + }, + m_params.get("diagnostics.blocking_timers") + }; + const auto diag_interval = m_params.template get( + "diagnostics.interval"); + + auto time_history = pbar::DurationHistory { 1000 }; + const auto clear_interval = m_params.template get( + "particles.clear_interval"); + + // main algorithm loop + while (step < max_steps) { + // run the engine-dependent algorithm step + m_metadomain.runOnLocalDomains([&timers, this](auto& dom) { + step_forward(timers, dom); + }); + // poststep (if defined) + if constexpr ( + arch::traits::pgen::HasCustomPostStep>) { + timers.start("Custom"); m_metadomain.runOnLocalDomains([&timers, this](auto& dom) { - step_forward(timers, dom); + m_pgen.CustomPostStep(step, time, dom); }); - // poststep (if defined) - if constexpr ( - traits::pgen::HasCustomPostStep>) { - timers.start("Custom"); - m_metadomain.runOnLocalDomains([&timers, this](auto& dom) { - m_pgen.CustomPostStep(step, time, dom); - }); - timers.stop("Custom"); - } - auto print_prtl_clear = (clear_interval > 0 and - step % clear_interval == 0 and step > 0); - - // advance time & step - time += dt; - ++step; - - auto print_output = false; - auto print_checkpoint = false; + timers.stop("Custom"); + } + auto print_prtl_clear = (clear_interval > 0 and + step % clear_interval == 0 and step > 0); + + // advance time & step + time += dt; + ++step; + + auto print_output = false; + auto print_checkpoint = false; #if defined(OUTPUT_ENABLED) - timers.start("Output"); - if constexpr ( - traits::pgen::HasCustomFieldOutput, M::Dim>) { - auto lambda_custom_field_output = [&](const std::string& name, - ndfield_t& buff, - index_t idx, - timestep_t step, - simtime_t time, - const Domain& dom) { - m_pgen.CustomFieldOutput(name, buff, idx, step, time, dom); - }; - print_output &= m_metadomain.Write(m_params, - step, - step - 1, - time, - time - dt, - lambda_custom_field_output); - } else { - print_output &= m_metadomain.Write(m_params, step, step - 1, time, time - dt); - } - if constexpr ( - traits::pgen::HasCustomStatOutput>) { - auto lambda_custom_stat = [&](const std::string& name, - timestep_t step, - simtime_t time, - const Domain& dom) -> real_t { - return m_pgen.CustomStat(name, step, time, dom); - }; - print_output &= m_metadomain.WriteStats(m_params, - step, - step - 1, - time, - time - dt, - lambda_custom_stat); - } else { - print_output &= m_metadomain.WriteStats(m_params, - step, - step - 1, - time, - time - dt); - } - timers.stop("Output"); - - timers.start("Checkpoint"); - print_checkpoint = m_metadomain.WriteCheckpoint(m_params, - step, - step - 1, - time, - time - dt); - timers.stop("Checkpoint"); + timers.start("Output"); + if constexpr ( + arch::traits::pgen::HasCustomFieldOutput>) { + auto lambda_custom_field_output = [&](const std::string& name, + ndfield_t& buff, + index_t idx, + timestep_t step, + simtime_t time, + const Domain& dom) { + m_pgen.CustomFieldOutput(name, buff, idx, step, time, dom); + }; + print_output &= m_metadomain.Write(m_params, + step, + step - 1, + time, + time - dt, + lambda_custom_field_output); + } else { + print_output &= m_metadomain.Write(m_params, step, step - 1, time, time - dt); + } + if constexpr ( + arch::traits::pgen::HasCustomStatOutput>) { + auto lambda_custom_stat = [&](const std::string& name, + timestep_t step, + simtime_t time, + const Domain& dom) -> real_t { + return m_pgen.CustomStat(name, step, time, dom); + }; + print_output &= m_metadomain.WriteStats(m_params, + step, + step - 1, + time, + time - dt, + lambda_custom_stat); + } else { + print_output &= m_metadomain.WriteStats(m_params, + step, + step - 1, + time, + time - dt); + } + timers.stop("Output"); + + timers.start("Checkpoint"); + print_checkpoint = m_metadomain.WriteCheckpoint(m_params, + step, + step - 1, + time, + time - dt); + timers.stop("Checkpoint"); #endif - // advance time_history - time_history.tick(); - // print timestep report - if (diag_interval > 0 and step % diag_interval == 0) { - diag::printDiagnostics( - step - 1, - max_steps, - time - dt, - dt, - timers, - time_history, - m_metadomain.l_ncells(), - m_metadomain.species_labels(), - m_metadomain.l_npart_perspec(), - m_metadomain.l_maxnpart_perspec(), - print_prtl_clear, - print_output, - print_checkpoint, - m_params.get("diagnostics.colored_stdout")); - } - timers.resetAll(); + // advance time_history + time_history.tick(); + // print timestep report + if (diag_interval > 0 and step % diag_interval == 0) { + diag::printDiagnostics( + step - 1, + max_steps, + time - dt, + dt, + timers, + time_history, + m_metadomain.l_ncells(), + m_metadomain.species_labels(), + m_metadomain.l_npart_perspec(), + m_metadomain.l_maxnpart_perspec(), + print_prtl_clear, + print_output, + print_checkpoint, + m_params.get("diagnostics.colored_stdout")); } + timers.resetAll(); } } diff --git a/src/engines/engine_traits.h b/src/engines/engine_traits.h deleted file mode 100644 index d26b0add..00000000 --- a/src/engines/engine_traits.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ENGINES_ENGINE_TRAITS_H -#define ENGINES_ENGINE_TRAITS_H - -#include "enums.h" - -#include "engines/grpic.hpp" -#include "engines/srpic.hpp" - -namespace ntt { - - template - struct EngineSelector; - - template <> - struct EngineSelector { - template - using type = SRPICEngine; - }; - - template <> - struct EngineSelector { - template - using type = GRPICEngine; - }; - -} // namespace ntt - -#endif // ENGINES_ENGINE_TRAITS_H diff --git a/src/engines/grpic.hpp b/src/engines/grpic.hpp index 3eec7c07..47aca361 100644 --- a/src/engines/grpic.hpp +++ b/src/engines/grpic.hpp @@ -66,13 +66,11 @@ namespace ntt { }; template - requires IsCompatibleWithEngine + // requires IsCompatibleWithEngine class GRPICEngine : public Engine { using base_t = Engine; using pgen_t = user::PGen; using domain_t = Domain; - // constexprs - using base_t::pgen_is_ok; // contents using base_t::m_metadomain; using base_t::m_params; diff --git a/src/engines/srpic.hpp b/src/engines/srpic.hpp index f78b0745..ca71e892 100644 --- a/src/engines/srpic.hpp +++ b/src/engines/srpic.hpp @@ -23,6 +23,8 @@ #include "utils/timer.h" #include "utils/toml.h" +#include "metrics/traits.h" + #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" #include "archetypes/spatial_dist.h" @@ -49,16 +51,14 @@ namespace ntt { template - requires IsCompatibleWithEngine && - traits::metric::HasH_ij && traits::metric::HasConvert_i && - traits::metric::HasSqrtH_ij + // requires IsCompatibleWithEngine && + requires metric::traits::HasH_ij && metric::traits::HasConvert_i && + metric::traits::HasSqrtH_ij class SRPICEngine : public Engine { using base_t = Engine; using pgen_t = user::PGen; using domain_t = Domain; - // constexprs - using base_t::pgen_is_ok; // contents using base_t::m_metadomain; using base_t::m_params; diff --git a/src/engines/traits.h b/src/engines/traits.h new file mode 100644 index 00000000..f6466117 --- /dev/null +++ b/src/engines/traits.h @@ -0,0 +1,26 @@ +/** + * @file engine/traits.h + * @brief Defines a set of traits to check if an engine class satisfies certain conditions + * @implements + * - ntt::traits::engine::HasRun<> - checks if an engine has a run() method + * @namespaces: + * - ntt::traits::engine:: + */ +#ifndef ENGINES_TRAITS_H +#define ENGINES_TRAITS_H + +#include + +namespace ntt { + + namespace traits { + namespace engine { + template + concept HasRun = requires(E& engine) { + { engine.run() } -> std::same_as; + }; + } // namespace engine + } // namespace traits +} // namespace ntt + +#endif // ENGINES_TRAITS_H diff --git a/src/entity.cpp b/src/entity.cpp index 8d88e265..0c6d0ac2 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -3,14 +3,32 @@ #include "arch/traits.h" #include "utils/error.h" -#include "engines/engine_traits.h" #include "framework/simulation.h" #include "framework/specialization_registry.h" +#include "engines/grpic.hpp" +#include "engines/srpic.hpp" #include "pgen.hpp" - + #include +namespace ntt { + template + struct EngineSelector; + + template <> + struct EngineSelector { + template + using type = SRPICEngine; + }; + + template <> + struct EngineSelector { + template + using type = GRPICEngine; + }; +} // namespace ntt + template class M, Dimension D> static constexpr bool should_compile { traits::check_compatibility::value(user::PGen>::engines) && diff --git a/src/framework/domain/domain.h b/src/framework/domain/domain.h index c4546110..a7da455d 100644 --- a/src/framework/domain/domain.h +++ b/src/framework/domain/domain.h @@ -43,10 +43,11 @@ #include "global.h" #include "arch/directions.h" -#include "arch/traits.h" #include "utils/formatting.h" #include "utils/numeric.h" +#include "metrics/traits.h" + #include "framework/containers/fields.h" #include "framework/containers/particles.h" #include "framework/containers/species.h" @@ -60,7 +61,7 @@ namespace ntt { template - requires traits::metric::HasD + requires metric::traits::HasD struct Domain { static constexpr Dimension D { M::Dim }; @@ -148,7 +149,8 @@ namespace ntt { } /* setters -------------------------------------------------------------- */ - auto set_neighbor_idx(const dir::direction_t& dir, unsigned int idx) -> void { + auto set_neighbor_idx(const dir::direction_t& dir, unsigned int idx) + -> void { m_neighbor_idx[dir] = idx; } @@ -166,8 +168,8 @@ namespace ntt { }; template - inline auto operator<<(std::ostream& os, - const Domain& domain) -> std::ostream& { + inline auto operator<<(std::ostream& os, const Domain& domain) + -> std::ostream& { os << "Domain #" << domain.index(); #if defined(MPI_ENABLED) os << " [MPI rank: " << domain.mpi_rank() << "]"; diff --git a/src/framework/domain/mesh.h b/src/framework/domain/mesh.h index 3cfd9117..b820eb89 100644 --- a/src/framework/domain/mesh.h +++ b/src/framework/domain/mesh.h @@ -17,11 +17,12 @@ #include "global.h" #include "arch/directions.h" -#include "arch/traits.h" #include "utils/comparators.h" #include "utils/error.h" #include "utils/numeric.h" +#include "metrics/traits.h" + #include "framework/domain/grid.h" #include @@ -32,7 +33,7 @@ namespace ntt { template - requires traits::metric::HasD && traits::metric::HasConvert_i + requires metric::traits::HasD && metric::traits::HasConvert_i struct Mesh : public Grid { static constexpr bool is_mesh { true }; static constexpr Dimension D { M::Dim }; @@ -132,9 +133,8 @@ namespace ntt { * @note indices are already shifted by N_GHOSTS (i.e. they start at N_GHOSTS not 0) */ [[nodiscard]] - auto ExtentToRange( - boundaries_t box, - boundaries_t incl_ghosts) const -> boundaries_t { + auto ExtentToRange(boundaries_t box, boundaries_t incl_ghosts) const + -> boundaries_t { raise::ErrorIf(box.size() != M::Dim, "Invalid box dimension", HERE); raise::ErrorIf(incl_ghosts.size() != M::Dim, "Invalid incl_ghosts dimension", diff --git a/src/framework/domain/metadomain.h b/src/framework/domain/metadomain.h index 611f658c..3319cafd 100644 --- a/src/framework/domain/metadomain.h +++ b/src/framework/domain/metadomain.h @@ -19,7 +19,8 @@ #include "global.h" #include "arch/kokkos_aliases.h" -#include "arch/traits.h" + +#include "metrics/traits.h" #include "framework/containers/species.h" #include "framework/domain/domain.h" @@ -48,9 +49,9 @@ namespace ntt { template - concept IsCompatibleWithMetadomain = traits::metric::HasD && - traits::metric::HasConvert && - traits::metric::HasTotVolume; + concept IsCompatibleWithMetadomain = metric::traits::HasD && + metric::traits::HasConvert && + metric::traits::HasTotVolume; template requires IsCompatibleWithMetadomain diff --git a/src/framework/simulation.h b/src/framework/simulation.h index f24c82b9..9f066440 100644 --- a/src/framework/simulation.h +++ b/src/framework/simulation.h @@ -16,10 +16,10 @@ #include "enums.h" -#include "arch/traits.h" #include "utils/error.h" #include "utils/toml.h" +#include "engines/traits.h" #include "framework/parameters/parameters.h" namespace ntt { diff --git a/src/global/arch/traits.h b/src/global/arch/traits.h index e950af53..88a81a51 100644 --- a/src/global/arch/traits.h +++ b/src/global/arch/traits.h @@ -2,6 +2,9 @@ * @file arch/traits.h * @brief Defines a set of traits to check if a class satisfies certain conditions * @implements + * - traits::fieldsetter::ex1_t, ::ex2_t, ::ex3_t - checks for special methods in field setter classes + * - traits::fieldsetter::bx1_t, ::bx2_t, ::bx3_t - checks for special methods in field setter classes + * - traits::fieldsetter::dx1_t, ::dx2_t, ::dx3_t - checks for special methods in field setter classes * - traits::has_method<> * - traits::has_member<> * - traits::ex1_t, ::ex2_t, ::ex3_t @@ -22,78 +25,82 @@ #include "global.h" -#include "arch/kokkos_aliases.h" - +#include +#include #include #include namespace traits { - template