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