Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-05 08:37:56

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
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/MathHelpers.hpp"
0016 #include "Acts/Utilities/UnitVectors.hpp"
0017 #include "ActsFatras/EventData/Barcode.hpp"
0018 #include "ActsFatras/EventData/GenerationProcess.hpp"
0019 #include "ActsFatras/EventData/Particle.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 /// This class handles the photon conversion. It evaluates the distance
0033 /// after which the interaction will occur and the final state due the
0034 /// interaction itself.
0035 class PhotonConversion {
0036  public:
0037   /// Scaling factor of children energy
0038   double childEnergyScaleFactor = 2.;
0039   /// Scaling factor for photon conversion probability
0040   double conversionProbScaleFactor = 0.98;
0041 
0042   /// Method for evaluating the distance after which the photon
0043   /// conversion will occur.
0044   ///
0045   /// @tparam generator_t Type of the random number generator
0046   /// @param [in, out] generator The random number generator
0047   /// @param [in] particle The particle
0048   ///
0049   /// @return valid X0 limit and no limit on L0
0050   template <typename generator_t>
0051   std::pair<double, double> generatePathLimits(generator_t& generator,
0052                                                const Particle& particle) const;
0053 
0054   /// This method evaluates the final state due to the photon conversion.
0055   ///
0056   /// @tparam generator_t Type of the random number generator
0057   /// @param [in, out] generator The random number generator
0058   /// @param [in, out] particle The interacting photon
0059   /// @param [out] generated List of generated particles
0060   ///
0061   /// @return True if the conversion occurred, else false
0062   template <typename generator_t>
0063   bool run(generator_t& generator, Particle& particle,
0064            std::vector<Particle>& generated) const;
0065 
0066  private:
0067   /// This method constructs and returns the child particles.
0068   ///
0069   /// @param [in] photon The interacting photon
0070   /// @param [in] child1Energy The energy of the first child particle
0071   /// @param [in] child1Direction The direction of the first child particle
0072   ///
0073   /// @return Array containing the produced leptons
0074   std::array<Particle, 2> generateChildren(
0075       const Particle& photon, double child1Energy,
0076       const Acts::Vector3& child1Direction) const;
0077 
0078   /// Generate the energy fraction of the first child particle.
0079   ///
0080   /// @tparam generator_t Type of the random number generator
0081   /// @param [in, out] generator The random number generator
0082   /// @param [in] gammaMom The momentum of the photon
0083   ///
0084   /// @return The energy of the child particle
0085   template <typename generator_t>
0086   double generateFirstChildEnergyFraction(generator_t& generator,
0087                                           double gammaMom) const;
0088 
0089   /// Generate the direction of the child particles.
0090   ///
0091   /// @tparam generator_t Type of the random number generator
0092   /// @param [in, out] generator The random number generator
0093   /// @param [in] particle The photon
0094   ///
0095   /// @return The direction vector of the child particle
0096   template <typename generator_t>
0097   Acts::Vector3 generateChildDirection(generator_t& generator,
0098                                        const Particle& particle) const;
0099 
0100   /// Helper methods for momentum evaluation
0101   /// @note These methods are taken from the Geant4 class
0102   /// G4PairProductionRelModel
0103   double screenFunction1(double delta) const;
0104   double screenFunction2(double delta) const;
0105 
0106   /// Helper method for the electron mass. This is used to avoid multiple
0107   /// lookups in the internal data tables.
0108   static float electronMass() {
0109     return Acts::findMass(Acts::PdgParticle::eElectron).value();
0110   }
0111 };
0112 
0113 inline double PhotonConversion::screenFunction1(double delta) const {
0114   // Compute the value of the screening function 3*PHI1(delta) - PHI2(delta)
0115   return (delta > 1.4) ? 42.038 - 8.29 * std::log(delta + 0.958)
0116                        : 42.184 - delta * (7.444 - 1.623 * delta);
0117 }
0118 
0119 inline double PhotonConversion::screenFunction2(double delta) const {
0120   // Compute the value of the screening function 1.5*PHI1(delta)
0121   // +0.5*PHI2(delta)
0122   return (delta > 1.4) ? 42.038 - 8.29 * std::log(delta + 0.958)
0123                        : 41.326 - delta * (5.848 - 0.902 * delta);
0124 }
0125 
0126 template <typename generator_t>
0127 std::pair<double, double> PhotonConversion::generatePathLimits(
0128     generator_t& generator, const Particle& particle) const {
0129   /// This method is based upon the Athena class PhotonConversionTool
0130 
0131   // Fast exit if not a photon or the energy is too low
0132   if (particle.pdg() != Acts::PdgParticle::eGamma ||
0133       particle.absoluteMomentum() < (2 * electronMass())) {
0134     return {std::numeric_limits<double>::infinity(),
0135             std::numeric_limits<double>::infinity()};
0136   }
0137 
0138   // Use for the moment only Al data - Yung Tsai - Rev.Mod.Particle Physics Vol.
0139   // 46, No.4, October 1974 optainef from a fit given in the momentum range 100
0140   // 10 6 2 1 0.6 0.4 0.2 0.1 GeV
0141 
0142   //// Quadratic background function
0143   //  Double_t fitFunction(Double_t *x, Double_t *par) {
0144   //  return par[0] + par[1]*pow(x[0],par[2]);
0145   // }
0146   // EXT PARAMETER                                   STEP         FIRST
0147   // NO.   NAME      VALUE            ERROR          SIZE      DERIVATIVE
0148   //  1  p0          -7.01612e-03   8.43478e-01   1.62766e-04   1.11914e-05
0149   //  2  p1           7.69040e-02   1.00059e+00   8.90718e-05  -8.41167e-07
0150   //  3  p2          -6.07682e-01   5.13256e+00   6.07228e-04  -9.44448e-07
0151   constexpr double p0 = -7.01612e-03;
0152   constexpr double p1 = 7.69040e-02;
0153   constexpr double p2 = -6.07682e-01;
0154 
0155   // Calculate xi
0156   const double xi = p0 + p1 * std::pow(particle.absoluteMomentum(), p2);
0157 
0158   std::uniform_real_distribution<double> uniformDistribution{0., 1.};
0159   // This is a transformation of eq. 3.75
0160   return {-9. / 7. *
0161               std::log(conversionProbScaleFactor *
0162                        (1 - uniformDistribution(generator))) /
0163               (1. - xi),
0164           std::numeric_limits<double>::infinity()};
0165 }
0166 
0167 template <typename generator_t>
0168 double PhotonConversion::generateFirstChildEnergyFraction(
0169     generator_t& generator, double gammaMom) const {
0170   /// This method is based upon the Geant4 class G4PairProductionRelModel
0171 
0172   /// @note This method is from the Geant4 class G4Element
0173   //
0174   //  Compute Coulomb correction factor (Phys Rev. D50 3-1 (1994) page 1254)
0175   constexpr double k1 = 0.0083;
0176   constexpr double k2 = 0.20206;
0177   constexpr double k3 = 0.0020;  // This term is missing in Athena
0178   constexpr double k4 = 0.0369;
0179   constexpr double alphaEM = 1. / 137.;
0180   constexpr double m_Z = 13.;  // Aluminium
0181   constexpr double az2 = (alphaEM * m_Z) * (alphaEM * m_Z);
0182   constexpr double az4 = az2 * az2;
0183   constexpr double coulombFactor =
0184       (k1 * az4 + k2 + 1. / (1. + az2)) * az2 - (k3 * az4 + k4) * az4;
0185 
0186   const double logZ13 = std::log(m_Z) * 1. / 3.;
0187   const double FZ = 8. * (logZ13 + coulombFactor);
0188   const double deltaMax = std::exp((42.038 - FZ) * 0.1206) - 0.958;
0189 
0190   const double deltaPreFactor = 136. / std::pow(m_Z, 1. / 3.);
0191   const double eps0 = electronMass() / gammaMom;
0192   const double deltaFactor = deltaPreFactor * eps0;
0193   const double deltaMin = 4. * deltaFactor;
0194 
0195   // Compute the limits of eps
0196   const double epsMin =
0197       std::max(eps0, 0.5 - 0.5 * std::sqrt(1. - deltaMin / deltaMax));
0198   const double epsRange = 0.5 - epsMin;
0199 
0200   // Sample the energy rate (eps) of the created electron (or positron)
0201   const double F10 = screenFunction1(deltaMin) - FZ;
0202   const double F20 = screenFunction2(deltaMin) - FZ;
0203   const double NormF1 = F10 * epsRange * epsRange;
0204   const double NormF2 = 1.5 * F20;
0205 
0206   // We will need 3 uniform random number for each trial of sampling
0207   double greject = 0.;
0208   double eps = 0.;
0209   std::uniform_real_distribution<double> rndmEngine;
0210   do {
0211     if (NormF1 > rndmEngine(generator) * (NormF1 + NormF2)) {
0212       eps = 0.5 - epsRange * std::pow(rndmEngine(generator), 1. / 3.);
0213       const double delta = deltaFactor / (eps * (1. - eps));
0214       greject = (screenFunction1(delta) - FZ) / F10;
0215     } else {
0216       eps = epsMin + epsRange * rndmEngine(generator);
0217       const double delta = deltaFactor / (eps * (1. - eps));
0218       greject = (screenFunction2(delta) - FZ) / F20;
0219     }
0220   } while (greject < rndmEngine(generator));
0221   //  End of eps sampling
0222   return eps * childEnergyScaleFactor;
0223 }
0224 
0225 template <typename generator_t>
0226 Acts::Vector3 PhotonConversion::generateChildDirection(
0227     generator_t& generator, const Particle& particle) const {
0228   // This method is based upon the Athena class PhotonConversionTool
0229 
0230   // Following the Geant4 approximation from L. Urban the azimutal angle
0231   std::uniform_real_distribution<double> uniformDistribution{0., 1.};
0232   const double u = -std::log(uniformDistribution(generator) *
0233                              uniformDistribution(generator)) *
0234                    1.6;
0235   const double theta = (electronMass() / particle.energy()) *
0236                        ((uniformDistribution(generator) < 0.25)
0237                             ? u
0238                             : u * 1. / 3.);  // 9./(9.+27) = 0.25
0239 
0240   // draw the random orientation angle
0241   const auto psi = std::uniform_real_distribution<double>(
0242       -std::numbers::pi, std::numbers::pi)(generator);
0243 
0244   // construct the combined rotation to the scattered direction
0245   const Acts::RotationMatrix3 rotation(
0246       // rotation of the scattering deflector axis relative to the reference
0247       Acts::AngleAxis3(psi, particle.direction()) *
0248       // rotation by the scattering angle around the deflector axis
0249       Acts::AngleAxis3(theta,
0250                        Acts::createCurvilinearUnitU(particle.direction())));
0251   return rotation * particle.direction();
0252 }
0253 
0254 inline std::array<Particle, 2> PhotonConversion::generateChildren(
0255     const Particle& photon, double child1Energy,
0256     const Acts::Vector3& child1Direction) const {
0257   using namespace Acts::UnitLiterals;
0258 
0259   // Calculate the child momentum
0260   const double massChild = electronMass();
0261   const double absoluteMomentum1 = Acts::fastCathetus(child1Energy, massChild);
0262 
0263   // Use energy-momentum conservation for the other child
0264   const Acts::Vector3 momentum2 =
0265       photon.fourMomentum().template segment<3>(Acts::eMom0) -
0266       absoluteMomentum1 * child1Direction;
0267   const Acts::Vector3 child2Direction = momentum2.normalized();
0268   const double absoluteMomentum2 = momentum2.norm();
0269 
0270   // The daughter particles are created with the explicit electron mass used in
0271   // the calculations for consistency. Using the full Particle constructor with
0272   // charge and mass also avoids an additional lookup in the internal data
0273   // tables.
0274   return {
0275       Particle(photon.particleId().makeDescendant(0), Acts::eElectron, -1_e,
0276                electronMass())
0277           .setPosition4(photon.fourPosition())
0278           .setDirection(child1Direction)
0279           .setAbsoluteMomentum(absoluteMomentum1)
0280           .setProcess(GenerationProcess::ePhotonConversion)
0281           .setReferenceSurface(photon.referenceSurface()),
0282       Particle(photon.particleId().makeDescendant(1), Acts::ePositron, 1_e,
0283                electronMass())
0284           .setPosition4(photon.fourPosition())
0285           .setDirection(child2Direction)
0286           .setAbsoluteMomentum(absoluteMomentum2)
0287           .setProcess(GenerationProcess::ePhotonConversion)
0288           .setReferenceSurface(photon.referenceSurface()),
0289   };
0290 }
0291 
0292 template <typename generator_t>
0293 bool PhotonConversion::run(generator_t& generator, Particle& particle,
0294                            std::vector<Particle>& generated) const {
0295   // Fast exit if particle is not a photon
0296   if (particle.pdg() != Acts::PdgParticle::eGamma) {
0297     return false;
0298   }
0299 
0300   // Fast exit if momentum is too low
0301   const double p = particle.absoluteMomentum();
0302   if (p < (2 * electronMass())) {
0303     return false;
0304   }
0305 
0306   // Get one child energy
0307   const double child1Energy =
0308       p * generateFirstChildEnergyFraction(generator, p);
0309 
0310   // Now get the deflection
0311   const Acts::Vector3 child1Dir = generateChildDirection(generator, particle);
0312 
0313   // Produce the final state
0314   const std::array<Particle, 2> finalState =
0315       generateChildren(particle, child1Energy, child1Dir);
0316   generated.insert(generated.end(), finalState.begin(), finalState.end());
0317 
0318   return true;
0319 }
0320 
0321 }  // namespace ActsFatras