File indexing completed on 2025-02-22 10:31:25
0001
0002
0003
0004
0005
0006
0007
0008 #pragma once
0009
0010 #include "corecel/Macros.hh"
0011 #include "corecel/Types.hh"
0012 #include "corecel/grid/NonuniformGrid.hh"
0013 #include "corecel/grid/TwodGridCalculator.hh"
0014 #include "corecel/grid/TwodGridData.hh"
0015 #include "corecel/math/ArrayOperators.hh"
0016 #include "corecel/math/ArrayUtils.hh"
0017 #include "celeritas/Quantities.hh"
0018 #include "celeritas/Types.hh"
0019 #include "celeritas/phys/FourVector.hh"
0020 #include "celeritas/random/distribution/GenerateCanonical.hh"
0021 #include "celeritas/random/distribution/UniformRealDistribution.hh"
0022
0023 #include "CascadeParticle.hh"
0024
0025 namespace celeritas
0026 {
0027 namespace detail
0028 {
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 class CascadeCollider
0045 {
0046 public:
0047
0048
0049 using FinalState = Array<CascadeParticle, 2>;
0050 using MevMass = units::MevMass;
0051
0052
0053 public:
0054
0055 inline CELER_FUNCTION CascadeCollider(NeutronInelasticRef const& shared,
0056 CascadeParticle const& bullet,
0057 CascadeParticle const& target);
0058
0059
0060 template<class Engine>
0061 inline CELER_FUNCTION FinalState operator()(Engine& rng);
0062
0063 private:
0064
0065
0066 using Grid = NonuniformGrid<real_type>;
0067 using UniformRealDist = UniformRealDistribution<real_type>;
0068
0069
0070
0071
0072 NeutronInelasticRef const& shared_;
0073
0074 CascadeParticle const& bullet_;
0075 CascadeParticle const& target_;
0076
0077
0078 ChannelId ch_id_;
0079
0080 Real3 cm_velocity_;
0081
0082 real_type cm_p_;
0083
0084 real_type kin_energy_;
0085
0086
0087 UniformRealDist sample_phi_;
0088
0089
0090
0091
0092 static CELER_CONSTEXPR_FUNCTION real_type epsilon()
0093 {
0094 return real_type{1e-10};
0095 }
0096
0097
0098
0099
0100 inline CELER_FUNCTION real_type calc_cm_p(FourVector const& v) const;
0101 };
0102
0103
0104
0105
0106
0107
0108
0109 CELER_FUNCTION
0110 CascadeCollider::CascadeCollider(NeutronInelasticRef const& shared,
0111 CascadeParticle const& bullet,
0112 CascadeParticle const& target)
0113 : shared_(shared)
0114 , bullet_(bullet)
0115 , target_(target)
0116 , ch_id_(ChannelId{
0117 static_cast<size_type>((bullet_.type == target_.type) ? 0 : 1)})
0118 , sample_phi_(0, 2 * constants::pi)
0119 {
0120
0121 FourVector sum_four_vec = bullet_.four_vec + target_.four_vec;
0122 cm_velocity_ = boost_vector(sum_four_vec);
0123 cm_p_ = this->calc_cm_p(sum_four_vec);
0124
0125
0126 FourVector bullet_p = bullet_.four_vec;
0127 boost(-boost_vector(target_.four_vec), &bullet_p);
0128 kin_energy_ = bullet_p.energy - norm(bullet_p);
0129 }
0130
0131
0132
0133
0134
0135 template<class Engine>
0136 CELER_FUNCTION auto CascadeCollider::operator()(Engine& rng) -> FinalState
0137 {
0138
0139 real_type cdf = generate_canonical(rng);
0140 TwodGridData const& cdf_grid = shared_.angular_cdf[ch_id_];
0141 Grid energy_grid(cdf_grid.x, shared_.reals);
0142 real_type cos_theta{0};
0143
0144 if (kin_energy_ < energy_grid.back())
0145 {
0146
0147 Grid cos_grid(cdf_grid.y, shared_.reals);
0148 TwodGridCalculator calc_cdf(cdf_grid, shared_.reals);
0149
0150 size_type idx = cos_grid.size() - 2;
0151 real_type cdf_upper = 0;
0152 real_type cdf_lower = 1;
0153
0154 do
0155 {
0156 cdf_upper = cdf_lower;
0157 cdf_lower = calc_cdf({kin_energy_, cos_grid[idx]});
0158 } while (cdf_lower > cdf && idx-- > 0);
0159
0160 real_type frac = (cdf - cdf_lower) / (cdf_upper - cdf_lower);
0161 cos_theta = fma(frac, cos_grid[idx + 1] - cos_grid[idx], cos_grid[idx]);
0162 }
0163 else
0164 {
0165
0166 real_type slope = 2 * energy_grid.back() * ipow<2>(cm_p_);
0167 cos_theta = std::log(1 + cdf * std::expm1(2 * slope)) / slope - 1;
0168 }
0169
0170
0171 Real3 mom = cm_p_ * from_spherical(cos_theta, sample_phi_(rng));
0172
0173
0174 FourVector fv = {mom,
0175 std::sqrt(dot_product(mom, mom)
0176 + ipow<2>(value_as<MevMass>(bullet_.mass)))};
0177
0178
0179 FinalState result = {bullet_, target_};
0180
0181 FourVector cm_momentum = target_.four_vec;
0182 boost(-cm_velocity_, &cm_momentum);
0183
0184 Real3 cm_dir = make_unit_vector(-cm_momentum.mom);
0185 real_type vel_parallel = dot_product(cm_velocity_, cm_dir);
0186
0187 Real3 vscm = cm_velocity_;
0188 axpy(-vel_parallel, cm_dir, &vscm);
0189
0190 if (norm(vscm) > this->epsilon())
0191 {
0192 vscm = make_unit_vector(vscm);
0193 Real3 vxcm = make_unit_vector(cross_product(cm_dir, cm_velocity_));
0194 if (norm(vxcm) > this->epsilon())
0195 {
0196 for (int i = 0; i < 3; ++i)
0197 {
0198 result[0].four_vec.mom[i] = fv.mom[0] * vscm[i]
0199 + fv.mom[1] * vxcm[i]
0200 + fv.mom[2] * cm_dir[i];
0201 }
0202 result[0].four_vec.energy = fv.energy;
0203 }
0204 }
0205 else
0206 {
0207
0208 result[0].four_vec = fv;
0209 }
0210
0211 result[1].four_vec
0212 = {{-result[0].four_vec.mom},
0213 std::sqrt(dot_product(mom, mom)
0214 + ipow<2>(value_as<MevMass>(target_.mass)))};
0215
0216
0217 for (auto i : range(2))
0218 {
0219 boost(cm_velocity_, &result[i].four_vec);
0220 }
0221
0222 return result;
0223 }
0224
0225
0226
0227
0228
0229
0230 CELER_FUNCTION real_type CascadeCollider::calc_cm_p(FourVector const& v) const
0231 {
0232
0233 real_type m0 = norm(v);
0234
0235 real_type m1 = value_as<MevMass>(bullet_.mass);
0236 real_type m2 = value_as<MevMass>(target_.mass);
0237
0238 real_type pc_sq = diffsq(m0, m1 - m2) * diffsq(m0, m1 + m2);
0239
0240 return std::sqrt(clamp_to_nonneg(pc_sq)) / (2 * m0);
0241 }
0242
0243
0244 }
0245 }