File indexing completed on 2025-07-11 07:50:03
0001
0002
0003
0004
0005
0006
0007
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
0034 first,
0035
0036 last,
0037
0038
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
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
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
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
0134
0135
0136
0137
0138
0139
0140
0141
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
0152 if (!smoothingResult.ok() && result.ok()) {
0153 result = smoothingResult.error();
0154 }
0155 }
0156
0157 return result;
0158 }
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169
0170
0171
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
0279 return Result<std::pair<TrackStateProxy, double>>::failure(
0280 TrackExtrapolationError::CompatibleTrackStateNotFound);
0281 }
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
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
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
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
0369 if (!extrapolateResult.ok() && result.ok()) {
0370 result = extrapolateResult.error();
0371 }
0372 }
0373
0374 return result;
0375 }
0376
0377
0378
0379
0380
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
0412
0413
0414
0415
0416
0417
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
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
0460
0461
0462
0463
0464
0465
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
0508
0509
0510
0511
0512
0513
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
0526
0527
0528
0529
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
0563
0564
0565
0566
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
0600
0601
0602
0603
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
0637
0638
0639
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
0662
0663
0664
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
0687
0688
0689
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
0712
0713
0714
0715
0716
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
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 }
0748
0749 namespace std {
0750
0751 template <>
0752 struct is_error_code_enum<Acts::TrackExtrapolationError> : std::true_type {};
0753 }