File indexing completed on 2025-12-15 09:42:23
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/TrackFitting/detail/GsfUtils.hpp"
0013
0014 #include <algorithm>
0015 #include <array>
0016 #include <cassert>
0017 #include <cmath>
0018 #include <cstddef>
0019 #include <fstream>
0020 #include <mutex>
0021 #include <numbers>
0022 #include <random>
0023 #include <stdexcept>
0024 #include <string>
0025 #include <tuple>
0026
0027 #include <boost/container/static_vector.hpp>
0028
0029 namespace Acts {
0030
0031 namespace detail {
0032
0033 struct GaussianComponent {
0034 double weight = 0.0;
0035 double mean = 0.0;
0036 double var = 0.0;
0037 };
0038
0039
0040
0041 inline void transformComponent(const GaussianComponent &cmp,
0042 double &transformed_weight,
0043 double &transformed_mean,
0044 double &transformed_var) {
0045 const auto &[weight, mean, var] = cmp;
0046
0047 transformed_weight = std::log(weight) - std::log(1 - weight);
0048 transformed_mean = std::log(mean) - std::log(1 - mean);
0049 transformed_var = std::log(var);
0050 }
0051
0052
0053
0054 inline auto inverseTransformComponent(double transformed_weight,
0055 double transformed_mean,
0056 double transformed_var) {
0057 GaussianComponent cmp;
0058 cmp.weight = 1. / (1 + std::exp(-transformed_weight));
0059 cmp.mean = 1. / (1 + std::exp(-transformed_mean));
0060 cmp.var = std::exp(transformed_var);
0061
0062 return cmp;
0063 }
0064
0065 }
0066
0067
0068
0069
0070 struct BetheHeitlerApproxSingleCmp {
0071
0072
0073 constexpr auto numComponents() const { return 1; }
0074
0075
0076
0077
0078
0079 constexpr bool validXOverX0(double x) const {
0080 return x < 0.002;
0081 ;
0082 }
0083
0084
0085
0086
0087
0088
0089 static auto mixture(const double x) {
0090 std::array<detail::GaussianComponent, 1> ret{};
0091
0092 ret[0].weight = 1.0;
0093
0094 const double c = x / std::numbers::ln2;
0095 ret[0].mean = std::pow(2, -c);
0096 ret[0].var = std::pow(3, -c) - std::pow(4, -c);
0097
0098 return ret;
0099 }
0100 };
0101
0102
0103
0104
0105
0106
0107
0108
0109 template <int NComponents, int PolyDegree>
0110 class AtlasBetheHeitlerApprox {
0111 static_assert(NComponents > 0);
0112 static_assert(PolyDegree > 0);
0113
0114 public:
0115 struct PolyData {
0116 std::array<double, PolyDegree + 1> weightCoeffs;
0117 std::array<double, PolyDegree + 1> meanCoeffs;
0118 std::array<double, PolyDegree + 1> varCoeffs;
0119 };
0120
0121
0122 using Data = std::array<PolyData, NComponents>;
0123
0124 private:
0125 Data m_lowData;
0126 Data m_highData;
0127 bool m_lowTransform;
0128 bool m_highTransform;
0129
0130 constexpr static double m_noChangeLimit = 0.0001;
0131 constexpr static double m_singleGaussianLimit = 0.002;
0132 double m_lowLimit = 0.10;
0133 double m_highLimit = 0.20;
0134 bool m_clampToRange = false;
0135
0136 public:
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149 constexpr AtlasBetheHeitlerApprox(const Data &lowData, const Data &highData,
0150 bool lowTransform, bool highTransform,
0151 double lowLimit = 0.1,
0152 double highLimit = 0.2,
0153 bool clampToRange = false)
0154 : m_lowData(lowData),
0155 m_highData(highData),
0156 m_lowTransform(lowTransform),
0157 m_highTransform(highTransform),
0158 m_lowLimit(lowLimit),
0159 m_highLimit(highLimit),
0160 m_clampToRange(clampToRange) {}
0161
0162
0163
0164 constexpr auto numComponents() const { return NComponents; }
0165
0166
0167
0168
0169
0170 constexpr bool validXOverX0(double x) const {
0171 if (m_clampToRange) {
0172 return true;
0173 } else {
0174 return x < m_highLimit;
0175 }
0176 }
0177
0178
0179
0180
0181
0182
0183 auto mixture(double x) const {
0184 using Array =
0185 boost::container::static_vector<detail::GaussianComponent, NComponents>;
0186
0187 if (m_clampToRange) {
0188 x = std::clamp(x, 0.0, m_highLimit);
0189 }
0190
0191
0192 auto poly = [](double xx,
0193 const std::array<double, PolyDegree + 1> &coeffs) {
0194 double sum{0.};
0195 for (const auto c : coeffs) {
0196 sum = xx * sum + c;
0197 }
0198 assert((std::isfinite(sum) && "polynom result not finite"));
0199 return sum;
0200 };
0201
0202
0203 auto make_mixture = [&](const Data &data, double xx, bool transform) {
0204
0205 Array ret(NComponents);
0206 double weight_sum = 0;
0207 for (int i = 0; i < NComponents; ++i) {
0208
0209
0210 if (transform) {
0211 ret[i] = detail::inverseTransformComponent(
0212 poly(xx, data[i].weightCoeffs), poly(xx, data[i].meanCoeffs),
0213 poly(xx, data[i].varCoeffs));
0214 } else {
0215 ret[i].weight = poly(xx, data[i].weightCoeffs);
0216 ret[i].mean = poly(xx, data[i].meanCoeffs);
0217 ret[i].var = poly(xx, data[i].varCoeffs);
0218 }
0219
0220 weight_sum += ret[i].weight;
0221 }
0222
0223 for (int i = 0; i < NComponents; ++i) {
0224 ret[i].weight /= weight_sum;
0225 }
0226
0227 return ret;
0228 };
0229
0230
0231 if (x < m_noChangeLimit) {
0232 Array ret(1);
0233
0234 ret[0].weight = 1.0;
0235 ret[0].mean = 1.0;
0236 ret[0].var = 0.0;
0237
0238 return ret;
0239 }
0240
0241 if (x < m_singleGaussianLimit) {
0242 Array ret(1);
0243 ret[0] = BetheHeitlerApproxSingleCmp::mixture(x)[0];
0244 return ret;
0245 }
0246
0247 if (x < m_lowLimit) {
0248 return make_mixture(m_lowData, x, m_lowTransform);
0249 }
0250
0251
0252 const auto high_x = std::min(m_highLimit, x);
0253 return make_mixture(m_highData, high_x, m_highTransform);
0254 }
0255
0256
0257
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267 static auto loadFromFiles(const std::string &low_parameters_path,
0268 const std::string &high_parameters_path,
0269 double lowLimit = 0.1, double highLimit = 0.2,
0270 bool clampToRange = false) {
0271 auto read_file = [](const std::string &filepath) {
0272 std::ifstream file(filepath);
0273
0274 if (!file) {
0275 throw std::invalid_argument("Could not open '" + filepath + "'");
0276 }
0277
0278 std::size_t n_cmps = 0, degree = 0;
0279 bool transform_code = false;
0280
0281 file >> n_cmps >> degree >> transform_code;
0282
0283 if (NComponents != n_cmps) {
0284 throw std::invalid_argument("Wrong number of components in '" +
0285 filepath + "'");
0286 }
0287
0288 if (PolyDegree != degree) {
0289 throw std::invalid_argument("Wrong polynom order in '" + filepath +
0290 "'");
0291 }
0292
0293 Data data;
0294
0295 for (auto &cmp : data) {
0296 for (auto &coeff : cmp.weightCoeffs) {
0297 file >> coeff;
0298 }
0299 for (auto &coeff : cmp.meanCoeffs) {
0300 file >> coeff;
0301 }
0302 for (auto &coeff : cmp.varCoeffs) {
0303 file >> coeff;
0304 }
0305 }
0306
0307 return std::make_tuple(data, transform_code);
0308 };
0309
0310 const auto [lowData, lowTransform] = read_file(low_parameters_path);
0311 const auto [highData, highTransform] = read_file(high_parameters_path);
0312
0313 return AtlasBetheHeitlerApprox(lowData, highData, lowTransform,
0314 highTransform, lowLimit, highLimit,
0315 clampToRange);
0316 }
0317 };
0318
0319
0320
0321
0322
0323
0324
0325 AtlasBetheHeitlerApprox<6, 5> makeDefaultBetheHeitlerApprox(
0326 bool clampToRange = false);
0327
0328 }