File indexing completed on 2025-09-19 08:49:41
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include "celeritas/em/data/FluctuationData.hh"
0010 #include "celeritas/em/distribution/EnergyLossHelper.hh"
0011 #include "celeritas/em/distribution/EnergyLossTraits.hh"
0012 #include "celeritas/global/CoreTrackView.hh"
0013 #include "celeritas/phys/PhysicsStepUtils.hh"
0014
0015 namespace celeritas
0016 {
0017 namespace detail
0018 {
0019
0020
0021
0022
0023 class FluctELoss
0024 {
0025 public:
0026
0027
0028 using Energy = EnergyLossHelper::Energy;
0029 using ParamsRef = NativeCRef<FluctuationData>;
0030
0031
0032 public:
0033
0034 inline explicit CELER_FUNCTION FluctELoss(ParamsRef const& params);
0035
0036
0037 inline CELER_FUNCTION bool is_applicable(CoreTrackView const&) const;
0038
0039
0040 inline CELER_FUNCTION Energy calc_eloss(CoreTrackView const& track,
0041 real_type step,
0042 bool apply_cut);
0043
0044
0045 static CELER_CONSTEXPR_FUNCTION bool imprecise_range() { return true; }
0046
0047 private:
0048
0049
0050
0051 ParamsRef const fluct_params_;
0052
0053
0054
0055 template<EnergyLossFluctuationModel M>
0056 inline CELER_FUNCTION Energy
0057 sample_energy_loss(EnergyLossHelper const& helper, RngEngine& rng);
0058 };
0059
0060
0061
0062
0063
0064
0065
0066 CELER_FUNCTION FluctELoss::FluctELoss(ParamsRef const& params)
0067 : fluct_params_{params}
0068 {
0069 CELER_EXPECT(fluct_params_);
0070 }
0071
0072
0073
0074
0075
0076 CELER_FUNCTION bool FluctELoss::is_applicable(CoreTrackView const& track) const
0077 {
0078
0079
0080 if (track.sim().status() == TrackStatus::errored)
0081 return false;
0082
0083
0084 return static_cast<bool>(track.physics().energy_loss_grid());
0085 }
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100 CELER_FUNCTION auto FluctELoss::calc_eloss(CoreTrackView const& track,
0101 real_type step,
0102 bool apply_cut) -> Energy
0103 {
0104 CELER_EXPECT(step > 0);
0105
0106 auto particle = track.particle();
0107 auto phys = track.physics();
0108
0109 if (apply_cut && particle.energy() < phys.particle_scalars().lowest_energy)
0110 {
0111
0112 return particle.energy();
0113 }
0114
0115
0116 auto eloss = calc_mean_energy_loss(particle, phys, step);
0117
0118 if (eloss < particle.energy() && eloss > zero_quantity())
0119 {
0120
0121 auto cutoffs = track.cutoff();
0122 auto material = track.material();
0123
0124 EnergyLossHelper loss_helper(
0125 fluct_params_, cutoffs, material, particle, eloss, step);
0126
0127 auto rng = track.rng();
0128 switch (loss_helper.model())
0129 {
0130 #define ASU_SAMPLE_ELOSS(MODEL) \
0131 case EnergyLossFluctuationModel::MODEL: \
0132 eloss = this->sample_energy_loss<EnergyLossFluctuationModel::MODEL>( \
0133 loss_helper, rng); \
0134 break
0135 ASU_SAMPLE_ELOSS(none);
0136 ASU_SAMPLE_ELOSS(gamma);
0137 ASU_SAMPLE_ELOSS(gaussian);
0138 ASU_SAMPLE_ELOSS(urban);
0139 #undef ASU_SAMPLE_ELOSS
0140 }
0141
0142 if (eloss >= particle.energy())
0143 {
0144
0145
0146
0147 if (apply_cut)
0148 {
0149
0150 eloss = particle.energy();
0151 }
0152 else
0153 {
0154
0155
0156
0157 eloss = loss_helper.mean_loss();
0158 CELER_ASSERT(eloss < particle.energy());
0159 }
0160 }
0161 }
0162
0163 if (apply_cut
0164 && (particle.energy() - eloss <= phys.particle_scalars().lowest_energy))
0165 {
0166
0167 return particle.energy();
0168 }
0169
0170 CELER_ASSERT(eloss <= particle.energy());
0171 CELER_ENSURE(eloss != particle.energy() || apply_cut
0172 || track.sim().post_step_action()
0173 == phys.scalars().range_action());
0174 return eloss;
0175 }
0176
0177
0178 template<EnergyLossFluctuationModel M>
0179 CELER_FUNCTION auto
0180 FluctELoss::sample_energy_loss(EnergyLossHelper const& helper,
0181 RngEngine& rng) -> Energy
0182 {
0183 CELER_EXPECT(helper.model() == M);
0184
0185 using Distribution = typename EnergyLossTraits<M>::type;
0186
0187 Distribution sample_eloss{helper};
0188 return sample_eloss(rng);
0189 }
0190
0191
0192 }
0193 }