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