File indexing completed on 2025-09-18 09:08:59
0001
0002
0003
0004
0005
0006
0007 #pragma once
0008
0009 #include "corecel/Macros.hh"
0010 #include "corecel/Types.hh"
0011 #include "corecel/data/Collection.hh"
0012 #include "corecel/math/Algorithms.hh"
0013 #include "corecel/math/ArrayUtils.hh"
0014 #include "corecel/random/distribution/GenerateCanonical.hh"
0015 #include "corecel/random/distribution/Selector.hh"
0016 #include "geocel/random/IsotropicDistribution.hh"
0017 #include "celeritas/Quantities.hh"
0018 #include "celeritas/Types.hh"
0019 #include "celeritas/em/data/RayleighData.hh"
0020 #include "celeritas/phys/Interaction.hh"
0021 #include "celeritas/phys/InteractionUtils.hh"
0022 #include "celeritas/phys/ParticleTrackView.hh"
0023
0024 namespace celeritas
0025 {
0026
0027
0028
0029
0030
0031
0032
0033
0034 class RayleighInteractor
0035 {
0036 public:
0037
0038 inline CELER_FUNCTION RayleighInteractor(RayleighRef const& shared,
0039 ParticleTrackView const& particle,
0040 Real3 const& inc_direction,
0041 ElementId element_id);
0042
0043
0044 template<class Engine>
0045 inline CELER_FUNCTION Interaction operator()(Engine& rng);
0046
0047 private:
0048
0049
0050
0051 RayleighRef const& shared_;
0052
0053 units::MevEnergy const inc_energy_;
0054
0055 Real3 const& inc_direction_;
0056
0057 ElementId element_id_;
0058
0059
0060
0061
0062 static CELER_CONSTEXPR_FUNCTION real_type fit_slice() { return 0.02; }
0063
0064
0065
0066
0067 struct SampleInput
0068 {
0069 real_type factor{0};
0070 Real3 weight{0, 0, 0};
0071 Real3 prob{0, 0, 0};
0072 };
0073
0074
0075
0076
0077 inline CELER_FUNCTION auto evaluate_weight_and_prob() const -> SampleInput;
0078 };
0079
0080
0081
0082
0083
0084
0085
0086 CELER_FUNCTION
0087 RayleighInteractor::RayleighInteractor(RayleighRef const& shared,
0088 ParticleTrackView const& particle,
0089 Real3 const& direction,
0090 ElementId el_id)
0091
0092 : shared_(shared)
0093 , inc_energy_(particle.energy())
0094 , inc_direction_(direction)
0095 , element_id_(el_id)
0096 {
0097 CELER_EXPECT(particle.particle_id() == shared_.gamma);
0098 CELER_EXPECT(element_id_ < shared_.params.size());
0099 }
0100
0101
0102
0103
0104
0105
0106 template<class Engine>
0107 CELER_FUNCTION Interaction RayleighInteractor::operator()(Engine& rng)
0108 {
0109
0110 Interaction result;
0111 result.energy = inc_energy_;
0112
0113 SampleInput input = this->evaluate_weight_and_prob();
0114
0115 Real3 const& pb = shared_.params[element_id_].b;
0116 Real3 const& pn = shared_.params[element_id_].n;
0117
0118 constexpr real_type half = 0.5;
0119 real_type cost;
0120
0121 do
0122 {
0123
0124 auto const index = celeritas::make_selector(
0125 [&input](size_type i) { return input.prob[i]; },
0126 input.prob.size())(rng);
0127
0128 real_type const w = input.weight[index];
0129 real_type const ninv = 1 / pn[index];
0130 real_type const b = pb[index];
0131
0132
0133 real_type x;
0134 real_type y = w * generate_canonical(rng);
0135
0136 if (y < fit_slice())
0137 {
0138 x = y * ninv
0139 * (1 + half * (ninv + 1) * y * (1 - (ninv + 2) * y / 3));
0140 }
0141 else
0142 {
0143 x = fastpow(1 - y, -ninv) - 1;
0144 }
0145
0146 cost = 1 - 2 * x / (b * input.factor);
0147
0148 } while (2 * generate_canonical(rng) > 1 + ipow<2>(cost) || cost < -1);
0149
0150
0151 result.direction = ExitingDirectionSampler{cost, inc_direction_}(rng);
0152
0153 CELER_ENSURE(result.action == Interaction::Action::scattered);
0154 return result;
0155 }
0156
0157 CELER_FUNCTION
0158 auto RayleighInteractor::evaluate_weight_and_prob() const -> SampleInput
0159 {
0160 Real3 const& a = shared_.params[element_id_].a;
0161 Real3 const& b = shared_.params[element_id_].b;
0162 Real3 const& n = shared_.params[element_id_].n;
0163
0164 SampleInput input;
0165 input.factor = ipow<2>(units::centimeter
0166 / (constants::c_light * constants::h_planck)
0167 * native_value_from(inc_energy_));
0168
0169 Real3 x = b;
0170 axpy(input.factor, b, &x);
0171
0172 Real3 prob;
0173 for (int i = 0; i < 3; ++i)
0174 {
0175 input.weight[i] = (x[i] > this->fit_slice())
0176 ? 1 - fastpow(1 + x[i], -n[i])
0177 : n[i] * x[i]
0178 * (1
0179 - (n[i] - 1) / 2 * x[i]
0180 * (1 - (n[i] - 2) / 3 * x[i]));
0181
0182 prob[i] = input.weight[i] * a[i] / (b[i] * n[i]);
0183 }
0184
0185 real_type inv_sum = 1 / (prob[0] + prob[1] + prob[2]);
0186 axpy(inv_sum, prob, &input.prob);
0187
0188 return input;
0189 }
0190
0191
0192 }