File indexing completed on 2025-02-22 10:31:20
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009
0010 #include "corecel/Macros.hh"
0011 #include "corecel/Types.hh"
0012 #include "corecel/math/Algorithms.hh"
0013 #include "celeritas/em/data/CommonCoulombData.hh"
0014 #include "celeritas/em/data/WentzelOKVIData.hh"
0015 #include "celeritas/mat/MaterialView.hh"
0016 #include "celeritas/phys/AtomicNumber.hh"
0017 #include "celeritas/phys/ParticleTrackView.hh"
0018
0019 namespace celeritas
0020 {
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032 class WentzelHelper
0033 {
0034 public:
0035
0036
0037 using Charge = units::ElementaryCharge;
0038 using Energy = units::MevEnergy;
0039 using Mass = units::MevMass;
0040 using MomentumSq = units::MevMomentumSq;
0041
0042
0043 public:
0044
0045 inline CELER_FUNCTION
0046 WentzelHelper(ParticleTrackView const& particle,
0047 MaterialView const& material,
0048 AtomicNumber target_z,
0049 NativeCRef<WentzelOKVIData> const& wentzel,
0050 CoulombIds const& ids,
0051 Energy cutoff);
0052
0053
0054 CELER_FUNCTION AtomicNumber atomic_number() const { return target_z_; }
0055
0056
0057 CELER_FUNCTION real_type screening_coefficient() const
0058 {
0059 return screening_coefficient_;
0060 }
0061
0062
0063 CELER_FUNCTION real_type mott_factor() const { return mott_factor_; }
0064
0065
0066 CELER_FUNCTION real_type kin_factor() const { return kin_factor_; }
0067
0068
0069 CELER_FUNCTION real_type cos_thetamax_electron() const
0070 {
0071 return cos_thetamax_elec_;
0072 }
0073
0074
0075 CELER_FUNCTION real_type cos_thetamax_nuclear() const
0076 {
0077 return cos_thetamax_nuc_;
0078 }
0079
0080
0081 inline CELER_FUNCTION real_type
0082 calc_xs_electron(real_type cos_thetamin, real_type cos_thetamax) const;
0083
0084
0085 inline CELER_FUNCTION real_type
0086 calc_xs_nuclear(real_type cos_thetamin, real_type cos_thetamax) const;
0087
0088 private:
0089
0090
0091 AtomicNumber const target_z_;
0092 real_type screening_coefficient_;
0093 real_type kin_factor_;
0094 real_type mott_factor_;
0095 real_type cos_thetamax_elec_;
0096 real_type cos_thetamax_nuc_;
0097
0098
0099
0100
0101 inline CELER_FUNCTION real_type
0102 calc_screening_coefficient(ParticleTrackView const& particle) const;
0103
0104
0105 CELER_CONSTEXPR_FUNCTION real_type screen_r_sq_elec() const;
0106
0107
0108 inline CELER_FUNCTION real_type
0109 calc_kin_factor(ParticleTrackView const&) const;
0110
0111
0112 inline CELER_FUNCTION real_type calc_cos_thetamax_electron(
0113 ParticleTrackView const&, CoulombIds const&, Energy) const;
0114
0115
0116 inline CELER_FUNCTION real_type calc_cos_thetamax_nuclear(
0117 ParticleTrackView const&,
0118 MaterialView const& material,
0119 NativeCRef<WentzelOKVIData> const& wentzel) const;
0120
0121
0122 inline CELER_FUNCTION real_type calc_xs_factor(real_type cos_thetamin,
0123 real_type cos_thetamax) const;
0124 };
0125
0126
0127
0128
0129
0130
0131
0132 CELER_FUNCTION
0133 WentzelHelper::WentzelHelper(ParticleTrackView const& particle,
0134 MaterialView const& material,
0135 AtomicNumber target_z,
0136 NativeCRef<WentzelOKVIData> const& wentzel,
0137 CoulombIds const& ids,
0138 Energy cutoff)
0139 : target_z_(target_z)
0140 , screening_coefficient_(this->calc_screening_coefficient(particle)
0141 * wentzel.params.screening_factor)
0142 , kin_factor_(this->calc_kin_factor(particle))
0143 , mott_factor_(particle.particle_id() == ids.electron
0144 ? 1 + real_type(2e-4) * ipow<2>(target_z_.get())
0145 : 1)
0146 , cos_thetamax_elec_(
0147 this->calc_cos_thetamax_electron(particle, ids, cutoff))
0148 , cos_thetamax_nuc_(
0149 this->calc_cos_thetamax_nuclear(particle, material, wentzel))
0150 {
0151 CELER_EXPECT(screening_coefficient_ > 0);
0152 CELER_EXPECT(cos_thetamax_elec_ >= -1 && cos_thetamax_elec_ <= 1);
0153 CELER_EXPECT(cos_thetamax_nuc_ >= -1 && cos_thetamax_nuc_ <= 1);
0154 }
0155
0156
0157
0158
0159
0160 CELER_FUNCTION real_type WentzelHelper::calc_xs_electron(
0161 real_type cos_thetamin, real_type cos_thetamax) const
0162 {
0163 cos_thetamin = max(cos_thetamin, cos_thetamax_elec_);
0164 cos_thetamax = max(cos_thetamax, cos_thetamax_elec_);
0165 if (cos_thetamin <= cos_thetamax)
0166 {
0167 return 0;
0168 }
0169 return this->calc_xs_factor(cos_thetamin, cos_thetamax);
0170 }
0171
0172
0173
0174
0175
0176 CELER_FUNCTION real_type WentzelHelper::calc_xs_nuclear(
0177 real_type cos_thetamin, real_type cos_thetamax) const
0178 {
0179 return target_z_.get() * this->calc_xs_factor(cos_thetamin, cos_thetamax);
0180 }
0181
0182
0183
0184
0185
0186 CELER_FUNCTION real_type WentzelHelper::calc_xs_factor(
0187 real_type cos_thetamin, real_type cos_thetamax) const
0188 {
0189 return kin_factor_ * mott_factor_ * (cos_thetamin - cos_thetamax)
0190 / ((1 - cos_thetamin + 2 * screening_coefficient_)
0191 * (1 - cos_thetamax + 2 * screening_coefficient_));
0192 }
0193
0194
0195
0196
0197
0198
0199
0200 CELER_FUNCTION real_type WentzelHelper::calc_screening_coefficient(
0201 ParticleTrackView const& particle) const
0202 {
0203
0204 real_type correction = 1;
0205 real_type sq_cbrt_z = fastpow(real_type(target_z_.get()), real_type{2} / 3);
0206 if (target_z_.get() > 1)
0207 {
0208 real_type tau = value_as<Energy>(particle.energy())
0209 / value_as<Mass>(particle.mass());
0210
0211 real_type factor = std::sqrt(tau / (tau + sq_cbrt_z));
0212
0213 correction = min(target_z_.get() * real_type{1.13},
0214 real_type{1.13}
0215 + real_type{3.76}
0216 * ipow<2>(target_z_.get()
0217 * constants::alpha_fine_structure)
0218 * factor / particle.beta_sq());
0219 }
0220
0221 return correction * this->screen_r_sq_elec() * sq_cbrt_z
0222 / value_as<MomentumSq>(particle.momentum_sq());
0223 }
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240 CELER_CONSTEXPR_FUNCTION real_type WentzelHelper::screen_r_sq_elec() const
0241 {
0242
0243 constexpr real_type ctf = 0.8853413770001135;
0244
0245 return native_value_to<MomentumSq>(
0246 ipow<2>(constants::hbar_planck / (2 * ctf * constants::a0_bohr)))
0247 .value();
0248 }
0249
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260
0261
0262 CELER_FUNCTION real_type
0263 WentzelHelper::calc_kin_factor(ParticleTrackView const& particle) const
0264 {
0265 real_type constexpr twopi_mrsq
0266 = 2 * constants::pi
0267 * ipow<2>(native_value_to<Mass>(constants::electron_mass).value()
0268 * constants::r_electron);
0269
0270 return twopi_mrsq * target_z_.get()
0271 * ipow<2>(value_as<Charge>(particle.charge()))
0272 / (particle.beta_sq()
0273 * value_as<MomentumSq>(particle.momentum_sq()));
0274 }
0275
0276
0277
0278
0279
0280
0281
0282
0283 CELER_FUNCTION real_type WentzelHelper::calc_cos_thetamax_electron(
0284 ParticleTrackView const& particle, CoulombIds const& ids, Energy cutoff) const
0285 {
0286 real_type inc_energy = value_as<Energy>(particle.energy());
0287 real_type mass = value_as<Mass>(particle.mass());
0288
0289 real_type max_energy = particle.particle_id() == ids.electron
0290 ? real_type{0.5} * inc_energy
0291 : inc_energy;
0292 real_type final_energy = inc_energy
0293 - min(value_as<Energy>(cutoff), max_energy);
0294
0295 if (final_energy > 0)
0296 {
0297 real_type incident_ratio = 1 + 2 * mass / inc_energy;
0298 real_type final_ratio = 1 + 2 * mass / final_energy;
0299 real_type cos_t_max = std::sqrt(incident_ratio / final_ratio);
0300
0301 return clamp(cos_t_max, real_type{0}, real_type{1});
0302 }
0303 return 0;
0304 }
0305
0306
0307
0308
0309
0310 CELER_FUNCTION real_type WentzelHelper::calc_cos_thetamax_nuclear(
0311 ParticleTrackView const& particle,
0312 MaterialView const& material,
0313 NativeCRef<WentzelOKVIData> const& wentzel) const
0314 {
0315 if (wentzel.params.is_combined)
0316 {
0317 CELER_ASSERT(material.material_id() < wentzel.inv_mass_cbrt_sq.size());
0318 return max(wentzel.params.costheta_limit,
0319 1
0320 - wentzel.params.a_sq_factor
0321 * wentzel.inv_mass_cbrt_sq[material.material_id()]
0322 / value_as<MomentumSq>(particle.momentum_sq()));
0323 }
0324 return wentzel.params.costheta_limit;
0325 }
0326
0327
0328 }