From 8dbd41c6cd3715e19a65264f7bea1e3c529b89a9 Mon Sep 17 00:00:00 2001 From: Goetz Gaycken Date: Thu, 31 Oct 2024 19:24:51 +0100 Subject: [PATCH] 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) can still be used, however require to setup an additional helper object which combines these independent components. The algorithmic code is unchanged. --- .../CombinatorialKalmanFilter.hpp | 405 ++---------------- .../CombinatorialKalmanFilterExtensions.hpp | 99 +++++ .../Acts/TrackFinding/TrackStateCreator.hpp | 294 +++++++++++++ .../TrackFinding/TrackFindingAlgorithm.hpp | 3 +- .../src/TrackFindingAlgorithm.cpp | 41 +- .../TrackFindingFromPrototrackAlgorithm.cpp | 36 +- .../CombinatorialKalmanFilterTests.cpp | 56 ++- docs/_extensions/lazy_autodoc.py | 1 + 8 files changed, 511 insertions(+), 424 deletions(-) create mode 100644 Core/include/Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp create mode 100644 Core/include/Acts/TrackFinding/TrackStateCreator.hpp diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index d991f2532c0..c498855e05d 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/TrackStateCreator.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; @@ -247,7 +124,7 @@ struct CombinatorialKalmanFilterResult { /// Indices into `tracks` which mark active branches std::vector collectedTracks; - /// Track state candidates buffer + /// Track state candidates buffer which can be used by the track state creator std::vector trackStateCandidates; /// Indicator if track finding has been done @@ -309,190 +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 backwards 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.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; @@ -735,20 +435,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 && expectMeasurements && !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); @@ -772,20 +458,20 @@ 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.createTrackStates( state.geoContext, *calibrationContextPtr, *surface, boundState, - slBegin, slEnd, prevTip, *result.trackStates, - result.trackStateCandidates, *result.trackStates, logger()); + prevTip, result.trackStateCandidates, *result.trackStates, + logger()); if (!tsRes.ok()) { - ACTS_ERROR( - "Processing of selected track states failed: " << tsRes.error()); + ACTS_ERROR("Track state creation failed on surface " + << surface->geometryId() << ": " << tsRes.error()); return tsRes.error(); } } @@ -1101,23 +787,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; @@ -1149,7 +818,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 /// @@ -1165,21 +833,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 @@ -1202,22 +866,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..f503d0b5c4a --- /dev/null +++ b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilterExtensions.hpp @@ -0,0 +1,99 @@ +// 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 { + +namespace CkfTypes { + +/// 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; +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 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 TrackStateCreator = + Delegate>( + const GeometryContext& geoContext, + const CalibrationContext& calibrationContext, const Surface& surface, + const CkfTypes::BoundState& boundState, TrackIndexType prevTip, + 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 TrackStateCreator + /// which makes uses of @ref MeasurementSelector and SourceLinkAccessor + TrackStateCreator createTrackStates; + + 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/TrackStateCreator.hpp b/Core/include/Acts/TrackFinding/TrackStateCreator.hpp new file mode 100644 index 00000000000..cecaaf3b719 --- /dev/null +++ b/Core/include/Acts/TrackFinding/TrackStateCreator.hpp @@ -0,0 +1,294 @@ +// 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/TrackFinding/CombinatorialKalmanFilterExtensions.hpp" +#include "Acts/TrackFitting/KalmanFitter.hpp" + +namespace Acts { + +/// @brief Create track states for selected measurements associated to a surface. +/// +/// - First get a source link range covering relevant measurements associated to +/// the given surface. This task is delegated to a SourceLinkAccessor. +/// - Then 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. +/// All track states, the temporary track states and track states for selected +/// measurements, are created in the given trajectory. The resulting container +/// may become big. Thus, it is advisable to copy selected tracks and their +/// track states to a separate container after each track finding step. +/// +template +struct TrackStateCreator { + 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; + using candidate_container_t = + typename std::vector; + + // delegate definition to get source link ranges for a surface + using SourceLinkAccessor = + Delegate( + const Surface&)>; + + // delegate to get calibrted measurements from a source link iterator + using Calibrator = + typename KalmanFitterExtensions::Calibrator; + + // delegate to select measurements from a track state range + using MeasurementSelector = + Delegate>( + candidate_container_t& trackStates, bool&, const Logger&)>; + + /// The source link accessor will return an source link range for a surface + /// which link to the associated measurements. + SourceLinkAccessor sourceLinkAccessor; + + /// 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{}}; + + public: + /// @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 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> createTrackStates( + const GeometryContext& gctx, const CalibrationContext& calibrationContext, + [[maybe_unused]] const Surface& surface, const BoundState& boundState, + TrackIndexType prevTip, + 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 = createSourceLinkTrackStates( + gctx, calibrationContext, surface, boundState, slBegin, slEnd, + prevTip, trackStateCandidates, trajectory, logger); + } + return tsRes; + } + + /// 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 trackStateCandidates a temporary buffer which can be used to + /// to keep track of newly created temporary track states. + /// @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, + 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); + // Temporary and final track states are created in the same + // trajectory, which could lead to very large containers. + + // CAREFUL! This trackstate has a previous index that is not in this + // MultiTrajectory Visiting backwards from this track state will + // fail! + auto ts = trajectory.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.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()}; + }; +}; + +} // namespace Acts diff --git a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp index 804d554515d..cafaa8e524e 100644 --- a/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp +++ b/Examples/Algorithms/TrackFinding/include/ActsExamples/TrackFinding/TrackFindingAlgorithm.hpp @@ -53,8 +53,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 53b47a86d07..29a748b0cda 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/TrackStateCreator.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 TrackStateCreatorType = + Acts::TrackStateCreator; + TrackStateCreatorType 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.createTrackStates + .template connect<&TrackStateCreatorType ::createTrackStates>( + &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/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp index bf28b1743ba..2dab3e1a360 100644 --- a/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp +++ b/Examples/Algorithms/TrackFindingExaTrkX/src/TrackFindingFromPrototrackAlgorithm.cpp @@ -9,6 +9,7 @@ #include "ActsExamples/TrackFindingExaTrkX/TrackFindingFromPrototrackAlgorithm.hpp" #include "Acts/EventData/ProxyAccessor.hpp" +#include "Acts/TrackFinding/TrackStateCreator.hpp" #include "ActsExamples/EventData/IndexSourceLink.hpp" #include "ActsExamples/EventData/MeasurementCalibration.hpp" @@ -86,28 +87,35 @@ ActsExamples::ProcessCode TrackFindingFromPrototrackAlgorithm::execute( Acts::GainMatrixSmoother kfSmoother; Acts::MeasurementSelector measSel{m_cfg.measurementSelectorCfg}; - Acts::CombinatorialKalmanFilterExtensions extensions; - extensions.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>( - &calibrator); - extensions.updater.connect<&Acts::GainMatrixUpdater::operator()< - typename TrackContainer::TrackStateContainerBackend>>(&kfUpdater); - extensions.measurementSelector.connect<&Acts::MeasurementSelector::select< - typename TrackContainer::TrackStateContainerBackend>>(&measSel); - // The source link accessor ProtoTrackSourceLinkAccessor sourceLinkAccessor; sourceLinkAccessor.loggerPtr = logger().clone("SourceLinkAccessor"); sourceLinkAccessor.container = &measurements.orderedIndices(); - Acts::SourceLinkAccessorDelegate - slAccessorDelegate; - slAccessorDelegate.connect<&ProtoTrackSourceLinkAccessor::range>( - &sourceLinkAccessor); + using TrackStateCreatorType = + Acts::TrackStateCreator; + TrackStateCreatorType trackStateCreator; + trackStateCreator.sourceLinkAccessor + .template connect<&ProtoTrackSourceLinkAccessor::range>( + &sourceLinkAccessor); + trackStateCreator.calibrator + .connect<&MeasurementCalibratorAdapter::calibrate>(&calibrator); + trackStateCreator.measurementSelector + .connect<&Acts::MeasurementSelector::select< + typename TrackContainer::TrackStateContainerBackend>>(&measSel); + + Acts::CombinatorialKalmanFilterExtensions extensions; + extensions.updater.connect<&Acts::GainMatrixUpdater::operator()< + typename TrackContainer::TrackStateContainerBackend>>(&kfUpdater); + extensions.createTrackStates + .template connect<&TrackStateCreatorType ::createTrackStates>( + &trackStateCreator); // Set the CombinatorialKalmanFilter options TrackFindingAlgorithm::TrackFinderOptions options( - ctx.geoContext, ctx.magFieldContext, ctx.calibContext, slAccessorDelegate, - extensions, pOptions, &(*pSurface)); + ctx.geoContext, ctx.magFieldContext, ctx.calibContext, extensions, + pOptions, &(*pSurface)); // Perform the track finding for all initial parameters ACTS_DEBUG("Invoke track finding with " << initialParameters.size() diff --git a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp index 6cb9559691c..e989359062c 100644 --- a/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp +++ b/Tests/UnitTests/Core/TrackFinding/CombinatorialKalmanFilterTests.cpp @@ -39,6 +39,7 @@ #include "Acts/Tests/CommonHelpers/MeasurementsCreator.hpp" #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" #include "Acts/TrackFinding/MeasurementSelector.hpp" +#include "Acts/TrackFinding/TrackStateCreator.hpp" #include "Acts/TrackFitting/GainMatrixSmoother.hpp" #include "Acts/TrackFitting/GainMatrixUpdater.hpp" #include "Acts/TrackFitting/KalmanFitter.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,40 @@ 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::TrackStateCreator; + 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 +333,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.createTrackStates + .template connect<&decltype(trackStateCreator)::createTrackStates>( + &trackStateCreator); TrackContainer tc{Acts::VectorTrackContainer{}, Acts::VectorMultiTrajectory{}}; @@ -370,8 +395,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.createTrackStates + .template connect<&decltype(trackStateCreator)::createTrackStates>( + &trackStateCreator); TrackContainer tc{Acts::VectorTrackContainer{}, Acts::VectorMultiTrajectory{}}; diff --git a/docs/_extensions/lazy_autodoc.py b/docs/_extensions/lazy_autodoc.py index 3c9513c4268..f7fcc96c2ae 100644 --- a/docs/_extensions/lazy_autodoc.py +++ b/docs/_extensions/lazy_autodoc.py @@ -76,6 +76,7 @@ def run() -> None: "Acts::Geant4PhysicalVolumeSelectors::NameSelector", "Acts::Geant4PhysicalVolumeSelectors::PositionSelector", "Acts::OrientedSurface", + "Acts::TrackStateCreator", } role_instances["class"] |= {