diff --git a/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp b/Core/include/Acts/TrackFinding/CombinatorialKalmanFilter.hpp index 35acbc49feb..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,22 +431,8 @@ 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 (isHole || isMaterialOnly) { + if (isMaterialOnly) { stepper.transportCovarianceToCurvilinear(state.stepping); } else { stepper.transportCovarianceToBound(state.stepping, *surface); @@ -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{}};