Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-11 07:50:03

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 #pragma once
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Tolerance.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/EventData/MeasurementHelpers.hpp"
0015 #include "Acts/EventData/MultiTrajectoryHelpers.hpp"
0016 #include "Acts/EventData/TrackContainerFrontendConcept.hpp"
0017 #include "Acts/EventData/TrackParameters.hpp"
0018 #include "Acts/EventData/TrackProxyConcept.hpp"
0019 #include "Acts/EventData/TrackStateProxyConcept.hpp"
0020 #include "Acts/EventData/TrackStateType.hpp"
0021 #include "Acts/Geometry/GeometryContext.hpp"
0022 #include "Acts/Propagator/StandardAborters.hpp"
0023 #include "Acts/Surfaces/Surface.hpp"
0024 #include "Acts/TrackFitting/GainMatrixSmoother.hpp"
0025 #include "Acts/Utilities/Logger.hpp"
0026 #include "Acts/Utilities/Result.hpp"
0027 
0028 #include <utility>
0029 
0030 namespace Acts {
0031 
0032 enum class TrackExtrapolationStrategy {
0033   /// Use the first track state to reach target surface
0034   first,
0035   /// Use the last track state to reach target surface
0036   last,
0037   /// Use the first or last track state to reach target surface depending on the
0038   /// distance
0039   firstOrLast,
0040 };
0041 
0042 enum class TrackExtrapolationError {
0043   CompatibleTrackStateNotFound = 1,
0044   ReferenceSurfaceUnreachable = 2,
0045 };
0046 
0047 std::error_code make_error_code(TrackExtrapolationError e);
0048 
0049 template <TrackProxyConcept track_proxy_t>
0050 Result<typename track_proxy_t::ConstTrackStateProxy> findFirstMeasurementState(
0051     const track_proxy_t &track) {
0052   using TrackStateProxy = typename track_proxy_t::ConstTrackStateProxy;
0053 
0054   // TODO specialize if track is forward linked
0055 
0056   std::optional<TrackStateProxy> firstMeasurementOpt;
0057 
0058   for (const auto &trackState : track.trackStatesReversed()) {
0059     bool isMeasurement =
0060         trackState.typeFlags().test(TrackStateFlag::MeasurementFlag);
0061     bool isOutlier = trackState.typeFlags().test(TrackStateFlag::OutlierFlag);
0062 
0063     if (isMeasurement && !isOutlier) {
0064       firstMeasurementOpt = trackState;
0065     }
0066   }
0067 
0068   if (firstMeasurementOpt) {
0069     return Result<TrackStateProxy>::success(*firstMeasurementOpt);
0070   }
0071 
0072   return Result<TrackStateProxy>::failure(
0073       TrackExtrapolationError::CompatibleTrackStateNotFound);
0074 }
0075 
0076 template <TrackProxyConcept track_proxy_t>
0077 Result<typename track_proxy_t::ConstTrackStateProxy> findLastMeasurementState(
0078     const track_proxy_t &track) {
0079   using TrackStateProxy = typename track_proxy_t::ConstTrackStateProxy;
0080 
0081   for (const auto &trackState : track.trackStatesReversed()) {
0082     bool isMeasurement =
0083         trackState.typeFlags().test(TrackStateFlag::MeasurementFlag);
0084     bool isOutlier = trackState.typeFlags().test(TrackStateFlag::OutlierFlag);
0085 
0086     if (isMeasurement && !isOutlier) {
0087       return TrackStateProxy{trackState};
0088     }
0089   }
0090 
0091   return Result<TrackStateProxy>::failure(
0092       TrackExtrapolationError::CompatibleTrackStateNotFound);
0093 }
0094 
0095 /// @brief Smooth a track using the gain matrix smoother
0096 ///
0097 /// @tparam track_proxy_t The track proxy type
0098 /// @tparam smoother_t The smoother type
0099 ///
0100 /// @param geoContext The geometry context
0101 /// @param track The track to smooth
0102 /// @param logger The logger
0103 /// @param smoother The smoother
0104 ///
0105 /// @return The result of the smoothing
0106 template <TrackProxyConcept track_proxy_t,
0107           typename smoother_t = GainMatrixSmoother>
0108 Result<void> smoothTrack(
0109     const GeometryContext &geoContext, track_proxy_t &track,
0110     const Logger &logger = *getDefaultLogger("TrackSmoother", Logging::INFO),
0111     smoother_t smoother = GainMatrixSmoother()) {
0112   auto &trackContainer = track.container();
0113   auto &trackStateContainer = trackContainer.trackStateContainer();
0114 
0115   auto last = findLastMeasurementState(track);
0116   if (!last.ok()) {
0117     ACTS_ERROR("no last track state found");
0118     return last.error();
0119   }
0120 
0121   auto smoothingResult =
0122       smoother(geoContext, trackStateContainer, last->index(), logger);
0123 
0124   if (!smoothingResult.ok()) {
0125     ACTS_ERROR("Smoothing track " << track.index() << " failed with error "
0126                                   << smoothingResult.error());
0127     return smoothingResult.error();
0128   }
0129 
0130   return Result<void>::success();
0131 }
0132 
0133 /// @brief Smooth tracks using the gain matrix smoother
0134 ///
0135 /// @tparam track_container_t The track container type
0136 ///
0137 /// @param geoContext The geometry context
0138 /// @param trackContainer The track container
0139 /// @param logger The logger
0140 ///
0141 /// @return The result of the smoothing
0142 template <TrackContainerFrontend track_container_t>
0143 Result<void> smoothTracks(
0144     const GeometryContext &geoContext, const track_container_t &trackContainer,
0145     const Logger &logger = *getDefaultLogger("TrackSmoother", Logging::INFO)) {
0146   Result<void> result = Result<void>::success();
0147 
0148   for (const auto &track : trackContainer) {
0149     auto smoothingResult = smoothTrack(geoContext, track, logger);
0150 
0151     // Only keep the first error
0152     if (!smoothingResult.ok() && result.ok()) {
0153       result = smoothingResult.error();
0154     }
0155   }
0156 
0157   return result;
0158 }
0159 
0160 /// @brief Find a track state for extrapolation
0161 ///
0162 /// @tparam track_proxy_t The track proxy type
0163 ///
0164 /// @param geoContext The geometry context
0165 /// @param track The track
0166 /// @param referenceSurface The reference surface
0167 /// @param strategy The extrapolation strategy
0168 /// @param logger The logger
0169 ///
0170 /// @return The result of the search containing the track state
0171 ///         and the distance to the reference surface
0172 template <TrackProxyConcept track_proxy_t>
0173 Result<std::pair<typename track_proxy_t::ConstTrackStateProxy, double>>
0174 findTrackStateForExtrapolation(
0175     const GeometryContext &geoContext, const track_proxy_t &track,
0176     const Surface &referenceSurface, TrackExtrapolationStrategy strategy,
0177     const Logger &logger = *getDefaultLogger("TrackExtrapolation",
0178                                              Logging::INFO)) {
0179   using TrackStateProxy = typename track_proxy_t::ConstTrackStateProxy;
0180 
0181   auto intersect = [&](const TrackStateProxy &state) -> SurfaceIntersection {
0182     assert(state.hasSmoothed() || state.hasFiltered());
0183 
0184     FreeVector freeVector;
0185     if (state.hasSmoothed()) {
0186       freeVector = MultiTrajectoryHelpers::freeSmoothed(geoContext, state);
0187     } else {
0188       freeVector = MultiTrajectoryHelpers::freeFiltered(geoContext, state);
0189     }
0190 
0191     return referenceSurface
0192         .intersect(geoContext, freeVector.template segment<3>(eFreePos0),
0193                    freeVector.template segment<3>(eFreeDir0),
0194                    BoundaryTolerance::None(), s_onSurfaceTolerance)
0195         .closest();
0196   };
0197 
0198   switch (strategy) {
0199     case TrackExtrapolationStrategy::first: {
0200       ACTS_VERBOSE("looking for first track state");
0201 
0202       auto first = findFirstMeasurementState(track);
0203       if (!first.ok()) {
0204         ACTS_ERROR("no first track state found");
0205         return first.error();
0206       }
0207 
0208       SurfaceIntersection intersection = intersect(*first);
0209       if (!intersection.isValid()) {
0210         ACTS_ERROR("no intersection found");
0211         return Result<std::pair<TrackStateProxy, double>>::failure(
0212             TrackExtrapolationError::ReferenceSurfaceUnreachable);
0213       }
0214 
0215       ACTS_VERBOSE("found intersection at " << intersection.pathLength());
0216       return std::pair(*first, intersection.pathLength());
0217     }
0218 
0219     case TrackExtrapolationStrategy::last: {
0220       ACTS_VERBOSE("looking for last track state");
0221 
0222       auto last = findLastMeasurementState(track);
0223       if (!last.ok()) {
0224         ACTS_ERROR("no last track state found");
0225         return last.error();
0226       }
0227 
0228       SurfaceIntersection intersection = intersect(*last);
0229       if (!intersection.isValid()) {
0230         ACTS_ERROR("no intersection found");
0231         return Result<std::pair<TrackStateProxy, double>>::failure(
0232             TrackExtrapolationError::ReferenceSurfaceUnreachable);
0233       }
0234 
0235       ACTS_VERBOSE("found intersection at " << intersection.pathLength());
0236       return std::pair(*last, intersection.pathLength());
0237     }
0238 
0239     case TrackExtrapolationStrategy::firstOrLast: {
0240       ACTS_VERBOSE("looking for first or last track state");
0241 
0242       auto first = findFirstMeasurementState(track);
0243       if (!first.ok()) {
0244         ACTS_ERROR("no first track state found");
0245         return first.error();
0246       }
0247 
0248       auto last = findLastMeasurementState(track);
0249       if (!last.ok()) {
0250         ACTS_ERROR("no last track state found");
0251         return last.error();
0252       }
0253 
0254       SurfaceIntersection intersectionFirst = intersect(*first);
0255       SurfaceIntersection intersectionLast = intersect(*last);
0256 
0257       double absDistanceFirst = std::abs(intersectionFirst.pathLength());
0258       double absDistanceLast = std::abs(intersectionLast.pathLength());
0259 
0260       if (intersectionFirst.isValid() && absDistanceFirst <= absDistanceLast) {
0261         ACTS_VERBOSE("using first track state with intersection at "
0262                      << intersectionFirst.pathLength());
0263         return std::pair(*first, intersectionFirst.pathLength());
0264       }
0265 
0266       if (intersectionLast.isValid() && absDistanceLast <= absDistanceFirst) {
0267         ACTS_VERBOSE("using last track state with intersection at "
0268                      << intersectionLast.pathLength());
0269         return std::pair(*last, intersectionLast.pathLength());
0270       }
0271 
0272       ACTS_ERROR("no intersection found");
0273       return Result<std::pair<TrackStateProxy, double>>::failure(
0274           TrackExtrapolationError::ReferenceSurfaceUnreachable);
0275     }
0276   }
0277 
0278   // unreachable
0279   return Result<std::pair<TrackStateProxy, double>>::failure(
0280       TrackExtrapolationError::CompatibleTrackStateNotFound);
0281 }
0282 
0283 /// @brief Extrapolate a track to a reference surface
0284 ///
0285 /// @tparam track_proxy_t The track proxy type
0286 /// @tparam propagator_t The propagator type
0287 /// @tparam propagator_options_t The propagator options type
0288 ///
0289 /// @param track The track which is modified in-place
0290 /// @param referenceSurface The reference surface
0291 /// @param propagator The propagator
0292 /// @param options The propagator options
0293 /// @param strategy The extrapolation strategy
0294 /// @param logger The logger
0295 ///
0296 /// @return The result of the extrapolation
0297 template <TrackProxyConcept track_proxy_t, typename propagator_t,
0298           typename propagator_options_t>
0299 Result<void> extrapolateTrackToReferenceSurface(
0300     track_proxy_t &track, const Surface &referenceSurface,
0301     const propagator_t &propagator, propagator_options_t options,
0302     TrackExtrapolationStrategy strategy,
0303     const Logger &logger = *getDefaultLogger("TrackExtrapolation",
0304                                              Logging::INFO)) {
0305   auto findResult = findTrackStateForExtrapolation(
0306       options.geoContext, track, referenceSurface, strategy, logger);
0307 
0308   if (!findResult.ok()) {
0309     ACTS_ERROR("failed to find track state for extrapolation");
0310     return findResult.error();
0311   }
0312 
0313   auto &[trackState, distance] = *findResult;
0314 
0315   options.direction = Direction::fromScalarZeroAsPositive(distance);
0316 
0317   BoundTrackParameters parameters = track.createParametersFromState(trackState);
0318   ACTS_VERBOSE("extrapolating track to reference surface at distance "
0319                << distance << " with direction " << options.direction
0320                << " with starting parameters " << parameters);
0321 
0322   auto propagateResult =
0323       propagator.template propagate<BoundTrackParameters, propagator_options_t,
0324                                     ForcedSurfaceReached>(
0325           parameters, referenceSurface, options);
0326 
0327   if (!propagateResult.ok()) {
0328     ACTS_ERROR("failed to extrapolate track: " << propagateResult.error());
0329     return propagateResult.error();
0330   }
0331 
0332   track.setReferenceSurface(referenceSurface.getSharedPtr());
0333   track.parameters() = propagateResult->endParameters.value().parameters();
0334   track.covariance() =
0335       propagateResult->endParameters.value().covariance().value();
0336 
0337   return Result<void>::success();
0338 }
0339 
0340 /// @brief Extrapolate tracks to a reference surface
0341 ///
0342 /// @tparam track_container_t The track container type
0343 /// @tparam propagator_t The propagator type
0344 /// @tparam propagator_options_t The propagator options type
0345 ///
0346 /// @param trackContainer The track container which is modified in-place
0347 /// @param referenceSurface The reference surface
0348 /// @param propagator The propagator
0349 /// @param options The propagator options
0350 /// @param strategy The extrapolation strategy
0351 /// @param logger The logger
0352 ///
0353 /// @return The result of the extrapolation
0354 template <TrackContainerFrontend track_container_t, typename propagator_t,
0355           typename propagator_options_t>
0356 Result<void> extrapolateTracksToReferenceSurface(
0357     const track_container_t &trackContainer, const Surface &referenceSurface,
0358     const propagator_t &propagator, propagator_options_t options,
0359     TrackExtrapolationStrategy strategy,
0360     const Logger &logger = *getDefaultLogger("TrackExtrapolation",
0361                                              Logging::INFO)) {
0362   Result<void> result = Result<void>::success();
0363 
0364   for (const auto &track : trackContainer) {
0365     auto extrapolateResult = extrapolateTrackToReferenceSurface(
0366         track, referenceSurface, propagator, options, strategy, logger);
0367 
0368     // Only keep the first error
0369     if (!extrapolateResult.ok() && result.ok()) {
0370       result = extrapolateResult.error();
0371     }
0372   }
0373 
0374   return result;
0375 }
0376 
0377 /// Helper function to calculate a number of track level quantities and store
0378 /// them on the track itself
0379 /// @tparam track_proxy_t The track proxy type
0380 /// @param track A mutable track proxy to operate on
0381 template <TrackProxyConcept track_proxy_t>
0382 void calculateTrackQuantities(track_proxy_t track)
0383   requires(!track_proxy_t::ReadOnly)
0384 {
0385   track.chi2() = 0;
0386   track.nDoF() = 0;
0387 
0388   track.nHoles() = 0;
0389   track.nMeasurements() = 0;
0390   track.nSharedHits() = 0;
0391   track.nOutliers() = 0;
0392 
0393   for (const auto &trackState : track.trackStatesReversed()) {
0394     ConstTrackStateType typeFlags = trackState.typeFlags();
0395 
0396     if (typeFlags.test(Acts::TrackStateFlag::HoleFlag)) {
0397       track.nHoles()++;
0398     } else if (typeFlags.test(Acts::TrackStateFlag::OutlierFlag)) {
0399       track.nOutliers()++;
0400     } else if (typeFlags.test(Acts::TrackStateFlag::MeasurementFlag)) {
0401       if (typeFlags.test(Acts::TrackStateFlag::SharedHitFlag)) {
0402         track.nSharedHits()++;
0403       }
0404       track.nMeasurements()++;
0405       track.chi2() += trackState.chi2();
0406       track.nDoF() += trackState.calibratedSize();
0407     }
0408   }
0409 }
0410 
0411 /// Helper function to trim track states from the front of a track
0412 /// @tparam track_proxy_t the track proxy type
0413 /// @param track the track to trim
0414 /// @param trimHoles whether to trim holes
0415 /// @param trimOutliers whether to trim outliers
0416 /// @param trimMaterial whether to trim pure material states
0417 /// @param trimOtherNoneMeasurement whether to trim other, non measurement, states
0418 template <TrackProxyConcept track_proxy_t>
0419 void trimTrackFront(track_proxy_t track, bool trimHoles, bool trimOutliers,
0420                     bool trimMaterial, bool trimOtherNoneMeasurement)
0421   requires(!track_proxy_t::ReadOnly)
0422 {
0423   using TrackStateProxy = typename track_proxy_t::TrackStateProxy;
0424 
0425   // TODO specialize if track is forward linked
0426 
0427   std::optional<TrackStateProxy> front;
0428 
0429   for (TrackStateProxy trackState : track.trackStatesReversed()) {
0430     TrackStateType typeFlags = trackState.typeFlags();
0431     bool isHole = typeFlags.test(TrackStateFlag::HoleFlag);
0432     bool isOutlier = typeFlags.test(TrackStateFlag::OutlierFlag);
0433     bool isMaterial = typeFlags.test(TrackStateFlag::MaterialFlag) &&
0434                       !typeFlags.test(TrackStateFlag::MeasurementFlag);
0435     bool isOtherNoneMeasurement =
0436         !typeFlags.test(TrackStateFlag::MeasurementFlag) && !isHole &&
0437         !isOutlier && !isMaterial;
0438     if (trimHoles && isHole) {
0439       continue;
0440     }
0441     if (trimOutliers && isOutlier) {
0442       continue;
0443     }
0444     if (trimMaterial && isMaterial) {
0445       continue;
0446     }
0447     if (trimOtherNoneMeasurement && isOtherNoneMeasurement) {
0448       continue;
0449     }
0450 
0451     front = trackState;
0452   }
0453 
0454   if (front.has_value()) {
0455     front.value().previous() = TrackStateProxy::kInvalid;
0456   }
0457 }
0458 
0459 /// Helper function to trim track states from the back of a track
0460 /// @tparam track_proxy_t the track proxy type
0461 /// @param track the track to trim
0462 /// @param trimHoles whether to trim holes
0463 /// @param trimOutliers whether to trim outliers
0464 /// @param trimMaterial whether to trim pure material states
0465 /// @param trimOtherNoneMeasurement whether to trim other, non measurement, states
0466 template <TrackProxyConcept track_proxy_t>
0467 void trimTrackBack(track_proxy_t track, bool trimHoles, bool trimOutliers,
0468                    bool trimMaterial, bool trimOtherNoneMeasurement)
0469   requires(!track_proxy_t::ReadOnly)
0470 {
0471   using TrackStateProxy = typename track_proxy_t::TrackStateProxy;
0472 
0473   std::optional<TrackStateProxy> back;
0474 
0475   for (TrackStateProxy trackState : track.trackStatesReversed()) {
0476     back = trackState;
0477 
0478     TrackStateType typeFlags = trackState.typeFlags();
0479     bool isHole = typeFlags.test(TrackStateFlag::HoleFlag);
0480     bool isOutlier = typeFlags.test(TrackStateFlag::OutlierFlag);
0481     bool isMaterial = typeFlags.test(TrackStateFlag::MaterialFlag) &&
0482                       !typeFlags.test(TrackStateFlag::MeasurementFlag);
0483     bool isOtherNoneMeasurement =
0484         !typeFlags.test(TrackStateFlag::MeasurementFlag) && !isHole &&
0485         !isOutlier && !isMaterial;
0486     if (trimHoles && isHole) {
0487       continue;
0488     }
0489     if (trimOutliers && isOutlier) {
0490       continue;
0491     }
0492     if (trimMaterial && isMaterial) {
0493       continue;
0494     }
0495     if (trimOtherNoneMeasurement && isOtherNoneMeasurement) {
0496       continue;
0497     }
0498 
0499     break;
0500   }
0501 
0502   if (back.has_value()) {
0503     track.tipIndex() = back.value().index();
0504   }
0505 }
0506 
0507 /// Helper function to trim track states from the front and back of a track
0508 /// @tparam track_proxy_t the track proxy type
0509 /// @param track the track to trim
0510 /// @param trimHoles whether to trim holes
0511 /// @param trimOutliers whether to trim outliers
0512 /// @param trimMaterial whether to trim pure material states
0513 /// @param trimOtherNoneMeasurement whether to trim other, non measurement, states
0514 template <TrackProxyConcept track_proxy_t>
0515 void trimTrack(track_proxy_t track, bool trimHoles, bool trimOutliers,
0516                bool trimMaterial, bool trimOtherNoneMeasurement)
0517   requires(!track_proxy_t::ReadOnly)
0518 {
0519   trimTrackFront(track, trimHoles, trimOutliers, trimMaterial,
0520                  trimOtherNoneMeasurement);
0521   trimTrackBack(track, trimHoles, trimOutliers, trimMaterial,
0522                 trimOtherNoneMeasurement);
0523 }
0524 
0525 /// Helper function to calculate the predicted residual and its covariance
0526 /// @tparam nMeasurementDim the dimension of the measurement
0527 /// @tparam track_state_proxy_t the track state proxy type
0528 /// @param trackState the track state to calculate the residual from
0529 /// @return a pair of the residual and its covariance
0530 template <std::size_t nMeasurementDim,
0531           TrackStateProxyConcept track_state_proxy_t>
0532 std::pair<ActsVector<nMeasurementDim>, ActsSquareMatrix<nMeasurementDim>>
0533 calculatePredictedResidual(track_state_proxy_t trackState) {
0534   using MeasurementVector = ActsVector<nMeasurementDim>;
0535   using MeasurementMatrix = ActsSquareMatrix<nMeasurementDim>;
0536 
0537   if (!trackState.hasPredicted()) {
0538     throw std::invalid_argument("track state has no predicted parameters");
0539   }
0540   if (!trackState.hasCalibrated()) {
0541     throw std::invalid_argument("track state has no calibrated parameters");
0542   }
0543 
0544   auto subspaceHelper =
0545       trackState.template projectorSubspaceHelper<nMeasurementDim>();
0546 
0547   auto measurement = trackState.template calibrated<nMeasurementDim>();
0548   auto measurementCovariance =
0549       trackState.template calibratedCovariance<nMeasurementDim>();
0550   MeasurementVector predicted =
0551       subspaceHelper.projectVector(trackState.predicted());
0552   MeasurementMatrix predictedCovariance =
0553       subspaceHelper.projectMatrix(trackState.predictedCovariance());
0554 
0555   MeasurementVector residual = measurement - predicted;
0556   MeasurementMatrix residualCovariance =
0557       measurementCovariance + predictedCovariance;
0558 
0559   return {residual, residualCovariance};
0560 }
0561 
0562 /// Helper function to calculate the filtered residual and its covariance
0563 /// @tparam nMeasurementDim the dimension of the measurement
0564 /// @tparam track_state_proxy_t the track state proxy type
0565 /// @param trackState the track state to calculate the residual from
0566 /// @return a pair of the residual and its covariance
0567 template <std::size_t nMeasurementDim,
0568           TrackStateProxyConcept track_state_proxy_t>
0569 std::pair<ActsVector<nMeasurementDim>, ActsSquareMatrix<nMeasurementDim>>
0570 calculateFilteredResidual(track_state_proxy_t trackState) {
0571   using MeasurementVector = ActsVector<nMeasurementDim>;
0572   using MeasurementMatrix = ActsSquareMatrix<nMeasurementDim>;
0573 
0574   if (!trackState.hasFiltered()) {
0575     throw std::invalid_argument("track state has no filtered parameters");
0576   }
0577   if (!trackState.hasCalibrated()) {
0578     throw std::invalid_argument("track state has no calibrated parameters");
0579   }
0580 
0581   auto subspaceHelper =
0582       trackState.template projectorSubspaceHelper<nMeasurementDim>();
0583 
0584   auto measurement = trackState.template calibrated<nMeasurementDim>();
0585   auto measurementCovariance =
0586       trackState.template calibratedCovariance<nMeasurementDim>();
0587   MeasurementVector filtered =
0588       subspaceHelper.projectVector(trackState.filtered());
0589   MeasurementMatrix filteredCovariance =
0590       subspaceHelper.projectMatrix(trackState.filteredCovariance());
0591 
0592   MeasurementVector residual = measurement - filtered;
0593   MeasurementMatrix residualCovariance =
0594       measurementCovariance + filteredCovariance;
0595 
0596   return {residual, residualCovariance};
0597 }
0598 
0599 /// Helper function to calculate the smoothed residual and its covariance
0600 /// @tparam nMeasurementDim the dimension of the measurement
0601 /// @tparam track_state_proxy_t the track state proxy type
0602 /// @param trackState the track state to calculate the residual from
0603 /// @return a pair of the residual and its covariance
0604 template <std::size_t nMeasurementDim,
0605           TrackStateProxyConcept track_state_proxy_t>
0606 std::pair<ActsVector<nMeasurementDim>, ActsSquareMatrix<nMeasurementDim>>
0607 calculateSmoothedResidual(track_state_proxy_t trackState) {
0608   using MeasurementVector = ActsVector<nMeasurementDim>;
0609   using MeasurementMatrix = ActsSquareMatrix<nMeasurementDim>;
0610 
0611   if (!trackState.hasSmoothed()) {
0612     throw std::invalid_argument("track state has no smoothed parameters");
0613   }
0614   if (!trackState.hasCalibrated()) {
0615     throw std::invalid_argument("track state has no calibrated parameters");
0616   }
0617 
0618   auto subspaceHelper =
0619       trackState.template projectorSubspaceHelper<nMeasurementDim>();
0620 
0621   auto measurement = trackState.template calibrated<nMeasurementDim>();
0622   auto measurementCovariance =
0623       trackState.template calibratedCovariance<nMeasurementDim>();
0624   MeasurementVector smoothed =
0625       subspaceHelper.projectVector(trackState.smoothed());
0626   MeasurementMatrix smoothedCovariance =
0627       subspaceHelper.projectMatrix(trackState.smoothedCovariance());
0628 
0629   MeasurementVector residual = measurement - smoothed;
0630   MeasurementMatrix residualCovariance =
0631       measurementCovariance + smoothedCovariance;
0632 
0633   return {residual, residualCovariance};
0634 }
0635 
0636 /// Helper function to calculate the predicted chi2
0637 /// @tparam track_state_proxy_t the track state proxy type
0638 /// @param trackState the track state to calculate the chi2 from
0639 /// @return the chi2
0640 template <TrackStateProxyConcept track_state_proxy_t>
0641 double calculatePredictedChi2(track_state_proxy_t trackState) {
0642   if (!trackState.hasPredicted()) {
0643     throw std::invalid_argument("track state has no predicted parameters");
0644   }
0645   if (!trackState.hasCalibrated()) {
0646     throw std::invalid_argument("track state has no calibrated parameters");
0647   }
0648 
0649   return visit_measurement(
0650       trackState.calibratedSize(),
0651       [&]<std::size_t measdim>(
0652           std::integral_constant<std::size_t, measdim>) -> double {
0653         auto [residual, residualCovariance] =
0654             calculatePredictedResidual<measdim>(trackState);
0655 
0656         return (residual.transpose() * residualCovariance.inverse() * residual)
0657             .eval()(0, 0);
0658       });
0659 }
0660 
0661 /// Helper function to calculate the filtered chi2
0662 /// @tparam track_state_proxy_t the track state proxy type
0663 /// @param trackState the track state to calculate the chi2 from
0664 /// @return the chi2
0665 template <TrackStateProxyConcept track_state_proxy_t>
0666 double calculateFilteredChi2(track_state_proxy_t trackState) {
0667   if (!trackState.hasFiltered()) {
0668     throw std::invalid_argument("track state has no filtered parameters");
0669   }
0670   if (!trackState.hasCalibrated()) {
0671     throw std::invalid_argument("track state has no calibrated parameters");
0672   }
0673 
0674   return visit_measurement(
0675       trackState.calibratedSize(),
0676       [&]<std::size_t measdim>(
0677           std::integral_constant<std::size_t, measdim>) -> double {
0678         auto [residual, residualCovariance] =
0679             calculateFilteredResidual<measdim>(trackState);
0680 
0681         return (residual.transpose() * residualCovariance.inverse() * residual)
0682             .eval()(0, 0);
0683       });
0684 }
0685 
0686 /// Helper function to calculate the smoothed chi2
0687 /// @tparam track_state_proxy_t the track state proxy type
0688 /// @param trackState the track state to calculate the chi2 from
0689 /// @return the chi2
0690 template <TrackStateProxyConcept track_state_proxy_t>
0691 double calculateSmoothedChi2(track_state_proxy_t trackState) {
0692   if (!trackState.hasSmoothed()) {
0693     throw std::invalid_argument("track state has no smoothed parameters");
0694   }
0695   if (!trackState.hasCalibrated()) {
0696     throw std::invalid_argument("track state has no calibrated parameters");
0697   }
0698 
0699   return visit_measurement(
0700       trackState.calibratedSize(),
0701       [&]<std::size_t measdim>(
0702           std::integral_constant<std::size_t, measdim>) -> double {
0703         auto [residual, residualCovariance] =
0704             calculateSmoothedResidual<measdim>(trackState);
0705 
0706         return (residual.transpose() * residualCovariance.inverse() * residual)
0707             .eval()(0, 0);
0708       });
0709 }
0710 
0711 /// Helper function to calculate the unbiased track parameters and their
0712 /// covariance (i.e. fitted track parameters with this measurement removed)
0713 /// using Eq.(12a)-Eq.(12c) of NIMA 262, 444 (1987)
0714 /// @tparam track_state_proxy_t the track state proxy type
0715 /// @param trackState the track state to calculate the unbiased parameters from
0716 /// @return a pair of the unbiased parameters and their covariance
0717 template <TrackStateProxyConcept track_state_proxy_t>
0718 std::pair<BoundVector, BoundMatrix> calculateUnbiasedParametersCovariance(
0719     track_state_proxy_t trackState) {
0720   if (!trackState.hasSmoothed()) {
0721     throw std::invalid_argument("track state has no smoothed parameters");
0722   }
0723   if (!trackState.hasCalibrated()) {
0724     throw std::invalid_argument("track state has no calibrated parameters");
0725   }
0726 
0727   return visit_measurement(
0728       trackState.calibratedSize(),
0729       [&]<std::size_t measdim>(std::integral_constant<std::size_t, measdim>) {
0730         FixedBoundSubspaceHelper<measdim> subspaceHelper =
0731             trackState.template projectorSubspaceHelper<measdim>();
0732 
0733         // TODO use subspace helper for projection instead
0734         auto H = subspaceHelper.projector();
0735         auto s = trackState.smoothed();
0736         auto C = trackState.smoothedCovariance();
0737         auto m = trackState.template calibrated<measdim>();
0738         auto V = trackState.template calibratedCovariance<measdim>();
0739         auto K =
0740             (C * H.transpose() * (H * C * H.transpose() - V).inverse()).eval();
0741         BoundVector unbiasedParamsVec = s + K * (m - H * s);
0742         BoundMatrix unbiasedParamsCov = C - K * H * C;
0743         return std::make_pair(unbiasedParamsVec, unbiasedParamsCov);
0744       });
0745 }
0746 
0747 }  // namespace Acts
0748 
0749 namespace std {
0750 // register with STL
0751 template <>
0752 struct is_error_code_enum<Acts::TrackExtrapolationError> : std::true_type {};
0753 }  // namespace std