From 8a784f4c22c04632be1f414147dfa85838cc369b Mon Sep 17 00:00:00 2001 From: Goetz Gaycken Date: Tue, 29 Oct 2024 13:20:12 +0100 Subject: [PATCH 1/2] Convert to bound state also on empty sensitive surfaces. This allows to merge retrieval of source link ranges, measurement selection and track state creation into one unit. This increases the computational effort slightly for empty, sensitive surfaces, since the computation of bound states is slightly more demanding than the computation of curvilinear states, but for complex events like HL-LHC events the degradation was not measurable. --- Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index 35acbc49feb..5f6b26d39dd 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -747,7 +747,7 @@ class CombinatorialKalmanFilter { } // Transport the covariance to the surface - if (isHole || isMaterialOnly) { + if (isMaterialOnly) { stepper.transportCovarianceToCurvilinear(state.stepping); } else { stepper.transportCovarianceToBound(state.stepping, *surface); From 2cc2eccaa9fbab2bbc4935357caea18ec77b5be3 Mon Sep 17 00:00:00 2001 From: Goetz Gaycken Date: Thu, 31 Oct 2024 19:24:51 +0100 Subject: [PATCH 2/2] Move track state creator etc. out of the CKF. Merge retrieval of source link ranges, measurement selection and track state creation into one unit which the CKF interacts with via a single delegate. The delegates for measurement selection track state creation and calibration are removed from the CKF options/extensions. The original building blocks (SourceLink accessor, measurement selector, and track state creator, or the combined measurement selector and track state creator) can still be used, however require to setup an additional helper object which combines these independent components. The algorithmic code is unchanged. --- .../CombinatorialKalmanFilter.hpp | 400 ++---------------- .../CombinatorialKalmanFilterExtensions.hpp | 101 +++++ .../ComposableTrackStateCreator.hpp | 368 ++++++++++++++++ .../TrackFinding/TrackFindingAlgorithm.hpp | 3 +- .../src/TrackFindingAlgorithm.cpp | 41 +- .../CombinatorialKalmanFilterTests.cpp | 55 ++- 6 files changed, 560 insertions(+), 408 deletions(-) create mode 100644 Core/include/Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp create mode 100644 Core/include/Acts/TrackFinding/ComposableTrackStateCreator.hpp diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index 5f6b26d39dd..2ab6851443f 100644 --- a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp @@ -25,7 +25,8 @@ #include "Acts/Propagator/detail/LoopProtection.hpp" #include "Acts/Propagator/detail/PointwiseMaterialInteraction.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilterError.hpp" -#include "Acts/TrackFitting/KalmanFitter.hpp" +#include "Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp" +#include "Acts/TrackFinding/ComposableTrackStateCreator.hpp" #include "Acts/TrackFitting/detail/VoidFitterComponents.hpp" #include "Acts/Utilities/CalibrationContext.hpp" #include "Acts/Utilities/Logger.hpp" @@ -39,97 +40,12 @@ namespace Acts { -/// Return type of the `BranchStopper` delegate for the -/// CombinatorialKalmanFilter -enum class CombinatorialKalmanFilterBranchStopperResult { - Continue, - StopAndDrop, - StopAndKeep, -}; - -/// Extension struct which holds the delegates to customize the CKF behavior -template -struct CombinatorialKalmanFilterExtensions { - using traj_t = typename track_container_t::TrackStateContainerBackend; - using candidate_container_t = - typename std::vector; - using TrackProxy = typename track_container_t::TrackProxy; - using TrackStateProxy = typename track_container_t::TrackStateProxy; - - using BranchStopperResult = CombinatorialKalmanFilterBranchStopperResult; - - using Calibrator = typename KalmanFitterExtensions::Calibrator; - using Updater = typename KalmanFitterExtensions::Updater; - using MeasurementSelector = - Delegate>( - candidate_container_t& trackStates, bool&, const Logger&)>; - using BranchStopper = - Delegate; - - /// The Calibrator is a dedicated calibration algorithm that allows to - /// calibrate measurements using track information, this could be e.g. sagging - /// for wires, module deformations, etc. - Calibrator calibrator{ - DelegateFuncTag>{}}; - - /// The updater incorporates measurement information into the track parameters - Updater updater{DelegateFuncTag>{}}; - - /// The measurement selector is called during the filtering by the Actor. - MeasurementSelector measurementSelector{ - DelegateFuncTag{}}; - - /// The branch stopper is called during the filtering by the Actor. - BranchStopper branchStopper{DelegateFuncTag{}}; - - private: - /// Default measurement selector which will return all measurements - /// @param candidates Measurement track state candidates - static Result::iterator, - typename std::vector::iterator>> - voidMeasurementSelector(typename std::vector& candidates, - bool& /*isOutlier*/, const Logger& /*logger*/) { - return std::pair{candidates.begin(), candidates.end()}; - }; - - /// Default branch stopper which will never stop - /// @return false - static BranchStopperResult voidBranchStopper( - const TrackProxy& /*track*/, const TrackStateProxy& /*trackState*/) { - return BranchStopperResult::Continue; - } -}; - -/// Delegate type that retrieves a range of source links to for a given surface -/// to be processed by the CKF -template -using SourceLinkAccessorDelegate = - Delegate( - const Surface&)>; - -/// expected max number of track states that are expected to be added by -/// stateCandidateCreator -/// @note if the number of states exceeds this number dynamic memory allocation will occur. -/// the number is chosen to yield a container size of 64 bytes. -static constexpr std::size_t s_maxBranchesPerSurface = 10; - -namespace CkfTypes { - -template -using BranchVector = boost::container::small_vector; - -} // namespace CkfTypes - /// Combined options for the combinatorial Kalman filter. /// /// @tparam source_link_iterator_t Type of the source link iterator /// @tparam track_container_t Type of the track container -template +template struct CombinatorialKalmanFilterOptions { - using SourceLinkIterator = source_link_iterator_t; - using SourceLinkAccessor = SourceLinkAccessorDelegate; - using TrackStateContainerBackend = typename track_container_t::TrackStateContainerBackend; using TrackStateProxy = typename track_container_t::TrackStateProxy; @@ -139,7 +55,6 @@ struct CombinatorialKalmanFilterOptions { /// @param gctx The geometry context for this track finding/fitting /// @param mctx The magnetic context for this track finding/fitting /// @param cctx The calibration context for this track finding/fitting - /// @param accessor_ The source link accessor /// @param extensions_ The extension struct /// @param pOptions The plain propagator options /// @param mScattering Whether to include multiple scattering @@ -147,14 +62,12 @@ struct CombinatorialKalmanFilterOptions { CombinatorialKalmanFilterOptions( const GeometryContext& gctx, const MagneticFieldContext& mctx, std::reference_wrapper cctx, - SourceLinkAccessor accessor_, CombinatorialKalmanFilterExtensions extensions_, const PropagatorPlainOptions& pOptions, bool mScattering = true, bool eLoss = true) : geoContext(gctx), magFieldContext(mctx), calibrationContext(cctx), - sourceLinkAccessor(std::move(accessor_)), extensions(extensions_), propagatorPlainOptions(pOptions), multipleScattering(mScattering), @@ -170,9 +83,6 @@ struct CombinatorialKalmanFilterOptions { /// context object for the calibration std::reference_wrapper calibrationContext; - /// The source link accessor - SourceLinkAccessor sourceLinkAccessor; - /// The filter extensions CombinatorialKalmanFilterExtensions extensions; @@ -184,39 +94,6 @@ struct CombinatorialKalmanFilterOptions { /// certain surface const Surface* targetSurface = nullptr; - using BoundState = std::tuple; - - /// Delegate definition to create track states for selected measurements - /// - /// @note expected to iterator over the given sourceLink range, - /// select measurements, and create track states for - /// which new tips are to be created, more over the outlier - /// flag should be set for states that are outlier. - /// - /// @param geoContext The current geometry context - /// @param calibrationContext pointer to the current calibration context - /// @param surface the surface at which new track states are to be created - /// @param boundState the current bound state of the trajectory - /// @param slBegin Begin iterator for sourceLinks - /// @param slEnd End iterator for sourceLinks - /// @param prevTip Index pointing at previous trajectory state (i.e. tip) - /// @param bufferTrajectory a temporary trajectory which can be used to create temporary track states - /// @param trackStateCandidates a temporary buffer that can be used to collect track states - /// @param trajectory the trajectory to which the new states are to be added - /// @param logger a logger for messages - using TrackStateCandidateCreator = - Delegate>( - const GeometryContext& geoContext, - const CalibrationContext& calibrationContext, const Surface& surface, - const BoundState& boundState, source_link_iterator_t slBegin, - source_link_iterator_t slEnd, TrackIndexType prevTip, - TrackStateContainerBackend& bufferTrajectory, - std::vector& trackStateCandidates, - TrackStateContainerBackend& trajectory, const Logger& logger)>; - - /// The delegate to create new track states. - TrackStateCandidateCreator trackStateCandidateCreator; - /// Whether to consider multiple scattering. bool multipleScattering = true; @@ -309,191 +186,13 @@ class CombinatorialKalmanFilter { const Logger& logger() const { return *m_logger; } - struct DefaultTrackStateCreator { - typename CombinatorialKalmanFilterExtensions::Calibrator - calibrator; - typename CombinatorialKalmanFilterExtensions< - track_container_t>::MeasurementSelector measurementSelector; - - /// Create track states for selected measurements given by the source links - /// - /// @param gctx The current geometry context - /// @param calibrationContext pointer to the current calibration context - /// @param surface the surface the sourceLinks are associated to - /// @param boundState Bound state from the propagation on this surface - /// @param slBegin Begin iterator for sourceLinks - /// @param slEnd End iterator for sourceLinks - /// @param prevTip Index pointing at previous trajectory state (i.e. tip) - /// @param bufferTrajectory a buffer for temporary candidate track states - /// @param trackStateCandidates a buffer for temporary track state proxies for candidates - /// @param trajectory the trajectory to which new track states for selected measurements will be added - /// @param logger the logger for messages. - template - Result> createSourceLinkTrackStates( - const GeometryContext& gctx, - const CalibrationContext& calibrationContext, - [[maybe_unused]] const Surface& surface, const BoundState& boundState, - source_link_iterator_t slBegin, source_link_iterator_t slEnd, - TrackIndexType prevTip, TrackStateContainerBackend& bufferTrajectory, - std::vector& trackStateCandidates, - TrackStateContainerBackend& trajectory, const Logger& logger) const { - using PM = TrackStatePropMask; - - using ResultTrackStateList = - Acts::Result>; - ResultTrackStateList resultTrackStateList{ - CkfTypes::BranchVector()}; - const auto& [boundParams, jacobian, pathLength] = boundState; - - trackStateCandidates.clear(); - if constexpr (std::ranges::random_access_range) { - trackStateCandidates.reserve(std::distance(slBegin, slEnd)); - } - - // Calibrate all the source links on the surface since the selection has - // to be done based on calibrated measurement - for (auto it = slBegin; it != slEnd; ++it) { - // get the source link - const auto sourceLink = *it; - - // prepare the track state - PM mask = PM::Predicted | PM::Jacobian | PM::Calibrated; - if (it != slBegin) { - // not the first TrackState, only need uncalibrated and calibrated - mask = PM::Calibrated; - } - - ACTS_VERBOSE("Create temp track state with mask: " << mask); - // CAREFUL! This trackstate has a previous index that is not in this - // MultiTrajectory Visiting brackwards from this track state will - // fail! - auto ts = bufferTrajectory.makeTrackState(mask, prevTip); - - if (it == slBegin) { - // only set these for first - ts.predicted() = boundParams.parameters(); - if (boundParams.covariance()) { - ts.predictedCovariance() = *boundParams.covariance(); - } - ts.jacobian() = jacobian; - } else { - // subsequent track states can reuse - auto& first = trackStateCandidates.front(); - ts.shareFrom(first, PM::Predicted); - ts.shareFrom(first, PM::Jacobian); - } - - ts.pathLength() = pathLength; - ts.setReferenceSurface(boundParams.referenceSurface().getSharedPtr()); - - // now calibrate the track state - calibrator(gctx, calibrationContext, sourceLink, ts); - - trackStateCandidates.push_back(ts); - } - - bool isOutlier = false; - Result::iterator, - typename std::vector::iterator>> - selectorResult = - measurementSelector(trackStateCandidates, isOutlier, logger); - if (!selectorResult.ok()) { - ACTS_ERROR("Selection of calibrated measurements failed: " - << selectorResult.error().message()); - resultTrackStateList = - ResultTrackStateList::failure(selectorResult.error()); - } else { - auto selectedTrackStateRange = *selectorResult; - resultTrackStateList = processSelectedTrackStates( - selectedTrackStateRange.first, selectedTrackStateRange.second, - trajectory, isOutlier, logger); - } - - return resultTrackStateList; - } - - /// Create track states for the given trajectory from candidate track states - /// - /// @param begin begin iterator of the list of candidate track states - /// @param end end iterator of the list of candidate track states - /// @param trackStates the trajectory to which the new track states are added - /// @param isOutlier true if the candidate(s) is(are) an outlier(s). - /// @param logger the logger for messages - Result> processSelectedTrackStates( - typename std::vector::const_iterator begin, - typename std::vector::const_iterator end, - TrackStateContainerBackend& trackStates, bool isOutlier, - const Logger& logger) const { - using PM = TrackStatePropMask; - - using ResultTrackStateList = - Acts::Result>; - ResultTrackStateList resultTrackStateList{ - CkfTypes::BranchVector()}; - CkfTypes::BranchVector& trackStateList = - *resultTrackStateList; - trackStateList.reserve(end - begin); - - std::optional firstTrackState{std::nullopt}; - for (auto it = begin; it != end; ++it) { - auto& candidateTrackState = *it; - - PM mask = PM::Predicted | PM::Filtered | PM::Jacobian | PM::Calibrated; - if (it != begin) { - // subsequent track states don't need storage for these as they will - // be shared - mask &= ~PM::Predicted & ~PM::Jacobian; - } - if (isOutlier) { - // outlier won't have separate filtered parameters - mask &= ~PM::Filtered; - } - - // copy this trackstate into fitted states MultiTrajectory - auto trackState = - trackStates.makeTrackState(mask, candidateTrackState.previous()); - ACTS_VERBOSE("Create SourceLink output track state #" - << trackState.index() << " with mask: " << mask); - - if (it != begin) { - // assign indices pointing to first track state - trackState.shareFrom(*firstTrackState, PM::Predicted); - trackState.shareFrom(*firstTrackState, PM::Jacobian); - } else { - firstTrackState = trackState; - } - - // either copy ALL or everything except for predicted and jacobian - trackState.allocateCalibrated(candidateTrackState.calibratedSize()); - trackState.copyFrom(candidateTrackState, mask, false); - - auto typeFlags = trackState.typeFlags(); - typeFlags.set(TrackStateFlag::ParameterFlag); - typeFlags.set(TrackStateFlag::MeasurementFlag); - if (trackState.referenceSurface().surfaceMaterial() != nullptr) { - typeFlags.set(TrackStateFlag::MaterialFlag); - } - if (isOutlier) { - // propagate information that this is an outlier state - ACTS_VERBOSE( - "Creating outlier track state with tip = " << trackState.index()); - typeFlags.set(TrackStateFlag::OutlierFlag); - } - - trackStateList.push_back(trackState.index()); - } - return resultTrackStateList; - } - }; - /// @brief Propagator Actor plugin for the CombinatorialKalmanFilter /// - /// @tparam source_link_accessor_t The type of source link accessor /// @tparam parameters_t The type of parameters used for "local" parameters. /// /// The CombinatorialKalmanFilter Actor does not rely on the measurements to /// be sorted along the track. - template + template class Actor { public: using BoundState = std::tuple; @@ -732,20 +431,6 @@ class CombinatorialKalmanFilter { return Result::success(); } - using SourceLinkRange = decltype(m_sourceLinkAccessor(*surface)); - std::optional slRange = std::nullopt; - bool hasMeasurements = false; - if (isSensitive) { - slRange = m_sourceLinkAccessor(*surface); - hasMeasurements = slRange->first != slRange->second; - } - bool isHole = isSensitive && !hasMeasurements; - - if (isHole) { - ACTS_VERBOSE("Detected hole before measurement selection on surface " - << surface->geometryId()); - } - // Transport the covariance to the surface if (isMaterialOnly) { stepper.transportCovarianceToCurvilinear(state.stepping); @@ -769,17 +454,17 @@ class CombinatorialKalmanFilter { auto currentBranch = result.activeBranches.back(); TrackIndexType prevTip = currentBranch.tipIndex(); - // Create trackstates for all source links (will be filtered later) using TrackStatesResult = Acts::Result>; TrackStatesResult tsRes = TrackStatesResult::success({}); - if (hasMeasurements) { - auto [slBegin, slEnd] = *slRange; - - tsRes = trackStateCandidateCreator( + if (isSensitive) { + // extend trajectory with measurements associated to the current surface + // which may create extra trajectory branches if more than one + // measurement is selected. + tsRes = m_extensions.extendOrBranchTrajectory( state.geoContext, *calibrationContextPtr, *surface, boundState, - slBegin, slEnd, prevTip, *result.trackStates, - result.trackStateCandidates, *result.trackStates, logger()); + prevTip, *result.trackStates, result.trackStateCandidates, + *result.trackStates, logger()); if (!tsRes.ok()) { ACTS_ERROR( "Processing of selected track states failed: " << tsRes.error()); @@ -1098,23 +783,6 @@ class CombinatorialKalmanFilter { CombinatorialKalmanFilterExtensions m_extensions; - /// The source link accessor - source_link_accessor_t m_sourceLinkAccessor; - - using SourceLinkIterator = - decltype(std::declval(nullptr)))>() - .first); - - using TrackStateCandidateCreator = - typename CombinatorialKalmanFilterOptions< - SourceLinkIterator, track_container_t>::TrackStateCandidateCreator; - - /// the stateCandidator to be used - /// @note will be set to a default trackStateCandidateCreator or the one - // provided via the extension - TrackStateCandidateCreator trackStateCandidateCreator; - /// End of world aborter EndOfWorldReached endOfWorldReached; @@ -1146,7 +814,6 @@ class CombinatorialKalmanFilter { public: /// Combinatorial Kalman Filter implementation, calls the Kalman filter /// - /// @tparam source_link_iterator_t Type of the source link iterator /// @tparam start_parameters_t Type of the initial parameters /// @tparam parameters_t Type of parameters used for local parameters /// @@ -1162,21 +829,17 @@ class CombinatorialKalmanFilter { /// /// @return a container of track finding result for all the initial track /// parameters - template - auto findTracks(const start_parameters_t& initialParameters, - const CombinatorialKalmanFilterOptions< - source_link_iterator_t, track_container_t>& tfOptions, - track_container_t& trackContainer, - typename track_container_t::TrackProxy rootBranch) const + auto findTracks( + const start_parameters_t& initialParameters, + const CombinatorialKalmanFilterOptions& tfOptions, + track_container_t& trackContainer, + typename track_container_t::TrackProxy rootBranch) const -> Result::TrackProxy>> { - using SourceLinkAccessor = - SourceLinkAccessorDelegate; - // Create the ActorList - using CombinatorialKalmanFilterActor = - Actor; + using CombinatorialKalmanFilterActor = Actor; using Actors = ActorList; // Create relevant options for the propagation options @@ -1199,22 +862,8 @@ class CombinatorialKalmanFilter { combKalmanActor.updaterLogger = m_updaterLogger.get(); combKalmanActor.calibrationContextPtr = &tfOptions.calibrationContext.get(); - // copy source link accessor, calibrator and measurement selector - combKalmanActor.m_sourceLinkAccessor = tfOptions.sourceLinkAccessor; + // copy delegates to calibrator, updater, branch stopper combKalmanActor.m_extensions = tfOptions.extensions; - combKalmanActor.trackStateCandidateCreator = - tfOptions.trackStateCandidateCreator; - DefaultTrackStateCreator defaultTrackStateCreator; - // connect a default state candidate creator if no state candidate creator - // was provided via the extension - if (!combKalmanActor.trackStateCandidateCreator.connected()) { - defaultTrackStateCreator.calibrator = tfOptions.extensions.calibrator; - defaultTrackStateCreator.measurementSelector = - tfOptions.extensions.measurementSelector; - combKalmanActor.trackStateCandidateCreator.template connect< - &DefaultTrackStateCreator::template createSourceLinkTrackStates< - source_link_iterator_t>>(&defaultTrackStateCreator); - } auto propState = m_propagator.template makeState - auto findTracks(const start_parameters_t& initialParameters, - const CombinatorialKalmanFilterOptions< - source_link_iterator_t, track_container_t>& tfOptions, - track_container_t& trackContainer) const + auto findTracks( + const start_parameters_t& initialParameters, + const CombinatorialKalmanFilterOptions& tfOptions, + track_container_t& trackContainer) const -> Result::TrackProxy>> { auto rootBranch = trackContainer.makeTrack(); diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp new file mode 100644 index 00000000000..6ca575c1d8c --- /dev/null +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp @@ -0,0 +1,101 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/TrackFitting/KalmanFitter.hpp" +#include "Acts/Utilities/Result.hpp" + +#include + +namespace Acts { + +/// expected max number of track states that are expected to be added by +/// stateCandidateCreator +/// @note if the number of states exceeds this number dynamic memory allocation will occur. +/// the number is chosen to yield a container size of 64 bytes. +static constexpr std::size_t s_maxBranchesPerSurface = 10; + +namespace CkfTypes { + +template +using BranchVector = boost::container::small_vector; + +using BoundState = std::tuple; + +} // namespace CkfTypes + +/// Return type of the `BranchStopper` delegate for the +/// CombinatorialKalmanFilter +enum class CombinatorialKalmanFilterBranchStopperResult { + Continue, + StopAndDrop, + StopAndKeep, +}; + +/// Extension struct which holds the delegates to customize the CKF behavior +template +struct CombinatorialKalmanFilterExtensions { + using traj_t = typename track_container_t::TrackStateContainerBackend; + using TrackProxy = typename track_container_t::TrackProxy; + using TrackStateProxy = typename track_container_t::TrackStateProxy; + + using BranchStopperResult = CombinatorialKalmanFilterBranchStopperResult; + + using Updater = typename KalmanFitterExtensions::Updater; + using BranchStopper = + Delegate; + + /// The updater incorporates measurement information into the track parameters + Updater updater{DelegateFuncTag>{}}; + + /// The branch stopper is called during the filtering by the Actor. + BranchStopper branchStopper{DelegateFuncTag{}}; + + /// @brief Delegate the extension of the trajectory onto the given surface to + /// an external unit. + /// + /// @note Expected to create track states for measurements associated to the + /// given surface which match the given bound state. Moreover the + /// The "filtered" data is not expected to be set, but the outlier + /// flag should be set for states that are considered to be outlier. + /// + /// @param geoContext The current geometry context + /// @param calibrationContext pointer to the current calibration context + /// @param surface the surface at which new track states are to be created + /// @param boundState the current bound state of the trajectory + /// @param prevTip Index pointing at previous trajectory state (i.e. tip) + /// @param bufferTrajectory a temporary trajectory which can be used to create temporary track states + /// @param trackStateCandidates a temporary buffer that can be used to collect track states + /// @param trajectory the trajectory to which the new states are to be added + /// @param logger a logger for messages + /// @return indices of new track states which extend the trajectory given by prevTip + + using TrajectoryExtender = + Delegate>( + const GeometryContext& geoContext, + const CalibrationContext& calibrationContext, const Surface& surface, + const CkfTypes::BoundState& boundState, TrackIndexType prevTip, + traj_t& bufferTrajectory, + std::vector& trackStateCandidates, + traj_t& trajectory, const Logger& logger)>; + + /// The delegate to create new track states. + /// @note a reference implementation can be found in @ref ComposableTrackStateCreator + /// which makes uses of @ref MeasurementSelector and SourceLinkAccessor + TrajectoryExtender extendOrBranchTrajectory; + + private: + /// Default branch stopper which will never stop + /// @return false + static BranchStopperResult voidBranchStopper( + const TrackProxy& /*track*/, const TrackStateProxy& /*trackState*/) { + return BranchStopperResult::Continue; + } +}; +} // namespace Acts diff --git a/Core/include/Acts/TrackFinding/ComposableTrackStateCreator.hpp b/Core/include/Acts/TrackFinding/ComposableTrackStateCreator.hpp new file mode 100644 index 00000000000..1ba70a7f69a --- /dev/null +++ b/Core/include/Acts/TrackFinding/ComposableTrackStateCreator.hpp @@ -0,0 +1,368 @@ +// This file is part of the ACTS project. +// +// Copyright (C) 2016 CERN for the benefit of the ACTS project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#pragma once + +// for definitions of Calibrator, MeasurementSelector +#include "Acts/TrackFitting/KalmanFitter.hpp" + +namespace Acts { +/// to be processed by the CKF +template +using SourceLinkAccessorDelegate = + Delegate( + const Surface&)>; + +/// @brief Get source link range for the given surface and pass to derived class for +/// the actual creation of track states. +/// +/// - First get a source link range covering relevant measurements for the given +/// surface +/// This task is delegated to a SourceLinkAccessor. +/// - Then, pass the source link range to the derived class which will do +/// everything else. +template +struct ComposableTrackStateCreator { + using SourceLinkAccessor = SourceLinkAccessorDelegate; + using TrackStatesResult = + Acts::Result>; + using TrackStateContainerBackend = + typename track_container_t::TrackStateContainerBackend; + using TrackProxy = typename track_container_t::TrackProxy; + using TrackStateProxy = typename track_container_t::TrackStateProxy; + using BoundState = std::tuple; + + SourceLinkAccessor sourceLinkAccessor; + + /// @brief extend the trajectory onto the given surface. + /// + /// @param gctx The geometry context to be used for this task + /// @param calibrationContext The calibration context used to fill the calibrated data + /// @param surface The surface onto which the trajectory is extended + /// @param boundState the predicted bound state on the given surface + /// @param prevTip the tip of the trajectory which is to be extended + /// @param bufferTrajectory a temporary buffer which can be used to create + /// temporary track states before the selection. + /// @param trackStateCandidates a temporary buffer which can be used to + /// to keep track of newly created temporary track states. + /// @param trajectory the trajectory to be extended. + /// @param logger a logger for messages. + /// + /// @return a list of indices of newly created track states which extend the + /// trajectory onto the given surface and match the bound state, or an + /// error. + /// + /// Extend or branch the trajectory onto the given surface. This may create + /// new track states using measurements which match the predicted bound state. + /// This may create multiple branches. The new track states still miss the + /// "filtered" data. + Result> extendTrajectoryOntoSurface( + const GeometryContext& gctx, const CalibrationContext& calibrationContext, + [[maybe_unused]] const Surface& surface, const BoundState& boundState, + TrackIndexType prevTip, TrackStateContainerBackend& bufferTrajectory, + std::vector& trackStateCandidates, + TrackStateContainerBackend& trajectory, const Logger& logger) const { + TrackStatesResult tsRes = TrackStatesResult::success({}); + using SourceLinkRange = decltype(sourceLinkAccessor(surface)); + SourceLinkRange slRange = sourceLinkAccessor(surface); + if (slRange.first != slRange.second) { + auto [slBegin, slEnd] = slRange; + tsRes = derived().createSourceLinkTrackStates( + gctx, calibrationContext, surface, boundState, slBegin, slEnd, + prevTip, bufferTrajectory, trackStateCandidates, trajectory, logger); + } + return tsRes; + } + + private: + const track_state_creator_derived_t& derived() const { + return *static_cast(this); + } +}; + +/// @brief Create track states for selected measurements from a source link range. +/// +/// - First create temporary track states for all measurements defined +/// by a source link range, calibrate the measurements and fill the +/// the calibrated data of these track states using a dedicated calibrator +/// - The measurement selection is delegated to a dedicated measurement +/// selector. +/// - Finally add branches to the given trajectory for the selected, temporary +/// track states. The track states of these branches still lack the filtered +// data which is to be filled by the next stage e.g. the +// CombinatorialKalmanFilter. +template +struct TrackStateCandidateCreatorImpl + : ComposableTrackStateCreator< + TrackStateCandidateCreatorImpl, + source_link_iterator_t, track_container_t> { + using candidate_container_t = + typename std::vector; + + using TrackStateContainerBackend = + typename track_container_t::TrackStateContainerBackend; + using TrackProxy = typename track_container_t::TrackProxy; + using TrackStateProxy = typename track_container_t::TrackStateProxy; + using BoundState = std::tuple; + + using Calibrator = + typename KalmanFitterExtensions::Calibrator; + using MeasurementSelector = + Delegate>( + candidate_container_t& trackStates, bool&, const Logger&)>; + + /// The Calibrator is a dedicated calibration algorithm that allows to + /// calibrate measurements using track information, this could be e.g. sagging + /// for wires, module deformations, etc. + Calibrator calibrator{DelegateFuncTag< + detail::voidFitterCalibrator>{}}; + + MeasurementSelector measurementSelector{ + DelegateFuncTag{}}; + + /// Create track states for selected measurements given by the source links + /// + /// @param gctx The current geometry context + /// @param calibrationContext pointer to the current calibration context + /// @param surface the surface the sourceLinks are associated to + /// @param boundState Bound state from the propagation on this surface + /// @param slBegin Begin iterator for sourceLinks + /// @param slEnd End iterator for sourceLinks + /// @param prevTip Index pointing at previous trajectory state (i.e. tip) + /// @param bufferTrajectory a buffer for temporary candidate track states + /// @param trackStateCandidates a buffer for temporary track state proxies for candidates + /// @param trajectory the trajectory to which new track states for selected measurements will be added + /// @param logger the logger for messages. + Result> createSourceLinkTrackStates( + const GeometryContext& gctx, const CalibrationContext& calibrationContext, + [[maybe_unused]] const Surface& surface, const BoundState& boundState, + source_link_iterator_t slBegin, source_link_iterator_t slEnd, + TrackIndexType prevTip, TrackStateContainerBackend& bufferTrajectory, + std::vector& trackStateCandidates, + TrackStateContainerBackend& trajectory, const Logger& logger) const { + using PM = TrackStatePropMask; + + using ResultTrackStateList = + Acts::Result>; + ResultTrackStateList resultTrackStateList{ + CkfTypes::BranchVector()}; + const auto& [boundParams, jacobian, pathLength] = boundState; + + trackStateCandidates.clear(); + if constexpr (std::ranges::random_access_range) { + trackStateCandidates.reserve(std::distance(slBegin, slEnd)); + } + + // Calibrate all the source links on the surface since the selection has + // to be done based on calibrated measurement + for (auto it = slBegin; it != slEnd; ++it) { + // get the source link + const auto sourceLink = *it; + + // prepare the track state + PM mask = PM::Predicted | PM::Jacobian | PM::Calibrated; + if (it != slBegin) { + // not the first TrackState, only need uncalibrated and calibrated + mask = PM::Calibrated; + } + + ACTS_VERBOSE("Create temp track state with mask: " << mask); + // CAREFUL! This trackstate has a previous index that is not in this + // MultiTrajectory Visiting brackwards from this track state will + // fail! + auto ts = bufferTrajectory.makeTrackState(mask, prevTip); + + if (it == slBegin) { + // only set these for first + ts.predicted() = boundParams.parameters(); + if (boundParams.covariance()) { + ts.predictedCovariance() = *boundParams.covariance(); + } + ts.jacobian() = jacobian; + } else { + // subsequent track states can reuse + auto& first = trackStateCandidates.front(); + ts.shareFrom(first, PM::Predicted); + ts.shareFrom(first, PM::Jacobian); + } + + ts.pathLength() = pathLength; + ts.setReferenceSurface(boundParams.referenceSurface().getSharedPtr()); + + // now calibrate the track state + calibrator(gctx, calibrationContext, sourceLink, ts); + + trackStateCandidates.push_back(ts); + } + + bool isOutlier = false; + Result::iterator, + typename std::vector::iterator>> + selectorResult = + measurementSelector(trackStateCandidates, isOutlier, logger); + if (!selectorResult.ok()) { + ACTS_ERROR("Selection of calibrated measurements failed: " + << selectorResult.error().message()); + resultTrackStateList = + ResultTrackStateList::failure(selectorResult.error()); + } else { + auto selectedTrackStateRange = *selectorResult; + resultTrackStateList = processSelectedTrackStates( + selectedTrackStateRange.first, selectedTrackStateRange.second, + trajectory, isOutlier, logger); + } + + return resultTrackStateList; + } + + /// Create track states for the given trajectory from candidate track states + /// + /// @param begin begin iterator of the list of candidate track states + /// @param end end iterator of the list of candidate track states + /// @param trackStates the trajectory to which the new track states are added + /// @param isOutlier true if the candidate(s) is(are) an outlier(s). + /// @param logger the logger for messages + Result> processSelectedTrackStates( + typename std::vector::const_iterator begin, + typename std::vector::const_iterator end, + TrackStateContainerBackend& trackStates, bool isOutlier, + const Logger& logger) const { + using PM = TrackStatePropMask; + + using ResultTrackStateList = + Acts::Result>; + ResultTrackStateList resultTrackStateList{ + CkfTypes::BranchVector()}; + CkfTypes::BranchVector& trackStateList = + *resultTrackStateList; + trackStateList.reserve(end - begin); + + std::optional firstTrackState{std::nullopt}; + for (auto it = begin; it != end; ++it) { + auto& candidateTrackState = *it; + + PM mask = PM::Predicted | PM::Filtered | PM::Jacobian | PM::Calibrated; + if (it != begin) { + // subsequent track states don't need storage for these as they will + // be shared + mask &= ~PM::Predicted & ~PM::Jacobian; + } + if (isOutlier) { + // outlier won't have separate filtered parameters + mask &= ~PM::Filtered; + } + + // copy this trackstate into fitted states MultiTrajectory + auto trackState = + trackStates.makeTrackState(mask, candidateTrackState.previous()); + ACTS_VERBOSE("Create SourceLink output track state #" + << trackState.index() << " with mask: " << mask); + + if (it != begin) { + // assign indices pointing to first track state + trackState.shareFrom(*firstTrackState, PM::Predicted); + trackState.shareFrom(*firstTrackState, PM::Jacobian); + } else { + firstTrackState = trackState; + } + + // either copy ALL or everything except for predicted and jacobian + trackState.allocateCalibrated(candidateTrackState.calibratedSize()); + trackState.copyFrom(candidateTrackState, mask, false); + + auto typeFlags = trackState.typeFlags(); + typeFlags.set(TrackStateFlag::ParameterFlag); + typeFlags.set(TrackStateFlag::MeasurementFlag); + if (trackState.referenceSurface().surfaceMaterial() != nullptr) { + typeFlags.set(TrackStateFlag::MaterialFlag); + } + if (isOutlier) { + // propagate information that this is an outlier state + ACTS_VERBOSE( + "Creating outlier track state with tip = " << trackState.index()); + typeFlags.set(TrackStateFlag::OutlierFlag); + } + + trackStateList.push_back(trackState.index()); + } + return resultTrackStateList; + } + + /// Default measurement selector which will return all measurements + /// @param candidates Measurement track state candidates + static Result::iterator, + typename std::vector::iterator>> + voidMeasurementSelector(typename std::vector& candidates, + bool& /*isOutlier*/, const Logger& /*logger*/) { + return std::pair{candidates.begin(), candidates.end()}; + }; +}; + +/// @brief Derived class of the ComposableTrackStateCreator which simply delegates its tasks +/// +/// A derived class of the ComposableTrackStateCreator which simply delegates +/// the track state creation and measurement selection to a dedicated +/// implementation. +template +struct TrackStateCreatorDelegate + : ComposableTrackStateCreator< + TrackStateCreatorDelegate, + source_link_iterator_t, track_container_t> { + using TrackStateContainerBackend = + typename track_container_t::TrackStateContainerBackend; + using TrackProxy = typename track_container_t::TrackProxy; + using TrackStateProxy = typename track_container_t::TrackStateProxy; + using BoundState = std::tuple; + + /// Delegate definition to create track states for selected measurements + /// + /// @note expected to iterator over the given sourceLink range, + /// select measurements, and create track states for + /// which new tips are to be created, more over the outlier + /// flag should be set for states that are outlier. + /// + /// @param geoContext The current geometry context + /// @param calibrationContext pointer to the current calibration context + /// @param surface the surface at which new track states are to be created + /// @param boundState the current bound state of the trajectory + /// @param slBegin Begin iterator for sourceLinks + /// @param slEnd End iterator for sourceLinks + /// @param prevTip Index pointing at previous trajectory state (i.e. tip) + /// @param bufferTrajectory a temporary trajectory which can be used to create temporary track states + /// @param trackStateCandidates a temporary buffer that can be used to collect track states + /// @param trajectory the trajectory to which the new states are to be added + /// @param logger a logger for messages + using TrackStateCandidateCreator = + Delegate>( + const GeometryContext& geoContext, + const CalibrationContext& calibrationContext, const Surface& surface, + const BoundState& boundState, source_link_iterator_t slBegin, + source_link_iterator_t slEnd, TrackIndexType prevTip, + TrackStateContainerBackend& bufferTrajectory, + std::vector& trackStateCandidates, + TrackStateContainerBackend& trajectory, const Logger& logger)>; + + TrackStateCandidateCreator trackStateCreator; + + Result> createSourceLinkTrackStates( + const GeometryContext& gctx, const CalibrationContext& calibrationContext, + [[maybe_unused]] const Surface& surface, const BoundState& boundState, + source_link_iterator_t slBegin, source_link_iterator_t slEnd, + TrackIndexType prevTip, TrackStateContainerBackend& bufferTrajectory, + std::vector& trackStateCandidates, + TrackStateContainerBackend& trajectory, const Logger& logger) const { + return trackStateCreator(gctx, calibrationContext, surface, boundState, + slBegin, slEnd, prevTip, bufferTrajectory, + trackStateCandidates, trajectory, logger); + } +}; + +} // namespace Acts diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp index c56e9508c9b..399a4a966e8 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp @@ -52,8 +52,7 @@ class TrackFindingAlgorithm final : public IAlgorithm { /// Track finder function that takes input measurements, initial trackstate /// and track finder options and returns some track-finder-specific result. using TrackFinderOptions = - Acts::CombinatorialKalmanFilterOptions; + Acts::CombinatorialKalmanFilterOptions; using TrackFinderResult = Acts::Result>; diff --git a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp index b1ef900c5b9..204a31b765d 100644 --- a/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp +++ b/Examples/Algorithms/TrackFinding/src/TrackFindingAlgorithm.cpp @@ -27,6 +27,7 @@ #include "Acts/Surfaces/PerigeeSurface.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" +#include "Acts/TrackFinding/ComposableTrackStateCreator.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" #include "Acts/Utilities/Enumerate.hpp" #include "Acts/Utilities/Logger.hpp" @@ -318,27 +319,34 @@ ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { PassThroughCalibrator pcalibrator; MeasurementCalibratorAdapter calibrator(pcalibrator, measurements); Acts::GainMatrixUpdater kfUpdater; - MeasurementSelector measSel{ - Acts::MeasurementSelector(m_cfg.measurementSelectorCfg)}; using Extensions = Acts::CombinatorialKalmanFilterExtensions; BranchStopper branchStopper(m_cfg); + MeasurementSelector measSel{ + Acts::MeasurementSelector(m_cfg.measurementSelectorCfg)}; + + IndexSourceLinkAccessor slAccessor; + slAccessor.container = &measurements.orderedIndices(); + + using TrackStateCandidateCreatorType = + Acts::TrackStateCandidateCreatorImpl; + TrackStateCandidateCreatorType trackStateCreator; + trackStateCreator.sourceLinkAccessor + .template connect<&IndexSourceLinkAccessor::range>(&slAccessor); + trackStateCreator.calibrator + .template connect<&MeasurementCalibratorAdapter::calibrate>(&calibrator); + trackStateCreator.measurementSelector + .template connect<&MeasurementSelector::select>(&measSel); Extensions extensions; - extensions.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>( - &calibrator); extensions.updater.connect<&Acts::GainMatrixUpdater::operator()< typename TrackContainer::TrackStateContainerBackend>>(&kfUpdater); - extensions.measurementSelector.connect<&MeasurementSelector::select>( - &measSel); extensions.branchStopper.connect<&BranchStopper::operator()>(&branchStopper); - - IndexSourceLinkAccessor slAccessor; - slAccessor.container = &measurements.orderedIndices(); - Acts::SourceLinkAccessorDelegate - slAccessorDelegate; - slAccessorDelegate.connect<&IndexSourceLinkAccessor::range>(&slAccessor); + extensions.extendOrBranchTrajectory.template connect< + &TrackStateCandidateCreatorType ::extendTrajectoryOntoSurface>( + &trackStateCreator); Acts::PropagatorPlainOptions firstPropOptions(ctx.geoContext, ctx.magFieldContext); @@ -357,13 +365,14 @@ ProcessCode TrackFindingAlgorithm::execute(const AlgorithmContext& ctx) const { // Set the CombinatorialKalmanFilter options TrackFinderOptions firstOptions(ctx.geoContext, ctx.magFieldContext, - ctx.calibContext, slAccessorDelegate, - extensions, firstPropOptions); + ctx.calibContext, extensions, + firstPropOptions); + firstOptions.targetSurface = m_cfg.reverseSearch ? pSurface.get() : nullptr; TrackFinderOptions secondOptions(ctx.geoContext, ctx.magFieldContext, - ctx.calibContext, slAccessorDelegate, - extensions, secondPropOptions); + ctx.calibContext, extensions, + secondPropOptions); secondOptions.targetSurface = m_cfg.reverseSearch ? nullptr : pSurface.get(); secondOptions.skipPrePropagationUpdate = true; diff --git a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp index 6cb9559691c..8344b9aa12f 100644 --- a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp +++ b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp @@ -38,6 +38,7 @@ #include "Acts/Tests/CommonHelpers/LineSurfaceStub.hpp" #include "Acts/Tests/CommonHelpers/MeasurementsCreator.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" +#include "Acts/TrackFinding/ComposableTrackStateCreator.hpp" #include "Acts/TrackFinding/MeasurementSelector.hpp" #include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" @@ -168,8 +169,7 @@ struct Fixture { std::unordered_multimap; using TestSourceLinkAccessor = TestContainerAccessor; using CombinatorialKalmanFilterOptions = - Acts::CombinatorialKalmanFilterOptions; + Acts::CombinatorialKalmanFilterOptions; KalmanUpdater kfUpdater; KalmanSmoother kfSmoother; @@ -201,13 +201,8 @@ struct Fixture { Acts::CombinatorialKalmanFilterExtensions getExtensions() const { Acts::CombinatorialKalmanFilterExtensions extensions; - extensions.calibrator.template connect< - &testSourceLinkCalibrator>(); extensions.updater.template connect< &KalmanUpdater::operator()>(&kfUpdater); - extensions.measurementSelector.template connect< - &Acts::MeasurementSelector::select>( - &measSel); return extensions; } @@ -289,14 +284,39 @@ struct Fixture { CombinatorialKalmanFilterOptions makeCkfOptions() const { // leave the accessor empty, this will have to be set before running the CKF return CombinatorialKalmanFilterOptions( - geoCtx, magCtx, calCtx, - Acts::SourceLinkAccessorDelegate{}, - getExtensions(), Acts::PropagatorPlainOptions(geoCtx, magCtx)); + geoCtx, magCtx, calCtx, getExtensions(), + Acts::PropagatorPlainOptions(geoCtx, magCtx)); } }; +// set up composable track state creator from these components: +// - source link accessor, +// - measurement selector +// - track state candidate creator +template +inline auto makeTrackStateCreator(const source_link_accessor_t& slAccessor, + const Acts::MeasurementSelector& measSel) { + using TrackStateCreatorType = Acts::TrackStateCandidateCreatorImpl< + typename source_link_accessor_t::Iterator, TrackContainer>; + TrackStateCreatorType trackStateCreator; + trackStateCreator.sourceLinkAccessor + .template connect<&source_link_accessor_t::range>(&slAccessor); + trackStateCreator.calibrator.template connect< + &testSourceLinkCalibrator>(); + trackStateCreator.measurementSelector.template connect< + &Acts::MeasurementSelector::select>(&measSel); + return trackStateCreator; +} } // namespace +// somehow this is not automatically instantiated +template Acts::Result<::std::pair< + std::vector::iterator, + std::vector::iterator>> +Acts::MeasurementSelector::select( + std::vector&, bool&, + const Acts::Logger&) const; + BOOST_AUTO_TEST_SUITE(TrackFindingCombinatorialKalmanFilter) BOOST_AUTO_TEST_CASE(ZeroFieldForward) { @@ -312,8 +332,12 @@ BOOST_AUTO_TEST_CASE(ZeroFieldForward) { Fixture::TestSourceLinkAccessor slAccessor; slAccessor.container = &f.sourceLinks; - options.sourceLinkAccessor.connect<&Fixture::TestSourceLinkAccessor::range>( - &slAccessor); + + auto trackStateCreator = makeTrackStateCreator(slAccessor, f.measSel); + + options.extensions.extendOrBranchTrajectory.template connect< + &decltype(trackStateCreator)::extendTrajectoryOntoSurface>( + &trackStateCreator); TrackContainer tc{Acts::VectorTrackContainer{}, Acts::VectorMultiTrajectory{}}; @@ -370,8 +394,11 @@ BOOST_AUTO_TEST_CASE(ZeroFieldBackward) { Fixture::TestSourceLinkAccessor slAccessor; slAccessor.container = &f.sourceLinks; - options.sourceLinkAccessor.connect<&Fixture::TestSourceLinkAccessor::range>( - &slAccessor); + + auto trackStateCreator = makeTrackStateCreator(slAccessor, f.measSel); + options.extensions.extendOrBranchTrajectory.template connect< + &decltype(trackStateCreator)::extendTrajectoryOntoSurface>( + &trackStateCreator); TrackContainer tc{Acts::VectorTrackContainer{}, Acts::VectorMultiTrajectory{}};