File indexing completed on 2025-09-16 08:52:17
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include <cmath>
0010
0011 #include "corecel/Macros.hh"
0012 #include "corecel/Types.hh"
0013 #include "corecel/math/Algorithms.hh"
0014 #include "corecel/random/distribution/BernoulliDistribution.hh"
0015 #include "corecel/random/distribution/RejectionSampler.hh"
0016 #include "celeritas/em/interactor/detail/PhysicsConstants.hh"
0017 #include "celeritas/em/xs/MottRatioCalculator.hh"
0018 #include "celeritas/em/xs/NuclearFormFactors.hh"
0019 #include "celeritas/em/xs/WentzelHelper.hh"
0020 #include "celeritas/mat/IsotopeView.hh"
0021 #include "celeritas/phys/ParticleTrackView.hh"
0022
0023 namespace celeritas
0024 {
0025
0026
0027
0028
0029
0030
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 class WentzelDistribution
0059 {
0060 public:
0061
0062
0063 using MomentumSq = units::MevMomentumSq;
0064 using Energy = units::MevEnergy;
0065 using Mass = units::MevMass;
0066 using MottCoeffMatrix = MottElementData::MottCoeffMatrix;
0067
0068
0069 public:
0070
0071 inline CELER_FUNCTION
0072 WentzelDistribution(NativeCRef<WentzelOKVIData> const& wentzel,
0073 WentzelHelper const& helper,
0074 ParticleTrackView const& particle,
0075 IsotopeView const& target,
0076 ElementId el_id,
0077 real_type cos_thetamin,
0078 real_type cos_thetamax);
0079
0080
0081 template<class Engine>
0082 inline CELER_FUNCTION real_type operator()(Engine& rng) const;
0083
0084 private:
0085
0086
0087
0088 NativeCRef<WentzelOKVIData> const& wentzel_;
0089
0090
0091 WentzelHelper const& helper_;
0092
0093
0094 ParticleTrackView const& particle_;
0095
0096
0097 IsotopeView const& target_;
0098
0099
0100 MottCoeffMatrix const& mott_coeffs_;
0101
0102
0103 real_type cos_thetamin_;
0104
0105
0106 real_type cos_thetamax_;
0107
0108
0109
0110 using InvMomSq = RealQuantity<UnitInverse<units::MevMomentumSq::unit_type>>;
0111
0112
0113
0114
0115 inline CELER_FUNCTION real_type calculate_form_factor(real_type cos_t) const;
0116
0117
0118 inline CELER_FUNCTION real_type nuclear_form_momentum_scale() const;
0119
0120
0121 inline CELER_FUNCTION real_type mom_transfer_sq(real_type cos_t) const;
0122
0123
0124 inline CELER_FUNCTION InvMomSq nuclear_form_prefactor() const;
0125
0126
0127 template<class Engine>
0128 inline CELER_FUNCTION real_type sample_costheta(real_type cos_thetamin,
0129 real_type cos_thetamax,
0130 Engine& rng) const;
0131
0132
0133 inline static CELER_FUNCTION real_type flat_form_factor(real_type x);
0134
0135
0136 static CELER_CONSTEXPR_FUNCTION real_type flat_coeff();
0137 };
0138
0139
0140
0141
0142
0143
0144
0145 CELER_FUNCTION
0146 WentzelDistribution::WentzelDistribution(
0147 NativeCRef<WentzelOKVIData> const& wentzel,
0148 WentzelHelper const& helper,
0149 ParticleTrackView const& particle,
0150 IsotopeView const& target,
0151 ElementId el_id,
0152 real_type cos_thetamin,
0153 real_type cos_thetamax)
0154 : wentzel_(wentzel)
0155 , helper_(helper)
0156 , particle_(particle)
0157 , target_(target)
0158 , mott_coeffs_(particle_.charge() < zero_quantity()
0159 ? wentzel_.mott_coeffs[el_id].electron
0160 : wentzel_.mott_coeffs[el_id].positron)
0161 , cos_thetamin_(cos_thetamin)
0162 , cos_thetamax_(cos_thetamax)
0163 {
0164 CELER_EXPECT(el_id < wentzel_.mott_coeffs.size());
0165 CELER_EXPECT(cos_thetamin_ >= -1 && cos_thetamin_ <= 1);
0166 CELER_EXPECT(cos_thetamax_ >= -1 && cos_thetamax_ <= 1);
0167 CELER_EXPECT(cos_thetamax_ <= cos_thetamin_);
0168 }
0169
0170
0171
0172
0173
0174 template<class Engine>
0175 CELER_FUNCTION real_type WentzelDistribution::operator()(Engine& rng) const
0176 {
0177 real_type cos_theta = 1;
0178 if (BernoulliDistribution(
0179 helper_.calc_xs_electron(cos_thetamin_, cos_thetamax_),
0180 helper_.calc_xs_nuclear(cos_thetamin_, cos_thetamax_))(rng))
0181 {
0182
0183 real_type const cos_thetamax_elec = helper_.cos_thetamax_electron();
0184 real_type cos_thetamin = max(cos_thetamin_, cos_thetamax_elec);
0185 real_type cos_thetamax = max(cos_thetamax_, cos_thetamax_elec);
0186 CELER_ASSERT(cos_thetamin > cos_thetamax);
0187 cos_theta = this->sample_costheta(cos_thetamin, cos_thetamax, rng);
0188 }
0189 else
0190 {
0191
0192 cos_theta = this->sample_costheta(cos_thetamin_, cos_thetamax_, rng);
0193
0194
0195 MottRatioCalculator calc_mott_ratio(mott_coeffs_,
0196 std::sqrt(particle_.beta_sq()));
0197 real_type xs = calc_mott_ratio(cos_theta)
0198 * ipow<2>(this->calculate_form_factor(cos_theta));
0199 if (RejectionSampler(xs, helper_.mott_factor())(rng))
0200 {
0201
0202 cos_theta = 1;
0203 }
0204 }
0205 return cos_theta;
0206 }
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216 CELER_FUNCTION real_type
0217 WentzelDistribution::calculate_form_factor(real_type cos_t) const
0218 {
0219 CELER_EXPECT(cos_t >= -1 && cos_t <= 1);
0220
0221 using MomSq = NuclearFormFactorTraits::MomentumSq;
0222
0223 if (wentzel_.params.form_factor_type == NuclearFormFactorType::none)
0224 {
0225 return 1;
0226 }
0227
0228
0229
0230 auto mt_sq
0231 = MomSq{2 * value_as<MomSq>(particle_.momentum_sq()) * (1 - cos_t)};
0232
0233 auto calc_ff = [mt_sq](auto&& calc_form_factor) {
0234 real_type result = calc_form_factor(mt_sq);
0235 CELER_ENSURE(result >= 0 && result <= 1);
0236 return result;
0237 };
0238
0239 switch (wentzel_.params.form_factor_type)
0240 {
0241 case NuclearFormFactorType::flat:
0242 return calc_ff(UUNuclearFormFactor{target_.atomic_mass_number()});
0243 case NuclearFormFactorType::exponential:
0244 return calc_ff(
0245 ExpNuclearFormFactor{this->nuclear_form_prefactor()});
0246 case NuclearFormFactorType::gaussian:
0247 return calc_ff(
0248 GaussianNuclearFormFactor{this->nuclear_form_prefactor()});
0249 default:
0250 CELER_ASSERT_UNREACHABLE();
0251 }
0252 }
0253
0254
0255
0256
0257
0258
0259
0260 CELER_FUNCTION real_type WentzelDistribution::flat_form_factor(real_type x)
0261 {
0262 return 3 * (std::sin(x) - x * std::cos(x)) / ipow<3>(x);
0263 }
0264
0265
0266
0267
0268
0269
0270
0271
0272 CELER_CONSTEXPR_FUNCTION real_type WentzelDistribution::flat_coeff()
0273 {
0274 return native_value_to<units::MevMomentum>(2 * units::femtometer
0275 / constants::hbar_planck)
0276 .value();
0277 }
0278
0279
0280
0281
0282
0283 CELER_FUNCTION auto
0284 WentzelDistribution::nuclear_form_prefactor() const -> InvMomSq
0285 {
0286 CELER_EXPECT(target_.isotope_id() < wentzel_.nuclear_form_prefactor.size());
0287 return InvMomSq{wentzel_.nuclear_form_prefactor[target_.isotope_id()]};
0288 }
0289
0290
0291
0292
0293
0294 template<class Engine>
0295 CELER_FUNCTION real_type WentzelDistribution::sample_costheta(
0296 real_type cos_thetamin, real_type cos_thetamax, Engine& rng) const
0297 {
0298
0299 real_type const mu1 = real_type{0.5} * (1 - cos_thetamin);
0300 real_type const mu2 = real_type{0.5} * (1 - cos_thetamax);
0301 real_type const w = generate_canonical(rng) * (mu2 - mu1);
0302 real_type const sc = helper_.screening_coefficient();
0303
0304 real_type result = 1 - 2 * mu1 - 2 * (sc + mu1) * w / (sc + mu2 - w);
0305 CELER_ENSURE(result >= -1 && result <= 1);
0306 return result;
0307 }
0308
0309
0310 }