Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:12:55

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