File indexing completed on 2025-09-16 08:52:16
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/InverseSquareDistribution.hh"
0015 #include "corecel/random/distribution/RejectionSampler.hh"
0016 #include "celeritas/Quantities.hh"
0017 #include "celeritas/phys/ParticleTrackView.hh"
0018
0019 #include "detail/Utils.hh"
0020
0021 namespace celeritas
0022 {
0023
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 class MuBBEnergyDistribution
0049 {
0050 public:
0051
0052
0053 using Energy = units::MevEnergy;
0054 using Mass = units::MevMass;
0055
0056
0057 public:
0058
0059 inline CELER_FUNCTION
0060 MuBBEnergyDistribution(ParticleTrackView const& particle,
0061 Energy electron_cutoff,
0062 Mass electron_mass);
0063
0064
0065 template<class Engine>
0066 inline CELER_FUNCTION Energy operator()(Engine& rng);
0067
0068
0069 CELER_FUNCTION Energy min_secondary_energy() const { return min_energy_; }
0070
0071
0072 CELER_FUNCTION Energy max_secondary_energy() const { return max_energy_; }
0073
0074 private:
0075
0076
0077
0078 real_type inc_mass_;
0079
0080 real_type total_energy_;
0081
0082 real_type beta_sq_;
0083
0084 real_type electron_mass_;
0085
0086 Energy min_energy_;
0087
0088 Energy max_energy_;
0089
0090 bool use_rad_correction_;
0091
0092 real_type envelope_;
0093
0094
0095
0096
0097 static CELER_CONSTEXPR_FUNCTION Energy rad_correction_limit()
0098 {
0099 return units::MevEnergy{250};
0100 }
0101
0102
0103 static CELER_CONSTEXPR_FUNCTION Energy kin_energy_limit()
0104 {
0105
0106
0107 return units::MevEnergy{0.1};
0108 }
0109
0110
0111 static CELER_CONSTEXPR_FUNCTION Constant alpha_over_twopi()
0112 {
0113 return constants::alpha_fine_structure / (2 * constants::pi);
0114 }
0115
0116
0117
0118 inline CELER_FUNCTION real_type calc_envelope_distribution() const;
0119 };
0120
0121
0122
0123
0124
0125
0126
0127 CELER_FUNCTION
0128 MuBBEnergyDistribution::MuBBEnergyDistribution(ParticleTrackView const& particle,
0129 Energy electron_cutoff,
0130 Mass electron_mass)
0131 : inc_mass_(value_as<Mass>(particle.mass()))
0132 , total_energy_(value_as<Energy>(particle.total_energy()))
0133 , beta_sq_(particle.beta_sq())
0134 , electron_mass_(value_as<Mass>(electron_mass))
0135 , min_energy_(electron_cutoff)
0136 , max_energy_(detail::calc_max_secondary_energy(particle, electron_mass))
0137 , use_rad_correction_(particle.energy() > rad_correction_limit()
0138 && max_energy_ > kin_energy_limit())
0139 , envelope_(this->calc_envelope_distribution())
0140 {
0141 }
0142
0143
0144
0145
0146
0147 template<class Engine>
0148 CELER_FUNCTION auto MuBBEnergyDistribution::operator()(Engine& rng) -> Energy
0149 {
0150 InverseSquareDistribution sample_energy(value_as<Energy>(min_energy_),
0151 value_as<Energy>(max_energy_));
0152 real_type energy;
0153 real_type target;
0154 do
0155 {
0156 energy = sample_energy(rng);
0157 target = 1 - (beta_sq_ / value_as<Energy>(max_energy_)) * energy
0158 + real_type(0.5) * ipow<2>(energy / total_energy_);
0159
0160 if (use_rad_correction_
0161 && energy > value_as<Energy>(kin_energy_limit()))
0162 {
0163 real_type a1 = std::log(1 + 2 * energy / electron_mass_);
0164 real_type a3 = std::log(4 * total_energy_ * (total_energy_ - energy)
0165 / ipow<2>(inc_mass_));
0166 target *= (1 + alpha_over_twopi() * a1 * (a3 - a1));
0167 }
0168 } while (RejectionSampler<>(target, envelope_)(rng));
0169
0170 CELER_ENSURE(energy > 0);
0171 return Energy{energy};
0172 }
0173
0174
0175
0176
0177
0178 CELER_FUNCTION real_type MuBBEnergyDistribution::calc_envelope_distribution() const
0179 {
0180 if (use_rad_correction_)
0181 {
0182 return 1
0183 + alpha_over_twopi()
0184 * ipow<2>(std::log(2 * total_energy_ / inc_mass_));
0185 }
0186 return 1;
0187 }
0188
0189
0190 }