From 1dc19e6452fca0aa0d5c2626c138df4a9fec7cc1 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 29 Dec 2025 12:35:24 -0500 Subject: [PATCH 01/35] Add initial muon CDF data --- src/celeritas/inp/MucfPhysics.cc | 13 ++- src/celeritas/inp/MucfPhysics.hh | 37 ++++--- src/celeritas/inp/MucfPhysicsData.hh | 117 +++++++++++++++++++++++ test/celeritas/ext/GeantImporter.test.cc | 20 +++- 4 files changed, 162 insertions(+), 25 deletions(-) create mode 100644 src/celeritas/inp/MucfPhysicsData.hh diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index 9724a6e0b0..472fb54590 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -6,6 +6,8 @@ //---------------------------------------------------------------------------// #include "MucfPhysics.hh" +#include "MucfPhysicsData.hh" + namespace celeritas { namespace inp @@ -21,16 +23,13 @@ namespace inp MucfPhysics MucfPhysics::from_default() { MucfPhysics result; - - //! \todo Initialize hardcoded CDF data - //! \todo Initialize hardcoded cycle rate data - //! \todo Initialize hardcoded atom transfer data - //! \todo Initialize hardcoded spin flip data + result.muon_energy_cdf = mucf_muon_energy_cdf(); + result.cycle_rates = mucf_cycle_rates(); + result.atom_transfer = mucf_atom_transfer_rates(); + result.atom_spin_flip = mucf_atom_spin_flip_rates(); // Temporary test dummy data to verify correct import { - result.muon_energy_cdf = Grid::from_constant(1.0); - MucfCycleRate dt_cycle; dt_cycle.molecule = MucfMuonicMolecule::deuterium_tritium; diff --git a/src/celeritas/inp/MucfPhysics.hh b/src/celeritas/inp/MucfPhysics.hh index 88c67a90e4..7854b85e0a 100644 --- a/src/celeritas/inp/MucfPhysics.hh +++ b/src/celeritas/inp/MucfPhysics.hh @@ -68,6 +68,9 @@ struct MucfCycleRate struct MucfAtomTransferRate { //! \todo Implement + + //! True if data is assigned \todo Implement + explicit operator bool() const { return true; } }; //---------------------------------------------------------------------------// @@ -75,22 +78,27 @@ struct MucfAtomTransferRate * Muon-catalyzed fusion mean atom spin flip data. * * Spin flip rates are as a function of temperature, with each grid/table - * representing an atom pair combination and its spin (e.g. deuterium-tritium, - * spin 1). Ordering is important, thus same spin deuterium-tritium and - * tritium-deuterium have different tables, which leads to a different final - * spin flip rate for a given material definition. + * representing an atom pair combination and its spin (e.g. + * deuterium-tritium, spin 1). Ordering is important, thus same spin + * deuterium-tritium and tritium-deuterium have different tables, which + * leads to a different final spin flip rate for a given material + * definition. * * Each struct holds one of such combinations, with the full set of - * combinations being the \c vector in \c MucfPhysics . + * combinations being the \c vector in \c MucfPhysics + * . * * \note These grids are host-only, with only the final spin flip rate per - * state (which is just a \c real_type ) for each combination being needed in - * the stepping loop. This is because these rates are material dependent, and - * thus can be cached at model construction. + * state (which is just a \c real_type ) for each combination being needed + * in the stepping loop. This is because these rates are material + * dependent, and thus can be cached at model construction. */ struct MucfAtomSpinFlipRate { //! \todo Implement + + //! True if data is assigned \todo Implement + explicit operator bool() const { return true; } }; //---------------------------------------------------------------------------// @@ -98,21 +106,22 @@ struct MucfAtomSpinFlipRate * Muon-catalyzed fusion physics options and data import. * * Minimum requirements for muon-catalyzed fusion: - * - Muon energy CDF data, required for sampling the outgoing muCF muon, and + * - Muon energy CDF data, required for sampling the outgoing muCF muon, + * and * - Mean cycle rate data for dd, dt, and tt muonic molecules. * - * Muonic atom transfer and muonic atom spin flip are secondary effects and not - * required for muCF to function. + * Muonic atom transfer and muonic atom spin flip are secondary effects and + * not required for muCF to function. */ struct MucfPhysics { template using Vec = std::vector; - Grid muon_energy_cdf; //!< CDF for outgoing muCF muon + Grid muon_energy_cdf; //!< CDF for sampling the outgoing muCF muon Vec cycle_rates; //!< Mean cycle rates for muonic molecules - Vec atom_transfer; //!< Muon atom transfer rates - Vec atom_spin_flip; //!< Muon atom spin flip rates + Vec atom_transfer; //!< Muonic atom transfer rates + Vec atom_spin_flip; //!< Muonic atom spin flip rates //! Whether muon-catalyzed fusion physics is enabled explicit operator bool() const diff --git a/src/celeritas/inp/MucfPhysicsData.hh b/src/celeritas/inp/MucfPhysicsData.hh new file mode 100644 index 0000000000..20c023dc07 --- /dev/null +++ b/src/celeritas/inp/MucfPhysicsData.hh @@ -0,0 +1,117 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/inp/MucfPhysicsData.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/inp/Grid.hh" + +#include "MucfPhysics.hh" + +namespace celeritas +{ +namespace inp +{ +//---------------------------------------------------------------------------// +/*! + * Static muon energy CDF data for muon-catalyzed fusion. + * + * \todo This data may be loaded from an external file in the future. + */ +static Grid mucf_muon_energy_cdf() +{ + Grid cdf; + cdf.interpolation.type = InterpolationType::cubic_spline; + + // Cumulative distribution data [unitless] + cdf.x = {0, + 0.04169381107491854, + 0.08664495114006499, + 0.14332247557003264, + 0.20456026058631915, + 0.2723127035830618, + 0.34136807817589576, + 0.41563517915309456, + 0.48990228013029324, + 0.5667752442996744, + 0.6306188925081434, + 0.6866449511400652, + 0.7309446254071662, + 0.7778501628664496, + 0.8104234527687297, + 0.8403908794788275, + 0.8618892508143323, + 0.8814332247557004, + 0.8970684039087949, + 0.903583061889251, + 1.0}; + + // Energy [keV] + cdf.y = {0, + 0.48850540675768084, + 0.8390389347819425, + 1.2521213482687141, + 1.7153033196164724, + 2.253638712180777, + 2.854653691809707, + 3.606073540073316, + 4.470346052913727, + 5.560291219507215, + 6.700556502915258, + 7.953772477101693, + 9.194596305637525, + 10.849180562221111, + 12.353474314071864, + 14.045888515617822, + 15.650634617544647, + 17.38079707555165, + 19.111008546659452, + 19.976130619913615, + 80.0}; + + CELER_ENSURE(cdf); + return cdf; +} + +//---------------------------------------------------------------------------// +/*! + * Static cycle rate data for muon-catalyzed fusion. + * + * \todo This data may be loaded from an external file in the future. + */ +static std::vector mucf_cycle_rates() +{ + //! \todo Implement + return {}; +}; + +//---------------------------------------------------------------------------// +/*! + * Static spin-flip data for muon-catalyzed fusion. + * + * \todo This data may be loaded from an external file in the future. + */ +static std::vector mucf_atom_spin_flip_rates() +{ + //! \todo Implement + return {}; +}; + +//---------------------------------------------------------------------------// +/*! + * Static atom transfer data for muon-catalyzed fusion. + * + * \todo This data may be loaded from an external file in the future. + */ +static std::vector mucf_atom_transfer_rates() +{ + //! \todo Implement + return {}; +}; + +//---------------------------------------------------------------------------// +} // namespace inp +} // namespace celeritas diff --git a/test/celeritas/ext/GeantImporter.test.cc b/test/celeritas/ext/GeantImporter.test.cc index 68022e0a32..a42578192a 100644 --- a/test/celeritas/ext/GeantImporter.test.cc +++ b/test/celeritas/ext/GeantImporter.test.cc @@ -2132,15 +2132,27 @@ TEST_F(OpticalSurfaces, surfaces) } //---------------------------------------------------------------------------// -TEST_F(MucfBox, run) +TEST_F(MucfBox, static_data) { auto const& mucf = this->imported_data().mucf_physics; EXPECT_TRUE(mucf); - static double const expected_muon_energy_cdf_y[] = {1, 1}; - EXPECT_EQ(2, mucf.muon_energy_cdf.x.size()); - EXPECT_VEC_EQ(expected_muon_energy_cdf_y, mucf.muon_energy_cdf.y); + auto average = [](inp::Grid::VecDbl const& data) -> double { + double sum{0}; + for (auto y : data) + { + sum += y; + } + return sum / data.size(); + }; + + static size_t const expected_muon_energy_cdf_size = 21; + EXPECT_EQ(expected_muon_energy_cdf_size, mucf.muon_energy_cdf.x.size()); + EXPECT_EQ(expected_muon_energy_cdf_size, mucf.muon_energy_cdf.y.size()); + EXPECT_SOFT_EQ(0.55157437567861023, average(mucf.muon_energy_cdf.x)); + EXPECT_SOFT_EQ(11.250286274435437, average(mucf.muon_energy_cdf.y)); + // Dummy data auto const& cycle_f0 = mucf.cycle_rates[0]; static double const expected_cycle_rate_f0_y[] = {2, 2}; EXPECT_TRUE(cycle_f0); From 23c85856634925d69f4313748c6110aed9f2892f Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 29 Dec 2025 12:45:40 -0500 Subject: [PATCH 02/35] Add process/model skeletons --- src/celeritas/mucf/model/DTMixMucfModel.hh | 75 ++++++++++++++++++++++ src/celeritas/mucf/process/MucfProcess.cc | 66 +++++++++++++++++++ src/celeritas/mucf/process/MucfProcess.hh | 59 +++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 src/celeritas/mucf/model/DTMixMucfModel.hh create mode 100644 src/celeritas/mucf/process/MucfProcess.cc create mode 100644 src/celeritas/mucf/process/MucfProcess.hh diff --git a/src/celeritas/mucf/model/DTMixMucfModel.hh b/src/celeritas/mucf/model/DTMixMucfModel.hh new file mode 100644 index 0000000000..d6cd37e531 --- /dev/null +++ b/src/celeritas/mucf/model/DTMixMucfModel.hh @@ -0,0 +1,75 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/DTMixMucfModel.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/mat/MaterialParams.hh" +#include "celeritas/mucf/data/DTMixMucfData.hh" +#include "celeritas/mucf/executor/DTMixMucfExecutor.hh" // IWYU pragma: associated +#include "celeritas/phys/InteractionApplier.hh" // IWYU pragma: associated +#include "celeritas/phys/Model.hh" +#include "celeritas/phys/ParticleParams.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion model for dd, dt, and tt molecules. + * + * In this model the executor performs the full muon-catalyzed fusion workflow. + * It forms a muonic d or t atom, samples which muonic molecule will be + * produced, selects the channel, and calls the appropriate interactor. + * + * The full set of "actions" is as follows, and in this ordering: + * - Define muon decay time to compete with the rest of the execution + * - Form muonic atom and select its spin + * - May execute atom spin flip or atom transfer + * - Form muonic molecule and select its spin + * - Calculate mean cycle time (time it takes from atom formation to fusion) + * - Confirm if fusion happens or the if the muon should decay + * - Call appropriate Interactor: Muon decay, or one of the muCF interactors + * + * \note This is an at-rest model. + */ +class DTMixMucfModel final : public Model, public StaticConcreteAction +{ + public: + //!@{ + //! \name Type aliases + using HostRef = HostCRef; + using DeviceRef = DeviceCRef; + //!@} + + // Construct from model ID and other necessary data + DTMixMucfModel(ActionId id, + ParticleParams const& particles, + MaterialParams const& materials); + + // Particle types and energy ranges that this model applies to + SetApplicability applicability() const final; + + // Get the microscopic cross sections for the given particle and material + XsTable micro_xs(Applicability) const final; + + // Apply the interaction kernel on host + void step(CoreParams const&, CoreStateHost&) const final; + + // Apply the interaction kernel on device + void step(CoreParams const&, CoreStateDevice&) const final; + + //!@{ + //! Access model data + HostRef const& host_ref() const { return data_.host_ref(); } + DeviceRef const& device_ref() const { return data_.device_ref(); } + //!@} + + private: + // Host/device storage and reference + CollectionMirror data_; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/mucf/process/MucfProcess.cc b/src/celeritas/mucf/process/MucfProcess.cc new file mode 100644 index 0000000000..27a9256e19 --- /dev/null +++ b/src/celeritas/mucf/process/MucfProcess.cc @@ -0,0 +1,66 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/process/MucfProcess.cc +//---------------------------------------------------------------------------// +#include "MucfProcess.hh" + +#include + +#include "celeritas/mucf/model/DTMixMucfModel.hh" +#include "celeritas/phys/Model.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct from host data. + */ +MucfProcess::MucfProcess(SPConstParticles particles, SPConstMaterials materials) + : particles_(particles), materials_(materials) +{ + //! \todo Fix ImportProcessClass + CELER_EXPECT(particles_); + CELER_EXPECT(materials_); + CELER_EXPECT(particles_->find(pdg::mu_minus())); +} + +//---------------------------------------------------------------------------// +/*! + * Construct the models associated with this process. + */ +auto MucfProcess::build_models(ActionIdIter start_id) const -> VecModel +{ + return {std::make_shared( + *start_id++, *particles_, *materials_)}; +} + +//---------------------------------------------------------------------------// +/*! + * Get the interaction cross sections for the given energy range. + */ +auto MucfProcess::macro_xs(Applicability applic) const -> XsGrid +{ + return {}; +} + +//---------------------------------------------------------------------------// +/*! + * Get the energy loss for the given energy range. + */ +auto MucfProcess::energy_loss(Applicability) const -> EnergyLossGrid +{ + return {}; +} +//---------------------------------------------------------------------------// +/*! + * Name of the process. + */ +std::string_view MucfProcess::label() const +{ + return "Muon-catalyzed fusion"; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/mucf/process/MucfProcess.hh b/src/celeritas/mucf/process/MucfProcess.hh new file mode 100644 index 0000000000..bb14033d79 --- /dev/null +++ b/src/celeritas/mucf/process/MucfProcess.hh @@ -0,0 +1,59 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/process/MucfProcess.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/Types.hh" +#include "celeritas/mat/MaterialParams.hh" +#include "celeritas/phys/Process.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion of muonic dd, dt, or tt molecules. + * + * \note This is an at-rest process. + */ +class MucfProcess final : public Process +{ + public: + //!@{ + //! \name Type aliases + using SPConstParticles = std::shared_ptr; + using SPConstMaterials = std::shared_ptr; + //!@} + + public: + // Construct from shared and imported data + explicit MucfProcess(SPConstParticles particles, + SPConstMaterials materials); + + // Construct the models associated with this process + VecModel build_models(ActionIdIter start_id) const final; + + // Get the interaction cross sections for the given energy range + XsGrid macro_xs(Applicability range) const final; + + // Get the energy loss for the given energy range + EnergyLossGrid energy_loss(Applicability range) const final; + + //! Whether the integral method can be used to sample interaction length + bool supports_integral_xs() const final { return true; } //! \todo Check + + //! Whether the process applies when the particle is stopped + bool applies_at_rest() const final { return true; } + + // Name of the process + std::string_view label() const final; + + private: + SPConstParticles particles_; + SPConstMaterials materials_; +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas From 1b8df651f9924ccd470e53309e655dbe00b3d6d5 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 29 Dec 2025 12:48:03 -0500 Subject: [PATCH 03/35] Draft host/device data --- src/celeritas/Types.hh | 13 +- src/celeritas/mucf/data/DTMixMucfData.hh | 156 +++++++++++++++++++++++ 2 files changed, 167 insertions(+), 2 deletions(-) create mode 100644 src/celeritas/mucf/data/DTMixMucfData.hh diff --git a/src/celeritas/Types.hh b/src/celeritas/Types.hh index c2bc81764a..a2d39abf08 100644 --- a/src/celeritas/Types.hh +++ b/src/celeritas/Types.hh @@ -98,6 +98,9 @@ using UniformGridId = OpaqueId; //! Opaque index of a cross section grid using XsGridId = OpaqueId; +//! Opaque index of a muCF material component +using MuCfMatCompId = OpaqueId; + //---------------------------------------------------------------------------// // ENUMERATIONS //---------------------------------------------------------------------------// @@ -228,7 +231,10 @@ enum class CylAxis }; //---------------------------------------------------------------------------// -//! Muon-catalyzed fusion atoms +/*! + * Muonic atom selection from material data. This is *not* intended to be used + * by the transport loop. + */ enum class MucfMuonicAtom { deuterium, @@ -237,7 +243,10 @@ enum class MucfMuonicAtom }; //---------------------------------------------------------------------------// -//! Muon-catalyzed fusion molecules +/*! + * Muonic molecule selection from material data. This is *not* intended to be + * used by the transport loop. + */ enum class MucfMuonicMolecule { deuterium_deuterium, diff --git a/src/celeritas/mucf/data/DTMixMucfData.hh b/src/celeritas/mucf/data/DTMixMucfData.hh new file mode 100644 index 0000000000..43a57c2508 --- /dev/null +++ b/src/celeritas/mucf/data/DTMixMucfData.hh @@ -0,0 +1,156 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/data/DTMixMucfData.hh +//---------------------------------------------------------------------------// +#pragma once + +#include +#include + +#include "corecel/Macros.hh" +#include "corecel/cont/EnumArray.hh" +#include "corecel/grid/NonuniformGridData.hh" +#include "corecel/io/Join.hh" +#include "celeritas/Types.hh" +#include "celeritas/phys/ParticleParams.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * ParticleIds used by the \c DTMixMucfModel . + */ +struct MucfParticles +{ + //! Primary + ParticleId mu_minus; + + //!@{ + //! Elementary particles and nuclei + ParticleId neutron; + ParticleId proton; + ParticleId alpha; + ParticleId helium3; + //!@} + + //!@{ + //! Muonic atoms + ParticleId muonic_hydrogen; + ParticleId muonic_deuteron; + ParticleId muonic_triton; + ParticleId muonic_alpha; + ParticleId muonic_helium3; + //!@} + + //! Check whether all particles are assigned + CELER_FUNCTION explicit operator bool() const + { + return mu_minus && neutron && proton && alpha && helium3 + && muonic_hydrogen && muonic_alpha && muonic_triton + && muonic_helium3; + } + + //! Assign from ParticleParams (anonymous free function in the model.cc?) + static MucfParticles from_params(ParticleParams const& particles) + { +#define MP_ADD(MEMBER) \ + pids.MEMBER = particles.find(pdg::MEMBER()); \ + if (!pids.MEMBER) \ + { \ + missing.push_back({#MEMBER, pdg::MEMBER()}); \ + } + + using PairStrPdg = std::pair; + std::vector missing; + MucfParticles pids; + + MP_ADD(mu_minus); + MP_ADD(neutron); + MP_ADD(proton); + MP_ADD(alpha); + //! \todo Decide whether to implement these PDGs in PDGNumber.hh +#if 0 + MP_ADD(helium_3); + MP_ADD(muonic_hydrogen); + MP_ADD(muonic_alpha); + MP_ADD(muonic_deuteron); + MP_ADD(muonic_triton); + MP_ADD(muonic_helium3); +#endif + CELER_VALIDATE( + missing.empty(), + << "missing particles required for muon-catalyzed fusion: " + << join(missing.begin(), missing.end(), ", ", [](PairStrPdg const& p) { + return p.first + " (PDG " + + std::to_string(p.second.unchecked_get()) + ')'; + })); + + return pids; + +#undef MP_ADD + } +}; + +//---------------------------------------------------------------------------// +/*! + * Data for for the \c DTMixMucfModel . + */ +template +struct DTMixMucfData +{ + template + using Items = Collection; + template + using MatCycleItems = Collection; + template + using MaterialItems = Collection; + using Grid = NonuniformGridRecord; + using CycleTimesArray = EnumArray>; + + //! Particle IDs + MucfParticles particles; + + //! Muon CDF energy grid for sampling outgoing muCF muons + Grid muon_energy_cdf; //! \todo Verify energy unit + Items reals; + + //!@{ + //! Material-dependent data calculated at model construction + //! \c PhysMatId indexed by \c MuCfMatCompId + MaterialItems matcompid_to_matid; + //! Cycle times per material: [mat_comp_id][muonic_molecule][spin_index] + MatCycleItems cycle_times; //!< In [s] + //! \todo Add mean atom spin flip times + //! \todo Add mean atom transfer times + //!@} + + //! \todo Check whether the data are assigned + explicit CELER_FUNCTION operator bool() const + { + //! \todo Finalize implementation + return particles && muon_energy_cdf && !matcompid_to_matid.empty() + && !cycle_times.empty() + && (matcompid_to_matid.size() == cycle_times.size()); + } + + //! Assign from another set of data + template + DTMixMucfData& operator=(DTMixMucfData const& other) + { + CELER_EXPECT(other); + + //! \todo Finish implementation + this->particles = other.particles; + this->reals = other.reals; + this->muon_energy_cdf = other.muon_energy_cdf; + this->matcompid_to_matid = other.matcompid_to_matid; + this->cycle_times = other.cycle_times; + + return *this; + } +}; + +//---------------------------------------------------------------------------// +} // namespace celeritas From 1a4d2ffe2164e34ace004cbe2a4cef0afd68b6f6 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 29 Dec 2025 12:57:06 -0500 Subject: [PATCH 04/35] Add model implementation; draft host/device data initialization --- src/celeritas/mucf/model/DTMixMucfModel.cc | 146 ++++++++++++ src/celeritas/mucf/model/DTMixMucfModel.cu | 33 +++ .../model/detail/DTMixMaterialCalculator.cc | 212 ++++++++++++++++++ .../model/detail/DTMixMaterialCalculator.hh | 142 ++++++++++++ 4 files changed, 533 insertions(+) create mode 100644 src/celeritas/mucf/model/DTMixMucfModel.cc create mode 100644 src/celeritas/mucf/model/DTMixMucfModel.cu create mode 100644 src/celeritas/mucf/model/detail/DTMixMaterialCalculator.cc create mode 100644 src/celeritas/mucf/model/detail/DTMixMaterialCalculator.hh diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc new file mode 100644 index 0000000000..ac4c4e9e56 --- /dev/null +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -0,0 +1,146 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/DTMixMucfModel.cc +//---------------------------------------------------------------------------// +#include "DTMixMucfModel.hh" + +#include + +#include "corecel/inp/Grid.hh" +#include "celeritas/global/ActionLauncher.hh" +#include "celeritas/global/TrackExecutor.hh" +#include "celeritas/grid/NonuniformGridBuilder.hh" +#include "celeritas/inp/MucfPhysicsData.hh" +#include "celeritas/mucf/executor/DTMixMucfExecutor.hh" // IWYU pragma: associated +#include "celeritas/phys/InteractionApplier.hh" // IWYU pragma: associated +#include "celeritas/phys/PDGNumber.hh" + +#include "detail/DTMixMaterialCalculator.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Construct from model ID and other necessary data. + * + * Most of the muon-catalyzed fusion data is static throughout the simulation, + * as it is only material-dependent (DT mixture and temperature). Therefore, + * most grids can be host-only and used to calculate final values, which are + * then cached and copied to device. The exception to this is the muon energy + * CDF grid, needed to sample the final state of the outgoing muon after a muCF + * interaction. + * + * \todo Correctly update \c ImportProcessClass and \c ImportModelClass . These + * operate under the assumption that there is a one-to-one equivalente between + * Geant4 and Celeritas. But for muCF, everything is done via one + * process/model/executor in Celeritas, whereas in Geant4 atom formation, spin + * flip, atom transfer, etc., are are all separate processes. + */ +DTMixMucfModel::DTMixMucfModel(ActionId id, + ParticleParams const& particles, + MaterialParams const& materials) + : StaticConcreteAction( + id, + "dt-mucf", + R"(interact by muon forming and fusing a dd, dt, or tt muonic molecule)") +{ + CELER_EXPECT(id); + + using VecCycleTimes + = std::vector; + using VecMatId = std::vector; + + // Initialize static muCF physics input data + //! \todo This may be replaced by user-provided data in the future + inp::MucfPhysics inp_data = inp::MucfPhysics::from_default(); + CELER_EXPECT(inp_data); + + // Load particle IDs + HostVal host_data; + host_data.particles = MucfParticles::from_params(particles); + + // Copy muon energy CDF data using NonuniformGridBuilder + NonuniformGridBuilder build_grid_record{&host_data.reals}; + host_data.muon_energy_cdf = build_grid_record(inp_data.muon_energy_cdf); + + // Calculate and cache quantities for all materials with dt mixtures + VecCycleTimes vec_cycle_times; + VecMatId vec_physmatid; + for (auto const& matid : range(materials.num_materials())) + { + auto const& mat_view = materials.get(PhysMatId{matid}); + + // Construct calculator for this material + detail::DTMixMaterialCalculator mat_calculator(mat_view); + if (!mat_calculator) + { + // Skip non-dt mixture materials + continue; + } + + // Store material ID and calculated data + //! \todo Store mean atom spin flip and transfer times + vec_physmatid.push_back(PhysMatId{matid}); + vec_cycle_times.push_back(mat_calculator.cycle_times()); + } + + make_builder(&host_data.matcompid_to_matid) + .insert_back(vec_physmatid.begin(), vec_physmatid.end()); + make_builder(&host_data.cycle_times) + .insert_back(vec_cycle_times.begin(), vec_cycle_times.end()); + + // Copy to device + data_ = CollectionMirror{std::move(host_data)}; + CELER_ENSURE(this->data_); +} + +//---------------------------------------------------------------------------// +/*! + * Particle types and energy ranges that this model applies to. + */ +auto DTMixMucfModel::applicability() const -> SetApplicability +{ + Applicability applic; + applic.particle = this->host_ref().particles.mu_minus; + // At-rest model + applic.lower = zero_quantity(); + applic.upper = zero_quantity(); + + return {applic}; +} + +//---------------------------------------------------------------------------// +/*! + * At-rest model does not require microscopic cross sections. + */ +auto DTMixMucfModel::micro_xs(Applicability) const -> XsTable +{ + return {}; +} + +//---------------------------------------------------------------------------// +/*! + * Interact with host data. + */ +void DTMixMucfModel::step(CoreParams const& params, CoreStateHost& state) const +{ + auto execute = make_action_track_executor( + params.ptr(), + state.ptr(), + this->action_id(), + InteractionApplier{DTMixMucfExecutor{this->host_ref()}}); + return launch_action(*this, params, state, execute); +} + +//---------------------------------------------------------------------------// +#if !CELER_USE_DEVICE +void DTMixMucfModel::step(CoreParams const&, CoreStateDevice&) const +{ + CELER_NOT_CONFIGURED("CUDA OR HIP"); +} +#endif + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cu b/src/celeritas/mucf/model/DTMixMucfModel.cu new file mode 100644 index 0000000000..4ac694e935 --- /dev/null +++ b/src/celeritas/mucf/model/DTMixMucfModel.cu @@ -0,0 +1,33 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/DTMixMucfModel.cu +//---------------------------------------------------------------------------// +#include "DTMixMucfModel.hh" + +#include "celeritas/global/ActionLauncher.device.hh" +#include "celeritas/global/CoreParams.hh" +#include "celeritas/global/CoreState.hh" +#include "celeritas/global/TrackExecutor.hh" +#include "celeritas/mucf/executor/DTMixMucfExecutor.hh" +#include "celeritas/phys/InteractionApplier.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Interact with device data. + */ +void DTMixMucfModel::step(CoreParams const& params, CoreStateDevice& state) const +{ + auto execute = make_action_track_executor( + params.ptr(), + state.ptr(), + this->action_id(), + InteractionApplier{DTMixMucfExecutor{this->device_ref()}}); + static ActionLauncher const launch_kernel(*this); + launch_kernel(*this, params, state, execute); +} +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.cc b/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.cc new file mode 100644 index 0000000000..b4da247641 --- /dev/null +++ b/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.cc @@ -0,0 +1,212 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/detail/DTMixMaterialCalculator.cc +//---------------------------------------------------------------------------// +#include "DTMixMaterialCalculator.hh" + +#include "corecel/Assert.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with material data. + * + * Calculates and caches material-dependent properties needed by the + * \c DTMixMucfModel . If the material does not contain deuterium and/or + * tritium the object's operator bool will return false. + */ +DTMixMaterialCalculator::DTMixMaterialCalculator(MaterialView const& material) + : material_(material) +{ + for (auto elcompid : range(material_.num_elements())) + { + auto const& element_view + = material_.element_record(ElementComponentId{elcompid}); + if (element_view.atomic_number() != AtomicNumber{1}) + { + // Skip non-hydrogen elements + continue; + } + + has_isotope_ = {false, false}; + for (auto el_comp : range(element_view.num_isotopes())) + { + auto iso_view + = element_view.isotope_record(IsotopeComponentId{el_comp}); + auto mass = iso_view.atomic_mass_number(); + if (mass == AtomicMassNumber{1}) + { + // Skip protium + continue; + } + + if (auto const atom = this->from_mass_number(mass); + atom < MucfMuonicAtom::size_) + { + // D and/or t isotopes found; calculate properties + has_isotope_[atom] = true; + lhd_densities_ = calc_lhd_densities(); + eq_densities_ = calc_equilibrium_densities(); + cycle_times_ = calc_cycle_times(element_view); + } + } + } +} + +//---------------------------------------------------------------------------// +/*! + * Convert dt mixture densities to units of liquid hydrogen density. + * + * Used during cycle time calculations. + */ +DTMixMaterialCalculator::LhdArray DTMixMaterialCalculator::calc_lhd_densities() +{ + LhdArray result; + + //! \todo Implement + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Calculate dt mixture densities after reaching thermodynamical + * equilibrium. + * + * Used during cycle time calculations. + */ +DTMixMaterialCalculator::EquilibriumArray +DTMixMaterialCalculator::calc_equilibrium_densities() +{ + EquilibriumArray result; + + //! \todo Implement + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Calculate fusion mean cycle times. + * + * This is designed to work with the user's material definition being either: + * - Single element, multiple isotopes (H element, with H, d, and t isotopes); + * or + * - Multiple elements, single isotope each (separate H, d, and t elements). + */ +DTMixMaterialCalculator::CycleTimesArray +DTMixMaterialCalculator::calc_cycle_times(ElementView const& element) +{ + CycleTimesArray result; + for (auto el_comp : range(element.num_isotopes())) + { + auto iso_view = element.isotope_record(IsotopeComponentId{el_comp}); + + // Select possible muonic atom based on the isotope/element mass number + auto atom = this->from_mass_number(iso_view.atomic_mass_number()); + switch (atom) + { + // Calculate cycle times for dd molecules + case MucfMuonicAtom::deuterium: { + result[MucfMuonicMolecule::deuterium_deuterium] + = this->calc_dd_cycle(); + if (has_isotope_[MucfMuonicAtom::tritium]) + { + // Calculate cycle times for dt molecules + result[MucfMuonicMolecule::deuterium_tritium] + = this->calc_dt_cycle(); + } + break; + } + // Calculate cycle times for tt molecules + case MucfMuonicAtom::tritium: { + result[MucfMuonicMolecule::tritium_tritium] + = this->calc_tt_cycle(); + break; + } + default: + CELER_ASSERT_UNREACHABLE(); + } + } + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Calculate dd muonic molecules cycle times from material properties and grid + * data. + * + * Cycle times for dd molecules come from F = 0 and F = 1 spin states. + */ +Array DTMixMaterialCalculator::calc_dd_cycle() +{ + Array result; + + //! \todo Implement + + // Reactive states are F = 0 and F = 1 + CELER_ENSURE(result[0] >= 0 && result[1] >= 0); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Calculate dt muonic molecules cycle times from material properties and grid + * data. + * + * Cycle times for dt molecules come from F = 1/2 and F = 3/2 spin states. + */ +Array DTMixMaterialCalculator::calc_dt_cycle() +{ + Array result; + + //! \todo Implement + + // Reactive states are F = 1/2 and F = 3/2 + CELER_ENSURE(result[0] >= 0 && result[1] >= 0); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Calculate tt muonic molecules cycle times from material properties and grid + * data. + * + * Cycle times for tt molecules come only from the F = 1/2 spin state. + */ +Array DTMixMaterialCalculator::calc_tt_cycle() +{ + Array result; + + //! \todo Implement + + // Only F = 1/2 is reactive + CELER_ENSURE(result[0] >= 0 && result[1] == 0); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Return \c MucfMuonicAtom from a given atomic mass number. + */ +MucfMuonicAtom DTMixMaterialCalculator::from_mass_number(AtomicMassNumber mass) +{ + if (mass == AtomicMassNumber{2}) + { + return MucfMuonicAtom::deuterium; + } + if (mass == AtomicMassNumber{3}) + { + return MucfMuonicAtom::tritium; + } + return MucfMuonicAtom::size_; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.hh b/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.hh new file mode 100644 index 0000000000..84a93430f0 --- /dev/null +++ b/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.hh @@ -0,0 +1,142 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/detail/DTMixMaterialCalculator.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/cont/EnumArray.hh" +#include "celeritas/inp/MucfPhysics.hh" +#include "celeritas/mat/MaterialView.hh" +#include "celeritas/mucf/data/DTMixMucfData.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Enum for safely accessing hydrogen isoprotologues. + * + * Hydrogen isoprotologue molecules are: + * - Homonuclear: \f$ ^2H \f$, \f$ ^2d \f$, and \f$ ^2t \f$ + * - Heteronuclear: hd, ht, and dt. + * + * \note Muon-catalyzed fusion data is only applicable to a material with + * concentrations in thermodynamic equilibrium. This equilibrium is calculated + * at model construction from the material temperature and its h, d, and t + * fractions. + */ +enum class MucfIsoprotologueMolecule +{ + protium_protium, + protium_deuterium, + protium_tritium, + deuterium_tritium, + tritium_tritium, + size_ +}; + +//---------------------------------------------------------------------------// +/*! + * Calculate material-dependent quantities for muon-catalyzed fusion. + * + * This class calculates all the muCF data that can be cached during model + * construction. + * Use its operator bool to store material data into \c DTMixMucfData : + * \code + for (auto matid : range(materials.num_materials())) + { + auto mat_view = materials.material_view(PhysMatId{matid}); + DTMixMaterialCalculator material_calculator(mat_view); + if (material_calculator) + { + // Valid d-t mixture material; Store data + } + } + * \endcode + */ +class DTMixMaterialCalculator +{ + public: + //!@{ + //! \name Type aliases + using CycleTimesArray = EnumArray>; + //!@} + + //! Construct with material data and calculate all quantities + DTMixMaterialCalculator(MaterialView const& material); + + //! Get mean cycle times + CycleTimesArray cycle_times() const { return cycle_times_; } + + //! Check if the material is valid for muon-catalyzed fusion + explicit operator bool() const { return !cycle_times_.empty(); } + + private: + using LhdArray = EnumArray; + using EquilibriumArray = EnumArray; + using AtomicMassNumber = AtomicNumber; + + //// DATA //// + + MaterialView material_; + LhdArray lhd_densities_; + EquilibriumArray eq_densities_; + EnumArray has_isotope_; + CycleTimesArray cycle_times_; + + //// LOCAL SCALARS //// + //! \todo Values are the same used by Acceleron and may need revisiting. + // { + // Atomic masses + static constexpr units::AmuMass protium() + { + return units::AmuMass{1.007825031898}; + } + + static constexpr units::AmuMass deuterium() + { + return units::AmuMass{2.014101777844}; + } + + static constexpr units::AmuMass tritium() + { + return units::AmuMass{3.016049281320}; + } + //} + + // Liquid hydrogen density (LHD) unit [1/cm^3] + static constexpr auto liquid_hydrogen_density() + { + return units::InvCcDensity{4.25e22}; + } + + //// HELPER FUNCTIONS //// + + // Calculate dt mixture densities in units of liquid hydrogen density + LhdArray calc_lhd_densities(); + + // Calculate thermal equilibrium densities + EquilibriumArray calc_equilibrium_densities(); + + // Return muonic atom from given atomic mass number + MucfMuonicAtom from_mass_number(AtomicMassNumber mass); + + // Calculate mean fusion cycle times for all reactive muonic molecules + CycleTimesArray calc_cycle_times(ElementView const& element); + + // Calculate mean fusion cycle times for dd muonic molecules + Array calc_dd_cycle(); + + // Calculate mean fusion cycle times for dt muonic molecules + Array calc_dt_cycle(); + + // Calculate mean fusion cycle times for tt muonic molecules + Array calc_tt_cycle(); +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas From 7e9e52734b2e7db4503b951ecdafab80294f8412 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 29 Dec 2025 12:59:26 -0500 Subject: [PATCH 05/35] Add mucf executor + interactors + helper classes skeletons --- .../mucf/executor/DTMixMucfExecutor.hh | 144 +++++++++++++++++ .../mucf/executor/detail/DDChannelSelector.hh | 73 +++++++++ .../mucf/executor/detail/DTChannelSelector.hh | 73 +++++++++ .../detail/DTMixMuonicAtomSelector.hh | 61 +++++++ .../detail/DTMixMuonicMoleculeSelector.hh | 66 ++++++++ .../executor/detail/MuonicAtomSpinSelector.hh | 76 +++++++++ .../detail/MuonicMoleculeSpinSelector.hh | 78 +++++++++ .../mucf/executor/detail/TTChannelSelector.hh | 74 +++++++++ .../mucf/interactor/DDMucfInteractor.hh | 152 ++++++++++++++++++ .../mucf/interactor/DTMucfInteractor.hh | 145 +++++++++++++++++ .../mucf/interactor/TTMucfInteractor.hh | 145 +++++++++++++++++ src/celeritas/phys/PDGNumber.hh | 4 + 12 files changed, 1091 insertions(+) create mode 100644 src/celeritas/mucf/executor/DTMixMucfExecutor.hh create mode 100644 src/celeritas/mucf/executor/detail/DDChannelSelector.hh create mode 100644 src/celeritas/mucf/executor/detail/DTChannelSelector.hh create mode 100644 src/celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh create mode 100644 src/celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh create mode 100644 src/celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh create mode 100644 src/celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh create mode 100644 src/celeritas/mucf/executor/detail/TTChannelSelector.hh create mode 100644 src/celeritas/mucf/interactor/DDMucfInteractor.hh create mode 100644 src/celeritas/mucf/interactor/DTMucfInteractor.hh create mode 100644 src/celeritas/mucf/interactor/TTMucfInteractor.hh diff --git a/src/celeritas/mucf/executor/DTMixMucfExecutor.hh b/src/celeritas/mucf/executor/DTMixMucfExecutor.hh new file mode 100644 index 0000000000..7abfc107c1 --- /dev/null +++ b/src/celeritas/mucf/executor/DTMixMucfExecutor.hh @@ -0,0 +1,144 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/executor/DTMixMucfExecutor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "celeritas/global/CoreTrackView.hh" +#include "celeritas/mat/ElementView.hh" +#include "celeritas/mucf/data/DTMixMucfData.hh" +#include "celeritas/mucf/interactor/DDMucfInteractor.hh" +#include "celeritas/mucf/interactor/DTMucfInteractor.hh" +#include "celeritas/mucf/interactor/TTMucfInteractor.hh" + +#include "detail/DDChannelSelector.hh" +#include "detail/DTChannelSelector.hh" +#include "detail/DTMixMuonicAtomSelector.hh" +#include "detail/DTMixMuonicMoleculeSelector.hh" +#include "detail/MuonicAtomSpinSelector.hh" +#include "detail/MuonicMoleculeSpinSelector.hh" +#include "detail/TTChannelSelector.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +struct DTMixMucfExecutor +{ + inline CELER_FUNCTION Interaction + operator()(celeritas::CoreTrackView const& track); + + NativeCRef data; +}; + +//---------------------------------------------------------------------------// +/*! + * Execute muon-catalyzed fusion for muonic dd, dt, or tt molecules. + */ +CELER_FUNCTION Interaction +DTMixMucfExecutor::operator()(celeritas::CoreTrackView const& track) +{ + auto phys_step_view = track.physics_step(); + auto elcomp_id = phys_step_view.element(); + CELER_ASSERT(elcomp_id); + + auto element = track.material().material_record().element_record(elcomp_id); + CELER_ASSERT(element.atomic_number() == AtomicNumber{1}); // Must be H + + auto rng = track.rng(); + + // Muon decay may compete against other "actions" in this executor + real_type const decay_len{}; //! \todo Set muon decay interaction length + + // Form d or t muonic atom + detail::DTMixMuonicAtomSelector form_atom; + auto muonic_atom = form_atom(rng); + + // Select atom spin via a helper class + detail::MuonicAtomSpinSelector select_atom_spin(muonic_atom); + + // { + // Competing at-rest processes which add to the total track time + //! \todo Muonic atom transfer + //! \todo Muonic atom spin flip + // } + + // Form dd, dt, or tt muonic molecule + detail::DTMixMuonicMoleculeSelector form_muonic_molecule; + auto muonic_molecule = form_muonic_molecule(rng); + + // Select molecule spin + detail::MuonicMoleculeSpinSelector select_molecule_spin(muonic_molecule); + auto const molecule_spin = select_molecule_spin(rng); + + // Load cycle time for the selected molecule + auto find_component = [&](PhysMatId matid) -> MuCfMatCompId { + for (auto i : range(data.matcompid_to_matid.size())) + { + auto const comp_id = MuCfMatCompId{i}; + if (data.matcompid_to_matid[comp_id] == matid) + { + return MuCfMatCompId{i}; + } + } + return MuCfMatCompId{}; + }; + + auto const mat_comp = find_component(track.material().material_id()); + CELER_ASSERT(mat_comp); + auto const cycle_time + = data.cycle_times[mat_comp][muonic_molecule][molecule_spin]; + CELER_ASSERT(cycle_time > 0); + + // Check if muon decays before fusion happens + real_type const mucf_len = cycle_time * track.sim().step_length(); + if (decay_len < mucf_len) + { + // Muon decays and halts the interaction + //! \todo Update track time and return muon decay interactor + } + + //! \todo Correct track time update? Or should be done in Interactors? + track.sim().add_time(cycle_time); + + // Fuse molecule and generate secondaries + //! \todo Maybe move the channel selectors into the interactors + auto allocate_secondaries = phys_step_view.make_secondary_allocator(); + Interaction result; + switch (muonic_molecule) + { + case MucfMuonicMolecule::deuterium_deuterium: { + // Return DD interaction + DDMucfInteractor interact( + data, detail::DDChannelSelector()(rng), allocate_secondaries); + result = interact(rng); + break; + } + case MucfMuonicMolecule::deuterium_tritium: { + // Return DT interaction + DTMucfInteractor interact( + data, detail::DTChannelSelector()(rng), allocate_secondaries); + result = interact(rng); + break; + } + case MucfMuonicMolecule::tritium_tritium: { + // Return TT interaction + TTMucfInteractor interact( + data, detail::TTChannelSelector()(rng), allocate_secondaries); + result = interact(rng); + break; + } + default: + CELER_ASSERT_UNREACHABLE(); + } + + //! \todo Muon stripping: strip muon from muonic atom secondaries + // May be added as a separate discrete process in the stepping loop + + return result; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/mucf/executor/detail/DDChannelSelector.hh b/src/celeritas/mucf/executor/detail/DDChannelSelector.hh new file mode 100644 index 0000000000..f2793b0979 --- /dev/null +++ b/src/celeritas/mucf/executor/detail/DDChannelSelector.hh @@ -0,0 +1,73 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/executor/detail/DDChannelSelector.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/mucf/interactor/DDMucfInteractor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Select final channel for muonic dd molecules. + * + * This selection already accounts for sticking, as that is one of the possible + * outcomes. + */ +class DDChannelSelector +{ + public: + //!@{ + //! \name Type aliases + using Channel = DDMucfInteractor::Channel; + //!@} + + public: + //! Construct with args; \todo Update documentation + inline DDChannelSelector(/* args */); + + // Select fusion channel to be used by the interactor + template + inline CELER_FUNCTION Channel operator()(Engine&); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with args. + * + * \todo Update documentation + */ +DDChannelSelector::DDChannelSelector(/* args */) +{ + CELER_NOT_IMPLEMENTED("Mucf dd fusion channel selection"); +} + +//---------------------------------------------------------------------------// +/*! + * Return a sampled channel to be used as input in the dd muCF interactor. + * + * \sa celeritas::DDMucfInteractor + */ +template +inline CELER_FUNCTION DDChannelSelector::Channel +DDChannelSelector::operator()(Engine&) +{ + Channel result{Channel::size_}; + + //! \todo Implement + // Final channel selection already takes into account sticking. + + CELER_ENSURE(result < Channel::size_); + return result; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/executor/detail/DTChannelSelector.hh b/src/celeritas/mucf/executor/detail/DTChannelSelector.hh new file mode 100644 index 0000000000..28483dea53 --- /dev/null +++ b/src/celeritas/mucf/executor/detail/DTChannelSelector.hh @@ -0,0 +1,73 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/executor/detail/DTChannelSelector.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/mucf/interactor/DTMucfInteractor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Select final channel for muonic dd molecules. + * + * This selection already accounts for sticking, as that is one of the possible + * outcomes. + */ +class DTChannelSelector +{ + public: + //!@{ + //! \name Type aliases + using Channel = DTMucfInteractor::Channel; + //!@} + + public: + //! Construct with args; \todo Update documentation + inline DTChannelSelector(/* args */); + + // Select fusion channel to be used by the interactor + template + inline CELER_FUNCTION Channel operator()(Engine&); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with args. + * + * \todo Update documentation + */ +DTChannelSelector::DTChannelSelector(/* args */) +{ + CELER_NOT_IMPLEMENTED("Mucf dt fusion channel selection"); +} + +//---------------------------------------------------------------------------// +/*! + * Return a sampled channel to be used as input in the dt muCF interactor. + * + * \sa celeritas::DTMucfInteractor + */ +template +inline CELER_FUNCTION DTChannelSelector::Channel +DTChannelSelector::operator()(Engine&) +{ + Channel result{Channel::size_}; + + //! \todo Implement + // Final channel selection already takes into account sticking. + + CELER_ENSURE(result < Channel::size_); + return result; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh b/src/celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh new file mode 100644 index 0000000000..a371141417 --- /dev/null +++ b/src/celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh @@ -0,0 +1,61 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/cont/EnumArray.hh" +#include "celeritas/Types.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Select a muonic atom given the material information. + */ +class DTMixMuonicAtomSelector +{ + public: + //! Construct with args; \todo Update documentation + inline DTMixMuonicAtomSelector(/* args */); + + // Select muonic atom + template + inline CELER_FUNCTION MucfMuonicAtom operator()(Engine&); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with args. + + * \todo Update documentation + */ +DTMixMuonicAtomSelector::DTMixMuonicAtomSelector(/* args */) +{ + CELER_NOT_IMPLEMENTED("Mucf muonic atom selection"); +} + +//---------------------------------------------------------------------------// +/*! + * Return selected muonic atom. + */ +template +inline CELER_FUNCTION MucfMuonicAtom DTMixMuonicAtomSelector::operator()(Engine&) +{ + MucfMuonicAtom result{MucfMuonicAtom::size_}; + + //! \todo Implement + + CELER_ENSURE(result < MucfMuonicAtom::size_); + return result; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh b/src/celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh new file mode 100644 index 0000000000..6558191a00 --- /dev/null +++ b/src/celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh @@ -0,0 +1,66 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/Types.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Select a muonic molecule by calculating the interaction lengths of the + * possible molecule formations. + * + * This is the equivalent of Geant4's + * \c G4VRestProcess::AtRestGetPhysicalInteractionLength + */ +class DTMixMuonicMoleculeSelector +{ + public: + //! Construct with args; \todo Update documentation + inline DTMixMuonicMoleculeSelector(/* args */); + + // Select muonic molecule + template + inline CELER_FUNCTION MucfMuonicMolecule operator()(Engine&); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with args. + * + * \todo Update documentation + */ +DTMixMuonicMoleculeSelector::DTMixMuonicMoleculeSelector(/* args */) +{ + //! \todo Implement + CELER_NOT_IMPLEMENTED("Mucf muonic molecule selection"); +} + +//---------------------------------------------------------------------------// +/*! + * Return selected muonic molecule. + */ +template +inline CELER_FUNCTION MucfMuonicMolecule +DTMixMuonicMoleculeSelector::operator()(Engine&) +{ + MucfMuonicMolecule result{MucfMuonicMolecule::size_}; + + //! \todo Implement + + CELER_ENSURE(result < MucfMuonicMolecule::size_); + return result; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh b/src/celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh new file mode 100644 index 0000000000..853950c4bd --- /dev/null +++ b/src/celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh @@ -0,0 +1,76 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "celeritas/Types.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Select muonic atom spin, in units of \f$ \frac{\hbar}{2} \f$. + * + * Applicable to \f$ (p)_\mu \f$, \f$ (d)_\mu \f$, and \f$ (t)_\mu \f$. + */ +class MuonicAtomSpinSelector +{ + public: + // Construct with muonic atom type + inline CELER_FUNCTION MuonicAtomSpinSelector(MucfMuonicAtom atom); + + // Sample and return a spin value in units of hbar / 2 + template + inline CELER_FUNCTION size_type operator()(Engine&); + + private: + MucfMuonicAtom atom_; + //! \todo Add constant atom spin sampling rejection fractions +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with muonic atom. + */ +MuonicAtomSpinSelector::MuonicAtomSpinSelector(MucfMuonicAtom atom) + : atom_(atom) +{ + CELER_EXPECT(atom_ < MucfMuonicAtom::size_); + CELER_NOT_IMPLEMENTED("Mucf muonic atom spin selection"); +} + +//---------------------------------------------------------------------------// +/*! + * Return selected spin, in units of \f$ \hbar / 2 \f$. + */ +template +CELER_FUNCTION size_type MuonicAtomSpinSelector::operator()(Engine&) +{ + size_type result{}; + + //! \todo Implement + + switch (atom_) + { + case MucfMuonicAtom::deuterium: + break; + case MucfMuonicAtom::tritium: + // Protium and tritium samplings are equivalent + break; + default: + CELER_ASSERT_UNREACHABLE(); + } + return result; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh b/src/celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh new file mode 100644 index 0000000000..51ddedb741 --- /dev/null +++ b/src/celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh @@ -0,0 +1,78 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Assert.hh" +#include "celeritas/Types.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Select muonic molecule spin, in units of \f$ \frac{\hbar}{2} \f$. + * + * Applicable to \f$ (dd)_\mu \f$, * \f$ (dt)_\mu \f$ , and \f$ (tt)_\mu \f$. + */ +class MuonicMoleculeSpinSelector +{ + public: + // Construct with muonic molecule type + inline CELER_FUNCTION + MuonicMoleculeSpinSelector(MucfMuonicMolecule molecule); + + // Sample and return a spin value in units of hbar / 2 + template + inline CELER_FUNCTION size_type operator()(Engine&); + + private: + MucfMuonicMolecule molecule_; +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with muonic molecule. + */ +MuonicMoleculeSpinSelector::MuonicMoleculeSpinSelector( + MucfMuonicMolecule molecule) + : molecule_(molecule) +{ + CELER_EXPECT(molecule_ < MucfMuonicMolecule::size_); + CELER_NOT_IMPLEMENTED("Mucf muonic molecule spin selection"); +} + +//---------------------------------------------------------------------------// +/*! + * Return selected spin, in units of \f$ \hbar / 2 \f$. + */ +template +CELER_FUNCTION size_type MuonicMoleculeSpinSelector::operator()(Engine&) +{ + size_type result{}; + + //! \todo Implement + + switch (molecule_) + { + case MucfMuonicMolecule::deuterium_deuterium: + break; + case MucfMuonicMolecule::deuterium_tritium: + break; + case MucfMuonicMolecule::tritium_tritium: + break; + default: + CELER_ASSERT_UNREACHABLE(); + } + return result; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/executor/detail/TTChannelSelector.hh b/src/celeritas/mucf/executor/detail/TTChannelSelector.hh new file mode 100644 index 0000000000..1ded52adb7 --- /dev/null +++ b/src/celeritas/mucf/executor/detail/TTChannelSelector.hh @@ -0,0 +1,74 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/executor/detail/TTChannelSelectionHelper.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "celeritas/mucf/interactor/TTMucfInteractor.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Select final channel for muonic dd molecules. + * + * This selection already accounts for sticking, as that is one of the possible + * outcomes. + */ +class TTChannelSelector +{ + public: + //!@{ + //! \name Type aliases + using Channel = TTMucfInteractor::Channel; + //!@} + + public: + //! Construct with args; \todo Update documentation + inline TTChannelSelector(/* args */); + + // Select fusion channel to be used by the interactor + template + inline CELER_FUNCTION Channel operator()(Engine&); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with args. + * + * \todo Update documentation + */ +TTChannelSelector::TTChannelSelector(/* args */) +{ + //! \todo Implement + CELER_NOT_IMPLEMENTED("Mucf tt fusion channel selection"); +} + +//---------------------------------------------------------------------------// +/*! + * Return a sampled channel to be used as input in the tt muCF interactor. + * + * \sa celeritas::TTMucfInteractor + */ +template +inline CELER_FUNCTION TTChannelSelector::Channel +TTChannelSelector::operator()(Engine&) +{ + Channel result{Channel::size_}; + + //! \todo Implement + // Final channel selection already takes into account sticking. + + CELER_ENSURE(result < Channel::size_); + return result; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/interactor/DDMucfInteractor.hh b/src/celeritas/mucf/interactor/DDMucfInteractor.hh new file mode 100644 index 0000000000..48aae54812 --- /dev/null +++ b/src/celeritas/mucf/interactor/DDMucfInteractor.hh @@ -0,0 +1,152 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/interactor/DDMucfInteractor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/data/StackAllocator.hh" +#include "celeritas/mat/ElementView.hh" +#include "celeritas/mat/MaterialView.hh" +#include "celeritas/mucf/data/DTMixMucfData.hh" +#include "celeritas/phys/Interaction.hh" +#include "celeritas/phys/ParticleTrackView.hh" +#include "celeritas/phys/Secondary.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion of \f$ (dd)_\mu \f$ molecules. + * + * This is an \em at-rest interaction. + */ +class DDMucfInteractor +{ + public: + //! \todo Implement muonichydrogen3_proton (\f$ (^3\text{H})_\mu + p \f$) + enum class Channel + { + helium3_muon_neutron, //!< \f$ ^3\text{He} + \mu + n \f$ + muonichelium3_neutron, //!< \f$ (^3\text{He})_\mu + n \f$ + hydrogen3_muon_proton, //!< \f$ ^3\text{H} + \mu + p \f$ + size_ + }; + + // Construct from shared and state data + inline CELER_FUNCTION + DDMucfInteractor(NativeCRef const& data, + Channel const channel, + StackAllocator& allocate); + + // Sample an interaction with the given RNG + template + inline CELER_FUNCTION Interaction operator()(Engine& rng); + + private: + // Shared constant physics properties + NativeCRef const& data_; + // Selected fusion channel + Channel channel_{Channel::size_}; + // Allocate space for secondary particles + StackAllocator& allocate_; + + // Get number of secondaries for each channel + inline CELER_FUNCTION size_type num_secondaries() const; + + // Sample Interaction secondaries + template + inline CELER_FUNCTION Span + sample_secondaries(Secondary* secondaries /*, other args */, Engine&); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with shared and state data. + */ +DDMucfInteractor::DDMucfInteractor(NativeCRef const& data, + Channel const channel, + StackAllocator& allocate) + : data_(data), channel_(channel), allocate_(allocate) +{ + CELER_EXPECT(data_); + CELER_EXPECT(channel < Channel::size_); +} + +//---------------------------------------------------------------------------// +/*! + * Sample a dd muonic molecule fusion. + */ +template +CELER_FUNCTION Interaction DDMucfInteractor::operator()(Engine& rng) +{ + // Allocate space for the final fusion channel + Secondary* secondaries = allocate_(this->num_secondaries()); + if (secondaries == nullptr) + { + // Failed to allocate space for secondaries + return Interaction::from_failure(); + } + + // Kill primary and generate secondaries + Interaction result = Interaction::from_absorption(); + result.secondaries = this->sample_secondaries(secondaries, rng); + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Return number of secondaries from each fusion channel. + */ +CELER_FUNCTION size_type DDMucfInteractor::num_secondaries() const +{ + switch (channel_) + { + case Channel::helium3_muon_neutron: + return 3; + case Channel::muonichelium3_neutron: + return 2; + case Channel::hydrogen3_muon_proton: + return 3; + default: + CELER_ASSERT_UNREACHABLE(); + } +} + +//---------------------------------------------------------------------------// +/*! + * Sample the secondaries of the selected channel. + * + * Since secondaries come from an at rest interaction, their final state is + * a simple combination of random direction + momentum conservation + */ +template +CELER_FUNCTION Span +DDMucfInteractor::sample_secondaries(Secondary* secondaries /*, other args */, + Engine&) +{ + switch (channel_) + { + case Channel::helium3_muon_neutron: + //! \todo Assign secondaries + break; + case Channel::muonichelium3_neutron: + //! \todo Assign secondaries + break; + case Channel::hydrogen3_muon_proton: + //! \todo Assign secondaries + break; + default: + CELER_ASSERT_UNREACHABLE(); + } + + return Span{secondaries, this->num_secondaries()}; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/mucf/interactor/DTMucfInteractor.hh b/src/celeritas/mucf/interactor/DTMucfInteractor.hh new file mode 100644 index 0000000000..5af9a5f3f8 --- /dev/null +++ b/src/celeritas/mucf/interactor/DTMucfInteractor.hh @@ -0,0 +1,145 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/interactor/DTMucfInteractor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/data/StackAllocator.hh" +#include "celeritas/mat/ElementView.hh" +#include "celeritas/mat/MaterialView.hh" +#include "celeritas/mucf/data/DTMixMucfData.hh" +#include "celeritas/phys/Interaction.hh" +#include "celeritas/phys/ParticleTrackView.hh" +#include "celeritas/phys/Secondary.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion of \f$ (dt)_\mu \f$ molecules. + * + * This is an \em at-rest interaction. + */ +class DTMucfInteractor +{ + public: + enum class Channel + { + alpha_muon_neutron, //!< \f$ \alpha + \mu + n \f$ + muonicalpha_neutron, //!< \f$ (\alpha)_\mu + n \f$ + size_ + }; + + // Construct from shared and state data + inline CELER_FUNCTION + DTMucfInteractor(NativeCRef const& data, + Channel const channel, + StackAllocator& allocate); + + // Sample an interaction with the given RNG + template + inline CELER_FUNCTION Interaction operator()(Engine& rng); + + private: + // Shared constant physics properties + NativeCRef const& data_; + // Selected fusion channel + Channel channel_{Channel::size_}; + // Allocate space for secondary particles + StackAllocator& allocate_; + + // Get number of secondaries for each channel + inline CELER_FUNCTION size_type num_secondaries() const; + + // Sample Interaction secondaries + template + inline CELER_FUNCTION Span + sample_secondaries(Secondary* secondaries /*, other args */, Engine&); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with shared and state data. + */ +DTMucfInteractor::DTMucfInteractor(NativeCRef const& data, + Channel const channel, + StackAllocator& allocate) + : data_(data), channel_(channel), allocate_(allocate) +{ + CELER_EXPECT(data_); + CELER_EXPECT(channel_ < Channel::size_); +} + +//---------------------------------------------------------------------------// +/*! + * Sample a dt muonic molecule fusion. + */ +template +CELER_FUNCTION Interaction DTMucfInteractor::operator()(Engine& rng) +{ + // Allocate space for the final fusion channel + Secondary* secondaries = allocate_(this->num_secondaries()); + if (secondaries == nullptr) + { + // Failed to allocate space for secondaries + return Interaction::from_failure(); + } + + // Kill primary and generate secondaries + Interaction result = Interaction::from_absorption(); + result.secondaries = this->sample_secondaries(secondaries, rng); + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Return number of secondaries from each fusion channel. + */ +CELER_FUNCTION size_type DTMucfInteractor::num_secondaries() const +{ + switch (channel_) + { + case Channel::alpha_muon_neutron: + return 3; + case Channel::muonicalpha_neutron: + return 2; + default: + CELER_ASSERT_UNREACHABLE(); + } +} + +//---------------------------------------------------------------------------// +/*! + * Sample the secondaries of the selected channel. + * + * Since secondaries come from an at rest interaction, their final state is + * a simple combination of random direction + momentum conservation + */ +template +CELER_FUNCTION Span +DTMucfInteractor::sample_secondaries(Secondary* secondaries /*, other args */, + Engine&) +{ + switch (channel_) + { + case Channel::alpha_muon_neutron: + //! \todo Assign secondaries + break; + case Channel::muonicalpha_neutron: + //! \todo Assign secondaries + break; + default: + CELER_ASSERT_UNREACHABLE(); + } + + return Span{secondaries, this->num_secondaries()}; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/mucf/interactor/TTMucfInteractor.hh b/src/celeritas/mucf/interactor/TTMucfInteractor.hh new file mode 100644 index 0000000000..97ade2512f --- /dev/null +++ b/src/celeritas/mucf/interactor/TTMucfInteractor.hh @@ -0,0 +1,145 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/interactor/TTMucfInteractor.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/Macros.hh" +#include "corecel/data/StackAllocator.hh" +#include "celeritas/mat/ElementView.hh" +#include "celeritas/mat/MaterialView.hh" +#include "celeritas/mucf/data/DTMixMucfData.hh" +#include "celeritas/phys/Interaction.hh" +#include "celeritas/phys/ParticleTrackView.hh" +#include "celeritas/phys/Secondary.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion of \f$ (tt)_\mu \f$ molecules. + * + * This is an \em at-rest interaction. + */ +class TTMucfInteractor +{ + public: + enum class Channel + { + alpha_muon_neutron_neutron, //!< \f$ \alpha + \mu + n + n \f$ + muonicalpha_neutron_neutron, //!< \f$ (\alpha)_\mu + n + n \f$ + size_ + }; + + // Construct from shared and state data + inline CELER_FUNCTION + TTMucfInteractor(NativeCRef const& data, + Channel const channel, + StackAllocator& allocate); + + // Sample an interaction with the given RNG + template + inline CELER_FUNCTION Interaction operator()(Engine& rng); + + private: + // Shared constant physics properties + NativeCRef const& data_; + // Selected fusion channel + Channel channel_{Channel::size_}; + // Allocate space for secondary particles + StackAllocator& allocate_; + + // Get number of secondaries for each channel + inline CELER_FUNCTION size_type num_secondaries() const; + + // Sample Interaction secondaries + template + inline CELER_FUNCTION Span + sample_secondaries(Secondary* secondaries /*, other args */, Engine&); +}; + +//---------------------------------------------------------------------------// +// INLINE DEFINITIONS +//---------------------------------------------------------------------------// +/*! + * Construct with shared and state data. + */ +TTMucfInteractor::TTMucfInteractor(NativeCRef const& data, + Channel const channel, + StackAllocator& allocate) + : data_(data), channel_(channel), allocate_(allocate) +{ + CELER_EXPECT(data_); + CELER_EXPECT(channel_ < Channel::size_); +} + +//---------------------------------------------------------------------------// +/*! + * Sample a dt muonic molecule fusion. + */ +template +CELER_FUNCTION Interaction TTMucfInteractor::operator()(Engine& rng) +{ + // Allocate space for the final fusion channel + Secondary* secondaries = allocate_(this->num_secondaries()); + if (secondaries == nullptr) + { + // Failed to allocate space for secondaries + return Interaction::from_failure(); + } + + // Kill primary and generate secondaries + Interaction result = Interaction::from_absorption(); + result.secondaries = this->sample_secondaries(secondaries, rng); + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Return number of secondaries from each fusion channel. + */ +CELER_FUNCTION size_type TTMucfInteractor::num_secondaries() const +{ + switch (channel_) + { + case Channel::alpha_muon_neutron_neutron: + return 4; + case Channel::muonicalpha_neutron_neutron: + return 3; + default: + CELER_ASSERT_UNREACHABLE(); + } +} + +//---------------------------------------------------------------------------// +/*! + * Sample the secondaries of the selected channel. + * + * Since secondaries come from an at rest interaction, their final state is + * a simple combination of random direction + momentum conservation + */ +template +CELER_FUNCTION Span +TTMucfInteractor::sample_secondaries(Secondary* secondaries /*, other args */, + Engine&) +{ + switch (channel_) + { + case Channel::alpha_muon_neutron_neutron: + //! \todo Assign secondaries + break; + case Channel::muonicalpha_neutron_neutron: + //! \todo Assign secondaries + break; + default: + CELER_ASSERT_UNREACHABLE(); + } + + return Span{secondaries, this->num_secondaries()}; +} + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/phys/PDGNumber.hh b/src/celeritas/phys/PDGNumber.hh index 44755468c1..15baf0edbf 100644 --- a/src/celeritas/phys/PDGNumber.hh +++ b/src/celeritas/phys/PDGNumber.hh @@ -146,6 +146,10 @@ CELER_DEFINE_PDGNUMBER(anti_he3, -1000020030) CELER_DEFINE_PDGNUMBER(alpha, 1000020040) CELER_DEFINE_PDGNUMBER(anti_alpha, -1000020040) +// Muonic nuclei +CELER_DEFINE_PDGNUMBER(muonic_deuteron, 2000010020) +CELER_DEFINE_PDGNUMBER(muonic_triton, 2000010030) + //!@} #undef CELER_DEFINE_PDGNUMBER From 2f874685b36d0c69ec2d03ed62f8bc37355e2e78 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 29 Dec 2025 13:01:16 -0500 Subject: [PATCH 06/35] Make sure it builds --- src/celeritas/CMakeLists.txt | 3 +++ src/celeritas/mucf/process/MucfProcess.hh | 1 + 2 files changed, 4 insertions(+) diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 8d7cc5269d..c4a2365e3f 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -100,6 +100,8 @@ list(APPEND SOURCES mat/MaterialParams.cc mat/MaterialParamsOutput.cc mat/detail/Utils.cc + mucf/model/detail/DTMixMaterialCalculator.cc + mucf/process/MucfProcess.cc neutron/model/CascadeOptions.cc neutron/model/CascadeOptionsIO.json.cc neutron/process/NeutronElasticProcess.cc @@ -375,6 +377,7 @@ celeritas_polysource(em/model/CoulombScatteringModel) celeritas_polysource(geo/detail/BoundaryAction) celeritas_polysource(global/detail/KillActive) celeritas_polysource(global/detail/TrackSlotUtils) +celeritas_polysource(mucf/model/DTMixMucfModel) celeritas_polysource(neutron/model/ChipsNeutronElasticModel) celeritas_polysource(neutron/model/NeutronInelasticModel) celeritas_polysource(optical/model/AbsorptionModel) diff --git a/src/celeritas/mucf/process/MucfProcess.hh b/src/celeritas/mucf/process/MucfProcess.hh index bb14033d79..146ec3d2c7 100644 --- a/src/celeritas/mucf/process/MucfProcess.hh +++ b/src/celeritas/mucf/process/MucfProcess.hh @@ -8,6 +8,7 @@ #include "celeritas/Types.hh" #include "celeritas/mat/MaterialParams.hh" +#include "celeritas/phys/ParticleParams.hh" #include "celeritas/phys/Process.hh" namespace celeritas From f517bbb440f300912853c831e4479b743bd91ca5 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 29 Dec 2025 13:01:23 -0500 Subject: [PATCH 07/35] Expand documentation --- doc/implementation/mucf-physics.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/doc/implementation/mucf-physics.rst b/doc/implementation/mucf-physics.rst index ec6d81f84e..c2b1823c7e 100644 --- a/doc/implementation/mucf-physics.rst +++ b/doc/implementation/mucf-physics.rst @@ -114,3 +114,30 @@ fusion data is constructed when the ``G4MuonMinusAtomicCapture`` process is registered. .. todo:: Add process/model/executor details + +Code implementation +=================== + +The ``MucfProcess`` process only has the model ``DTMixMucfModel`` attached to +it, responsible for deuterium-tritium mixtures. It can simulate materials from +near absolute zero to 1500 kelvin, and it is an *at rest* model that encompasses +the full cycle---atom formation, molecule formation, and fusion. + +.. note:: Only reactive channels are implemented. + +.. doxygenclass:: celeritas::MucfProcess + +The main cycle is managed by the ``DTMixMucfExecutor``, with the +Interactors reserved for sampling final states of the outgoing secondaries. + +.. doxygenclass:: celeritas::DTMixMucfModel +.. doxygenclass:: celeritas::DTMixMucfExecutor + +Most of the data is material-dependent, and thus can be calculated and cached +during model construction. This is done by the ``DTMixMaterialCalculator``. + +.. doxygenclass:: celeritas::detail::DTMixMaterialCalculator +.. doxygenclass:: celeritas::DTMixMucfExecutor +.. doxygenclass:: celeritas::DDMucfInteractor +.. doxygenclass:: celeritas::DTMucfInteractor +.. doxygenclass:: celeritas::TTMucfInteractor From dc206341daaade4e05cd24bae3ddcf7630a6d71b Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 29 Dec 2025 13:17:07 -0500 Subject: [PATCH 08/35] Fixup --- src/celeritas/mucf/process/MucfProcess.cc | 2 +- src/celeritas/mucf/process/MucfProcess.hh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/celeritas/mucf/process/MucfProcess.cc b/src/celeritas/mucf/process/MucfProcess.cc index 27a9256e19..cde972edfb 100644 --- a/src/celeritas/mucf/process/MucfProcess.cc +++ b/src/celeritas/mucf/process/MucfProcess.cc @@ -40,7 +40,7 @@ auto MucfProcess::build_models(ActionIdIter start_id) const -> VecModel /*! * Get the interaction cross sections for the given energy range. */ -auto MucfProcess::macro_xs(Applicability applic) const -> XsGrid +auto MucfProcess::macro_xs(Applicability) const -> XsGrid { return {}; } diff --git a/src/celeritas/mucf/process/MucfProcess.hh b/src/celeritas/mucf/process/MucfProcess.hh index 146ec3d2c7..565b4c590c 100644 --- a/src/celeritas/mucf/process/MucfProcess.hh +++ b/src/celeritas/mucf/process/MucfProcess.hh @@ -37,10 +37,10 @@ class MucfProcess final : public Process VecModel build_models(ActionIdIter start_id) const final; // Get the interaction cross sections for the given energy range - XsGrid macro_xs(Applicability range) const final; + XsGrid macro_xs(Applicability) const final; // Get the energy loss for the given energy range - EnergyLossGrid energy_loss(Applicability range) const final; + EnergyLossGrid energy_loss(Applicability) const final; //! Whether the integral method can be used to sample interaction length bool supports_integral_xs() const final { return true; } //! \todo Check From 91fdbbf3762cfc92da36ade39bf957f18725a4ea Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Fri, 2 Jan 2026 15:28:11 -0500 Subject: [PATCH 09/35] Address most comments: - Move types to new mucf/Types.hh - Move MucfParticles::from_params to model.cc as a free function - Move all static data to MucfPhysics.cc - Refactor/Remove types in DTMixMucfData - Remove CELER_EXPECT(mu_minus) during MucfProcess construction --- src/celeritas/Types.hh | 28 ----- src/celeritas/inp/MucfPhysics.cc | 99 ++++++++++++++- src/celeritas/inp/MucfPhysics.hh | 2 +- src/celeritas/inp/MucfPhysicsData.hh | 117 ------------------ src/celeritas/mucf/Types.hh | 48 +++++++ src/celeritas/mucf/data/DTMixMucfData.hh | 53 +------- .../mucf/executor/detail/DTChannelSelector.hh | 2 +- .../mucf/executor/detail/TTChannelSelector.hh | 2 +- .../mucf/interactor/DDMucfInteractor.hh | 2 +- .../mucf/interactor/DTMucfInteractor.hh | 2 +- .../mucf/interactor/TTMucfInteractor.hh | 2 +- src/celeritas/mucf/model/DTMixMucfModel.cc | 55 +++++++- src/celeritas/mucf/process/MucfProcess.cc | 1 - 13 files changed, 209 insertions(+), 204 deletions(-) delete mode 100644 src/celeritas/inp/MucfPhysicsData.hh create mode 100644 src/celeritas/mucf/Types.hh diff --git a/src/celeritas/Types.hh b/src/celeritas/Types.hh index a2d39abf08..c2cd0e9e63 100644 --- a/src/celeritas/Types.hh +++ b/src/celeritas/Types.hh @@ -98,9 +98,6 @@ using UniformGridId = OpaqueId; //! Opaque index of a cross section grid using XsGridId = OpaqueId; -//! Opaque index of a muCF material component -using MuCfMatCompId = OpaqueId; - //---------------------------------------------------------------------------// // ENUMERATIONS //---------------------------------------------------------------------------// @@ -230,31 +227,6 @@ enum class CylAxis size_ }; -//---------------------------------------------------------------------------// -/*! - * Muonic atom selection from material data. This is *not* intended to be used - * by the transport loop. - */ -enum class MucfMuonicAtom -{ - deuterium, - tritium, - size_ -}; - -//---------------------------------------------------------------------------// -/*! - * Muonic molecule selection from material data. This is *not* intended to be - * used by the transport loop. - */ -enum class MucfMuonicMolecule -{ - deuterium_deuterium, - deuterium_tritium, - tritium_tritium, - size_ -}; - //---------------------------------------------------------------------------// // HELPER STRUCTS //---------------------------------------------------------------------------// diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index 472fb54590..32a7e37ff1 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -6,12 +6,107 @@ //---------------------------------------------------------------------------// #include "MucfPhysics.hh" -#include "MucfPhysicsData.hh" - namespace celeritas { namespace inp { +//---------------------------------------------------------------------------// +/*! + * Static muon energy CDF data for muon-catalyzed fusion. + * + * \todo This data may be loaded from an external file in the future. + */ +static Grid mucf_muon_energy_cdf() +{ + Grid cdf; + cdf.interpolation.type = InterpolationType::cubic_spline; + + // Cumulative distribution data [unitless] + cdf.x = {0, + 0.04169381107491854, + 0.08664495114006499, + 0.14332247557003264, + 0.20456026058631915, + 0.2723127035830618, + 0.34136807817589576, + 0.41563517915309456, + 0.48990228013029324, + 0.5667752442996744, + 0.6306188925081434, + 0.6866449511400652, + 0.7309446254071662, + 0.7778501628664496, + 0.8104234527687297, + 0.8403908794788275, + 0.8618892508143323, + 0.8814332247557004, + 0.8970684039087949, + 0.903583061889251, + 1.0}; + + // Energy [keV] + cdf.y = {0, + 0.48850540675768084, + 0.8390389347819425, + 1.2521213482687141, + 1.7153033196164724, + 2.253638712180777, + 2.854653691809707, + 3.606073540073316, + 4.470346052913727, + 5.560291219507215, + 6.700556502915258, + 7.953772477101693, + 9.194596305637525, + 10.849180562221111, + 12.353474314071864, + 14.045888515617822, + 15.650634617544647, + 17.38079707555165, + 19.111008546659452, + 19.976130619913615, + 80.0}; + + CELER_ENSURE(cdf); + return cdf; +} + +//---------------------------------------------------------------------------// +/*! + * Static cycle rate data for muon-catalyzed fusion. + * + * \todo This data may be loaded from an external file in the future. + */ +static std::vector mucf_cycle_rates() +{ + //! \todo Implement + return {}; +}; + +//---------------------------------------------------------------------------// +/*! + * Static spin-flip data for muon-catalyzed fusion. + * + * \todo This data may be loaded from an external file in the future. + */ +static std::vector mucf_atom_spin_flip_rates() +{ + //! \todo Implement + return {}; +}; + +//---------------------------------------------------------------------------// +/*! + * Static atom transfer data for muon-catalyzed fusion. + * + * \todo This data may be loaded from an external file in the future. + */ +static std::vector mucf_atom_transfer_rates() +{ + //! \todo Implement + return {}; +}; + //---------------------------------------------------------------------------// /*! * Construct hardcoded muon-catalyzed fusion physics data. diff --git a/src/celeritas/inp/MucfPhysics.hh b/src/celeritas/inp/MucfPhysics.hh index 7854b85e0a..c55f13fd95 100644 --- a/src/celeritas/inp/MucfPhysics.hh +++ b/src/celeritas/inp/MucfPhysics.hh @@ -10,7 +10,7 @@ #include #include "corecel/inp/Grid.hh" -#include "celeritas/Types.hh" +#include "celeritas/mucf/Types.hh" namespace celeritas { diff --git a/src/celeritas/inp/MucfPhysicsData.hh b/src/celeritas/inp/MucfPhysicsData.hh deleted file mode 100644 index 20c023dc07..0000000000 --- a/src/celeritas/inp/MucfPhysicsData.hh +++ /dev/null @@ -1,117 +0,0 @@ -//------------------------------- -*- C++ -*- -------------------------------// -// Copyright Celeritas contributors: see top-level COPYRIGHT file for details -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file celeritas/inp/MucfPhysicsData.hh -//---------------------------------------------------------------------------// -#pragma once - -#include "corecel/Macros.hh" -#include "corecel/inp/Grid.hh" - -#include "MucfPhysics.hh" - -namespace celeritas -{ -namespace inp -{ -//---------------------------------------------------------------------------// -/*! - * Static muon energy CDF data for muon-catalyzed fusion. - * - * \todo This data may be loaded from an external file in the future. - */ -static Grid mucf_muon_energy_cdf() -{ - Grid cdf; - cdf.interpolation.type = InterpolationType::cubic_spline; - - // Cumulative distribution data [unitless] - cdf.x = {0, - 0.04169381107491854, - 0.08664495114006499, - 0.14332247557003264, - 0.20456026058631915, - 0.2723127035830618, - 0.34136807817589576, - 0.41563517915309456, - 0.48990228013029324, - 0.5667752442996744, - 0.6306188925081434, - 0.6866449511400652, - 0.7309446254071662, - 0.7778501628664496, - 0.8104234527687297, - 0.8403908794788275, - 0.8618892508143323, - 0.8814332247557004, - 0.8970684039087949, - 0.903583061889251, - 1.0}; - - // Energy [keV] - cdf.y = {0, - 0.48850540675768084, - 0.8390389347819425, - 1.2521213482687141, - 1.7153033196164724, - 2.253638712180777, - 2.854653691809707, - 3.606073540073316, - 4.470346052913727, - 5.560291219507215, - 6.700556502915258, - 7.953772477101693, - 9.194596305637525, - 10.849180562221111, - 12.353474314071864, - 14.045888515617822, - 15.650634617544647, - 17.38079707555165, - 19.111008546659452, - 19.976130619913615, - 80.0}; - - CELER_ENSURE(cdf); - return cdf; -} - -//---------------------------------------------------------------------------// -/*! - * Static cycle rate data for muon-catalyzed fusion. - * - * \todo This data may be loaded from an external file in the future. - */ -static std::vector mucf_cycle_rates() -{ - //! \todo Implement - return {}; -}; - -//---------------------------------------------------------------------------// -/*! - * Static spin-flip data for muon-catalyzed fusion. - * - * \todo This data may be loaded from an external file in the future. - */ -static std::vector mucf_atom_spin_flip_rates() -{ - //! \todo Implement - return {}; -}; - -//---------------------------------------------------------------------------// -/*! - * Static atom transfer data for muon-catalyzed fusion. - * - * \todo This data may be loaded from an external file in the future. - */ -static std::vector mucf_atom_transfer_rates() -{ - //! \todo Implement - return {}; -}; - -//---------------------------------------------------------------------------// -} // namespace inp -} // namespace celeritas diff --git a/src/celeritas/mucf/Types.hh b/src/celeritas/mucf/Types.hh new file mode 100644 index 0000000000..ed64826410 --- /dev/null +++ b/src/celeritas/mucf/Types.hh @@ -0,0 +1,48 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/Types.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/OpaqueId.hh" + +namespace celeritas +{ +//---------------------------------------------------------------------------// +// ENUMERATIONS +//---------------------------------------------------------------------------// +/*! + * Muonic atom selection from material data. This is *not* intended to be used + * by the transport loop. + */ +enum class MucfMuonicAtom +{ + deuterium, + tritium, + size_ +}; + +//---------------------------------------------------------------------------// +/*! + * Muonic molecule selection from material data. This is *not* intended to be + * used by the transport loop. + */ +enum class MucfMuonicMolecule +{ + deuterium_deuterium, + deuterium_tritium, + tritium_tritium, + size_ +}; + +//---------------------------------------------------------------------------// +// TYPE ALIASES +//---------------------------------------------------------------------------// + +//! Opaque index of a muCF material component +using MuCfMatCompId = OpaqueId; + +//---------------------------------------------------------------------------// +} // namespace celeritas diff --git a/src/celeritas/mucf/data/DTMixMucfData.hh b/src/celeritas/mucf/data/DTMixMucfData.hh index 43a57c2508..4074765d79 100644 --- a/src/celeritas/mucf/data/DTMixMucfData.hh +++ b/src/celeritas/mucf/data/DTMixMucfData.hh @@ -14,6 +14,7 @@ #include "corecel/grid/NonuniformGridData.hh" #include "corecel/io/Join.hh" #include "celeritas/Types.hh" +#include "celeritas/mucf/Types.hh" #include "celeritas/phys/ParticleParams.hh" namespace celeritas @@ -22,7 +23,7 @@ namespace celeritas /*! * ParticleIds used by the \c DTMixMucfModel . */ -struct MucfParticles +struct MucfParticleIds { //! Primary ParticleId mu_minus; @@ -51,46 +52,6 @@ struct MucfParticles && muonic_hydrogen && muonic_alpha && muonic_triton && muonic_helium3; } - - //! Assign from ParticleParams (anonymous free function in the model.cc?) - static MucfParticles from_params(ParticleParams const& particles) - { -#define MP_ADD(MEMBER) \ - pids.MEMBER = particles.find(pdg::MEMBER()); \ - if (!pids.MEMBER) \ - { \ - missing.push_back({#MEMBER, pdg::MEMBER()}); \ - } - - using PairStrPdg = std::pair; - std::vector missing; - MucfParticles pids; - - MP_ADD(mu_minus); - MP_ADD(neutron); - MP_ADD(proton); - MP_ADD(alpha); - //! \todo Decide whether to implement these PDGs in PDGNumber.hh -#if 0 - MP_ADD(helium_3); - MP_ADD(muonic_hydrogen); - MP_ADD(muonic_alpha); - MP_ADD(muonic_deuteron); - MP_ADD(muonic_triton); - MP_ADD(muonic_helium3); -#endif - CELER_VALIDATE( - missing.empty(), - << "missing particles required for muon-catalyzed fusion: " - << join(missing.begin(), missing.end(), ", ", [](PairStrPdg const& p) { - return p.first + " (PDG " - + std::to_string(p.second.unchecked_get()) + ')'; - })); - - return pids; - -#undef MP_ADD - } }; //---------------------------------------------------------------------------// @@ -103,17 +64,15 @@ struct DTMixMucfData template using Items = Collection; template - using MatCycleItems = Collection; - template using MaterialItems = Collection; - using Grid = NonuniformGridRecord; + using GridRecord = NonuniformGridRecord; using CycleTimesArray = EnumArray>; //! Particle IDs - MucfParticles particles; + MucfParticleIds particles; //! Muon CDF energy grid for sampling outgoing muCF muons - Grid muon_energy_cdf; //! \todo Verify energy unit + GridRecord muon_energy_cdf; //! \todo Verify energy unit Items reals; //!@{ @@ -121,7 +80,7 @@ struct DTMixMucfData //! \c PhysMatId indexed by \c MuCfMatCompId MaterialItems matcompid_to_matid; //! Cycle times per material: [mat_comp_id][muonic_molecule][spin_index] - MatCycleItems cycle_times; //!< In [s] + MaterialItems cycle_times; //!< In [s] //! \todo Add mean atom spin flip times //! \todo Add mean atom transfer times //!@} diff --git a/src/celeritas/mucf/executor/detail/DTChannelSelector.hh b/src/celeritas/mucf/executor/detail/DTChannelSelector.hh index 28483dea53..418571083a 100644 --- a/src/celeritas/mucf/executor/detail/DTChannelSelector.hh +++ b/src/celeritas/mucf/executor/detail/DTChannelSelector.hh @@ -14,7 +14,7 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Select final channel for muonic dd molecules. + * Select final channel for muonic dt molecules. * * This selection already accounts for sticking, as that is one of the possible * outcomes. diff --git a/src/celeritas/mucf/executor/detail/TTChannelSelector.hh b/src/celeritas/mucf/executor/detail/TTChannelSelector.hh index 1ded52adb7..d69bab7bfd 100644 --- a/src/celeritas/mucf/executor/detail/TTChannelSelector.hh +++ b/src/celeritas/mucf/executor/detail/TTChannelSelector.hh @@ -14,7 +14,7 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Select final channel for muonic dd molecules. + * Select final channel for muonic tt molecules. * * This selection already accounts for sticking, as that is one of the possible * outcomes. diff --git a/src/celeritas/mucf/interactor/DDMucfInteractor.hh b/src/celeritas/mucf/interactor/DDMucfInteractor.hh index 48aae54812..a659758f10 100644 --- a/src/celeritas/mucf/interactor/DDMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DDMucfInteractor.hh @@ -38,7 +38,7 @@ class DDMucfInteractor // Construct from shared and state data inline CELER_FUNCTION DDMucfInteractor(NativeCRef const& data, - Channel const channel, + Channel channel, StackAllocator& allocate); // Sample an interaction with the given RNG diff --git a/src/celeritas/mucf/interactor/DTMucfInteractor.hh b/src/celeritas/mucf/interactor/DTMucfInteractor.hh index 5af9a5f3f8..94498de7f0 100644 --- a/src/celeritas/mucf/interactor/DTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DTMucfInteractor.hh @@ -36,7 +36,7 @@ class DTMucfInteractor // Construct from shared and state data inline CELER_FUNCTION DTMucfInteractor(NativeCRef const& data, - Channel const channel, + Channel channel, StackAllocator& allocate); // Sample an interaction with the given RNG diff --git a/src/celeritas/mucf/interactor/TTMucfInteractor.hh b/src/celeritas/mucf/interactor/TTMucfInteractor.hh index 97ade2512f..c37cda8733 100644 --- a/src/celeritas/mucf/interactor/TTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/TTMucfInteractor.hh @@ -36,7 +36,7 @@ class TTMucfInteractor // Construct from shared and state data inline CELER_FUNCTION TTMucfInteractor(NativeCRef const& data, - Channel const channel, + Channel channel, StackAllocator& allocate); // Sample an interaction with the given RNG diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc index ac4c4e9e56..b21e045710 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.cc +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -8,11 +8,11 @@ #include +#include "corecel/OpaqueIdIO.hh" #include "corecel/inp/Grid.hh" #include "celeritas/global/ActionLauncher.hh" #include "celeritas/global/TrackExecutor.hh" #include "celeritas/grid/NonuniformGridBuilder.hh" -#include "celeritas/inp/MucfPhysicsData.hh" #include "celeritas/mucf/executor/DTMixMucfExecutor.hh" // IWYU pragma: associated #include "celeritas/phys/InteractionApplier.hh" // IWYU pragma: associated #include "celeritas/phys/PDGNumber.hh" @@ -21,6 +21,56 @@ namespace celeritas { +namespace +{ +//---------------------------------------------------------------------------// +/*! + * Assign particle IDs from \c ParticleParams . + */ +static MucfParticleIds from_params(ParticleParams const& particles) +{ + using PairStrPdg = std::pair; + std::vector missing; + MucfParticleIds result; + +#define MP_ADD(MEMBER) \ + result.MEMBER = particles.find(pdg::MEMBER()); \ + if (!result.MEMBER) \ + { \ + missing.push_back({#MEMBER, pdg::MEMBER()}); \ + } + + MP_ADD(mu_minus); + MP_ADD(neutron); + MP_ADD(proton); + MP_ADD(alpha); + + //! \todo Decide whether to implement these PDGs in PDGNumber.hh +#if 0 + MP_ADD(helium_3); + MP_ADD(muonic_hydrogen); + MP_ADD(muonic_deuteron); + MP_ADD(muonic_triton); + MP_ADD(muonic_alpha); + MP_ADD(muonic_helium3); +#endif + + CELER_VALIDATE(missing.empty(), + << "missing particles required for muon-catalyzed fusion: " + << join_stream(missing.begin(), + missing.end(), + ", ", + [](std::ostream& os, PairStrPdg const& p) { + os << p.first << " (PDG " + << p.second.unchecked_get() << ')'; + })); + return result; + +#undef MP_ADD +} +//---------------------------------------------------------------------------// +} // namespace + //---------------------------------------------------------------------------// /*! * Construct from model ID and other necessary data. @@ -57,9 +107,8 @@ DTMixMucfModel::DTMixMucfModel(ActionId id, inp::MucfPhysics inp_data = inp::MucfPhysics::from_default(); CELER_EXPECT(inp_data); - // Load particle IDs HostVal host_data; - host_data.particles = MucfParticles::from_params(particles); + host_data.particles = from_params(particles); // Copy muon energy CDF data using NonuniformGridBuilder NonuniformGridBuilder build_grid_record{&host_data.reals}; diff --git a/src/celeritas/mucf/process/MucfProcess.cc b/src/celeritas/mucf/process/MucfProcess.cc index cde972edfb..b3b3dc5d8c 100644 --- a/src/celeritas/mucf/process/MucfProcess.cc +++ b/src/celeritas/mucf/process/MucfProcess.cc @@ -23,7 +23,6 @@ MucfProcess::MucfProcess(SPConstParticles particles, SPConstMaterials materials) //! \todo Fix ImportProcessClass CELER_EXPECT(particles_); CELER_EXPECT(materials_); - CELER_EXPECT(particles_->find(pdg::mu_minus())); } //---------------------------------------------------------------------------// From 67be430ec278043cce795d55e47eb80f57cdb395 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 5 Jan 2026 14:50:43 -0500 Subject: [PATCH 10/35] Move component id getter function into DTMixMucfData --- src/celeritas/mucf/data/DTMixMucfData.hh | 19 +++++++++++++++++++ .../mucf/executor/DTMixMucfExecutor.hh | 19 ++++--------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/celeritas/mucf/data/DTMixMucfData.hh b/src/celeritas/mucf/data/DTMixMucfData.hh index 4074765d79..b53c0b1ab0 100644 --- a/src/celeritas/mucf/data/DTMixMucfData.hh +++ b/src/celeritas/mucf/data/DTMixMucfData.hh @@ -94,6 +94,25 @@ struct DTMixMucfData && (matcompid_to_matid.size() == cycle_times.size()); } + //! Get the material componend ID from a given \c PhysMatId . + CELER_FUNCTION MuCfMatCompId material_component_id(PhysMatId matid) const + { + CELER_EXPECT(matid); + CELER_EXPECT(this); + CELER_EXPECT(!this->matcompid_to_matid.empty()); + + for (auto i : range(this->matcompid_to_matid.size())) + { + if (auto const comp_id = MuCfMatCompId{i}; + this->matcompid_to_matid[comp_id] == matid) + { + return comp_id; + } + } + // Component ID not found + return MuCfMatCompId{}; + } + //! Assign from another set of data template DTMixMucfData& operator=(DTMixMucfData const& other) diff --git a/src/celeritas/mucf/executor/DTMixMucfExecutor.hh b/src/celeritas/mucf/executor/DTMixMucfExecutor.hh index 7abfc107c1..ce594cbbd5 100644 --- a/src/celeritas/mucf/executor/DTMixMucfExecutor.hh +++ b/src/celeritas/mucf/executor/DTMixMucfExecutor.hh @@ -74,22 +74,11 @@ DTMixMucfExecutor::operator()(celeritas::CoreTrackView const& track) auto const molecule_spin = select_molecule_spin(rng); // Load cycle time for the selected molecule - auto find_component = [&](PhysMatId matid) -> MuCfMatCompId { - for (auto i : range(data.matcompid_to_matid.size())) - { - auto const comp_id = MuCfMatCompId{i}; - if (data.matcompid_to_matid[comp_id] == matid) - { - return MuCfMatCompId{i}; - } - } - return MuCfMatCompId{}; - }; - - auto const mat_comp = find_component(track.material().material_id()); - CELER_ASSERT(mat_comp); + auto const mat_comp_id + = data.material_component_id(track.material().material_id()); + CELER_ASSERT(mat_comp_id); auto const cycle_time - = data.cycle_times[mat_comp][muonic_molecule][molecule_spin]; + = data.cycle_times[mat_comp_id][muonic_molecule][molecule_spin]; CELER_ASSERT(cycle_time > 0); // Check if muon decays before fusion happens From 0842322a8e1a34e7262db1038eb524526545b21b Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 5 Jan 2026 14:54:55 -0500 Subject: [PATCH 11/35] Assign all current PDG numbers --- src/celeritas/mucf/data/DTMixMucfData.hh | 9 ++++----- src/celeritas/mucf/model/DTMixMucfModel.cc | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/celeritas/mucf/data/DTMixMucfData.hh b/src/celeritas/mucf/data/DTMixMucfData.hh index b53c0b1ab0..d6104ddea6 100644 --- a/src/celeritas/mucf/data/DTMixMucfData.hh +++ b/src/celeritas/mucf/data/DTMixMucfData.hh @@ -33,7 +33,7 @@ struct MucfParticleIds ParticleId neutron; ParticleId proton; ParticleId alpha; - ParticleId helium3; + ParticleId he3; //!@} //!@{ @@ -42,15 +42,14 @@ struct MucfParticleIds ParticleId muonic_deuteron; ParticleId muonic_triton; ParticleId muonic_alpha; - ParticleId muonic_helium3; + ParticleId muonic_he3; //!@} //! Check whether all particles are assigned CELER_FUNCTION explicit operator bool() const { - return mu_minus && neutron && proton && alpha && helium3 - && muonic_hydrogen && muonic_alpha && muonic_triton - && muonic_helium3; + return mu_minus && neutron && proton && alpha && he3 && muonic_hydrogen + && muonic_alpha && muonic_triton && muonic_he3; } }; diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc index b21e045710..7328920b3a 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.cc +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -44,15 +44,15 @@ static MucfParticleIds from_params(ParticleParams const& particles) MP_ADD(neutron); MP_ADD(proton); MP_ADD(alpha); + MP_ADD(he3); + MP_ADD(muonic_deuteron); + MP_ADD(muonic_triton); //! \todo Decide whether to implement these PDGs in PDGNumber.hh #if 0 - MP_ADD(helium_3); MP_ADD(muonic_hydrogen); - MP_ADD(muonic_deuteron); - MP_ADD(muonic_triton); MP_ADD(muonic_alpha); - MP_ADD(muonic_helium3); + MP_ADD(muonic_he3); #endif CELER_VALIDATE(missing.empty(), From 7123f3ed44531d1c6d29b72fe365cfb5af5bd1c2 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Fri, 9 Jan 2026 14:25:46 -0500 Subject: [PATCH 12/35] Address comments: - Fix import data - Refactor the material calculator to behave as a material inserter - Interactors: define number of secondaries as EnumArrays - Rename internal material id to MucfMatId - Remove find MucfMatId function from DTMixMucfData and make it a lambda in the executor --- src/celeritas/CMakeLists.txt | 2 +- src/celeritas/inp/MucfPhysics.cc | 123 +++++++-------- src/celeritas/inp/MucfPhysics.hh | 50 ++++-- src/celeritas/mucf/Types.hh | 25 ++- src/celeritas/mucf/data/DTMixMucfData.hh | 34 +---- .../mucf/executor/DTMixMucfExecutor.hh | 22 ++- .../mucf/interactor/DDMucfInteractor.hh | 32 +--- .../mucf/interactor/DTMucfInteractor.hh | 29 +--- .../mucf/interactor/TTMucfInteractor.hh | 29 +--- src/celeritas/mucf/model/DTMixMucfModel.cc | 32 +--- .../model/detail/DTMixMaterialCalculator.hh | 142 ------------------ ...lCalculator.cc => MucfMaterialInserter.cc} | 117 +++++++++------ .../mucf/model/detail/MucfMaterialInserter.hh | 73 +++++++++ 13 files changed, 324 insertions(+), 386 deletions(-) delete mode 100644 src/celeritas/mucf/model/detail/DTMixMaterialCalculator.hh rename src/celeritas/mucf/model/detail/{DTMixMaterialCalculator.cc => MucfMaterialInserter.cc} (66%) create mode 100644 src/celeritas/mucf/model/detail/MucfMaterialInserter.hh diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index c4a2365e3f..04dec8e4e3 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -100,7 +100,7 @@ list(APPEND SOURCES mat/MaterialParams.cc mat/MaterialParamsOutput.cc mat/detail/Utils.cc - mucf/model/detail/DTMixMaterialCalculator.cc + mucf/model/detail/MucfMaterialInserter.cc mucf/process/MucfProcess.cc neutron/model/CascadeOptions.cc neutron/model/CascadeOptionsIO.json.cc diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index 32a7e37ff1..de87bbcba8 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -10,62 +10,66 @@ namespace celeritas { namespace inp { +namespace +{ //---------------------------------------------------------------------------// /*! - * Static muon energy CDF data for muon-catalyzed fusion. - * - * \todo This data may be loaded from an external file in the future. + * Muon energy CDF data for muon-catalyzed fusion. */ -static Grid mucf_muon_energy_cdf() +Grid mucf_muon_energy_cdf() { Grid cdf; cdf.interpolation.type = InterpolationType::cubic_spline; // Cumulative distribution data [unitless] - cdf.x = {0, - 0.04169381107491854, - 0.08664495114006499, - 0.14332247557003264, - 0.20456026058631915, - 0.2723127035830618, - 0.34136807817589576, - 0.41563517915309456, - 0.48990228013029324, - 0.5667752442996744, - 0.6306188925081434, - 0.6866449511400652, - 0.7309446254071662, - 0.7778501628664496, - 0.8104234527687297, - 0.8403908794788275, - 0.8618892508143323, - 0.8814332247557004, - 0.8970684039087949, - 0.903583061889251, - 1.0}; + cdf.x = { + 0, + 0.04169381107491854, + 0.08664495114006499, + 0.14332247557003264, + 0.20456026058631915, + 0.2723127035830618, + 0.34136807817589576, + 0.41563517915309456, + 0.48990228013029324, + 0.5667752442996744, + 0.6306188925081434, + 0.6866449511400652, + 0.7309446254071662, + 0.7778501628664496, + 0.8104234527687297, + 0.8403908794788275, + 0.8618892508143323, + 0.8814332247557004, + 0.8970684039087949, + 0.903583061889251, + 1.0, + }; // Energy [keV] - cdf.y = {0, - 0.48850540675768084, - 0.8390389347819425, - 1.2521213482687141, - 1.7153033196164724, - 2.253638712180777, - 2.854653691809707, - 3.606073540073316, - 4.470346052913727, - 5.560291219507215, - 6.700556502915258, - 7.953772477101693, - 9.194596305637525, - 10.849180562221111, - 12.353474314071864, - 14.045888515617822, - 15.650634617544647, - 17.38079707555165, - 19.111008546659452, - 19.976130619913615, - 80.0}; + cdf.y = { + 0, + 0.48850540675768084, + 0.8390389347819425, + 1.2521213482687141, + 1.7153033196164724, + 2.253638712180777, + 2.854653691809707, + 3.606073540073316, + 4.470346052913727, + 5.560291219507215, + 6.700556502915258, + 7.953772477101693, + 9.194596305637525, + 10.849180562221111, + 12.353474314071864, + 14.045888515617822, + 15.650634617544647, + 17.38079707555165, + 19.111008546659452, + 19.976130619913615, + 80.0, + }; CELER_ENSURE(cdf); return cdf; @@ -73,39 +77,35 @@ static Grid mucf_muon_energy_cdf() //---------------------------------------------------------------------------// /*! - * Static cycle rate data for muon-catalyzed fusion. - * - * \todo This data may be loaded from an external file in the future. + * Cycle rate data for muon-catalyzed fusion. */ -static std::vector mucf_cycle_rates() +std::vector mucf_cycle_rates() { //! \todo Implement return {}; -}; +} //---------------------------------------------------------------------------// /*! - * Static spin-flip data for muon-catalyzed fusion. - * - * \todo This data may be loaded from an external file in the future. + * Spin-flip data for muon-catalyzed fusion. */ -static std::vector mucf_atom_spin_flip_rates() +std::vector mucf_atom_spin_flip_rates() { //! \todo Implement return {}; -}; +} //---------------------------------------------------------------------------// /*! - * Static atom transfer data for muon-catalyzed fusion. - * - * \todo This data may be loaded from an external file in the future. + * Atom transfer data for muon-catalyzed fusion. */ -static std::vector mucf_atom_transfer_rates() +std::vector mucf_atom_transfer_rates() { //! \todo Implement return {}; -}; +} +//---------------------------------------------------------------------------// +} // namespace //---------------------------------------------------------------------------// /*! @@ -118,6 +118,7 @@ static std::vector mucf_atom_transfer_rates() MucfPhysics MucfPhysics::from_default() { MucfPhysics result; + result.scalars = MucfScalars::from_default(); result.muon_energy_cdf = mucf_muon_energy_cdf(); result.cycle_rates = mucf_cycle_rates(); result.atom_transfer = mucf_atom_transfer_rates(); diff --git a/src/celeritas/inp/MucfPhysics.hh b/src/celeritas/inp/MucfPhysics.hh index c55f13fd95..820f4fa358 100644 --- a/src/celeritas/inp/MucfPhysics.hh +++ b/src/celeritas/inp/MucfPhysics.hh @@ -10,12 +10,39 @@ #include #include "corecel/inp/Grid.hh" +#include "celeritas/Quantities.hh" #include "celeritas/mucf/Types.hh" namespace celeritas { namespace inp { +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion scalars. + * + * Default values are the same used by Acceleron. + */ +struct MucfScalars +{ + // Atomic masses + units::AmuMass protium; //!< Protium atomic mass [AMU] + units::AmuMass deuterium; //!< Deuterium atomic mass [AMU] + units::AmuMass tritium; //!< Tritium atomic mass [AMU] + units::InvCcDensity liquid_hydrogen_density; //!< LHD unit [1/cm^3] + + //! Initialize with hardcoded values + static MucfScalars from_default() + { + MucfScalars result; + result.protium = units::AmuMass{1.007825031898}; + result.deuterium = units::AmuMass{2.014101777844}; + result.tritium = units::AmuMass{3.016049281320}; + result.liquid_hydrogen_density = units::InvCcDensity{4.25e22}; + return result; + } +}; + //---------------------------------------------------------------------------// /*! * Muon-catalyzed fusion mean cycle rate data. @@ -78,20 +105,18 @@ struct MucfAtomTransferRate * Muon-catalyzed fusion mean atom spin flip data. * * Spin flip rates are as a function of temperature, with each grid/table - * representing an atom pair combination and its spin (e.g. - * deuterium-tritium, spin 1). Ordering is important, thus same spin - * deuterium-tritium and tritium-deuterium have different tables, which - * leads to a different final spin flip rate for a given material - * definition. + * representing an atom pair combination and its spin (e.g. deuterium-tritium, + * spin 1). Ordering is important, thus same spin deuterium-tritium and + * tritium-deuterium have different tables, which leads to a different final + * spin flip rate for a given material definition. * * Each struct holds one of such combinations, with the full set of - * combinations being the \c vector in \c MucfPhysics - * . + * combinations being the \c vector in \c MucfPhysics . * * \note These grids are host-only, with only the final spin flip rate per - * state (which is just a \c real_type ) for each combination being needed - * in the stepping loop. This is because these rates are material - * dependent, and thus can be cached at model construction. + * state (which is just a \c real_type ) for each combination being needed in + * the stepping loop. This is because these rates are material dependent, and + * thus can be cached at model construction. */ struct MucfAtomSpinFlipRate { @@ -110,14 +135,15 @@ struct MucfAtomSpinFlipRate * and * - Mean cycle rate data for dd, dt, and tt muonic molecules. * - * Muonic atom transfer and muonic atom spin flip are secondary effects and - * not required for muCF to function. + * Muonic atom transfer and muonic atom spin flip are secondary effects and not + * required for muCF to function. */ struct MucfPhysics { template using Vec = std::vector; + MucfScalars scalars; Grid muon_energy_cdf; //!< CDF for sampling the outgoing muCF muon Vec cycle_rates; //!< Mean cycle rates for muonic molecules Vec atom_transfer; //!< Muonic atom transfer rates diff --git a/src/celeritas/mucf/Types.hh b/src/celeritas/mucf/Types.hh index ed64826410..0a7c410dda 100644 --- a/src/celeritas/mucf/Types.hh +++ b/src/celeritas/mucf/Types.hh @@ -37,12 +37,35 @@ enum class MucfMuonicMolecule size_ }; +//---------------------------------------------------------------------------// +/*! + * Enum for safely accessing hydrogen isoprotologues. + * + * Hydrogen isoprotologue molecules are: + * - Homonuclear: \f$ ^2H \f$, \f$ ^2d \f$, and \f$ ^2t \f$ + * - Heteronuclear: hd, ht, and dt. + * + * \note Muon-catalyzed fusion data is only applicable to a material with + * concentrations in thermodynamic equilibrium. This equilibrium is calculated + * at model construction from the material temperature and its h, d, and t + * fractions. + */ +enum class MucfIsoprotologueMolecule +{ + protium_protium, + protium_deuterium, + protium_tritium, + deuterium_tritium, + tritium_tritium, + size_ +}; + //---------------------------------------------------------------------------// // TYPE ALIASES //---------------------------------------------------------------------------// //! Opaque index of a muCF material component -using MuCfMatCompId = OpaqueId; +using MuCfMatId = OpaqueId; //---------------------------------------------------------------------------// } // namespace celeritas diff --git a/src/celeritas/mucf/data/DTMixMucfData.hh b/src/celeritas/mucf/data/DTMixMucfData.hh index d6104ddea6..63affe3867 100644 --- a/src/celeritas/mucf/data/DTMixMucfData.hh +++ b/src/celeritas/mucf/data/DTMixMucfData.hh @@ -63,7 +63,7 @@ struct DTMixMucfData template using Items = Collection; template - using MaterialItems = Collection; + using MaterialItems = Collection; using GridRecord = NonuniformGridRecord; using CycleTimesArray = EnumArray>; @@ -76,40 +76,20 @@ struct DTMixMucfData //!@{ //! Material-dependent data calculated at model construction - //! \c PhysMatId indexed by \c MuCfMatCompId - MaterialItems matcompid_to_matid; + //! \c PhysMatId indexed by \c MuCfMatId + MaterialItems mucfmatid_to_matid; //! Cycle times per material: [mat_comp_id][muonic_molecule][spin_index] MaterialItems cycle_times; //!< In [s] //! \todo Add mean atom spin flip times //! \todo Add mean atom transfer times //!@} - //! \todo Check whether the data are assigned + //! Check whether the data are assigned explicit CELER_FUNCTION operator bool() const { - //! \todo Finalize implementation - return particles && muon_energy_cdf && !matcompid_to_matid.empty() + return particles && muon_energy_cdf && !mucfmatid_to_matid.empty() && !cycle_times.empty() - && (matcompid_to_matid.size() == cycle_times.size()); - } - - //! Get the material componend ID from a given \c PhysMatId . - CELER_FUNCTION MuCfMatCompId material_component_id(PhysMatId matid) const - { - CELER_EXPECT(matid); - CELER_EXPECT(this); - CELER_EXPECT(!this->matcompid_to_matid.empty()); - - for (auto i : range(this->matcompid_to_matid.size())) - { - if (auto const comp_id = MuCfMatCompId{i}; - this->matcompid_to_matid[comp_id] == matid) - { - return comp_id; - } - } - // Component ID not found - return MuCfMatCompId{}; + && (mucfmatid_to_matid.size() == cycle_times.size()); } //! Assign from another set of data @@ -122,7 +102,7 @@ struct DTMixMucfData this->particles = other.particles; this->reals = other.reals; this->muon_energy_cdf = other.muon_energy_cdf; - this->matcompid_to_matid = other.matcompid_to_matid; + this->mucfmatid_to_matid = other.mucfmatid_to_matid; this->cycle_times = other.cycle_times; return *this; diff --git a/src/celeritas/mucf/executor/DTMixMucfExecutor.hh b/src/celeritas/mucf/executor/DTMixMucfExecutor.hh index ce594cbbd5..6988fb3cdf 100644 --- a/src/celeritas/mucf/executor/DTMixMucfExecutor.hh +++ b/src/celeritas/mucf/executor/DTMixMucfExecutor.hh @@ -73,12 +73,26 @@ DTMixMucfExecutor::operator()(celeritas::CoreTrackView const& track) detail::MuonicMoleculeSpinSelector select_molecule_spin(muonic_molecule); auto const molecule_spin = select_molecule_spin(rng); + // Find muCF material ID from PhysMatId + auto find = [&](PhysMatId matid) -> MuCfMatId { + CELER_EXPECT(matid); + for (auto i : range(data.mucfmatid_to_matid.size())) + { + if (auto const comp_id = MuCfMatId{i}; + data.mucfmatid_to_matid[comp_id] == matid) + { + return comp_id; + } + } + // MuCF material ID not found + return MuCfMatId{}; + }; + // Load cycle time for the selected molecule - auto const mat_comp_id - = data.material_component_id(track.material().material_id()); - CELER_ASSERT(mat_comp_id); + auto const mucf_matid = find(track.material().material_id()); + CELER_ASSERT(mucf_matid); auto const cycle_time - = data.cycle_times[mat_comp_id][muonic_molecule][molecule_spin]; + = data.cycle_times[mucf_matid][muonic_molecule][molecule_spin]; CELER_ASSERT(cycle_time > 0); // Check if muon decays before fusion happens diff --git a/src/celeritas/mucf/interactor/DDMucfInteractor.hh b/src/celeritas/mucf/interactor/DDMucfInteractor.hh index a659758f10..d5686b3008 100644 --- a/src/celeritas/mucf/interactor/DDMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DDMucfInteractor.hh @@ -52,9 +52,12 @@ class DDMucfInteractor Channel channel_{Channel::size_}; // Allocate space for secondary particles StackAllocator& allocate_; - - // Get number of secondaries for each channel - inline CELER_FUNCTION size_type num_secondaries() const; + // Number of secondaries per channel + static constexpr EnumArray num_secondaries_{ + 3, // helium3_muon_neutron + 2, // muonichelium3_neutron + 3 // hydrogen3_muon_proton + }; // Sample Interaction secondaries template @@ -85,7 +88,7 @@ template CELER_FUNCTION Interaction DDMucfInteractor::operator()(Engine& rng) { // Allocate space for the final fusion channel - Secondary* secondaries = allocate_(this->num_secondaries()); + Secondary* secondaries = allocate_(num_secondaries_[channel_]); if (secondaries == nullptr) { // Failed to allocate space for secondaries @@ -99,25 +102,6 @@ CELER_FUNCTION Interaction DDMucfInteractor::operator()(Engine& rng) return result; } -//---------------------------------------------------------------------------// -/*! - * Return number of secondaries from each fusion channel. - */ -CELER_FUNCTION size_type DDMucfInteractor::num_secondaries() const -{ - switch (channel_) - { - case Channel::helium3_muon_neutron: - return 3; - case Channel::muonichelium3_neutron: - return 2; - case Channel::hydrogen3_muon_proton: - return 3; - default: - CELER_ASSERT_UNREACHABLE(); - } -} - //---------------------------------------------------------------------------// /*! * Sample the secondaries of the selected channel. @@ -145,7 +129,7 @@ DDMucfInteractor::sample_secondaries(Secondary* secondaries /*, other args */, CELER_ASSERT_UNREACHABLE(); } - return Span{secondaries, this->num_secondaries()}; + return Span{secondaries, num_secondaries_[channel_]}; } //---------------------------------------------------------------------------// diff --git a/src/celeritas/mucf/interactor/DTMucfInteractor.hh b/src/celeritas/mucf/interactor/DTMucfInteractor.hh index 94498de7f0..93a981811f 100644 --- a/src/celeritas/mucf/interactor/DTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DTMucfInteractor.hh @@ -50,9 +50,11 @@ class DTMucfInteractor Channel channel_{Channel::size_}; // Allocate space for secondary particles StackAllocator& allocate_; - - // Get number of secondaries for each channel - inline CELER_FUNCTION size_type num_secondaries() const; + // Number of secondaries per channel + static constexpr EnumArray num_secondaries_{ + 3, // alpha_muon_neutron + 2 // muonicalpha_neutron + }; // Sample Interaction secondaries template @@ -83,7 +85,7 @@ template CELER_FUNCTION Interaction DTMucfInteractor::operator()(Engine& rng) { // Allocate space for the final fusion channel - Secondary* secondaries = allocate_(this->num_secondaries()); + Secondary* secondaries = allocate_(num_secondaries_[channel_]); if (secondaries == nullptr) { // Failed to allocate space for secondaries @@ -97,23 +99,6 @@ CELER_FUNCTION Interaction DTMucfInteractor::operator()(Engine& rng) return result; } -//---------------------------------------------------------------------------// -/*! - * Return number of secondaries from each fusion channel. - */ -CELER_FUNCTION size_type DTMucfInteractor::num_secondaries() const -{ - switch (channel_) - { - case Channel::alpha_muon_neutron: - return 3; - case Channel::muonicalpha_neutron: - return 2; - default: - CELER_ASSERT_UNREACHABLE(); - } -} - //---------------------------------------------------------------------------// /*! * Sample the secondaries of the selected channel. @@ -138,7 +123,7 @@ DTMucfInteractor::sample_secondaries(Secondary* secondaries /*, other args */, CELER_ASSERT_UNREACHABLE(); } - return Span{secondaries, this->num_secondaries()}; + return Span{secondaries, num_secondaries_[channel_]}; } //---------------------------------------------------------------------------// diff --git a/src/celeritas/mucf/interactor/TTMucfInteractor.hh b/src/celeritas/mucf/interactor/TTMucfInteractor.hh index c37cda8733..30588024c2 100644 --- a/src/celeritas/mucf/interactor/TTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/TTMucfInteractor.hh @@ -50,9 +50,11 @@ class TTMucfInteractor Channel channel_{Channel::size_}; // Allocate space for secondary particles StackAllocator& allocate_; - - // Get number of secondaries for each channel - inline CELER_FUNCTION size_type num_secondaries() const; + // Number of secondaries per channel + static constexpr EnumArray num_secondaries_{ + 4, // alpha_muon_neutron_neutron + 3 // muonicalpha_neutron_neutron + }; // Sample Interaction secondaries template @@ -83,7 +85,7 @@ template CELER_FUNCTION Interaction TTMucfInteractor::operator()(Engine& rng) { // Allocate space for the final fusion channel - Secondary* secondaries = allocate_(this->num_secondaries()); + Secondary* secondaries = allocate_(num_secondaries_[channel_]); if (secondaries == nullptr) { // Failed to allocate space for secondaries @@ -97,23 +99,6 @@ CELER_FUNCTION Interaction TTMucfInteractor::operator()(Engine& rng) return result; } -//---------------------------------------------------------------------------// -/*! - * Return number of secondaries from each fusion channel. - */ -CELER_FUNCTION size_type TTMucfInteractor::num_secondaries() const -{ - switch (channel_) - { - case Channel::alpha_muon_neutron_neutron: - return 4; - case Channel::muonicalpha_neutron_neutron: - return 3; - default: - CELER_ASSERT_UNREACHABLE(); - } -} - //---------------------------------------------------------------------------// /*! * Sample the secondaries of the selected channel. @@ -138,7 +123,7 @@ TTMucfInteractor::sample_secondaries(Secondary* secondaries /*, other args */, CELER_ASSERT_UNREACHABLE(); } - return Span{secondaries, this->num_secondaries()}; + return Span{secondaries, num_secondaries_[channel_]}; } //---------------------------------------------------------------------------// diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc index 7328920b3a..0ad0e84372 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.cc +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -13,11 +13,12 @@ #include "celeritas/global/ActionLauncher.hh" #include "celeritas/global/TrackExecutor.hh" #include "celeritas/grid/NonuniformGridBuilder.hh" +#include "celeritas/inp/MucfPhysics.hh" #include "celeritas/mucf/executor/DTMixMucfExecutor.hh" // IWYU pragma: associated #include "celeritas/phys/InteractionApplier.hh" // IWYU pragma: associated #include "celeritas/phys/PDGNumber.hh" -#include "detail/DTMixMaterialCalculator.hh" +#include "detail/MucfMaterialInserter.hh" namespace celeritas { @@ -98,12 +99,7 @@ DTMixMucfModel::DTMixMucfModel(ActionId id, { CELER_EXPECT(id); - using VecCycleTimes - = std::vector; - using VecMatId = std::vector; - - // Initialize static muCF physics input data - //! \todo This may be replaced by user-provided data in the future + // Initialize muCF physics input data inp::MucfPhysics inp_data = inp::MucfPhysics::from_default(); CELER_EXPECT(inp_data); @@ -115,31 +111,17 @@ DTMixMucfModel::DTMixMucfModel(ActionId id, host_data.muon_energy_cdf = build_grid_record(inp_data.muon_energy_cdf); // Calculate and cache quantities for all materials with dt mixtures - VecCycleTimes vec_cycle_times; - VecMatId vec_physmatid; + detail::MucfMaterialInserter insert(&host_data); for (auto const& matid : range(materials.num_materials())) { auto const& mat_view = materials.get(PhysMatId{matid}); - - // Construct calculator for this material - detail::DTMixMaterialCalculator mat_calculator(mat_view); - if (!mat_calculator) + if (insert(mat_view)) { - // Skip non-dt mixture materials - continue; + CELER_LOG(debug) << "Added material ID " << mat_view.material_id() + << " as a muCF d-t mixture"; } - - // Store material ID and calculated data - //! \todo Store mean atom spin flip and transfer times - vec_physmatid.push_back(PhysMatId{matid}); - vec_cycle_times.push_back(mat_calculator.cycle_times()); } - make_builder(&host_data.matcompid_to_matid) - .insert_back(vec_physmatid.begin(), vec_physmatid.end()); - make_builder(&host_data.cycle_times) - .insert_back(vec_cycle_times.begin(), vec_cycle_times.end()); - // Copy to device data_ = CollectionMirror{std::move(host_data)}; CELER_ENSURE(this->data_); diff --git a/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.hh b/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.hh deleted file mode 100644 index 84a93430f0..0000000000 --- a/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.hh +++ /dev/null @@ -1,142 +0,0 @@ -//------------------------------- -*- C++ -*- -------------------------------// -// Copyright Celeritas contributors: see top-level COPYRIGHT file for details -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -//---------------------------------------------------------------------------// -//! \file celeritas/mucf/model/detail/DTMixMaterialCalculator.hh -//---------------------------------------------------------------------------// -#pragma once - -#include "corecel/cont/EnumArray.hh" -#include "celeritas/inp/MucfPhysics.hh" -#include "celeritas/mat/MaterialView.hh" -#include "celeritas/mucf/data/DTMixMucfData.hh" - -namespace celeritas -{ -namespace detail -{ -//---------------------------------------------------------------------------// -/*! - * Enum for safely accessing hydrogen isoprotologues. - * - * Hydrogen isoprotologue molecules are: - * - Homonuclear: \f$ ^2H \f$, \f$ ^2d \f$, and \f$ ^2t \f$ - * - Heteronuclear: hd, ht, and dt. - * - * \note Muon-catalyzed fusion data is only applicable to a material with - * concentrations in thermodynamic equilibrium. This equilibrium is calculated - * at model construction from the material temperature and its h, d, and t - * fractions. - */ -enum class MucfIsoprotologueMolecule -{ - protium_protium, - protium_deuterium, - protium_tritium, - deuterium_tritium, - tritium_tritium, - size_ -}; - -//---------------------------------------------------------------------------// -/*! - * Calculate material-dependent quantities for muon-catalyzed fusion. - * - * This class calculates all the muCF data that can be cached during model - * construction. - * Use its operator bool to store material data into \c DTMixMucfData : - * \code - for (auto matid : range(materials.num_materials())) - { - auto mat_view = materials.material_view(PhysMatId{matid}); - DTMixMaterialCalculator material_calculator(mat_view); - if (material_calculator) - { - // Valid d-t mixture material; Store data - } - } - * \endcode - */ -class DTMixMaterialCalculator -{ - public: - //!@{ - //! \name Type aliases - using CycleTimesArray = EnumArray>; - //!@} - - //! Construct with material data and calculate all quantities - DTMixMaterialCalculator(MaterialView const& material); - - //! Get mean cycle times - CycleTimesArray cycle_times() const { return cycle_times_; } - - //! Check if the material is valid for muon-catalyzed fusion - explicit operator bool() const { return !cycle_times_.empty(); } - - private: - using LhdArray = EnumArray; - using EquilibriumArray = EnumArray; - using AtomicMassNumber = AtomicNumber; - - //// DATA //// - - MaterialView material_; - LhdArray lhd_densities_; - EquilibriumArray eq_densities_; - EnumArray has_isotope_; - CycleTimesArray cycle_times_; - - //// LOCAL SCALARS //// - //! \todo Values are the same used by Acceleron and may need revisiting. - // { - // Atomic masses - static constexpr units::AmuMass protium() - { - return units::AmuMass{1.007825031898}; - } - - static constexpr units::AmuMass deuterium() - { - return units::AmuMass{2.014101777844}; - } - - static constexpr units::AmuMass tritium() - { - return units::AmuMass{3.016049281320}; - } - //} - - // Liquid hydrogen density (LHD) unit [1/cm^3] - static constexpr auto liquid_hydrogen_density() - { - return units::InvCcDensity{4.25e22}; - } - - //// HELPER FUNCTIONS //// - - // Calculate dt mixture densities in units of liquid hydrogen density - LhdArray calc_lhd_densities(); - - // Calculate thermal equilibrium densities - EquilibriumArray calc_equilibrium_densities(); - - // Return muonic atom from given atomic mass number - MucfMuonicAtom from_mass_number(AtomicMassNumber mass); - - // Calculate mean fusion cycle times for all reactive muonic molecules - CycleTimesArray calc_cycle_times(ElementView const& element); - - // Calculate mean fusion cycle times for dd muonic molecules - Array calc_dd_cycle(); - - // Calculate mean fusion cycle times for dt muonic molecules - Array calc_dt_cycle(); - - // Calculate mean fusion cycle times for tt muonic molecules - Array calc_tt_cycle(); -}; - -//---------------------------------------------------------------------------// -} // namespace detail -} // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc similarity index 66% rename from src/celeritas/mucf/model/detail/DTMixMaterialCalculator.cc rename to src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index b4da247641..416008db44 100644 --- a/src/celeritas/mucf/model/detail/DTMixMaterialCalculator.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -2,9 +2,9 @@ // Copyright Celeritas contributors: see top-level COPYRIGHT file for details // SPDX-License-Identifier: (Apache-2.0 OR MIT) //---------------------------------------------------------------------------// -//! \file celeritas/mucf/model/detail/DTMixMaterialCalculator.cc +//! \file celeritas/mucf/model/detail/MucfMaterialInserter.cc //---------------------------------------------------------------------------// -#include "DTMixMaterialCalculator.hh" +#include "MucfMaterialInserter.hh" #include "corecel/Assert.hh" @@ -14,26 +14,41 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Construct with material data. + * Construct with \c DTMixMucfModel model data. + */ +MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data) + : mucfmatid_to_matid_(&host_data->mucfmatid_to_matid) + , cycle_times_(&host_data->cycle_times) +{ + CELER_EXPECT(host_data); +} + +//---------------------------------------------------------------------------// +/*! + * Insert material information if applicable. * * Calculates and caches material-dependent properties needed by the * \c DTMixMucfModel . If the material does not contain deuterium and/or - * tritium the object's operator bool will return false. + * tritium the operator will return false. */ -DTMixMaterialCalculator::DTMixMaterialCalculator(MaterialView const& material) - : material_(material) +bool MucfMaterialInserter::operator()(MaterialView const& material) { - for (auto elcompid : range(material_.num_elements())) + LhdArray lhd_densities; + EquilibriumArray eq_densities; + CycleTimesArray cycle_times; + + for (auto elcompid : range(material.num_elements())) { auto const& element_view - = material_.element_record(ElementComponentId{elcompid}); + = material.element_record(ElementComponentId{elcompid}); if (element_view.atomic_number() != AtomicNumber{1}) { // Skip non-hydrogen elements continue; } - has_isotope_ = {false, false}; + // Found hydrogen; Check for d and t isotopes + IsotopeChecker has_isotope{false, false}; for (auto el_comp : range(element_view.num_isotopes())) { auto iso_view @@ -45,17 +60,44 @@ DTMixMaterialCalculator::DTMixMaterialCalculator(MaterialView const& material) continue; } - if (auto const atom = this->from_mass_number(mass); - atom < MucfMuonicAtom::size_) + auto const atom = this->from_mass_number(mass); + if (atom < MucfMuonicAtom::size_) { - // D and/or t isotopes found; calculate properties - has_isotope_[atom] = true; - lhd_densities_ = calc_lhd_densities(); - eq_densities_ = calc_equilibrium_densities(); - cycle_times_ = calc_cycle_times(element_view); + // Mark d or t isotope as found + has_isotope[atom] = true; } } + + if (!has_isotope[MucfMuonicAtom::deuterium] + && !has_isotope[MucfMuonicAtom::tritium]) + { + // No deuterium or tritium found; skip material + return false; + } + + // Calculate and insert material-dependent muCF properties + mucfmatid_to_matid_.push_back(material.material_id()); + cycle_times_.push_back(calc_cycle_times(element_view, has_isotope)); + //! \todo Store mean atom spin flip and transfer times } + return true; +} + +//---------------------------------------------------------------------------// +/*! + * Return \c MucfMuonicAtom from a given atomic mass number. + */ +MucfMuonicAtom MucfMaterialInserter::from_mass_number(AtomicMassNumber mass) +{ + if (mass == AtomicMassNumber{2}) + { + return MucfMuonicAtom::deuterium; + } + if (mass == AtomicMassNumber{3}) + { + return MucfMuonicAtom::tritium; + } + return MucfMuonicAtom::size_; } //---------------------------------------------------------------------------// @@ -64,7 +106,8 @@ DTMixMaterialCalculator::DTMixMaterialCalculator(MaterialView const& material) * * Used during cycle time calculations. */ -DTMixMaterialCalculator::LhdArray DTMixMaterialCalculator::calc_lhd_densities() +MucfMaterialInserter::LhdArray +MucfMaterialInserter::calc_lhd_densities(ElementView const&) { LhdArray result; @@ -80,8 +123,8 @@ DTMixMaterialCalculator::LhdArray DTMixMaterialCalculator::calc_lhd_densities() * * Used during cycle time calculations. */ -DTMixMaterialCalculator::EquilibriumArray -DTMixMaterialCalculator::calc_equilibrium_densities() +MucfMaterialInserter::EquilibriumArray +MucfMaterialInserter::calc_equilibrium_densities(ElementView const&) { EquilibriumArray result; @@ -99,8 +142,9 @@ DTMixMaterialCalculator::calc_equilibrium_densities() * or * - Multiple elements, single isotope each (separate H, d, and t elements). */ -DTMixMaterialCalculator::CycleTimesArray -DTMixMaterialCalculator::calc_cycle_times(ElementView const& element) +MucfMaterialInserter::CycleTimesArray +MucfMaterialInserter::calc_cycle_times(ElementView const& element, + IsotopeChecker const& has_isotope) { CycleTimesArray result; for (auto el_comp : range(element.num_isotopes())) @@ -114,19 +158,19 @@ DTMixMaterialCalculator::calc_cycle_times(ElementView const& element) // Calculate cycle times for dd molecules case MucfMuonicAtom::deuterium: { result[MucfMuonicMolecule::deuterium_deuterium] - = this->calc_dd_cycle(); - if (has_isotope_[MucfMuonicAtom::tritium]) + = this->calc_dd_cycle(element); + if (has_isotope[MucfMuonicAtom::tritium]) { // Calculate cycle times for dt molecules result[MucfMuonicMolecule::deuterium_tritium] - = this->calc_dt_cycle(); + = this->calc_dt_cycle(element); } break; } // Calculate cycle times for tt molecules case MucfMuonicAtom::tritium: { result[MucfMuonicMolecule::tritium_tritium] - = this->calc_tt_cycle(); + = this->calc_tt_cycle(element); break; } default: @@ -143,7 +187,7 @@ DTMixMaterialCalculator::calc_cycle_times(ElementView const& element) * * Cycle times for dd molecules come from F = 0 and F = 1 spin states. */ -Array DTMixMaterialCalculator::calc_dd_cycle() +Array MucfMaterialInserter::calc_dd_cycle(ElementView const&) { Array result; @@ -161,7 +205,7 @@ Array DTMixMaterialCalculator::calc_dd_cycle() * * Cycle times for dt molecules come from F = 1/2 and F = 3/2 spin states. */ -Array DTMixMaterialCalculator::calc_dt_cycle() +Array MucfMaterialInserter::calc_dt_cycle(ElementView const&) { Array result; @@ -179,7 +223,7 @@ Array DTMixMaterialCalculator::calc_dt_cycle() * * Cycle times for tt molecules come only from the F = 1/2 spin state. */ -Array DTMixMaterialCalculator::calc_tt_cycle() +Array MucfMaterialInserter::calc_tt_cycle(ElementView const&) { Array result; @@ -190,23 +234,6 @@ Array DTMixMaterialCalculator::calc_tt_cycle() return result; } -//---------------------------------------------------------------------------// -/*! - * Return \c MucfMuonicAtom from a given atomic mass number. - */ -MucfMuonicAtom DTMixMaterialCalculator::from_mass_number(AtomicMassNumber mass) -{ - if (mass == AtomicMassNumber{2}) - { - return MucfMuonicAtom::deuterium; - } - if (mass == AtomicMassNumber{3}) - { - return MucfMuonicAtom::tritium; - } - return MucfMuonicAtom::size_; -} - //---------------------------------------------------------------------------// } // namespace detail } // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh new file mode 100644 index 0000000000..79d6109541 --- /dev/null +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -0,0 +1,73 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/detail/MucfMaterialInserter.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/data/CollectionBuilder.hh" +#include "celeritas/mat/MaterialView.hh" +#include "celeritas/mucf/Types.hh" +#include "celeritas/mucf/data/DTMixMucfData.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Helper class to calculate and insert muCF material-dependent data into + * \c DTMixMucfData . + */ +class MucfMaterialInserter +{ + public: + // Construct with muCF host data + explicit MucfMaterialInserter(HostVal* host_data); + + // Insert material if it is a valid d-t mixture + bool operator()(MaterialView const& material); + + private: + //// DATA //// + + using CycleTimesArray = EnumArray>; + using LhdArray = EnumArray; + using EquilibriumArray = EnumArray; + using AtomicMassNumber = AtomicNumber; + using MucfIsotope = MucfMuonicAtom; + using IsotopeChecker = EnumArray; + + // DTMixMucfModel host data references populated by operator() + CollectionBuilder mucfmatid_to_matid_; + CollectionBuilder cycle_times_; + + //// HELPER FUNCTIONS //// + + // Return muonic atom from given atomic mass number + MucfMuonicAtom from_mass_number(AtomicMassNumber mass); + + // Calculate dt mixture densities in units of liquid hydrogen density + LhdArray calc_lhd_densities(ElementView const&); + + // Calculate thermal equilibrium densities + EquilibriumArray calc_equilibrium_densities(ElementView const&); + + // Calculate mean fusion cycle times for all reactive muonic molecules + CycleTimesArray calc_cycle_times(ElementView const& element, + IsotopeChecker const& has_isotope); + + // Calculate mean fusion cycle times for dd muonic molecules + Array calc_dd_cycle(ElementView const&); + + // Calculate mean fusion cycle times for dt muonic molecules + Array calc_dt_cycle(ElementView const&); + + // Calculate mean fusion cycle times for tt muonic molecules + Array calc_tt_cycle(ElementView const&); +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas From 31a72c5f0a106248b500020ff3c06e40e59f964c Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Tue, 13 Jan 2026 11:04:39 -0500 Subject: [PATCH 13/35] Minor improvements --- .../mucf/executor/DTMixMucfExecutor.hh | 1 + .../mucf/model/detail/MucfMaterialInserter.cc | 28 +++++++++++-------- .../mucf/model/detail/MucfMaterialInserter.hh | 6 +++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/celeritas/mucf/executor/DTMixMucfExecutor.hh b/src/celeritas/mucf/executor/DTMixMucfExecutor.hh index 6988fb3cdf..030d2c2bf9 100644 --- a/src/celeritas/mucf/executor/DTMixMucfExecutor.hh +++ b/src/celeritas/mucf/executor/DTMixMucfExecutor.hh @@ -74,6 +74,7 @@ DTMixMucfExecutor::operator()(celeritas::CoreTrackView const& track) auto const molecule_spin = select_molecule_spin(rng); // Find muCF material ID from PhysMatId + // Make this a View if ever used beyond this executor auto find = [&](PhysMatId matid) -> MuCfMatId { CELER_EXPECT(matid); for (auto i : range(data.mucfmatid_to_matid.size())) diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index 416008db44..f859297ab9 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -33,10 +33,6 @@ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data) */ bool MucfMaterialInserter::operator()(MaterialView const& material) { - LhdArray lhd_densities; - EquilibriumArray eq_densities; - CycleTimesArray cycle_times; - for (auto elcompid : range(material.num_elements())) { auto const& element_view @@ -75,9 +71,14 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) return false; } - // Calculate and insert material-dependent muCF properties + // Temporary data needed to calculate model data, such as cycle times + lhd_densities_ = this->calc_lhd_densities(element_view); + equilibrium_densities_ = this->calc_equilibrium_densities(element_view); + + // Calculate and insert muCF material data into model data mucfmatid_to_matid_.push_back(material.material_id()); - cycle_times_.push_back(calc_cycle_times(element_view, has_isotope)); + cycle_times_.push_back( + this->calc_cycle_times(element_view, has_isotope)); //! \todo Store mean atom spin flip and transfer times } return true; @@ -187,9 +188,10 @@ MucfMaterialInserter::calc_cycle_times(ElementView const& element, * * Cycle times for dd molecules come from F = 0 and F = 1 spin states. */ -Array MucfMaterialInserter::calc_dd_cycle(ElementView const&) +MucfMaterialInserter::MoleculeCycles +MucfMaterialInserter::calc_dd_cycle(ElementView const&) { - Array result; + MoleculeCycles result; //! \todo Implement @@ -205,9 +207,10 @@ Array MucfMaterialInserter::calc_dd_cycle(ElementView const&) * * Cycle times for dt molecules come from F = 1/2 and F = 3/2 spin states. */ -Array MucfMaterialInserter::calc_dt_cycle(ElementView const&) +MucfMaterialInserter::MoleculeCycles +MucfMaterialInserter::calc_dt_cycle(ElementView const&) { - Array result; + MoleculeCycles result; //! \todo Implement @@ -223,9 +226,10 @@ Array MucfMaterialInserter::calc_dt_cycle(ElementView const&) * * Cycle times for tt molecules come only from the F = 1/2 spin state. */ -Array MucfMaterialInserter::calc_tt_cycle(ElementView const&) +MucfMaterialInserter::MoleculeCycles +MucfMaterialInserter::calc_tt_cycle(ElementView const&) { - Array result; + MoleculeCycles result; //! \todo Implement diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index 79d6109541..ad7c307b9b 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -32,7 +32,8 @@ class MucfMaterialInserter private: //// DATA //// - using CycleTimesArray = EnumArray>; + using MoleculeCycles = Array; + using CycleTimesArray = EnumArray; using LhdArray = EnumArray; using EquilibriumArray = EnumArray; using AtomicMassNumber = AtomicNumber; @@ -42,6 +43,9 @@ class MucfMaterialInserter // DTMixMucfModel host data references populated by operator() CollectionBuilder mucfmatid_to_matid_; CollectionBuilder cycle_times_; + // Temporary quantities needed for calculating the model data + LhdArray lhd_densities_; + EquilibriumArray equilibrium_densities_; //// HELPER FUNCTIONS //// From 38667c506e6be97a57a6c46d2f75802938e6bc15 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Tue, 13 Jan 2026 11:54:28 -0500 Subject: [PATCH 14/35] Update documentation --- doc/implementation/mucf-physics.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/implementation/mucf-physics.rst b/doc/implementation/mucf-physics.rst index c2b1823c7e..3671aa0428 100644 --- a/doc/implementation/mucf-physics.rst +++ b/doc/implementation/mucf-physics.rst @@ -134,9 +134,9 @@ Interactors reserved for sampling final states of the outgoing secondaries. .. doxygenclass:: celeritas::DTMixMucfExecutor Most of the data is material-dependent, and thus can be calculated and cached -during model construction. This is done by the ``DTMixMaterialCalculator``. +during model construction. This is done by the ``MucfMaterialInserter``. -.. doxygenclass:: celeritas::detail::DTMixMaterialCalculator +.. doxygenclass:: celeritas::detail::MucfMaterialInserter .. doxygenclass:: celeritas::DTMixMucfExecutor .. doxygenclass:: celeritas::DDMucfInteractor .. doxygenclass:: celeritas::DTMucfInteractor From 88acf69fc941747e70376ed6b90b6dbe3732354f Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Wed, 14 Jan 2026 10:06:11 -0500 Subject: [PATCH 15/35] Fixup --- doc/CMakeLists.txt | 5 +++++ doc/implementation/mucf-physics.rst | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index eb2d012e88..d8eb1d6382 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -263,6 +263,11 @@ file(GLOB _DOXYGEN_SOURCE "${PROJECT_SOURCE_DIR}/src/celeritas/em/msc/detail/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/em/process/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/em/xs/*.hh" + "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/data/*.hh" + "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/executor/*.hh" + "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/interactor/*.hh" + "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/model/*.hh" + "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/process/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/neutron/interactor/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/neutron/interactor/detail/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/neutron/model/*.hh" diff --git a/doc/implementation/mucf-physics.rst b/doc/implementation/mucf-physics.rst index 3671aa0428..511cba14a4 100644 --- a/doc/implementation/mucf-physics.rst +++ b/doc/implementation/mucf-physics.rst @@ -136,7 +136,6 @@ Interactors reserved for sampling final states of the outgoing secondaries. Most of the data is material-dependent, and thus can be calculated and cached during model construction. This is done by the ``MucfMaterialInserter``. -.. doxygenclass:: celeritas::detail::MucfMaterialInserter .. doxygenclass:: celeritas::DTMixMucfExecutor .. doxygenclass:: celeritas::DDMucfInteractor .. doxygenclass:: celeritas::DTMucfInteractor From 39b329eaf344aaa0a1dfb10cf1b2353a87aa4619 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Wed, 14 Jan 2026 15:48:35 -0500 Subject: [PATCH 16/35] Improve documentation --- doc/CMakeLists.txt | 1 + doc/implementation/mucf-physics.rst | 43 ++++++++++--------- .../mucf/interactor/DDMucfInteractor.hh | 5 ++- .../mucf/interactor/DTMucfInteractor.hh | 4 +- .../mucf/interactor/TTMucfInteractor.hh | 4 +- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index d8eb1d6382..ad297309b8 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -267,6 +267,7 @@ file(GLOB _DOXYGEN_SOURCE "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/executor/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/interactor/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/model/*.hh" + "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/model/detail/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/mucf/process/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/neutron/interactor/*.hh" "${PROJECT_SOURCE_DIR}/src/celeritas/neutron/interactor/detail/*.hh" diff --git a/doc/implementation/mucf-physics.rst b/doc/implementation/mucf-physics.rst index 511cba14a4..75e13de3bf 100644 --- a/doc/implementation/mucf-physics.rst +++ b/doc/implementation/mucf-physics.rst @@ -72,8 +72,8 @@ sticking factor and the fusion cycle time are the main conditions that define how many fusion cycles a muon can undergo. The fusion cycle time depends on the d-t mixture, its temperature, and on the final spin of the molecule. Only muonic molecules where the total spin :math:`F = I_N \pm 1/2` is on, or has a -projection onto the total angular momentum J = 1 are reactive. The spin states -of the three possible muonic molecules are summarized in table +projection onto the total angular momentum :math:`J = 1` are reactive. The spin +states of the three possible muonic molecules are summarized in table :numref:`muon_spin_states`. @@ -108,35 +108,36 @@ enabling the ``mucf_physics`` option in Geant4 integration ------------------ -For integration interfaces, if ``mucf_physics`` option in -:cpp:class:`celeritas::ext::GeantPhysicsOptions` is enabled, the muon-catalyzed -fusion data is constructed when the ``G4MuonMinusAtomicCapture`` process is -registered. - -.. todo:: Add process/model/executor details +For integration interfaces, enabling the ``mucf_physics`` option in +:cpp:class:`celeritas::ext::GeantPhysicsOptions` will check if the +``G4MuonMinusAtomicCapture`` process is registered in the Geant4's Physics List. +If the process is present, the :cpp:class:`celeritas::inp::MucfPhysics` will be +populated, and the :cpp:class:`celeritas::MucfProcess` will be initialized. Code implementation =================== -The ``MucfProcess`` process only has the model ``DTMixMucfModel`` attached to -it, responsible for deuterium-tritium mixtures. It can simulate materials from -near absolute zero to 1500 kelvin, and it is an *at rest* model that encompasses -the full cycle---atom formation, molecule formation, and fusion. - -.. note:: Only reactive channels are implemented. +The :cpp:class:`celeritas::MucfProcess` process has only the +:cpp:class:`celeritas::DTMixMucfModel` attached to it, responsible for +deuterium-tritium mixtures. It can simulate materials from near absolute zero to +1500 kelvin. It is an *at rest* model that encompasses the full cycle---atom +formation, molecule formation, and fusion. .. doxygenclass:: celeritas::MucfProcess +.. doxygenclass:: celeritas::DTMixMucfModel -The main cycle is managed by the ``DTMixMucfExecutor``, with the -Interactors reserved for sampling final states of the outgoing secondaries. +Most of the data is material-dependent, being calculated and cached during model +construction. All of the cached quantities are calculated and added to +host/device data via :cpp:class:`celeritas::detail::MucfMaterialInserter`. -.. doxygenclass:: celeritas::DTMixMucfModel -.. doxygenclass:: celeritas::DTMixMucfExecutor +.. doxygenclass:: celeritas::detail::MucfMaterialInserter + +The main cycle is managed by the model's +:cpp:class:`celeritas::DTMixMucfExecutor`, with the Interactors reserved for +sampling final states of the outgoing secondaries. -Most of the data is material-dependent, and thus can be calculated and cached -during model construction. This is done by the ``MucfMaterialInserter``. +.. note:: Only reactive channels are implemented. -.. doxygenclass:: celeritas::DTMixMucfExecutor .. doxygenclass:: celeritas::DDMucfInteractor .. doxygenclass:: celeritas::DTMucfInteractor .. doxygenclass:: celeritas::TTMucfInteractor diff --git a/src/celeritas/mucf/interactor/DDMucfInteractor.hh b/src/celeritas/mucf/interactor/DDMucfInteractor.hh index d5686b3008..1f074097b6 100644 --- a/src/celeritas/mucf/interactor/DDMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DDMucfInteractor.hh @@ -21,7 +21,10 @@ namespace celeritas /*! * Muon-catalyzed fusion of \f$ (dd)_\mu \f$ molecules. * - * This is an \em at-rest interaction. + * Fusion channels: + * - \f$ ^3\text{He} + \mu + n \f$ + * - \f$ (^3\text{He})_\mu + n \f$ + * - \f$ ^3\text{H} + \mu + p \f$ */ class DDMucfInteractor { diff --git a/src/celeritas/mucf/interactor/DTMucfInteractor.hh b/src/celeritas/mucf/interactor/DTMucfInteractor.hh index 93a981811f..32b413b54d 100644 --- a/src/celeritas/mucf/interactor/DTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DTMucfInteractor.hh @@ -21,7 +21,9 @@ namespace celeritas /*! * Muon-catalyzed fusion of \f$ (dt)_\mu \f$ molecules. * - * This is an \em at-rest interaction. + * Fusion channels: + * - \f$ \alpha + \mu + n \f$ + * - \f$ (\alpha)_\mu + n \f$ */ class DTMucfInteractor { diff --git a/src/celeritas/mucf/interactor/TTMucfInteractor.hh b/src/celeritas/mucf/interactor/TTMucfInteractor.hh index 30588024c2..0532c1b5bf 100644 --- a/src/celeritas/mucf/interactor/TTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/TTMucfInteractor.hh @@ -21,7 +21,9 @@ namespace celeritas /*! * Muon-catalyzed fusion of \f$ (tt)_\mu \f$ molecules. * - * This is an \em at-rest interaction. + * Fusion channels: + * - \f$ \alpha + \mu + n + n \f$ + * - \f$ (\alpha)_\mu + n + n \f$ */ class TTMucfInteractor { From f7347639ff3dd4a3b7a1ca0b10ed08befd6810fd Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Wed, 14 Jan 2026 16:20:23 -0500 Subject: [PATCH 17/35] Fix `RootInterfaceLinkDef` --- src/celeritas/ext/RootInterfaceLinkDef.h | 8 ++++++-- .../data/four-steel-slabs.root-dump.json | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/celeritas/ext/RootInterfaceLinkDef.h b/src/celeritas/ext/RootInterfaceLinkDef.h index 3e1ac8f498..0425bc3110 100644 --- a/src/celeritas/ext/RootInterfaceLinkDef.h +++ b/src/celeritas/ext/RootInterfaceLinkDef.h @@ -51,10 +51,11 @@ #pragma link C++ class celeritas::inp::Grid+; #pragma link C++ class celeritas::inp::GridReflection+; #pragma link C++ class celeritas::inp::Interpolation+; -#pragma link C++ class celeritas::inp::MucfPhysics+; -#pragma link C++ class celeritas::inp::MucfCycleRate+; #pragma link C++ class celeritas::inp::MucfAtomTransferRate+; #pragma link C++ class celeritas::inp::MucfAtomSpinFlipRate+; +#pragma link C++ class celeritas::inp::MucfCycleRate+; +#pragma link C++ class celeritas::inp::MucfPhysics+; +#pragma link C++ class celeritas::inp::MucfScalars+; #pragma link C++ class celeritas::inp::MuPairProductionEnergyTransferTable+; #pragma link C++ class celeritas::inp::NoRoughness+; #pragma link C++ class celeritas::inp::OpticalPhysics+; @@ -82,6 +83,9 @@ // Quantities #pragma link C++ class celeritas::Quantity+; #pragma link C++ class celeritas::Quantity+; +#pragma link C++ class celeritas::Quantity+; +#pragma link C++ class celeritas::Quantity+; + // Event data used by Geant4/Celeritas offloading applications #pragma link C++ class celeritas::EventData+; diff --git a/test/celeritas/data/four-steel-slabs.root-dump.json b/test/celeritas/data/four-steel-slabs.root-dump.json index 22948401fa..cba069f76c 100644 --- a/test/celeritas/data/four-steel-slabs.root-dump.json +++ b/test/celeritas/data/four-steel-slabs.root-dump.json @@ -569,6 +569,25 @@ }, "mucf_physics" : { "_typename" : "celeritas::inp::MucfPhysics", + "scalars" : { + "_typename" : "celeritas::inp::MucfScalars", + "protium" : { + "_typename" : "celeritas::Quantity", + "value_" : 0 + }, + "deuterium" : { + "_typename" : "celeritas::Quantity", + "value_" : 0 + }, + "tritium" : { + "_typename" : "celeritas::Quantity", + "value_" : 0 + }, + "liquid_hydrogen_density" : { + "_typename" : "celeritas::Quantity", + "value_" : 0 + } + }, "muon_energy_cdf" : { "_typename" : "celeritas::inp::Grid", "x" : [], From cea2b35d34e05ec475ddc6309d84b946155ece6c Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Thu, 15 Jan 2026 15:52:51 -0500 Subject: [PATCH 18/35] Address comments: - Fix integral_xs bool - Fwd declare material and particle params - Remove trailing includes --- src/celeritas/mucf/model/DTMixMucfModel.cc | 2 ++ src/celeritas/mucf/model/DTMixMucfModel.hh | 7 +++---- src/celeritas/mucf/process/MucfProcess.cc | 2 ++ src/celeritas/mucf/process/MucfProcess.hh | 7 ++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc index 0ad0e84372..d4cee2333a 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.cc +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -14,9 +14,11 @@ #include "celeritas/global/TrackExecutor.hh" #include "celeritas/grid/NonuniformGridBuilder.hh" #include "celeritas/inp/MucfPhysics.hh" +#include "celeritas/mat/MaterialParams.hh" #include "celeritas/mucf/executor/DTMixMucfExecutor.hh" // IWYU pragma: associated #include "celeritas/phys/InteractionApplier.hh" // IWYU pragma: associated #include "celeritas/phys/PDGNumber.hh" +#include "celeritas/phys/ParticleParams.hh" #include "detail/MucfMaterialInserter.hh" diff --git a/src/celeritas/mucf/model/DTMixMucfModel.hh b/src/celeritas/mucf/model/DTMixMucfModel.hh index d6cd37e531..893d6c2e5e 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.hh +++ b/src/celeritas/mucf/model/DTMixMucfModel.hh @@ -6,15 +6,14 @@ //---------------------------------------------------------------------------// #pragma once -#include "celeritas/mat/MaterialParams.hh" #include "celeritas/mucf/data/DTMixMucfData.hh" -#include "celeritas/mucf/executor/DTMixMucfExecutor.hh" // IWYU pragma: associated -#include "celeritas/phys/InteractionApplier.hh" // IWYU pragma: associated #include "celeritas/phys/Model.hh" -#include "celeritas/phys/ParticleParams.hh" namespace celeritas { +class MaterialParams; +class ParticleParams; + //---------------------------------------------------------------------------// /*! * Muon-catalyzed fusion model for dd, dt, and tt molecules. diff --git a/src/celeritas/mucf/process/MucfProcess.cc b/src/celeritas/mucf/process/MucfProcess.cc index b3b3dc5d8c..cb2947e0a6 100644 --- a/src/celeritas/mucf/process/MucfProcess.cc +++ b/src/celeritas/mucf/process/MucfProcess.cc @@ -8,8 +8,10 @@ #include +#include "celeritas/mat/MaterialParams.hh" #include "celeritas/mucf/model/DTMixMucfModel.hh" #include "celeritas/phys/Model.hh" +#include "celeritas/phys/ParticleParams.hh" namespace celeritas { diff --git a/src/celeritas/mucf/process/MucfProcess.hh b/src/celeritas/mucf/process/MucfProcess.hh index 565b4c590c..23949c2acc 100644 --- a/src/celeritas/mucf/process/MucfProcess.hh +++ b/src/celeritas/mucf/process/MucfProcess.hh @@ -7,12 +7,13 @@ #pragma once #include "celeritas/Types.hh" -#include "celeritas/mat/MaterialParams.hh" -#include "celeritas/phys/ParticleParams.hh" #include "celeritas/phys/Process.hh" namespace celeritas { +class MaterialParams; +class ParticleParams; + //---------------------------------------------------------------------------// /*! * Muon-catalyzed fusion of muonic dd, dt, or tt molecules. @@ -43,7 +44,7 @@ class MucfProcess final : public Process EnergyLossGrid energy_loss(Applicability) const final; //! Whether the integral method can be used to sample interaction length - bool supports_integral_xs() const final { return true; } //! \todo Check + bool supports_integral_xs() const final { return false; } //! Whether the process applies when the particle is stopped bool applies_at_rest() const final { return true; } From 97cf7297a97eb75124cfaaf2b269e0399c43baa9 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Fri, 16 Jan 2026 09:32:58 -0500 Subject: [PATCH 19/35] Fix missing CELER_FUNCTION --- src/celeritas/mucf/executor/detail/DDChannelSelector.hh | 7 +++---- src/celeritas/mucf/executor/detail/DTChannelSelector.hh | 7 +++---- .../mucf/executor/detail/DTMixMuonicAtomSelector.hh | 6 +++--- .../mucf/executor/detail/DTMixMuonicMoleculeSelector.hh | 5 +++-- .../mucf/executor/detail/MuonicAtomSpinSelector.hh | 1 + .../mucf/executor/detail/MuonicMoleculeSpinSelector.hh | 2 +- src/celeritas/mucf/executor/detail/TTChannelSelector.hh | 7 +++---- src/celeritas/mucf/interactor/DDMucfInteractor.hh | 1 + src/celeritas/mucf/interactor/DTMucfInteractor.hh | 1 + src/celeritas/mucf/interactor/TTMucfInteractor.hh | 3 ++- 10 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/celeritas/mucf/executor/detail/DDChannelSelector.hh b/src/celeritas/mucf/executor/detail/DDChannelSelector.hh index f2793b0979..05394c8019 100644 --- a/src/celeritas/mucf/executor/detail/DDChannelSelector.hh +++ b/src/celeritas/mucf/executor/detail/DDChannelSelector.hh @@ -29,7 +29,7 @@ class DDChannelSelector public: //! Construct with args; \todo Update documentation - inline DDChannelSelector(/* args */); + inline CELER_FUNCTION DDChannelSelector(/* args */); // Select fusion channel to be used by the interactor template @@ -44,7 +44,7 @@ class DDChannelSelector * * \todo Update documentation */ -DDChannelSelector::DDChannelSelector(/* args */) +CELER_FUNCTION DDChannelSelector::DDChannelSelector(/* args */) { CELER_NOT_IMPLEMENTED("Mucf dd fusion channel selection"); } @@ -56,8 +56,7 @@ DDChannelSelector::DDChannelSelector(/* args */) * \sa celeritas::DDMucfInteractor */ template -inline CELER_FUNCTION DDChannelSelector::Channel -DDChannelSelector::operator()(Engine&) +CELER_FUNCTION DDChannelSelector::Channel DDChannelSelector::operator()(Engine&) { Channel result{Channel::size_}; diff --git a/src/celeritas/mucf/executor/detail/DTChannelSelector.hh b/src/celeritas/mucf/executor/detail/DTChannelSelector.hh index 418571083a..47e097cdfd 100644 --- a/src/celeritas/mucf/executor/detail/DTChannelSelector.hh +++ b/src/celeritas/mucf/executor/detail/DTChannelSelector.hh @@ -29,7 +29,7 @@ class DTChannelSelector public: //! Construct with args; \todo Update documentation - inline DTChannelSelector(/* args */); + inline CELER_FUNCTION DTChannelSelector(/* args */); // Select fusion channel to be used by the interactor template @@ -44,7 +44,7 @@ class DTChannelSelector * * \todo Update documentation */ -DTChannelSelector::DTChannelSelector(/* args */) +CELER_FUNCTION DTChannelSelector::DTChannelSelector(/* args */) { CELER_NOT_IMPLEMENTED("Mucf dt fusion channel selection"); } @@ -56,8 +56,7 @@ DTChannelSelector::DTChannelSelector(/* args */) * \sa celeritas::DTMucfInteractor */ template -inline CELER_FUNCTION DTChannelSelector::Channel -DTChannelSelector::operator()(Engine&) +CELER_FUNCTION DTChannelSelector::Channel DTChannelSelector::operator()(Engine&) { Channel result{Channel::size_}; diff --git a/src/celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh b/src/celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh index a371141417..f193109e0c 100644 --- a/src/celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh +++ b/src/celeritas/mucf/executor/detail/DTMixMuonicAtomSelector.hh @@ -21,7 +21,7 @@ class DTMixMuonicAtomSelector { public: //! Construct with args; \todo Update documentation - inline DTMixMuonicAtomSelector(/* args */); + inline CELER_FUNCTION DTMixMuonicAtomSelector(/* args */); // Select muonic atom template @@ -36,7 +36,7 @@ class DTMixMuonicAtomSelector * \todo Update documentation */ -DTMixMuonicAtomSelector::DTMixMuonicAtomSelector(/* args */) +CELER_FUNCTION DTMixMuonicAtomSelector::DTMixMuonicAtomSelector(/* args */) { CELER_NOT_IMPLEMENTED("Mucf muonic atom selection"); } @@ -46,7 +46,7 @@ DTMixMuonicAtomSelector::DTMixMuonicAtomSelector(/* args */) * Return selected muonic atom. */ template -inline CELER_FUNCTION MucfMuonicAtom DTMixMuonicAtomSelector::operator()(Engine&) +CELER_FUNCTION MucfMuonicAtom DTMixMuonicAtomSelector::operator()(Engine&) { MucfMuonicAtom result{MucfMuonicAtom::size_}; diff --git a/src/celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh b/src/celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh index 6558191a00..cc4c7bd30b 100644 --- a/src/celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh +++ b/src/celeritas/mucf/executor/detail/DTMixMuonicMoleculeSelector.hh @@ -24,7 +24,7 @@ class DTMixMuonicMoleculeSelector { public: //! Construct with args; \todo Update documentation - inline DTMixMuonicMoleculeSelector(/* args */); + inline CELER_FUNCTION DTMixMuonicMoleculeSelector(/* args */); // Select muonic molecule template @@ -39,6 +39,7 @@ class DTMixMuonicMoleculeSelector * * \todo Update documentation */ +CELER_FUNCTION DTMixMuonicMoleculeSelector::DTMixMuonicMoleculeSelector(/* args */) { //! \todo Implement @@ -50,7 +51,7 @@ DTMixMuonicMoleculeSelector::DTMixMuonicMoleculeSelector(/* args */) * Return selected muonic molecule. */ template -inline CELER_FUNCTION MucfMuonicMolecule +CELER_FUNCTION MucfMuonicMolecule DTMixMuonicMoleculeSelector::operator()(Engine&) { MucfMuonicMolecule result{MucfMuonicMolecule::size_}; diff --git a/src/celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh b/src/celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh index 853950c4bd..15ff2c7fe4 100644 --- a/src/celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh +++ b/src/celeritas/mucf/executor/detail/MuonicAtomSpinSelector.hh @@ -40,6 +40,7 @@ class MuonicAtomSpinSelector /*! * Construct with muonic atom. */ +CELER_FUNCTION MuonicAtomSpinSelector::MuonicAtomSpinSelector(MucfMuonicAtom atom) : atom_(atom) { diff --git a/src/celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh b/src/celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh index 51ddedb741..ee4b5777bd 100644 --- a/src/celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh +++ b/src/celeritas/mucf/executor/detail/MuonicMoleculeSpinSelector.hh @@ -40,7 +40,7 @@ class MuonicMoleculeSpinSelector /*! * Construct with muonic molecule. */ -MuonicMoleculeSpinSelector::MuonicMoleculeSpinSelector( +CELER_FUNCTION MuonicMoleculeSpinSelector::MuonicMoleculeSpinSelector( MucfMuonicMolecule molecule) : molecule_(molecule) { diff --git a/src/celeritas/mucf/executor/detail/TTChannelSelector.hh b/src/celeritas/mucf/executor/detail/TTChannelSelector.hh index d69bab7bfd..1f7c0b40ea 100644 --- a/src/celeritas/mucf/executor/detail/TTChannelSelector.hh +++ b/src/celeritas/mucf/executor/detail/TTChannelSelector.hh @@ -29,7 +29,7 @@ class TTChannelSelector public: //! Construct with args; \todo Update documentation - inline TTChannelSelector(/* args */); + inline CELER_FUNCTION TTChannelSelector(/* args */); // Select fusion channel to be used by the interactor template @@ -44,7 +44,7 @@ class TTChannelSelector * * \todo Update documentation */ -TTChannelSelector::TTChannelSelector(/* args */) +CELER_FUNCTION TTChannelSelector::TTChannelSelector(/* args */) { //! \todo Implement CELER_NOT_IMPLEMENTED("Mucf tt fusion channel selection"); @@ -57,8 +57,7 @@ TTChannelSelector::TTChannelSelector(/* args */) * \sa celeritas::TTMucfInteractor */ template -inline CELER_FUNCTION TTChannelSelector::Channel -TTChannelSelector::operator()(Engine&) +CELER_FUNCTION TTChannelSelector::Channel TTChannelSelector::operator()(Engine&) { Channel result{Channel::size_}; diff --git a/src/celeritas/mucf/interactor/DDMucfInteractor.hh b/src/celeritas/mucf/interactor/DDMucfInteractor.hh index 1f074097b6..578e6f8b66 100644 --- a/src/celeritas/mucf/interactor/DDMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DDMucfInteractor.hh @@ -74,6 +74,7 @@ class DDMucfInteractor /*! * Construct with shared and state data. */ +CELER_FUNCTION DDMucfInteractor::DDMucfInteractor(NativeCRef const& data, Channel const channel, StackAllocator& allocate) diff --git a/src/celeritas/mucf/interactor/DTMucfInteractor.hh b/src/celeritas/mucf/interactor/DTMucfInteractor.hh index 32b413b54d..87de422df8 100644 --- a/src/celeritas/mucf/interactor/DTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DTMucfInteractor.hh @@ -70,6 +70,7 @@ class DTMucfInteractor /*! * Construct with shared and state data. */ +CELER_FUNCTION DTMucfInteractor::DTMucfInteractor(NativeCRef const& data, Channel const channel, StackAllocator& allocate) diff --git a/src/celeritas/mucf/interactor/TTMucfInteractor.hh b/src/celeritas/mucf/interactor/TTMucfInteractor.hh index 0532c1b5bf..a9d0a03121 100644 --- a/src/celeritas/mucf/interactor/TTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/TTMucfInteractor.hh @@ -70,6 +70,7 @@ class TTMucfInteractor /*! * Construct with shared and state data. */ +CELER_FUNCTION TTMucfInteractor::TTMucfInteractor(NativeCRef const& data, Channel const channel, StackAllocator& allocate) @@ -81,7 +82,7 @@ TTMucfInteractor::TTMucfInteractor(NativeCRef const& data, //---------------------------------------------------------------------------// /*! - * Sample a dt muonic molecule fusion. + * Sample a tt muonic molecule fusion. */ template CELER_FUNCTION Interaction TTMucfInteractor::operator()(Engine& rng) From d3b635478f54b78675ff4ec33519f8fb08d10eac Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 19 Jan 2026 14:54:59 -0500 Subject: [PATCH 20/35] Fix device data --- src/celeritas/mucf/interactor/DDMucfInteractor.hh | 2 +- src/celeritas/mucf/interactor/DTMucfInteractor.hh | 2 +- src/celeritas/mucf/interactor/TTMucfInteractor.hh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/celeritas/mucf/interactor/DDMucfInteractor.hh b/src/celeritas/mucf/interactor/DDMucfInteractor.hh index 578e6f8b66..3bafdd7163 100644 --- a/src/celeritas/mucf/interactor/DDMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DDMucfInteractor.hh @@ -56,7 +56,7 @@ class DDMucfInteractor // Allocate space for secondary particles StackAllocator& allocate_; // Number of secondaries per channel - static constexpr EnumArray num_secondaries_{ + EnumArray num_secondaries_{ 3, // helium3_muon_neutron 2, // muonichelium3_neutron 3 // hydrogen3_muon_proton diff --git a/src/celeritas/mucf/interactor/DTMucfInteractor.hh b/src/celeritas/mucf/interactor/DTMucfInteractor.hh index 87de422df8..06d02c85c7 100644 --- a/src/celeritas/mucf/interactor/DTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/DTMucfInteractor.hh @@ -53,7 +53,7 @@ class DTMucfInteractor // Allocate space for secondary particles StackAllocator& allocate_; // Number of secondaries per channel - static constexpr EnumArray num_secondaries_{ + EnumArray num_secondaries_{ 3, // alpha_muon_neutron 2 // muonicalpha_neutron }; diff --git a/src/celeritas/mucf/interactor/TTMucfInteractor.hh b/src/celeritas/mucf/interactor/TTMucfInteractor.hh index a9d0a03121..a44a078875 100644 --- a/src/celeritas/mucf/interactor/TTMucfInteractor.hh +++ b/src/celeritas/mucf/interactor/TTMucfInteractor.hh @@ -53,7 +53,7 @@ class TTMucfInteractor // Allocate space for secondary particles StackAllocator& allocate_; // Number of secondaries per channel - static constexpr EnumArray num_secondaries_{ + EnumArray num_secondaries_{ 4, // alpha_muon_neutron_neutron 3 // muonicalpha_neutron_neutron }; From 383797736f688ab3159f1ab71338c00d0b359802 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Tue, 20 Jan 2026 14:12:28 -0500 Subject: [PATCH 21/35] Cache LHD densities --- src/celeritas/inp/MucfPhysics.hh | 21 ++++-- src/celeritas/mucf/Types.hh | 13 ++++ src/celeritas/mucf/model/DTMixMucfModel.cc | 2 +- .../mucf/model/detail/MucfMaterialInserter.cc | 68 ++++++++----------- .../mucf/model/detail/MucfMaterialInserter.hh | 13 ++-- 5 files changed, 62 insertions(+), 55 deletions(-) diff --git a/src/celeritas/inp/MucfPhysics.hh b/src/celeritas/inp/MucfPhysics.hh index 820f4fa358..6b344806e2 100644 --- a/src/celeritas/inp/MucfPhysics.hh +++ b/src/celeritas/inp/MucfPhysics.hh @@ -26,18 +26,27 @@ namespace inp struct MucfScalars { // Atomic masses - units::AmuMass protium; //!< Protium atomic mass [AMU] - units::AmuMass deuterium; //!< Deuterium atomic mass [AMU] - units::AmuMass tritium; //!< Tritium atomic mass [AMU] + units::AmuMass protium_mass; //!< Protium atomic mass [AMU] + units::AmuMass deuterium_mass; //!< Deuterium atomic mass [AMU] + units::AmuMass tritium_mass; //!< Tritium atomic mass [AMU] units::InvCcDensity liquid_hydrogen_density; //!< LHD unit [1/cm^3] + //! Whether the data are assigned + explicit operator bool() const + { + return protium_mass > zero_quantity() + && deuterium_mass > zero_quantity() + && tritium_mass > zero_quantity() + && liquid_hydrogen_density > zero_quantity(); + } + //! Initialize with hardcoded values static MucfScalars from_default() { MucfScalars result; - result.protium = units::AmuMass{1.007825031898}; - result.deuterium = units::AmuMass{2.014101777844}; - result.tritium = units::AmuMass{3.016049281320}; + result.protium_mass = units::AmuMass{1.007825031898}; + result.deuterium_mass = units::AmuMass{2.014101777844}; + result.tritium_mass = units::AmuMass{3.016049281320}; result.liquid_hydrogen_density = units::InvCcDensity{4.25e22}; return result; } diff --git a/src/celeritas/mucf/Types.hh b/src/celeritas/mucf/Types.hh index 0a7c410dda..1fd0d46d3f 100644 --- a/src/celeritas/mucf/Types.hh +++ b/src/celeritas/mucf/Types.hh @@ -12,6 +12,19 @@ namespace celeritas { //---------------------------------------------------------------------------// // ENUMERATIONS +//---------------------------------------------------------------------------// +/*! + * Muonic atom selection from material data. This is *not* intended to be used + * by the transport loop. + */ +enum class MucfIsotope +{ + protium, + deuterium, + tritium, + size_ +}; + //---------------------------------------------------------------------------// /*! * Muonic atom selection from material data. This is *not* intended to be used diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc index d4cee2333a..53c1079786 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.cc +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -113,7 +113,7 @@ DTMixMucfModel::DTMixMucfModel(ActionId id, host_data.muon_energy_cdf = build_grid_record(inp_data.muon_energy_cdf); // Calculate and cache quantities for all materials with dt mixtures - detail::MucfMaterialInserter insert(&host_data); + detail::MucfMaterialInserter insert(&host_data, inp_data.scalars); for (auto const& matid : range(materials.num_materials())) { auto const& mat_view = materials.get(PhysMatId{matid}); diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index f859297ab9..5011f7f92b 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -16,11 +16,13 @@ namespace detail /*! * Construct with \c DTMixMucfModel model data. */ -MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data) +MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, + inp::MucfScalars const& scalars_) : mucfmatid_to_matid_(&host_data->mucfmatid_to_matid) , cycle_times_(&host_data->cycle_times) + , scalars_(scalars_) { - CELER_EXPECT(host_data); + CELER_EXPECT(scalars_); } //---------------------------------------------------------------------------// @@ -33,6 +35,8 @@ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data) */ bool MucfMaterialInserter::operator()(MaterialView const& material) { + auto const mat_num_density = material.number_density(); + for (auto elcompid : range(material.num_elements())) { auto const& element_view @@ -45,34 +49,28 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) // Found hydrogen; Check for d and t isotopes IsotopeChecker has_isotope{false, false}; + auto const elem_rel_abundance = material.elements()[elcompid].fraction; for (auto el_comp : range(element_view.num_isotopes())) { auto iso_view = element_view.isotope_record(IsotopeComponentId{el_comp}); - auto mass = iso_view.atomic_mass_number(); - if (mass == AtomicMassNumber{1}) - { - // Skip protium - continue; - } + auto const atom + = this->from_mass_number(iso_view.atomic_mass_number()); - auto const atom = this->from_mass_number(mass); - if (atom < MucfMuonicAtom::size_) - { - // Mark d or t isotope as found - has_isotope[atom] = true; - } + CELER_ASSERT(atom < MucfIsotope::size_); + has_isotope[atom] = true; // D or t isotope found + lhd_densities_[atom] = elem_rel_abundance * mat_num_density + / scalars_.liquid_hydrogen_density.value(); } - if (!has_isotope[MucfMuonicAtom::deuterium] - && !has_isotope[MucfMuonicAtom::tritium]) + if (!has_isotope[MucfIsotope::deuterium] + && !has_isotope[MucfIsotope::tritium]) { // No deuterium or tritium found; skip material return false; } // Temporary data needed to calculate model data, such as cycle times - lhd_densities_ = this->calc_lhd_densities(element_view); equilibrium_densities_ = this->calc_equilibrium_densities(element_view); // Calculate and insert muCF material data into model data @@ -86,35 +84,23 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) //---------------------------------------------------------------------------// /*! - * Return \c MucfMuonicAtom from a given atomic mass number. + * Return \c MucfIsotope from a given atomic mass number. */ -MucfMuonicAtom MucfMaterialInserter::from_mass_number(AtomicMassNumber mass) +MucfIsotope MucfMaterialInserter::from_mass_number(AtomicMassNumber mass) { + if (mass == AtomicMassNumber{1}) + { + return MucfIsotope::protium; + } if (mass == AtomicMassNumber{2}) { - return MucfMuonicAtom::deuterium; + return MucfIsotope::deuterium; } if (mass == AtomicMassNumber{3}) { - return MucfMuonicAtom::tritium; + return MucfIsotope::tritium; } - return MucfMuonicAtom::size_; -} - -//---------------------------------------------------------------------------// -/*! - * Convert dt mixture densities to units of liquid hydrogen density. - * - * Used during cycle time calculations. - */ -MucfMaterialInserter::LhdArray -MucfMaterialInserter::calc_lhd_densities(ElementView const&) -{ - LhdArray result; - - //! \todo Implement - - return result; + return MucfIsotope::size_; } //---------------------------------------------------------------------------// @@ -157,10 +143,10 @@ MucfMaterialInserter::calc_cycle_times(ElementView const& element, switch (atom) { // Calculate cycle times for dd molecules - case MucfMuonicAtom::deuterium: { + case MucfIsotope::deuterium: { result[MucfMuonicMolecule::deuterium_deuterium] = this->calc_dd_cycle(element); - if (has_isotope[MucfMuonicAtom::tritium]) + if (has_isotope[MucfIsotope::tritium]) { // Calculate cycle times for dt molecules result[MucfMuonicMolecule::deuterium_tritium] @@ -169,7 +155,7 @@ MucfMaterialInserter::calc_cycle_times(ElementView const& element, break; } // Calculate cycle times for tt molecules - case MucfMuonicAtom::tritium: { + case MucfIsotope::tritium: { result[MucfMuonicMolecule::tritium_tritium] = this->calc_tt_cycle(element); break; diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index ad7c307b9b..561ddbdc94 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -7,6 +7,7 @@ #pragma once #include "corecel/data/CollectionBuilder.hh" +#include "celeritas/inp/MucfPhysics.hh" #include "celeritas/mat/MaterialView.hh" #include "celeritas/mucf/Types.hh" #include "celeritas/mucf/data/DTMixMucfData.hh" @@ -24,7 +25,8 @@ class MucfMaterialInserter { public: // Construct with muCF host data - explicit MucfMaterialInserter(HostVal* host_data); + explicit MucfMaterialInserter(HostVal* host_data, + inp::MucfScalars const& scalars); // Insert material if it is a valid d-t mixture bool operator()(MaterialView const& material); @@ -34,26 +36,23 @@ class MucfMaterialInserter using MoleculeCycles = Array; using CycleTimesArray = EnumArray; - using LhdArray = EnumArray; + using LhdArray = EnumArray; using EquilibriumArray = EnumArray; using AtomicMassNumber = AtomicNumber; - using MucfIsotope = MucfMuonicAtom; using IsotopeChecker = EnumArray; // DTMixMucfModel host data references populated by operator() CollectionBuilder mucfmatid_to_matid_; CollectionBuilder cycle_times_; // Temporary quantities needed for calculating the model data + inp::MucfScalars scalars_; LhdArray lhd_densities_; EquilibriumArray equilibrium_densities_; //// HELPER FUNCTIONS //// // Return muonic atom from given atomic mass number - MucfMuonicAtom from_mass_number(AtomicMassNumber mass); - - // Calculate dt mixture densities in units of liquid hydrogen density - LhdArray calc_lhd_densities(ElementView const&); + MucfIsotope from_mass_number(AtomicMassNumber mass); // Calculate thermal equilibrium densities EquilibriumArray calc_equilibrium_densities(ElementView const&); From 6aa9cc4f7c31f247c132ee8f13ae883a62bbba55 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 2 Feb 2026 17:35:01 -0500 Subject: [PATCH 22/35] Add calculator helper class to find hdt equilibrium densities --- src/celeritas/CMakeLists.txt | 1 + src/celeritas/mucf/Types.hh | 1 + .../detail/EquilibrateDensitiesCalculator.cc | 241 ++++++++++++++++++ .../detail/EquilibrateDensitiesCalculator.hh | 80 ++++++ .../mucf/model/detail/MucfMaterialInserter.cc | 63 ++--- .../mucf/model/detail/MucfMaterialInserter.hh | 16 +- 6 files changed, 366 insertions(+), 36 deletions(-) create mode 100644 src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc create mode 100644 src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 04dec8e4e3..587f4529e4 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -100,6 +100,7 @@ list(APPEND SOURCES mat/MaterialParams.cc mat/MaterialParamsOutput.cc mat/detail/Utils.cc + mucf/model/detail/EquilibrateDensitiesCalculator.cc mucf/model/detail/MucfMaterialInserter.cc mucf/process/MucfProcess.cc neutron/model/CascadeOptions.cc diff --git a/src/celeritas/mucf/Types.hh b/src/celeritas/mucf/Types.hh index 1fd0d46d3f..6580ea7ce5 100644 --- a/src/celeritas/mucf/Types.hh +++ b/src/celeritas/mucf/Types.hh @@ -68,6 +68,7 @@ enum class MucfIsoprotologueMolecule protium_protium, protium_deuterium, protium_tritium, + deuterium_deuterium, deuterium_tritium, tritium_tritium, size_ diff --git a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc new file mode 100644 index 0000000000..4a60fe6418 --- /dev/null +++ b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc @@ -0,0 +1,241 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc +//---------------------------------------------------------------------------// +#include "EquilibrateDensitiesCalculator.hh" + +#include + +#include "corecel/cont/Range.hh" +#include "corecel/io/Logger.hh" +#include "corecel/math/Algorithms.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with material information. + */ +EquilibrateDensitiesCalculator::EquilibrateDensitiesCalculator( + LhdArray const& lhd_densities, real_type const temperature) + : lhd_densities_(lhd_densities), temperature_(temperature) +{ + CELER_EXPECT(temperature > 0); + CELER_EXPECT(lhd_densities[MucfIsotope::protium] + + lhd_densities[MucfIsotope::deuterium] + + lhd_densities[MucfIsotope::tritium] + > 0); +} + +//---------------------------------------------------------------------------// +/*! + * Return equilibrated isoprotologue values. + */ +EquilibrateDensitiesCalculator::EquilibriumArray +EquilibrateDensitiesCalculator::operator()() +{ + using Iso = MucfIsotope; + using IsoProt = MucfIsoprotologueMolecule; + + real_type const total_density = lhd_densities_[Iso::protium] + + lhd_densities_[Iso::deuterium] + + lhd_densities_[Iso::tritium]; + CELER_ASSERT(total_density > 0); + real_type const inv_tot_density = real_type{1} / total_density; + + // Cache equilibrium constants for this temperature for the while loop + real_type const k_hd = this->calc_hd_equilibrium_constant(); + real_type const k_dt = this->calc_dt_equilibrium_constant(); + real_type const k_ht = this->calc_ht_equilibrium_constant(); + + // Initialize result and calculate equilibrium densities + EquilibriumArray result; + result[IsoProt::protium_protium] = lhd_densities_[Iso::protium] + * inv_tot_density; + result[IsoProt::deuterium_deuterium] = lhd_densities_[Iso::deuterium] + * inv_tot_density; + result[IsoProt::tritium_tritium] = lhd_densities_[Iso::tritium] + * inv_tot_density; + result[IsoProt::protium_deuterium] = 0; + result[IsoProt::deuterium_tritium] = 0; + result[IsoProt::protium_tritium] = 0; + + EquilibriumArray previous_equilib_dens = result; + auto iter_diff = std::numeric_limits::infinity(); + size_type iter{0}; + while (iter_diff > convergence_err() && iter < max_iterations()) + { + // Equilibrate HD + this->equilibrate_pair(IsoProt::protium_protium, + IsoProt::deuterium_deuterium, + IsoProt::protium_deuterium, + k_hd, + result); + // Equilibrate DT + this->equilibrate_pair(IsoProt::deuterium_deuterium, + IsoProt::tritium_tritium, + IsoProt::deuterium_tritium, + k_dt, + result); + // Equilibrate HT + this->equilibrate_pair(IsoProt::protium_protium, + IsoProt::tritium_tritium, + IsoProt::protium_tritium, + k_ht, + result); + + for (auto const& i : range(MucfIsoprotologueMolecule::size_)) + { + // Calculate difference between current and previous densities + real_type diff = std::abs(result[i] - previous_equilib_dens[i]); + if (diff > iter_diff) + { + // Select maximum difference for convergence check + iter_diff = diff; + } + } + // Save current state to compare with next iteration + previous_equilib_dens = result; + iter++; + } + + if (iter == this->max_iterations()) + { + CELER_LOG(warning) << "Equilibration did not converge after " + << max_iterations() + << " iterations. Current error is " << iter_diff; + } + + for (auto& dens : result) + { + dens *= total_density; + } + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Calculate equilibrium constant for the + * \f$ H_2 + D_2 \rightleftharpoons 2HD \f$ reaction. + */ +real_type EquilibrateDensitiesCalculator::calc_hd_equilibrium_constant() +{ + real_type result; + if (temperature_ < 30) + { + result = 6.785 * exp(-654.3 / (r_gas_constant_ * temperature_)); + } + else + { + real_type const c_hd = r_gas_constant_ * 30 * (log(4) - log(0.49)); + result = (4.0 * exp(-c_hd / (r_gas_constant_ * temperature_))); + } + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Calculate equilibrium constant for the + * \f$ H_2 + T_2 \rightleftharpoons 2HT \f$ reaction. + */ +real_type EquilibrateDensitiesCalculator::calc_ht_equilibrium_constant() +{ + real_type result; + + if (temperature_ < 30) + { + result = 10.22 * exp(-1423 / (r_gas_constant_ * temperature_)); + } + else + { + real_type const c_ht = r_gas_constant_ * 30 * (log(4) - log(0.034)); + result = (4.0 * exp(-c_ht / (r_gas_constant_ * temperature_))); + } + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Calculate equilibrium constant for the + * \f$ D_2 + T_2 \rightleftharpoons 2DT \f$ reaction. + */ +real_type EquilibrateDensitiesCalculator::calc_dt_equilibrium_constant() +{ + real_type result; + + if (temperature_ < 15) + { + result = 5.924 * exp(-168.3 / (r_gas_constant_ * temperature_)); + } + else if (temperature_ < 30) + { + result = 2.995 * exp(-89.96 / (r_gas_constant_ * temperature_)); + } + else if (temperature_ < 100) + { + real_type const c_dt = r_gas_constant_ * 30 * (log(4) - log(2.09)); + result = 4.0 * exp(-c_dt / (r_gas_constant_ * temperature_)); + } + else + { + real_type const c_dt = r_gas_constant_ * 100 * (log(4) - log(3.29)); + result = 4.0 * exp(-c_dt / (r_gas_constant_ * temperature_)); + } + + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Equilibrate a pair of isotopes and return the new density. + * + * Since there are 3 isotopes (H, D, and T), and 6 molecular combinations, the + * equilibrium cannot be solved at once and has to be done iteratively for each + * pair until a convergence criterion is met. + * + * Therefore, this function takes 2 isotope combinations (e.g. DD, TT, and DT), + * the equilibrium constant for this temperature, and calculates how much of + * the homonuclear molecules (e.g. DD and TT) convert to the heteronuclear + * molecule (e.g. DT). + * + * The new densities are written into the input array. + */ +void EquilibrateDensitiesCalculator::equilibrate_pair( + MucfIsoprotologueMolecule molecule_aa, + MucfIsoprotologueMolecule molecule_bb, + MucfIsoprotologueMolecule molecule_ab, + real_type eq_constant_ab, + EquilibriumArray& input) +{ + auto const& dens_aa = input[molecule_aa]; + auto const& dens_bb = input[molecule_bb]; + auto const& dens_ab = input[molecule_ab]; + + // (AA + AB) / 2 + real_type const mix_a = (dens_aa + dens_ab) * real_type{0.5}; + // (BB + AB) / 2 + real_type const mix_b = (dens_bb + dens_ab) * real_type{0.5}; + + real_type sigma + = ((mix_a + mix_b) + - sqrt(ipow<2>(mix_a - mix_b) + + 16 * mix_a * mix_b + / (eq_constant_ab - this->convergence_err()))) + / (2 * (1 - 4 / (eq_constant_ab - this->convergence_err()))); + + // Write new density into the equilibrium array + input[molecule_aa] = mix_a - sigma; + input[molecule_ab] = 2 * sigma; + input[molecule_bb] = mix_b - sigma; +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh new file mode 100644 index 0000000000..651246e2db --- /dev/null +++ b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh @@ -0,0 +1,80 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/cont/EnumArray.hh" +#include "celeritas/Constants.hh" +#include "celeritas/mucf/Types.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Calculate dt mixture densities after reaching thermodynamical + * equilibrium based on LHD densities and material temperature. + * + * Based on the theory on https://www.osti.gov/biblio/6205719. + * + * The equilibrated densities are needed to correctly calculate the cycle time + * of dd, dt, and tt fusion cycles. + * + * \sa MucfMaterialInserter + */ +class EquilibrateDensitiesCalculator +{ + public: + //!@{ + //! \name Type aliases + using LhdArray = EnumArray; + using EquilibriumArray = EnumArray; + //!@} + + // Construct with material information + EquilibrateDensitiesCalculator(LhdArray const& lhd_densities, + real_type const temperature); + + // Calculate equilibrated isoprotologue densities + EquilibriumArray operator()(); + + private: + //// DATA //// + + LhdArray lhd_densities_; //!< Number densities in units of LHD + real_type temperature_; //!< Material temperature [K] + real_type const r_gas_constant_ = constants::k_boltzmann.value() + * constants::na_avogadro.value(); + + //// HELPER FUNCTIONS //// + + // Acceptance error between current and previous equilibrium iteration + static real_type constexpr convergence_err() { return 1e-6; } + + // Maximum number of iterations to reach convergence + static size_type constexpr max_iterations() { return 1000; } + + // Calculate equilibrium constant: \f$ H_2 + D_2 \rightleftharpoons 2HD \f$ + real_type calc_hd_equilibrium_constant(); + + // Calculate equilibrium constant: \f$ D_2 + T_2 \rightleftharpoons 2DT \f$ + real_type calc_dt_equilibrium_constant(); + + // Calculate equilibrium constant: \f$ H_2 + T_2 \rightleftharpoons 2HT \f$ + real_type calc_ht_equilibrium_constant(); + + // Equilibrate a pair of isotopes and write the new density in the array + void equilibrate_pair(MucfIsoprotologueMolecule molecule_aa, + MucfIsoprotologueMolecule molecule_bb, + MucfIsoprotologueMolecule molecule_ab, + real_type eq_constant_ab, + EquilibriumArray& input); +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index 5011f7f92b..ce9a5c00ea 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -35,6 +35,7 @@ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, */ bool MucfMaterialInserter::operator()(MaterialView const& material) { + this->clear(); auto const mat_num_density = material.number_density(); for (auto elcompid : range(material.num_elements())) @@ -47,8 +48,7 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) continue; } - // Found hydrogen; Check for d and t isotopes - IsotopeChecker has_isotope{false, false}; + // Found hydrogen; calculate quantities for its isotopes auto const elem_rel_abundance = material.elements()[elcompid].fraction; for (auto el_comp : range(element_view.num_isotopes())) { @@ -58,25 +58,25 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) = this->from_mass_number(iso_view.atomic_mass_number()); CELER_ASSERT(atom < MucfIsotope::size_); - has_isotope[atom] = true; // D or t isotope found + has_isotope_[atom] = true; lhd_densities_[atom] = elem_rel_abundance * mat_num_density / scalars_.liquid_hydrogen_density.value(); } - if (!has_isotope[MucfIsotope::deuterium] - && !has_isotope[MucfIsotope::tritium]) + if (!has_isotope_[MucfIsotope::deuterium] + && !has_isotope_[MucfIsotope::tritium]) { // No deuterium or tritium found; skip material return false; } - // Temporary data needed to calculate model data, such as cycle times - equilibrium_densities_ = this->calc_equilibrium_densities(element_view); + // Found hydrogen with deuterium and/or tritium; Calculate quantities + equilibrium_densities_ = EquilibrateDensitiesCalculator( + lhd_densities_, material.temperature())(); // Calculate and insert muCF material data into model data mucfmatid_to_matid_.push_back(material.material_id()); - cycle_times_.push_back( - this->calc_cycle_times(element_view, has_isotope)); + cycle_times_.push_back(this->calc_cycle_times(element_view)); //! \todo Store mean atom spin flip and transfer times } return true; @@ -103,23 +103,6 @@ MucfIsotope MucfMaterialInserter::from_mass_number(AtomicMassNumber mass) return MucfIsotope::size_; } -//---------------------------------------------------------------------------// -/*! - * Calculate dt mixture densities after reaching thermodynamical - * equilibrium. - * - * Used during cycle time calculations. - */ -MucfMaterialInserter::EquilibriumArray -MucfMaterialInserter::calc_equilibrium_densities(ElementView const&) -{ - EquilibriumArray result; - - //! \todo Implement - - return result; -} - //---------------------------------------------------------------------------// /*! * Calculate fusion mean cycle times. @@ -130,9 +113,14 @@ MucfMaterialInserter::calc_equilibrium_densities(ElementView const&) * - Multiple elements, single isotope each (separate H, d, and t elements). */ MucfMaterialInserter::CycleTimesArray -MucfMaterialInserter::calc_cycle_times(ElementView const& element, - IsotopeChecker const& has_isotope) +MucfMaterialInserter::calc_cycle_times(ElementView const& element) { + CELER_EXPECT(element.atomic_number() == AtomicNumber{1}); + CELER_EXPECT(has_isotope_[MucfIsotope::deuterium] + || has_isotope_[MucfIsotope::tritium]); + CELER_EXPECT(lhd_densities_[MucfIsotope::deuterium] > 0 + || lhd_densities_[MucfIsotope::tritium] > 0); + CycleTimesArray result; for (auto el_comp : range(element.num_isotopes())) { @@ -146,7 +134,7 @@ MucfMaterialInserter::calc_cycle_times(ElementView const& element, case MucfIsotope::deuterium: { result[MucfMuonicMolecule::deuterium_deuterium] = this->calc_dd_cycle(element); - if (has_isotope[MucfIsotope::tritium]) + if (has_isotope_[MucfIsotope::tritium]) { // Calculate cycle times for dt molecules result[MucfMuonicMolecule::deuterium_tritium] @@ -224,6 +212,23 @@ MucfMaterialInserter::calc_tt_cycle(ElementView const&) return result; } +//---------------------------------------------------------------------------// +/*! + * Clear temporary data before next insertion. + */ +void MucfMaterialInserter::clear() +{ + for (auto& lhd : lhd_densities_) + { + lhd = 0; + } + + for (auto& has_iso : has_isotope_) + { + has_iso = false; + } +} + //---------------------------------------------------------------------------// } // namespace detail } // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index 561ddbdc94..a932dc760d 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -12,6 +12,8 @@ #include "celeritas/mucf/Types.hh" #include "celeritas/mucf/data/DTMixMucfData.hh" +#include "EquilibrateDensitiesCalculator.hh" + namespace celeritas { namespace detail @@ -36,8 +38,8 @@ class MucfMaterialInserter using MoleculeCycles = Array; using CycleTimesArray = EnumArray; - using LhdArray = EnumArray; - using EquilibriumArray = EnumArray; + using LhdArray = EquilibrateDensitiesCalculator::LhdArray; + using EquilibriumArray = EquilibrateDensitiesCalculator::EquilibriumArray; using AtomicMassNumber = AtomicNumber; using IsotopeChecker = EnumArray; @@ -46,6 +48,7 @@ class MucfMaterialInserter CollectionBuilder cycle_times_; // Temporary quantities needed for calculating the model data inp::MucfScalars scalars_; + IsotopeChecker has_isotope_; LhdArray lhd_densities_; EquilibriumArray equilibrium_densities_; @@ -54,12 +57,8 @@ class MucfMaterialInserter // Return muonic atom from given atomic mass number MucfIsotope from_mass_number(AtomicMassNumber mass); - // Calculate thermal equilibrium densities - EquilibriumArray calc_equilibrium_densities(ElementView const&); - // Calculate mean fusion cycle times for all reactive muonic molecules - CycleTimesArray calc_cycle_times(ElementView const& element, - IsotopeChecker const& has_isotope); + CycleTimesArray calc_cycle_times(ElementView const& element); // Calculate mean fusion cycle times for dd muonic molecules Array calc_dd_cycle(ElementView const&); @@ -69,6 +68,9 @@ class MucfMaterialInserter // Calculate mean fusion cycle times for tt muonic molecules Array calc_tt_cycle(ElementView const&); + + // Clear temporary data before next insertion + void clear(); }; //---------------------------------------------------------------------------// From 649359e89bb814e9adaf77523c78880d7eb028db Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 2 Feb 2026 18:33:35 -0500 Subject: [PATCH 23/35] Draft import of dt fusion cycle data --- src/celeritas/Quantities.hh | 3 + src/celeritas/UnitTypes.hh | 10 + src/celeritas/inp/MucfPhysics.cc | 319 +++++++++++++++++- src/celeritas/inp/MucfPhysics.hh | 21 +- .../mucf/model/detail/MucfMaterialInserter.cc | 42 ++- .../mucf/model/detail/MucfMaterialInserter.hh | 9 +- test/celeritas/ext/GeantImporter.test.cc | 17 +- 7 files changed, 370 insertions(+), 51 deletions(-) diff --git a/src/celeritas/Quantities.hh b/src/celeritas/Quantities.hh index e16bf7ec91..f7bd2c7ea2 100644 --- a/src/celeritas/Quantities.hh +++ b/src/celeritas/Quantities.hh @@ -30,6 +30,9 @@ using AmuMass = RealQuantity; //! Special faux quantity for overloading cross section calculation using LogMevEnergy = RealQuantity; +//! Spin (in units of hbar/2) +using HalfSpinInt = Quantity; + //---------------------------------------------------------------------------// //!@{ //! \name Quantities for manual input and/or test harnesses diff --git a/src/celeritas/UnitTypes.hh b/src/celeritas/UnitTypes.hh index 5d5c28373a..a1d275c576 100644 --- a/src/celeritas/UnitTypes.hh +++ b/src/celeritas/UnitTypes.hh @@ -133,6 +133,16 @@ struct Mol //!@} +//! Spin (in units of hbar/2) +struct HalfSpin +{ + static CELER_CONSTEXPR_FUNCTION Constant value() + { + return constants::hbar_planck / 2; + } + static char const* label() { return "hbar/2"; } +}; + //---------------------------------------------------------------------------// //!@{ //! \name Gaussian units for unit tests diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index de87bbcba8..78188e455b 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -75,14 +75,315 @@ Grid mucf_muon_energy_cdf() return cdf; } +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 0 state. + */ +MucfCycleRate dd0_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::deuterium_deuterium; + result.spin_state = units::HalfSpinInt{0}; // F = 0 + + result.rate.interpolation.type = InterpolationType::cubic_spline; + // Temperature [K] + result.rate.x = { + 9.719, + 61.224, + 113.677, + 149.956, + 203.175, + 301.385, + 407.915, + 491.843, + 609.109, + 731.772, + 859.935, + 1041.085, + 1205.668, + 1370.401, + 1487.748, + }; + // Mean cycle rate [1/s] + result.rate.y = { + 0.073e8, + 1.614e8, + 2.294e8, + 2.404e8, + 2.386e8, + 2.195e8, + 2.077e8, + 2.143e8, + 2.447e8, + 2.934e8, + 3.514e8, + 4.286e8, + 4.847e8, + 5.271e8, + 5.502e8, + }; + + CELER_ENSURE(result); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 0 state. + */ +MucfCycleRate dt0_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::deuterium_tritium; + result.spin_state = units::HalfSpinInt{0}; // F = 0 + + result.rate.interpolation.type = InterpolationType::cubic_spline; + // Temperature [K] + result.rate.x = { + 2.800, + 40.599, + 90.979, + 155.357, + 194.476, + 233.474, + 277.910, + 319.385, + 392.354, + 462.421, + 554.546, + 669.030, + 831.184, + 991.254, + 1175.567, + 1349.074, + 1500.362, + }; + // Mean cycle rate [1/s] + result.rate.y = { + 0.000e8, + 0.001e8, + 0.020e8, + 0.039e8, + 0.113e8, + 0.296e8, + 0.627e8, + 1.104e8, + 2.224e8, + 3.435e8, + 4.958e8, + 6.518e8, + 8.015e8, + 8.860e8, + 9.303e8, + 9.388e8, + 9.307e8, + }; + + CELER_ENSURE(result); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 0 state. + */ +MucfCycleRate hd0_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::deuterium_tritium; + result.spin_state = units::HalfSpinInt{0}; // F = 0 + + result.rate.interpolation.type = InterpolationType::cubic_spline; + // Temperature [K] + result.rate.x = { + 1.400, 65.789, 141.358, 205.626, 237.603, 273.559, 308.014, + 344.996, 409.061, 474.303, 555.983, 628.326, 714.709, 813.915, + 926.103, 1058.264, 1183.899, 1262.237, 1368.676, 1500.476, + }; + // Mean cycle rate [1/s] + result.rate.y = { + 0.000e8, 0.010e8, 0.039e8, 0.159e8, 0.361e8, 0.765e8, 1.260e8, + 2.003e8, 3.581e8, 5.361e8, 7.470e8, 9.158e8, 10.810e8, 12.260e8, + 13.361e8, 14.124e8, 14.456e8, 14.512e8, 14.476e8, 14.295e8, + }; + + CELER_ENSURE(result); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 1 state. + */ +MucfCycleRate dd1_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::deuterium_deuterium; + result.spin_state = units::HalfSpinInt{2}; // F = 1 + + result.rate.interpolation.type = InterpolationType::cubic_spline; + // Temperature [K] + result.rate.x = { + 0.0, + 40.97238, + 86.18093, + 154.00251, + 216.16937, + 309.43132, + 391.40911, + 484.73130, + 592.21748, + 733.66109, + 900.53147, + 1070.18645, + 1251.11005, + 1383.94917, + 1501.23102, + }; + // Mean cycle rate [1/s] + result.rate.y = { + 0.0e8, + 0.02590e8, + 0.03788e8, + 0.11774e8, + 0.17032e8, + 0.33172e8, + 0.61735e8, + 1.20516e8, + 2.05375e8, + 3.27240e8, + 4.47630e8, + 5.39123e8, + 6.07188e8, + 6.38302e8, + 6.57099e8, + }; + + CELER_ENSURE(result); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for dt fusion for the F = 1 state. + */ +MucfCycleRate dt1_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::deuterium_tritium; + result.spin_state = units::HalfSpinInt{2}; // F = 1 + + result.rate.interpolation.type = InterpolationType::cubic_spline; + // Temperature [K] + result.rate.x = { + 0.0, + 42.3405, + 90.3261, + 149.6118, + 193.4004, + 257.0266, + 312.1948, + 377.2730, + 445.1485, + 527.1292, + 630.2549, + 731.9188, + 825.0831, + 935.1662, + 1079.0999, + 1268.1724, + 1502.3866, + }; + // Mean cycle rate [1/s] + result.rate.y = { + 0.0, + 0.0254e8, + 0.0505e8, + 0.2126e8, + 0.7603e8, + 2.4341e8, + 4.2458e8, + 6.4969e8, + 8.3905e8, + 10.1734e8, + 11.6117e8, + 12.3352e8, + 12.6055e8, + 12.6414e8, + 12.3869e8, + 11.8140e8, + 10.9640e8, + }; + + CELER_ENSURE(result); + return result; +} + +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for dt fusion for the F = 1 state. + */ +MucfCycleRate hd1_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::deuterium_tritium; + result.spin_state = units::HalfSpinInt{2}; // F = 1 + + result.rate.interpolation.type = InterpolationType::cubic_spline; + // Temperature [K] + result.rate.x = { + 0.0, 57.92486, 108.78618, 151.19847, 182.34991, 197.94409, + 227.75862, 274.69662, 320.26078, 380.06082, 442.66685, 515.12296, + 595.97369, 690.86987, 795.54230, 883.16703, 976.39400, 1089.35418, + 1261.56836, 1405.53035, 1505.73924, + }; + // Mean cycle rate [1/s] + result.rate.y = { + 0.0, 0.02523e8, 0.05075e8, 0.26916e8, 0.77688e8, 1.16141e8, + 2.20563e8, 4.45963e8, 6.98879e8, 10.28768e8, 13.44890e8, 16.33464e8, + 18.64233e8, 20.37175e8, 21.30299e8, 21.56089e8, 21.47469e8, 21.07136e8, + 20.10173e8, 19.14696e8, 18.48278e8, + }; + + CELER_ENSURE(result); + return result; +} + //---------------------------------------------------------------------------// /*! * Cycle rate data for muon-catalyzed fusion. + * + * Data comes from https://doi.org/10.1007/BF02227621 . */ std::vector mucf_cycle_rates() { - //! \todo Implement - return {}; + std::vector result; + + // DD fusion + { + // F = 1/2 + // F = 3/2 + // F = 5/2 + } + + // DT fusion (requires hd, dd, and dt data for both spin states) + { + // F = 0 + result.push_back(hd0_cycle_data()); + result.push_back(dd0_cycle_data()); + result.push_back(dt0_cycle_data()); + // F = 1 + result.push_back(hd1_cycle_data()); + result.push_back(dd1_cycle_data()); + result.push_back(dt1_cycle_data()); + } + + // TT fusion + { + // F = 1/2 + } + + return result; } //---------------------------------------------------------------------------// @@ -124,20 +425,6 @@ MucfPhysics MucfPhysics::from_default() result.atom_transfer = mucf_atom_transfer_rates(); result.atom_spin_flip = mucf_atom_spin_flip_rates(); - // Temporary test dummy data to verify correct import - { - MucfCycleRate dt_cycle; - dt_cycle.molecule = MucfMuonicMolecule::deuterium_tritium; - - dt_cycle.spin_label = "F=0"; - dt_cycle.rate = Grid::from_constant(2.0); - result.cycle_rates.push_back(dt_cycle); - - dt_cycle.spin_label = "F=1"; - dt_cycle.rate = Grid::from_constant(3.0); - result.cycle_rates.push_back(dt_cycle); - } - return result; } diff --git a/src/celeritas/inp/MucfPhysics.hh b/src/celeritas/inp/MucfPhysics.hh index 6b344806e2..55fe2dfac3 100644 --- a/src/celeritas/inp/MucfPhysics.hh +++ b/src/celeritas/inp/MucfPhysics.hh @@ -52,6 +52,20 @@ struct MucfScalars } }; +//---------------------------------------------------------------------------// +/*! + * Safely access cycle rate tables. + */ +enum class CycleTableType +{ + protium_deuterium, + protium_tritium, + deuterium_deuterium, + deuterium_tritium, + tritium_tritium, + size_ +}; + //---------------------------------------------------------------------------// /*! * Muon-catalyzed fusion mean cycle rate data. @@ -70,15 +84,14 @@ struct MucfScalars */ struct MucfCycleRate { - MucfMuonicMolecule molecule; + CycleTableType type; //!< Cycle table type Grid rate; //!< x = temperature [K], y = mean cycle rate [1/time] - std::string spin_label; + units::HalfSpinInt spin_state; // Spin state F, in unit of hbar/2 //! True if data is assigned explicit operator bool() const { - return molecule < MucfMuonicMolecule::size_ && rate - && !spin_label.empty(); + return type < CycleTableType::size_ && rate; } }; diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index ce9a5c00ea..fc8e843c59 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -70,13 +70,16 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) return false; } - // Found hydrogen with deuterium and/or tritium; Calculate quantities + // Found hydrogen with deuterium and/or tritium + // Calculate HDT equilibrium densities for cycle time calculations equilibrium_densities_ = EquilibrateDensitiesCalculator( lhd_densities_, material.temperature())(); // Calculate and insert muCF material data into model data mucfmatid_to_matid_.push_back(material.material_id()); - cycle_times_.push_back(this->calc_cycle_times(element_view)); + cycle_times_.push_back( + this->calc_cycle_times(element_view, material.temperature())); + //! \todo Store mean atom spin flip and transfer times } return true; @@ -113,7 +116,8 @@ MucfIsotope MucfMaterialInserter::from_mass_number(AtomicMassNumber mass) * - Multiple elements, single isotope each (separate H, d, and t elements). */ MucfMaterialInserter::CycleTimesArray -MucfMaterialInserter::calc_cycle_times(ElementView const& element) +MucfMaterialInserter::calc_cycle_times(ElementView const& element, + real_type const temperature) { CELER_EXPECT(element.atomic_number() == AtomicNumber{1}); CELER_EXPECT(has_isotope_[MucfIsotope::deuterium] @@ -133,19 +137,19 @@ MucfMaterialInserter::calc_cycle_times(ElementView const& element) // Calculate cycle times for dd molecules case MucfIsotope::deuterium: { result[MucfMuonicMolecule::deuterium_deuterium] - = this->calc_dd_cycle(element); + = this->calc_dd_cycle(temperature); if (has_isotope_[MucfIsotope::tritium]) { // Calculate cycle times for dt molecules result[MucfMuonicMolecule::deuterium_tritium] - = this->calc_dt_cycle(element); + = this->calc_dt_cycle(temperature); } break; } // Calculate cycle times for tt molecules case MucfIsotope::tritium: { result[MucfMuonicMolecule::tritium_tritium] - = this->calc_tt_cycle(element); + = this->calc_tt_cycle(temperature); break; } default: @@ -163,7 +167,7 @@ MucfMaterialInserter::calc_cycle_times(ElementView const& element) * Cycle times for dd molecules come from F = 0 and F = 1 spin states. */ MucfMaterialInserter::MoleculeCycles -MucfMaterialInserter::calc_dd_cycle(ElementView const&) +MucfMaterialInserter::calc_dd_cycle(real_type const temperature) { MoleculeCycles result; @@ -182,13 +186,29 @@ MucfMaterialInserter::calc_dd_cycle(ElementView const&) * Cycle times for dt molecules come from F = 1/2 and F = 3/2 spin states. */ MucfMaterialInserter::MoleculeCycles -MucfMaterialInserter::calc_dt_cycle(ElementView const&) +MucfMaterialInserter::calc_dt_cycle(real_type const temperature) { - MoleculeCycles result; + using IsoProt = MucfIsoprotologueMolecule; - //! \todo Implement + auto const& dd_dens = equilibrium_densities_[IsoProt::deuterium_deuterium]; + auto const& dt_dens = equilibrium_densities_[IsoProt::deuterium_tritium]; + auto const& hd_dens = equilibrium_densities_[IsoProt::protium_deuterium]; // Reactive states are F = 1/2 and F = 3/2 + MoleculeCycles result; + +#if 0 + // F = 1/2 state + result[0] = dd_dens * dd1_interp(temperature) + + dt_dens * dt1_interp(temperature) + + hd_dens * hd1_interp(temperature); + + // F = 3/2 state + result[1] = dd_dens * dd0_interp(temperature) + + dt_dens * dt0_interp(temperature) + + hd_dens * hd0_interp(temperature); +#endif + CELER_ENSURE(result[0] >= 0 && result[1] >= 0); return result; } @@ -201,7 +221,7 @@ MucfMaterialInserter::calc_dt_cycle(ElementView const&) * Cycle times for tt molecules come only from the F = 1/2 spin state. */ MucfMaterialInserter::MoleculeCycles -MucfMaterialInserter::calc_tt_cycle(ElementView const&) +MucfMaterialInserter::calc_tt_cycle(real_type const temperature) { MoleculeCycles result; diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index a932dc760d..af2c9eab5b 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -58,16 +58,17 @@ class MucfMaterialInserter MucfIsotope from_mass_number(AtomicMassNumber mass); // Calculate mean fusion cycle times for all reactive muonic molecules - CycleTimesArray calc_cycle_times(ElementView const& element); + CycleTimesArray + calc_cycle_times(ElementView const& element, real_type const temperature); // Calculate mean fusion cycle times for dd muonic molecules - Array calc_dd_cycle(ElementView const&); + Array calc_dd_cycle(real_type const temperature); // Calculate mean fusion cycle times for dt muonic molecules - Array calc_dt_cycle(ElementView const&); + Array calc_dt_cycle(real_type const temperature); // Calculate mean fusion cycle times for tt muonic molecules - Array calc_tt_cycle(ElementView const&); + Array calc_tt_cycle(real_type const temperature); // Clear temporary data before next insertion void clear(); diff --git a/test/celeritas/ext/GeantImporter.test.cc b/test/celeritas/ext/GeantImporter.test.cc index a42578192a..2d5128d98b 100644 --- a/test/celeritas/ext/GeantImporter.test.cc +++ b/test/celeritas/ext/GeantImporter.test.cc @@ -2152,22 +2152,7 @@ TEST_F(MucfBox, static_data) EXPECT_SOFT_EQ(0.55157437567861023, average(mucf.muon_energy_cdf.x)); EXPECT_SOFT_EQ(11.250286274435437, average(mucf.muon_energy_cdf.y)); - // Dummy data - auto const& cycle_f0 = mucf.cycle_rates[0]; - static double const expected_cycle_rate_f0_y[] = {2, 2}; - EXPECT_TRUE(cycle_f0); - EXPECT_EQ(cycle_f0.molecule, MucfMuonicMolecule::deuterium_tritium); - EXPECT_EQ("F=0", cycle_f0.spin_label); - EXPECT_EQ(2, cycle_f0.rate.x.size()); - EXPECT_VEC_EQ(expected_cycle_rate_f0_y, cycle_f0.rate.y); - - auto const& cycle_f1 = mucf.cycle_rates[1]; - static double const expected_cycle_rate_f1_y[] = {3, 3}; - EXPECT_TRUE(cycle_f1); - EXPECT_EQ(cycle_f1.molecule, MucfMuonicMolecule::deuterium_tritium); - EXPECT_EQ("F=1", cycle_f1.spin_label); - EXPECT_EQ(2, cycle_f1.rate.x.size()); - EXPECT_VEC_EQ(expected_cycle_rate_f1_y, cycle_f1.rate.y); + //! \todo Add real cycle rate data test EXPECT_TRUE(mucf.atom_transfer.empty()); EXPECT_TRUE(mucf.atom_spin_flip.empty()); From 9629aaa7af860c8dd98054f0ccf3f8d251bc4518 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Mon, 2 Feb 2026 21:54:06 -0500 Subject: [PATCH 24/35] Draft interpolator helper class --- src/celeritas/CMakeLists.txt | 1 + src/celeritas/inp/MucfPhysics.cc | 7 +- src/celeritas/mucf/model/DTMixMucfModel.cc | 2 +- .../mucf/model/detail/InterpolatorHelper.cc | 37 ++++++++++ .../mucf/model/detail/InterpolatorHelper.hh | 44 ++++++++++++ .../mucf/model/detail/MucfMaterialInserter.cc | 67 +++++++++++++------ .../mucf/model/detail/MucfMaterialInserter.hh | 4 +- 7 files changed, 135 insertions(+), 27 deletions(-) create mode 100644 src/celeritas/mucf/model/detail/InterpolatorHelper.cc create mode 100644 src/celeritas/mucf/model/detail/InterpolatorHelper.hh diff --git a/src/celeritas/CMakeLists.txt b/src/celeritas/CMakeLists.txt index 587f4529e4..a5822148e9 100644 --- a/src/celeritas/CMakeLists.txt +++ b/src/celeritas/CMakeLists.txt @@ -101,6 +101,7 @@ list(APPEND SOURCES mat/MaterialParamsOutput.cc mat/detail/Utils.cc mucf/model/detail/EquilibrateDensitiesCalculator.cc + mucf/model/detail/InterpolatorHelper.cc mucf/model/detail/MucfMaterialInserter.cc mucf/process/MucfProcess.cc neutron/model/CascadeOptions.cc diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index 78188e455b..c660dad7d6 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -265,7 +265,7 @@ MucfCycleRate dd1_cycle_data() //---------------------------------------------------------------------------// /*! - * Muon-catalyzed fusion cycle rate data for dt fusion for the F = 1 state. + * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 1 state. */ MucfCycleRate dt1_cycle_data() { @@ -321,7 +321,7 @@ MucfCycleRate dt1_cycle_data() //---------------------------------------------------------------------------// /*! - * Muon-catalyzed fusion cycle rate data for dt fusion for the F = 1 state. + * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 1 state. */ MucfCycleRate hd1_cycle_data() { @@ -353,7 +353,7 @@ MucfCycleRate hd1_cycle_data() /*! * Cycle rate data for muon-catalyzed fusion. * - * Data comes from https://doi.org/10.1007/BF02227621 . + * Data from https://doi.org/10.1007/BF02227621 . */ std::vector mucf_cycle_rates() { @@ -363,7 +363,6 @@ std::vector mucf_cycle_rates() { // F = 1/2 // F = 3/2 - // F = 5/2 } // DT fusion (requires hd, dd, and dt data for both spin states) diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc index 53c1079786..9611560142 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.cc +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -113,7 +113,7 @@ DTMixMucfModel::DTMixMucfModel(ActionId id, host_data.muon_energy_cdf = build_grid_record(inp_data.muon_energy_cdf); // Calculate and cache quantities for all materials with dt mixtures - detail::MucfMaterialInserter insert(&host_data, inp_data.scalars); + detail::MucfMaterialInserter insert(&host_data, inp_data); for (auto const& matid : range(materials.num_materials())) { auto const& mat_view = materials.get(PhysMatId{matid}); diff --git a/src/celeritas/mucf/model/detail/InterpolatorHelper.cc b/src/celeritas/mucf/model/detail/InterpolatorHelper.cc new file mode 100644 index 0000000000..e637b0e13a --- /dev/null +++ b/src/celeritas/mucf/model/detail/InterpolatorHelper.cc @@ -0,0 +1,37 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/detail/InterpolatorHelper.cc +//---------------------------------------------------------------------------// +#include "InterpolatorHelper.hh" + +#include "corecel/Macros.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Construct with grid input data. + */ +InterpolatorHelper::InterpolatorHelper(inp::Grid input) + : grid_record_(NonuniformGridBuilder(&reals_)(input)) + , interpolate_(grid_record_, &reals_) +{ + CELER_EXPECT(input); +} + +//---------------------------------------------------------------------------// +/*! + * Interpolate data at given point. + */ +real_type InterpolatorHelper::operator()(real_type value) const +{ + return interpolate_(value); +} + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/InterpolatorHelper.hh b/src/celeritas/mucf/model/detail/InterpolatorHelper.hh new file mode 100644 index 0000000000..ac3649054f --- /dev/null +++ b/src/celeritas/mucf/model/detail/InterpolatorHelper.hh @@ -0,0 +1,44 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/model/detail/InterpolatorHelper.hh +//---------------------------------------------------------------------------// +#pragma once + +#include "corecel/grid/NonuniformGridData.hh" +#include "corecel/inp/Grid.hh" +#include "celeritas/grid/NonuniformGridBuilder.hh" +#include "celeritas/grid/NonuniformGridCalculator.hh" +#include "celeritas/inp/MucfPhysics.hh" + +namespace celeritas +{ +namespace detail +{ +//---------------------------------------------------------------------------// +/*! + * Helper class for creating interpolators for host-only muCF input data. + * + * \sa MucfMaterialInserter + */ +class InterpolatorHelper +{ + public: + // Construct with grid input data + InterpolatorHelper(inp::Grid input); + + // Interpolate data at given point + real_type operator()(real_type value) const; + + private: + using Items = Collection; + + NonuniformGridRecord grid_record_; + Items reals_; + NonuniformGridCalculator interpolate_; +}; + +//---------------------------------------------------------------------------// +} // namespace detail +} // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index fc8e843c59..f120c355f8 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -8,6 +8,8 @@ #include "corecel/Assert.hh" +#include "InterpolatorHelper.hh" + namespace celeritas { namespace detail @@ -17,12 +19,12 @@ namespace detail * Construct with \c DTMixMucfModel model data. */ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, - inp::MucfScalars const& scalars_) + inp::MucfPhysics const& data) : mucfmatid_to_matid_(&host_data->mucfmatid_to_matid) , cycle_times_(&host_data->cycle_times) - , scalars_(scalars_) + , data_(data) { - CELER_EXPECT(scalars_); + CELER_EXPECT(data_); } //---------------------------------------------------------------------------// @@ -59,8 +61,9 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) CELER_ASSERT(atom < MucfIsotope::size_); has_isotope_[atom] = true; - lhd_densities_[atom] = elem_rel_abundance * mat_num_density - / scalars_.liquid_hydrogen_density.value(); + lhd_densities_[atom] + = elem_rel_abundance * mat_num_density + / data_.scalars.liquid_hydrogen_density.value(); } if (!has_isotope_[MucfIsotope::deuterium] @@ -164,7 +167,7 @@ MucfMaterialInserter::calc_cycle_times(ElementView const& element, * Calculate dd muonic molecules cycle times from material properties and grid * data. * - * Cycle times for dd molecules come from F = 0 and F = 1 spin states. + * Cycle times for dd molecules come from F = 1/2 and F = 3/2 spin states. */ MucfMaterialInserter::MoleculeCycles MucfMaterialInserter::calc_dd_cycle(real_type const temperature) @@ -173,7 +176,7 @@ MucfMaterialInserter::calc_dd_cycle(real_type const temperature) //! \todo Implement - // Reactive states are F = 0 and F = 1 + // Reactive states are F = 1/2 and F = 3/2 CELER_ENSURE(result[0] >= 0 && result[1] >= 0); return result; } @@ -183,7 +186,7 @@ MucfMaterialInserter::calc_dd_cycle(real_type const temperature) * Calculate dt muonic molecules cycle times from material properties and grid * data. * - * Cycle times for dt molecules come from F = 1/2 and F = 3/2 spin states. + * Cycle times for dt molecules come from F = 0 and F = 1 spin states. */ MucfMaterialInserter::MoleculeCycles MucfMaterialInserter::calc_dt_cycle(real_type const temperature) @@ -194,20 +197,44 @@ MucfMaterialInserter::calc_dt_cycle(real_type const temperature) auto const& dt_dens = equilibrium_densities_[IsoProt::deuterium_tritium]; auto const& hd_dens = equilibrium_densities_[IsoProt::protium_deuterium]; - // Reactive states are F = 1/2 and F = 3/2 + auto find_grid + = [&](inp::CycleTableType type, units::HalfSpinInt spin) -> inp::Grid { + for (auto const& cycle_rate : data_.cycle_rates) + { + if (cycle_rate.type == type && cycle_rate.spin_state == spin) + { + return cycle_rate.rate; + } + } + return inp::Grid{}; + }; + + // F = 0 interpolators + InterpolatorHelper dd0_interpolate(find_grid( + inp::CycleTableType::deuterium_deuterium, units::HalfSpinInt{0})); + InterpolatorHelper dt0_interpolate(find_grid( + inp::CycleTableType::deuterium_tritium, units::HalfSpinInt{0})); + InterpolatorHelper hd0_interpolate(find_grid( + inp::CycleTableType::protium_deuterium, units::HalfSpinInt{0})); + + // F = 1 interpolators + InterpolatorHelper dd1_interpolate(find_grid( + inp::CycleTableType::deuterium_deuterium, units::HalfSpinInt{2})); + InterpolatorHelper dt1_interpolate(find_grid( + inp::CycleTableType::deuterium_tritium, units::HalfSpinInt{2})); + InterpolatorHelper hd1_interpolate(find_grid( + inp::CycleTableType::protium_deuterium, units::HalfSpinInt{2})); + MoleculeCycles result; + // F = 0 + result[0] = dd_dens * dd0_interpolate(temperature) + + dt_dens * dt0_interpolate(temperature) + + hd_dens * hd0_interpolate(temperature); -#if 0 - // F = 1/2 state - result[0] = dd_dens * dd1_interp(temperature) - + dt_dens * dt1_interp(temperature) - + hd_dens * hd1_interp(temperature); - - // F = 3/2 state - result[1] = dd_dens * dd0_interp(temperature) - + dt_dens * dt0_interp(temperature) - + hd_dens * hd0_interp(temperature); -#endif + // F = 1 + result[1] = dd_dens * dd1_interpolate(temperature) + + dt_dens * dt1_interpolate(temperature) + + hd_dens * hd1_interpolate(temperature); CELER_ENSURE(result[0] >= 0 && result[1] >= 0); return result; diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index af2c9eab5b..c532022bfb 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -28,7 +28,7 @@ class MucfMaterialInserter public: // Construct with muCF host data explicit MucfMaterialInserter(HostVal* host_data, - inp::MucfScalars const& scalars); + inp::MucfPhysics const& data); // Insert material if it is a valid d-t mixture bool operator()(MaterialView const& material); @@ -47,7 +47,7 @@ class MucfMaterialInserter CollectionBuilder mucfmatid_to_matid_; CollectionBuilder cycle_times_; // Temporary quantities needed for calculating the model data - inp::MucfScalars scalars_; + inp::MucfPhysics const& data_; IsotopeChecker has_isotope_; LhdArray lhd_densities_; EquilibriumArray equilibrium_densities_; From 5354c223c288d9e5905db01b09e3630b854b19d2 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Tue, 3 Feb 2026 14:54:33 -0500 Subject: [PATCH 25/35] Fix interpolator helper class; Improve material inserter --- src/celeritas/inp/MucfPhysics.cc | 2 +- src/celeritas/mucf/data/DTMixMucfData.hh | 2 +- .../detail/EquilibrateDensitiesCalculator.cc | 9 +- .../detail/EquilibrateDensitiesCalculator.hh | 11 +- .../mucf/model/detail/InterpolatorHelper.cc | 3 +- .../mucf/model/detail/InterpolatorHelper.hh | 5 +- .../mucf/model/detail/MucfMaterialInserter.cc | 236 +++++++----------- .../mucf/model/detail/MucfMaterialInserter.hh | 42 ++-- 8 files changed, 128 insertions(+), 182 deletions(-) diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index c660dad7d6..43f399db24 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -337,7 +337,7 @@ MucfCycleRate hd1_cycle_data() 595.97369, 690.86987, 795.54230, 883.16703, 976.39400, 1089.35418, 1261.56836, 1405.53035, 1505.73924, }; - // Mean cycle rate [1/s] + // Mean cycle rate [1/s] use native units result.rate.y = { 0.0, 0.02523e8, 0.05075e8, 0.26916e8, 0.77688e8, 1.16141e8, 2.20563e8, 4.45963e8, 6.98879e8, 10.28768e8, 13.44890e8, 16.33464e8, diff --git a/src/celeritas/mucf/data/DTMixMucfData.hh b/src/celeritas/mucf/data/DTMixMucfData.hh index 63affe3867..eb3d379ea1 100644 --- a/src/celeritas/mucf/data/DTMixMucfData.hh +++ b/src/celeritas/mucf/data/DTMixMucfData.hh @@ -79,7 +79,7 @@ struct DTMixMucfData //! \c PhysMatId indexed by \c MuCfMatId MaterialItems mucfmatid_to_matid; //! Cycle times per material: [mat_comp_id][muonic_molecule][spin_index] - MaterialItems cycle_times; //!< In [s] + MaterialItems cycle_times; //!< [time] //! \todo Add mean atom spin flip times //! \todo Add mean atom transfer times //!@} diff --git a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc index 4a60fe6418..55f1de94f0 100644 --- a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc +++ b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc @@ -67,7 +67,7 @@ EquilibrateDensitiesCalculator::operator()() EquilibriumArray previous_equilib_dens = result; auto iter_diff = std::numeric_limits::infinity(); size_type iter{0}; - while (iter_diff > convergence_err() && iter < max_iterations()) + while (iter_diff > this->convergence_err() && iter < this->max_iterations()) { // Equilibrate HD this->equilibrate_pair(IsoProt::protium_protium, @@ -126,6 +126,7 @@ EquilibrateDensitiesCalculator::operator()() real_type EquilibrateDensitiesCalculator::calc_hd_equilibrium_constant() { real_type result; + if (temperature_ < 30) { result = 6.785 * exp(-654.3 / (r_gas_constant_ * temperature_)); @@ -225,9 +226,9 @@ void EquilibrateDensitiesCalculator::equilibrate_pair( real_type sigma = ((mix_a + mix_b) - - sqrt(ipow<2>(mix_a - mix_b) - + 16 * mix_a * mix_b - / (eq_constant_ab - this->convergence_err()))) + - std::sqrt(ipow<2>(mix_a - mix_b) + + 16 * mix_a * mix_b + / (eq_constant_ab - this->convergence_err()))) / (2 * (1 - 4 / (eq_constant_ab - this->convergence_err()))); // Write new density into the equilibrium array diff --git a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh index 651246e2db..e97fa402dd 100644 --- a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh +++ b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh @@ -19,7 +19,7 @@ namespace detail * Calculate dt mixture densities after reaching thermodynamical * equilibrium based on LHD densities and material temperature. * - * Based on the theory on https://www.osti.gov/biblio/6205719. + * Based on the theory from https://www.osti.gov/biblio/6205719. * * The equilibrated densities are needed to correctly calculate the cycle time * of dd, dt, and tt fusion cycles. @@ -52,21 +52,24 @@ class EquilibrateDensitiesCalculator //// HELPER FUNCTIONS //// + // { + // Convergence limit parameters // Acceptance error between current and previous equilibrium iteration static real_type constexpr convergence_err() { return 1e-6; } // Maximum number of iterations to reach convergence static size_type constexpr max_iterations() { return 1000; } + // } // Calculate equilibrium constant: \f$ H_2 + D_2 \rightleftharpoons 2HD \f$ real_type calc_hd_equilibrium_constant(); - // Calculate equilibrium constant: \f$ D_2 + T_2 \rightleftharpoons 2DT \f$ - real_type calc_dt_equilibrium_constant(); - // Calculate equilibrium constant: \f$ H_2 + T_2 \rightleftharpoons 2HT \f$ real_type calc_ht_equilibrium_constant(); + // Calculate equilibrium constant: \f$ D_2 + T_2 \rightleftharpoons 2DT \f$ + real_type calc_dt_equilibrium_constant(); + // Equilibrate a pair of isotopes and write the new density in the array void equilibrate_pair(MucfIsoprotologueMolecule molecule_aa, MucfIsoprotologueMolecule molecule_bb, diff --git a/src/celeritas/mucf/model/detail/InterpolatorHelper.cc b/src/celeritas/mucf/model/detail/InterpolatorHelper.cc index e637b0e13a..f4774478b7 100644 --- a/src/celeritas/mucf/model/detail/InterpolatorHelper.cc +++ b/src/celeritas/mucf/model/detail/InterpolatorHelper.cc @@ -18,7 +18,8 @@ namespace detail */ InterpolatorHelper::InterpolatorHelper(inp::Grid input) : grid_record_(NonuniformGridBuilder(&reals_)(input)) - , interpolate_(grid_record_, &reals_) + , reals_ref_(reals_) + , interpolate_(grid_record_, reals_ref_) { CELER_EXPECT(input); } diff --git a/src/celeritas/mucf/model/detail/InterpolatorHelper.hh b/src/celeritas/mucf/model/detail/InterpolatorHelper.hh index ac3649054f..5dfb02c06e 100644 --- a/src/celeritas/mucf/model/detail/InterpolatorHelper.hh +++ b/src/celeritas/mucf/model/detail/InterpolatorHelper.hh @@ -33,9 +33,12 @@ class InterpolatorHelper private: using Items = Collection; + using ItemsRef + = Collection; - NonuniformGridRecord grid_record_; Items reals_; + NonuniformGridRecord grid_record_; + ItemsRef reals_ref_; NonuniformGridCalculator interpolate_; }; diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index f120c355f8..af560dd74a 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -8,8 +8,6 @@ #include "corecel/Assert.hh" -#include "InterpolatorHelper.hh" - namespace celeritas { namespace detail @@ -25,6 +23,14 @@ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, , data_(data) { CELER_EXPECT(data_); + + // Initialize interpolators for cycle time tables + for (auto const& cycle_data : data_.cycle_rates) + { + InterpolatorHelper interp(cycle_data.rate); + interpolators_.insert( + {{cycle_data.type, cycle_data.spin_state}, interp}); + } } //---------------------------------------------------------------------------// @@ -34,11 +40,24 @@ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, * Calculates and caches material-dependent properties needed by the * \c DTMixMucfModel . If the material does not contain deuterium and/or * tritium the operator will return false. + * + * * This is designed to work with the user's material definition being either: + * - Single element, multiple isotopes (H element, with H, d, and t isotopes); + * or + * - Multiple elements, single isotope each (separate H, d, and t elements). */ bool MucfMaterialInserter::operator()(MaterialView const& material) { - this->clear(); - auto const mat_num_density = material.number_density(); + using LhdArray = EquilibrateDensitiesCalculator::LhdArray; + + CycleTimesArray cycle_times; + LhdArray lhd_densities{}; + + auto from_mass_number = [&](AtomicMassNumber mass) -> MucfIsotope { + auto it = mass_isotope_map_.find(mass); + return (it != mass_isotope_map_.end()) ? it->second + : MucfIsotope::size_; + }; for (auto elcompid : range(material.num_elements())) { @@ -50,127 +69,71 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) continue; } - // Found hydrogen; calculate quantities for its isotopes + // Found hydrogen; Check isotopes auto const elem_rel_abundance = material.elements()[elcompid].fraction; for (auto el_comp : range(element_view.num_isotopes())) { auto iso_view = element_view.isotope_record(IsotopeComponentId{el_comp}); - auto const atom - = this->from_mass_number(iso_view.atomic_mass_number()); - + auto const atom = from_mass_number(iso_view.atomic_mass_number()); CELER_ASSERT(atom < MucfIsotope::size_); - has_isotope_[atom] = true; - lhd_densities_[atom] - = elem_rel_abundance * mat_num_density - / data_.scalars.liquid_hydrogen_density.value(); - } - if (!has_isotope_[MucfIsotope::deuterium] - && !has_isotope_[MucfIsotope::tritium]) - { - // No deuterium or tritium found; skip material - return false; + // Cache density for hydrogen isotope + lhd_densities[atom] + = elem_rel_abundance * material.number_density() + / data_.scalars.liquid_hydrogen_density.value(); } + } - // Found hydrogen with deuterium and/or tritium - // Calculate HDT equilibrium densities for cycle time calculations - equilibrium_densities_ = EquilibrateDensitiesCalculator( - lhd_densities_, material.temperature())(); + if (!lhd_densities[MucfIsotope::deuterium] + && !lhd_densities[MucfIsotope::tritium]) + { + // No deuterium or tritium densities; skip material + return false; + } - // Calculate and insert muCF material data into model data - mucfmatid_to_matid_.push_back(material.material_id()); - cycle_times_.push_back( - this->calc_cycle_times(element_view, material.temperature())); + // Found d and/or t, calculate and insert data into collection - //! \todo Store mean atom spin flip and transfer times - } - return true; -} + auto equilibrium_densities = EquilibrateDensitiesCalculator( + lhd_densities, material.temperature())(); -//---------------------------------------------------------------------------// -/*! - * Return \c MucfIsotope from a given atomic mass number. - */ -MucfIsotope MucfMaterialInserter::from_mass_number(AtomicMassNumber mass) -{ - if (mass == AtomicMassNumber{1}) + if (lhd_densities[MucfIsotope::deuterium]) { - return MucfIsotope::protium; + cycle_times[MucfMuonicMolecule::deuterium_deuterium] + = this->calc_dd_cycle(equilibrium_densities, + material.temperature()); } - if (mass == AtomicMassNumber{2}) + if (lhd_densities[MucfIsotope::tritium]) { - return MucfIsotope::deuterium; + cycle_times[MucfMuonicMolecule::tritium_tritium] = this->calc_tt_cycle( + equilibrium_densities, material.temperature()); } - if (mass == AtomicMassNumber{3}) + if (lhd_densities[MucfIsotope::deuterium] + && lhd_densities[MucfIsotope::tritium]) { - return MucfIsotope::tritium; + cycle_times[MucfMuonicMolecule::deuterium_tritium] + = this->calc_dt_cycle(equilibrium_densities, + material.temperature()); } - return MucfIsotope::size_; -} -//---------------------------------------------------------------------------// -/*! - * Calculate fusion mean cycle times. - * - * This is designed to work with the user's material definition being either: - * - Single element, multiple isotopes (H element, with H, d, and t isotopes); - * or - * - Multiple elements, single isotope each (separate H, d, and t elements). - */ -MucfMaterialInserter::CycleTimesArray -MucfMaterialInserter::calc_cycle_times(ElementView const& element, - real_type const temperature) -{ - CELER_EXPECT(element.atomic_number() == AtomicNumber{1}); - CELER_EXPECT(has_isotope_[MucfIsotope::deuterium] - || has_isotope_[MucfIsotope::tritium]); - CELER_EXPECT(lhd_densities_[MucfIsotope::deuterium] > 0 - || lhd_densities_[MucfIsotope::tritium] > 0); + // Add muCF material to the model's host/device data + mucfmatid_to_matid_.push_back(material.material_id()); + cycle_times_.push_back(std::move(cycle_times)); - CycleTimesArray result; - for (auto el_comp : range(element.num_isotopes())) - { - auto iso_view = element.isotope_record(IsotopeComponentId{el_comp}); + //! \todo Store mean atom spin flip and transfer times - // Select possible muonic atom based on the isotope/element mass number - auto atom = this->from_mass_number(iso_view.atomic_mass_number()); - switch (atom) - { - // Calculate cycle times for dd molecules - case MucfIsotope::deuterium: { - result[MucfMuonicMolecule::deuterium_deuterium] - = this->calc_dd_cycle(temperature); - if (has_isotope_[MucfIsotope::tritium]) - { - // Calculate cycle times for dt molecules - result[MucfMuonicMolecule::deuterium_tritium] - = this->calc_dt_cycle(temperature); - } - break; - } - // Calculate cycle times for tt molecules - case MucfIsotope::tritium: { - result[MucfMuonicMolecule::tritium_tritium] - = this->calc_tt_cycle(temperature); - break; - } - default: - CELER_ASSERT_UNREACHABLE(); - } - } - return result; + return true; } //---------------------------------------------------------------------------// /*! - * Calculate dd muonic molecules cycle times from material properties and grid - * data. + * Calculate dd muonic molecules cycle times. * - * Cycle times for dd molecules come from F = 1/2 and F = 3/2 spin states. + * F = 1/2 and F = 3/2 are the reactive spin states for dd fusion. */ MucfMaterialInserter::MoleculeCycles -MucfMaterialInserter::calc_dd_cycle(real_type const temperature) +MucfMaterialInserter::calc_dd_cycle(EquilibriumArray const& eq_dens, + real_type const temperature) { MoleculeCycles result; @@ -183,47 +146,39 @@ MucfMaterialInserter::calc_dd_cycle(real_type const temperature) //---------------------------------------------------------------------------// /*! - * Calculate dt muonic molecules cycle times from material properties and grid - * data. + * Calculate dt muonic molecules cycle times. * - * Cycle times for dt molecules come from F = 0 and F = 1 spin states. + * F = 0 and F = 1 are the 2 reactive spin states for dt fusion. */ MucfMaterialInserter::MoleculeCycles -MucfMaterialInserter::calc_dt_cycle(real_type const temperature) +MucfMaterialInserter::calc_dt_cycle(EquilibriumArray const& eq_dens, + real_type const temperature) { - using IsoProt = MucfIsoprotologueMolecule; + CELER_EXPECT(temperature > 0); - auto const& dd_dens = equilibrium_densities_[IsoProt::deuterium_deuterium]; - auto const& dt_dens = equilibrium_densities_[IsoProt::deuterium_tritium]; - auto const& hd_dens = equilibrium_densities_[IsoProt::protium_deuterium]; + using IsoProt = MucfIsoprotologueMolecule; + using CTT = inp::CycleTableType; + using units::HalfSpinInt; - auto find_grid - = [&](inp::CycleTableType type, units::HalfSpinInt spin) -> inp::Grid { - for (auto const& cycle_rate : data_.cycle_rates) - { - if (cycle_rate.type == type && cycle_rate.spin_state == spin) - { - return cycle_rate.rate; - } - } - return inp::Grid{}; - }; + auto const& dd_dens = eq_dens[IsoProt::deuterium_deuterium]; + auto const& dt_dens = eq_dens[IsoProt::deuterium_tritium]; + auto const& hd_dens = eq_dens[IsoProt::protium_deuterium]; // F = 0 interpolators - InterpolatorHelper dd0_interpolate(find_grid( - inp::CycleTableType::deuterium_deuterium, units::HalfSpinInt{0})); - InterpolatorHelper dt0_interpolate(find_grid( - inp::CycleTableType::deuterium_tritium, units::HalfSpinInt{0})); - InterpolatorHelper hd0_interpolate(find_grid( - inp::CycleTableType::protium_deuterium, units::HalfSpinInt{0})); + auto dd0_interpolate + = interpolators_.find({CTT::deuterium_deuterium, HalfSpinInt{0}})->second; + auto dt0_interpolate + = interpolators_.find({CTT::deuterium_tritium, HalfSpinInt{0}})->second; + auto hd0_interpolate + = interpolators_.find({CTT::protium_deuterium, HalfSpinInt{0}})->second; // F = 1 interpolators - InterpolatorHelper dd1_interpolate(find_grid( - inp::CycleTableType::deuterium_deuterium, units::HalfSpinInt{2})); - InterpolatorHelper dt1_interpolate(find_grid( - inp::CycleTableType::deuterium_tritium, units::HalfSpinInt{2})); - InterpolatorHelper hd1_interpolate(find_grid( - inp::CycleTableType::protium_deuterium, units::HalfSpinInt{2})); + auto dd1_interpolate + = interpolators_.find({CTT::deuterium_deuterium, HalfSpinInt{2}})->second; + auto dt1_interpolate + = interpolators_.find({CTT::deuterium_tritium, HalfSpinInt{2}})->second; + auto hd1_interpolate + = interpolators_.find({CTT::protium_deuterium, HalfSpinInt{2}})->second; MoleculeCycles result; // F = 0 @@ -242,13 +197,13 @@ MucfMaterialInserter::calc_dt_cycle(real_type const temperature) //---------------------------------------------------------------------------// /*! - * Calculate tt muonic molecules cycle times from material properties and grid - * data. + * Calculate tt muonic molecules cycle times. * - * Cycle times for tt molecules come only from the F = 1/2 spin state. + * F = 1/2 is the only reactive spin state for tt fusion. */ MucfMaterialInserter::MoleculeCycles -MucfMaterialInserter::calc_tt_cycle(real_type const temperature) +MucfMaterialInserter::calc_tt_cycle(EquilibriumArray const& eq_dens, + real_type const temperature) { MoleculeCycles result; @@ -259,23 +214,6 @@ MucfMaterialInserter::calc_tt_cycle(real_type const temperature) return result; } -//---------------------------------------------------------------------------// -/*! - * Clear temporary data before next insertion. - */ -void MucfMaterialInserter::clear() -{ - for (auto& lhd : lhd_densities_) - { - lhd = 0; - } - - for (auto& has_iso : has_isotope_) - { - has_iso = false; - } -} - //---------------------------------------------------------------------------// } // namespace detail } // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index c532022bfb..931344c165 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -6,6 +6,8 @@ //---------------------------------------------------------------------------// #pragma once +#include + #include "corecel/data/CollectionBuilder.hh" #include "celeritas/inp/MucfPhysics.hh" #include "celeritas/mat/MaterialView.hh" @@ -13,6 +15,7 @@ #include "celeritas/mucf/data/DTMixMucfData.hh" #include "EquilibrateDensitiesCalculator.hh" +#include "InterpolatorHelper.hh" namespace celeritas { @@ -34,44 +37,41 @@ class MucfMaterialInserter bool operator()(MaterialView const& material); private: - //// DATA //// - using MoleculeCycles = Array; using CycleTimesArray = EnumArray; - using LhdArray = EquilibrateDensitiesCalculator::LhdArray; using EquilibriumArray = EquilibrateDensitiesCalculator::EquilibriumArray; using AtomicMassNumber = AtomicNumber; - using IsotopeChecker = EnumArray; + using InterpolatorsMap + = std::map, + InterpolatorHelper>; + + //// DATA //// // DTMixMucfModel host data references populated by operator() CollectionBuilder mucfmatid_to_matid_; CollectionBuilder cycle_times_; - // Temporary quantities needed for calculating the model data + // Const data + std::map const mass_isotope_map_{ + {AtomicMassNumber{1}, MucfIsotope::protium}, + {AtomicMassNumber{2}, MucfIsotope::deuterium}, + {AtomicMassNumber{3}, MucfIsotope::tritium}, + }; inp::MucfPhysics const& data_; - IsotopeChecker has_isotope_; - LhdArray lhd_densities_; - EquilibriumArray equilibrium_densities_; + InterpolatorsMap interpolators_; //// HELPER FUNCTIONS //// - // Return muonic atom from given atomic mass number - MucfIsotope from_mass_number(AtomicMassNumber mass); - - // Calculate mean fusion cycle times for all reactive muonic molecules - CycleTimesArray - calc_cycle_times(ElementView const& element, real_type const temperature); - // Calculate mean fusion cycle times for dd muonic molecules - Array calc_dd_cycle(real_type const temperature); + Array calc_dd_cycle(EquilibriumArray const& eq_dens, + real_type const temperature); // Calculate mean fusion cycle times for dt muonic molecules - Array calc_dt_cycle(real_type const temperature); + Array calc_dt_cycle(EquilibriumArray const& eq_dens, + real_type const temperature); // Calculate mean fusion cycle times for tt muonic molecules - Array calc_tt_cycle(real_type const temperature); - - // Clear temporary data before next insertion - void clear(); + Array calc_tt_cycle(EquilibriumArray const& eq_dens, + real_type const temperature); }; //---------------------------------------------------------------------------// From 4ae3c2deeca3d31dd562d25fe8b01c903423666c Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Tue, 3 Feb 2026 19:59:08 -0500 Subject: [PATCH 26/35] Draft model test skeleton; Complete interactor test data --- src/celeritas/inp/MucfPhysics.cc | 4 +- src/celeritas/mucf/data/DTMixMucfData.hh | 28 +++--- src/celeritas/mucf/model/DTMixMucfModel.cc | 6 +- .../detail/EquilibrateDensitiesCalculator.cc | 6 +- .../mucf/model/detail/MucfMaterialInserter.cc | 11 ++- src/celeritas/phys/PDGNumber.hh | 3 + test/celeritas/CMakeLists.txt | 5 +- test/celeritas/mucf/DTMixMucfModel.test.cc | 94 +++++++++++++++++++ test/celeritas/mucf/DTMucfInteractor.test.cc | 37 ++++++++ .../mucf/MucfInteractorHostTestBase.cc | 25 ++++- 10 files changed, 189 insertions(+), 30 deletions(-) create mode 100644 test/celeritas/mucf/DTMixMucfModel.test.cc diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index 51546d7491..b90a5e7b5c 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -339,7 +339,7 @@ MucfCycleRate hd1_cycle_data() 595.97369, 690.86987, 795.54230, 883.16703, 976.39400, 1089.35418, 1261.56836, 1405.53035, 1505.73924, }; - // Mean cycle rate [1/s] use native units + // Mean cycle rate [1/s] result.rate.y = { 0.0, 0.02523e8, 0.05075e8, 0.26916e8, 0.77688e8, 1.16141e8, 2.20563e8, 4.45963e8, 6.98879e8, 10.28768e8, 13.44890e8, 16.33464e8, @@ -356,6 +356,8 @@ MucfCycleRate hd1_cycle_data() * Cycle rate data for muon-catalyzed fusion. * * Data from https://doi.org/10.1007/BF02227621 . + * + * \todo Use native units. */ std::vector mucf_cycle_rates() { diff --git a/src/celeritas/mucf/data/DTMixMucfData.hh b/src/celeritas/mucf/data/DTMixMucfData.hh index bcb27fc6c0..954eb2c144 100644 --- a/src/celeritas/mucf/data/DTMixMucfData.hh +++ b/src/celeritas/mucf/data/DTMixMucfData.hh @@ -30,8 +30,8 @@ struct MucfParticleIds //!@{ //! Elementary particles and nuclei - ParticleId neutron; ParticleId proton; + ParticleId neutron; ParticleId alpha; ParticleId he3; //!@} @@ -48,8 +48,9 @@ struct MucfParticleIds //! Check whether all particles are assigned CELER_FUNCTION explicit operator bool() const { - return mu_minus && neutron && proton && alpha && he3 && muonic_hydrogen - && muonic_alpha && muonic_triton && muonic_he3; + return mu_minus && proton && neutron && alpha && he3 && muonic_hydrogen + && muonic_deuteron && muonic_triton && muonic_alpha + && muonic_he3; } }; @@ -64,8 +65,8 @@ struct MucfParticleMasses //!@{ //! Elementary particles and nuclei - units::MevMass neutron; units::MevMass proton; + units::MevMass neutron; units::MevMass alpha; units::MevMass he3; //!@} @@ -82,11 +83,12 @@ struct MucfParticleMasses //! Check whether all data are assigned CELER_FUNCTION explicit operator bool() const { - return mu_minus > zero_quantity() && neutron > zero_quantity() - && proton > zero_quantity() && alpha > zero_quantity() + return mu_minus > zero_quantity() && proton > zero_quantity() + && neutron > zero_quantity() && alpha > zero_quantity() && he3 > zero_quantity() && muonic_hydrogen > zero_quantity() - && muonic_alpha > zero_quantity() + && muonic_deuteron > zero_quantity() && muonic_triton > zero_quantity() + && muonic_alpha > zero_quantity() && muonic_he3 > zero_quantity(); } }; @@ -118,7 +120,7 @@ struct DTMixMucfData //! Material-dependent data calculated at model construction //! \c PhysMatId indexed by \c MuCfMatId MaterialItems mucfmatid_to_matid; - //! Cycle times per material: [mat_comp_id][muonic_molecule][spin_index] + //! Cycle times per material: [mucf_matid][muonic_molecule][spin_index] MaterialItems cycle_times; //!< [time] //! \todo Add mean atom spin flip times //! \todo Add mean atom transfer times @@ -127,13 +129,9 @@ struct DTMixMucfData //! Check whether the data are assigned explicit CELER_FUNCTION operator bool() const { - return true; -#if 0 - // Re-enable once full data assignment is implemented return particle_ids && particle_masses && muon_energy_cdf - && !mucfmatid_to_matid.empty() && !cycle_times.empty() - && (mucfmatid_to_matid.size() == cycle_times.size()); -#endif + && !reals.empty() && !mucfmatid_to_matid.empty() + && !cycle_times.empty(); } //! Assign from another set of data @@ -145,8 +143,8 @@ struct DTMixMucfData //! \todo Finish implementation this->particle_ids = other.particle_ids; this->particle_masses = other.particle_masses; - this->reals = other.reals; this->muon_energy_cdf = other.muon_energy_cdf; + this->reals = other.reals; this->mucfmatid_to_matid = other.mucfmatid_to_matid; this->cycle_times = other.cycle_times; diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc index 38cf45edc0..a5d3ad9b86 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.cc +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -54,15 +54,11 @@ from_params(ParticleParams const& particles) MP_ADD(proton); MP_ADD(alpha); MP_ADD(he3); + MP_ADD(muonic_hydrogen); MP_ADD(muonic_deuteron); MP_ADD(muonic_triton); MP_ADD(muonic_alpha); - - //! \todo Decide whether to implement these PDGs in PDGNumber.hh -#if 0 - MP_ADD(muonic_hydrogen); MP_ADD(muonic_he3); -#endif CELER_VALIDATE(missing.empty(), << "missing particles required for muon-catalyzed fusion: " diff --git a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc index 55f1de94f0..76a6230193 100644 --- a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc +++ b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc @@ -65,7 +65,7 @@ EquilibrateDensitiesCalculator::operator()() result[IsoProt::protium_tritium] = 0; EquilibriumArray previous_equilib_dens = result; - auto iter_diff = std::numeric_limits::infinity(); + real_type iter_diff{0}; size_type iter{0}; while (iter_diff > this->convergence_err() && iter < this->max_iterations()) { @@ -88,11 +88,11 @@ EquilibrateDensitiesCalculator::operator()() k_ht, result); - for (auto const& i : range(MucfIsoprotologueMolecule::size_)) + for (auto i : range(MucfIsoprotologueMolecule::size_)) { // Calculate difference between current and previous densities real_type diff = std::abs(result[i] - previous_equilib_dens[i]); - if (diff > iter_diff) + if (iter_diff < diff) { // Select maximum difference for convergence check iter_diff = diff; diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index af560dd74a..f16d6aa045 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -135,7 +135,7 @@ MucfMaterialInserter::MoleculeCycles MucfMaterialInserter::calc_dd_cycle(EquilibriumArray const& eq_dens, real_type const temperature) { - MoleculeCycles result; + MoleculeCycles result{0, 0}; //! \todo Implement @@ -148,7 +148,7 @@ MucfMaterialInserter::calc_dd_cycle(EquilibriumArray const& eq_dens, /*! * Calculate dt muonic molecules cycle times. * - * F = 0 and F = 1 are the 2 reactive spin states for dt fusion. + * F = 0 and F = 1 are the reactive spin states for dt fusion. */ MucfMaterialInserter::MoleculeCycles MucfMaterialInserter::calc_dt_cycle(EquilibriumArray const& eq_dens, @@ -180,7 +180,8 @@ MucfMaterialInserter::calc_dt_cycle(EquilibriumArray const& eq_dens, auto hd1_interpolate = interpolators_.find({CTT::protium_deuterium, HalfSpinInt{2}})->second; - MoleculeCycles result; + MoleculeCycles result{1, 2}; +#if 0 // F = 0 result[0] = dd_dens * dd0_interpolate(temperature) + dt_dens * dt0_interpolate(temperature) @@ -190,7 +191,9 @@ MucfMaterialInserter::calc_dt_cycle(EquilibriumArray const& eq_dens, result[1] = dd_dens * dd1_interpolate(temperature) + dt_dens * dt1_interpolate(temperature) + hd_dens * hd1_interpolate(temperature); +#endif + // Reactive states are F = 0 and F = 1 CELER_ENSURE(result[0] >= 0 && result[1] >= 0); return result; } @@ -205,7 +208,7 @@ MucfMaterialInserter::MoleculeCycles MucfMaterialInserter::calc_tt_cycle(EquilibriumArray const& eq_dens, real_type const temperature) { - MoleculeCycles result; + MoleculeCycles result{0, 0}; //! \todo Implement diff --git a/src/celeritas/phys/PDGNumber.hh b/src/celeritas/phys/PDGNumber.hh index f97f1fdf13..e1f1a2c3ec 100644 --- a/src/celeritas/phys/PDGNumber.hh +++ b/src/celeritas/phys/PDGNumber.hh @@ -137,6 +137,7 @@ CELER_DEFINE_PDGNUMBER(proton, 2212) CELER_DEFINE_PDGNUMBER(anti_proton, -2212) // Ions +CELER_DEFINE_PDGNUMBER(hydrogen, 1000010010) CELER_DEFINE_PDGNUMBER(deuteron, 1000010020) CELER_DEFINE_PDGNUMBER(anti_deuteron, -1000010020) CELER_DEFINE_PDGNUMBER(triton, 1000010030) @@ -147,9 +148,11 @@ CELER_DEFINE_PDGNUMBER(alpha, 1000020040) CELER_DEFINE_PDGNUMBER(anti_alpha, -1000020040) // Muonic nuclei +CELER_DEFINE_PDGNUMBER(muonic_hydrogen, 2000010010) CELER_DEFINE_PDGNUMBER(muonic_deuteron, 2000010020) CELER_DEFINE_PDGNUMBER(muonic_triton, 2000010030) CELER_DEFINE_PDGNUMBER(muonic_alpha, 2000020040) +CELER_DEFINE_PDGNUMBER(muonic_he3, 2000020030) //!@} diff --git a/test/celeritas/CMakeLists.txt b/test/celeritas/CMakeLists.txt index 2288bfcb65..1f53a8845d 100644 --- a/test/celeritas/CMakeLists.txt +++ b/test/celeritas/CMakeLists.txt @@ -152,7 +152,10 @@ celeritas_add_test(decay/MuDecay.test.cc ${_needs_double}) # MuCF # Interactors -celeritas_add_test(mucf/DTMucfInteractor.test.cc ${_needs_double}) +celeritas_add_test(mucf/DTMucfInteractor.test.cc) + +# Model +celeritas_add_test(mucf/DTMixMucfModel.test.cc) #-----------------------------------------------------------------------------# # EM diff --git a/test/celeritas/mucf/DTMixMucfModel.test.cc b/test/celeritas/mucf/DTMixMucfModel.test.cc new file mode 100644 index 0000000000..665ba8efbd --- /dev/null +++ b/test/celeritas/mucf/DTMixMucfModel.test.cc @@ -0,0 +1,94 @@ +//------------------------------- -*- C++ -*- -------------------------------// +// Copyright Celeritas contributors: see top-level COPYRIGHT file for details +// SPDX-License-Identifier: (Apache-2.0 OR MIT) +//---------------------------------------------------------------------------// +//! \file celeritas/mucf/DTMixMucfModel.test.cc +//---------------------------------------------------------------------------// + +#include "celeritas/mucf/model/DTMixMucfModel.hh" + +#include "corecel/io/Logger.hh" + +#include "MucfInteractorHostTestBase.hh" +#include "celeritas_test.hh" + +namespace celeritas +{ +namespace test +{ +//---------------------------------------------------------------------------// + +class DTMixMucfModelTest : public MucfInteractorHostTestBase +{ + protected: + void SetUp() override + { + particles_ = this->particle_params(); + this->set_material("hdt_fuel"); + materials_ = this->material_params(); + } + + protected: + std::shared_ptr particles_; + std::shared_ptr materials_; +}; + +//---------------------------------------------------------------------------// +TEST_F(DTMixMucfModelTest, data) +{ + using Molecule = MucfMuonicMolecule; + + auto model = DTMixMucfModel(ActionId{0}, *particles_, *materials_); + auto const& data = model.host_ref(); + auto const& pids = data.particle_ids; + auto const& masses = data.particle_masses; + +#define EXPECT_PDG_EQ(MEMBER) \ + EXPECT_EQ(pdg::MEMBER().get(), particles_->id_to_pdg(pids.MEMBER).get()) + + EXPECT_PDG_EQ(mu_minus); + EXPECT_PDG_EQ(neutron); + EXPECT_PDG_EQ(proton); + EXPECT_PDG_EQ(alpha); + EXPECT_PDG_EQ(he3); + EXPECT_PDG_EQ(muonic_hydrogen); + EXPECT_PDG_EQ(muonic_deuteron); + EXPECT_PDG_EQ(muonic_triton); + EXPECT_PDG_EQ(muonic_alpha); + EXPECT_PDG_EQ(muonic_he3); + +#undef EXPECT_PDG_EQ + +#define EXPECT_MASS_EQ(MEMBER) \ + EXPECT_EQ(masses.MEMBER, particles_->get(pids.MEMBER).mass()) + + EXPECT_MASS_EQ(mu_minus); + EXPECT_MASS_EQ(neutron); + EXPECT_MASS_EQ(proton); + EXPECT_MASS_EQ(alpha); + EXPECT_MASS_EQ(he3); + EXPECT_MASS_EQ(muonic_hydrogen); + EXPECT_MASS_EQ(muonic_deuteron); + EXPECT_MASS_EQ(muonic_triton); + EXPECT_MASS_EQ(muonic_alpha); + EXPECT_MASS_EQ(muonic_he3); + +#undef EXPECT_MASS_EQ + + EXPECT_EQ(21, data.muon_energy_cdf.grid.size()); + + auto const& cycles = data.cycle_times; + // DD cycle times + EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::deuterium_deuterium][0]); + EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::deuterium_deuterium][1]); + // DT cycle times + EXPECT_SOFT_EQ(1, cycles[MuCfMatId{0}][Molecule::deuterium_tritium][0]); + EXPECT_SOFT_EQ(2, cycles[MuCfMatId{0}][Molecule::deuterium_tritium][1]); + // TT cycle times + EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::tritium_tritium][0]); + EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::tritium_tritium][1]); +} + +//---------------------------------------------------------------------------// +} // namespace test +} // namespace celeritas diff --git a/test/celeritas/mucf/DTMucfInteractor.test.cc b/test/celeritas/mucf/DTMucfInteractor.test.cc index 42bbcf6008..6e6c512e29 100644 --- a/test/celeritas/mucf/DTMucfInteractor.test.cc +++ b/test/celeritas/mucf/DTMucfInteractor.test.cc @@ -41,25 +41,62 @@ class DTMucfInteractorTest : public MucfInteractorHostTestBase // Set up particle IDs host_data.particle_ids.mu_minus = params.find(pdg::mu_minus()); + host_data.particle_ids.proton = params.find(pdg::proton()); host_data.particle_ids.neutron = params.find(pdg::neutron()); host_data.particle_ids.alpha = params.find(pdg::alpha()); + host_data.particle_ids.he3 = params.find(pdg::he3()); + host_data.particle_ids.muonic_hydrogen + = params.find(pdg::muonic_hydrogen()); + host_data.particle_ids.muonic_deuteron + = params.find(pdg::muonic_deuteron()); + host_data.particle_ids.muonic_triton + = params.find(pdg::muonic_triton()); host_data.particle_ids.muonic_alpha = params.find(pdg::muonic_alpha()); + host_data.particle_ids.muonic_he3 = params.find(pdg::muonic_he3()); // Set up particle masses host_data.particle_masses.mu_minus = params.get(host_data.particle_ids.mu_minus).mass(); + host_data.particle_masses.proton + = params.get(host_data.particle_ids.proton).mass(); host_data.particle_masses.neutron = params.get(host_data.particle_ids.neutron).mass(); host_data.particle_masses.alpha = params.get(host_data.particle_ids.alpha).mass(); + host_data.particle_masses.he3 + = params.get(host_data.particle_ids.he3).mass(); + host_data.particle_masses.muonic_hydrogen + = params.get(host_data.particle_ids.muonic_hydrogen).mass(); + host_data.particle_masses.muonic_deuteron + = params.get(host_data.particle_ids.muonic_deuteron).mass(); + host_data.particle_masses.muonic_triton + = params.get(host_data.particle_ids.muonic_triton).mass(); host_data.particle_masses.muonic_alpha = params.get(host_data.particle_ids.muonic_alpha).mass(); + host_data.particle_masses.muonic_he3 + = params.get(host_data.particle_ids.muonic_he3).mass(); // Set up muon energy CDF auto const inp_data = inp::MucfPhysics::from_default(); NonuniformGridBuilder build_grid_record{&host_data.reals}; host_data.muon_energy_cdf = build_grid_record(inp_data.muon_energy_cdf); + // Set up dummy cycle data + using CycleTimesArray + = EnumArray>; + CollectionBuilder mucfid( + &host_data.mucfmatid_to_matid); + CollectionBuilder cycles( + &host_data.cycle_times); + + CycleTimesArray cycle_data; + cycle_data[MucfMuonicMolecule::deuterium_deuterium] = {1, 2}; + cycle_data[MucfMuonicMolecule::deuterium_tritium] = {3, 4}; + cycle_data[MucfMuonicMolecule::tritium_tritium] = {5, 6}; + + mucfid.push_back(PhysMatId{0}); + cycles.push_back(std::move(cycle_data)); + // Construct collection data_ = CollectionMirror{std::move(host_data)}; diff --git a/test/celeritas/mucf/MucfInteractorHostTestBase.cc b/test/celeritas/mucf/MucfInteractorHostTestBase.cc index e39de6f2bb..1889f4b3be 100644 --- a/test/celeritas/mucf/MucfInteractorHostTestBase.cc +++ b/test/celeritas/mucf/MucfInteractorHostTestBase.cc @@ -62,7 +62,8 @@ MucfInteractorHostBase::MucfInteractorHostBase() muon_mass, ElementaryCharge{1}, native_value_from(muon_decay_constant)}, - // Nuclei and ions + + // Ions {"proton", pdg::proton(), protium_mass, @@ -89,11 +90,33 @@ MucfInteractorHostBase::MucfInteractorHostBase() ElementaryCharge{2}, stable_decay_constant}, {"he3", pdg::he3(), he3_mass, ElementaryCharge{2}, stable_decay_constant}, + + // Muonic atoms + {"muonic_hydrogen", + pdg::muonic_hydrogen(), + protium_mass + muon_mass, + ElementaryCharge{1}, + stable_decay_constant}, + {"muonic_deuteron", + pdg::muonic_deuteron(), + deuterium_mass + muon_mass, + ElementaryCharge{1}, + stable_decay_constant}, + {"muonic_triton", + pdg::muonic_triton(), + tritium_mass + muon_mass, + ElementaryCharge{1}, + stable_decay_constant}, {"muonic_alpha", pdg::muonic_alpha(), alpha_mass + muon_mass, ElementaryCharge{1}, native_value_from(muon_decay_constant)}, + {"muonic_he3", + pdg::muonic_he3(), + he3_mass + muon_mass, + ElementaryCharge{1}, + native_value_from(muon_decay_constant)}, }; this->set_particle_params(std::move(par_inp)); From c20f26d83ea6dd4c76ad3e37fdc1be613a61a0fc Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Wed, 4 Feb 2026 16:02:46 -0500 Subject: [PATCH 27/35] Fix input bug --- src/celeritas/inp/MucfPhysics.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index b90a5e7b5c..8ff3e49bcf 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -192,7 +192,7 @@ MucfCycleRate dt0_cycle_data() MucfCycleRate hd0_cycle_data() { MucfCycleRate result; - result.type = CycleTableType::deuterium_tritium; + result.type = CycleTableType::protium_deuterium; result.spin_state = units::HalfSpinInt{0}; // F = 0 result.rate.interpolation.type = InterpolationType::cubic_spline; @@ -328,7 +328,7 @@ MucfCycleRate dt1_cycle_data() MucfCycleRate hd1_cycle_data() { MucfCycleRate result; - result.type = CycleTableType::deuterium_tritium; + result.type = CycleTableType::protium_deuterium; result.spin_state = units::HalfSpinInt{2}; // F = 1 result.rate.interpolation.type = InterpolationType::cubic_spline; From 62707b73aaa463ea4a4f2912ebefcaed5a5730d8 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Wed, 4 Feb 2026 16:08:44 -0500 Subject: [PATCH 28/35] Multiple fixes while calculating/inserting host/device data --- .../detail/EquilibrateDensitiesCalculator.cc | 52 +++++++------- .../mucf/model/detail/InterpolatorHelper.cc | 5 +- .../mucf/model/detail/InterpolatorHelper.hh | 5 +- .../mucf/model/detail/MucfMaterialInserter.cc | 68 +++++++++---------- test/celeritas/mucf/DTMixMucfModel.test.cc | 13 ++-- 5 files changed, 78 insertions(+), 65 deletions(-) diff --git a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc index 76a6230193..0eae18f1b6 100644 --- a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc +++ b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.cc @@ -49,10 +49,10 @@ EquilibrateDensitiesCalculator::operator()() // Cache equilibrium constants for this temperature for the while loop real_type const k_hd = this->calc_hd_equilibrium_constant(); - real_type const k_dt = this->calc_dt_equilibrium_constant(); real_type const k_ht = this->calc_ht_equilibrium_constant(); + real_type const k_dt = this->calc_dt_equilibrium_constant(); - // Initialize result and calculate equilibrium densities + // Initialize result and set homonuclear molecules values EquilibriumArray result; result[IsoProt::protium_protium] = lhd_densities_[Iso::protium] * inv_tot_density; @@ -60,21 +60,16 @@ EquilibrateDensitiesCalculator::operator()() * inv_tot_density; result[IsoProt::tritium_tritium] = lhd_densities_[Iso::tritium] * inv_tot_density; - result[IsoProt::protium_deuterium] = 0; - result[IsoProt::deuterium_tritium] = 0; - result[IsoProt::protium_tritium] = 0; - EquilibriumArray previous_equilib_dens = result; + EquilibriumArray previous_equilib_dens; real_type iter_diff{0}; size_type iter{0}; - while (iter_diff > this->convergence_err() && iter < this->max_iterations()) + do { - // Equilibrate HD - this->equilibrate_pair(IsoProt::protium_protium, - IsoProt::deuterium_deuterium, - IsoProt::protium_deuterium, - k_hd, - result); + iter++; + iter_diff = 0; + previous_equilib_dens = result; + // Equilibrate DT this->equilibrate_pair(IsoProt::deuterium_deuterium, IsoProt::tritium_tritium, @@ -87,21 +82,25 @@ EquilibrateDensitiesCalculator::operator()() IsoProt::protium_tritium, k_ht, result); + // Equilibrate HD + this->equilibrate_pair(IsoProt::protium_protium, + IsoProt::deuterium_deuterium, + IsoProt::protium_deuterium, + k_hd, + result); for (auto i : range(MucfIsoprotologueMolecule::size_)) { // Calculate difference between current and previous densities real_type diff = std::abs(result[i] - previous_equilib_dens[i]); - if (iter_diff < diff) + if (diff > iter_diff) { // Select maximum difference for convergence check iter_diff = diff; } } - // Save current state to compare with next iteration - previous_equilib_dens = result; - iter++; - } + } while ((iter_diff > this->convergence_err()) + && (iter < this->max_iterations())); if (iter == this->max_iterations()) { @@ -110,9 +109,9 @@ EquilibrateDensitiesCalculator::operator()() << " iterations. Current error is " << iter_diff; } - for (auto& dens : result) + for (auto& val : result) { - dens *= total_density; + val *= total_density; } return result; @@ -215,14 +214,19 @@ void EquilibrateDensitiesCalculator::equilibrate_pair( real_type eq_constant_ab, EquilibriumArray& input) { + CELER_EXPECT(molecule_aa < MucfIsoprotologueMolecule::size_); + CELER_EXPECT(molecule_bb < MucfIsoprotologueMolecule::size_); + CELER_EXPECT(molecule_ab < MucfIsoprotologueMolecule::size_); + CELER_EXPECT(eq_constant_ab > 0); + auto const& dens_aa = input[molecule_aa]; auto const& dens_bb = input[molecule_bb]; auto const& dens_ab = input[molecule_ab]; - // (AA + AB) / 2 - real_type const mix_a = (dens_aa + dens_ab) * real_type{0.5}; - // (BB + AB) / 2 - real_type const mix_b = (dens_bb + dens_ab) * real_type{0.5}; + // AA + AB / 2 + real_type const mix_a = dens_aa + dens_ab * real_type{0.5}; + // BB + AB / 2 + real_type const mix_b = dens_bb + dens_ab * real_type{0.5}; real_type sigma = ((mix_a + mix_b) diff --git a/src/celeritas/mucf/model/detail/InterpolatorHelper.cc b/src/celeritas/mucf/model/detail/InterpolatorHelper.cc index f4774478b7..3d9beb6ead 100644 --- a/src/celeritas/mucf/model/detail/InterpolatorHelper.cc +++ b/src/celeritas/mucf/model/detail/InterpolatorHelper.cc @@ -6,7 +6,7 @@ //---------------------------------------------------------------------------// #include "InterpolatorHelper.hh" -#include "corecel/Macros.hh" +#include "corecel/Assert.hh" namespace celeritas { @@ -16,12 +16,13 @@ namespace detail /*! * Construct with grid input data. */ -InterpolatorHelper::InterpolatorHelper(inp::Grid input) +InterpolatorHelper::InterpolatorHelper(inp::Grid const& input) : grid_record_(NonuniformGridBuilder(&reals_)(input)) , reals_ref_(reals_) , interpolate_(grid_record_, reals_ref_) { CELER_EXPECT(input); + CELER_ENSURE(grid_record_); } //---------------------------------------------------------------------------// diff --git a/src/celeritas/mucf/model/detail/InterpolatorHelper.hh b/src/celeritas/mucf/model/detail/InterpolatorHelper.hh index 5dfb02c06e..68c0bb6127 100644 --- a/src/celeritas/mucf/model/detail/InterpolatorHelper.hh +++ b/src/celeritas/mucf/model/detail/InterpolatorHelper.hh @@ -6,6 +6,7 @@ //---------------------------------------------------------------------------// #pragma once +#include "corecel/Macros.hh" #include "corecel/grid/NonuniformGridData.hh" #include "corecel/inp/Grid.hh" #include "celeritas/grid/NonuniformGridBuilder.hh" @@ -26,7 +27,9 @@ class InterpolatorHelper { public: // Construct with grid input data - InterpolatorHelper(inp::Grid input); + explicit InterpolatorHelper(inp::Grid const& input); + // Prevent copy and move + CELER_DELETE_COPY_MOVE(InterpolatorHelper); // Interpolate data at given point real_type operator()(real_type value) const; diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index f16d6aa045..d59f0716b1 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -27,9 +27,9 @@ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, // Initialize interpolators for cycle time tables for (auto const& cycle_data : data_.cycle_rates) { - InterpolatorHelper interp(cycle_data.rate); - interpolators_.insert( - {{cycle_data.type, cycle_data.spin_state}, interp}); + // Use emplace to avoid copy/move of InterpolatorHelper objects + interpolators_.emplace( + std::pair{cycle_data.type, cycle_data.spin_state}, cycle_data.rate); } } @@ -45,6 +45,9 @@ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, * - Single element, multiple isotopes (H element, with H, d, and t isotopes); * or * - Multiple elements, single isotope each (separate H, d, and t elements). + * + * The input data stores the cycle \em rate \f$\lambda\f$, while the cached + * data is the cycle \em time \f$\tau = 1/\lambda\f$. */ bool MucfMaterialInserter::operator()(MaterialView const& material) { @@ -78,10 +81,11 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) auto const atom = from_mass_number(iso_view.atomic_mass_number()); CELER_ASSERT(atom < MucfIsotope::size_); - // Cache density for hydrogen isotope + // Cache density for this hydrogen isotope lhd_densities[atom] - = elem_rel_abundance * material.number_density() - / data_.scalars.liquid_hydrogen_density.value(); + = element_view.isotopes()[el_comp].fraction + * (elem_rel_abundance * material.number_density() + / data_.scalars.liquid_hydrogen_density.value()); } } @@ -164,36 +168,32 @@ MucfMaterialInserter::calc_dt_cycle(EquilibriumArray const& eq_dens, auto const& dt_dens = eq_dens[IsoProt::deuterium_tritium]; auto const& hd_dens = eq_dens[IsoProt::protium_deuterium]; - // F = 0 interpolators - auto dd0_interpolate - = interpolators_.find({CTT::deuterium_deuterium, HalfSpinInt{0}})->second; - auto dt0_interpolate - = interpolators_.find({CTT::deuterium_tritium, HalfSpinInt{0}})->second; - auto hd0_interpolate - = interpolators_.find({CTT::protium_deuterium, HalfSpinInt{0}})->second; + auto get = [&](inp::CycleTableType type, units::HalfSpinInt spin) -> auto& { + auto it = interpolators_.find({type, spin}); + CELER_ASSERT(it != interpolators_.end()); + return it->second; + }; + // F = 0 interpolators + auto const& hd0_interpolate = get(CTT::protium_deuterium, HalfSpinInt{0}); + auto const& dd0_interpolate = get(CTT::deuterium_deuterium, HalfSpinInt{0}); + auto const& dt0_interpolate = get(CTT::deuterium_tritium, HalfSpinInt{0}); // F = 1 interpolators - auto dd1_interpolate - = interpolators_.find({CTT::deuterium_deuterium, HalfSpinInt{2}})->second; - auto dt1_interpolate - = interpolators_.find({CTT::deuterium_tritium, HalfSpinInt{2}})->second; - auto hd1_interpolate - = interpolators_.find({CTT::protium_deuterium, HalfSpinInt{2}})->second; - - MoleculeCycles result{1, 2}; -#if 0 - // F = 0 - result[0] = dd_dens * dd0_interpolate(temperature) - + dt_dens * dt0_interpolate(temperature) - + hd_dens * hd0_interpolate(temperature); - - // F = 1 - result[1] = dd_dens * dd1_interpolate(temperature) - + dt_dens * dt1_interpolate(temperature) - + hd_dens * hd1_interpolate(temperature); -#endif - - // Reactive states are F = 0 and F = 1 + auto const& hd1_interpolate = get(CTT::protium_deuterium, HalfSpinInt{2}); + auto const& dd1_interpolate = get(CTT::deuterium_deuterium, HalfSpinInt{2}); + auto const& dt1_interpolate = get(CTT::deuterium_tritium, HalfSpinInt{2}); + + // Interpolate over rates, store final cycle time (1/rate) + MoleculeCycles result; + result[0] = real_type{1} + / (hd_dens * hd0_interpolate(temperature) + + dd_dens * dd0_interpolate(temperature) + + dt_dens * dt0_interpolate(temperature)); // F = 0 + result[1] = real_type{1} + / (hd_dens * hd1_interpolate(temperature) + + dd_dens * dd1_interpolate(temperature) + + dt_dens * dt1_interpolate(temperature)); // F = 1 + CELER_ENSURE(result[0] >= 0 && result[1] >= 0); return result; } diff --git a/test/celeritas/mucf/DTMixMucfModel.test.cc b/test/celeritas/mucf/DTMixMucfModel.test.cc index 665ba8efbd..abfa345fcf 100644 --- a/test/celeritas/mucf/DTMixMucfModel.test.cc +++ b/test/celeritas/mucf/DTMixMucfModel.test.cc @@ -7,8 +7,6 @@ #include "celeritas/mucf/model/DTMixMucfModel.hh" -#include "corecel/io/Logger.hh" - #include "MucfInteractorHostTestBase.hh" #include "celeritas_test.hh" @@ -77,13 +75,20 @@ TEST_F(DTMixMucfModelTest, data) EXPECT_EQ(21, data.muon_energy_cdf.grid.size()); + // In seconds + static double const expected_dt_f0_cycle_time{1.0182824459351898e-08}; + static double const expected_dt_f1_cycle_time{5.098478246172425e-09}; + auto const& cycles = data.cycle_times; + // DD cycle times EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::deuterium_deuterium][0]); EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::deuterium_deuterium][1]); // DT cycle times - EXPECT_SOFT_EQ(1, cycles[MuCfMatId{0}][Molecule::deuterium_tritium][0]); - EXPECT_SOFT_EQ(2, cycles[MuCfMatId{0}][Molecule::deuterium_tritium][1]); + EXPECT_SOFT_EQ(expected_dt_f0_cycle_time, + cycles[MuCfMatId{0}][Molecule::deuterium_tritium][0]); + EXPECT_SOFT_EQ(expected_dt_f1_cycle_time, + cycles[MuCfMatId{0}][Molecule::deuterium_tritium][1]); // TT cycle times EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::tritium_tritium][0]); EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::tritium_tritium][1]); From 87da1cbc7a69a7a752de322ee9a16d811de3a42c Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Wed, 4 Feb 2026 17:02:39 -0500 Subject: [PATCH 29/35] Add DD and TT cycle data --- src/celeritas/inp/MucfPhysics.cc | 219 +++++++++++++++--- .../mucf/model/detail/MucfMaterialInserter.cc | 64 +++-- .../mucf/model/detail/MucfMaterialInserter.hh | 4 + test/celeritas/mucf/DTMixMucfModel.test.cc | 25 +- 4 files changed, 258 insertions(+), 54 deletions(-) diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index 8ff3e49bcf..987fd4536d 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -77,11 +77,156 @@ Grid mucf_muon_energy_cdf() return cdf; } +//---------------------------------------------------------------------------// +// DD cycle rate data +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for \em dd fusion with F = 1/2 state. + */ +MucfCycleRate dd_1_over_2_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::deuterium_deuterium; + result.spin_state = units::HalfSpinInt{1}; // F = 1/2 + + result.rate.interpolation.type = InterpolationType::cubic_spline; + // Temperature [K] + result.rate.x = { + 0.0, + 36.43103676856282, + 49.84753686046977, + 62.30013430590361, + 72.8249264583911, + 94.80254726485413, + 118.65929788178391, + 134.8637502794038, + 174.9275151614521, + 212.16105692405534, + 260.90506935936025, + 325.03565799325077, + 380.6089687138337, + 434.29674495520254, + 486.0836155123639, + 564.7393523983244, + 668.3489596576321, + 764.2819309563899, + 860.2110594538995, + 935.0316806939586, + 998.3392693938174, + }; + // Mean cycle rate [1/s] + result.rate.y = { + 0.0, + 0.005464463375221662e6, + 0.020292872692477815e6, + 0.04715981762065269e6, + 0.098103833770665e6, + 0.2721222062034343e6, + 0.5362926959236205e6, + 0.7584940318094278e6, + 1.2388537948623028e6, + 1.6110396634730852e6, + 1.9710284809214524e6, + 2.2225696043387786e6, + 2.3059026708109145e6, + 2.3141137763784285e6, + 2.283275296360035e6, + 2.19792475923249e6, + 2.05207892345204e6, + 1.921378528091838e6, + 1.7996962265613146e6, + 1.7144033314524956e6, + 1.6473195500592115e6, + }; + + CELER_ENSURE(result); + return result; +} +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for \em dd fusion with F = 3/2 state. + */ +MucfCycleRate dd_3_over_2_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::deuterium_deuterium; + result.spin_state = units::HalfSpinInt{3}; // F = 3/2 + + result.rate.interpolation.type = InterpolationType::cubic_spline; + // Temperature [K] + result.rate.x = { + 0.0, + 5.4317995646105715, + 8.023769006655044, + 15.563345056011997, + 21.23403876489806, + 24.19235525928977, + 30.970416194589205, + 36.77176514592074, + 40.6299375993124, + 48.3052926261127, + 54.996250066448454, + 65.48389514020138, + 83.57516295078466, + 104.55813870078731, + 127.50862868996978, + 141.88390769335217, + 157.2358986807023, + 176.44862398894298, + 208.18055529758965, + 248.59273415949275, + 289.00491302139596, + 347.6774429488119, + 437.0821353934164, + 532.1831402218961, + 626.2766906564209, + 748.1620201862349, + 868.0888020131156, + 992.7877025236317, + }; + // Mean cycle rate [1/s] + result.rate.y = { + 0.0, + 3.002924371750031, + 3.670220083632165, + 3.976719989803767, + 4.169019528475477, + 3.9765902952616323, + 3.820175795347777, + 3.705860143810432, + 3.6516939388136374, + 3.6395545296699483, + 3.6875501565621276, + 3.8256690797323616, + 4.119986345246231, + 4.378188003927343, + 4.519125621813277, + 4.533939620625851, + 4.506654771061913, + 4.419191653948062, + 4.202281855381299, + 3.8650011112100278, + 3.5277203670387562, + 3.0879607598755188, + 2.527498765500099, + 2.0992156842652023, + 1.785176202044242, + 1.500779127953113, + 1.312603875721246, + 1.1754591026674808, + }; + + CELER_ENSURE(result); + return result; +} + +//---------------------------------------------------------------------------// +// DT cycle rate data //---------------------------------------------------------------------------// /*! * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 0 state. */ -MucfCycleRate dd0_cycle_data() +MucfCycleRate dd_0_cycle_data() { MucfCycleRate result; result.type = CycleTableType::deuterium_deuterium; @@ -133,7 +278,7 @@ MucfCycleRate dd0_cycle_data() /*! * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 0 state. */ -MucfCycleRate dt0_cycle_data() +MucfCycleRate dt_0_cycle_data() { MucfCycleRate result; result.type = CycleTableType::deuterium_tritium; @@ -189,7 +334,7 @@ MucfCycleRate dt0_cycle_data() /*! * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 0 state. */ -MucfCycleRate hd0_cycle_data() +MucfCycleRate hd_0_cycle_data() { MucfCycleRate result; result.type = CycleTableType::protium_deuterium; @@ -217,7 +362,7 @@ MucfCycleRate hd0_cycle_data() /*! * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 1 state. */ -MucfCycleRate dd1_cycle_data() +MucfCycleRate dd_1_cycle_data() { MucfCycleRate result; result.type = CycleTableType::deuterium_deuterium; @@ -269,7 +414,7 @@ MucfCycleRate dd1_cycle_data() /*! * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 1 state. */ -MucfCycleRate dt1_cycle_data() +MucfCycleRate dt_1_cycle_data() { MucfCycleRate result; result.type = CycleTableType::deuterium_tritium; @@ -325,7 +470,7 @@ MucfCycleRate dt1_cycle_data() /*! * Muon-catalyzed fusion cycle rate data for \em dt fusion with F = 1 state. */ -MucfCycleRate hd1_cycle_data() +MucfCycleRate hd_1_cycle_data() { MucfCycleRate result; result.type = CycleTableType::protium_deuterium; @@ -351,40 +496,58 @@ MucfCycleRate hd1_cycle_data() return result; } +//---------------------------------------------------------------------------// +// TT cycle rate data +//---------------------------------------------------------------------------// +/*! + * Muon-catalyzed fusion cycle rate data for \em tt fusion with F = 1/2 state. + * + * The tt fusion cycle rate data is currently a constant value at 2.8e6 1/s. + */ +MucfCycleRate tt_1_over_2_cycle_data() +{ + MucfCycleRate result; + result.type = CycleTableType::tritium_tritium; + result.spin_state = units::HalfSpinInt{1}; // F = 1/2 + + result.rate.interpolation.type = InterpolationType::linear; + // Temperature [K] + result.rate.x = { + 0.0, + std::numeric_limits::max(), + }; + // Mean cycle rate [1/s] + result.rate.y = {2.8e6, 2.8e6}; + + CELER_ENSURE(result); + return result; +} + //---------------------------------------------------------------------------// /*! * Cycle rate data for muon-catalyzed fusion. * - * Data from https://doi.org/10.1007/BF02227621 . + * Data extracted from https://doi.org/10.1007/BF02227621 . * * \todo Use native units. */ std::vector mucf_cycle_rates() { std::vector result; - // DD fusion - { - // F = 1/2 - // F = 3/2 - } - - // DT fusion (requires hd, dd, and dt data for both spin states) - { - // F = 0 - result.push_back(hd0_cycle_data()); - result.push_back(dd0_cycle_data()); - result.push_back(dt0_cycle_data()); - // F = 1 - result.push_back(hd1_cycle_data()); - result.push_back(dd1_cycle_data()); - result.push_back(dt1_cycle_data()); - } - + result.push_back(dd_1_over_2_cycle_data()); // F = 1/2 + result.push_back(dd_3_over_2_cycle_data()); // F = 3/2 + // DT fusion + // F = 0 + result.push_back(hd_0_cycle_data()); + result.push_back(dd_0_cycle_data()); + result.push_back(dt_0_cycle_data()); + // F = 1 + result.push_back(hd_1_cycle_data()); + result.push_back(dd_1_cycle_data()); + result.push_back(dt_1_cycle_data()); // TT fusion - { - // F = 1/2 - } + result.push_back(tt_1_over_2_cycle_data()); // F = 1/2 return result; } diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index d59f0716b1..eab929efcd 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -139,11 +139,23 @@ MucfMaterialInserter::MoleculeCycles MucfMaterialInserter::calc_dd_cycle(EquilibriumArray const& eq_dens, real_type const temperature) { - MoleculeCycles result{0, 0}; + using IsoProt = MucfIsoprotologueMolecule; + using CTT = inp::CycleTableType; + using units::HalfSpinInt; + + auto const& dd_dens = eq_dens[IsoProt::deuterium_deuterium]; - //! \todo Implement + auto const& dd_1_over_2_interpolate + = this->interpolator(CTT::deuterium_deuterium, HalfSpinInt{1}); + auto const& dd_3_over_2_interpolate + = this->interpolator(CTT::deuterium_deuterium, HalfSpinInt{3}); + + MoleculeCycles result; + result[0] = real_type{1} + / (dd_dens * dd_1_over_2_interpolate(temperature)); // F = 1/2 + result[1] = real_type{1} + / (dd_dens * dd_3_over_2_interpolate(temperature)); // F = 3/2 - // Reactive states are F = 1/2 and F = 3/2 CELER_ENSURE(result[0] >= 0 && result[1] >= 0); return result; } @@ -168,20 +180,20 @@ MucfMaterialInserter::calc_dt_cycle(EquilibriumArray const& eq_dens, auto const& dt_dens = eq_dens[IsoProt::deuterium_tritium]; auto const& hd_dens = eq_dens[IsoProt::protium_deuterium]; - auto get = [&](inp::CycleTableType type, units::HalfSpinInt spin) -> auto& { - auto it = interpolators_.find({type, spin}); - CELER_ASSERT(it != interpolators_.end()); - return it->second; - }; - // F = 0 interpolators - auto const& hd0_interpolate = get(CTT::protium_deuterium, HalfSpinInt{0}); - auto const& dd0_interpolate = get(CTT::deuterium_deuterium, HalfSpinInt{0}); - auto const& dt0_interpolate = get(CTT::deuterium_tritium, HalfSpinInt{0}); + auto const& hd0_interpolate + = this->interpolator(CTT::protium_deuterium, HalfSpinInt{0}); + auto const& dd0_interpolate + = this->interpolator(CTT::deuterium_deuterium, HalfSpinInt{0}); + auto const& dt0_interpolate + = this->interpolator(CTT::deuterium_tritium, HalfSpinInt{0}); // F = 1 interpolators - auto const& hd1_interpolate = get(CTT::protium_deuterium, HalfSpinInt{2}); - auto const& dd1_interpolate = get(CTT::deuterium_deuterium, HalfSpinInt{2}); - auto const& dt1_interpolate = get(CTT::deuterium_tritium, HalfSpinInt{2}); + auto const& hd1_interpolate + = this->interpolator(CTT::protium_deuterium, HalfSpinInt{2}); + auto const& dd1_interpolate + = this->interpolator(CTT::deuterium_deuterium, HalfSpinInt{2}); + auto const& dt1_interpolate + = this->interpolator(CTT::deuterium_tritium, HalfSpinInt{2}); // Interpolate over rates, store final cycle time (1/rate) MoleculeCycles result; @@ -208,15 +220,31 @@ MucfMaterialInserter::MoleculeCycles MucfMaterialInserter::calc_tt_cycle(EquilibriumArray const& eq_dens, real_type const temperature) { - MoleculeCycles result{0, 0}; + using IsoProt = MucfIsoprotologueMolecule; + using CTT = inp::CycleTableType; + using units::HalfSpinInt; + + auto const& tt_dens = eq_dens[IsoProt::tritium_tritium]; + auto const& tt_interpolate + = this->interpolator(CTT::tritium_tritium, HalfSpinInt{1}); - //! \todo Implement + MoleculeCycles result; + result[0] = real_type{1} / (tt_dens * tt_interpolate(temperature)); - // Only F = 1/2 is reactive CELER_ENSURE(result[0] >= 0 && result[1] == 0); return result; } +//---------------------------------------------------------------------------// +InterpolatorHelper const& +MucfMaterialInserter::interpolator(inp::CycleTableType type, + units::HalfSpinInt spin) const +{ + auto it = interpolators_.find({type, spin}); + CELER_ASSERT(it != interpolators_.end()); + return it->second; +} + //---------------------------------------------------------------------------// } // namespace detail } // namespace celeritas diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index 931344c165..f09f4bff7e 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -72,6 +72,10 @@ class MucfMaterialInserter // Calculate mean fusion cycle times for tt muonic molecules Array calc_tt_cycle(EquilibriumArray const& eq_dens, real_type const temperature); + + // Get interpolator for given cycle type and spin + InterpolatorHelper const& + interpolator(inp::CycleTableType type, units::HalfSpinInt spin) const; }; //---------------------------------------------------------------------------// diff --git a/test/celeritas/mucf/DTMixMucfModel.test.cc b/test/celeritas/mucf/DTMixMucfModel.test.cc index abfa345fcf..4377018d9b 100644 --- a/test/celeritas/mucf/DTMixMucfModel.test.cc +++ b/test/celeritas/mucf/DTMixMucfModel.test.cc @@ -75,22 +75,31 @@ TEST_F(DTMixMucfModelTest, data) EXPECT_EQ(21, data.muon_energy_cdf.grid.size()); - // In seconds - static double const expected_dt_f0_cycle_time{1.0182824459351898e-08}; - static double const expected_dt_f1_cycle_time{5.098478246172425e-09}; + // Cycle times are in seconds + // DD (reactivity of F = 3/2 is almost negligible, with huge cycle times) + static double const expected_dd_1_over_2_cycle_time{1.8312922823566493e-06}; + static double const expected_dd_3_over_2_cycle_time{1.1439517165483279}; + // DT + static double const expected_dt_0_cycle_time{1.0182824459351898e-08}; + static double const expected_dt_1_cycle_time{5.098478246172425e-09}; + // TT + static double const expected_tt_1_over_2_cycle_time{1.4056833511329384e-06}; auto const& cycles = data.cycle_times; // DD cycle times - EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::deuterium_deuterium][0]); - EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::deuterium_deuterium][1]); + EXPECT_SOFT_EQ(expected_dd_1_over_2_cycle_time, + cycles[MuCfMatId{0}][Molecule::deuterium_deuterium][0]); + EXPECT_SOFT_EQ(expected_dd_3_over_2_cycle_time, + cycles[MuCfMatId{0}][Molecule::deuterium_deuterium][1]); // DT cycle times - EXPECT_SOFT_EQ(expected_dt_f0_cycle_time, + EXPECT_SOFT_EQ(expected_dt_0_cycle_time, cycles[MuCfMatId{0}][Molecule::deuterium_tritium][0]); - EXPECT_SOFT_EQ(expected_dt_f1_cycle_time, + EXPECT_SOFT_EQ(expected_dt_1_cycle_time, cycles[MuCfMatId{0}][Molecule::deuterium_tritium][1]); // TT cycle times - EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::tritium_tritium][0]); + EXPECT_SOFT_EQ(expected_tt_1_over_2_cycle_time, + cycles[MuCfMatId{0}][Molecule::tritium_tritium][0]); EXPECT_SOFT_EQ(0, cycles[MuCfMatId{0}][Molecule::tritium_tritium][1]); } From c91db2ef97e6576e6df1130e43d16a7536050bd7 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Thu, 5 Feb 2026 16:02:31 -0500 Subject: [PATCH 30/35] Add missing triton to device data --- src/celeritas/mucf/data/DTMixMucfData.hh | 13 ++++++++----- src/celeritas/mucf/model/DTMixMucfModel.cc | 3 ++- .../model/detail/EquilibrateDensitiesCalculator.hh | 4 ++-- .../mucf/model/detail/InterpolatorHelper.hh | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/celeritas/mucf/data/DTMixMucfData.hh b/src/celeritas/mucf/data/DTMixMucfData.hh index 954eb2c144..041672bebf 100644 --- a/src/celeritas/mucf/data/DTMixMucfData.hh +++ b/src/celeritas/mucf/data/DTMixMucfData.hh @@ -31,6 +31,7 @@ struct MucfParticleIds //!@{ //! Elementary particles and nuclei ParticleId proton; + ParticleId triton; ParticleId neutron; ParticleId alpha; ParticleId he3; @@ -48,9 +49,9 @@ struct MucfParticleIds //! Check whether all particles are assigned CELER_FUNCTION explicit operator bool() const { - return mu_minus && proton && neutron && alpha && he3 && muonic_hydrogen - && muonic_deuteron && muonic_triton && muonic_alpha - && muonic_he3; + return mu_minus && proton && triton && neutron && alpha && he3 + && muonic_hydrogen && muonic_deuteron && muonic_triton + && muonic_alpha && muonic_he3; } }; @@ -66,6 +67,7 @@ struct MucfParticleMasses //!@{ //! Elementary particles and nuclei units::MevMass proton; + units::MevMass triton; units::MevMass neutron; units::MevMass alpha; units::MevMass he3; @@ -84,8 +86,9 @@ struct MucfParticleMasses CELER_FUNCTION explicit operator bool() const { return mu_minus > zero_quantity() && proton > zero_quantity() - && neutron > zero_quantity() && alpha > zero_quantity() - && he3 > zero_quantity() && muonic_hydrogen > zero_quantity() + && triton > zero_quantity() && neutron > zero_quantity() + && alpha > zero_quantity() && he3 > zero_quantity() + && muonic_hydrogen > zero_quantity() && muonic_deuteron > zero_quantity() && muonic_triton > zero_quantity() && muonic_alpha > zero_quantity() diff --git a/src/celeritas/mucf/model/DTMixMucfModel.cc b/src/celeritas/mucf/model/DTMixMucfModel.cc index a5d3ad9b86..c420f88206 100644 --- a/src/celeritas/mucf/model/DTMixMucfModel.cc +++ b/src/celeritas/mucf/model/DTMixMucfModel.cc @@ -50,8 +50,9 @@ from_params(ParticleParams const& particles) } MP_ADD(mu_minus); - MP_ADD(neutron); MP_ADD(proton); + MP_ADD(neutron); + MP_ADD(triton); MP_ADD(alpha); MP_ADD(he3); MP_ADD(muonic_hydrogen); diff --git a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh index e97fa402dd..99f21aacb7 100644 --- a/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh +++ b/src/celeritas/mucf/model/detail/EquilibrateDensitiesCalculator.hh @@ -16,8 +16,8 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Calculate dt mixture densities after reaching thermodynamical - * equilibrium based on LHD densities and material temperature. + * Calculate dt mixture densities after reaching thermodynamic + * equilibrium based isotopic fraction, density, and material temperature. * * Based on the theory from https://www.osti.gov/biblio/6205719. * diff --git a/src/celeritas/mucf/model/detail/InterpolatorHelper.hh b/src/celeritas/mucf/model/detail/InterpolatorHelper.hh index 68c0bb6127..7f4e496049 100644 --- a/src/celeritas/mucf/model/detail/InterpolatorHelper.hh +++ b/src/celeritas/mucf/model/detail/InterpolatorHelper.hh @@ -19,7 +19,7 @@ namespace detail { //---------------------------------------------------------------------------// /*! - * Helper class for creating interpolators for host-only muCF input data. + * Host-only interpolator wrapper class for muCF input data. * * \sa MucfMaterialInserter */ From 01174d6a2b347154cc77b6cfe174472287dac859 Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Fri, 6 Feb 2026 15:04:39 -0500 Subject: [PATCH 31/35] Update material inserted to populate "new" host/device data --- src/celeritas/mucf/model/detail/MucfMaterialInserter.cc | 8 +++++++- src/celeritas/mucf/model/detail/MucfMaterialInserter.hh | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index eab929efcd..89fe612d8c 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -19,6 +19,7 @@ namespace detail MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, inp::MucfPhysics const& data) : mucfmatid_to_matid_(&host_data->mucfmatid_to_matid) + , isotopic_fractions_(&host_data->isotopic_fractions) , cycle_times_(&host_data->cycle_times) , data_(data) { @@ -53,6 +54,7 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) { using LhdArray = EquilibrateDensitiesCalculator::LhdArray; + MaterialFractionsArray isotopic_fractions; CycleTimesArray cycle_times; LhdArray lhd_densities{}; @@ -81,9 +83,12 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) auto const atom = from_mass_number(iso_view.atomic_mass_number()); CELER_ASSERT(atom < MucfIsotope::size_); + auto const iso_frac = element_view.isotopes()[el_comp].fraction; + // Cache density for this hydrogen isotope + isotopic_fractions[atom] = iso_frac; lhd_densities[atom] - = element_view.isotopes()[el_comp].fraction + = iso_frac * (elem_rel_abundance * material.number_density() / data_.scalars.liquid_hydrogen_density.value()); } @@ -122,6 +127,7 @@ bool MucfMaterialInserter::operator()(MaterialView const& material) // Add muCF material to the model's host/device data mucfmatid_to_matid_.push_back(material.material_id()); + isotopic_fractions_.push_back(std::move(isotopic_fractions)); cycle_times_.push_back(std::move(cycle_times)); //! \todo Store mean atom spin flip and transfer times diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index f09f4bff7e..b459b31c3a 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -40,6 +40,7 @@ class MucfMaterialInserter using MoleculeCycles = Array; using CycleTimesArray = EnumArray; using EquilibriumArray = EquilibrateDensitiesCalculator::EquilibriumArray; + using MaterialFractionsArray = EnumArray; using AtomicMassNumber = AtomicNumber; using InterpolatorsMap = std::map, @@ -49,6 +50,8 @@ class MucfMaterialInserter // DTMixMucfModel host data references populated by operator() CollectionBuilder mucfmatid_to_matid_; + CollectionBuilder + isotopic_fractions_; CollectionBuilder cycle_times_; // Const data std::map const mass_isotope_map_{ From 13ddc2dc629f94862ca012403ad01df3f6e9674b Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Fri, 6 Feb 2026 15:05:55 -0500 Subject: [PATCH 32/35] Update test harnesses with complete model host data --- test/celeritas/mucf/DDMucfInteractor.test.cc | 33 +---- test/celeritas/mucf/DTMucfInteractor.test.cc | 60 +------- .../mucf/MucfInteractorHostTestBase.cc | 138 ++++++++++++++++-- .../mucf/MucfInteractorHostTestBase.hh | 4 + 4 files changed, 132 insertions(+), 103 deletions(-) diff --git a/test/celeritas/mucf/DDMucfInteractor.test.cc b/test/celeritas/mucf/DDMucfInteractor.test.cc index 283534290e..82dab46545 100644 --- a/test/celeritas/mucf/DDMucfInteractor.test.cc +++ b/test/celeritas/mucf/DDMucfInteractor.test.cc @@ -9,7 +9,6 @@ #include "corecel/cont/Range.hh" #include "corecel/math/ArrayUtils.hh" #include "celeritas/Quantities.hh" -#include "celeritas/grid/NonuniformGridBuilder.hh" #include "celeritas/inp/MucfPhysics.hh" #include "MucfInteractorHostTestBase.hh" @@ -33,37 +32,7 @@ class DDMucfInteractorTest : public MucfInteractorHostTestBase protected: void SetUp() override { - auto const& params = *this->particle_params(); - this->set_material("hdt_fuel"); - - HostVal host_data; - - // Set up particle IDs - host_data.particle_ids.mu_minus = params.find(pdg::mu_minus()); - host_data.particle_ids.neutron = params.find(pdg::neutron()); - host_data.particle_ids.proton = params.find(pdg::proton()); - host_data.particle_ids.he3 = params.find(pdg::he3()); - host_data.particle_ids.triton = params.find(pdg::triton()); - host_data.particle_ids.muonic_he3 = params.find(pdg::muonic_he3()); - - // Set up particle masses - host_data.particle_masses.mu_minus - = params.get(host_data.particle_ids.mu_minus).mass(); - host_data.particle_masses.neutron - = params.get(host_data.particle_ids.neutron).mass(); - host_data.particle_masses.proton - = params.get(host_data.particle_ids.proton).mass(); - host_data.particle_masses.he3 - = params.get(host_data.particle_ids.he3).mass(); - host_data.particle_masses.triton - = params.get(host_data.particle_ids.triton).mass(); - host_data.particle_masses.muonic_he3 - = params.get(host_data.particle_ids.muonic_he3).mass(); - - // Set up muon energy CDF - auto const inp_data = inp::MucfPhysics::from_default(); - NonuniformGridBuilder build_grid_record{&host_data.reals}; - host_data.muon_energy_cdf = build_grid_record(inp_data.muon_energy_cdf); + auto host_data = this->make_host_data(); // Construct collection data_ = ParamsDataStore{std::move(host_data)}; diff --git a/test/celeritas/mucf/DTMucfInteractor.test.cc b/test/celeritas/mucf/DTMucfInteractor.test.cc index 4631148a35..986800bb9a 100644 --- a/test/celeritas/mucf/DTMucfInteractor.test.cc +++ b/test/celeritas/mucf/DTMucfInteractor.test.cc @@ -36,65 +36,7 @@ class DTMucfInteractorTest : public MucfInteractorHostTestBase auto const& params = *this->particle_params(); this->set_material("hdt_fuel"); - HostVal host_data; - - // Set up particle IDs - host_data.particle_ids.mu_minus = params.find(pdg::mu_minus()); - host_data.particle_ids.proton = params.find(pdg::proton()); - host_data.particle_ids.neutron = params.find(pdg::neutron()); - host_data.particle_ids.alpha = params.find(pdg::alpha()); - host_data.particle_ids.he3 = params.find(pdg::he3()); - host_data.particle_ids.muonic_hydrogen - = params.find(pdg::muonic_hydrogen()); - host_data.particle_ids.muonic_deuteron - = params.find(pdg::muonic_deuteron()); - host_data.particle_ids.muonic_triton - = params.find(pdg::muonic_triton()); - host_data.particle_ids.muonic_alpha = params.find(pdg::muonic_alpha()); - host_data.particle_ids.muonic_he3 = params.find(pdg::muonic_he3()); - - // Set up particle masses - host_data.particle_masses.mu_minus - = params.get(host_data.particle_ids.mu_minus).mass(); - host_data.particle_masses.proton - = params.get(host_data.particle_ids.proton).mass(); - host_data.particle_masses.neutron - = params.get(host_data.particle_ids.neutron).mass(); - host_data.particle_masses.alpha - = params.get(host_data.particle_ids.alpha).mass(); - host_data.particle_masses.he3 - = params.get(host_data.particle_ids.he3).mass(); - host_data.particle_masses.muonic_hydrogen - = params.get(host_data.particle_ids.muonic_hydrogen).mass(); - host_data.particle_masses.muonic_deuteron - = params.get(host_data.particle_ids.muonic_deuteron).mass(); - host_data.particle_masses.muonic_triton - = params.get(host_data.particle_ids.muonic_triton).mass(); - host_data.particle_masses.muonic_alpha - = params.get(host_data.particle_ids.muonic_alpha).mass(); - host_data.particle_masses.muonic_he3 - = params.get(host_data.particle_ids.muonic_he3).mass(); - - // Set up muon energy CDF - auto const inp_data = inp::MucfPhysics::from_default(); - NonuniformGridBuilder build_grid_record{&host_data.reals}; - host_data.muon_energy_cdf = build_grid_record(inp_data.muon_energy_cdf); - - // Set up dummy cycle data - using CycleTimesArray - = EnumArray>; - CollectionBuilder mucfid( - &host_data.mucfmatid_to_matid); - CollectionBuilder cycles( - &host_data.cycle_times); - - CycleTimesArray cycle_data; - cycle_data[MucfMuonicMolecule::deuterium_deuterium] = {1, 2}; - cycle_data[MucfMuonicMolecule::deuterium_tritium] = {3, 4}; - cycle_data[MucfMuonicMolecule::tritium_tritium] = {5, 6}; - - mucfid.push_back(PhysMatId{0}); - cycles.push_back(std::move(cycle_data)); + auto host_data = this->make_host_data(); // Construct collection data_ = ParamsDataStore{std::move(host_data)}; diff --git a/test/celeritas/mucf/MucfInteractorHostTestBase.cc b/test/celeritas/mucf/MucfInteractorHostTestBase.cc index 1889f4b3be..43cde61501 100644 --- a/test/celeritas/mucf/MucfInteractorHostTestBase.cc +++ b/test/celeritas/mucf/MucfInteractorHostTestBase.cc @@ -7,6 +7,7 @@ #include "MucfInteractorHostTestBase.hh" #include "celeritas/Units.hh" +#include "celeritas/grid/NonuniformGridBuilder.hh" #include "celeritas/inp/MucfPhysics.hh" namespace celeritas @@ -69,27 +70,27 @@ MucfInteractorHostBase::MucfInteractorHostBase() protium_mass, ElementaryCharge{1}, stable_decay_constant}, - {"neutron", - pdg::neutron(), - neutron_mass, - zero_quantity(), - stable_decay_constant}, - {"deuterium", - pdg::deuteron(), - deuterium_mass, - ElementaryCharge{1}, - stable_decay_constant}, {"tritium", pdg::triton(), tritium_mass, ElementaryCharge{1}, native_value_from(tritium_decay_constant)}, + {"neutron", + pdg::neutron(), + neutron_mass, + zero_quantity(), + stable_decay_constant}, {"alpha", pdg::alpha(), alpha_mass, ElementaryCharge{2}, stable_decay_constant}, {"he3", pdg::he3(), he3_mass, ElementaryCharge{2}, stable_decay_constant}, + {"deuterium", + pdg::deuteron(), + deuterium_mass, + ElementaryCharge{1}, + stable_decay_constant}, // Muonic atoms {"muonic_hydrogen", @@ -110,12 +111,12 @@ MucfInteractorHostBase::MucfInteractorHostBase() {"muonic_alpha", pdg::muonic_alpha(), alpha_mass + muon_mass, - ElementaryCharge{1}, + ElementaryCharge{2}, native_value_from(muon_decay_constant)}, {"muonic_he3", pdg::muonic_he3(), he3_mass + muon_mass, - ElementaryCharge{1}, + ElementaryCharge{2}, native_value_from(muon_decay_constant)}, }; this->set_particle_params(std::move(par_inp)); @@ -179,6 +180,119 @@ MucfInteractorHostBase::MucfInteractorHostBase() this->set_material_params(std::move(mat_inp)); } +//---------------------------------------------------------------------------// +/*! + * Return a populated \c DTMixMucfData host data. + */ +HostVal MucfInteractorHostBase::make_host_data() +{ + using AtomicMassNumber = AtomicNumber; + using MaterialFractionsArray = EnumArray; + using MoleculeCycles = Array; + using CycleTimesArray = EnumArray; + + auto const& particles = *this->particle_params(); + this->set_material("hdt_fuel"); + + HostVal host_data; + + // Set up particle IDs + host_data.particle_ids.mu_minus = particles.find(pdg::mu_minus()); + + host_data.particle_ids.proton = particles.find(pdg::proton()); + host_data.particle_ids.triton = particles.find(pdg::triton()); + host_data.particle_ids.neutron = particles.find(pdg::neutron()); + host_data.particle_ids.alpha = particles.find(pdg::alpha()); + host_data.particle_ids.he3 = particles.find(pdg::he3()); + + host_data.particle_ids.muonic_hydrogen + = particles.find(pdg::muonic_hydrogen()); + host_data.particle_ids.muonic_deuteron + = particles.find(pdg::muonic_deuteron()); + host_data.particle_ids.muonic_triton = particles.find(pdg::muonic_triton()); + host_data.particle_ids.muonic_alpha = particles.find(pdg::muonic_alpha()); + host_data.particle_ids.muonic_he3 = particles.find(pdg::muonic_he3()); + + // Set up particle masses + host_data.particle_masses.mu_minus + = particles.get(host_data.particle_ids.mu_minus).mass(); + + host_data.particle_masses.proton + = particles.get(host_data.particle_ids.proton).mass(); + host_data.particle_masses.triton + = particles.get(host_data.particle_ids.triton).mass(); + host_data.particle_masses.neutron + = particles.get(host_data.particle_ids.neutron).mass(); + host_data.particle_masses.alpha + = particles.get(host_data.particle_ids.alpha).mass(); + host_data.particle_masses.he3 + = particles.get(host_data.particle_ids.he3).mass(); + + host_data.particle_masses.muonic_hydrogen + = particles.get(host_data.particle_ids.muonic_hydrogen).mass(); + host_data.particle_masses.muonic_deuteron + = particles.get(host_data.particle_ids.muonic_deuteron).mass(); + host_data.particle_masses.muonic_triton + = particles.get(host_data.particle_ids.muonic_triton).mass(); + host_data.particle_masses.muonic_alpha + = particles.get(host_data.particle_ids.muonic_alpha).mass(); + host_data.particle_masses.muonic_he3 + = particles.get(host_data.particle_ids.muonic_he3).mass(); + + // Set up muon energy CDF + auto const inp_data = inp::MucfPhysics::from_default(); + NonuniformGridBuilder build_grid_record{&host_data.reals}; + host_data.muon_energy_cdf = build_grid_record(inp_data.muon_energy_cdf); + + auto const& material = *this->material_params(); + auto const& el_view = material.get(ElementId{0}); // Only one element + + MaterialFractionsArray iso_fractions_array; + for (auto const& frac : el_view.isotopes()) + { + auto const& iso_view = material.get(frac.isotope); + if (iso_view.atomic_number() != AtomicNumber{1}) + { + // Skip non-hydrogen (if added later to test) + continue; + } + + // Set up isotopic fractions for D and T + + if (iso_view.atomic_mass_number() == AtomicMassNumber{2}) + { + iso_fractions_array[MucfIsotope::deuterium] = frac.fraction; + } + if (iso_view.atomic_mass_number() == AtomicMassNumber{3}) + { + iso_fractions_array[MucfIsotope::tritium] = frac.fraction; + } + } + + // Set up fractions + CollectionBuilder + host_iso_frac(&host_data.isotopic_fractions); + host_iso_frac.push_back(std::move(iso_fractions_array)); + + // Set up mucf material id to physics material id mapping + CollectionBuilder host_matid( + &host_data.mucfmatid_to_matid); + auto const& mat_view = material.get(PhysMatId{0}); // Only one material + host_matid.push_back(mat_view.material_id()); + + // Set up cycle times (numbers from DTMixMucfModel test) + CycleTimesArray ct_array; + ct_array[MucfMuonicMolecule::deuterium_deuterium] = {1.83e-6, 1.14}; + ct_array[MucfMuonicMolecule::deuterium_tritium] = {1.018e-8, 5.098e-9}; + ct_array[MucfMuonicMolecule::deuterium_tritium] = {1.40e-6, 0}; + + CollectionBuilder host_ct( + &host_data.cycle_times); + host_ct.push_back(std::move(ct_array)); + + return host_data; +} + //---------------------------------------------------------------------------// } // namespace test } // namespace celeritas diff --git a/test/celeritas/mucf/MucfInteractorHostTestBase.hh b/test/celeritas/mucf/MucfInteractorHostTestBase.hh index 5160828030..c89af305c3 100644 --- a/test/celeritas/mucf/MucfInteractorHostTestBase.hh +++ b/test/celeritas/mucf/MucfInteractorHostTestBase.hh @@ -6,6 +6,7 @@ //---------------------------------------------------------------------------// #pragma once +#include "celeritas/mucf/data/DTMixMucfData.hh" #include "celeritas/phys/InteractorHostTestBase.hh" namespace celeritas @@ -27,6 +28,9 @@ class MucfInteractorHostBase : public InteractorHostBase MucfInteractorHostBase(); ~MucfInteractorHostBase() = default; //!@} + + // Construct MuCF data from test values for interactors + HostVal make_host_data(); }; class MucfInteractorHostTestBase : public MucfInteractorHostBase, public Test From 3b30371557c77b2425bbeaab76840cb05a5174ab Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Fri, 6 Feb 2026 15:23:35 -0500 Subject: [PATCH 33/35] Update documentation --- doc/_static/zotero.bib | 16 ++++++++++++++++ doc/implementation/mucf-physics.rst | 2 +- src/celeritas/inp/MucfPhysics.cc | 6 ++++-- .../mucf/model/detail/MucfMaterialInserter.cc | 12 ------------ .../mucf/model/detail/MucfMaterialInserter.hh | 11 ++++++++++- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/doc/_static/zotero.bib b/doc/_static/zotero.bib index 837492d408..36c84fa9a9 100644 --- a/doc/_static/zotero.bib +++ b/doc/_static/zotero.bib @@ -3760,3 +3760,19 @@ @article{yamashita-muonicspin-2022 url = {https://www.nature.com/articles/s41598-022-09487-0}, pages = {6393} } + +@article{faifman-mucfformation-1996, + title = {Quadrupole corrections to matrix elements of transitions in resonant reactions of muonic molecule formation}, + author = {Faifman, M. P. and Strizh, T. A. and Armour, E. A. G. and Harston, M. R.}, + year = 1996, + month = dec, + volume = {101-102}, + rights = {http://www.springer.com/tdm}, + issn = {0304-3834, 1572-9540}, + url = {http://link.springer.com/10.1007/BF02227621}, + doi = {10.1007/BF02227621}, + pages = {179--189}, + number = {1}, + journaltitle = {Hyperfine Interactions}, + shortjournal = {Hyperfine Interact}, +} diff --git a/doc/implementation/mucf-physics.rst b/doc/implementation/mucf-physics.rst index ff014d715d..819c263f85 100644 --- a/doc/implementation/mucf-physics.rst +++ b/doc/implementation/mucf-physics.rst @@ -127,7 +127,7 @@ formation, molecule formation, and fusion. .. doxygenclass:: celeritas::DTMixMucfModel Most of the data is material-dependent, being calculated and cached during model -construction. All of the cached quantities are calculated and added to +construction. All the cached quantities are calculated and added to host/device data via :cpp:class:`celeritas::detail::MucfMaterialInserter`. .. doxygenclass:: celeritas::detail::MucfMaterialInserter diff --git a/src/celeritas/inp/MucfPhysics.cc b/src/celeritas/inp/MucfPhysics.cc index 987fd4536d..d201688905 100644 --- a/src/celeritas/inp/MucfPhysics.cc +++ b/src/celeritas/inp/MucfPhysics.cc @@ -16,7 +16,8 @@ namespace /*! * Muon energy CDF data for muon-catalyzed fusion. * - * Data is extracted from https://doi.org/10.1103/PhysRevC.107.034607 . + * Data is extracted from \citet{kamimura-mucf-2023, + * https://doi.org/10.1103/PhysRevC.107.034607}. */ Grid mucf_muon_energy_cdf() { @@ -527,7 +528,8 @@ MucfCycleRate tt_1_over_2_cycle_data() /*! * Cycle rate data for muon-catalyzed fusion. * - * Data extracted from https://doi.org/10.1007/BF02227621 . + * Data is extracted from \citet{faifman-mucfformation-1996, + * https://doi.org/10.1007/BF02227621}. * * \todo Use native units. */ diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc index 89fe612d8c..282ee5ddac 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.cc @@ -37,18 +37,6 @@ MucfMaterialInserter::MucfMaterialInserter(HostVal* host_data, //---------------------------------------------------------------------------// /*! * Insert material information if applicable. - * - * Calculates and caches material-dependent properties needed by the - * \c DTMixMucfModel . If the material does not contain deuterium and/or - * tritium the operator will return false. - * - * * This is designed to work with the user's material definition being either: - * - Single element, multiple isotopes (H element, with H, d, and t isotopes); - * or - * - Multiple elements, single isotope each (separate H, d, and t elements). - * - * The input data stores the cycle \em rate \f$\lambda\f$, while the cached - * data is the cycle \em time \f$\tau = 1/\lambda\f$. */ bool MucfMaterialInserter::operator()(MaterialView const& material) { diff --git a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh index b459b31c3a..77a44496a3 100644 --- a/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh +++ b/src/celeritas/mucf/model/detail/MucfMaterialInserter.hh @@ -24,7 +24,16 @@ namespace detail //---------------------------------------------------------------------------// /*! * Helper class to calculate and insert muCF material-dependent data into - * \c DTMixMucfData . + * \c DTMixMucfData . If the material does not contain deuterium and/or + * tritium the operator will return false. + * + * This is designed to work with the user's material definition being either: + * - Single element, multiple isotopes (H element, with H, d, and t isotopes); + * or + * - Multiple elements, single isotope each (separate H, d, and t elements). + * + * The \c inp:: data has cycle \em rate (\f$\lambda\f$) tables, while the + * host/device cached data is the cycle \em time \f$\tau = 1/\lambda\f$. */ class MucfMaterialInserter { From a5495260a242c77175ade6fc90fe9ee700c0ef8f Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Fri, 6 Feb 2026 15:33:53 -0500 Subject: [PATCH 34/35] Also test isotopic fractions import --- test/celeritas/mucf/DTMixMucfModel.test.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/celeritas/mucf/DTMixMucfModel.test.cc b/test/celeritas/mucf/DTMixMucfModel.test.cc index 4377018d9b..37c3886dfc 100644 --- a/test/celeritas/mucf/DTMixMucfModel.test.cc +++ b/test/celeritas/mucf/DTMixMucfModel.test.cc @@ -75,6 +75,13 @@ TEST_F(DTMixMucfModelTest, data) EXPECT_EQ(21, data.muon_energy_cdf.grid.size()); + // Check sotopic fractions + // Single material with 50/50 d and t fractions + EXPECT_SOFT_EQ( + 0.5, data.isotopic_fractions[MuCfMatId{0}][MucfIsotope::deuterium]); + EXPECT_SOFT_EQ( + 0.5, data.isotopic_fractions[MuCfMatId{0}][MucfIsotope::tritium]); + // Cycle times are in seconds // DD (reactivity of F = 3/2 is almost negligible, with huge cycle times) static double const expected_dd_1_over_2_cycle_time{1.8312922823566493e-06}; From 52f81545d50fcd80b3deae6b97f88149c57434ae Mon Sep 17 00:00:00 2001 From: Stefano Tognini Date: Fri, 6 Feb 2026 15:47:02 -0500 Subject: [PATCH 35/35] Fix tests --- test/celeritas/mucf/DDMucfInteractor.test.cc | 3 +-- test/celeritas/mucf/DTMucfInteractor.test.cc | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/test/celeritas/mucf/DDMucfInteractor.test.cc b/test/celeritas/mucf/DDMucfInteractor.test.cc index 82dab46545..522e656da0 100644 --- a/test/celeritas/mucf/DDMucfInteractor.test.cc +++ b/test/celeritas/mucf/DDMucfInteractor.test.cc @@ -32,9 +32,8 @@ class DDMucfInteractorTest : public MucfInteractorHostTestBase protected: void SetUp() override { - auto host_data = this->make_host_data(); - // Construct collection + auto host_data = this->make_host_data(); data_ = ParamsDataStore{std::move(host_data)}; // At-rest muon primary diff --git a/test/celeritas/mucf/DTMucfInteractor.test.cc b/test/celeritas/mucf/DTMucfInteractor.test.cc index 986800bb9a..994b00cadd 100644 --- a/test/celeritas/mucf/DTMucfInteractor.test.cc +++ b/test/celeritas/mucf/DTMucfInteractor.test.cc @@ -33,12 +33,8 @@ class DTMucfInteractorTest : public MucfInteractorHostTestBase protected: void SetUp() override { - auto const& params = *this->particle_params(); - this->set_material("hdt_fuel"); - - auto host_data = this->make_host_data(); - // Construct collection + auto host_data = this->make_host_data(); data_ = ParamsDataStore{std::move(host_data)}; // At-rest muon primary