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