File indexing completed on 2026-06-06 08:05:06
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/PdgParticle.hpp"
0012 #include "Acts/Material/Interactions.hpp"
0013 #include "ActsFatras/EventData/Particle.hpp"
0014
0015 #include <numbers>
0016 #include <random>
0017
0018 namespace ActsFatras::detail {
0019
0020
0021
0022
0023
0024
0025
0026
0027 struct GeneralMixture {
0028
0029 bool log_include = true;
0030
0031 double genMixtureScalor = 1.;
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041 template <typename generator_t>
0042 double operator()(generator_t &generator, const Acts::MaterialSlab &slab,
0043 Particle &particle) const {
0044 double theta = 0.0;
0045
0046 if (particle.absolutePdg() != Acts::PdgParticle::eElectron) {
0047
0048
0049
0050
0051
0052
0053 std::array<double, 4> scattering_params{};
0054
0055
0056
0057
0058 double mOverP = particle.mass() / particle.absoluteMomentum();
0059 double beta2Inv = 1 + mOverP * mOverP;
0060 double beta = 1 / std::sqrt(beta2Inv);
0061 double tInX0 = slab.thicknessInX0();
0062 double tob2 = tInX0 * beta2Inv;
0063 if (tob2 > 0.6 / std::pow(slab.material().Z(), 0.6)) {
0064
0065 if (tob2 > 10) {
0066 scattering_params = getGaussian(beta, particle.absoluteMomentum(),
0067 tInX0, genMixtureScalor);
0068 } else {
0069 scattering_params =
0070 getGaussmix(beta, particle.absoluteMomentum(), tInX0,
0071 slab.material().Z(), genMixtureScalor);
0072 }
0073
0074 theta = gaussmix(generator, scattering_params);
0075 } else {
0076
0077 auto scattering_params_sg =
0078 getSemigauss(beta, particle.absoluteMomentum(), tInX0,
0079 slab.material().Z(), genMixtureScalor);
0080
0081 theta = semigauss(generator, scattering_params_sg);
0082 }
0083 } else {
0084
0085
0086 const auto theta0 = Acts::computeMultipleScatteringTheta0(
0087 slab, particle.absolutePdg(), particle.mass(), particle.qOverP(),
0088 particle.absoluteCharge());
0089 theta = std::normal_distribution<double>(0.0, theta0)(generator);
0090 }
0091
0092 return std::numbers::sqrt2 * theta;
0093 }
0094
0095
0096
0097 std::array<double, 4> getGaussian(double beta, double p, double tInX0,
0098 double scale) const {
0099 std::array<double, 4> scattering_params{};
0100
0101 scattering_params[0] = 15. / beta / p * std::sqrt(tInX0) * scale;
0102 scattering_params[1] = 1.0;
0103 scattering_params[2] = 1.0;
0104 scattering_params[3] = 0.5;
0105 return scattering_params;
0106 }
0107
0108 std::array<double, 4> getGaussmix(double beta, double p, double tInX0,
0109 double Z, double scale) const {
0110 std::array<double, 4> scattering_params{};
0111 scattering_params[0] = 15. / beta / p * std::sqrt(tInX0) *
0112 scale;
0113 double d1 = std::log(tInX0 / (beta * beta));
0114 double d2 = std::log(std::pow(Z, 2.0 / 3.0) * tInX0 / (beta * beta));
0115 double epsi = 0;
0116 double var1 = (-1.843e-3 * d1 + 3.347e-2) * d1 + 8.471e-1;
0117
0118 if (d2 < 0.5) {
0119 epsi = (6.096e-4 * d2 + 6.348e-3) * d2 + 4.841e-2;
0120 } else {
0121 epsi = (-5.729e-3 * d2 + 1.106e-1) * d2 - 1.908e-2;
0122 }
0123 scattering_params[1] = var1;
0124 scattering_params[2] = (1 - (1 - epsi) * var1) / epsi;
0125 scattering_params[3] = epsi;
0126 return scattering_params;
0127 }
0128
0129 std::array<double, 6> getSemigauss(double beta, double p, double tInX0,
0130 double Z, double scale) const {
0131 std::array<double, 6> scattering_params{};
0132 double N = tInX0 * 1.587E7 * std::pow(Z, 1.0 / 3.0) / (beta * beta) /
0133 (Z + 1) / std::log(287 / std::sqrt(Z));
0134 scattering_params[4] = 15. / beta / p * std::sqrt(tInX0) *
0135 scale;
0136 double rho = 41000 / std::pow(Z, 2.0 / 3.0);
0137 double b = rho / std::sqrt(N * (std::log(rho) - 0.5));
0138 double n = std::pow(Z, 0.1) * std::log(N);
0139 double var1 = (5.783E-4 * n + 3.803E-2) * n + 1.827E-1;
0140 double a =
0141 (((-4.590E-5 * n + 1.330E-3) * n - 1.355E-2) * n + 9.828E-2) * n +
0142 2.822E-1;
0143 double epsi = (1 - var1) / (a * a * (std::log(b / a) - 0.5) - var1);
0144 scattering_params[3] =
0145 (epsi > 0) ? epsi : 0.0;
0146 scattering_params[0] = a;
0147 scattering_params[1] = b;
0148 scattering_params[2] = var1;
0149 scattering_params[5] = N;
0150 return scattering_params;
0151 }
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161 template <typename generator_t>
0162 double gaussmix(generator_t &generator,
0163 const std::array<double, 4> &scattering_params) const {
0164 std::uniform_real_distribution<double> udist(0.0, 1.0);
0165 double sigma_tot = scattering_params[0];
0166 double var1 = scattering_params[1];
0167 double var2 = scattering_params[2];
0168 double epsi = scattering_params[3];
0169 bool ind = udist(generator) > epsi;
0170 double u = udist(generator);
0171 if (ind) {
0172 return std::sqrt(var1) * std::sqrt(-2 * std::log(u)) * sigma_tot;
0173 } else {
0174 return std::sqrt(var2) * std::sqrt(-2 * std::log(u)) * sigma_tot;
0175 }
0176 }
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186 template <typename generator_t>
0187 double semigauss(generator_t &generator,
0188 const std::array<double, 6> &scattering_params) const {
0189 std::uniform_real_distribution<double> udist(0.0, 1.0);
0190 double a = scattering_params[0];
0191 double b = scattering_params[1];
0192 double var1 = scattering_params[2];
0193 double epsi = scattering_params[3];
0194 double sigma_tot = scattering_params[4];
0195 bool ind = udist(generator) > epsi;
0196 double u = udist(generator);
0197 if (ind) {
0198 return std::sqrt(var1) * std::sqrt(-2 * std::log(u)) * sigma_tot;
0199 } else {
0200 return a * b * std::sqrt((1 - u) / (u * b * b + a * a)) * sigma_tot;
0201 }
0202 }
0203 };
0204
0205 }