File indexing completed on 2025-12-15 10:10:52
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include "corecel/Macros.hh"
0010 #include "corecel/Types.hh"
0011 #include "corecel/data/StackAllocator.hh"
0012 #include "corecel/math/ArrayOperators.hh"
0013 #include "corecel/math/ArrayUtils.hh"
0014 #include "corecel/random/distribution/BernoulliDistribution.hh"
0015 #include "corecel/random/distribution/ReciprocalDistribution.hh"
0016 #include "geocel/random/IsotropicDistribution.hh"
0017 #include "celeritas/Quantities.hh"
0018 #include "celeritas/em/data/EPlusGGData.hh"
0019 #include "celeritas/phys/Interaction.hh"
0020 #include "celeritas/phys/InteractionUtils.hh"
0021 #include "celeritas/phys/ParticleTrackView.hh"
0022 #include "celeritas/phys/Secondary.hh"
0023
0024 namespace celeritas
0025 {
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040 class EPlusGGInteractor
0041 {
0042 public:
0043
0044
0045 using Mass = units::MevMass;
0046 using Energy = units::MevEnergy;
0047
0048
0049 public:
0050
0051 inline CELER_FUNCTION
0052 EPlusGGInteractor(EPlusGGData const& shared,
0053 ParticleTrackView const& particle,
0054 Real3 const& inc_direction,
0055 StackAllocator<Secondary>& allocate);
0056
0057
0058 template<class Engine>
0059 inline CELER_FUNCTION Interaction operator()(Engine& rng);
0060
0061 private:
0062
0063 EPlusGGData const& shared_;
0064
0065 real_type const inc_energy_;
0066
0067 Real3 const& inc_direction_;
0068
0069 StackAllocator<Secondary>& allocate_;
0070 };
0071
0072
0073
0074
0075
0076
0077
0078 CELER_FUNCTION
0079 EPlusGGInteractor::EPlusGGInteractor(EPlusGGData const& shared,
0080 ParticleTrackView const& particle,
0081 Real3 const& inc_direction,
0082 StackAllocator<Secondary>& allocate)
0083 : shared_(shared)
0084 , inc_energy_(value_as<Energy>(particle.energy()))
0085 , inc_direction_(inc_direction)
0086 , allocate_(allocate)
0087 {
0088 CELER_EXPECT(particle.particle_id() == shared_.positron);
0089 }
0090
0091
0092
0093
0094
0095
0096
0097 template<class Engine>
0098 CELER_FUNCTION Interaction EPlusGGInteractor::operator()(Engine& rng)
0099 {
0100
0101 Secondary* secondaries = allocate_(2);
0102 if (secondaries == nullptr)
0103 {
0104
0105 return Interaction::from_failure();
0106 }
0107
0108
0109 Interaction result = Interaction::from_absorption();
0110 result.secondaries = {secondaries, 2};
0111
0112
0113 secondaries[0].particle_id = secondaries[1].particle_id = shared_.gamma;
0114
0115 if (inc_energy_ == 0)
0116 {
0117
0118 secondaries[0].energy = secondaries[1].energy
0119 = Energy{value_as<Mass>(shared_.electron_mass)};
0120
0121 IsotropicDistribution<real_type> gamma_dir;
0122 secondaries[0].direction = gamma_dir(rng);
0123 secondaries[1].direction = -secondaries[0].direction;
0124 }
0125 else
0126 {
0127 constexpr real_type half = 0.5;
0128 real_type const tau = inc_energy_
0129 / value_as<Mass>(shared_.electron_mass);
0130 real_type const tau2 = tau + 2;
0131 real_type const sqgrate = std::sqrt(tau / tau2) * half;
0132
0133
0134 ReciprocalDistribution<real_type> sample_eps(half - sqgrate,
0135 half + sqgrate);
0136
0137
0138 real_type epsil;
0139 do
0140 {
0141 epsil = sample_eps(rng);
0142 } while (BernoulliDistribution(
0143 epsil - (2 * (tau + 1) * epsil - 1) / (epsil * ipow<2>(tau2)))(rng));
0144
0145
0146 real_type const cost = (epsil * tau2 - 1)
0147 / (epsil * std::sqrt(tau * tau2));
0148 CELER_ASSERT(std::fabs(cost) <= 1);
0149
0150
0151 real_type const total_energy
0152 = inc_energy_ + 2 * value_as<Mass>(shared_.electron_mass);
0153 real_type const gamma_energy = epsil * total_energy;
0154 real_type const eplus_moment = std::sqrt(inc_energy_ * total_energy);
0155
0156
0157 secondaries[0].energy = Energy{gamma_energy};
0158 secondaries[0].direction
0159 = ExitingDirectionSampler{cost, inc_direction_}(rng);
0160 secondaries[1].energy = Energy{total_energy - gamma_energy};
0161 secondaries[1].direction = calc_exiting_direction(
0162 {eplus_moment, inc_direction_}, {inc_energy_, inc_direction_});
0163 }
0164
0165 return result;
0166 }
0167
0168
0169 }