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/Algebra.hpp"
0012 #include "Acts/Definitions/Common.hpp"
0013 #include "Acts/Definitions/PdgParticle.hpp"
0014 #include "Acts/Definitions/Units.hpp"
0015 #include "Acts/Material/MaterialSlab.hpp"
0016 #include "Acts/Utilities/UnitVectors.hpp"
0017 #include "ActsFatras/EventData/Barcode.hpp"
0018 #include "ActsFatras/EventData/Particle.hpp"
0019 #include "ActsFatras/EventData/ProcessType.hpp"
0020
0021 #include <algorithm>
0022 #include <array>
0023 #include <cmath>
0024 #include <limits>
0025 #include <numbers>
0026 #include <random>
0027 #include <utility>
0028 #include <vector>
0029
0030 namespace ActsFatras {
0031
0032
0033
0034
0035 class PhotonConversion {
0036 public:
0037
0038 double childEnergyScaleFactor = 2.;
0039
0040 double conversionProbScaleFactor = 0.98;
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050 template <typename generator_t>
0051 std::pair<double, double> generatePathLimits(generator_t& generator,
0052 const Particle& particle) const;
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062 template <typename generator_t>
0063 bool run(generator_t& generator, Particle& particle,
0064 std::vector<Particle>& generated) const;
0065
0066 private:
0067
0068
0069
0070
0071
0072
0073
0074 std::array<Particle, 2> generateChildren(
0075 const Particle& photon, double childEnergy,
0076 const Acts::Vector3& childDirection) const;
0077
0078
0079
0080
0081
0082
0083
0084
0085 template <typename generator_t>
0086 double generateFirstChildEnergyFraction(generator_t& generator,
0087 double gammaMom) const;
0088
0089
0090
0091
0092
0093
0094
0095
0096 template <typename generator_t>
0097 Acts::Vector3 generateChildDirection(generator_t& generator,
0098 const Particle& particle) const;
0099
0100
0101
0102
0103 double screenFunction1(double delta) const;
0104 double screenFunction2(double delta) const;
0105
0106
0107
0108 static const double kElectronMass;
0109 };
0110
0111 inline double PhotonConversion::screenFunction1(double delta) const {
0112
0113 return (delta > 1.4) ? 42.038 - 8.29 * std::log(delta + 0.958)
0114 : 42.184 - delta * (7.444 - 1.623 * delta);
0115 }
0116
0117 inline double PhotonConversion::screenFunction2(double delta) const {
0118
0119
0120 return (delta > 1.4) ? 42.038 - 8.29 * std::log(delta + 0.958)
0121 : 41.326 - delta * (5.848 - 0.902 * delta);
0122 }
0123
0124 template <typename generator_t>
0125 std::pair<double, double> PhotonConversion::generatePathLimits(
0126 generator_t& generator, const Particle& particle) const {
0127
0128
0129
0130 if (particle.pdg() != Acts::PdgParticle::eGamma ||
0131 particle.absoluteMomentum() < (2 * kElectronMass)) {
0132 return {std::numeric_limits<double>::infinity(),
0133 std::numeric_limits<double>::infinity()};
0134 }
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149 constexpr double p0 = -7.01612e-03;
0150 constexpr double p1 = 7.69040e-02;
0151 constexpr double p2 = -6.07682e-01;
0152
0153
0154 const double xi = p0 + p1 * std::pow(particle.absoluteMomentum(), p2);
0155
0156 std::uniform_real_distribution<double> uniformDistribution{0., 1.};
0157
0158 return {-9. / 7. *
0159 std::log(conversionProbScaleFactor *
0160 (1 - uniformDistribution(generator))) /
0161 (1. - xi),
0162 std::numeric_limits<double>::infinity()};
0163 }
0164
0165 template <typename generator_t>
0166 double PhotonConversion::generateFirstChildEnergyFraction(
0167 generator_t& generator, double gammaMom) const {
0168
0169
0170
0171
0172
0173 constexpr double k1 = 0.0083;
0174 constexpr double k2 = 0.20206;
0175 constexpr double k3 = 0.0020;
0176 constexpr double k4 = 0.0369;
0177 constexpr double alphaEM = 1. / 137.;
0178 constexpr double m_Z = 13.;
0179 constexpr double az2 = (alphaEM * m_Z) * (alphaEM * m_Z);
0180 constexpr double az4 = az2 * az2;
0181 constexpr double coulombFactor =
0182 (k1 * az4 + k2 + 1. / (1. + az2)) * az2 - (k3 * az4 + k4) * az4;
0183
0184 const double logZ13 = std::log(m_Z) * 1. / 3.;
0185 const double FZ = 8. * (logZ13 + coulombFactor);
0186 const double deltaMax = std::exp((42.038 - FZ) * 0.1206) - 0.958;
0187
0188 const double deltaPreFactor = 136. / std::pow(m_Z, 1. / 3.);
0189 const double eps0 = kElectronMass / gammaMom;
0190 const double deltaFactor = deltaPreFactor * eps0;
0191 const double deltaMin = 4. * deltaFactor;
0192
0193
0194 const double epsMin =
0195 std::max(eps0, 0.5 - 0.5 * std::sqrt(1. - deltaMin / deltaMax));
0196 const double epsRange = 0.5 - epsMin;
0197
0198
0199 const double F10 = screenFunction1(deltaMin) - FZ;
0200 const double F20 = screenFunction2(deltaMin) - FZ;
0201 const double NormF1 = F10 * epsRange * epsRange;
0202 const double NormF2 = 1.5 * F20;
0203
0204
0205 double greject = 0.;
0206 double eps = 0.;
0207 std::uniform_real_distribution<double> rndmEngine;
0208 do {
0209 if (NormF1 > rndmEngine(generator) * (NormF1 + NormF2)) {
0210 eps = 0.5 - epsRange * std::pow(rndmEngine(generator), 1. / 3.);
0211 const double delta = deltaFactor / (eps * (1. - eps));
0212 greject = (screenFunction1(delta) - FZ) / F10;
0213 } else {
0214 eps = epsMin + epsRange * rndmEngine(generator);
0215 const double delta = deltaFactor / (eps * (1. - eps));
0216 greject = (screenFunction2(delta) - FZ) / F20;
0217 }
0218 } while (greject < rndmEngine(generator));
0219
0220 return eps * childEnergyScaleFactor;
0221 }
0222
0223 template <typename generator_t>
0224 Acts::Vector3 PhotonConversion::generateChildDirection(
0225 generator_t& generator, const Particle& particle) const {
0226
0227
0228
0229
0230 double theta = kElectronMass / particle.energy();
0231
0232 std::uniform_real_distribution<double> uniformDistribution{0., 1.};
0233 const double u = -std::log(uniformDistribution(generator) *
0234 uniformDistribution(generator)) *
0235 1.6;
0236
0237 theta *= (uniformDistribution(generator) < 0.25)
0238 ? u
0239 : u * 1. / 3.;
0240
0241
0242 const auto psi = std::uniform_real_distribution<double>(
0243 -std::numbers::pi, std::numbers::pi)(generator);
0244
0245 Acts::Vector3 direction = particle.direction();
0246
0247 Acts::RotationMatrix3 rotation(
0248
0249 Acts::AngleAxis3(psi, direction) *
0250
0251 Acts::AngleAxis3(theta, Acts::makeCurvilinearUnitU(direction)));
0252 direction.applyOnTheLeft(rotation);
0253 return direction;
0254 }
0255
0256 inline std::array<Particle, 2> PhotonConversion::generateChildren(
0257 const Particle& photon, double childEnergy,
0258 const Acts::Vector3& childDirection) const {
0259 using namespace Acts::UnitLiterals;
0260
0261
0262 const double massChild = kElectronMass;
0263 const double momentum1 =
0264 sqrt(childEnergy * childEnergy - massChild * massChild);
0265
0266
0267 const Acts::Vector3 vtmp =
0268 photon.fourMomentum().template segment<3>(Acts::eMom0) -
0269 momentum1 * childDirection;
0270 const double momentum2 = vtmp.norm();
0271
0272
0273
0274
0275
0276 std::array<Particle, 2> children = {
0277 Particle(photon.particleId().makeDescendant(0), Acts::eElectron, -1_e,
0278 kElectronMass)
0279 .setPosition4(photon.fourPosition())
0280 .setDirection(childDirection)
0281 .setAbsoluteMomentum(momentum1)
0282 .setProcess(ProcessType::ePhotonConversion)
0283 .setReferenceSurface(photon.referenceSurface()),
0284 Particle(photon.particleId().makeDescendant(1), Acts::ePositron, 1_e,
0285 kElectronMass)
0286 .setPosition4(photon.fourPosition())
0287 .setDirection(childDirection)
0288 .setAbsoluteMomentum(momentum2)
0289 .setProcess(ProcessType::ePhotonConversion)
0290 .setReferenceSurface(photon.referenceSurface()),
0291 };
0292 return children;
0293 }
0294
0295 template <typename generator_t>
0296 bool PhotonConversion::run(generator_t& generator, Particle& particle,
0297 std::vector<Particle>& generated) const {
0298
0299 if (particle.pdg() != Acts::PdgParticle::eGamma) {
0300 return false;
0301 }
0302
0303
0304 const double p = particle.absoluteMomentum();
0305 if (p < (2 * kElectronMass)) {
0306 return false;
0307 }
0308
0309
0310 const double childEnergy = p * generateFirstChildEnergyFraction(generator, p);
0311
0312
0313 const Acts::Vector3 childDir = generateChildDirection(generator, particle);
0314
0315
0316 const std::array<Particle, 2> finalState =
0317 generateChildren(particle, childEnergy, childDir);
0318 generated.insert(generated.end(), finalState.begin(), finalState.end());
0319
0320 return true;
0321 }
0322
0323 }