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