Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-20 08:23:07

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/TrackParametrization.hpp"
0013 #include "Acts/Definitions/Units.hpp"
0014 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0015 #include "Acts/EventData/detail/TestSourceLink.hpp"
0016 #include "Acts/Propagator/EigenStepper.hpp"
0017 #include "Acts/Propagator/Navigator.hpp"
0018 #include "Acts/Propagator/Propagator.hpp"
0019 #include "Acts/Propagator/StraightLineStepper.hpp"
0020 #include "Acts/TrackFitting/GainMatrixSmoother.hpp"
0021 #include "Acts/TrackFitting/GainMatrixUpdater.hpp"
0022 #include "Acts/TrackFitting/KalmanFitter.hpp"
0023 #include "Acts/Utilities/Logger.hpp"
0024 
0025 #include <functional>
0026 #include <memory>
0027 #include <optional>
0028 #include <random>
0029 #include <utility>
0030 
0031 #include "FitterTestsCommon.hpp"
0032 
0033 using namespace Acts;
0034 using namespace Acts::detail::Test;
0035 using namespace Acts::UnitLiterals;
0036 
0037 namespace ActsTests {
0038 
0039 using StraightPropagator =
0040     Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0041 using ConstantFieldStepper = Acts::EigenStepper<>;
0042 using ConstantFieldPropagator =
0043     Acts::Propagator<ConstantFieldStepper, Acts::Navigator>;
0044 
0045 using KalmanUpdater = Acts::GainMatrixUpdater;
0046 using KalmanSmoother = Acts::GainMatrixSmoother;
0047 using KalmanFitter =
0048     Acts::KalmanFitter<ConstantFieldPropagator, VectorMultiTrajectory>;
0049 
0050 static const auto pion = Acts::ParticleHypothesis::pion();
0051 
0052 KalmanUpdater kfUpdater;
0053 KalmanSmoother kfSmoother;
0054 
0055 // Construct initial track parameters.
0056 Acts::BoundTrackParameters makeParameters() {
0057   // create covariance matrix from reasonable standard deviations
0058   Acts::BoundVector stddev;
0059   stddev[Acts::eBoundLoc0] = 100_um;
0060   stddev[Acts::eBoundLoc1] = 100_um;
0061   stddev[Acts::eBoundTime] = 25_ns;
0062   stddev[Acts::eBoundPhi] = 2_degree;
0063   stddev[Acts::eBoundTheta] = 2_degree;
0064   stddev[Acts::eBoundQOverP] = 1 / 100_GeV;
0065   Acts::BoundMatrix cov = stddev.cwiseProduct(stddev).asDiagonal();
0066   // define a track in the transverse plane along x
0067   Acts::Vector4 mPos4(-3_m, 0., 0., 42_ns);
0068   return Acts::BoundTrackParameters::createCurvilinear(
0069       mPos4, 0_degree, 90_degree, 1_e / 1_GeV, cov, pion);
0070 }
0071 
0072 // Instantiate the tester
0073 const FitterTester tester;
0074 
0075 // reconstruction propagator and fitter
0076 auto kfLogger = getDefaultLogger("KalmanFilter", Logging::INFO);
0077 const auto kfZeroPropagator =
0078     makeConstantFieldPropagator<ConstantFieldStepper>(tester.geometry, 0_T);
0079 const auto kfZero = KalmanFitter(kfZeroPropagator, std::move(kfLogger));
0080 
0081 std::default_random_engine rng(42);
0082 
0083 auto makeDefaultKalmanFitterOptions() {
0084   KalmanFitterExtensions<VectorMultiTrajectory> extensions;
0085   extensions.calibrator
0086       .connect<&testSourceLinkCalibratorStrict<VectorMultiTrajectory>>();
0087   extensions.updater.connect<&KalmanUpdater::operator()<VectorMultiTrajectory>>(
0088       &kfUpdater);
0089   extensions.smoother
0090       .connect<&KalmanSmoother::operator()<VectorMultiTrajectory>>(&kfSmoother);
0091   extensions.surfaceAccessor.connect<
0092       &Acts::detail::Test::TestSourceLink::SurfaceAccessor::operator()>(
0093       &tester.surfaceAccessor);
0094 
0095   return KalmanFitterOptions(
0096       tester.geoCtx, tester.magCtx, tester.calCtx, extensions,
0097       PropagatorPlainOptions(tester.geoCtx, tester.magCtx));
0098 }
0099 
0100 /// Checks that the calibrator is never handed a track state whose "best
0101 /// available" parameters have not been computed yet, see
0102 /// https://github.com/acts-project/acts/issues/5777
0103 struct CalibratorContractChecker {
0104   mutable std::size_t nCalls = 0;
0105 
0106   template <typename trajectory_t>
0107   void operator()(const GeometryContext& gctx, const CalibrationContext& cctx,
0108                   const SourceLink& sourceLink,
0109                   typename trajectory_t::TrackStateProxy trackState) const {
0110     // filtering and smoothing have not happened yet, so the corresponding
0111     // components must not be allocated
0112     BOOST_CHECK(!trackState.hasFiltered());
0113     BOOST_CHECK(!trackState.hasSmoothed());
0114     // consequently the best available parameters are the predicted ones. we
0115     // compare the storage addresses to make sure no copy is involved
0116     BOOST_REQUIRE(trackState.hasPredicted());
0117     BOOST_CHECK_EQUAL(trackState.parameters().data(),
0118                       trackState.predicted().data());
0119     BOOST_CHECK_EQUAL(trackState.covariance().data(),
0120                       trackState.predictedCovariance().data());
0121     BOOST_CHECK(trackState.parameters().allFinite());
0122     BOOST_CHECK(trackState.covariance().allFinite());
0123 
0124     ++nCalls;
0125 
0126     testSourceLinkCalibrator<trajectory_t>(gctx, cctx, sourceLink, trackState);
0127   }
0128 };
0129 
0130 BOOST_AUTO_TEST_SUITE(TrackFittingSuite)
0131 
0132 // The calibrator must see valid parameters. Before
0133 // https://github.com/acts-project/acts/issues/5777 the filtered parameters
0134 // were allocated up front, so `parameters()` returned uninitialized memory.
0135 BOOST_AUTO_TEST_CASE(CalibratorSeesValidParameters) {
0136   auto start = makeParameters();
0137   auto kfOptions = makeDefaultKalmanFitterOptions();
0138 
0139   CalibratorContractChecker checker;
0140   kfOptions.extensions.calibrator
0141       .connect<&CalibratorContractChecker::operator()<VectorMultiTrajectory>>(
0142           &checker);
0143 
0144   bool expected_reversed = false;
0145   bool expected_smoothed = true;
0146   tester.test_ZeroFieldNoSurfaceForward(kfZero, kfOptions, start, rng,
0147                                         expected_reversed, expected_smoothed,
0148                                         true);
0149 
0150   BOOST_CHECK_GT(checker.nCalls, 0u);
0151 }
0152 
0153 BOOST_AUTO_TEST_CASE(ZeroFieldNoSurfaceForward) {
0154   auto start = makeParameters();
0155   auto kfOptions = makeDefaultKalmanFitterOptions();
0156 
0157   bool expected_reversed = false;
0158   bool expected_smoothed = true;
0159   tester.test_ZeroFieldNoSurfaceForward(kfZero, kfOptions, start, rng,
0160                                         expected_reversed, expected_smoothed,
0161                                         true);
0162 }
0163 
0164 BOOST_AUTO_TEST_CASE(ZeroFieldWithSurfaceForward) {
0165   auto start = makeParameters();
0166   auto kfOptions = makeDefaultKalmanFitterOptions();
0167 
0168   // regular smoothing
0169   kfOptions.reverseFiltering = false;
0170   bool expected_reversed = false;
0171   bool expected_smoothed = true;
0172   tester.test_ZeroFieldWithSurfaceForward(kfZero, kfOptions, start, rng,
0173                                           expected_reversed, expected_smoothed,
0174                                           true);
0175 
0176   // reverse filtering instead of smoothing
0177   kfOptions.reverseFiltering = true;
0178   kfOptions.reverseFilteringCovarianceScaling = 100.0;
0179   expected_reversed = true;
0180   expected_smoothed = false;
0181   tester.test_ZeroFieldWithSurfaceForward(kfZero, kfOptions, start, rng,
0182                                           expected_reversed, expected_smoothed,
0183                                           true);
0184 }
0185 
0186 BOOST_AUTO_TEST_CASE(ZeroFieldWithSurfaceBackward) {
0187   auto start = makeParameters();
0188   auto kfOptions = makeDefaultKalmanFitterOptions();
0189 
0190   // regular smoothing
0191   kfOptions.reverseFiltering = false;
0192   bool expected_reversed = false;
0193   bool expected_smoothed = true;
0194   tester.test_ZeroFieldWithSurfaceBackward(kfZero, kfOptions, start, rng,
0195                                            expected_reversed, expected_smoothed,
0196                                            true);
0197 
0198   // reverse filtering instead of smoothing
0199   kfOptions.reverseFiltering = true;
0200   kfOptions.reverseFilteringCovarianceScaling = 100.0;
0201   expected_reversed = true;
0202   expected_smoothed = false;
0203   tester.test_ZeroFieldWithSurfaceBackward(kfZero, kfOptions, start, rng,
0204                                            expected_reversed, expected_smoothed,
0205                                            true);
0206 }
0207 
0208 BOOST_AUTO_TEST_CASE(ZeroFieldWithSurfaceAtExit) {
0209   auto start = makeParameters();
0210   auto kfOptions = makeDefaultKalmanFitterOptions();
0211 
0212   bool expected_reversed = false;
0213   bool expected_smoothed = true;
0214   tester.test_ZeroFieldWithSurfaceAtExit(kfZero, kfOptions, start, rng,
0215                                          expected_reversed, expected_smoothed,
0216                                          true);
0217 }
0218 
0219 BOOST_AUTO_TEST_CASE(ZeroFieldShuffled) {
0220   auto start = makeParameters();
0221   auto kfOptions = makeDefaultKalmanFitterOptions();
0222 
0223   bool expected_reversed = false;
0224   bool expected_smoothed = true;
0225   tester.test_ZeroFieldShuffled(kfZero, kfOptions, start, rng,
0226                                 expected_reversed, expected_smoothed, true);
0227 }
0228 
0229 BOOST_AUTO_TEST_CASE(ZeroFieldWithHole) {
0230   auto start = makeParameters();
0231   auto kfOptions = makeDefaultKalmanFitterOptions();
0232 
0233   bool expected_reversed = false;
0234   bool expected_smoothed = true;
0235   tester.test_ZeroFieldWithHole(kfZero, kfOptions, start, rng,
0236                                 expected_reversed, expected_smoothed, true);
0237 }
0238 
0239 BOOST_AUTO_TEST_CASE(ZeroFieldWithOutliers) {
0240   auto start = makeParameters();
0241 
0242   // fitter options w/o target surface. outlier distance is set to be below the
0243   // default outlier distance in the `MeasurementsCreator`
0244   auto kfOptions = makeDefaultKalmanFitterOptions();
0245 
0246   TestOutlierFinder tof{5_mm};
0247   kfOptions.extensions.outlierFinder
0248       .connect<&TestOutlierFinder::operator()<VectorMultiTrajectory>>(&tof);
0249 
0250   bool expected_reversed = false;
0251   bool expected_smoothed = true;
0252   tester.test_ZeroFieldWithOutliers(kfZero, kfOptions, start, rng,
0253                                     expected_reversed, expected_smoothed, true);
0254 }
0255 
0256 BOOST_AUTO_TEST_CASE(ZeroFieldWithReverseFiltering) {
0257   auto start = makeParameters();
0258 
0259   auto test = [&](double threshold, bool reverse, bool expected_reversed,
0260                   bool expected_smoothed) {
0261     auto kfOptions = makeDefaultKalmanFitterOptions();
0262 
0263     TestReverseFilteringLogic trfl{threshold};
0264     kfOptions.extensions.reverseFilteringLogic
0265         .connect<&TestReverseFilteringLogic::operator()<VectorMultiTrajectory>>(
0266             &trfl);
0267 
0268     kfOptions.reverseFiltering = reverse;
0269     kfOptions.reverseFilteringCovarianceScaling = 100.0;
0270 
0271     tester.test_ZeroFieldWithReverseFiltering(kfZero, kfOptions, start, rng,
0272                                               expected_reversed,
0273                                               expected_smoothed, true);
0274   };
0275 
0276   // Track of 1 GeV with a threshold set at 0.1 GeV, reversed filtering should
0277   // not be used
0278   test(0.1_GeV, false, false, true);
0279 
0280   // Track of 1 GeV with a threshold set at 10 GeV, reversed filtering should
0281   // be used
0282   test(10._GeV, false, true, false);
0283 
0284   // Track of 1 GeV with a threshold set at 10 GeV, reversed filtering should
0285   // be used
0286   test(0.1_GeV, true, true, false);
0287 }
0288 
0289 // TODO this is not really Kalman fitter specific. is probably better tested
0290 // with a synthetic trajectory.
0291 BOOST_AUTO_TEST_CASE(GlobalCovariance) {
0292   auto start = makeParameters();
0293   auto kfOptions = makeDefaultKalmanFitterOptions();
0294 
0295   tester.test_GlobalCovariance(kfZero, kfOptions, start, rng);
0296 }
0297 
0298 BOOST_AUTO_TEST_SUITE_END()
0299 
0300 }  // namespace ActsTests