Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GeneralMixture.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file GeneralMixture.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2018-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 
13 
14 #include <random>
15 
16 namespace ActsFatras {
17 namespace detail {
18 
28  bool log_include = true;
30  double genMixtureScalor = 1.;
31 
40  template <typename generator_t>
41  double operator()(generator_t &generator, const Acts::MaterialSlab &slab,
42  Particle &particle) const {
43  double theta = 0.0;
44 
45  if (particle.absolutePdg() != Acts::PdgParticle::eElectron) {
46  //----------------------------------------------------------------------------
47  // see Mixture models of multiple scattering: computation and simulation.
48  // -
49  // R.Fruehwirth, M. Liendl. -
50  // Computer Physics Communications 141 (2001) 230–246
51  //----------------------------------------------------------------------------
52  std::array<double, 4> scattering_params{};
53  // Decide which mixture is best
54  // beta² = (p/E)² = p²/(p² + m²) = 1/(1 + (m/p)²)
55  // 1/beta² = 1 + (m/p)²
56  // beta = 1/sqrt(1 + (m/p)²)
57  double mOverP = particle.mass() / particle.absoluteMomentum();
58  double beta2Inv = 1 + mOverP * mOverP;
59  double beta = 1 / std::sqrt(beta2Inv);
60  double tInX0 = slab.thicknessInX0();
61  double tob2 = tInX0 * beta2Inv;
62  if (tob2 > 0.6 / std::pow(slab.material().Z(), 0.6)) {
63  // Gaussian mixture or pure Gaussian
64  if (tob2 > 10) {
65  scattering_params = getGaussian(beta, particle.absoluteMomentum(),
66  tInX0, genMixtureScalor);
67  } else {
68  scattering_params =
69  getGaussmix(beta, particle.absoluteMomentum(), tInX0,
70  slab.material().Z(), genMixtureScalor);
71  }
72  // Simulate
73  theta = gaussmix(generator, scattering_params);
74  } else {
75  // Semigaussian mixture - get parameters
76  auto scattering_params_sg =
77  getSemigauss(beta, particle.absoluteMomentum(), tInX0,
78  slab.material().Z(), genMixtureScalor);
79  // Simulate
80  theta = semigauss(generator, scattering_params_sg);
81  }
82  } else {
83  // for electrons we fall back to the Highland (extension)
84  // return projection factor times sigma times gauss random
85  const auto theta0 = Acts::computeMultipleScatteringTheta0(
86  slab, particle.absolutePdg(), particle.mass(), particle.qOverP(),
87  particle.absoluteCharge());
88  theta = std::normal_distribution<double>(0.0, theta0)(generator);
89  }
90  // scale from planar to 3d angle
91  return M_SQRT2 * theta;
92  }
93 
94  // helper methods for getting parameters and simulating
95 
96  std::array<double, 4> getGaussian(double beta, double p, double tInX0,
97  double scale) const {
98  std::array<double, 4> scattering_params{};
99  // Total standard deviation of mixture
100  scattering_params[0] = 15. / beta / p * std::sqrt(tInX0) * scale;
101  scattering_params[1] = 1.0; // Variance of core
102  scattering_params[2] = 1.0; // Variance of tails
103  scattering_params[3] = 0.5; // Mixture weight of tail component
104  return scattering_params;
105  }
106 
107  std::array<double, 4> getGaussmix(double beta, double p, double tInX0,
108  double Z, double scale) const {
109  std::array<double, 4> scattering_params{};
110  scattering_params[0] = 15. / beta / p * std::sqrt(tInX0) *
111  scale; // Total standard deviation of mixture
112  double d1 = std::log(tInX0 / (beta * beta));
113  double d2 = std::log(std::pow(Z, 2.0 / 3.0) * tInX0 / (beta * beta));
114  double epsi = 0;
115  double var1 = (-1.843e-3 * d1 + 3.347e-2) * d1 + 8.471e-1; // Variance of
116  // core
117  if (d2 < 0.5) {
118  epsi = (6.096e-4 * d2 + 6.348e-3) * d2 + 4.841e-2;
119  } else {
120  epsi = (-5.729e-3 * d2 + 1.106e-1) * d2 - 1.908e-2;
121  }
122  scattering_params[1] = var1; // Variance of core
123  scattering_params[2] = (1 - (1 - epsi) * var1) / epsi; // Variance of tails
124  scattering_params[3] = epsi; // Mixture weight of tail component
125  return scattering_params;
126  }
127 
128  std::array<double, 6> getSemigauss(double beta, double p, double tInX0,
129  double Z, double scale) const {
130  std::array<double, 6> scattering_params{};
131  double N = tInX0 * 1.587E7 * std::pow(Z, 1.0 / 3.0) / (beta * beta) /
132  (Z + 1) / std::log(287 / std::sqrt(Z));
133  scattering_params[4] = 15. / beta / p * std::sqrt(tInX0) *
134  scale; // Total standard deviation of mixture
135  double rho = 41000 / std::pow(Z, 2.0 / 3.0);
136  double b = rho / std::sqrt(N * (std::log(rho) - 0.5));
137  double n = std::pow(Z, 0.1) * std::log(N);
138  double var1 = (5.783E-4 * n + 3.803E-2) * n + 1.827E-1;
139  double a =
140  (((-4.590E-5 * n + 1.330E-3) * n - 1.355E-2) * n + 9.828E-2) * n +
141  2.822E-1;
142  double epsi = (1 - var1) / (a * a * (std::log(b / a) - 0.5) - var1);
143  scattering_params[3] =
144  (epsi > 0) ? epsi : 0.0; // Mixture weight of tail component
145  scattering_params[0] = a; // Parameter 1 of tails
146  scattering_params[1] = b; // Parameter 2 of tails
147  scattering_params[2] = var1; // Variance of core
148  scattering_params[5] = N; // Average number of scattering processes
149  return scattering_params;
150  }
151 
160  template <typename generator_t>
161  double gaussmix(generator_t &generator,
162  const std::array<double, 4> &scattering_params) const {
163  std::uniform_real_distribution<double> udist(0.0, 1.0);
164  double sigma_tot = scattering_params[0];
165  double var1 = scattering_params[1];
166  double var2 = scattering_params[2];
167  double epsi = scattering_params[3];
168  bool ind = udist(generator) > epsi;
169  double u = udist(generator);
170  if (ind) {
171  return std::sqrt(var1) * std::sqrt(-2 * std::log(u)) * sigma_tot;
172  } else {
173  return std::sqrt(var2) * std::sqrt(-2 * std::log(u)) * sigma_tot;
174  }
175  }
176 
185  template <typename generator_t>
186  double semigauss(generator_t &generator,
187  const std::array<double, 6> &scattering_params) const {
188  std::uniform_real_distribution<double> udist(0.0, 1.0);
189  double a = scattering_params[0];
190  double b = scattering_params[1];
191  double var1 = scattering_params[2];
192  double epsi = scattering_params[3];
193  double sigma_tot = scattering_params[4];
194  bool ind = udist(generator) > epsi;
195  double u = udist(generator);
196  if (ind) {
197  return std::sqrt(var1) * std::sqrt(-2 * std::log(u)) * sigma_tot;
198  } else {
199  return a * b * std::sqrt((1 - u) / (u * b * b + a * a)) * sigma_tot;
200  }
201  }
202 };
203 
204 } // namespace detail
205 } // namespace ActsFatras