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