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