File indexing completed on 2026-05-27 07:23:59
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "detray/definitions/algebra.hpp"
0013 #include "detray/definitions/detail/qualifiers.hpp"
0014 #include "detray/definitions/math.hpp"
0015 #include "detray/definitions/pdg_particle.hpp"
0016 #include "detray/definitions/units.hpp"
0017 #include "detray/material/detail/relativistic_quantities.hpp"
0018
0019 namespace detray {
0020
0021 template <concepts::scalar scalar_t>
0022 struct interaction {
0023 using scalar_type = scalar_t;
0024 using relativistic_quantities = detail::relativistic_quantities<scalar_type>;
0025
0026
0027 DETRAY_HOST_DEVICE scalar_type
0028 compute_stopping_power(const detray::material<scalar_type>& mat,
0029 const pdg_particle<scalar_type>& ptc,
0030 const relativistic_quantities& rq) const {
0031 scalar_type stopping_power{0.f};
0032
0033
0034 stopping_power += compute_bethe_bloch(mat, ptc, rq);
0035
0036
0037 stopping_power += compute_bremsstrahlung(mat, ptc, rq);
0038
0039 return stopping_power;
0040 }
0041
0042 DETRAY_HOST_DEVICE scalar_type
0043 compute_bethe_bloch(const detray::material<scalar_type>& mat,
0044 const pdg_particle<scalar_type>& ,
0045 const relativistic_quantities& rq) const {
0046 const scalar_type eps_per_length{rq.compute_epsilon_per_length(mat)};
0047 if (eps_per_length <= 0.f) {
0048 return 0.f;
0049 }
0050
0051 const scalar_type dhalf{rq.compute_delta_half(mat)};
0052 const scalar_type A = rq.compute_bethe_bloch_log_term(mat);
0053 const scalar_type running{A - rq.m_beta2 - dhalf};
0054 return 2.f * eps_per_length * running;
0055 }
0056
0057
0058
0059
0060
0061
0062 DETRAY_HOST_DEVICE scalar_type
0063 compute_bremsstrahlung(const detray::material<scalar_type>& mat,
0064 const pdg_particle<scalar_type>& ptc,
0065 const relativistic_quantities& rq) const {
0066 scalar_type stopping_power{0.f};
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080 if (ptc.pdg_num() == electron<scalar_type>().pdg_num() ||
0081 ptc.pdg_num() == positron<scalar_type>().pdg_num()) {
0082
0083
0084 stopping_power = rq.m_gamma * ptc.mass() / mat.X0();
0085 }
0086
0087 return stopping_power;
0088 }
0089
0090 DETRAY_HOST_DEVICE scalar_type
0091 derive_stopping_power(const detray::material<scalar_type>& mat,
0092 const pdg_particle<scalar_type>& ptc,
0093 const relativistic_quantities& rq) const {
0094 scalar_type derivative{0.f};
0095
0096
0097 derivative += derive_bethe_bloch(mat, ptc, rq);
0098
0099
0100 derivative += derive_bremsstrahlung(mat, ptc, rq);
0101
0102 return derivative;
0103 }
0104
0105 DETRAY_HOST_DEVICE scalar_type
0106 derive_bethe_bloch(const detray::material<scalar_type>& mat,
0107 const pdg_particle<scalar_type>& ptc,
0108 const relativistic_quantities& rq) const {
0109 const scalar_type bethe_stopping_power = compute_bethe_bloch(mat, ptc, rq);
0110
0111
0112 const scalar_type eps_per_length{rq.compute_epsilon_per_length(mat)};
0113 if (eps_per_length <= 0.f) {
0114 return 0.f;
0115 }
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132
0133
0134
0135
0136 const scalar_type first_term =
0137 2.f / (rq.m_qOverP * rq.m_gamma2) * bethe_stopping_power;
0138
0139 const scalar_type dAdqop = rq.derive_bethe_bloch_log_term();
0140 const scalar_type dBdqop = rq.derive_beta2();
0141 const scalar_type dCdqop = rq.derive_delta_half(mat);
0142
0143 const scalar_type second_term =
0144 2.f * eps_per_length * (dAdqop - dBdqop - dCdqop);
0145
0146 return first_term + second_term;
0147 }
0148
0149 DETRAY_HOST_DEVICE scalar_type
0150 derive_bremsstrahlung(const detray::material<scalar_type>& mat,
0151 const pdg_particle<scalar_type>& ptc,
0152 const relativistic_quantities& rq) const {
0153 scalar_type derivative{0.f};
0154
0155 if (ptc.pdg_num() == electron<scalar_type>().pdg_num() ||
0156 ptc.pdg_num() == positron<scalar_type>().pdg_num()) {
0157
0158
0159
0160 derivative =
0161 -rq.m_beta2 * rq.m_gamma / rq.m_qOverP * ptc.mass() / mat.X0();
0162 }
0163
0164 return derivative;
0165 }
0166
0167 DETRAY_HOST_DEVICE scalar_type compute_energy_loss_bethe_bloch(
0168 const scalar_type path_segment, const detray::material<scalar_type>& mat,
0169 const pdg_particle<scalar_type>& ptc,
0170 const relativistic_quantities& rq) const {
0171 return path_segment * compute_bethe_bloch(mat, ptc, rq);
0172 }
0173
0174 DETRAY_HOST_DEVICE scalar_type compute_energy_loss_landau(
0175 const scalar_type path_segment, const material<scalar_type>& mat,
0176 const pdg_particle<scalar_type>& ,
0177 const relativistic_quantities& rq) const {
0178 const scalar_type I{mat.mean_excitation_energy()};
0179 const scalar_type eps{rq.compute_epsilon(mat, path_segment)};
0180
0181 if (eps <= 0.f) {
0182 return 0.f;
0183 }
0184
0185 const scalar_type dhalf{rq.compute_delta_half(mat)};
0186 const scalar_type t{rq.compute_mass_term(constant<scalar_type>::m_e)};
0187
0188 const scalar_type running{math::log(t / I) + math::log(eps / I) + 0.2f -
0189 rq.m_beta2 - 2.f * dhalf};
0190 return eps * running;
0191 }
0192
0193 DETRAY_HOST_DEVICE scalar_type compute_energy_loss_landau_fwhm(
0194 const scalar_type path_segment, const material<scalar_type>& mat,
0195 const pdg_particle<scalar_type>& ,
0196 const relativistic_quantities& rq) const {
0197
0198 return 4.f * rq.compute_epsilon(mat, path_segment);
0199 }
0200
0201 DETRAY_HOST_DEVICE scalar_type compute_energy_loss_landau_sigma(
0202 const scalar_type path_segment, const material<scalar_type>& mat,
0203 const pdg_particle<scalar_type>& ptc,
0204 const relativistic_quantities& rq) const {
0205 const scalar_type fwhm{
0206 compute_energy_loss_landau_fwhm(path_segment, mat, ptc, rq)};
0207
0208 return convert_landau_fwhm_to_gaussian_sigma(fwhm);
0209 }
0210
0211 template <typename material_t>
0212 DETRAY_HOST_DEVICE scalar_type compute_energy_loss_landau_sigma_QOverP(
0213 const scalar_type path_segment, const material_t& mat,
0214 const pdg_particle<scalar_type>& ptc,
0215 const relativistic_quantities& rq) const {
0216 const scalar_type sigmaE{
0217 compute_energy_loss_landau_sigma(path_segment, mat, ptc, rq)};
0218
0219
0220
0221
0222
0223
0224
0225
0226 const scalar_type pInv{rq.m_qOverP / ptc.charge()};
0227 return math::sqrt(rq.m_q2OverBeta2) * pInv * pInv * sigmaE;
0228 }
0229
0230 DETRAY_HOST_DEVICE scalar_type compute_multiple_scattering_theta0(
0231 const scalar_type xOverX0, const pdg_particle<scalar_type>& ptc,
0232 const relativistic_quantities& rq) const {
0233
0234 const scalar_type momentumInv{math::fabs(rq.m_qOverP / ptc.charge())};
0235
0236
0237
0238
0239 if ((ptc.pdg_num() == 11) || (ptc.pdg_num() == -11)) {
0240
0241
0242 return theta0RossiGreisen(xOverX0, momentumInv, rq.m_q2OverBeta2);
0243 } else {
0244 return theta0Highland(xOverX0, momentumInv, rq.m_q2OverBeta2);
0245 }
0246 }
0247
0248 private:
0249
0250
0251
0252 DETRAY_HOST_DEVICE scalar_type
0253 theta0Highland(const scalar_type xOverX0, const scalar_type momentumInv,
0254 const scalar_type q2OverBeta2) const {
0255 if (xOverX0 <= 0.f) {
0256 return 0.f;
0257 }
0258
0259
0260 const scalar_type t{math::sqrt(xOverX0 * q2OverBeta2)};
0261
0262
0263 return 13.6f * unit<scalar_type>::MeV * momentumInv * t *
0264 (1.0f + 0.038f * 2.f * math::log(t));
0265 }
0266
0267
0268 DETRAY_HOST_DEVICE scalar_type
0269 theta0RossiGreisen(const scalar_type xOverX0, const scalar_type momentumInv,
0270 const scalar_type q2OverBeta2) const {
0271 if (xOverX0 <= 0.f) {
0272 return 0.f;
0273 }
0274
0275
0276 const scalar_type t{math::sqrt(xOverX0 * q2OverBeta2)};
0277 return 17.5f * unit<scalar_type>::MeV * momentumInv * t *
0278 (1.0f + 0.125f * math::log10(10.0f * xOverX0));
0279 }
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290 DETRAY_HOST_DEVICE scalar_type
0291 convert_landau_fwhm_to_gaussian_sigma(const scalar_type fwhm) const {
0292 return 0.5f * constant<scalar_type>::inv_sqrt2 * fwhm /
0293 math::sqrt(constant<scalar_type>::ln2);
0294 }
0295 };
0296
0297 }