File indexing completed on 2025-07-13 08:05:02
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "Acts/Utilities/detail/ReferenceWrapperAnyCompat.hpp"
0013
0014 #include "Acts/Definitions/Algebra.hpp"
0015 #include "Acts/Definitions/Common.hpp"
0016 #include "Acts/EventData/MeasurementHelpers.hpp"
0017 #include "Acts/EventData/MultiTrajectory.hpp"
0018 #include "Acts/EventData/MultiTrajectoryHelpers.hpp"
0019 #include "Acts/EventData/ProxyAccessor.hpp"
0020 #include "Acts/EventData/TrackContainer.hpp"
0021 #include "Acts/EventData/TrackParameters.hpp"
0022 #include "Acts/EventData/TrackStatePropMask.hpp"
0023 #include "Acts/EventData/Types.hpp"
0024 #include "Acts/Geometry/GeometryContext.hpp"
0025 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0026 #include "Acts/Material/MaterialSlab.hpp"
0027 #include "Acts/Propagator/AbortList.hpp"
0028 #include "Acts/Propagator/ActionList.hpp"
0029 #include "Acts/Propagator/ConstrainedStep.hpp"
0030 #include "Acts/Propagator/Propagator.hpp"
0031 #include "Acts/Propagator/StandardAborters.hpp"
0032 #include "Acts/Propagator/detail/LoopProtection.hpp"
0033 #include "Acts/Propagator/detail/PointwiseMaterialInteraction.hpp"
0034 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0035 #include "Acts/TrackFinding/CombinatorialKalmanFilterError.hpp"
0036 #include "Acts/TrackFitting/KalmanFitter.hpp"
0037 #include "Acts/TrackFitting/detail/VoidFitterComponents.hpp"
0038 #include "Acts/Utilities/CalibrationContext.hpp"
0039 #include "Acts/Utilities/HashedString.hpp"
0040 #include "Acts/Utilities/Logger.hpp"
0041 #include "Acts/Utilities/Result.hpp"
0042 #include "Acts/Utilities/TrackHelpers.hpp"
0043 #include "Acts/Utilities/Zip.hpp"
0044
0045 #include <functional>
0046 #include <limits>
0047 #include <memory>
0048 #include <string_view>
0049 #include <type_traits>
0050 #include <unordered_map>
0051
0052 namespace Acts {
0053
0054
0055
0056 enum class CombinatorialKalmanFilterBranchStopperResult {
0057 Continue,
0058 StopAndDrop,
0059 StopAndKeep,
0060 };
0061
0062
0063 template <typename track_container_t>
0064 struct CombinatorialKalmanFilterExtensions {
0065 using traj_t = typename track_container_t::TrackStateContainerBackend;
0066 using candidate_container_t =
0067 typename std::vector<typename track_container_t::TrackStateProxy>;
0068 using TrackProxy = typename track_container_t::TrackProxy;
0069 using TrackStateProxy = typename track_container_t::TrackStateProxy;
0070
0071 using BranchStopperResult = CombinatorialKalmanFilterBranchStopperResult;
0072
0073 using Calibrator = typename KalmanFitterExtensions<traj_t>::Calibrator;
0074 using Updater = typename KalmanFitterExtensions<traj_t>::Updater;
0075 using MeasurementSelector =
0076 Delegate<Result<std::pair<typename candidate_container_t::iterator,
0077 typename candidate_container_t::iterator>>(
0078 candidate_container_t& trackStates, bool&, const Logger&)>;
0079 using BranchStopper =
0080 Delegate<BranchStopperResult(const TrackProxy&, const TrackStateProxy&)>;
0081
0082
0083
0084
0085 Calibrator calibrator{
0086 DelegateFuncTag<detail::voidFitterCalibrator<traj_t>>{}};
0087
0088
0089 Updater updater{DelegateFuncTag<detail::voidFitterUpdater<traj_t>>{}};
0090
0091
0092 MeasurementSelector measurementSelector{
0093 DelegateFuncTag<voidMeasurementSelector>{}};
0094
0095
0096 BranchStopper branchStopper{DelegateFuncTag<voidBranchStopper>{}};
0097
0098 private:
0099
0100
0101 static Result<std::pair<typename std::vector<TrackStateProxy>::iterator,
0102 typename std::vector<TrackStateProxy>::iterator>>
0103 voidMeasurementSelector(typename std::vector<TrackStateProxy>& candidates,
0104 bool& , const Logger& ) {
0105 return std::pair{candidates.begin(), candidates.end()};
0106 };
0107
0108
0109
0110 static BranchStopperResult voidBranchStopper(
0111 const TrackProxy& , const TrackStateProxy& ) {
0112 return BranchStopperResult::Continue;
0113 }
0114 };
0115
0116
0117
0118 template <typename source_link_iterator_t>
0119 using SourceLinkAccessorDelegate =
0120 Delegate<std::pair<source_link_iterator_t, source_link_iterator_t>(
0121 const Surface&)>;
0122
0123
0124
0125
0126
0127 static constexpr std::size_t s_maxBranchesPerSurface = 10;
0128
0129 namespace CkfTypes {
0130
0131 template <typename T>
0132 using BranchVector = boost::container::small_vector<T, s_maxBranchesPerSurface>;
0133
0134 }
0135
0136
0137
0138
0139
0140 template <typename source_link_iterator_t, typename track_container_t>
0141 struct CombinatorialKalmanFilterOptions {
0142 using SourceLinkIterator = source_link_iterator_t;
0143 using SourceLinkAccessor = SourceLinkAccessorDelegate<source_link_iterator_t>;
0144
0145 using TrackStateContainerBackend =
0146 typename track_container_t::TrackStateContainerBackend;
0147 using TrackStateProxy = typename track_container_t::TrackStateProxy;
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159 CombinatorialKalmanFilterOptions(
0160 const GeometryContext& gctx, const MagneticFieldContext& mctx,
0161 std::reference_wrapper<const CalibrationContext> cctx,
0162 SourceLinkAccessor accessor_,
0163 CombinatorialKalmanFilterExtensions<track_container_t> extensions_,
0164 const PropagatorPlainOptions& pOptions, bool mScattering = true,
0165 bool eLoss = true)
0166 : geoContext(gctx),
0167 magFieldContext(mctx),
0168 calibrationContext(cctx),
0169 sourcelinkAccessor(std::move(accessor_)),
0170 extensions(extensions_),
0171 propagatorPlainOptions(pOptions),
0172 multipleScattering(mScattering),
0173 energyLoss(eLoss) {}
0174
0175
0176 CombinatorialKalmanFilterOptions() = delete;
0177
0178
0179 std::reference_wrapper<const GeometryContext> geoContext;
0180
0181 std::reference_wrapper<const MagneticFieldContext> magFieldContext;
0182
0183 std::reference_wrapper<const CalibrationContext> calibrationContext;
0184
0185
0186 SourceLinkAccessor sourcelinkAccessor;
0187
0188
0189 CombinatorialKalmanFilterExtensions<track_container_t> extensions;
0190
0191
0192 PropagatorPlainOptions propagatorPlainOptions;
0193
0194
0195
0196
0197 const Surface* targetSurface = nullptr;
0198
0199 using BoundState = std::tuple<BoundTrackParameters, BoundMatrix, double>;
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219 using TrackStateCandidateCreator =
0220 Delegate<Result<CkfTypes::BranchVector<TrackIndexType>>(
0221 const GeometryContext& geoContext,
0222 const CalibrationContext& calibrationContext, const Surface& surface,
0223 const BoundState& boundState, source_link_iterator_t slBegin,
0224 source_link_iterator_t slEnd, TrackIndexType prevTip,
0225 TrackStateContainerBackend& bufferTrajectory,
0226 std::vector<TrackStateProxy>& trackStateCandidates,
0227 TrackStateContainerBackend& trajectory, const Logger& logger)>;
0228
0229
0230 TrackStateCandidateCreator trackStateCandidateCreator;
0231
0232
0233 bool multipleScattering = true;
0234
0235
0236 bool energyLoss = true;
0237
0238
0239
0240 bool skipPrePropagationUpdate = false;
0241 };
0242
0243 template <typename track_container_t>
0244 struct CombinatorialKalmanFilterResult {
0245 using TrackStateContainerBackend =
0246 typename track_container_t::TrackStateContainerBackend;
0247 using TrackProxy = typename track_container_t::TrackProxy;
0248 using TrackStateProxy = typename track_container_t::TrackStateProxy;
0249
0250
0251 track_container_t* tracks{nullptr};
0252
0253
0254 TrackStateContainerBackend* trackStates{nullptr};
0255
0256
0257 std::vector<TrackProxy> activeBranches;
0258
0259
0260 std::vector<TrackProxy> collectedTracks;
0261
0262
0263 std::vector<TrackStateProxy> trackStateCandidates;
0264
0265
0266 bool finished = false;
0267
0268
0269 Result<void> lastError{Result<void>::success()};
0270
0271
0272 PathLimitReached pathLimitReached;
0273 };
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294 template <typename propagator_t, typename track_container_t>
0295 class CombinatorialKalmanFilter {
0296 public:
0297
0298 CombinatorialKalmanFilter() = delete;
0299
0300 CombinatorialKalmanFilter(propagator_t pPropagator,
0301 std::unique_ptr<const Logger> _logger =
0302 getDefaultLogger("CKF", Logging::INFO))
0303 : m_propagator(std::move(pPropagator)),
0304 m_logger(std::move(_logger)),
0305 m_actorLogger{m_logger->cloneWithSuffix("Actor")},
0306 m_updaterLogger{m_logger->cloneWithSuffix("Updater")} {}
0307
0308 private:
0309 using BoundState = std::tuple<BoundTrackParameters, BoundMatrix, double>;
0310 using TrackStateContainerBackend =
0311 typename track_container_t::TrackStateContainerBackend;
0312 using TrackProxy = typename track_container_t::TrackProxy;
0313 using TrackStateProxy = typename track_container_t::TrackStateProxy;
0314
0315
0316 propagator_t m_propagator;
0317
0318 std::unique_ptr<const Logger> m_logger;
0319 std::shared_ptr<const Logger> m_actorLogger;
0320 std::shared_ptr<const Logger> m_updaterLogger;
0321
0322 const Logger& logger() const { return *m_logger; }
0323
0324 struct DefaultTrackStateCreator {
0325 typename CombinatorialKalmanFilterExtensions<track_container_t>::Calibrator
0326 calibrator;
0327 typename CombinatorialKalmanFilterExtensions<
0328 track_container_t>::MeasurementSelector measurementSelector;
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343 template <typename source_link_iterator_t>
0344 Result<CkfTypes::BranchVector<TrackIndexType>> createSourceLinkTrackStates(
0345 const GeometryContext& gctx,
0346 const CalibrationContext& calibrationContext,
0347 [[maybe_unused]] const Surface& surface, const BoundState& boundState,
0348 source_link_iterator_t slBegin, source_link_iterator_t slEnd,
0349 TrackIndexType prevTip, TrackStateContainerBackend& bufferTrajectory,
0350 std::vector<TrackStateProxy>& trackStateCandidates,
0351 TrackStateContainerBackend& trajectory, const Logger& logger) const {
0352 using PM = TrackStatePropMask;
0353
0354 using ResultTrackStateList =
0355 Acts::Result<CkfTypes::BranchVector<TrackIndexType>>;
0356 ResultTrackStateList resultTrackStateList{
0357 CkfTypes::BranchVector<TrackIndexType>()};
0358 const auto& [boundParams, jacobian, pathLength] = boundState;
0359
0360 trackStateCandidates.clear();
0361 if constexpr (std::is_same_v<
0362 typename std::iterator_traits<
0363 source_link_iterator_t>::iterator_category,
0364 std::random_access_iterator_tag>) {
0365 trackStateCandidates.reserve(std::distance(slBegin, slEnd));
0366 }
0367
0368
0369
0370 for (auto it = slBegin; it != slEnd; ++it) {
0371
0372 const auto sourceLink = *it;
0373
0374
0375 PM mask = PM::Predicted | PM::Jacobian | PM::Calibrated;
0376 if (it != slBegin) {
0377
0378 mask = PM::Calibrated;
0379 }
0380
0381 ACTS_VERBOSE("Create temp track state with mask: " << mask);
0382
0383
0384
0385 auto ts = bufferTrajectory.makeTrackState(mask, prevTip);
0386
0387 if (it == slBegin) {
0388
0389 ts.predicted() = boundParams.parameters();
0390 if (boundParams.covariance()) {
0391 ts.predictedCovariance() = *boundParams.covariance();
0392 }
0393 ts.jacobian() = jacobian;
0394 } else {
0395
0396 auto& first = trackStateCandidates.front();
0397 ts.shareFrom(first, PM::Predicted);
0398 ts.shareFrom(first, PM::Jacobian);
0399 }
0400
0401 ts.pathLength() = pathLength;
0402 ts.setReferenceSurface(boundParams.referenceSurface().getSharedPtr());
0403
0404
0405 calibrator(gctx, calibrationContext, sourceLink, ts);
0406
0407 trackStateCandidates.push_back(ts);
0408 }
0409
0410 bool isOutlier = false;
0411 Result<std::pair<typename std::vector<TrackStateProxy>::iterator,
0412 typename std::vector<TrackStateProxy>::iterator>>
0413 selectorResult =
0414 measurementSelector(trackStateCandidates, isOutlier, logger);
0415 if (!selectorResult.ok()) {
0416 ACTS_ERROR("Selection of calibrated measurements failed: "
0417 << selectorResult.error());
0418 resultTrackStateList =
0419 ResultTrackStateList::failure(selectorResult.error());
0420 } else {
0421 auto selectedTrackStateRange = *selectorResult;
0422 resultTrackStateList = processSelectedTrackStates(
0423 selectedTrackStateRange.first, selectedTrackStateRange.second,
0424 trajectory, isOutlier, logger);
0425 }
0426
0427 return resultTrackStateList;
0428 }
0429
0430
0431
0432
0433
0434
0435
0436
0437 Result<CkfTypes::BranchVector<TrackIndexType>> processSelectedTrackStates(
0438 typename std::vector<TrackStateProxy>::const_iterator begin,
0439 typename std::vector<TrackStateProxy>::const_iterator end,
0440 TrackStateContainerBackend& trackStates, bool isOutlier,
0441 const Logger& logger) const {
0442 using PM = TrackStatePropMask;
0443
0444 using ResultTrackStateList =
0445 Acts::Result<CkfTypes::BranchVector<TrackIndexType>>;
0446 ResultTrackStateList resultTrackStateList{
0447 CkfTypes::BranchVector<TrackIndexType>()};
0448 CkfTypes::BranchVector<TrackIndexType>& trackStateList =
0449 *resultTrackStateList;
0450 trackStateList.reserve(end - begin);
0451
0452 std::optional<TrackStateProxy> firstTrackState{std::nullopt};
0453 for (auto it = begin; it != end; ++it) {
0454 auto& candidateTrackState = *it;
0455
0456 PM mask = PM::Predicted | PM::Filtered | PM::Jacobian | PM::Calibrated;
0457 if (it != begin) {
0458
0459
0460 mask &= ~PM::Predicted & ~PM::Jacobian;
0461 }
0462 if (isOutlier) {
0463
0464 mask &= ~PM::Filtered;
0465 }
0466
0467
0468 auto trackState =
0469 trackStates.makeTrackState(mask, candidateTrackState.previous());
0470 ACTS_VERBOSE("Create SourceLink output track state #"
0471 << trackState.index() << " with mask: " << mask);
0472
0473 if (it != begin) {
0474
0475 trackState.shareFrom(*firstTrackState, PM::Predicted);
0476 trackState.shareFrom(*firstTrackState, PM::Jacobian);
0477 } else {
0478 firstTrackState = trackState;
0479 }
0480
0481
0482 trackState.allocateCalibrated(candidateTrackState.calibratedSize());
0483 trackState.copyFrom(candidateTrackState, mask, false);
0484
0485 auto typeFlags = trackState.typeFlags();
0486 typeFlags.set(TrackStateFlag::ParameterFlag);
0487 typeFlags.set(TrackStateFlag::MeasurementFlag);
0488 if (trackState.referenceSurface().surfaceMaterial() != nullptr) {
0489 typeFlags.set(TrackStateFlag::MaterialFlag);
0490 }
0491 if (isOutlier) {
0492
0493 ACTS_VERBOSE(
0494 "Creating outlier track state with tip = " << trackState.index());
0495 typeFlags.set(TrackStateFlag::OutlierFlag);
0496 }
0497
0498 trackStateList.push_back(trackState.index());
0499 }
0500 return resultTrackStateList;
0501 }
0502 };
0503
0504
0505
0506
0507
0508
0509
0510
0511 template <typename source_link_accessor_t, typename parameters_t>
0512 class Actor {
0513 public:
0514 using BoundState = std::tuple<parameters_t, BoundMatrix, double>;
0515 using CurvilinearState =
0516 std::tuple<CurvilinearTrackParameters, BoundMatrix, double>;
0517
0518 using result_type = CombinatorialKalmanFilterResult<track_container_t>;
0519
0520 using BranchStopperResult = CombinatorialKalmanFilterBranchStopperResult;
0521
0522
0523 SurfaceReached targetReached{std::numeric_limits<double>::lowest()};
0524
0525
0526 bool multipleScattering = true;
0527
0528
0529 bool energyLoss = true;
0530
0531
0532 bool skipPrePropagationUpdate = false;
0533
0534
0535 const CalibrationContext* calibrationContextPtr{nullptr};
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546 template <typename propagator_state_t, typename stepper_t,
0547 typename navigator_t>
0548 void operator()(propagator_state_t& state, const stepper_t& stepper,
0549 const navigator_t& navigator, result_type& result,
0550 const Logger& ) const {
0551 assert(result.trackStates && "No MultiTrajectory set");
0552
0553 if (result.finished) {
0554 return;
0555 }
0556
0557 if (state.stage == PropagatorStage::prePropagation &&
0558 skipPrePropagationUpdate) {
0559 ACTS_VERBOSE("Skip pre-propagation update (first surface)");
0560 return;
0561 }
0562
0563 ACTS_VERBOSE("CombinatorialKalmanFilter step");
0564
0565 assert(!result.activeBranches.empty() && "No active branches");
0566
0567
0568 if (result.pathLimitReached.internalLimit ==
0569 std::numeric_limits<double>::max()) {
0570 detail::setupLoopProtection(state, stepper, result.pathLimitReached,
0571 true, logger());
0572 }
0573
0574
0575
0576 if (auto surface = navigator.currentSurface(state.navigation);
0577 surface != nullptr) {
0578
0579
0580
0581
0582
0583
0584
0585
0586
0587
0588
0589
0590
0591
0592 ACTS_VERBOSE("Perform filter step");
0593 auto res = filter(surface, state, stepper, navigator, result);
0594 if (!res.ok()) {
0595 ACTS_ERROR("Error in filter: " << res.error());
0596 result.lastError = res.error();
0597 }
0598 }
0599
0600 const bool isEndOfWorldReached =
0601 endOfWorldReached(state, stepper, navigator, logger());
0602 const bool isPathLimitReached =
0603 result.pathLimitReached(state, stepper, navigator, logger());
0604 const bool isTargetReached =
0605 targetReached(state, stepper, navigator, logger());
0606 const bool allBranchesStopped = result.activeBranches.empty();
0607 if (isEndOfWorldReached || isPathLimitReached || isTargetReached ||
0608 allBranchesStopped) {
0609 if (isEndOfWorldReached) {
0610 ACTS_VERBOSE("End of world reached");
0611 } else if (isPathLimitReached) {
0612 ACTS_VERBOSE("Path limit reached");
0613 } else if (isTargetReached) {
0614 ACTS_VERBOSE("Target surface reached");
0615
0616
0617 auto res = stepper.boundState(state.stepping, *targetReached.surface);
0618 if (!res.ok()) {
0619 ACTS_ERROR("Error while acquiring bound state for target surface: "
0620 << res.error() << " " << res.error().message());
0621 result.lastError = res.error();
0622 } else {
0623 const auto& [boundParams, jacobian, pathLength] = *res;
0624 auto currentBranch = result.activeBranches.back();
0625
0626 currentBranch.parameters() = boundParams.parameters();
0627 currentBranch.covariance() = *boundParams.covariance();
0628 currentBranch.setReferenceSurface(
0629 boundParams.referenceSurface().getSharedPtr());
0630 }
0631
0632 stepper.releaseStepSize(state.stepping, ConstrainedStep::actor);
0633 }
0634
0635 if (!allBranchesStopped) {
0636
0637 storeLastActiveBranch(result);
0638 result.activeBranches.pop_back();
0639 } else {
0640
0641 ACTS_VERBOSE("All branches stopped");
0642 }
0643
0644
0645
0646 if (!result.activeBranches.empty()) {
0647 ACTS_VERBOSE("Propagation jumps to branch with tip = "
0648 << result.activeBranches.back().tipIndex());
0649 reset(state, stepper, navigator, result);
0650 } else {
0651 ACTS_VERBOSE("Stop Kalman filtering with "
0652 << result.collectedTracks.size() << " found tracks");
0653 result.finished = true;
0654 }
0655 }
0656 }
0657
0658
0659
0660
0661
0662
0663
0664
0665
0666
0667
0668 template <typename propagator_state_t, typename stepper_t,
0669 typename navigator_t>
0670 void reset(propagator_state_t& state, const stepper_t& stepper,
0671 const navigator_t& navigator, result_type& result) const {
0672 auto currentBranch = result.activeBranches.back();
0673 auto currentState = currentBranch.outermostTrackState();
0674
0675
0676 stepper.resetState(state.stepping, currentState.filtered(),
0677 currentState.filteredCovariance(),
0678 currentState.referenceSurface(),
0679 state.options.stepping.maxStepSize);
0680
0681
0682
0683 auto navigationOptions = state.navigation.options;
0684 navigationOptions.startSurface = ¤tState.referenceSurface();
0685 navigationOptions.targetSurface = nullptr;
0686 state.navigation = navigator.makeState(navigationOptions);
0687 navigator.initialize(state, stepper);
0688
0689
0690
0691 materialInteractor(navigator.currentSurface(state.navigation), state,
0692 stepper, navigator, MaterialUpdateStage::PostUpdate);
0693
0694 detail::setupLoopProtection(state, stepper, result.pathLimitReached, true,
0695 logger());
0696 }
0697
0698
0699
0700
0701
0702
0703
0704
0705
0706
0707
0708
0709
0710
0711
0712 template <typename propagator_state_t, typename stepper_t,
0713 typename navigator_t>
0714 Result<void> filter(const Surface* surface, propagator_state_t& state,
0715 const stepper_t& stepper, const navigator_t& navigator,
0716 result_type& result) const {
0717 using PM = TrackStatePropMask;
0718
0719 std::size_t nBranchesOnSurface = 0;
0720
0721 if (auto [slBegin, slEnd] = m_sourcelinkAccessor(*surface);
0722 slBegin != slEnd) {
0723
0724 ACTS_VERBOSE("Measurement surface " << surface->geometryId()
0725 << " detected.");
0726
0727
0728 stepper.transportCovarianceToBound(state.stepping, *surface);
0729
0730
0731 materialInteractor(surface, state, stepper, navigator,
0732 MaterialUpdateStage::PreUpdate);
0733
0734
0735 auto boundStateRes =
0736 stepper.boundState(state.stepping, *surface, false);
0737 if (!boundStateRes.ok()) {
0738 return boundStateRes.error();
0739 }
0740 auto& boundState = *boundStateRes;
0741 auto& [boundParams, jacobian, pathLength] = boundState;
0742 boundParams.covariance() = state.stepping.cov;
0743
0744 auto currentBranch = result.activeBranches.back();
0745 TrackIndexType prevTip = currentBranch.tipIndex();
0746
0747
0748 using TrackStatesResult =
0749 Acts::Result<CkfTypes::BranchVector<TrackIndexType>>;
0750 TrackStatesResult tsRes = trackStateCandidateCreator(
0751 state.geoContext, *calibrationContextPtr, *surface, boundState,
0752 slBegin, slEnd, prevTip, *result.trackStates,
0753 result.trackStateCandidates, *result.trackStates, logger());
0754 if (!tsRes.ok()) {
0755 ACTS_ERROR(
0756 "Processing of selected track states failed: " << tsRes.error());
0757 return tsRes.error();
0758 }
0759 const CkfTypes::BranchVector<TrackIndexType>& newTrackStateList =
0760 *tsRes;
0761
0762 if (newTrackStateList.empty()) {
0763 ACTS_VERBOSE("Detected hole after measurement selection on surface "
0764 << surface->geometryId());
0765
0766
0767
0768 nBranchesOnSurface = 1;
0769
0770 auto stateMask = PM::Predicted | PM::Jacobian;
0771
0772
0773 TrackIndexType currentTip = addNonSourcelinkState(
0774 stateMask, boundState, result, true, prevTip);
0775 auto nonSourcelinkState =
0776 result.trackStates->getTrackState(currentTip);
0777 currentBranch.tipIndex() = currentTip;
0778 currentBranch.nHoles()++;
0779
0780 BranchStopperResult branchStopperResult =
0781 m_extensions.branchStopper(currentBranch, nonSourcelinkState);
0782
0783
0784 if (branchStopperResult == BranchStopperResult::Continue) {
0785
0786 } else {
0787
0788 nBranchesOnSurface = 0;
0789 if (branchStopperResult == BranchStopperResult::StopAndKeep) {
0790 storeLastActiveBranch(result);
0791 }
0792
0793 result.activeBranches.pop_back();
0794 }
0795 } else {
0796 Result<unsigned int> procRes = processNewTrackStates(
0797 state.geoContext, newTrackStateList, result);
0798 if (!procRes.ok()) {
0799 ACTS_ERROR("Processing of selected track states failed: "
0800 << procRes.error());
0801 return procRes.error();
0802 }
0803 nBranchesOnSurface = *procRes;
0804
0805 if (nBranchesOnSurface == 0) {
0806
0807
0808 ACTS_VERBOSE("All branches on surface " << surface->geometryId()
0809 << " have been stopped");
0810 } else {
0811
0812 currentBranch = result.activeBranches.back();
0813 prevTip = currentBranch.tipIndex();
0814
0815 auto currentState = currentBranch.outermostTrackState();
0816
0817 if (currentState.typeFlags().test(TrackStateFlag::OutlierFlag)) {
0818
0819 ACTS_VERBOSE("Outlier state detected on surface "
0820 << surface->geometryId());
0821 } else {
0822
0823 ACTS_VERBOSE("Filtering step successful with "
0824 << nBranchesOnSurface << " branches");
0825
0826
0827 stepper.update(state.stepping,
0828 MultiTrajectoryHelpers::freeFiltered(
0829 state.options.geoContext, currentState),
0830 currentState.filtered(),
0831 currentState.filteredCovariance(), *surface);
0832 ACTS_VERBOSE(
0833 "Stepping state is updated with filtered parameter:");
0834 ACTS_VERBOSE("-> " << currentState.filtered().transpose()
0835 << " of track state with tip = "
0836 << currentState.index());
0837 }
0838 }
0839 }
0840
0841
0842 materialInteractor(surface, state, stepper, navigator,
0843 MaterialUpdateStage::PostUpdate);
0844 } else if (surface->associatedDetectorElement() != nullptr ||
0845 surface->surfaceMaterial() != nullptr) {
0846
0847
0848 nBranchesOnSurface = 1;
0849
0850 auto currentBranch = result.activeBranches.back();
0851 TrackIndexType prevTip = currentBranch.tipIndex();
0852
0853
0854 bool isSensitive = (surface->associatedDetectorElement() != nullptr);
0855 bool isMaterial = (surface->surfaceMaterial() != nullptr);
0856 ACTS_VERBOSE("Detected " << (isSensitive ? "sensitive" : "passive")
0857 << " surface: " << surface->geometryId());
0858
0859 if (currentBranch.nMeasurements() > 0 || isMaterial) {
0860
0861
0862
0863 auto stateMask = PM::Predicted | PM::Jacobian;
0864
0865
0866 stepper.transportCovarianceToCurvilinear(state.stepping);
0867
0868
0869 materialInteractor(surface, state, stepper, navigator,
0870 MaterialUpdateStage::PreUpdate);
0871
0872
0873 auto boundStateRes =
0874 stepper.boundState(state.stepping, *surface, false);
0875 if (!boundStateRes.ok()) {
0876 return boundStateRes.error();
0877 }
0878 auto& boundState = *boundStateRes;
0879 auto& [boundParams, jacobian, pathLength] = boundState;
0880 boundParams.covariance() = state.stepping.cov;
0881
0882
0883 TrackIndexType currentTip = addNonSourcelinkState(
0884 stateMask, boundState, result, isSensitive, prevTip);
0885 auto nonSourcelinkState =
0886 result.trackStates->getTrackState(currentTip);
0887 currentBranch.tipIndex() = currentTip;
0888 if (isSensitive) {
0889 currentBranch.nHoles()++;
0890 }
0891
0892 BranchStopperResult branchStopperResult =
0893 m_extensions.branchStopper(currentBranch, nonSourcelinkState);
0894
0895
0896 if (branchStopperResult == BranchStopperResult::Continue) {
0897
0898 } else {
0899
0900 nBranchesOnSurface = 0;
0901 if (branchStopperResult == BranchStopperResult::StopAndKeep) {
0902 storeLastActiveBranch(result);
0903 }
0904
0905 result.activeBranches.pop_back();
0906 }
0907
0908
0909 materialInteractor(surface, state, stepper, navigator,
0910 MaterialUpdateStage::PostUpdate);
0911 }
0912 } else {
0913
0914
0915 nBranchesOnSurface = 1;
0916 }
0917
0918
0919 if (nBranchesOnSurface == 0) {
0920 ACTS_DEBUG("Branch on surface " << surface->geometryId()
0921 << " is stopped");
0922 if (!result.activeBranches.empty()) {
0923 ACTS_VERBOSE("Propagation jumps to branch with tip = "
0924 << result.activeBranches.back().tipIndex());
0925 reset(state, stepper, navigator, result);
0926 } else {
0927 ACTS_VERBOSE("Stop Kalman filtering with "
0928 << result.collectedTracks.size() << " found tracks");
0929 result.finished = true;
0930 }
0931 }
0932
0933 return Result<void>::success();
0934 }
0935
0936
0937
0938
0939
0940
0941
0942
0943
0944
0945
0946 Result<unsigned int> processNewTrackStates(
0947 const Acts::GeometryContext& gctx,
0948 const CkfTypes::BranchVector<TrackIndexType>& newTrackStateList,
0949 result_type& result) const {
0950 using PM = TrackStatePropMask;
0951
0952 unsigned int nBranchesOnSurface = 0;
0953
0954 auto rootBranch = result.activeBranches.back();
0955
0956
0957
0958 CkfTypes::BranchVector<TrackProxy> newBranches;
0959 for (auto it = newTrackStateList.rbegin(); it != newTrackStateList.rend();
0960 ++it) {
0961
0962 auto newBranch = (it == newTrackStateList.rbegin())
0963 ? rootBranch
0964 : rootBranch.shallowCopy();
0965 newBranch.tipIndex() = *it;
0966 newBranches.push_back(newBranch);
0967 }
0968
0969
0970 result.activeBranches.pop_back();
0971
0972
0973 for (TrackProxy newBranch : newBranches) {
0974 auto trackState = newBranch.outermostTrackState();
0975 TrackStateType typeFlags = trackState.typeFlags();
0976
0977 if (typeFlags.test(TrackStateFlag::OutlierFlag)) {
0978
0979
0980
0981 trackState.shareFrom(PM::Predicted, PM::Filtered);
0982
0983 newBranch.nOutliers()++;
0984 } else if (typeFlags.test(TrackStateFlag::MeasurementFlag)) {
0985
0986 auto updateRes =
0987 m_extensions.updater(gctx, trackState, *updaterLogger);
0988 if (!updateRes.ok()) {
0989 ACTS_ERROR("Update step failed: " << updateRes.error());
0990 return updateRes.error();
0991 }
0992 ACTS_VERBOSE("Appended measurement track state with tip = "
0993 << newBranch.tipIndex());
0994
0995 typeFlags.set(TrackStateFlag::MeasurementFlag);
0996
0997 newBranch.nMeasurements()++;
0998 newBranch.nDoF() += trackState.calibratedSize();
0999 newBranch.chi2() += trackState.chi2();
1000 } else {
1001 ACTS_WARNING("Cannot handle this track state flags");
1002 continue;
1003 }
1004
1005 result.activeBranches.push_back(newBranch);
1006
1007 BranchStopperResult branchStopperResult =
1008 m_extensions.branchStopper(newBranch, trackState);
1009
1010
1011 if (branchStopperResult == BranchStopperResult::Continue) {
1012
1013 nBranchesOnSurface++;
1014 } else {
1015
1016 if (branchStopperResult == BranchStopperResult::StopAndKeep) {
1017 storeLastActiveBranch(result);
1018 }
1019
1020 result.activeBranches.pop_back();
1021 }
1022 }
1023
1024 return nBranchesOnSurface;
1025 }
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036 TrackIndexType addNonSourcelinkState(TrackStatePropMask stateMask,
1037 const BoundState& boundState,
1038 result_type& result, bool isSensitive,
1039 TrackIndexType prevTip) const {
1040 using PM = TrackStatePropMask;
1041
1042
1043 auto trackStateProxy =
1044 result.trackStates->makeTrackState(stateMask, prevTip);
1045 ACTS_VERBOSE("Create " << (isSensitive ? "Hole" : "Material")
1046 << " output track state #"
1047 << trackStateProxy.index()
1048 << " with mask: " << stateMask);
1049
1050 const auto& [boundParams, jacobian, pathLength] = boundState;
1051
1052 trackStateProxy.predicted() = boundParams.parameters();
1053 trackStateProxy.predictedCovariance() = boundParams.covariance().value();
1054 trackStateProxy.jacobian() = jacobian;
1055 trackStateProxy.pathLength() = pathLength;
1056
1057 trackStateProxy.setReferenceSurface(
1058 boundParams.referenceSurface().getSharedPtr());
1059
1060
1061 auto typeFlags = trackStateProxy.typeFlags();
1062 if (trackStateProxy.referenceSurface().surfaceMaterial() != nullptr) {
1063 typeFlags.set(TrackStateFlag::MaterialFlag);
1064 }
1065 typeFlags.set(TrackStateFlag::ParameterFlag);
1066 if (isSensitive) {
1067 typeFlags.set(TrackStateFlag::HoleFlag);
1068 }
1069
1070
1071
1072 trackStateProxy.shareFrom(PM::Predicted, PM::Filtered);
1073
1074 return trackStateProxy.index();
1075 }
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088 template <typename propagator_state_t, typename stepper_t,
1089 typename navigator_t>
1090 void materialInteractor(const Surface* surface, propagator_state_t& state,
1091 const stepper_t& stepper,
1092 const navigator_t& navigator,
1093 const MaterialUpdateStage& updateStage) const {
1094 if (surface == nullptr) {
1095 return;
1096 }
1097
1098
1099 bool hasMaterial = false;
1100
1101 if (surface->surfaceMaterial() != nullptr) {
1102
1103 detail::PointwiseMaterialInteraction interaction(surface, state,
1104 stepper);
1105
1106 if (interaction.evaluateMaterialSlab(state, navigator, updateStage)) {
1107
1108 hasMaterial = true;
1109
1110
1111 interaction.evaluatePointwiseMaterialInteraction(multipleScattering,
1112 energyLoss);
1113
1114
1115 ACTS_VERBOSE("Material effects on surface: "
1116 << surface->geometryId()
1117 << " at update stage: " << updateStage << " are :");
1118 ACTS_VERBOSE("eLoss = "
1119 << interaction.Eloss * interaction.navDir << ", "
1120 << "variancePhi = " << interaction.variancePhi << ", "
1121 << "varianceTheta = " << interaction.varianceTheta
1122 << ", "
1123 << "varianceQoverP = " << interaction.varianceQoverP);
1124
1125
1126 interaction.updateState(state, stepper, addNoise);
1127 }
1128 }
1129
1130 if (!hasMaterial) {
1131
1132 ACTS_VERBOSE("No material effects on surface: " << surface->geometryId()
1133 << " at update stage: "
1134 << updateStage);
1135 }
1136 }
1137
1138 void storeLastActiveBranch(result_type& result) const {
1139 auto currentBranch = result.activeBranches.back();
1140 TrackIndexType currentTip = currentBranch.tipIndex();
1141
1142 ACTS_VERBOSE("Find track with entry index = "
1143 << currentTip << " and there are nMeasurements = "
1144 << currentBranch.nMeasurements()
1145 << ", nOutliers = " << currentBranch.nOutliers()
1146 << ", nHoles = " << currentBranch.nHoles() << " on track");
1147
1148 std::optional<TrackStateProxy> lastMeasurement;
1149 for (const auto& trackState : currentBranch.trackStatesReversed()) {
1150 if (trackState.typeFlags().test(TrackStateFlag::MeasurementFlag)) {
1151 lastMeasurement = trackState;
1152 break;
1153 }
1154 }
1155
1156 if (lastMeasurement.has_value()) {
1157 currentBranch.tipIndex() = lastMeasurement->index();
1158 result.collectedTracks.push_back(currentBranch);
1159 ACTS_VERBOSE("Last measurement found on track with entry index = "
1160 << currentTip << " and measurement index = "
1161 << lastMeasurement->index());
1162 } else {
1163 ACTS_VERBOSE(
1164 "No measurement found on track with entry index = " << currentTip);
1165 }
1166 }
1167
1168 CombinatorialKalmanFilterExtensions<track_container_t> m_extensions;
1169
1170
1171 source_link_accessor_t m_sourcelinkAccessor;
1172
1173 using source_link_iterator_t =
1174 decltype(std::declval<decltype(m_sourcelinkAccessor(
1175 *static_cast<const Surface*>(nullptr)))>()
1176 .first);
1177
1178 using TrackStateCandidateCreator =
1179 typename CombinatorialKalmanFilterOptions<
1180 source_link_iterator_t,
1181 track_container_t>::TrackStateCandidateCreator;
1182
1183
1184
1185
1186 TrackStateCandidateCreator trackStateCandidateCreator;
1187
1188
1189 EndOfWorldReached endOfWorldReached;
1190
1191
1192 const Logger* actorLogger{nullptr};
1193
1194 const Logger* updaterLogger{nullptr};
1195
1196 const Logger& logger() const { return *actorLogger; }
1197 };
1198
1199 template <typename source_link_accessor_t, typename parameters_t>
1200 class Aborter {
1201 public:
1202
1203 using action_type = Actor<source_link_accessor_t, parameters_t>;
1204
1205 template <typename propagator_state_t, typename stepper_t,
1206 typename navigator_t, typename result_t>
1207 bool operator()(propagator_state_t& , const stepper_t& ,
1208 const navigator_t& , const result_t& result,
1209 const Logger& ) const {
1210 return !result.lastError.ok() || result.finished;
1211 }
1212 };
1213
1214
1215
1216 struct StubPathLimitReached {
1217 double internalLimit{};
1218
1219 template <typename propagator_state_t, typename stepper_t,
1220 typename navigator_t>
1221 bool operator()(propagator_state_t& , const stepper_t& ,
1222 const navigator_t& ,
1223 const Logger& ) const {
1224 return false;
1225 }
1226 };
1227
1228 public:
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245 template <typename source_link_iterator_t, typename start_parameters_t,
1246 typename parameters_t = BoundTrackParameters>
1247 auto findTracks(const start_parameters_t& initialParameters,
1248 const CombinatorialKalmanFilterOptions<
1249 source_link_iterator_t, track_container_t>& tfOptions,
1250 track_container_t& trackContainer) const
1251 -> Result<std::vector<
1252 typename std::decay_t<decltype(trackContainer)>::TrackProxy>> {
1253 using SourceLinkAccessor =
1254 SourceLinkAccessorDelegate<source_link_iterator_t>;
1255
1256
1257 using CombinatorialKalmanFilterAborter =
1258 Aborter<SourceLinkAccessor, parameters_t>;
1259 using CombinatorialKalmanFilterActor =
1260 Actor<SourceLinkAccessor, parameters_t>;
1261 using Actors = ActionList<CombinatorialKalmanFilterActor>;
1262 using Aborters = AbortList<CombinatorialKalmanFilterAborter>;
1263
1264
1265 using PropagatorOptions =
1266 typename propagator_t::template Options<Actors, Aborters>;
1267 PropagatorOptions propOptions(tfOptions.geoContext,
1268 tfOptions.magFieldContext);
1269
1270
1271 propOptions.setPlainOptions(tfOptions.propagatorPlainOptions);
1272
1273
1274 auto& combKalmanActor =
1275 propOptions.actionList.template get<CombinatorialKalmanFilterActor>();
1276 combKalmanActor.targetReached.surface = tfOptions.targetSurface;
1277 combKalmanActor.multipleScattering = tfOptions.multipleScattering;
1278 combKalmanActor.energyLoss = tfOptions.energyLoss;
1279 combKalmanActor.skipPrePropagationUpdate =
1280 tfOptions.skipPrePropagationUpdate;
1281 combKalmanActor.actorLogger = m_actorLogger.get();
1282 combKalmanActor.updaterLogger = m_updaterLogger.get();
1283 combKalmanActor.calibrationContextPtr = &tfOptions.calibrationContext.get();
1284
1285
1286 combKalmanActor.m_sourcelinkAccessor = tfOptions.sourcelinkAccessor;
1287 combKalmanActor.m_extensions = tfOptions.extensions;
1288 combKalmanActor.trackStateCandidateCreator =
1289 tfOptions.trackStateCandidateCreator;
1290 DefaultTrackStateCreator defaultTrackStateCreator;
1291
1292
1293 if (!combKalmanActor.trackStateCandidateCreator.connected()) {
1294 defaultTrackStateCreator.calibrator = tfOptions.extensions.calibrator;
1295 defaultTrackStateCreator.measurementSelector =
1296 tfOptions.extensions.measurementSelector;
1297 combKalmanActor.trackStateCandidateCreator.template connect<
1298 &DefaultTrackStateCreator::template createSourceLinkTrackStates<
1299 source_link_iterator_t>>(&defaultTrackStateCreator);
1300 }
1301
1302 auto propState =
1303 m_propagator.template makeState<start_parameters_t, PropagatorOptions,
1304 StubPathLimitReached>(initialParameters,
1305 propOptions);
1306
1307 auto& r =
1308 propState
1309 .template get<CombinatorialKalmanFilterResult<track_container_t>>();
1310 r.tracks = &trackContainer;
1311 r.trackStates = &trackContainer.trackStateContainer();
1312
1313 auto rootBranch = trackContainer.makeTrack();
1314 r.activeBranches.push_back(rootBranch);
1315
1316 auto propagationResult = m_propagator.propagate(propState);
1317
1318 auto result = m_propagator.makeResult(
1319 std::move(propState), propagationResult, propOptions, false);
1320
1321 if (!result.ok()) {
1322 ACTS_ERROR("Propagation failed: " << result.error() << " "
1323 << result.error().message()
1324 << " with the initial parameters: \n"
1325 << initialParameters.parameters());
1326 return result.error();
1327 }
1328
1329 auto& propRes = *result;
1330
1331
1332 auto combKalmanResult =
1333 std::move(propRes.template get<
1334 CombinatorialKalmanFilterResult<track_container_t>>());
1335
1336 Result<void> error = combKalmanResult.lastError;
1337 if (error.ok() && !combKalmanResult.finished) {
1338 error = Result<void>(
1339 CombinatorialKalmanFilterError::PropagationReachesMaxSteps);
1340 }
1341 if (!error.ok()) {
1342 ACTS_ERROR("CombinatorialKalmanFilter failed: "
1343 << combKalmanResult.lastError.error() << " "
1344 << combKalmanResult.lastError.error().message()
1345 << " with the initial parameters: "
1346 << initialParameters.parameters().transpose());
1347 return error.error();
1348 }
1349
1350 return std::move(combKalmanResult.collectedTracks);
1351 }
1352 };
1353
1354 }