Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-05 07:46:32

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