File indexing completed on 2025-02-22 10:31:16
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 "celeritas/Constants.hh"
0015 #include "celeritas/Quantities.hh"
0016 #include "celeritas/em/data/FluctuationData.hh"
0017 #include "celeritas/mat/MaterialTrackView.hh"
0018 #include "celeritas/phys/CutoffView.hh"
0019 #include "celeritas/phys/ParticleTrackView.hh"
0020
0021 namespace celeritas
0022 {
0023
0024
0025 enum class EnergyLossFluctuationModel
0026 {
0027 none,
0028 gamma,
0029 gaussian,
0030 urban,
0031 };
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063 class EnergyLossHelper
0064 {
0065 public:
0066
0067
0068 using FluctuationRef = NativeCRef<FluctuationData>;
0069 using Energy = units::MevEnergy;
0070 using EnergySq = Quantity<UnitProduct<units::Mev, units::Mev>>;
0071 using Mass = units::MevMass;
0072 using Charge = units::ElementaryCharge;
0073 using Model = EnergyLossFluctuationModel;
0074 using Real2 = Array<real_type, 2>;
0075
0076
0077 public:
0078
0079 inline CELER_FUNCTION EnergyLossHelper(FluctuationRef const& shared,
0080 CutoffView const& cutoffs,
0081 MaterialTrackView const& material,
0082 ParticleTrackView const& particle,
0083 Energy mean_loss,
0084 real_type step_length);
0085
0086
0087
0088
0089 static CELER_CONSTEXPR_FUNCTION Energy ionization_energy()
0090 {
0091 return units::MevEnergy{1e-5};
0092 }
0093
0094
0095
0096
0097 CELER_FORCEINLINE_FUNCTION FluctuationRef const& shared() const
0098 {
0099 return shared_;
0100 }
0101
0102
0103 CELER_FORCEINLINE_FUNCTION MaterialTrackView const& material() const
0104 {
0105 return material_;
0106 }
0107
0108
0109 CELER_FORCEINLINE_FUNCTION Model model() const { return model_; }
0110
0111
0112 CELER_FUNCTION Energy mean_loss() const { return Energy{mean_loss_}; }
0113
0114
0115 CELER_FUNCTION Energy max_energy() const
0116 {
0117 CELER_EXPECT(model_ != Model::none);
0118 return Energy{max_energy_};
0119 }
0120
0121
0122 CELER_FUNCTION real_type beta_sq() const
0123 {
0124 CELER_ENSURE(beta_sq_ > 0);
0125 return beta_sq_;
0126 }
0127
0128
0129 CELER_FUNCTION EnergySq bohr_variance() const
0130 {
0131 CELER_EXPECT(model_ != Model::none);
0132 CELER_ENSURE(bohr_var_ > 0);
0133 return EnergySq{bohr_var_};
0134 }
0135
0136
0137 CELER_FUNCTION Mass two_mebsgs() const
0138 {
0139 CELER_ENSURE(two_mebsgs_ > 0);
0140 return Mass{two_mebsgs_};
0141 }
0142
0143 private:
0144
0145
0146
0147 FluctuationRef const& shared_;
0148
0149 MaterialTrackView const& material_;
0150
0151 Model model_{Model::none};
0152
0153 real_type mean_loss_{0};
0154
0155
0156 real_type beta_sq_{0};
0157
0158 real_type max_energy_{0};
0159
0160 real_type two_mebsgs_{0};
0161
0162
0163 real_type bohr_var_{0};
0164
0165
0166
0167
0168 static CELER_CONSTEXPR_FUNCTION size_type min_kappa() { return 10; }
0169
0170
0171 static CELER_CONSTEXPR_FUNCTION real_type min_energy()
0172 {
0173 return value_as<Energy>(EnergyLossHelper::ionization_energy());
0174 }
0175 };
0176
0177
0178
0179
0180
0181
0182
0183 CELER_FUNCTION
0184 EnergyLossHelper::EnergyLossHelper(FluctuationRef const& shared,
0185 CutoffView const& cutoffs,
0186 MaterialTrackView const& material,
0187 ParticleTrackView const& particle,
0188 Energy mean_loss,
0189 real_type step_length)
0190 : shared_(shared)
0191 , material_(material)
0192 , mean_loss_(value_as<Energy>(mean_loss))
0193 {
0194 CELER_EXPECT(shared_);
0195 CELER_EXPECT(mean_loss_ > 0);
0196 CELER_EXPECT(step_length > 0);
0197
0198 if (mean_loss_ < this->min_energy())
0199 {
0200 model_ = Model::none;
0201 return;
0202 }
0203
0204 constexpr real_type half = 0.5;
0205 real_type const gamma = particle.lorentz_factor();
0206 beta_sq_ = particle.beta_sq();
0207 two_mebsgs_ = 2 * value_as<Mass>(shared_.electron_mass) * beta_sq_
0208 * ipow<2>(gamma);
0209
0210
0211 real_type max_energy_transfer;
0212 real_type mass_ratio = 1;
0213 if (particle.particle_id() == shared_.electron_id)
0214 {
0215 max_energy_transfer = half * value_as<Energy>(particle.energy());
0216 }
0217 else
0218 {
0219 mass_ratio = value_as<Mass>(shared_.electron_mass)
0220 / value_as<Mass>(particle.mass());
0221 max_energy_transfer = two_mebsgs_
0222 / (1 + mass_ratio * (2 * gamma + mass_ratio));
0223 }
0224 max_energy_ = min(value_as<Energy>(cutoffs.energy(shared_.electron_id)),
0225 max_energy_transfer);
0226
0227 if (max_energy_ <= value_as<Energy>(this->ionization_energy()))
0228 {
0229 model_ = Model::none;
0230 return;
0231 }
0232
0233
0234
0235 bohr_var_ = 2 * constants::pi * ipow<2>(constants::r_electron)
0236 * value_as<Mass>(shared_.electron_mass)
0237 * material_.make_material_view().electron_density()
0238 * ipow<2>(value_as<Charge>(particle.charge())) * max_energy_
0239 * step_length * (1 / beta_sq_ - half);
0240
0241 if (mass_ratio >= 1 || mean_loss_ < this->min_kappa() * max_energy_
0242 || max_energy_transfer > 2 * max_energy_)
0243 {
0244 model_ = Model::urban;
0245 }
0246 else if (ipow<2>(mean_loss_) >= 4 * bohr_var_)
0247 {
0248 model_ = Model::gaussian;
0249 }
0250 else
0251 {
0252 model_ = Model::gamma;
0253 }
0254 }
0255
0256
0257 }