Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-13 08:37:10

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