Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PhotonConversion.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PhotonConversion.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
20 
21 #include <algorithm>
22 #include <array>
23 #include <cmath>
24 #include <limits>
25 #include <random>
26 #include <utility>
27 #include <vector>
28 
29 namespace ActsFatras {
30 
35  public:
37 
42 
51  template <typename generator_t>
52  std::pair<Scalar, Scalar> generatePathLimits(generator_t& generator,
53  const Particle& particle) const;
54 
63  template <typename generator_t>
64  bool run(generator_t& generator, Particle& particle,
65  std::vector<Particle>& generated) const;
66 
67  private:
75  std::array<Particle, 2> generateChildren(
76  const Particle& photon, Scalar childEnergy,
77  const Particle::Vector3& childDirection) const;
78 
86  template <typename generator_t>
88  Scalar gammaMom) const;
89 
97  template <typename generator_t>
99  const Particle& particle) const;
100 
106 
109  static const Scalar kElectronMass;
110 };
111 
113  // Compute the value of the screening function 3*PHI1(delta) - PHI2(delta)
114  return (delta > 1.4) ? 42.038 - 8.29 * std::log(delta + 0.958)
115  : 42.184 - delta * (7.444 - 1.623 * delta);
116 }
117 
119  // Compute the value of the screening function 1.5*PHI1(delta)
120  // +0.5*PHI2(delta)
121  return (delta > 1.4) ? 42.038 - 8.29 * std::log(delta + 0.958)
122  : 41.326 - delta * (5.848 - 0.902 * delta);
123 }
124 
125 template <typename generator_t>
126 std::pair<Particle::Scalar, Particle::Scalar>
128  const Particle& particle) const {
130 
131  // Fast exit if not a photon or the energy is too low
132  if (particle.pdg() != Acts::PdgParticle::eGamma ||
133  particle.absoluteMomentum() < (2 * kElectronMass)) {
134  return std::make_pair(std::numeric_limits<Scalar>::infinity(),
135  std::numeric_limits<Scalar>::infinity());
136  }
137 
138  // Use for the moment only Al data - Yung Tsai - Rev.Mod.Particle Physics Vol.
139  // 46, No.4, October 1974 optainef from a fit given in the momentum range 100
140  // 10 6 2 1 0.6 0.4 0.2 0.1 GeV
141 
143  // Double_t fitFunction(Double_t *x, Double_t *par) {
144  // return par[0] + par[1]*pow(x[0],par[2]);
145  // }
146  // EXT PARAMETER STEP FIRST
147  // NO. NAME VALUE ERROR SIZE DERIVATIVE
148  // 1 p0 -7.01612e-03 8.43478e-01 1.62766e-04 1.11914e-05
149  // 2 p1 7.69040e-02 1.00059e+00 8.90718e-05 -8.41167e-07
150  // 3 p2 -6.07682e-01 5.13256e+00 6.07228e-04 -9.44448e-07
151  constexpr Scalar p0 = -7.01612e-03;
152  constexpr Scalar p1 = 7.69040e-02;
153  constexpr Scalar p2 = -6.07682e-01;
154 
155  // Calculate xi
156  const Scalar xi = p0 + p1 * std::pow(particle.absoluteMomentum(), p2);
157 
158  std::uniform_real_distribution<Scalar> uniformDistribution{0., 1.};
159  // This is a transformation of eq. 3.75
160  return std::make_pair(-9. / 7. *
161  std::log(conversionProbScaleFactor *
162  (1 - uniformDistribution(generator))) /
163  (1. - xi),
164  std::numeric_limits<Scalar>::infinity());
165 }
166 
167 template <typename generator_t>
169  generator_t& generator, Scalar gammaMom) const {
171 
173  //
174  // Compute Coulomb correction factor (Phys Rev. D50 3-1 (1994) page 1254)
175  constexpr Scalar k1 = 0.0083;
176  constexpr Scalar k2 = 0.20206;
177  constexpr Scalar k3 = 0.0020; // This term is missing in Athena
178  constexpr Scalar k4 = 0.0369;
179  constexpr Scalar alphaEM = 1. / 137.;
180  constexpr Scalar m_Z = 13.; // Aluminium
181  constexpr Scalar az2 = (alphaEM * m_Z) * (alphaEM * m_Z);
182  constexpr Scalar az4 = az2 * az2;
183  constexpr Scalar coulombFactor =
184  (k1 * az4 + k2 + 1. / (1. + az2)) * az2 - (k3 * az4 + k4) * az4;
185 
186  const Scalar logZ13 = std::log(m_Z) * 1. / 3.;
187  const Scalar FZ = 8. * (logZ13 + coulombFactor);
188  const Scalar deltaMax = exp((42.038 - FZ) * 0.1206) - 0.958;
189 
190  const Scalar deltaPreFactor = 136. / std::pow(m_Z, 1. / 3.);
191  const Scalar eps0 = kElectronMass / gammaMom;
192  const Scalar deltaFactor = deltaPreFactor * eps0;
193  const Scalar deltaMin = 4. * deltaFactor;
194 
195  // Compute the limits of eps
196  const Scalar epsMin =
197  std::max(eps0, 0.5 - 0.5 * std::sqrt(1. - deltaMin / deltaMax));
198  const Scalar epsRange = 0.5 - epsMin;
199 
200  // Sample the energy rate (eps) of the created electron (or positron)
201  const Scalar F10 = screenFunction1(deltaMin) - FZ;
202  const Scalar F20 = screenFunction2(deltaMin) - FZ;
203  const Scalar NormF1 = F10 * epsRange * epsRange;
204  const Scalar NormF2 = 1.5 * F20;
205 
206  // We will need 3 uniform random number for each trial of sampling
207  Scalar greject = 0.;
208  Scalar eps = 0.;
209  std::uniform_real_distribution<Scalar> rndmEngine;
210  do {
211  if (NormF1 > rndmEngine(generator) * (NormF1 + NormF2)) {
212  eps = 0.5 - epsRange * std::pow(rndmEngine(generator), 1. / 3.);
213  const Scalar delta = deltaFactor / (eps * (1. - eps));
214  greject = (screenFunction1(delta) - FZ) / F10;
215  } else {
216  eps = epsMin + epsRange * rndmEngine(generator);
217  const Scalar delta = deltaFactor / (eps * (1. - eps));
218  greject = (screenFunction2(delta) - FZ) / F20;
219  }
220  } while (greject < rndmEngine(generator));
221  // End of eps sampling
222  return eps * childEnergyScaleFactor;
223 }
224 
225 template <typename generator_t>
227  generator_t& generator, const Particle& particle) const {
229 
230  // Following the Geant4 approximation from L. Urban
231  // the azimutal angle
232  Scalar theta = kElectronMass / particle.energy();
233 
234  std::uniform_real_distribution<Scalar> uniformDistribution{0., 1.};
235  const Scalar u = -std::log(uniformDistribution(generator) *
236  uniformDistribution(generator)) *
237  1.6;
238 
239  theta *= (uniformDistribution(generator) < 0.25)
240  ? u
241  : u * 1. / 3.; // 9./(9.+27) = 0.25
242 
243  // draw the random orientation angle
244  const auto psi =
245  std::uniform_real_distribution<double>(-M_PI, M_PI)(generator);
246 
247  Acts::Vector3 direction = particle.direction();
248  // construct the combined rotation to the scattered direction
249  Acts::RotationMatrix3 rotation(
250  // rotation of the scattering deflector axis relative to the reference
251  Acts::AngleAxis3(psi, direction) *
252  // rotation by the scattering angle around the deflector axis
253  Acts::AngleAxis3(theta, Acts::makeCurvilinearUnitU(direction)));
254  direction.applyOnTheLeft(rotation);
255  return direction;
256 }
257 
258 std::array<Particle, 2> PhotonConversion::generateChildren(
259  const Particle& photon, Scalar childEnergy,
260  const Particle::Vector3& childDirection) const {
261  using namespace Acts::UnitLiterals;
262 
263  // Calculate the child momentum
264  const Scalar massChild = kElectronMass;
265  const Scalar momentum1 =
266  sqrt(childEnergy * childEnergy - massChild * massChild);
267 
268  // Use energy-momentum conservation for the other child
269  const Particle::Vector3 vtmp =
270  photon.fourMomentum().template segment<3>(Acts::eMom0) -
271  momentum1 * childDirection;
272  const Scalar momentum2 = vtmp.norm();
273 
274  // The daughter particles are created with the explicit electron mass used in
275  // the calculations for consistency. Using the full Particle constructor with
276  // charge and mass also avoids an additional lookup in the internal data
277  // tables.
278  std::array<Particle, 2> children = {
281  .setPosition4(photon.fourPosition())
282  .setDirection(childDirection)
283  .setAbsoluteMomentum(momentum1)
284  .setProcess(ProcessType::ePhotonConversion)
288  .setPosition4(photon.fourPosition())
289  .setDirection(childDirection)
290  .setAbsoluteMomentum(momentum2)
291  .setProcess(ProcessType::ePhotonConversion)
293  };
294  return children;
295 }
296 
297 template <typename generator_t>
299  std::vector<Particle>& generated) const {
300  // Fast exit if particle is not a photon
301  if (particle.pdg() != Acts::PdgParticle::eGamma) {
302  return false;
303  }
304 
305  // Fast exit if momentum is too low
306  const Scalar p = particle.absoluteMomentum();
307  if (p < (2 * kElectronMass)) {
308  return false;
309  }
310 
311  // Get one child energy
312  const Scalar childEnergy = p * generateFirstChildEnergyFraction(generator, p);
313 
314  // Now get the deflection
315  const Particle::Vector3 childDir =
316  generateChildDirection(generator, particle);
317 
318  // Produce the final state
319  const std::array<Particle, 2> finalState =
320  generateChildren(particle, childEnergy, childDir);
321  generated.insert(generated.end(), finalState.begin(), finalState.end());
322 
323  return true;
324 }
325 
326 } // namespace ActsFatras