Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:25:14

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