File indexing completed on 2026-05-27 07:23:36
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Common.hpp"
0012 #include "Acts/EventData/MultiTrajectory.hpp"
0013 #include "Acts/EventData/MultiTrajectoryHelpers.hpp"
0014 #include "Acts/EventData/SourceLink.hpp"
0015 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0016 #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp"
0017 #include "Acts/Geometry/GeometryContext.hpp"
0018 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0019 #include "Acts/Propagator/ActorList.hpp"
0020 #include "Acts/Propagator/DirectNavigator.hpp"
0021 #include "Acts/Propagator/PropagatorOptions.hpp"
0022 #include "Acts/Propagator/StandardAborters.hpp"
0023 #include "Acts/Propagator/detail/LoopProtection.hpp"
0024 #include "Acts/Propagator/detail/PointwiseMaterialInteraction.hpp"
0025 #include "Acts/TrackFitting/KalmanFitterError.hpp"
0026 #include "Acts/TrackFitting/detail/VoidFitterComponents.hpp"
0027 #include "Acts/Utilities/CalibrationContext.hpp"
0028 #include "Acts/Utilities/Delegate.hpp"
0029 #include "Acts/Utilities/Logger.hpp"
0030 #include "Acts/Utilities/Result.hpp"
0031 #include "Acts/Utilities/TrackHelpers.hpp"
0032
0033 #include <memory>
0034 #include <unordered_map>
0035 #include <vector>
0036
0037 namespace Acts {
0038
0039
0040
0041
0042
0043 template <typename traj_t>
0044 struct KalmanFitterExtensions {
0045
0046 using TrackStateProxy = typename traj_t::TrackStateProxy;
0047
0048 using ConstTrackStateProxy = typename traj_t::ConstTrackStateProxy;
0049
0050 using Parameters = typename TrackStateProxy::Parameters;
0051
0052
0053 using Calibrator =
0054 Delegate<void(const GeometryContext&, const CalibrationContext&,
0055 const SourceLink&, TrackStateProxy)>;
0056
0057
0058 using Updater = Delegate<Result<void>(const GeometryContext&, TrackStateProxy,
0059 const Logger&)>;
0060
0061
0062 using OutlierFinder = Delegate<bool(ConstTrackStateProxy)>;
0063
0064
0065 using ReverseFilteringLogic = Delegate<bool(ConstTrackStateProxy)>;
0066
0067
0068 using Smoother = Delegate<Result<void>(const GeometryContext&, traj_t&,
0069 std::size_t, const Logger&)>;
0070
0071
0072 SourceLinkSurfaceAccessor surfaceAccessor;
0073
0074
0075
0076
0077 Calibrator calibrator;
0078
0079
0080 Updater updater;
0081
0082
0083
0084 OutlierFinder outlierFinder;
0085
0086
0087
0088 ReverseFilteringLogic reverseFilteringLogic;
0089
0090
0091 Smoother smoother;
0092
0093
0094 KalmanFitterExtensions() {
0095 surfaceAccessor.connect<&detail::voidSurfaceAccessor>();
0096 calibrator.template connect<&detail::voidFitterCalibrator<traj_t>>();
0097 updater.template connect<&detail::voidFitterUpdater<traj_t>>();
0098 outlierFinder.template connect<&detail::voidOutlierFinder<traj_t>>();
0099 reverseFilteringLogic
0100 .template connect<&detail::voidReverseFilteringLogic<traj_t>>();
0101 smoother.template connect<&detail::voidFitterSmoother<traj_t>>();
0102 }
0103 };
0104
0105
0106
0107
0108 template <typename traj_t>
0109 struct KalmanFitterOptions {
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123 KalmanFitterOptions(const GeometryContext& gctx,
0124 const MagneticFieldContext& mctx,
0125 std::reference_wrapper<const CalibrationContext> cctx,
0126 KalmanFitterExtensions<traj_t> extensions_,
0127 const PropagatorPlainOptions& pOptions,
0128 const Surface* tSurface = nullptr,
0129 bool mScattering = true, bool eLoss = true,
0130 bool rFiltering = false, double rfScaling = 1.0,
0131 const FreeToBoundCorrection& freeToBoundCorrection_ =
0132 FreeToBoundCorrection(false))
0133 : geoContext(gctx),
0134 magFieldContext(mctx),
0135 calibrationContext(cctx),
0136 extensions(extensions_),
0137 propagatorPlainOptions(pOptions),
0138 referenceSurface(tSurface),
0139 multipleScattering(mScattering),
0140 energyLoss(eLoss),
0141 reverseFiltering(rFiltering),
0142 reverseFilteringCovarianceScaling(rfScaling),
0143 freeToBoundCorrection(freeToBoundCorrection_) {}
0144
0145
0146 std::reference_wrapper<const GeometryContext> geoContext;
0147
0148 std::reference_wrapper<const MagneticFieldContext> magFieldContext;
0149
0150 std::reference_wrapper<const CalibrationContext> calibrationContext;
0151
0152
0153 KalmanFitterExtensions<traj_t> extensions;
0154
0155
0156 PropagatorPlainOptions propagatorPlainOptions;
0157
0158
0159 const Surface* referenceSurface = nullptr;
0160
0161
0162 TrackExtrapolationStrategy referenceSurfaceStrategy =
0163 TrackExtrapolationStrategy::firstOrLast;
0164
0165
0166 bool multipleScattering = true;
0167
0168
0169 bool energyLoss = true;
0170
0171
0172
0173 bool reverseFiltering = false;
0174
0175
0176
0177
0178
0179
0180
0181 double reverseFilteringCovarianceScaling = 100.0;
0182
0183
0184
0185 FreeToBoundCorrection freeToBoundCorrection;
0186 };
0187
0188
0189 template <typename traj_t>
0190 struct KalmanFitterResult {
0191
0192 traj_t* fittedStates{nullptr};
0193
0194
0195
0196
0197
0198 std::size_t lastMeasurementIndex = kTrackIndexInvalid;
0199
0200
0201
0202
0203
0204 std::size_t lastTrackIndex = kTrackIndexInvalid;
0205
0206
0207 std::optional<BoundTrackParameters> fittedParameters;
0208
0209
0210 std::size_t measurementStates = 0;
0211
0212
0213
0214
0215
0216 std::size_t measurementHoles = 0;
0217
0218
0219 std::size_t processedStates = 0;
0220
0221
0222 bool finished = false;
0223
0224
0225 std::vector<const Surface*> missedActiveSurfaces;
0226
0227
0228 PathLimitReached pathLimitReached;
0229 };
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252 template <typename propagator_t, typename traj_t>
0253 class KalmanFitter {
0254
0255 using KalmanNavigator = typename propagator_t::Navigator;
0256
0257
0258 static constexpr bool isDirectNavigator =
0259 std::is_same_v<KalmanNavigator, DirectNavigator>;
0260
0261 public:
0262
0263
0264
0265 explicit KalmanFitter(propagator_t pPropagator,
0266 std::unique_ptr<const Logger> _logger =
0267 getDefaultLogger("KalmanFitter", Logging::INFO))
0268 : m_propagator(std::move(pPropagator)),
0269 m_logger{std::move(_logger)},
0270 m_actorLogger{m_logger->cloneWithSuffix("Actor")} {}
0271
0272 private:
0273
0274 propagator_t m_propagator;
0275
0276
0277 std::unique_ptr<const Logger> m_logger;
0278 std::unique_ptr<const Logger> m_actorLogger;
0279
0280 const Logger& logger() const { return *m_logger; }
0281
0282
0283
0284
0285
0286 class Actor {
0287 public:
0288
0289 using result_type = KalmanFitterResult<traj_t>;
0290
0291
0292 SurfaceReached targetReached{std::numeric_limits<double>::lowest()};
0293
0294
0295 std::unordered_map<const Surface*, SourceLink> inputMeasurements;
0296
0297
0298 bool multipleScattering = true;
0299
0300
0301 bool energyLoss = true;
0302
0303
0304
0305 FreeToBoundCorrection freeToBoundCorrection;
0306
0307
0308 std::shared_ptr<traj_t> outputStates;
0309
0310 KalmanFitterExtensions<traj_t> extensions;
0311
0312
0313 const CalibrationContext* calibrationContext{nullptr};
0314
0315
0316 EndOfWorldReached endOfWorldReached;
0317
0318
0319 VolumeConstraintAborter volumeConstraintAborter;
0320
0321
0322 const Logger* actorLogger{nullptr};
0323
0324
0325 const Logger& logger() const { return *actorLogger; }
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337 template <typename propagator_state_t, typename stepper_t,
0338 typename navigator_t>
0339 Result<void> act(propagator_state_t& state, const stepper_t& stepper,
0340 const navigator_t& navigator, result_type& result,
0341 const Logger& ) const {
0342 assert(result.fittedStates && "No MultiTrajectory set");
0343
0344 if (result.finished) {
0345 return Result<void>::success();
0346 }
0347
0348 ACTS_VERBOSE("KalmanFitter step at pos: "
0349 << stepper.position(state.stepping).transpose()
0350 << " dir: " << stepper.direction(state.stepping).transpose()
0351 << " momentum: "
0352 << stepper.absoluteMomentum(state.stepping));
0353
0354
0355 if (result.pathLimitReached.internalLimit ==
0356 std::numeric_limits<double>::max()) {
0357 detail::setupLoopProtection(state, stepper, result.pathLimitReached,
0358 true, logger());
0359 }
0360
0361
0362
0363 const Surface* surface = navigator.currentSurface(state.navigation);
0364 if (surface != nullptr) {
0365
0366
0367
0368
0369
0370
0371
0372 ACTS_VERBOSE("Perform " << state.options.direction << " filter step");
0373 auto res = filter(*surface, state, stepper, navigator, result);
0374 if (!res.ok()) {
0375 ACTS_DEBUG("Error in " << state.options.direction
0376 << " filter: " << res.error());
0377 return res.error();
0378 }
0379 }
0380
0381
0382
0383 const bool isTrackComplete =
0384 result.measurementStates == inputMeasurements.size();
0385 const bool isEndOfWorldReached =
0386 endOfWorldReached.checkAbort(state, stepper, navigator, logger());
0387 const bool isVolumeConstraintReached = volumeConstraintAborter.checkAbort(
0388 state, stepper, navigator, logger());
0389 const bool isPathLimitReached = result.pathLimitReached.checkAbort(
0390 state, stepper, navigator, logger());
0391 const bool isTargetReached =
0392 targetReached.checkAbort(state, stepper, navigator, logger());
0393 if (isTrackComplete || isEndOfWorldReached || isVolumeConstraintReached ||
0394 isPathLimitReached || isTargetReached) {
0395 ACTS_VERBOSE(
0396 "Finalizing Kalman fit: "
0397 << (isTrackComplete ? "track complete; " : "")
0398 << (isEndOfWorldReached ? "end of world reached; " : "")
0399 << (isVolumeConstraintReached ? "volume constraint reached; " : "")
0400 << (isPathLimitReached ? "path limit reached; " : "")
0401 << (isTargetReached ? "target surface reached; " : ""));
0402
0403 if (isTargetReached) {
0404 ACTS_VERBOSE("Setting fitted parameters at target surface");
0405
0406
0407 auto res = stepper.boundState(state.stepping, *targetReached.surface);
0408 if (!res.ok()) {
0409 ACTS_DEBUG("Error while acquiring bound state for target surface: "
0410 << res.error() << " " << res.error().message());
0411 return res.error();
0412 } else {
0413 const auto& [boundParams, jacobian, pathLength] = *res;
0414 result.fittedParameters = boundParams;
0415 }
0416 }
0417
0418 result.finished = true;
0419 }
0420
0421 return Result<void>::success();
0422 }
0423
0424 template <typename propagator_state_t, typename stepper_t,
0425 typename navigator_t>
0426 bool checkAbort(propagator_state_t& , const stepper_t& ,
0427 const navigator_t& , const result_type& result,
0428 const Logger& ) const {
0429 return result.finished;
0430 }
0431
0432
0433
0434
0435
0436
0437
0438
0439
0440
0441
0442
0443 template <typename propagator_state_t, typename stepper_t,
0444 typename navigator_t>
0445 Result<void> filter(const Surface& surface, propagator_state_t& state,
0446 const stepper_t& stepper, const navigator_t& navigator,
0447 result_type& result) const {
0448 const bool precedingMeasurementExists = result.measurementStates > 0;
0449 const bool surfaceIsSensitive = surface.isSensitive();
0450 const bool surfaceHasMaterial = surface.hasMaterial();
0451
0452
0453 const auto sourceLinkIt = inputMeasurements.find(&surface);
0454 if (sourceLinkIt != inputMeasurements.end()) {
0455
0456 ACTS_VERBOSE("Measurement surface " << surface.geometryId()
0457 << " detected.");
0458
0459 stepper.transportCovarianceToBound(state.stepping, surface,
0460 freeToBoundCorrection);
0461
0462
0463 detail::performMaterialInteraction(
0464 state, stepper, surface,
0465 detail::determineMaterialUpdateMode(state, navigator,
0466 MaterialUpdateMode::PreUpdate),
0467 NoiseUpdateMode::addNoise, multipleScattering, energyLoss,
0468 logger());
0469
0470
0471 TrackStatePropMask mask =
0472 TrackStatePropMask::Predicted | TrackStatePropMask::Filtered |
0473 TrackStatePropMask::Jacobian | TrackStatePropMask::Calibrated;
0474 typename traj_t::TrackStateProxy trackStateProxy =
0475 result.fittedStates->makeTrackState(mask, result.lastTrackIndex);
0476
0477 typename traj_t::ConstTrackStateProxy trackStateProxyConst{
0478 trackStateProxy};
0479
0480
0481
0482 trackStateProxy.setReferenceSurface(surface.getSharedPtr());
0483
0484 auto res = stepper.boundState(state.stepping, surface, false,
0485 freeToBoundCorrection);
0486 if (!res.ok()) {
0487 ACTS_DEBUG("Propagate to surface " << surface.geometryId()
0488 << " failed: " << res.error());
0489 return res.error();
0490 }
0491 const auto& [boundParams, jacobian, pathLength] = *res;
0492
0493
0494 trackStateProxy.predicted() = boundParams.parameters();
0495 trackStateProxy.predictedCovariance() = state.stepping.cov;
0496
0497 trackStateProxy.jacobian() = jacobian;
0498 trackStateProxy.pathLength() = pathLength;
0499
0500
0501
0502 extensions.calibrator(state.geoContext, *calibrationContext,
0503 sourceLinkIt->second, trackStateProxy);
0504
0505
0506 auto typeFlags = trackStateProxy.typeFlags();
0507 typeFlags.setHasParameters();
0508 if (surface.hasMaterial()) {
0509 typeFlags.setHasMaterial();
0510 }
0511
0512
0513
0514
0515
0516
0517
0518 if (!extensions.outlierFinder(trackStateProxyConst)) {
0519
0520 auto updateRes =
0521 extensions.updater(state.geoContext, trackStateProxy, logger());
0522 if (!updateRes.ok()) {
0523 ACTS_DEBUG("Update step failed: " << updateRes.error());
0524 return updateRes.error();
0525 }
0526
0527 typeFlags.setIsMeasurement();
0528 } else {
0529 ACTS_VERBOSE(
0530 "Filtering step successful. But measurement is determined "
0531 "to be an outlier. Stepping state is not updated.");
0532
0533 typeFlags.setIsOutlier();
0534 trackStateProxy.shareFrom(trackStateProxy,
0535 TrackStatePropMask::Predicted,
0536 TrackStatePropMask::Filtered);
0537 }
0538
0539 result.lastTrackIndex = trackStateProxy.index();
0540
0541
0542 if (trackStateProxy.typeFlags().isMeasurement()) {
0543
0544 ACTS_VERBOSE("Filtering step successful, updated parameters are:\n"
0545 << trackStateProxy.filtered().transpose());
0546
0547 stepper.update(state.stepping,
0548 MultiTrajectoryHelpers::freeFiltered(
0549 state.options.geoContext, trackStateProxy),
0550 trackStateProxy.filtered(),
0551 trackStateProxy.filteredCovariance(), surface);
0552
0553 ++result.measurementStates;
0554 }
0555
0556
0557 detail::performMaterialInteraction(
0558 state, stepper, surface,
0559 detail::determineMaterialUpdateMode(state, navigator,
0560 MaterialUpdateMode::PostUpdate),
0561 NoiseUpdateMode::addNoise, multipleScattering, energyLoss,
0562 logger());
0563
0564 ++result.processedStates;
0565
0566
0567 result.measurementHoles = result.missedActiveSurfaces.size();
0568
0569
0570 result.lastMeasurementIndex = result.lastTrackIndex;
0571
0572 } else if ((precedingMeasurementExists && surfaceIsSensitive) ||
0573 surfaceHasMaterial) {
0574
0575
0576
0577
0578
0579 TrackStatePropMask mask =
0580 TrackStatePropMask::Predicted | TrackStatePropMask::Jacobian;
0581 typename traj_t::TrackStateProxy trackStateProxy =
0582 result.fittedStates->makeTrackState(mask, result.lastTrackIndex);
0583
0584
0585
0586 trackStateProxy.setReferenceSurface(surface.getSharedPtr());
0587
0588 auto res = stepper.boundState(state.stepping, surface, true,
0589 freeToBoundCorrection);
0590 if (!res.ok()) {
0591 return res.error();
0592 }
0593 const auto& [boundParams, jacobian, pathLength] = *res;
0594
0595
0596 trackStateProxy.predicted() = boundParams.parameters();
0597 trackStateProxy.predictedCovariance() = state.stepping.cov;
0598
0599 trackStateProxy.jacobian() = jacobian;
0600 trackStateProxy.pathLength() = pathLength;
0601
0602
0603
0604 trackStateProxy.shareFrom(trackStateProxy,
0605 TrackStatePropMask::Predicted,
0606 TrackStatePropMask::Filtered);
0607
0608
0609 auto typeFlags = trackStateProxy.typeFlags();
0610 typeFlags.setHasParameters();
0611
0612 if (surfaceHasMaterial) {
0613 typeFlags.setHasMaterial();
0614 }
0615
0616 if (surfaceIsSensitive && precedingMeasurementExists) {
0617 ACTS_VERBOSE("Detected hole on " << surface.geometryId());
0618
0619 typeFlags.setIsHole();
0620 } else if (surfaceIsSensitive) {
0621 ACTS_VERBOSE("Skip hole (no preceding measurements) on surface "
0622 << surface.geometryId());
0623 } else if (surfaceHasMaterial) {
0624 ACTS_VERBOSE("Detected in-sensitive surface "
0625 << surface.geometryId());
0626 }
0627
0628 result.lastTrackIndex = trackStateProxy.index();
0629
0630 if (trackStateProxy.typeFlags().isHole()) {
0631
0632 result.missedActiveSurfaces.push_back(&surface);
0633 }
0634
0635 ++result.processedStates;
0636
0637
0638 detail::performMaterialInteraction(
0639 state, stepper, surface,
0640 detail::determineMaterialUpdateMode(state, navigator,
0641 MaterialUpdateMode::FullUpdate),
0642 NoiseUpdateMode::addNoise, multipleScattering, energyLoss,
0643 logger());
0644 }
0645
0646 return Result<void>::success();
0647 }
0648 };
0649
0650 public:
0651
0652
0653
0654
0655
0656
0657
0658
0659
0660
0661
0662
0663
0664
0665
0666
0667 template <typename source_link_iterator_t,
0668 TrackContainerFrontend track_container_t>
0669 Result<typename track_container_t::TrackProxy> fit(
0670 source_link_iterator_t it, source_link_iterator_t end,
0671 const BoundTrackParameters& sParameters,
0672 const KalmanFitterOptions<traj_t>& kfOptions,
0673 track_container_t& trackContainer) const {
0674 return fit_impl(it, end, sParameters, kfOptions, nullptr, trackContainer);
0675 }
0676
0677
0678
0679
0680
0681
0682
0683
0684
0685
0686
0687
0688
0689
0690
0691
0692
0693
0694
0695 template <typename source_link_iterator_t,
0696 TrackContainerFrontend track_container_t>
0697 Result<typename track_container_t::TrackProxy> fit(
0698 source_link_iterator_t it, source_link_iterator_t end,
0699 const BoundTrackParameters& sParameters,
0700 const KalmanFitterOptions<traj_t>& kfOptions,
0701 const std::vector<const Surface*>& sSequence,
0702 track_container_t& trackContainer) const
0703 requires(isDirectNavigator)
0704 {
0705 return fit_impl(it, end, sParameters, kfOptions, &sSequence,
0706 trackContainer);
0707 }
0708
0709 private:
0710 template <typename source_link_iterator_t>
0711 auto make_propagator_options(source_link_iterator_t it,
0712 source_link_iterator_t end,
0713 const KalmanFitterOptions<traj_t>& kfOptions,
0714 const std::vector<const Surface*>* sSequence,
0715 const Surface* targetSurface,
0716 bool reverseDirection) const {
0717 using KalmanActor = Actor;
0718 using Actors = ActorList<KalmanActor>;
0719 using PropagatorOptions = typename propagator_t::template Options<Actors>;
0720
0721 const std::size_t nMeasurements = std::distance(it, end);
0722
0723
0724
0725 ACTS_VERBOSE("Preparing " << nMeasurements << " input measurements");
0726 std::unordered_map<const Surface*, SourceLink> inputMeasurements;
0727 for (; it != end; ++it) {
0728 SourceLink sl = *it;
0729 const Surface* surface = kfOptions.extensions.surfaceAccessor(sl);
0730 inputMeasurements.try_emplace(surface, std::move(sl));
0731 }
0732
0733
0734 PropagatorOptions propagatorOptions(kfOptions.geoContext,
0735 kfOptions.magFieldContext);
0736
0737
0738 propagatorOptions.setPlainOptions(kfOptions.propagatorPlainOptions);
0739
0740 if (reverseDirection) {
0741 propagatorOptions.direction = propagatorOptions.direction.invert();
0742 }
0743
0744 if constexpr (!isDirectNavigator) {
0745
0746
0747 for (const auto& [surface, _] : inputMeasurements) {
0748 propagatorOptions.navigation.appendExternalSurface(*surface);
0749 }
0750 } else {
0751 assert(sSequence != nullptr &&
0752 "DirectNavigator requires a surface sequence for KalmanFitter");
0753
0754 propagatorOptions.navigation.externalSurfaces = *sSequence;
0755 }
0756
0757
0758 auto& kalmanActor = propagatorOptions.actorList.template get<KalmanActor>();
0759 kalmanActor.inputMeasurements = std::move(inputMeasurements);
0760 kalmanActor.targetReached.surface = targetSurface;
0761 kalmanActor.multipleScattering = kfOptions.multipleScattering;
0762 kalmanActor.energyLoss = kfOptions.energyLoss;
0763 kalmanActor.freeToBoundCorrection = kfOptions.freeToBoundCorrection;
0764 kalmanActor.calibrationContext = &kfOptions.calibrationContext.get();
0765 kalmanActor.extensions = kfOptions.extensions;
0766 kalmanActor.actorLogger = m_actorLogger.get();
0767
0768 return propagatorOptions;
0769 }
0770
0771 template <typename propagator_options_t,
0772 TrackContainerFrontend track_container_t>
0773 auto filter_impl(const BoundTrackParameters& sParameters,
0774 const propagator_options_t& propagatorOptions,
0775 track_container_t& trackContainer) const
0776 -> Result<typename track_container_t::TrackProxy> {
0777 auto propagatorState = m_propagator.makeState(propagatorOptions);
0778
0779 auto propagatorInitResult =
0780 m_propagator.initialize(propagatorState, sParameters);
0781 if (!propagatorInitResult.ok()) {
0782 ACTS_DEBUG("Propagation initialization failed: "
0783 << propagatorInitResult.error());
0784 return propagatorInitResult.error();
0785 }
0786
0787 auto& kalmanResult =
0788 propagatorState.template get<KalmanFitterResult<traj_t>>();
0789 kalmanResult.fittedStates = &trackContainer.trackStateContainer();
0790
0791
0792 auto result = m_propagator.propagate(propagatorState);
0793
0794 if (!result.ok()) {
0795 ACTS_DEBUG("Propagation failed: " << result.error());
0796 return result.error();
0797 }
0798
0799
0800
0801 if (!kalmanResult.measurementStates) {
0802 ACTS_DEBUG("KalmanFilter failed: No measurement states found");
0803 return KalmanFitterError::NoMeasurementFound;
0804 }
0805
0806 auto track = trackContainer.makeTrack();
0807 track.tipIndex() = kalmanResult.lastMeasurementIndex;
0808 if (kalmanResult.fittedParameters) {
0809 const auto& params = kalmanResult.fittedParameters.value();
0810 track.parameters() = params.parameters();
0811 track.covariance() = params.covariance().value();
0812 track.setReferenceSurface(params.referenceSurface().getSharedPtr());
0813 }
0814
0815 calculateTrackQuantities(track);
0816
0817 return track;
0818 }
0819
0820
0821
0822
0823
0824
0825
0826
0827
0828
0829
0830
0831
0832
0833 template <typename source_link_iterator_t,
0834 TrackContainerFrontend track_container_t>
0835 auto fit_impl(source_link_iterator_t it, source_link_iterator_t end,
0836 const BoundTrackParameters& sParameters,
0837 const KalmanFitterOptions<traj_t>& kfOptions,
0838 const std::vector<const Surface*>* sSequence,
0839 track_container_t& trackContainer) const
0840 -> Result<typename track_container_t::TrackProxy> {
0841 using TrackProxy = typename track_container_t::TrackProxy;
0842 using TrackStateProxy = typename track_container_t::TrackStateProxy;
0843
0844 auto forwardPropagatorOptions =
0845 make_propagator_options(it, end, kfOptions, sSequence, nullptr, false);
0846
0847 auto forwardFilterResult =
0848 filter_impl(sParameters, forwardPropagatorOptions, trackContainer);
0849
0850 if (!forwardFilterResult.ok()) {
0851 ACTS_DEBUG("KalmanFilter failed: "
0852 << forwardFilterResult.error() << ", "
0853 << forwardFilterResult.error().message());
0854 return forwardFilterResult.error();
0855 }
0856
0857 TrackProxy forwardTrack = forwardFilterResult.value();
0858
0859 TrackStateProxy firstMeasurementState =
0860 trackContainer.trackStateContainer().getTrackState(
0861 findFirstMeasurementState(forwardTrack).value().index());
0862 TrackStateProxy lastMeasurementState =
0863 trackContainer.trackStateContainer().getTrackState(
0864 findLastMeasurementState(forwardTrack).value().index());
0865 lastMeasurementState.shareFrom(lastMeasurementState,
0866 TrackStatePropMask::Filtered,
0867 TrackStatePropMask::Smoothed);
0868
0869 TrackProxy track = forwardTrack;
0870
0871 const bool doReverseFilter =
0872 kfOptions.reverseFiltering ||
0873 kfOptions.extensions.reverseFilteringLogic(
0874 typename traj_t::ConstTrackStateProxy(lastMeasurementState));
0875 if (doReverseFilter) {
0876 ACTS_VERBOSE("Smooth track by reversed filtering");
0877
0878 auto reverseStartParameters = forwardTrack.createParametersFromState(
0879 typename traj_t::ConstTrackStateProxy(lastMeasurementState));
0880 reverseStartParameters.covariance().value() *=
0881 kfOptions.reverseFilteringCovarianceScaling;
0882 auto reversePropagatorOptions = make_propagator_options(
0883 it, end, kfOptions, sSequence, kfOptions.referenceSurface, true);
0884 auto reverseFilterResult = filter_impl(
0885 reverseStartParameters, reversePropagatorOptions, trackContainer);
0886
0887 if (!reverseFilterResult.ok()) {
0888 ACTS_DEBUG("Reversed KalmanFilter failed: "
0889 << reverseFilterResult.error() << ", "
0890 << reverseFilterResult.error().message());
0891 return reverseFilterResult.error();
0892 }
0893
0894 TrackProxy reverseTrack = reverseFilterResult.value();
0895
0896 TrackStateProxy reverseLastMeasurementState =
0897 trackContainer.trackStateContainer().getTrackState(
0898 findLastMeasurementState(reverseTrack).value().index());
0899
0900 if (&firstMeasurementState.referenceSurface() !=
0901 &reverseLastMeasurementState.referenceSurface()) {
0902 ACTS_DEBUG(
0903 "Inconsistent reference surfaces between forward and "
0904 "reversed filtered tracks");
0905 return Result<TrackProxy>::failure(
0906 KalmanFitterError::InconsistentTrackStates);
0907 }
0908 firstMeasurementState.shareFrom(reverseLastMeasurementState,
0909 TrackStatePropMask::Filtered,
0910 TrackStatePropMask::Smoothed);
0911
0912 if (reverseTrack.hasReferenceSurface()) {
0913 track.parameters() = reverseTrack.parameters();
0914 track.covariance() = reverseTrack.covariance();
0915 track.setReferenceSurface(
0916 reverseTrack.referenceSurface().getSharedPtr());
0917 }
0918
0919 trackContainer.removeTrack(reverseTrack.index());
0920 } else {
0921 ACTS_VERBOSE("Smooth track directly without reversed filtering");
0922
0923 auto smoothRes = kfOptions.extensions.smoother(
0924 kfOptions.geoContext, trackContainer.trackStateContainer(),
0925 forwardTrack.tipIndex(), logger());
0926 if (!smoothRes.ok()) {
0927 ACTS_DEBUG("Smoothing step failed: " << smoothRes.error() << ", "
0928 << smoothRes.error().message());
0929 return smoothRes.error();
0930 }
0931 }
0932
0933 if (!track.hasReferenceSurface() && kfOptions.referenceSurface != nullptr) {
0934 typename propagator_t::template Options<> extrapolationOptions(
0935 kfOptions.geoContext, kfOptions.magFieldContext);
0936 auto extrapolationResult = extrapolateTrackToReferenceSurface(
0937 track, *kfOptions.referenceSurface, m_propagator,
0938 extrapolationOptions, kfOptions.referenceSurfaceStrategy, logger());
0939
0940 if (!extrapolationResult.ok()) {
0941 ACTS_DEBUG("Extrapolation to reference surface failed: "
0942 << extrapolationResult.error() << ", "
0943 << extrapolationResult.error().message());
0944 return extrapolationResult.error();
0945 }
0946 }
0947
0948 if (trackContainer.hasColumn(hashString("smoothed"))) {
0949 track.template component<bool, hashString("smoothed")>() =
0950 !doReverseFilter;
0951 }
0952 if (trackContainer.hasColumn(hashString("reversed"))) {
0953 track.template component<bool, hashString("reversed")>() =
0954 doReverseFilter;
0955 }
0956
0957 return track;
0958 }
0959 };
0960
0961
0962
0963 }