Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
BetheHeitlerApprox.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file BetheHeitlerApprox.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 
13 
14 #include <algorithm>
15 #include <array>
16 #include <cassert>
17 #include <cmath>
18 #include <cstddef>
19 #include <fstream>
20 #include <mutex>
21 #include <random>
22 #include <stdexcept>
23 #include <string>
24 #include <tuple>
25 
26 #include <boost/container/static_vector.hpp>
27 
28 namespace Acts {
29 
30 namespace detail {
31 
35  ActsScalar var = 0.0;
36 };
37 
41  double &transformed_weight,
42  double &transformed_mean,
43  double &transformed_var) {
44  const auto &[weight, mean, var] = cmp;
45 
46  transformed_weight = std::log(weight) - std::log(1 - weight);
47  transformed_mean = std::log(mean) - std::log(1 - mean);
48  transformed_var = std::log(var);
49 }
50 
53 inline auto inverseTransformComponent(double transformed_weight,
54  double transformed_mean,
55  double transformed_var) {
57  cmp.weight = 1. / (1 + std::exp(-transformed_weight));
58  cmp.mean = 1. / (1 + std::exp(-transformed_mean));
59  cmp.var = std::exp(transformed_var);
60 
61  return cmp;
62 }
63 
64 } // namespace detail
65 
71  constexpr auto numComponents() const { return 1; }
72 
75  constexpr bool validXOverX0(ActsScalar x) const {
76  return x < 0.002;
77  ;
78  }
79 
84  static auto mixture(const ActsScalar x) {
85  std::array<detail::GaussianComponent, 1> ret{};
86 
87  ret[0].weight = 1.0;
88 
89  const double c = x / std::log(2);
90  ret[0].mean = std::pow(2, -c);
91  ret[0].var = std::pow(3, -c) - std::pow(4, -c);
92 
93  return ret;
94  }
95 };
96 
101 template <int NComponents, int PolyDegree>
103  static_assert(NComponents > 0);
104  static_assert(PolyDegree > 0);
105 
106  public:
107  struct PolyData {
108  std::array<ActsScalar, PolyDegree + 1> weightCoeffs;
109  std::array<ActsScalar, PolyDegree + 1> meanCoeffs;
110  std::array<ActsScalar, PolyDegree + 1> varCoeffs;
111  };
112 
113  using Data = std::array<PolyData, NComponents>;
114 
115  constexpr static double noChangeLimit = 0.0001;
116  constexpr static double singleGaussianLimit = 0.002;
117  constexpr static double lowerLimit = 0.10;
118  constexpr static double higherLimit = 0.20;
119 
120  private:
125 
126  public:
135  constexpr AtlasBetheHeitlerApprox(const Data &low_data, const Data &high_data,
136  bool low_transform, bool high_transform)
137  : m_low_data(low_data),
138  m_high_data(high_data),
139  m_low_transform(low_transform),
140  m_high_transform(high_transform) {}
141 
143  constexpr auto numComponents() const { return NComponents; }
144 
148  constexpr bool validXOverX0(ActsScalar x) const { return x < higherLimit; }
149 
154  auto mixture(ActsScalar x) const {
155  using Array =
156  boost::container::static_vector<detail::GaussianComponent, NComponents>;
157  // Build a polynom
158  auto poly = [](ActsScalar xx,
159  const std::array<ActsScalar, PolyDegree + 1> &coeffs) {
160  ActsScalar sum{0.};
161  for (const auto c : coeffs) {
162  sum = xx * sum + c;
163  }
164  assert((std::isfinite(sum) && "polynom result not finite"));
165  return sum;
166  };
167 
168  // Lambda which builds the components
169  auto make_mixture = [&](const Data &data, double xx, bool transform) {
170  // Value initialization should garanuee that all is initialized to zero
171  Array ret(NComponents);
172  ActsScalar weight_sum = 0;
173  for (int i = 0; i < NComponents; ++i) {
174  // These transformations must be applied to the data according to ATHENA
175  // (TrkGaussianSumFilter/src/GsfCombinedMaterialEffects.cxx:79)
176  if (transform) {
178  poly(xx, data[i].weightCoeffs), poly(xx, data[i].meanCoeffs),
179  poly(xx, data[i].varCoeffs));
180  } else {
181  ret[i].weight = poly(xx, data[i].weightCoeffs);
182  ret[i].mean = poly(xx, data[i].meanCoeffs);
183  ret[i].var = poly(xx, data[i].varCoeffs);
184  }
185 
186  weight_sum += ret[i].weight;
187  }
188 
189  for (int i = 0; i < NComponents; ++i) {
190  ret[i].weight /= weight_sum;
191  }
192 
193  return ret;
194  };
195 
196  // Return no change
197  if (x < noChangeLimit) {
198  Array ret(1);
199 
200  ret[0].weight = 1.0;
201  ret[0].mean = 1.0; // p_initial = p_final
202  ret[0].var = 0.0;
203 
204  return ret;
205  }
206  // Return single gaussian approximation
207  if (x < singleGaussianLimit) {
208  Array ret(1);
210  return ret;
211  }
212  // Return a component representation for lower x0
213  if (x < lowerLimit) {
214  return make_mixture(m_low_data, x, m_low_transform);
215  }
216  // Return a component representation for higher x0
217  // Cap the x because beyond the parameterization goes wild
218  const auto high_x = std::min(higherLimit, x);
219  return make_mixture(m_high_data, high_x, m_high_transform);
220  }
221 
229  static auto loadFromFiles(const std::string &low_parameters_path,
230  const std::string &high_parameters_path) {
231  auto read_file = [](const std::string &filepath) {
232  std::ifstream file(filepath);
233 
234  if (!file) {
235  throw std::invalid_argument("Could not open '" + filepath + "'");
236  }
237 
238  std::size_t n_cmps = 0, degree = 0;
239  bool transform_code = false;
240 
241  file >> n_cmps >> degree >> transform_code;
242 
243  if (NComponents != n_cmps) {
244  throw std::invalid_argument("Wrong number of components in '" +
245  filepath + "'");
246  }
247 
248  if (PolyDegree != degree) {
249  throw std::invalid_argument("Wrong polynom order in '" + filepath +
250  "'");
251  }
252 
253  Data data;
254 
255  for (auto &cmp : data) {
256  for (auto &coeff : cmp.weightCoeffs) {
257  file >> coeff;
258  }
259  for (auto &coeff : cmp.meanCoeffs) {
260  file >> coeff;
261  }
262  for (auto &coeff : cmp.varCoeffs) {
263  file >> coeff;
264  }
265  }
266 
267  return std::make_tuple(data, transform_code);
268  };
269 
270  const auto [low_data, low_transform] = read_file(low_parameters_path);
271  const auto [high_data, high_transform] = read_file(high_parameters_path);
272 
273  return AtlasBetheHeitlerApprox(low_data, high_data, low_transform,
274  high_transform);
275  }
276 };
277 
282 AtlasBetheHeitlerApprox<6, 5> makeDefaultBetheHeitlerApprox();
283 
284 } // namespace Acts