Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-05 08:12:54

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/Units.hpp"
0013 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0014 #include "Acts/EventData/VectorTrackContainer.hpp"
0015 #include "Acts/EventData/detail/TestSourceLink.hpp"
0016 #include "Acts/Geometry/CuboidVolumeBuilder.hpp"
0017 #include "Acts/Geometry/TrackingGeometry.hpp"
0018 #include "Acts/Geometry/TrackingGeometryBuilder.hpp"
0019 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
0020 #include "Acts/Material/MaterialSlab.hpp"
0021 #include "Acts/Propagator/EigenStepper.hpp"
0022 #include "Acts/Propagator/Navigator.hpp"
0023 #include "Acts/Propagator/Propagator.hpp"
0024 #include "Acts/Propagator/StraightLineStepper.hpp"
0025 #include "Acts/Surfaces/RectangleBounds.hpp"
0026 #include "Acts/Tests/CommonHelpers/DetectorElementStub.hpp"
0027 #include "Acts/Tests/CommonHelpers/MeasurementsCreator.hpp"
0028 #include "Acts/Tests/CommonHelpers/PredefinedMaterials.hpp"
0029 #include "Acts/TrackFitting/GlobalChiSquareFitter.hpp"
0030 #include "Acts/Utilities/Logger.hpp"
0031 #include "Acts/Visualization/EventDataView3D.hpp"
0032 #include "Acts/Visualization/GeometryView3D.hpp"
0033 #include "Acts/Visualization/ObjVisualization3D.hpp"
0034 
0035 #include <numbers>
0036 #include <vector>
0037 
0038 #include "FitterTestsCommon.hpp"
0039 
0040 using namespace Acts::UnitLiterals;
0041 using namespace Acts::detail::Test;
0042 
0043 Acts::Logging::Level logLevel = Acts::Logging::VERBOSE;
0044 const auto gx2fLogger = Acts::getDefaultLogger("Gx2f", logLevel);
0045 
0046 namespace Acts::Test {
0047 
0048 /// @brief Helper function to visualise measurements in a 3D environment.
0049 ///
0050 /// This function iterates through the provided measurements and visualises each
0051 /// one using the specified 3D visualisation helper. The visualisation takes
0052 /// into account the surface transformations and localisation errors.
0053 ///
0054 /// @param helper The 3D visualisation helper used to draw the measurements.
0055 /// @param measurements A collection of measurements to be visualised, containing source links with parameters and covariance information.
0056 /// @param geometry A shared pointer to the constant tracking geometry used to find surfaces associated with measurements.
0057 /// @param geoCtx The geometry context used for transformations and accessing geometry-related information.
0058 /// @param locErrorScale Scaling factor for localisation errors. Default value is 1.0.
0059 /// @param viewConfig Configuration settings for the visualisation view. Default value is s_viewMeasurement.
0060 static void drawMeasurements(
0061     IVisualization3D& helper, const Measurements& measurements,
0062     const std::shared_ptr<const TrackingGeometry>& geometry,
0063     const Acts::GeometryContext& geoCtx, double locErrorScale = 1.,
0064     const ViewConfig& viewConfig = s_viewMeasurement) {
0065   std::cout << "\n*** Draw measurements ***\n" << std::endl;
0066 
0067   for (auto& singleMeasurement : measurements.sourceLinks) {
0068     auto cov = singleMeasurement.covariance;
0069     auto lposition = singleMeasurement.parameters;
0070 
0071     auto surf = geometry->findSurface(singleMeasurement.m_geometryId);
0072     auto transf = surf->transform(geoCtx);
0073 
0074     EventDataView3D::drawMeasurement(helper, lposition, cov, transf,
0075                                      locErrorScale, viewConfig);
0076   }
0077 }
0078 
0079 //// Construct initial track parameters.
0080 Acts::BoundTrackParameters makeParameters(
0081     const double x = 0.0_m, const double y = 0.0_m, const double z = 0.0_m,
0082     const double w = 42_ns, const double phi = 0_degree,
0083     const double theta = 90_degree, const double p = 2_GeV,
0084     const double q = 1_e) {
0085   // create covariance matrix from reasonable standard deviations
0086   Acts::BoundVector stddev;
0087   stddev[Acts::eBoundLoc0] = 100_um;
0088   stddev[Acts::eBoundLoc1] = 100_um;
0089   stddev[Acts::eBoundTime] = 25_ns;
0090   stddev[Acts::eBoundPhi] = 2_degree;
0091   stddev[Acts::eBoundTheta] = 2_degree;
0092   stddev[Acts::eBoundQOverP] = 1 / 100_GeV;
0093   const Acts::BoundSquareMatrix cov = stddev.cwiseProduct(stddev).asDiagonal();
0094   // define a track in the transverse plane along x
0095   const Acts::Vector4 mPos4(x, y, z, w);
0096   return Acts::BoundTrackParameters::createCurvilinear(
0097       mPos4, phi, theta, q / p, cov, Acts::ParticleHypothesis::pion());
0098 }
0099 
0100 static std::vector<Acts::SourceLink> prepareSourceLinks(
0101     const std::vector<TestSourceLink>& sourceLinks) {
0102   std::vector<Acts::SourceLink> result;
0103   std::transform(sourceLinks.begin(), sourceLinks.end(),
0104                  std::back_inserter(result),
0105                  [](const auto& sl) { return Acts::SourceLink{sl}; });
0106   return result;
0107 }
0108 
0109 /// @brief Create a simple telescope detector
0110 ///
0111 /// @param geoCtx
0112 /// @param nSurfaces Number of surfaces
0113 /// @param surfaceIndexWithMaterial A set of index of the material surfaces
0114 std::shared_ptr<const TrackingGeometry> makeToyDetector(
0115     const Acts::GeometryContext& geoCtx, const std::size_t nSurfaces = 5,
0116     const std::set<std::size_t>& surfaceIndexWithMaterial = {}) {
0117   if (nSurfaces < 1) {
0118     throw std::invalid_argument("At least 1 surfaces needs to be created.");
0119   }
0120 
0121   // Define the dimensions of the square surfaces
0122   const double halfSizeSurface = 1_m;
0123 
0124   // Rotation of the surfaces around the y-axis
0125   const double rotationAngle = std::numbers::pi / 2.;
0126   const Vector3 xPos(cos(rotationAngle), 0., sin(rotationAngle));
0127   const Vector3 yPos(0., 1., 0.);
0128   const Vector3 zPos(-sin(rotationAngle), 0., cos(rotationAngle));
0129 
0130   // Construct builder
0131   CuboidVolumeBuilder cvb;
0132 
0133   // Create configurations for surfaces
0134   std::vector<CuboidVolumeBuilder::SurfaceConfig> surfaceConfig;
0135   for (std::size_t surfPos = 1; surfPos <= nSurfaces; surfPos++) {
0136     // Position of the surfaces
0137     CuboidVolumeBuilder::SurfaceConfig cfg;
0138     cfg.position = {surfPos * UnitConstants::m, 0., 0.};
0139 
0140     // Rotation of the surfaces
0141     cfg.rotation.col(0) = xPos;
0142     cfg.rotation.col(1) = yPos;
0143     cfg.rotation.col(2) = zPos;
0144 
0145     // Boundaries of the surfaces (shape)
0146     cfg.rBounds = std::make_shared<const RectangleBounds>(
0147         RectangleBounds(halfSizeSurface, halfSizeSurface));
0148 
0149     // Add material only for selected surfaces
0150     if (surfaceIndexWithMaterial.contains(surfPos)) {
0151       // Material of the surfaces
0152       MaterialSlab matProp(makeSilicon(), 5_mm);
0153       cfg.surMat = std::make_shared<HomogeneousSurfaceMaterial>(matProp);
0154     }
0155 
0156     // Thickness of the detector element
0157     cfg.thickness = 1_um;
0158 
0159     cfg.detElementConstructor =
0160         [](const Transform3& trans,
0161            const std::shared_ptr<const RectangleBounds>& bounds,
0162            double thickness) {
0163           return new DetectorElementStub(trans, bounds, thickness);
0164         };
0165     surfaceConfig.push_back(cfg);
0166   }
0167 
0168   // Build layer configurations
0169   std::vector<CuboidVolumeBuilder::LayerConfig> layerConfig;
0170   for (auto& sCfg : surfaceConfig) {
0171     CuboidVolumeBuilder::LayerConfig cfg;
0172     cfg.surfaceCfg = {sCfg};
0173     cfg.active = true;
0174     cfg.envelopeX = {-0.1_mm, 0.1_mm};
0175     cfg.envelopeY = {-0.1_mm, 0.1_mm};
0176     cfg.envelopeZ = {-0.1_mm, 0.1_mm};
0177     layerConfig.push_back(cfg);
0178   }
0179 
0180   // Inner Volume - Build volume configuration
0181   CuboidVolumeBuilder::VolumeConfig volumeConfig;
0182   volumeConfig.length = {(nSurfaces + 1) * 1_m, 2 * halfSizeSurface,
0183                          2 * halfSizeSurface};
0184   volumeConfig.position = {volumeConfig.length.x() / 2, 0., 0.};
0185   volumeConfig.layerCfg = layerConfig;
0186   volumeConfig.name = "TestVolume";
0187 
0188   // Outer volume - Build TrackingGeometry configuration
0189   CuboidVolumeBuilder::Config config;
0190   config.length = {(nSurfaces + 1) * 1_m, 2 * halfSizeSurface,
0191                    2 * halfSizeSurface};
0192   config.position = {volumeConfig.length.x() / 2, 0., 0.};
0193   config.volumeCfg = {volumeConfig};
0194 
0195   cvb.setConfig(config);
0196 
0197   TrackingGeometryBuilder::Config tgbCfg;
0198 
0199   tgbCfg.trackingVolumeBuilders.push_back(
0200       [=](const auto& context, const auto& inner, const auto&) {
0201         return cvb.trackingVolume(context, inner, nullptr);
0202       });
0203 
0204   TrackingGeometryBuilder tgb(tgbCfg);
0205 
0206   std::unique_ptr<const TrackingGeometry> detector =
0207       tgb.trackingGeometry(geoCtx);
0208   return detector;
0209 }
0210 
0211 struct Detector {
0212   // geometry
0213   std::shared_ptr<const TrackingGeometry> geometry;
0214 };
0215 
0216 BOOST_AUTO_TEST_SUITE(Gx2fTest)
0217 ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("Gx2fTests", logLevel))
0218 
0219 // Context objects
0220 const Acts::GeometryContext geoCtx;
0221 const Acts::MagneticFieldContext magCtx;
0222 const Acts::CalibrationContext calCtx;
0223 
0224 // Measurement resolutions
0225 const MeasurementResolution resPixel = {MeasurementType::eLoc01,
0226                                         {25_um, 50_um}};
0227 const MeasurementResolution resStrip0 = {MeasurementType::eLoc0, {25_um}};
0228 const MeasurementResolution resStrip1 = {MeasurementType::eLoc1, {50_um}};
0229 const MeasurementResolutionMap resMapAllPixel = {
0230     {Acts::GeometryIdentifier().withVolume(0), resPixel}};
0231 
0232 // This test checks if the call to the fitter works and no errors occur in the
0233 // framework, without fitting and updating any parameters
0234 BOOST_AUTO_TEST_CASE(NoFit) {
0235   ACTS_INFO("*** Test: NoFit -- Start");
0236 
0237   std::default_random_engine rng(42);
0238 
0239   ACTS_DEBUG("Create the detector");
0240   const std::size_t nSurfaces = 5;
0241   Detector detector;
0242   detector.geometry = makeToyDetector(geoCtx, nSurfaces);
0243 
0244   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0245   const auto parametersMeasurements = makeParameters();
0246   const auto startParametersFit = makeParameters(
0247       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0248 
0249   ACTS_DEBUG("Create the measurements");
0250   using SimPropagator =
0251       Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0252   const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
0253   const auto measurements =
0254       createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
0255                          resMapAllPixel, rng);
0256   const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
0257   ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());
0258 
0259   ACTS_DEBUG("Set up the fitter");
0260   // Reuse the SimPropagator, since we will not actually use it
0261   using Gx2Fitter =
0262       Experimental::Gx2Fitter<SimPropagator, VectorMultiTrajectory>;
0263   const Gx2Fitter fitter(simPropagator, gx2fLogger->clone());
0264 
0265   const Surface* rSurface = &parametersMeasurements.referenceSurface();
0266 
0267   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
0268   extensions.calibrator
0269       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
0270   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
0271   extensions.surfaceAccessor
0272       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
0273 
0274   Experimental::Gx2FitterOptions gx2fOptions(
0275       geoCtx, magCtx, calCtx, extensions,
0276       PropagatorPlainOptions(geoCtx, magCtx), rSurface, false, false,
0277       FreeToBoundCorrection(false), 0, 0);
0278 
0279   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0280                               Acts::VectorMultiTrajectory{}};
0281 
0282   ACTS_DEBUG("Fit the track");
0283   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
0284   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
0285   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
0286                               startParametersFit, gx2fOptions, tracks);
0287 
0288   BOOST_REQUIRE(res.ok());
0289 
0290   const auto& track = *res;
0291   BOOST_CHECK_EQUAL(track.tipIndex(), Acts::MultiTrajectoryTraits::kInvalid);
0292   BOOST_CHECK(track.hasReferenceSurface());
0293 
0294   // Track quantities
0295   BOOST_CHECK_EQUAL(track.chi2(), 0.);
0296   BOOST_CHECK_EQUAL(track.nDoF(), 0u);
0297   BOOST_CHECK_EQUAL(track.nHoles(), 0u);
0298   BOOST_CHECK_EQUAL(track.nMeasurements(), 0u);
0299   BOOST_CHECK_EQUAL(track.nSharedHits(), 0u);
0300   BOOST_CHECK_EQUAL(track.nOutliers(), 0u);
0301 
0302   // Parameters
0303   BOOST_CHECK_EQUAL(track.parameters(), startParametersFit.parameters());
0304   BOOST_CHECK_EQUAL(track.covariance(), BoundMatrix::Identity());
0305 
0306   // Convergence
0307   BOOST_CHECK_EQUAL(
0308       (track.template component<
0309           std::uint32_t,
0310           hashString(Experimental::Gx2fConstants::gx2fnUpdateColumn)>()),
0311       0);
0312 
0313   ACTS_INFO("*** Test: NoFit -- Finish");
0314 }
0315 
0316 BOOST_AUTO_TEST_CASE(Fit5Iterations) {
0317   ACTS_INFO("*** Test: Fit5Iterations -- Start");
0318 
0319   std::default_random_engine rng(42);
0320 
0321   ACTS_DEBUG("Create the detector");
0322   const std::size_t nSurfaces = 5;
0323   Detector detector;
0324   detector.geometry = makeToyDetector(geoCtx, nSurfaces);
0325 
0326   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0327   const auto parametersMeasurements = makeParameters();
0328   const auto startParametersFit = makeParameters(
0329       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0330 
0331   ACTS_DEBUG("Create the measurements");
0332   using SimPropagator =
0333       Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0334   const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
0335   const auto measurements =
0336       createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
0337                          resMapAllPixel, rng);
0338   const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
0339   ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());
0340 
0341   BOOST_REQUIRE_EQUAL(sourceLinks.size(), nSurfaces);
0342 
0343   ACTS_DEBUG("Set up the fitter");
0344   const Surface* rSurface = &parametersMeasurements.referenceSurface();
0345 
0346   using RecoStepper = EigenStepper<>;
0347   const auto recoPropagator =
0348       makeConstantFieldPropagator<RecoStepper>(detector.geometry, 0_T);
0349 
0350   using RecoPropagator = decltype(recoPropagator);
0351   using Gx2Fitter =
0352       Experimental::Gx2Fitter<RecoPropagator, VectorMultiTrajectory>;
0353   const Gx2Fitter fitter(recoPropagator, gx2fLogger->clone());
0354 
0355   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
0356   extensions.calibrator
0357       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
0358   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
0359   extensions.surfaceAccessor
0360       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
0361 
0362   const Experimental::Gx2FitterOptions gx2fOptions(
0363       geoCtx, magCtx, calCtx, extensions,
0364       PropagatorPlainOptions(geoCtx, magCtx), rSurface, false, false,
0365       FreeToBoundCorrection(false), 5, 0);
0366 
0367   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0368                               Acts::VectorMultiTrajectory{}};
0369 
0370   ACTS_DEBUG("Fit the track");
0371   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
0372   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
0373   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
0374                               startParametersFit, gx2fOptions, tracks);
0375 
0376   BOOST_REQUIRE(res.ok());
0377 
0378   const auto& track = *res;
0379 
0380   BOOST_CHECK_EQUAL(track.tipIndex(), nSurfaces - 1);
0381   BOOST_CHECK(track.hasReferenceSurface());
0382 
0383   // Track quantities
0384   CHECK_CLOSE_ABS(track.chi2(), 8., 2.);
0385   BOOST_CHECK_EQUAL(track.nDoF(), nSurfaces * 2);
0386   BOOST_CHECK_EQUAL(track.nHoles(), 0u);
0387   BOOST_CHECK_EQUAL(track.nMeasurements(), nSurfaces);
0388   BOOST_CHECK_EQUAL(track.nSharedHits(), 0u);
0389   BOOST_CHECK_EQUAL(track.nOutliers(), 0u);
0390 
0391   // Parameters
0392   // We need quite coarse checks here, since on different builds
0393   // the created measurements differ in the randomness
0394   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -11., 7e0);
0395   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -15., 6e0);
0396   BOOST_CHECK_CLOSE(track.parameters()[eBoundPhi], 1e-5, 1e3);
0397   BOOST_CHECK_CLOSE(track.parameters()[eBoundTheta], std::numbers::pi / 2,
0398                     1e-3);
0399   BOOST_CHECK_EQUAL(track.parameters()[eBoundQOverP], 1);
0400   BOOST_CHECK_CLOSE(track.parameters()[eBoundTime],
0401                     startParametersFit.parameters()[eBoundTime], 1e-6);
0402   BOOST_CHECK_CLOSE(track.covariance().determinant(), 1e-27, 4e0);
0403 
0404   // Convergence
0405   BOOST_CHECK_EQUAL(
0406       (track.template component<
0407           std::uint32_t,
0408           hashString(Experimental::Gx2fConstants::gx2fnUpdateColumn)>()),
0409       4);
0410 
0411   ACTS_INFO("*** Test: Fit5Iterations -- Finish");
0412 }
0413 
0414 BOOST_AUTO_TEST_CASE(MixedDetector) {
0415   ACTS_INFO("*** Test: MixedDetector -- Start");
0416 
0417   std::default_random_engine rng(42);
0418 
0419   ACTS_DEBUG("Create the detector");
0420   const std::size_t nSurfaces = 7;
0421   Detector detector;
0422   detector.geometry = makeToyDetector(geoCtx, nSurfaces);
0423 
0424   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0425   const auto parametersMeasurements = makeParameters();
0426   const auto startParametersFit = makeParameters(
0427       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0428 
0429   ACTS_DEBUG("Create the measurements");
0430   const MeasurementResolutionMap resMap = {
0431       {Acts::GeometryIdentifier().withVolume(2).withLayer(2), resPixel},
0432       {Acts::GeometryIdentifier().withVolume(2).withLayer(4), resStrip0},
0433       {Acts::GeometryIdentifier().withVolume(2).withLayer(6), resStrip1},
0434       {Acts::GeometryIdentifier().withVolume(2).withLayer(8), resPixel},
0435       {Acts::GeometryIdentifier().withVolume(2).withLayer(10), resStrip0},
0436       {Acts::GeometryIdentifier().withVolume(2).withLayer(12), resStrip1},
0437       {Acts::GeometryIdentifier().withVolume(2).withLayer(14), resPixel},
0438   };
0439 
0440   using SimPropagator =
0441       Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0442   const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
0443   const auto measurements = createMeasurements(
0444       simPropagator, geoCtx, magCtx, parametersMeasurements, resMap, rng);
0445   const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
0446   ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());
0447 
0448   BOOST_REQUIRE_EQUAL(sourceLinks.size(), nSurfaces);
0449 
0450   ACTS_DEBUG("Set up the fitter");
0451   const Surface* rSurface = &parametersMeasurements.referenceSurface();
0452 
0453   using RecoStepper = EigenStepper<>;
0454   const auto recoPropagator =
0455       makeConstantFieldPropagator<RecoStepper>(detector.geometry, 0_T);
0456 
0457   using RecoPropagator = decltype(recoPropagator);
0458   using Gx2Fitter =
0459       Experimental::Gx2Fitter<RecoPropagator, VectorMultiTrajectory>;
0460   const Gx2Fitter fitter(recoPropagator, gx2fLogger->clone());
0461 
0462   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
0463   extensions.calibrator
0464       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
0465   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
0466   extensions.surfaceAccessor
0467       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
0468 
0469   const Experimental::Gx2FitterOptions gx2fOptions(
0470       geoCtx, magCtx, calCtx, extensions,
0471       PropagatorPlainOptions(geoCtx, magCtx), rSurface, false, false,
0472       FreeToBoundCorrection(false), 5, 0);
0473 
0474   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0475                               Acts::VectorMultiTrajectory{}};
0476 
0477   ACTS_DEBUG("Fit the track");
0478   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
0479   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
0480   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
0481                               startParametersFit, gx2fOptions, tracks);
0482 
0483   BOOST_REQUIRE(res.ok());
0484 
0485   const auto& track = *res;
0486   BOOST_CHECK_EQUAL(track.tipIndex(), nSurfaces - 1);
0487   BOOST_CHECK(track.hasReferenceSurface());
0488 
0489   // Track quantities
0490   CHECK_CLOSE_ABS(track.chi2(), 8.5, 4.);
0491   BOOST_CHECK_EQUAL(track.nDoF(), 10u);
0492   BOOST_CHECK_EQUAL(track.nHoles(), 0u);
0493   BOOST_CHECK_EQUAL(track.nMeasurements(), nSurfaces);
0494   BOOST_CHECK_EQUAL(track.nSharedHits(), 0u);
0495   BOOST_CHECK_EQUAL(track.nOutliers(), 0u);
0496 
0497   // Parameters
0498   // We need quite coarse checks here, since on different builds
0499   // the created measurements differ in the randomness
0500   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -11., 7e0);
0501   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -15., 6e0);
0502   BOOST_CHECK_CLOSE(track.parameters()[eBoundPhi], 1e-5, 1e3);
0503   BOOST_CHECK_CLOSE(track.parameters()[eBoundTheta], std::numbers::pi / 2,
0504                     1e-3);
0505   BOOST_CHECK_EQUAL(track.parameters()[eBoundQOverP], 1);
0506   BOOST_CHECK_CLOSE(track.parameters()[eBoundTime],
0507                     startParametersFit.parameters()[eBoundTime], 1e-6);
0508   BOOST_CHECK_CLOSE(track.covariance().determinant(), 2e-28, 1e0);
0509 
0510   // Convergence
0511   BOOST_CHECK_EQUAL(
0512       (track.template component<
0513           std::uint32_t,
0514           hashString(Experimental::Gx2fConstants::gx2fnUpdateColumn)>()),
0515       4);
0516 
0517   ACTS_INFO("*** Test: MixedDetector -- Finish");
0518 }
0519 
0520 // This test checks if we can fit QOverP, when a magnetic field is introduced
0521 BOOST_AUTO_TEST_CASE(FitWithBfield) {
0522   ACTS_INFO("*** Test: FitWithBfield -- Start");
0523 
0524   std::default_random_engine rng(42);
0525 
0526   ACTS_DEBUG("Create the detector");
0527   const std::size_t nSurfaces = 5;
0528   Detector detector;
0529   detector.geometry = makeToyDetector(geoCtx, nSurfaces);
0530 
0531   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0532   const auto parametersMeasurements = makeParameters();
0533   const auto startParametersFit = makeParameters(
0534       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0535 
0536   ACTS_DEBUG("Create the measurements");
0537   using SimStepper = EigenStepper<>;
0538   const auto simPropagator =
0539       makeConstantFieldPropagator<SimStepper>(detector.geometry, 0.3_T);
0540 
0541   const auto measurements =
0542       createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
0543                          resMapAllPixel, rng);
0544 
0545   const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
0546   ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());
0547 
0548   BOOST_REQUIRE_EQUAL(sourceLinks.size(), nSurfaces);
0549 
0550   ACTS_DEBUG("Set up the fitter");
0551   const Surface* rSurface = &parametersMeasurements.referenceSurface();
0552 
0553   // Reuse the SimPropagator, since it already uses the EigenStepper<>
0554   using SimPropagator = decltype(simPropagator);
0555   using Gx2Fitter =
0556       Experimental::Gx2Fitter<SimPropagator, VectorMultiTrajectory>;
0557   const Gx2Fitter fitter(simPropagator, gx2fLogger->clone());
0558 
0559   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
0560   extensions.calibrator
0561       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
0562   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
0563   extensions.surfaceAccessor
0564       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
0565 
0566   const Experimental::Gx2FitterOptions gx2fOptions(
0567       geoCtx, magCtx, calCtx, extensions,
0568       PropagatorPlainOptions(geoCtx, magCtx), rSurface, false, false,
0569       FreeToBoundCorrection(false), 5, 0);
0570 
0571   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0572                               Acts::VectorMultiTrajectory{}};
0573 
0574   ACTS_DEBUG("Fit the track");
0575   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
0576   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
0577   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
0578                               startParametersFit, gx2fOptions, tracks);
0579 
0580   BOOST_REQUIRE(res.ok());
0581 
0582   const auto& track = *res;
0583   BOOST_CHECK_EQUAL(track.tipIndex(), nSurfaces - 1);
0584   BOOST_CHECK(track.hasReferenceSurface());
0585 
0586   // Track quantities
0587   CHECK_CLOSE_ABS(track.chi2(), 7.5, 1.5);
0588   BOOST_CHECK_EQUAL(track.nDoF(), nSurfaces * 2);
0589   BOOST_CHECK_EQUAL(track.nHoles(), 0u);
0590   BOOST_CHECK_EQUAL(track.nMeasurements(), nSurfaces);
0591   BOOST_CHECK_EQUAL(track.nSharedHits(), 0u);
0592   BOOST_CHECK_EQUAL(track.nOutliers(), 0u);
0593 
0594   // Parameters
0595   // We need quite coarse checks here, since on different builds
0596   // the created measurements differ in the randomness
0597   // TODO investigate further the reference values for eBoundPhi and det(cov)
0598   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -11., 8e0);
0599   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -15., 6e0);
0600   BOOST_CHECK_CLOSE(track.parameters()[eBoundPhi], 1e-4, 1e3);
0601   BOOST_CHECK_CLOSE(track.parameters()[eBoundTheta], std::numbers::pi / 2,
0602                     1e-3);
0603   BOOST_CHECK_CLOSE(track.parameters()[eBoundQOverP], 0.5, 2e-1);
0604   BOOST_CHECK_CLOSE(track.parameters()[eBoundTime],
0605                     startParametersFit.parameters()[eBoundTime], 1e-6);
0606   BOOST_CHECK_CLOSE(track.covariance().determinant(), 8e-35, 4e0);
0607 
0608   // Convergence
0609   BOOST_CHECK_EQUAL(
0610       (track.template component<
0611           std::uint32_t,
0612           hashString(Experimental::Gx2fConstants::gx2fnUpdateColumn)>()),
0613       4);
0614 
0615   ACTS_INFO("*** Test: FitWithBfield -- Finish");
0616 }
0617 
0618 BOOST_AUTO_TEST_CASE(relChi2changeCutOff) {
0619   ACTS_INFO("*** Test: relChi2changeCutOff -- Start");
0620 
0621   std::default_random_engine rng(42);
0622 
0623   ACTS_DEBUG("Create the detector");
0624   const std::size_t nSurfaces = 5;
0625   Detector detector;
0626   detector.geometry = makeToyDetector(geoCtx, nSurfaces);
0627 
0628   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0629   const auto parametersMeasurements = makeParameters();
0630   const auto startParametersFit = makeParameters(
0631       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0632 
0633   ACTS_DEBUG("Create the measurements");
0634   // simulation propagator
0635   using SimPropagator =
0636       Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0637   const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
0638   const auto measurements =
0639       createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
0640                          resMapAllPixel, rng);
0641   const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
0642   ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());
0643 
0644   BOOST_REQUIRE_EQUAL(sourceLinks.size(), nSurfaces);
0645 
0646   ACTS_DEBUG("Set up the fitter");
0647   const Surface* rSurface = &parametersMeasurements.referenceSurface();
0648 
0649   using RecoStepper = EigenStepper<>;
0650   const auto recoPropagator =
0651       makeConstantFieldPropagator<RecoStepper>(detector.geometry, 0_T);
0652 
0653   using RecoPropagator = decltype(recoPropagator);
0654   using Gx2Fitter =
0655       Experimental::Gx2Fitter<RecoPropagator, VectorMultiTrajectory>;
0656   const Gx2Fitter fitter(recoPropagator, gx2fLogger->clone());
0657 
0658   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
0659   extensions.calibrator
0660       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
0661   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
0662   extensions.surfaceAccessor
0663       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
0664 
0665   const Experimental::Gx2FitterOptions gx2fOptions(
0666       geoCtx, magCtx, calCtx, extensions,
0667       PropagatorPlainOptions(geoCtx, magCtx), rSurface, false, false,
0668       FreeToBoundCorrection(false), 500, 1e-5);
0669 
0670   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0671                               Acts::VectorMultiTrajectory{}};
0672 
0673   ACTS_DEBUG("Fit the track");
0674   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
0675   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
0676   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
0677                               startParametersFit, gx2fOptions, tracks);
0678 
0679   BOOST_REQUIRE(res.ok());
0680 
0681   const auto& track = *res;
0682   BOOST_CHECK_EQUAL(track.tipIndex(), nSurfaces - 1);
0683   BOOST_CHECK(track.hasReferenceSurface());
0684 
0685   // Track quantities
0686   CHECK_CLOSE_ABS(track.chi2(), 8., 2.);
0687   BOOST_CHECK_EQUAL(track.nDoF(), nSurfaces * 2);
0688   BOOST_CHECK_EQUAL(track.nHoles(), 0u);
0689   BOOST_CHECK_EQUAL(track.nMeasurements(), nSurfaces);
0690   BOOST_CHECK_EQUAL(track.nSharedHits(), 0u);
0691   BOOST_CHECK_EQUAL(track.nOutliers(), 0u);
0692 
0693   // Parameters
0694   // We need quite coarse checks here, since on different builds
0695   // the created measurements differ in the randomness
0696   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -11., 7e0);
0697   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -15., 6e0);
0698   BOOST_CHECK_CLOSE(track.parameters()[eBoundPhi], 1e-5, 1e3);
0699   BOOST_CHECK_CLOSE(track.parameters()[eBoundTheta], std::numbers::pi / 2,
0700                     1e-3);
0701   BOOST_CHECK_EQUAL(track.parameters()[eBoundQOverP], 1);
0702   BOOST_CHECK_CLOSE(track.parameters()[eBoundTime],
0703                     startParametersFit.parameters()[eBoundTime], 1e-6);
0704   BOOST_CHECK_CLOSE(track.covariance().determinant(), 1e-27, 4e0);
0705 
0706   // Convergence
0707   BOOST_CHECK_LT(
0708       (track.template component<
0709           std::uint32_t,
0710           hashString(Experimental::Gx2fConstants::gx2fnUpdateColumn)>()),
0711       10);
0712 
0713   ACTS_INFO("*** Test: relChi2changeCutOff -- Finish");
0714 }
0715 
0716 BOOST_AUTO_TEST_CASE(DidNotConverge) {
0717   ACTS_INFO("*** Test: DidNotConverge -- Start");
0718 
0719   std::default_random_engine rng(42);
0720 
0721   ACTS_DEBUG("Create the detector");
0722   const std::size_t nSurfaces = 5;
0723   Detector detector;
0724   detector.geometry = makeToyDetector(geoCtx, nSurfaces);
0725 
0726   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0727   const auto parametersMeasurements = makeParameters();
0728   const auto startParametersFit = makeParameters(
0729       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0730 
0731   ACTS_DEBUG("Create the measurements");
0732   // simulation propagator
0733   using SimPropagator =
0734       Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0735   const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
0736   const auto measurements =
0737       createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
0738                          resMapAllPixel, rng);
0739   const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
0740   ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());
0741 
0742   BOOST_REQUIRE_EQUAL(sourceLinks.size(), nSurfaces);
0743 
0744   ACTS_DEBUG("Set up the fitter");
0745   const Surface* rSurface = &parametersMeasurements.referenceSurface();
0746 
0747   using RecoStepper = EigenStepper<>;
0748   const auto recoPropagator =
0749       makeConstantFieldPropagator<RecoStepper>(detector.geometry, 0_T);
0750 
0751   using RecoPropagator = decltype(recoPropagator);
0752   using Gx2Fitter =
0753       Experimental::Gx2Fitter<RecoPropagator, VectorMultiTrajectory>;
0754   const Gx2Fitter fitter(recoPropagator, gx2fLogger->clone());
0755 
0756   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
0757   extensions.calibrator
0758       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
0759   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
0760   extensions.surfaceAccessor
0761       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
0762 
0763   // The relChi2changeCutOff = 0 prevents to stop the fitter after convergence,
0764   // therefore all updates will be done (even if the result does not change).
0765   // Since we didn't break due to convergence, we reach nUpdatesMax and
0766   // therefore fail the fit.
0767   const Experimental::Gx2FitterOptions gx2fOptions(
0768       geoCtx, magCtx, calCtx, extensions,
0769       PropagatorPlainOptions(geoCtx, magCtx), rSurface, false, false,
0770       FreeToBoundCorrection(false), 6, 0);
0771 
0772   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0773                               Acts::VectorMultiTrajectory{}};
0774 
0775   ACTS_DEBUG("Fit the track");
0776   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
0777   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
0778   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
0779                               startParametersFit, gx2fOptions, tracks);
0780 
0781   BOOST_REQUIRE(!res.ok());
0782   BOOST_CHECK_EQUAL(
0783       res.error(),
0784       Acts::Experimental::GlobalChiSquareFitterError::DidNotConverge);
0785 
0786   ACTS_INFO("*** Test: DidNotConverge -- Finish");
0787 }
0788 
0789 BOOST_AUTO_TEST_CASE(NotEnoughMeasurements) {
0790   ACTS_INFO("*** Test: NotEnoughMeasurements -- Start");
0791 
0792   std::default_random_engine rng(42);
0793 
0794   ACTS_DEBUG("Create the detector");
0795   const std::size_t nSurfaces = 2;
0796   Detector detector;
0797   detector.geometry = makeToyDetector(geoCtx, nSurfaces);
0798 
0799   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0800   const auto parametersMeasurements = makeParameters();
0801   const auto startParametersFit = makeParameters(
0802       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0803 
0804   ACTS_DEBUG("Create the measurements");
0805   // simulation propagator
0806   using SimPropagator =
0807       Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0808   const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
0809   const auto measurements =
0810       createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
0811                          resMapAllPixel, rng);
0812   const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
0813   ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());
0814 
0815   BOOST_REQUIRE_EQUAL(sourceLinks.size(), nSurfaces);
0816 
0817   ACTS_DEBUG("Set up the fitter");
0818   const Surface* rSurface = &parametersMeasurements.referenceSurface();
0819 
0820   using RecoStepper = EigenStepper<>;
0821   const auto recoPropagator =
0822       makeConstantFieldPropagator<RecoStepper>(detector.geometry, 0_T);
0823 
0824   using RecoPropagator = decltype(recoPropagator);
0825   using Gx2Fitter =
0826       Experimental::Gx2Fitter<RecoPropagator, VectorMultiTrajectory>;
0827   const Gx2Fitter fitter(recoPropagator, gx2fLogger->clone());
0828 
0829   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
0830   extensions.calibrator
0831       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
0832   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
0833   extensions.surfaceAccessor
0834       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
0835 
0836   const Experimental::Gx2FitterOptions gx2fOptions(
0837       geoCtx, magCtx, calCtx, extensions,
0838       PropagatorPlainOptions(geoCtx, magCtx), rSurface, false, false,
0839       FreeToBoundCorrection(false), 6, 0);
0840 
0841   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0842                               Acts::VectorMultiTrajectory{}};
0843 
0844   ACTS_DEBUG("Fit the track");
0845   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
0846   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
0847   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
0848                               startParametersFit, gx2fOptions, tracks);
0849 
0850   BOOST_REQUIRE(!res.ok());
0851   BOOST_CHECK_EQUAL(
0852       res.error(),
0853       Acts::Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements);
0854 
0855   ACTS_INFO("*** Test: NotEnoughMeasurements -- Finish");
0856 }
0857 
0858 BOOST_AUTO_TEST_CASE(FindHoles) {
0859   ACTS_INFO("*** Test: FindHoles -- Start");
0860 
0861   std::default_random_engine rng(42);
0862 
0863   ACTS_DEBUG("Create the detector");
0864   //  const std::size_t nSurfaces = 7;
0865   const std::size_t nSurfaces = 8;
0866   Detector detector;
0867   detector.geometry = makeToyDetector(geoCtx, nSurfaces);
0868 
0869   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0870   const auto parametersMeasurements = makeParameters();
0871   const auto startParametersFit = makeParameters(
0872       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0873 
0874   ACTS_DEBUG("Create the measurements");
0875   using SimPropagator =
0876       Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0877   const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
0878   const auto measurements =
0879       createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
0880                          resMapAllPixel, rng);
0881   auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
0882   ACTS_VERBOSE("sourceLinks.size() [before] = " << sourceLinks.size());
0883 
0884   // We remove the first measurement in the list. This does not create a hole.
0885   sourceLinks.erase(std::next(sourceLinks.begin(), 0));
0886   ACTS_VERBOSE(
0887       "sourceLinks.size() [after first erase] = " << sourceLinks.size());
0888 
0889   // We remove the last measurement in the list. This does not create a hole.
0890   sourceLinks.pop_back();
0891   ACTS_VERBOSE("sourceLinks.size() [after pop] = " << sourceLinks.size());
0892 
0893   // We remove the second to last measurement in the list. This effectively
0894   // creates a hole on that surface.
0895   const std::size_t indexHole = sourceLinks.size() - 2;
0896   ACTS_VERBOSE("Remove measurement " << indexHole);
0897   sourceLinks.erase(std::next(sourceLinks.begin(), indexHole));
0898   ACTS_VERBOSE("sourceLinks.size() [after second-to-last erase]= "
0899                << sourceLinks.size());
0900 
0901   // We removed 3 measurements
0902   //  const std::size_t nMeasurements = nSurfaces - 2;
0903   const std::size_t nMeasurements = nSurfaces - 3;
0904   BOOST_REQUIRE_EQUAL(sourceLinks.size(), nMeasurements);
0905 
0906   ACTS_DEBUG("Set up the fitter");
0907   const Surface* rSurface = &parametersMeasurements.referenceSurface();
0908 
0909   using RecoStepper = EigenStepper<>;
0910   const auto recoPropagator =
0911       makeConstantFieldPropagator<RecoStepper>(detector.geometry, 0_T);
0912 
0913   using RecoPropagator = decltype(recoPropagator);
0914   using Gx2Fitter =
0915       Experimental::Gx2Fitter<RecoPropagator, VectorMultiTrajectory>;
0916   const Gx2Fitter fitter(recoPropagator, gx2fLogger->clone());
0917 
0918   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
0919   extensions.calibrator
0920       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
0921   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
0922   extensions.surfaceAccessor
0923       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
0924 
0925   const Experimental::Gx2FitterOptions gx2fOptions(
0926       geoCtx, magCtx, calCtx, extensions,
0927       PropagatorPlainOptions(geoCtx, magCtx), rSurface, false, false,
0928       FreeToBoundCorrection(false), 20, 1e-5);
0929 
0930   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
0931                               Acts::VectorMultiTrajectory{}};
0932 
0933   ACTS_DEBUG("Fit the track");
0934   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
0935   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
0936   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
0937                               startParametersFit, gx2fOptions, tracks);
0938 
0939   BOOST_REQUIRE(res.ok());
0940 
0941   const auto& track = *res;
0942 
0943   // -1, because the index starts at 0
0944   // -2, because the first and the last surface are not part of the track
0945   BOOST_CHECK_EQUAL(track.tipIndex(), nSurfaces - 1 - 2);
0946   BOOST_CHECK(track.hasReferenceSurface());
0947 
0948   // Track quantities
0949   CHECK_CLOSE_ABS(track.chi2(), 6.5, 2.);
0950   BOOST_CHECK_EQUAL(track.nDoF(), 10u);
0951   BOOST_CHECK_EQUAL(track.nHoles(), 1u);
0952   BOOST_CHECK_EQUAL(track.nMeasurements(), nMeasurements);
0953   BOOST_CHECK_EQUAL(track.nSharedHits(), 0u);
0954   BOOST_CHECK_EQUAL(track.nOutliers(), 0u);
0955 
0956   // Parameters
0957   // We need quite coarse checks here, since on different builds
0958   // the created measurements differ in the randomness
0959   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -11., 7e0);
0960   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -15., 6e0);
0961   BOOST_CHECK_CLOSE(track.parameters()[eBoundPhi], 1e-5, 1e3);
0962   BOOST_CHECK_CLOSE(track.parameters()[eBoundTheta], std::numbers::pi / 2,
0963                     1e-3);
0964   BOOST_CHECK_EQUAL(track.parameters()[eBoundQOverP], 1);
0965   BOOST_CHECK_CLOSE(track.parameters()[eBoundTime],
0966                     startParametersFit.parameters()[eBoundTime], 1e-6);
0967   BOOST_CHECK_CLOSE(track.covariance().determinant(), 4.7e-28, 2e0);
0968 
0969   ACTS_INFO("*** Test: FindHoles -- Finish");
0970 }
0971 
0972 BOOST_AUTO_TEST_CASE(Material) {
0973   ACTS_INFO("*** Test: Material -- Start");
0974 
0975   std::default_random_engine rng(42);
0976 
0977   ACTS_DEBUG("Create the detector");
0978   const std::size_t nSurfaces = 7;
0979   const std::set<std::size_t> surfaceIndexWithMaterial = {4};
0980   Detector detector;
0981   detector.geometry =
0982       makeToyDetector(geoCtx, nSurfaces, surfaceIndexWithMaterial);
0983 
0984   ACTS_DEBUG("Set the start parameters for measurement creation and fit");
0985   const auto parametersMeasurements = makeParameters();
0986   const auto startParametersFit = makeParameters(
0987       7_mm, 11_mm, 15_mm, 42_ns, 10_degree, 80_degree, 1_GeV, 1_e);
0988 
0989   ACTS_DEBUG("Create the measurements");
0990   using SimPropagator =
0991       Acts::Propagator<Acts::StraightLineStepper, Acts::Navigator>;
0992   const SimPropagator simPropagator = makeStraightPropagator(detector.geometry);
0993   auto measurements =
0994       createMeasurements(simPropagator, geoCtx, magCtx, parametersMeasurements,
0995                          resMapAllPixel, rng);
0996 
0997   const Acts::Vector2 scatterOffset = {100_mm, 100_mm};
0998   const std::size_t indexMaterialSurface = 3;
0999   for (std::size_t iMeas = indexMaterialSurface; iMeas < nSurfaces; iMeas++) {
1000     // This only works, because our detector is evenly spaced
1001     const std::size_t offsetFactor = iMeas - indexMaterialSurface;
1002 
1003     auto& sl = measurements.sourceLinks[iMeas];
1004     sl.parameters[0] += scatterOffset[0] * offsetFactor;
1005     sl.parameters[1] += scatterOffset[1] * offsetFactor;
1006   }
1007 
1008   const auto sourceLinks = prepareSourceLinks(measurements.sourceLinks);
1009   ACTS_VERBOSE("sourceLinks.size() = " << sourceLinks.size());
1010 
1011   BOOST_REQUIRE_EQUAL(sourceLinks.size(), nSurfaces);
1012 
1013   ACTS_DEBUG("Set up the fitter");
1014   const Surface* rSurface = &parametersMeasurements.referenceSurface();
1015 
1016   using RecoStepper = EigenStepper<>;
1017   const auto recoPropagator =
1018       makeConstantFieldPropagator<RecoStepper>(detector.geometry, 0_T);
1019 
1020   using RecoPropagator = decltype(recoPropagator);
1021   using Gx2Fitter =
1022       Experimental::Gx2Fitter<RecoPropagator, VectorMultiTrajectory>;
1023   const Gx2Fitter fitter(recoPropagator, gx2fLogger->clone());
1024 
1025   Experimental::Gx2FitterExtensions<VectorMultiTrajectory> extensions;
1026   extensions.calibrator
1027       .connect<&testSourceLinkCalibrator<VectorMultiTrajectory>>();
1028   TestSourceLink::SurfaceAccessor surfaceAccessor{*detector.geometry};
1029   extensions.surfaceAccessor
1030       .connect<&TestSourceLink::SurfaceAccessor::operator()>(&surfaceAccessor);
1031 
1032   const Experimental::Gx2FitterOptions gx2fOptions(
1033       geoCtx, magCtx, calCtx, extensions,
1034       PropagatorPlainOptions(geoCtx, magCtx), rSurface, true, false,
1035       FreeToBoundCorrection(false), 5, 0);
1036 
1037   Acts::TrackContainer tracks{Acts::VectorTrackContainer{},
1038                               Acts::VectorMultiTrajectory{}};
1039 
1040   ACTS_DEBUG("Fit the track");
1041   ACTS_VERBOSE("startParameter unsmeared:\n" << parametersMeasurements);
1042   ACTS_VERBOSE("startParameter fit:\n" << startParametersFit);
1043   const auto res = fitter.fit(sourceLinks.begin(), sourceLinks.end(),
1044                               startParametersFit, gx2fOptions, tracks);
1045 
1046   // Helper to visualise the detector
1047   {
1048     std::cout << "\n*** Create .obj of Detector ***\n" << std::endl;
1049     // Only need for obj
1050     ObjVisualization3D obj;
1051 
1052     bool triangulate = true;
1053     ViewConfig viewSensitive = {.color = {0, 180, 240}};
1054     viewSensitive.triangulate = triangulate;
1055     ViewConfig viewPassive = {.color = {240, 280, 0}};
1056     viewPassive.triangulate = triangulate;
1057     ViewConfig viewVolume = {.color = {220, 220, 0}};
1058     viewVolume.triangulate = triangulate;
1059     ViewConfig viewContainer = {.color = {220, 220, 0}};
1060     viewContainer.triangulate = triangulate;
1061     ViewConfig viewGrid = {.color = {220, 0, 0}};
1062     viewGrid.quarterSegments = 8;
1063     viewGrid.offset = 3.;
1064     viewGrid.triangulate = triangulate;
1065 
1066     std::string tag = "gx2f_toydet";
1067 
1068     const Acts::TrackingVolume& tgVolume =
1069         *(detector.geometry->highestTrackingVolume());
1070 
1071     GeometryView3D::drawTrackingVolume(obj, tgVolume, geoCtx, viewContainer,
1072                                        viewVolume, viewPassive, viewSensitive,
1073                                        viewGrid, true, tag);
1074   }
1075   // Helper to visualise the measurements
1076   {
1077     std::cout << "\n*** Create .obj of measurements ***\n" << std::endl;
1078     ObjVisualization3D obj;
1079 
1080     double localErrorScale = 10000000.;
1081     ViewConfig mcolor{.color = {255, 145, 48}};
1082     mcolor.offset = 2;
1083     //  mcolor.visible = true;
1084 
1085     drawMeasurements(obj, measurements, detector.geometry, geoCtx,
1086                      localErrorScale, mcolor);
1087 
1088     obj.write("meas");
1089   }
1090 
1091   BOOST_REQUIRE(res.ok());
1092 
1093   const auto& track = *res;
1094 
1095   BOOST_CHECK_EQUAL(track.tipIndex(), nSurfaces - 1);
1096   BOOST_CHECK(track.hasReferenceSurface());
1097 
1098   // TODO Add material handling to the gx2f, to pass the 6 commented tests
1099   // Track quantities
1100   //  CHECK_CLOSE_ABS(track.chi2(), 8., 2.);
1101   BOOST_CHECK_EQUAL(track.nDoF(), nSurfaces * 2);
1102   BOOST_CHECK_EQUAL(track.nHoles(), 0u);
1103   BOOST_CHECK_EQUAL(track.nMeasurements(), nSurfaces);
1104   BOOST_CHECK_EQUAL(track.nSharedHits(), 0u);
1105   BOOST_CHECK_EQUAL(track.nOutliers(), 0u);
1106 
1107   // Parameters
1108   // We need quite coarse checks here, since on different builds
1109   // the created measurements differ in the randomness
1110   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc0], -11., 26e0);
1111   BOOST_CHECK_CLOSE(track.parameters()[eBoundLoc1], -15., 15e0);
1112   BOOST_CHECK_CLOSE(track.parameters()[eBoundPhi], 1e-5, 1.1e3);
1113   BOOST_CHECK_CLOSE(track.parameters()[eBoundTheta], std::numbers::pi / 2,
1114                     2e-2);
1115   BOOST_CHECK_EQUAL(track.parameters()[eBoundQOverP], 1);
1116   BOOST_CHECK_CLOSE(track.parameters()[eBoundTime],
1117                     startParametersFit.parameters()[eBoundTime], 1e-6);
1118   BOOST_CHECK_CLOSE(track.covariance().determinant(), 3.5e-27, 1e1);
1119 
1120   // Convergence
1121   BOOST_CHECK_EQUAL(
1122       (track.template component<
1123           std::uint32_t,
1124           hashString(Experimental::Gx2fConstants::gx2fnUpdateColumn)>()),
1125       4);
1126 
1127   ACTS_INFO("*** Test: Material -- Finish");
1128 }
1129 BOOST_AUTO_TEST_SUITE_END()
1130 }  // namespace Acts::Test