Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-03 08:21:54

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 #include "Acts/TrackFitting/BetheHeitlerApprox.hpp"
0010 
0011 #include "Acts/Utilities/RangeXD.hpp"
0012 
0013 #include <algorithm>
0014 #include <stdexcept>
0015 
0016 namespace Acts {
0017 
0018 PolynomialBetheHeitlerApprox::PolynomialBetheHeitlerApprox(
0019     std::vector<RangeData> ranges, bool clampToRange, double noChangeLimit,
0020     double singleGaussianLimit)
0021     : m_ranges(std::move(ranges)),
0022       m_clampToRange(clampToRange),
0023       m_noChangeLimit(noChangeLimit),
0024       m_singleGaussianLimit(singleGaussianLimit) {
0025   if (m_ranges.empty()) {
0026     throw std::invalid_argument("At least one range is required");
0027   }
0028 
0029   // Sort ranges by minimum value
0030   std::ranges::sort(m_ranges, {}, [](const auto &r) { return r.range.min(); });
0031 
0032   // Validate that ranges don't overlap
0033   for (std::size_t i = 1; i < m_ranges.size(); ++i) {
0034     if (m_ranges[i - 1].range && m_ranges[i].range) {
0035       throw std::invalid_argument(
0036           "Overlapping ranges detected. Ranges must be non-overlapping.");
0037     }
0038   }
0039 }
0040 
0041 std::span<PolynomialBetheHeitlerApprox::Component>
0042 PolynomialBetheHeitlerApprox::mixture(
0043     double xOverX0, const std::span<Component> mixture) const {
0044   if (m_clampToRange) {
0045     xOverX0 = std::clamp(xOverX0, 0.0, rangeMax());
0046   }
0047 
0048   // Evaluate polynomial at x
0049   const auto poly = [](const double xx, const std::span<const double> coeffs) {
0050     double sum{0.};
0051     for (const auto c : coeffs) {
0052       sum = xx * sum + c;
0053     }
0054     return sum;
0055   };
0056 
0057   // Lambda which builds the components for a single range
0058   const auto make_mixture = [&](const Data &data, double xx,
0059                                 bool transform) -> std::span<Component> {
0060     // Value initialization should guarantee that all is initialized to zero
0061     double weight_sum = 0;
0062     for (std::size_t i = 0; i < data.size(); ++i) {
0063       // These transformations must be applied to the data according to ATHENA
0064       // (TrkGaussianSumFilter/src/GsfCombinedMaterialEffects.cxx:79)
0065       if (transform) {
0066         mixture[i] = detail::inverseTransformComponent(
0067             {poly(xx, data[i].weightCoeffs), poly(xx, data[i].meanCoeffs),
0068              poly(xx, data[i].varCoeffs)});
0069       } else {
0070         mixture[i].weight = poly(xx, data[i].weightCoeffs);
0071         mixture[i].mean = poly(xx, data[i].meanCoeffs);
0072         mixture[i].var = poly(xx, data[i].varCoeffs);
0073       }
0074 
0075       weight_sum += mixture[i].weight;
0076     }
0077 
0078     for (std::size_t i = 0; i < data.size(); ++i) {
0079       mixture[i].weight /= weight_sum;
0080     }
0081 
0082     return {mixture.data(), data.size()};
0083   };
0084 
0085   // Return no change
0086   if (xOverX0 < m_noChangeLimit) {
0087     mixture[0].weight = 1.0;
0088     mixture[0].mean = 1.0;  // p_initial = p_final
0089     mixture[0].var = 0.0;
0090 
0091     return {mixture.data(), 1};
0092   }
0093 
0094   // Return single gaussian approximation
0095   if (xOverX0 < m_singleGaussianLimit) {
0096     BetheHeitlerApproxSingleCmp().mixture(xOverX0, mixture);
0097     return {mixture.data(), 1};
0098   }
0099 
0100   // Find the appropriate range and return mixture for that range
0101   for (const auto &[range, data, transform] : m_ranges) {
0102     if (range.contains(xOverX0)) {
0103       return make_mixture(data, xOverX0, transform);
0104     }
0105   }
0106 
0107   // Should not reach here if validXOverX0 is called first
0108   // But return the last range's mixture as fallback
0109   const auto &[lastRange, lastData, lastTransform] = m_ranges.back();
0110   return make_mixture(lastData, lastRange.max(), lastTransform);
0111 }
0112 
0113 PolynomialBetheHeitlerApprox makeDefaultBetheHeitlerApprox(bool clampToRange) {
0114   // Tracking/TrkFitter/TrkGaussianSumFilterUtils/Data/BetheHeitler_cdf_nC6_O5.par
0115   // clang-format off
0116   static PolynomialBetheHeitlerApprox::Data cdf_cmps6_order5_data = {{
0117       // Component #1
0118       {
0119           {{3.74397e+004,-1.95241e+004, 3.51047e+003,-2.54377e+002, 1.81080e+001,-3.57643e+000}},
0120           {{3.56728e+004,-1.78603e+004, 2.81521e+003,-8.93555e+001,-1.14015e+001, 2.55769e-001}},
0121           {{3.73938e+004,-1.92800e+004, 3.21580e+003,-1.46203e+002,-5.65392e+000,-2.78008e+000}}
0122       },
0123       // Component #2
0124       {
0125           {{-4.14035e+004, 2.31883e+004,-4.37145e+003, 2.44289e+002, 1.13098e+001,-3.21230e+000}},
0126           {{-2.06936e+003, 2.65334e+003,-1.01413e+003, 1.78338e+002,-1.85556e+001, 1.91430e+000}},
0127           {{-5.19068e+004, 2.55327e+004,-4.22147e+003, 1.90227e+002, 9.34602e+000,-4.80961e+000}}
0128       },
0129       // Component #3
0130       {
0131           {{2.52200e+003,-4.86348e+003, 2.11942e+003,-3.84534e+002, 2.94503e+001,-2.83310e+000}},
0132           {{1.80405e+003,-1.93347e+003, 6.27196e+002,-4.32429e+001,-1.43533e+001, 3.58782e+000}},
0133           {{-4.61617e+004, 1.78221e+004,-1.95746e+003,-8.80646e+001, 3.43153e+001,-7.57830e+000}}
0134       },
0135       // Component #4
0136       {
0137           {{4.94537e+003,-2.08737e+003, 1.78089e+002, 2.29879e+001,-5.52783e+000,-1.86800e+000}},
0138           {{4.60220e+003,-1.62269e+003,-1.57552e+002, 2.01796e+002,-5.01636e+001, 6.47438e+000}},
0139           {{-9.50373e+004, 4.05517e+004,-5.62596e+003, 4.58534e+001, 6.70479e+001,-1.22430e+001}}
0140       },
0141       // Component #5
0142       {
0143           {{-1.04129e+003, 1.15222e+002,-2.70356e+001, 3.18611e+001,-7.78800e+000,-1.50242e+000}},
0144           {{-2.71361e+004, 2.00625e+004,-6.19444e+003, 1.10061e+003,-1.29354e+002, 1.08289e+001}},
0145           {{3.15252e+004,-3.31508e+004, 1.20371e+004,-2.23822e+003, 2.44396e+002,-2.09130e+001}}
0146       },
0147       // Component #6
0148       {
0149           {{1.27751e+004,-6.79813e+003, 1.24650e+003,-8.20622e+001,-2.33476e+000, 2.46459e-001}},
0150           {{3.64336e+005,-2.08457e+005, 4.33028e+004,-3.67825e+003, 4.22914e+001, 1.42701e+001}},
0151           {{-1.79298e+006, 1.01843e+006,-2.10037e+005, 1.82222e+004,-4.33573e+002,-2.72725e+001}}
0152       },
0153   }};
0154   // clang-format on
0155 
0156   std::vector<PolynomialBetheHeitlerApprox::RangeData> ranges = {
0157       {Range1D<double>{0.0, 0.2}, cdf_cmps6_order5_data, true}};
0158 
0159   return PolynomialBetheHeitlerApprox(std::move(ranges), clampToRange, 0.0001,
0160                                       0.002);
0161 }
0162 
0163 }  // namespace Acts