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