File indexing completed on 2026-10-06 08:10:00
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/TrackFitting/detail/GsfUtils.hpp"
0010
0011 #include "Acts/EventData/MeasurementHelpers.hpp"
0012 #include "Acts/EventData/ParticleHypothesis.hpp"
0013 #include "Acts/EventData/SubspaceHelpers.hpp"
0014 #include "Acts/EventData/Types.hpp"
0015 #include "Acts/Material/MaterialSlab.hpp"
0016
0017 #include <cstddef>
0018 #include <cstdint>
0019 #include <span>
0020
0021 namespace Acts {
0022
0023 double detail::Gsf::calculateDeterminant(
0024 const double *fullCalibratedCovariance,
0025 TrackStateTraits<kMeasurementSizeMax, true>::Covariance predictedCovariance,
0026 BoundSubspaceIndices projector, unsigned int calibratedSize) {
0027 return visit_measurement(calibratedSize, [&](auto N) {
0028 constexpr std::size_t kMeasurementSize = decltype(N)::value;
0029 std::span<const std::uint8_t, kMeasurementSize> validSubspaceIndices(
0030 projector.begin(), projector.begin() + kMeasurementSize);
0031 FixedBoundSubspaceHelper<kMeasurementSize> subspaceHelper(
0032 validSubspaceIndices);
0033
0034 typename Acts::TrackStateTraits<
0035 kMeasurementSize, true>::CalibratedCovariance calibratedCovariance{
0036 fullCalibratedCovariance};
0037
0038 const auto H = subspaceHelper.projector();
0039
0040 return (H * predictedCovariance * H.transpose() + calibratedCovariance)
0041 .determinant();
0042 });
0043 }
0044
0045 void detail::Gsf::removeLowWeightComponents(std::vector<GsfComponent> &cmps,
0046 double weightCutoff) {
0047 auto proj = [](auto &cmp) -> double & { return cmp.weight; };
0048
0049 normalizeWeights(cmps, proj);
0050
0051 auto newEnd = std::remove_if(cmps.begin(), cmps.end(), [&](auto &cmp) {
0052 return proj(cmp) < weightCutoff;
0053 });
0054
0055
0056 if (std::distance(cmps.begin(), newEnd) == 0) {
0057 cmps = {*std::max_element(cmps.begin(), cmps.end(), [&](auto &a, auto &b) {
0058 return proj(a) < proj(b);
0059 })};
0060 cmps.front().weight = 1.0;
0061 } else {
0062 cmps.erase(newEnd, cmps.end());
0063 normalizeWeights(cmps, proj);
0064 }
0065 }
0066
0067 double detail::Gsf::applyBetheHeitler(
0068 const GeometryContext &geoContext, const Surface &surface,
0069 Direction direction, const BoundTrackParameters &initialParameters,
0070 double initialWeight, const BetheHeitlerApprox &betheHeitlerApprox,
0071 std::vector<BetheHeitlerApprox::Component> &betheHeitlerCache,
0072 double weightCutoff, std::vector<GsfComponent> &componentCache,
0073 std::size_t &nInvalidBetheHeitler, double &maxPathXOverX0,
0074 const Logger &logger) {
0075 const double initialMomentum = initialParameters.absoluteMomentum();
0076 const ParticleHypothesis &particleHypothesis =
0077 initialParameters.particleHypothesis();
0078
0079
0080 const Vector2 lposition =
0081 initialParameters.parameters().template segment<2>(eBoundLoc0);
0082 MaterialSlab slab = surface.materialSlab(lposition, direction,
0083 MaterialUpdateMode::FullUpdate);
0084
0085 const double pathCorrection =
0086 surface.pathCorrection(geoContext, initialParameters.position(geoContext),
0087 initialParameters.direction());
0088 slab.scaleThickness(pathCorrection);
0089
0090 const double pathXOverX0 = slab.thicknessInX0();
0091 maxPathXOverX0 = std::max(maxPathXOverX0, pathXOverX0);
0092
0093
0094 if (!betheHeitlerApprox.validXOverX0(pathXOverX0)) {
0095 ++nInvalidBetheHeitler;
0096 ACTS_DEBUG("Bethe-Heitler approximation encountered invalid value for x/x0="
0097 << pathXOverX0 << " at surface " << surface.geometryId());
0098 }
0099
0100
0101 betheHeitlerCache.resize(betheHeitlerApprox.maxComponents());
0102 const auto mixture =
0103 betheHeitlerApprox.mixture(pathXOverX0, betheHeitlerCache);
0104
0105
0106 for (const GaussianComponent &gaussian : mixture) {
0107
0108
0109 const double newWeight = gaussian.weight * initialWeight;
0110
0111 if (newWeight < weightCutoff) {
0112 ACTS_VERBOSE("Skip component with weight " << newWeight);
0113 continue;
0114 }
0115
0116 if (gaussian.mean < 1e-8) {
0117 ACTS_WARNING("Skip component with gaussian " << gaussian.mean << " +- "
0118 << gaussian.var);
0119 continue;
0120 }
0121
0122
0123 BoundVector newPars = initialParameters.parameters();
0124
0125 const double deltaP = [&]() {
0126 if (direction == Direction::Forward()) {
0127 return initialMomentum * (gaussian.mean - 1);
0128 } else {
0129 return initialMomentum * (1 / gaussian.mean - 1);
0130 }
0131 }();
0132
0133 assert(initialMomentum + deltaP > 0 && "new momentum must be > 0");
0134 newPars[eBoundQOverP] = particleHypothesis.qOverP(
0135 initialMomentum + deltaP, initialParameters.charge());
0136
0137
0138 BoundMatrix newCov = initialParameters.covariance().value();
0139
0140 const double varInvP = [&]() {
0141 if (direction == Direction::Forward()) {
0142 const double f = 1 / (initialMomentum * gaussian.mean);
0143 return f * f * gaussian.var;
0144 } else {
0145 return gaussian.var / (initialMomentum * initialMomentum);
0146 }
0147 }();
0148
0149 newCov(eBoundQOverP, eBoundQOverP) += varInvP;
0150 assert(std::isfinite(newCov(eBoundQOverP, eBoundQOverP)) &&
0151 "new cov not finite");
0152
0153
0154 componentCache.emplace_back(newWeight, newPars, newCov);
0155 }
0156
0157 return pathXOverX0;
0158 }
0159
0160 }