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