File indexing completed on 2025-09-16 08:52:18
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include "corecel/Macros.hh"
0010 #include "corecel/Types.hh"
0011 #include "corecel/math/Algorithms.hh"
0012 #include "corecel/math/PolyEvaluator.hh"
0013 #include "corecel/random/distribution/NormalDistribution.hh"
0014 #include "celeritas/Quantities.hh"
0015 #include "celeritas/Types.hh"
0016 #include "celeritas/em/data/UrbanMscData.hh"
0017 #include "celeritas/phys/Interaction.hh"
0018 #include "celeritas/phys/ParticleTrackView.hh"
0019 #include "celeritas/phys/PhysicsTrackView.hh"
0020
0021 #include "UrbanMscHelper.hh"
0022
0023 namespace celeritas
0024 {
0025 namespace detail
0026 {
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039 class UrbanMscSafetyStepLimit
0040 {
0041 public:
0042
0043
0044 using Energy = units::MevEnergy;
0045 using UrbanMscRef = NativeCRef<UrbanMscData>;
0046
0047
0048 public:
0049
0050 inline CELER_FUNCTION
0051 UrbanMscSafetyStepLimit(UrbanMscRef const& shared,
0052 UrbanMscHelper const& helper,
0053 ParticleTrackView const& particle,
0054 PhysicsTrackView* physics,
0055 PhysMatId matid,
0056 bool on_boundary,
0057 real_type safety,
0058 real_type phys_step);
0059
0060
0061 template<class Engine>
0062 inline CELER_FUNCTION real_type operator()(Engine& rng);
0063
0064 private:
0065
0066
0067
0068 UrbanMscRef const& shared_;
0069
0070 UrbanMscHelper const& helper_;
0071
0072
0073 real_type max_step_{};
0074
0075 real_type limit_min_{};
0076
0077 real_type limit_{};
0078
0079
0080
0081
0082 static CELER_CONSTEXPR_FUNCTION real_type min_range()
0083 {
0084 return 1e-3 * units::centimeter;
0085 }
0086
0087
0088 static CELER_CONSTEXPR_FUNCTION real_type max_step_over_range()
0089 {
0090 return 0.35;
0091 }
0092
0093
0094
0095
0096 inline CELER_FUNCTION real_type calc_limit_min(UrbanMscMaterialData const&,
0097 Energy const) const;
0098 };
0099
0100
0101
0102
0103
0104
0105
0106 CELER_FUNCTION
0107 UrbanMscSafetyStepLimit::UrbanMscSafetyStepLimit(
0108 UrbanMscRef const& shared,
0109 UrbanMscHelper const& helper,
0110 ParticleTrackView const& particle,
0111 PhysicsTrackView* physics,
0112 PhysMatId matid,
0113 bool on_boundary,
0114 real_type safety,
0115 real_type phys_step)
0116 : shared_(shared), helper_(helper), max_step_(phys_step)
0117 {
0118 CELER_EXPECT(safety >= 0);
0119 CELER_EXPECT(safety < helper_.max_step());
0120 CELER_EXPECT(max_step_ > shared_.params.min_step);
0121 CELER_EXPECT(max_step_ <= physics->dedx_range());
0122
0123 bool use_safety_plus = physics->particle_scalars().step_limit_algorithm
0124 == MscStepLimitAlgorithm::safety_plus;
0125 real_type const range = physics->dedx_range();
0126 auto const& msc_range = physics->msc_range();
0127
0128 if (!msc_range || on_boundary)
0129 {
0130 MscRange new_range;
0131
0132 new_range.range_factor = physics->particle_scalars().range_factor;
0133 new_range.range_init = range;
0134
0135
0136
0137 if (!particle.is_heavy())
0138 {
0139 real_type mfp = helper.msc_mfp();
0140 if (!use_safety_plus && mfp > range)
0141 {
0142 new_range.range_init = mfp;
0143 }
0144 if (mfp > physics->scalars().lambda_limit)
0145 {
0146 real_type c = use_safety_plus ? 0.84 : 0.75;
0147 new_range.range_factor
0148 *= c + (1 - c) * mfp / physics->scalars().lambda_limit;
0149 }
0150 }
0151 new_range.limit_min = this->calc_limit_min(
0152 shared_.material_data[matid], particle.energy());
0153
0154
0155 physics->msc_range(new_range);
0156
0157 CELER_ASSERT(msc_range);
0158 }
0159 limit_min_ = msc_range.limit_min;
0160
0161 limit_ = range;
0162 if (safety < range)
0163 {
0164 limit_ = max<real_type>(msc_range.range_factor * msc_range.range_init,
0165 physics->scalars().safety_factor * safety);
0166 }
0167 limit_ = max<real_type>(limit_, limit_min_);
0168
0169 if (use_safety_plus)
0170 {
0171 real_type rho = UrbanMscSafetyStepLimit::min_range();
0172 if (range > rho)
0173 {
0174
0175
0176
0177
0178 real_type alpha = UrbanMscSafetyStepLimit::max_step_over_range();
0179 real_type limit_step = alpha * range
0180 + rho * (1 - alpha) * (2 - rho / range);
0181 max_step_ = min(max_step_, limit_step);
0182 }
0183 }
0184 }
0185
0186
0187
0188
0189
0190 template<class Engine>
0191 CELER_FUNCTION real_type UrbanMscSafetyStepLimit::operator()(Engine& rng)
0192 {
0193
0194
0195 if (max_step_ <= limit_)
0196 {
0197
0198 return max_step_;
0199 }
0200 if (limit_ == limit_min_)
0201 {
0202
0203 return limit_min_;
0204 }
0205
0206
0207 NormalDistribution<real_type> sample_gauss(
0208 limit_, real_type(0.1) * (limit_ - limit_min_));
0209 real_type sampled_limit = sample_gauss(rng);
0210
0211
0212 return clamp(sampled_limit, limit_min_, max_step_);
0213 }
0214
0215
0216
0217
0218
0219
0220
0221 CELER_FUNCTION real_type UrbanMscSafetyStepLimit::calc_limit_min(
0222 UrbanMscMaterialData const& msc, Energy const inc_energy) const
0223 {
0224 using PolyQuad = PolyEvaluator<real_type, 2>;
0225
0226
0227 PolyQuad calc_min_mfp(2, msc.stepmin_coeff[0], msc.stepmin_coeff[1]);
0228 real_type xm = helper_.msc_mfp() / calc_min_mfp(inc_energy.value());
0229
0230
0231 xm *= helper_.scaled_zeff();
0232
0233 if (inc_energy < shared_.params.min_scaling_energy)
0234 {
0235
0236 xm *= (real_type(0.5)
0237 + real_type(0.5) * value_as<Energy>(inc_energy)
0238 / value_as<Energy>(shared_.params.min_scaling_energy));
0239 }
0240
0241 return max<real_type>(xm, shared_.params.min_step);
0242 }
0243
0244
0245 }
0246 }