diff --git a/Core/include/Acts/TrackFitting/GsfOptions.hpp b/Core/include/Acts/TrackFitting/GsfOptions.hpp index 511d298f08b..626969c69f9 100644 --- a/Core/include/Acts/TrackFitting/GsfOptions.hpp +++ b/Core/include/Acts/TrackFitting/GsfOptions.hpp @@ -117,6 +117,8 @@ struct GsfOptions { double weightCutoff = 1.e-4; + double transverseMomentumCut = 0.0; // GeV, no cut by default + bool abortOnError = false; bool disableAllMaterialHandling = false; diff --git a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp index 088777403a7..6c3b7506e26 100644 --- a/Core/include/Acts/TrackFitting/detail/GsfActor.hpp +++ b/Core/include/Acts/TrackFitting/detail/GsfActor.hpp @@ -97,6 +97,9 @@ struct GsfActor { /// When to discard components double weightCutoff = 1.0e-4; + /// Minimum transverse momentum (in GeV) for components + double transverseMomentumCut = 0.0; // GeV, no cut by default + /// When this option is enabled, material information on all surfaces is /// ignored. This disables the component convolution as well as the handling /// of energy. This may be useful for debugging. @@ -271,6 +274,13 @@ struct GsfActor { return; } + // Check if all components were removed by momentum cut + if (tmpStates.tips.empty()) { + ACTS_VERBOSE("All components removed by transverse momentum cut"); + stepper.clearComponents(state.stepping); + return; + } + updateStepper(state, stepper, tmpStates); } // We have material, we thus need a component cache since we will @@ -301,11 +311,9 @@ struct GsfActor { result); if (componentCache.empty()) { - ACTS_WARNING( - "No components left after applying energy loss. " - "Is the weight cutoff " - << m_cfg.weightCutoff << " too high?"); - ACTS_WARNING("Return to propagator without applying energy loss"); + ACTS_VERBOSE( + "All components removed by cuts (weight or momentum cutoff)"); + stepper.clearComponents(state.stepping); return; } @@ -328,7 +336,7 @@ struct GsfActor { template - bool checkAbort(propagator_state_t& /*state*/, const stepper_t& /*stepper*/, + bool checkAbort(propagator_state_t& state, const stepper_t& stepper, const navigator_t& /*navigator*/, const result_type& result, const Logger& /*logger*/) const { if (m_cfg.numberMeasurements && @@ -337,6 +345,13 @@ struct GsfActor { return true; } + // Stop if all components have been removed + if (stepper.numberComponents(state.stepping) == 0) { + ACTS_VERBOSE( + "Stop navigation because all components were removed by cuts"); + return true; + } + return false; } @@ -434,6 +449,14 @@ struct GsfActor { }(); assert(p_prev + delta_p > 0. && "new momentum must be > 0"); + + // Apply pT cut here to avoid const of expansion and merging later + const auto pT = (p_prev + delta_p) * std::sin(new_pars[eBoundTheta]); + if (pT < m_cfg.transverseMomentumCut) { + ACTS_VERBOSE("Skip new component with pT=" << pT << " GeV"); + continue; + } + new_pars[eBoundQOverP] = particleHypothesis.qOverP(p_prev + delta_p, old_bound.charge()); @@ -583,6 +606,14 @@ struct GsfActor { const auto& trackStateProxy = *trackStateProxyRes; + const auto ¶ms = trackStateProxy.parameters(); + const auto p = stepper.particleHypothesis(state.stepping).extractMomentum(params[eBoundQOverP]); + const auto pT = p * std::sin(params[eBoundTheta]); + if (pT < m_cfg.transverseMomentumCut) { + ACTS_VERBOSE("Skip component with pT=" << pT << " after Kalman update"); + continue; + } + // If at least one component is no outlier, we consider the whole thing // as a measurementState if (trackStateProxy.typeFlags().test(TrackStateFlag::MeasurementFlag)) { @@ -801,6 +832,7 @@ struct GsfActor { m_cfg.abortOnError = options.abortOnError; m_cfg.disableAllMaterialHandling = options.disableAllMaterialHandling; m_cfg.weightCutoff = options.weightCutoff; + m_cfg.transverseMomentumCut = options.transverseMomentumCut; m_cfg.mergeMethod = options.componentMergeMethod; m_cfg.calibrationContext = &options.calibrationContext.get(); } diff --git a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp index 593c933725c..4a612963a86 100644 --- a/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp +++ b/Examples/Algorithms/TrackFitting/include/ActsExamples/TrackFitting/TrackFitterFunction.hpp @@ -93,6 +93,8 @@ enum class MixtureReductionAlgorithm { weightCut, KLDistance }; /// @param betheHeitlerApprox The object that encapsulates the approximation. /// @param maxComponents number of maximum components in the track state /// @param weightCutoff when to drop components +/// @param transverseMomentumCut minimum transverse momentum (in GeV) for +/// components /// @param componentMergeMethod How to merge a mixture to a single set of /// parameters and covariance /// @param mixtureReductionAlgorithm How to reduce the number of components @@ -105,6 +107,7 @@ std::shared_ptr makeGsfFitterFunction( std::shared_ptr magneticField, const std::shared_ptr& betheHeitlerApprox, std::size_t maxComponents, double weightCutoff, + double transverseMomentumCut, Acts::ComponentMergeMethod componentMergeMethod, MixtureReductionAlgorithm mixtureReductionAlgorithm, double reverseFilteringCovarianceScaling, const Acts::Logger& logger); diff --git a/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp b/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp index 0504b2fb0a9..9bd7db494d6 100644 --- a/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp +++ b/Examples/Algorithms/TrackFitting/src/GsfFitterFunction.cpp @@ -60,7 +60,8 @@ struct GsfFitterFunctionImpl final : public ActsExamples::TrackFitterFunction { std::size_t maxComponents = 0; double weightCutoff = 0; - const double momentumCutoff = 0; // 500_MeV; + double transverseMomentumCut = 0.0; // GeV, no cut by default + const double momentumCutoff = 0; // 500_MeV; bool abortOnError = false; bool disableAllMaterialHandling = false; MixtureReductionAlgorithm reductionAlg = @@ -93,6 +94,7 @@ struct GsfFitterFunctionImpl final : public ActsExamples::TrackFitterFunction { gsfOptions.referenceSurface = options.referenceSurface; gsfOptions.maxComponents = maxComponents; gsfOptions.weightCutoff = weightCutoff; + gsfOptions.transverseMomentumCut = transverseMomentumCut; gsfOptions.abortOnError = abortOnError; gsfOptions.disableAllMaterialHandling = disableAllMaterialHandling; gsfOptions.componentMergeMethod = mergeMethod; @@ -177,6 +179,7 @@ std::shared_ptr ActsExamples::makeGsfFitterFunction( std::shared_ptr magneticField, const std::shared_ptr& betheHeitlerApprox, std::size_t maxComponents, double weightCutoff, + double transverseMomentumCut, Acts::ComponentMergeMethod componentMergeMethod, MixtureReductionAlgorithm mixtureReductionAlgorithm, double reverseFilteringCovarianceScaling, const Acts::Logger& logger) { @@ -210,6 +213,7 @@ std::shared_ptr ActsExamples::makeGsfFitterFunction( std::move(trackFitter), std::move(directTrackFitter), geo); fitterFunction->maxComponents = maxComponents; fitterFunction->weightCutoff = weightCutoff; + fitterFunction->transverseMomentumCut = transverseMomentumCut; fitterFunction->mergeMethod = componentMergeMethod; fitterFunction->reductionAlg = mixtureReductionAlgorithm; fitterFunction->reverseFilteringCovarianceScaling = diff --git a/Python/Examples/python/reconstruction.py b/Python/Examples/python/reconstruction.py index 6a892159ed9..35187210fba 100644 --- a/Python/Examples/python/reconstruction.py +++ b/Python/Examples/python/reconstruction.py @@ -1643,6 +1643,7 @@ def addTruthTrackingGsf( trackingGeometry: acts.TrackingGeometry, field: acts.MagneticFieldProvider, inputProtoTracks: str = "truth_particle_tracks", + transverseMomentumCut: float = 0.5, logLevel: Optional[acts.logging.Level] = None, ) -> None: customLogLevel = acts.examples.defaultLogging(s, logLevel) @@ -1658,6 +1659,7 @@ def addTruthTrackingGsf( "componentMergeMethod": acts.examples.ComponentMergeMethod.maxWeight, "mixtureReductionAlgorithm": acts.examples.MixtureReductionAlgorithm.KLDistance, "weightCutoff": 1.0e-4, + "transverseMomentumCut": transverseMomentumCut, "reverseFilteringCovarianceScaling": 100.0, "level": customLogLevel(), } diff --git a/Python/Examples/src/TrackFitting.cpp b/Python/Examples/src/TrackFitting.cpp index ed16560bae4..1c6de004342 100644 --- a/Python/Examples/src/TrackFitting.cpp +++ b/Python/Examples/src/TrackFitting.cpp @@ -112,20 +112,21 @@ void addTrackFitting(py::module& mex) { std::shared_ptr magneticField, const std::shared_ptr& betheHeitlerApprox, std::size_t maxComponents, double weightCutoff, + double transverseMomentumCut, ComponentMergeMethod componentMergeMethod, MixtureReductionAlgorithm mixtureReductionAlgorithm, double reverseFilteringCovarianceScaling, Logging::Level level) { return makeGsfFitterFunction( std::move(trackingGeometry), std::move(magneticField), betheHeitlerApprox, maxComponents, weightCutoff, - componentMergeMethod, mixtureReductionAlgorithm, - reverseFilteringCovarianceScaling, + transverseMomentumCut, componentMergeMethod, + mixtureReductionAlgorithm, reverseFilteringCovarianceScaling, *getDefaultLogger("GSFFunc", level)); }, "trackingGeometry"_a, "magneticField"_a, "betheHeitlerApprox"_a, - "maxComponents"_a, "weightCutoff"_a, "componentMergeMethod"_a, - "mixtureReductionAlgorithm"_a, "reverseFilteringCovarianceScaling"_a, - "level"_a); + "maxComponents"_a, "weightCutoff"_a, "transverseMomentumCut"_a, + "componentMergeMethod"_a, "mixtureReductionAlgorithm"_a, + "reverseFilteringCovarianceScaling"_a, "level"_a); mex.def( "makeGlobalChiSquareFitterFunction",