File indexing completed on 2025-02-22 10:31:17
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009
0010 #include "corecel/Macros.hh"
0011 #include "corecel/Types.hh"
0012 #include "corecel/data/StackAllocator.hh"
0013 #include "corecel/math/ArrayUtils.hh"
0014 #include "celeritas/Constants.hh"
0015 #include "celeritas/Quantities.hh"
0016 #include "celeritas/em/data/KleinNishinaData.hh"
0017 #include "celeritas/phys/Interaction.hh"
0018 #include "celeritas/phys/InteractionUtils.hh"
0019 #include "celeritas/phys/ParticleTrackView.hh"
0020 #include "celeritas/phys/Secondary.hh"
0021 #include "celeritas/random/distribution/BernoulliDistribution.hh"
0022 #include "celeritas/random/distribution/ReciprocalDistribution.hh"
0023 #include "celeritas/random/distribution/UniformRealDistribution.hh"
0024
0025 namespace celeritas
0026 {
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042 class KleinNishinaInteractor
0043 {
0044 public:
0045
0046 inline CELER_FUNCTION
0047 KleinNishinaInteractor(KleinNishinaData const& shared,
0048 ParticleTrackView const& particle,
0049 Real3 const& inc_direction,
0050 StackAllocator<Secondary>& allocate);
0051
0052
0053 template<class Engine>
0054 inline CELER_FUNCTION Interaction operator()(Engine& rng);
0055
0056
0057 static CELER_CONSTEXPR_FUNCTION units::MevEnergy secondary_cutoff()
0058 {
0059 return units::MevEnergy{1e-4};
0060 }
0061
0062 private:
0063
0064 KleinNishinaData const& shared_;
0065
0066 units::MevEnergy const inc_energy_;
0067
0068 Real3 const& inc_direction_;
0069
0070 StackAllocator<Secondary>& allocate_;
0071 };
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082 CELER_FUNCTION KleinNishinaInteractor::KleinNishinaInteractor(
0083 KleinNishinaData const& shared,
0084 ParticleTrackView const& particle,
0085 Real3 const& inc_direction,
0086 StackAllocator<Secondary>& allocate)
0087 : shared_(shared)
0088 , inc_energy_(particle.energy())
0089 , inc_direction_(inc_direction)
0090 , allocate_(allocate)
0091 {
0092 CELER_EXPECT(particle.particle_id() == shared_.ids.gamma);
0093 CELER_EXPECT(inc_energy_ > zero_quantity());
0094 }
0095
0096
0097
0098
0099
0100
0101
0102
0103 template<class Engine>
0104 CELER_FUNCTION Interaction KleinNishinaInteractor::operator()(Engine& rng)
0105 {
0106 using Energy = units::MevEnergy;
0107
0108
0109 Secondary* electron_secondary = allocate_(1);
0110 if (electron_secondary == nullptr)
0111 {
0112
0113 return Interaction::from_failure();
0114 }
0115
0116
0117 real_type const inc_energy_per_mecsq = value_as<Energy>(inc_energy_)
0118 * shared_.inv_electron_mass;
0119 real_type const epsilon_0 = 1 / (1 + 2 * inc_energy_per_mecsq);
0120
0121
0122 BernoulliDistribution choose_f1(-std::log(epsilon_0),
0123 real_type(0.5) * (1 - ipow<2>(epsilon_0)));
0124
0125 ReciprocalDistribution<real_type> sample_f1(epsilon_0);
0126
0127 UniformRealDistribution<real_type> sample_f2_sq(ipow<2>(epsilon_0), 1);
0128
0129
0130 real_type epsilon;
0131 real_type one_minus_costheta;
0132
0133 real_type reject_prob;
0134 do
0135 {
0136
0137 real_type epsilon_sq;
0138 if (choose_f1(rng))
0139 {
0140
0141
0142 epsilon = sample_f1(rng);
0143 epsilon_sq = epsilon * epsilon;
0144 }
0145 else
0146 {
0147
0148 epsilon_sq = sample_f2_sq(rng);
0149 epsilon = std::sqrt(epsilon_sq);
0150 }
0151 CELER_ASSERT(epsilon >= epsilon_0 && epsilon <= 1);
0152
0153
0154 one_minus_costheta = (1 - epsilon) / (epsilon * inc_energy_per_mecsq);
0155 CELER_ASSERT(one_minus_costheta >= 0 && one_minus_costheta <= 2);
0156 real_type sintheta_sq = one_minus_costheta * (2 - one_minus_costheta);
0157 reject_prob = epsilon * sintheta_sq / (1 + epsilon_sq);
0158 } while (BernoulliDistribution(reject_prob)(rng));
0159
0160
0161 Interaction result;
0162 result.energy = Energy{epsilon * inc_energy_.value()};
0163 result.direction = inc_direction_;
0164 result.secondaries = {electron_secondary, 1};
0165
0166
0167 result.direction = ExitingDirectionSampler{1 - one_minus_costheta,
0168 result.direction}(rng);
0169
0170
0171 electron_secondary->energy
0172 = Energy{inc_energy_.value() - result.energy.value()};
0173
0174
0175 if (electron_secondary->energy < KleinNishinaInteractor::secondary_cutoff())
0176 {
0177 result.energy_deposition = electron_secondary->energy;
0178 *electron_secondary = {};
0179 return result;
0180 }
0181
0182
0183 electron_secondary->particle_id = shared_.ids.electron;
0184
0185 electron_secondary->direction
0186 = calc_exiting_direction({inc_energy_.value(), inc_direction_},
0187 {result.energy.value(), result.direction});
0188
0189 return result;
0190 }
0191
0192
0193 }