Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:13:01

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/Direction.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/Definitions/Units.hpp"
0015 #include "Acts/EventData/Charge.hpp"
0016 #include "Acts/EventData/GenericBoundTrackParameters.hpp"
0017 #include "Acts/EventData/TrackParameters.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/Geometry/GeometryIdentifier.hpp"
0020 #include "Acts/MagneticField/ConstantBField.hpp"
0021 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0022 #include "Acts/Propagator/EigenStepper.hpp"
0023 #include "Acts/Propagator/Propagator.hpp"
0024 #include "Acts/Surfaces/PerigeeSurface.hpp"
0025 #include "Acts/Surfaces/Surface.hpp"
0026 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0027 #include "Acts/Utilities/AnnealingUtility.hpp"
0028 #include "Acts/Utilities/Logger.hpp"
0029 #include "Acts/Utilities/Result.hpp"
0030 #include "Acts/Vertexing/AMVFInfo.hpp"
0031 #include "Acts/Vertexing/AdaptiveMultiVertexFitter.hpp"
0032 #include "Acts/Vertexing/HelicalTrackLinearizer.hpp"
0033 #include "Acts/Vertexing/ImpactPointEstimator.hpp"
0034 #include "Acts/Vertexing/TrackAtVertex.hpp"
0035 #include "Acts/Vertexing/Vertex.hpp"
0036 #include "Acts/Vertexing/VertexingOptions.hpp"
0037 
0038 #include <algorithm>
0039 #include <array>
0040 #include <cmath>
0041 #include <iostream>
0042 #include <map>
0043 #include <memory>
0044 #include <numbers>
0045 #include <random>
0046 #include <tuple>
0047 #include <type_traits>
0048 #include <utility>
0049 #include <vector>
0050 
0051 using namespace Acts::UnitLiterals;
0052 
0053 namespace Acts::Test {
0054 
0055 using Acts::VectorHelpers::makeVector4;
0056 
0057 // Set up logger
0058 ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("AMVFitterTests", Acts::Logging::INFO))
0059 
0060 using Covariance = BoundSquareMatrix;
0061 using Propagator = Acts::Propagator<EigenStepper<>>;
0062 using Linearizer = HelicalTrackLinearizer;
0063 
0064 // Create a test context
0065 GeometryContext geoContext = GeometryContext();
0066 MagneticFieldContext magFieldContext = MagneticFieldContext();
0067 
0068 // Vertex x/y position distribution
0069 std::uniform_real_distribution<double> vXYDist(-0.1_mm, 0.1_mm);
0070 // Vertex z position distribution
0071 std::uniform_real_distribution<double> vZDist(-20_mm, 20_mm);
0072 // Track d0 distribution
0073 std::uniform_real_distribution<double> d0Dist(-0.01_mm, 0.01_mm);
0074 // Track z0 distribution
0075 std::uniform_real_distribution<double> z0Dist(-0.2_mm, 0.2_mm);
0076 // Track pT distribution
0077 std::uniform_real_distribution<double> pTDist(1._GeV, 30._GeV);
0078 // Track phi distribution
0079 std::uniform_real_distribution<double> phiDist(-std::numbers::pi,
0080                                                std::numbers::pi);
0081 // Track theta distribution
0082 std::uniform_real_distribution<double> thetaDist(1., std::numbers::pi - 1.);
0083 // Track charge helper distribution
0084 std::uniform_real_distribution<double> qDist(-1, 1);
0085 // Distribution of track time (relative to vertex time). Values are unrealistic
0086 // and only used for testing purposes.
0087 std::uniform_real_distribution<double> relTDist(-4_ps, 4_ps);
0088 // Track IP resolution distribution
0089 std::uniform_real_distribution<double> resIPDist(0., 100._um);
0090 // Track angular distribution
0091 std::uniform_real_distribution<double> resAngDist(0., 0.1);
0092 // Track q/p resolution distribution
0093 std::uniform_real_distribution<double> resQoPDist(-0.1, 0.1);
0094 // Track time resolution distribution. Values are unrealistic and only used for
0095 // testing purposes.
0096 std::uniform_real_distribution<double> resTDist(0_ps, 8_ps);
0097 
0098 /// @brief Unit test for AdaptiveMultiVertexFitter
0099 ///
0100 BOOST_AUTO_TEST_CASE(adaptive_multi_vertex_fitter_test) {
0101   // Set up RNG
0102   int mySeed = 31415;
0103   std::mt19937 gen(mySeed);
0104 
0105   // Set up constant B-Field
0106   auto bField = std::make_shared<ConstantBField>(Vector3{0.0, 0.0, 1_T});
0107 
0108   // Set up EigenStepper
0109   EigenStepper<> stepper(bField);
0110 
0111   // Set up propagator with void navigator
0112   auto propagator = std::make_shared<Propagator>(stepper);
0113 
0114   VertexingOptions vertexingOptions(geoContext, magFieldContext);
0115 
0116   // IP 3D Estimator
0117   ImpactPointEstimator::Config ip3dEstCfg(bField, propagator);
0118   ImpactPointEstimator ip3dEst(ip3dEstCfg);
0119 
0120   // Linearizer for BoundTrackParameters type test
0121   Linearizer::Config ltConfig;
0122   ltConfig.bField = bField;
0123   ltConfig.propagator = propagator;
0124   Linearizer linearizer(ltConfig);
0125 
0126   AdaptiveMultiVertexFitter::Config fitterCfg(ip3dEst);
0127   fitterCfg.trackLinearizer.connect<&Linearizer::linearizeTrack>(&linearizer);
0128 
0129   // Test smoothing
0130   fitterCfg.doSmoothing = true;
0131   fitterCfg.extractParameters.connect<&InputTrack::extractParameters>();
0132 
0133   AdaptiveMultiVertexFitter fitter(std::move(fitterCfg));
0134 
0135   // Create positions of three vertices, two of which (1 and 2) are
0136   // close to one another and will share a common track later
0137   Vector3 vtxPos1(-0.15_mm, -0.1_mm, -1.5_mm);
0138   Vector3 vtxPos2(-0.1_mm, -0.15_mm, -3._mm);
0139   Vector3 vtxPos3(0.2_mm, 0.2_mm, 10._mm);
0140 
0141   std::vector<Vector3> vtxPosVec{vtxPos1, vtxPos2, vtxPos3};
0142 
0143   // Resolutions, use the same for all tracks
0144   double resD0 = resIPDist(gen);
0145   double resZ0 = resIPDist(gen);
0146   double resPh = resAngDist(gen);
0147   double resTh = resAngDist(gen);
0148   double resQp = resQoPDist(gen);
0149 
0150   std::vector<Vertex> vtxList;
0151   for (auto& vtxPos : vtxPosVec) {
0152     Vertex vtx(vtxPos);
0153     // Set some vertex covariance
0154     SquareMatrix4 posCovariance(SquareMatrix4::Identity());
0155     vtx.setFullCovariance(posCovariance);
0156     // Add to vertex list
0157     vtxList.push_back(vtx);
0158   }
0159 
0160   std::vector<Vertex*> vtxPtrList;
0161   ACTS_DEBUG("All vertices in test case:");
0162   int cv = 0;
0163   for (auto& vtx : vtxList) {
0164     cv++;
0165     ACTS_DEBUG("\t" << cv << ". vertex ptr: " << &vtx);
0166     vtxPtrList.push_back(&vtx);
0167   }
0168 
0169   std::vector<BoundTrackParameters> allTracks;
0170 
0171   unsigned int nTracksPerVtx = 4;
0172   // Construct nTracksPerVtx * 3 (3 vertices) random track emerging
0173   // from vicinity of vertex positions
0174   for (unsigned int iTrack = 0; iTrack < nTracksPerVtx * vtxPosVec.size();
0175        iTrack++) {
0176     // Construct positive or negative charge randomly
0177     double q = qDist(gen) < 0 ? -1. : 1.;
0178 
0179     // Fill vector of track objects with simple covariance matrix
0180     Covariance covMat;
0181 
0182     covMat << resD0 * resD0, 0., 0., 0., 0., 0., 0., resZ0 * resZ0, 0., 0., 0.,
0183         0., 0., 0., resPh * resPh, 0., 0., 0., 0., 0., 0., resTh * resTh, 0.,
0184         0., 0., 0., 0., 0., resQp * resQp, 0., 0., 0., 0., 0., 0., 1.;
0185 
0186     // Index of current vertex
0187     int vtxIdx = static_cast<int>(iTrack / nTracksPerVtx);
0188 
0189     // Construct random track parameters
0190     BoundTrackParameters::ParametersVector paramVec;
0191     paramVec << d0Dist(gen), z0Dist(gen), phiDist(gen), thetaDist(gen),
0192         q / pTDist(gen), 0.;
0193 
0194     std::shared_ptr<PerigeeSurface> perigeeSurface =
0195         Surface::makeShared<PerigeeSurface>(vtxPosVec[vtxIdx]);
0196 
0197     allTracks.emplace_back(perigeeSurface, paramVec, std::move(covMat),
0198                            ParticleHypothesis::pion());
0199   }
0200 
0201   int ct = 0;
0202   ACTS_DEBUG("All tracks in test case:");
0203   for (auto& trk : allTracks) {
0204     ct++;
0205     ACTS_DEBUG("\t" << ct << ". track ptr: " << &trk);
0206   }
0207 
0208   AdaptiveMultiVertexFitter::State state(*bField, magFieldContext);
0209 
0210   for (unsigned int iTrack = 0; iTrack < nTracksPerVtx * vtxPosVec.size();
0211        iTrack++) {
0212     // Index of current vertex
0213     int vtxIdx = static_cast<int>(iTrack / nTracksPerVtx);
0214 
0215     InputTrack inputTrack{&allTracks[iTrack]};
0216 
0217     state.vtxInfoMap[&(vtxList[vtxIdx])].trackLinks.push_back(inputTrack);
0218     state.tracksAtVerticesMap.insert(
0219         std::make_pair(std::make_pair(inputTrack, &(vtxList[vtxIdx])),
0220                        TrackAtVertex(1., allTracks[iTrack], inputTrack)));
0221 
0222     // Use first track also for second vertex to let vtx1 and vtx2
0223     // share this track
0224     if (iTrack == 0) {
0225       state.vtxInfoMap[&(vtxList.at(1))].trackLinks.push_back(inputTrack);
0226       state.tracksAtVerticesMap.insert(
0227           std::make_pair(std::make_pair(inputTrack, &(vtxList.at(1))),
0228                          TrackAtVertex(1., allTracks[iTrack], inputTrack)));
0229     }
0230   }
0231 
0232   for (auto& vtx : vtxPtrList) {
0233     state.addVertexToMultiMap(*vtx);
0234     ACTS_DEBUG("Vertex, with ptr: " << vtx);
0235     for (auto& trk : state.vtxInfoMap[vtx].trackLinks) {
0236       ACTS_DEBUG("\t track ptr: " << trk);
0237     }
0238   }
0239 
0240   ACTS_DEBUG("Checking all vertices linked to a single track:");
0241   for (auto& trk : allTracks) {
0242     ACTS_DEBUG("Track with ptr: " << &trk);
0243     auto range = state.trackToVerticesMultiMap.equal_range(InputTrack{&trk});
0244     for (auto vtxIter = range.first; vtxIter != range.second; ++vtxIter) {
0245       ACTS_DEBUG("\t used by vertex: " << vtxIter->second);
0246     }
0247   }
0248 
0249   // Copy vertex seeds from state.vertexCollection to new
0250   // list in order to be able to compare later
0251   std::vector<Vertex> seedListCopy = vtxList;
0252 
0253   auto res1 = fitter.addVtxToFit(state, vtxList.at(0), vertexingOptions);
0254   ACTS_DEBUG("Tracks linked to each vertex AFTER fit:");
0255   int c = 0;
0256   for (auto& vtx : vtxPtrList) {
0257     c++;
0258     ACTS_DEBUG(c << ". vertex, with ptr: " << vtx);
0259     for (const auto& trk : state.vtxInfoMap[vtx].trackLinks) {
0260       ACTS_DEBUG("\t track ptr: " << trk);
0261     }
0262   }
0263 
0264   ACTS_DEBUG("Checking all vertices linked to a single track AFTER fit:");
0265   for (auto& trk : allTracks) {
0266     ACTS_DEBUG("Track with ptr: " << &trk);
0267     auto range = state.trackToVerticesMultiMap.equal_range(InputTrack{&trk});
0268     for (auto vtxIter = range.first; vtxIter != range.second; ++vtxIter) {
0269       ACTS_DEBUG("\t used by vertex: " << vtxIter->second);
0270     }
0271   }
0272 
0273   BOOST_CHECK(res1.ok());
0274 
0275   ACTS_DEBUG("Vertex positions after fit of vertex 1 and 2:");
0276   for (std::size_t vtxIter = 0; vtxIter < 3; vtxIter++) {
0277     ACTS_DEBUG("Vtx " << vtxIter + 1 << ", seed position:\n "
0278                       << seedListCopy.at(vtxIter).fullPosition()
0279                       << "\nFitted position:\n "
0280                       << vtxList.at(vtxIter).fullPosition());
0281   }
0282 
0283   // After fit of first vertex, only first and second vertex seed
0284   // should have been modified while third vertex should remain untouched
0285   BOOST_CHECK_NE(vtxList.at(0).fullPosition(),
0286                  seedListCopy.at(0).fullPosition());
0287   BOOST_CHECK_NE(vtxList.at(1).fullPosition(),
0288                  seedListCopy.at(1).fullPosition());
0289   BOOST_CHECK_EQUAL(vtxList.at(2).fullPosition(),
0290                     seedListCopy.at(2).fullPosition());
0291 
0292   CHECK_CLOSE_ABS(vtxList.at(0).fullPosition(),
0293                   seedListCopy.at(0).fullPosition(), 1_mm);
0294   CHECK_CLOSE_ABS(vtxList.at(1).fullPosition(),
0295                   seedListCopy.at(1).fullPosition(), 1_mm);
0296 
0297   auto res2 = fitter.addVtxToFit(state, vtxList.at(2), vertexingOptions);
0298   BOOST_CHECK(res2.ok());
0299 
0300   // Now also the third vertex should have been modified and fitted
0301   BOOST_CHECK_NE(vtxList.at(2).fullPosition(),
0302                  seedListCopy.at(2).fullPosition());
0303   CHECK_CLOSE_ABS(vtxList.at(2).fullPosition(),
0304                   seedListCopy.at(2).fullPosition(), 1_mm);
0305 
0306   ACTS_DEBUG("Vertex positions after fit of vertex 3:");
0307   ACTS_DEBUG("Vtx 1, seed position:\n " << seedListCopy.at(0).fullPosition()
0308                                         << "\nFitted position:\n "
0309                                         << vtxList.at(0).fullPosition());
0310   ACTS_DEBUG("Vtx 2, seed position:\n " << seedListCopy.at(1).fullPosition()
0311                                         << "\nFitted position:\n "
0312                                         << vtxList.at(1).fullPosition());
0313   ACTS_DEBUG("Vtx 3, seed position:\n " << seedListCopy.at(2).fullPosition()
0314                                         << "\nFitted position:\n "
0315                                         << vtxList.at(2).fullPosition());
0316 }
0317 
0318 /// @brief Unit test for fitting a 4D vertex position
0319 ///
0320 BOOST_AUTO_TEST_CASE(time_fitting) {
0321   // Set up RNG
0322   int mySeed = 31415;
0323   std::mt19937 gen(mySeed);
0324 
0325   // Set up constant B-Field
0326   auto bField = std::make_shared<ConstantBField>(Vector3{0.0, 0.0, 1_T});
0327 
0328   // Set up EigenStepper
0329   EigenStepper<> stepper(bField);
0330 
0331   // Set up propagator with void navigator
0332   auto propagator = std::make_shared<Propagator>(stepper);
0333 
0334   VertexingOptions vertexingOptions(geoContext, magFieldContext);
0335 
0336   ImpactPointEstimator::Config ip3dEstCfg(bField, propagator);
0337   ImpactPointEstimator ip3dEst(ip3dEstCfg);
0338 
0339   AdaptiveMultiVertexFitter::Config fitterCfg(ip3dEst);
0340 
0341   // Linearizer for BoundTrackParameters type test
0342   Linearizer::Config ltConfig;
0343   ltConfig.bField = bField;
0344   ltConfig.propagator = propagator;
0345   Linearizer linearizer(ltConfig);
0346 
0347   // Test smoothing
0348   fitterCfg.doSmoothing = true;
0349   // Do time fit
0350   fitterCfg.useTime = true;
0351   fitterCfg.extractParameters.connect<&InputTrack::extractParameters>();
0352   fitterCfg.trackLinearizer.connect<&Linearizer::linearizeTrack>(&linearizer);
0353 
0354   AdaptiveMultiVertexFitter fitter(std::move(fitterCfg));
0355 
0356   // Vertex position
0357   double trueVtxTime = 40.0_ps;
0358   Vector3 trueVtxPos(-0.15_mm, -0.1_mm, -1.5_mm);
0359 
0360   // Seed position of the vertex
0361   Vector4 vtxSeedPos(0.0_mm, 0.0_mm, -1.4_mm, 0.0_ps);
0362 
0363   Vertex vtx(vtxSeedPos);
0364   // Set initial covariance matrix to a large value
0365   SquareMatrix4 initialCovariance(SquareMatrix4::Identity() * 1e+8);
0366   vtx.setFullCovariance(initialCovariance);
0367 
0368   // Vector of all tracks
0369   std::vector<BoundTrackParameters> trks;
0370 
0371   unsigned int nTracks = 4;
0372   for (unsigned int _ = 0; _ < nTracks; _++) {
0373     // Construct positive or negative charge randomly
0374     double q = qDist(gen) < 0 ? -1. : 1.;
0375 
0376     // Track resolution
0377     double resD0 = resIPDist(gen);
0378     double resZ0 = resIPDist(gen);
0379     double resPh = resAngDist(gen);
0380     double resTh = resAngDist(gen);
0381     double resQp = resQoPDist(gen);
0382     double resT = resTDist(gen);
0383 
0384     // Random diagonal covariance matrix
0385     Covariance covMat;
0386 
0387     // clang-format off
0388     covMat <<
0389       resD0 * resD0, 0., 0., 0., 0., 0.,
0390       0., resZ0 * resZ0, 0., 0., 0., 0.,
0391       0., 0., resPh * resPh, 0., 0., 0.,
0392       0., 0., 0., resTh * resTh, 0., 0.,
0393       0., 0., 0., 0., resQp * resQp, 0.,
0394       0., 0., 0., 0., 0., resT * resT;
0395     // clang-format on
0396 
0397     // Random track parameters
0398     BoundTrackParameters::ParametersVector paramVec;
0399     paramVec << d0Dist(gen), z0Dist(gen), phiDist(gen), thetaDist(gen),
0400         q / pTDist(gen), trueVtxTime + relTDist(gen);
0401 
0402     std::shared_ptr<PerigeeSurface> perigeeSurface =
0403         Surface::makeShared<PerigeeSurface>(trueVtxPos);
0404 
0405     trks.emplace_back(perigeeSurface, paramVec, std::move(covMat),
0406                       ParticleHypothesis::pion());
0407   }
0408 
0409   std::vector<const BoundTrackParameters*> trksPtr;
0410   for (const auto& trk : trks) {
0411     trksPtr.push_back(&trk);
0412   }
0413 
0414   // Prepare fitter state
0415   AdaptiveMultiVertexFitter::State state(*bField, magFieldContext);
0416 
0417   for (const auto& trk : trks) {
0418     ACTS_DEBUG("Track parameters:\n" << trk);
0419     // Index of current vertex
0420     state.vtxInfoMap[&vtx].trackLinks.push_back(InputTrack{&trk});
0421     state.tracksAtVerticesMap.insert(
0422         std::make_pair(std::make_pair(InputTrack{&trk}, &vtx),
0423                        TrackAtVertex(1., trk, InputTrack{&trk})));
0424   }
0425 
0426   state.addVertexToMultiMap(vtx);
0427 
0428   auto res = fitter.addVtxToFit(state, vtx, vertexingOptions);
0429 
0430   BOOST_CHECK(res.ok());
0431 
0432   ACTS_DEBUG("Truth vertex position:  " << trueVtxPos.transpose());
0433   ACTS_DEBUG("Fitted vertex position: " << vtx.position().transpose());
0434 
0435   ACTS_DEBUG("Truth vertex time:  " << trueVtxTime);
0436   ACTS_DEBUG("Fitted vertex time: " << vtx.time());
0437 
0438   // Check that true vertex position and time approximately recovered
0439   CHECK_CLOSE_ABS(trueVtxPos, vtx.position(), 60_um);
0440   CHECK_CLOSE_ABS(trueVtxTime, vtx.time(), 2_ps);
0441 
0442   const SquareMatrix4& vtxCov = vtx.fullCovariance();
0443 
0444   ACTS_DEBUG("Vertex 4D covariance after the fit:\n" << vtxCov);
0445 
0446   // Check that variances of the vertex position/time are positive
0447   for (std::size_t i = 0; i <= 3; i++) {
0448     BOOST_CHECK_GT(vtxCov(i, i), 0.);
0449   }
0450 
0451   // Check that the covariance matrix is approximately symmetric
0452   CHECK_CLOSE_ABS(vtxCov - vtxCov.transpose(), SquareMatrix4::Zero(), 1e-5);
0453 }
0454 
0455 /// @brief Unit test for AdaptiveMultiVertexFitter
0456 /// based on Athena unit test, i.e. same setting and
0457 /// test values are used here
0458 BOOST_AUTO_TEST_CASE(adaptive_multi_vertex_fitter_test_athena) {
0459   // Set up constant B-Field
0460   auto bField = std::make_shared<ConstantBField>(Vector3{0.0, 0.0, 2_T});
0461 
0462   // Set up EigenStepper
0463   // EigenStepper<> stepper(bField);
0464   EigenStepper<> stepper(bField);
0465 
0466   // Set up propagator with void navigator
0467   auto propagator = std::make_shared<Propagator>(stepper);
0468 
0469   VertexingOptions vertexingOptions(geoContext, magFieldContext);
0470 
0471   ImpactPointEstimator::Config ip3dEstCfg(bField, propagator);
0472   ImpactPointEstimator ip3dEst(ip3dEstCfg);
0473 
0474   std::vector<double> temperatures(1, 3.);
0475   AnnealingUtility::Config annealingConfig;
0476   annealingConfig.setOfTemperatures = temperatures;
0477   AnnealingUtility annealingUtility(annealingConfig);
0478 
0479   AdaptiveMultiVertexFitter::Config fitterCfg(ip3dEst);
0480 
0481   fitterCfg.annealingTool = annealingUtility;
0482   fitterCfg.extractParameters.connect<&InputTrack::extractParameters>();
0483 
0484   // Linearizer for BoundTrackParameters type test
0485   Linearizer::Config ltConfig;
0486   ltConfig.bField = bField;
0487   ltConfig.propagator = propagator;
0488   Linearizer linearizer(ltConfig);
0489 
0490   fitterCfg.trackLinearizer.connect<&Linearizer::linearizeTrack>(&linearizer);
0491 
0492   // Test smoothing
0493   // fitterCfg.doSmoothing = true;
0494 
0495   AdaptiveMultiVertexFitter fitter(std::move(fitterCfg));
0496 
0497   // Create first vector of tracks
0498   Vector3 pos1a(0.5_mm, -0.5_mm, 2.4_mm);
0499   Vector3 mom1a(1000_MeV, 0_MeV, -500_MeV);
0500   Vector3 pos1b(0.5_mm, -0.5_mm, 3.5_mm);
0501   Vector3 mom1b(0_MeV, 1000_MeV, 500_MeV);
0502   Vector3 pos1c(-0.2_mm, 0.1_mm, 3.4_mm);
0503   Vector3 mom1c(-50_MeV, 180_MeV, 300_MeV);
0504 
0505   Vector3 pos1d(-0.1_mm, 0.3_mm, 3.0_mm);
0506   Vector3 mom1d(-80_MeV, 480_MeV, -100_MeV);
0507   Vector3 pos1e(-0.01_mm, 0.01_mm, 2.9_mm);
0508   Vector3 mom1e(-600_MeV, 10_MeV, 210_MeV);
0509 
0510   Vector3 pos1f(-0.07_mm, 0.03_mm, 2.5_mm);
0511   Vector3 mom1f(240_MeV, 110_MeV, 150_MeV);
0512 
0513   // Start creating some track parameters
0514   Covariance covMat1;
0515   covMat1 << 1_mm * 1_mm, 0, 0., 0, 0., 0, 0, 1_mm * 1_mm, 0, 0., 0, 0, 0., 0,
0516       0.1, 0, 0, 0, 0, 0., 0, 0.1, 0, 0, 0., 0, 0, 0, 1. / (10_GeV * 10_GeV), 0,
0517       0, 0, 0, 0, 0, 1_ns;
0518 
0519   std::vector<BoundTrackParameters> params1 = {
0520       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos1a),
0521                                    geoContext, makeVector4(pos1a, 0),
0522                                    mom1a.normalized(), 1_e / mom1a.norm(),
0523                                    covMat1, ParticleHypothesis::pion())
0524           .value(),
0525       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos1b),
0526                                    geoContext, makeVector4(pos1b, 0),
0527                                    mom1b.normalized(), -1_e / mom1b.norm(),
0528                                    covMat1, ParticleHypothesis::pion())
0529           .value(),
0530       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos1c),
0531                                    geoContext, makeVector4(pos1c, 0),
0532                                    mom1c.normalized(), 1_e / mom1c.norm(),
0533                                    covMat1, ParticleHypothesis::pion())
0534           .value(),
0535       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos1d),
0536                                    geoContext, makeVector4(pos1d, 0),
0537                                    mom1d.normalized(), -1_e / mom1d.norm(),
0538                                    covMat1, ParticleHypothesis::pion())
0539           .value(),
0540       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos1e),
0541                                    geoContext, makeVector4(pos1e, 0),
0542                                    mom1e.normalized(), 1_e / mom1e.norm(),
0543                                    covMat1, ParticleHypothesis::pion())
0544           .value(),
0545       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos1f),
0546                                    geoContext, makeVector4(pos1f, 0),
0547                                    mom1f.normalized(), -1_e / mom1f.norm(),
0548                                    covMat1, ParticleHypothesis::pion())
0549           .value(),
0550   };
0551 
0552   // Create second vector of tracks
0553   Vector3 pos2a(0.2_mm, 0_mm, -4.9_mm);
0554   Vector3 mom2a(5000_MeV, 30_MeV, 200_MeV);
0555   Vector3 pos2b(-0.5_mm, 0.1_mm, -5.1_mm);
0556   Vector3 mom2b(800_MeV, 1200_MeV, 200_MeV);
0557   Vector3 pos2c(0.05_mm, -0.5_mm, -4.7_mm);
0558   Vector3 mom2c(400_MeV, -300_MeV, -200_MeV);
0559 
0560   // Define covariance as used in athena unit test
0561   Covariance covMat2 = covMat1;
0562 
0563   std::vector<BoundTrackParameters> params2 = {
0564       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos2a),
0565                                    geoContext, makeVector4(pos2a, 0),
0566                                    mom2a.normalized(), 1_e / mom2a.norm(),
0567                                    covMat2, ParticleHypothesis::pion())
0568           .value(),
0569       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos2b),
0570                                    geoContext, makeVector4(pos2b, 0),
0571                                    mom2b.normalized(), -1_e / mom2b.norm(),
0572                                    covMat2, ParticleHypothesis::pion())
0573           .value(),
0574       BoundTrackParameters::create(Surface::makeShared<PerigeeSurface>(pos2c),
0575                                    geoContext, makeVector4(pos2c, 0),
0576                                    mom2c.normalized(), -1_e / mom2c.norm(),
0577                                    covMat2, ParticleHypothesis::pion())
0578           .value(),
0579   };
0580 
0581   AdaptiveMultiVertexFitter::State state(*bField, magFieldContext);
0582 
0583   // The constraint vertex position covariance
0584   SquareMatrix4 covConstr(SquareMatrix4::Identity());
0585   covConstr = covConstr * 1e+8;
0586   covConstr(3, 3) = 0.;
0587 
0588   // Prepare first vertex
0589   Vector3 vtxPos1(0.15_mm, 0.15_mm, 2.9_mm);
0590   Vertex vtx1(vtxPos1);
0591 
0592   // Add to vertex list
0593   state.vertexCollection.push_back(&vtx1);
0594 
0595   // The constraint vtx for vtx1
0596   Vertex vtx1Constr(vtxPos1);
0597   vtx1Constr.setFullCovariance(covConstr);
0598   vtx1Constr.setFitQuality(0, -3);
0599 
0600   // Prepare vtx info for fitter
0601   VertexInfo vtxInfo1;
0602   vtxInfo1.seedPosition = vtxInfo1.linPoint;
0603   vtxInfo1.linPoint.setZero();
0604   vtxInfo1.linPoint.head<3>() = vtxPos1;
0605   vtxInfo1.constraint = std::move(vtx1Constr);
0606   vtxInfo1.oldPosition = vtxInfo1.linPoint;
0607 
0608   for (const auto& trk : params1) {
0609     vtxInfo1.trackLinks.push_back(InputTrack{&trk});
0610     state.tracksAtVerticesMap.insert(
0611         std::make_pair(std::make_pair(InputTrack{&trk}, &vtx1),
0612                        TrackAtVertex(1.5, trk, InputTrack{&trk})));
0613   }
0614 
0615   // Prepare second vertex
0616   Vector3 vtxPos2(0.3_mm, -0.2_mm, -4.8_mm);
0617   Vertex vtx2(vtxPos2);
0618 
0619   // Add to vertex list
0620   state.vertexCollection.push_back(&vtx2);
0621 
0622   // The constraint vtx for vtx2
0623   Vertex vtx2Constr(vtxPos2);
0624   vtx2Constr.setFullCovariance(covConstr);
0625   vtx2Constr.setFitQuality(0, -3);
0626 
0627   // Prepare vtx info for fitter
0628   VertexInfo vtxInfo2;
0629   vtxInfo2.linPoint.setZero();
0630   vtxInfo2.linPoint.head<3>() = vtxPos2;
0631   vtxInfo2.constraint = std::move(vtx2Constr);
0632   vtxInfo2.oldPosition = vtxInfo2.linPoint;
0633   vtxInfo2.seedPosition = vtxInfo2.linPoint;
0634 
0635   for (const auto& trk : params2) {
0636     vtxInfo2.trackLinks.push_back(InputTrack{&trk});
0637     state.tracksAtVerticesMap.insert(
0638         std::make_pair(std::make_pair(InputTrack{&trk}, &vtx2),
0639                        TrackAtVertex(1.5, trk, InputTrack{&trk})));
0640   }
0641 
0642   state.vtxInfoMap[&vtx1] = std::move(vtxInfo1);
0643   state.vtxInfoMap[&vtx2] = std::move(vtxInfo2);
0644 
0645   state.addVertexToMultiMap(vtx1);
0646   state.addVertexToMultiMap(vtx2);
0647 
0648   // Fit vertices
0649   fitter.fit(state, vertexingOptions);
0650 
0651   auto vtx1Fitted = state.vertexCollection.at(0);
0652   auto vtx1PosFitted = vtx1Fitted->position();
0653   auto vtx1CovFitted = vtx1Fitted->covariance();
0654   auto trks1 = state.vtxInfoMap.at(vtx1Fitted).trackLinks;
0655   auto vtx1FQ = vtx1Fitted->fitQuality();
0656 
0657   auto vtx2Fitted = state.vertexCollection.at(1);
0658   auto vtx2PosFitted = vtx2Fitted->position();
0659   auto vtx2CovFitted = vtx2Fitted->covariance();
0660   auto trks2 = state.vtxInfoMap.at(vtx2Fitted).trackLinks;
0661   auto vtx2FQ = vtx2Fitted->fitQuality();
0662 
0663   // Vertex 1
0664   ACTS_DEBUG("Vertex 1, position: " << vtx1PosFitted);
0665   ACTS_DEBUG("Vertex 1, covariance: " << vtx1CovFitted);
0666   for (const auto& trk : trks1) {
0667     auto& trkAtVtx =
0668         state.tracksAtVerticesMap.at(std::make_pair(trk, vtx1Fitted));
0669     ACTS_DEBUG("\tTrack weight:" << trkAtVtx.trackWeight);
0670   }
0671   ACTS_DEBUG("Vertex 1, chi2: " << vtx1FQ.first);
0672   ACTS_DEBUG("Vertex 1, ndf: " << vtx1FQ.second);
0673 
0674   // Vertex 2
0675   ACTS_DEBUG("Vertex 2, position: " << vtx2PosFitted);
0676   ACTS_DEBUG("Vertex 2, covariance: " << vtx2CovFitted);
0677   for (const auto& trk : trks2) {
0678     auto& trkAtVtx =
0679         state.tracksAtVerticesMap.at(std::make_pair(trk, vtx2Fitted));
0680     ACTS_DEBUG("\tTrack weight:" << trkAtVtx.trackWeight);
0681   }
0682   ACTS_DEBUG("Vertex 2, chi2: " << vtx2FQ.first);
0683   ACTS_DEBUG("Vertex 2, ndf: " << vtx2FQ.second);
0684 
0685   // Expected values from Athena implementation
0686   // Vertex 1
0687   const Vector3 expVtx1Pos(0.077_mm, -0.189_mm, 2.924_mm);
0688 
0689   // Helper matrix to create const expVtx1Cov below
0690   SquareMatrix3 expVtx1Cov;
0691   expVtx1Cov << 0.329, 0.016, -0.035, 0.016, 0.250, 0.085, -0.035, 0.085, 0.242;
0692 
0693   ActsVector<6> expVtx1TrkWeights;
0694   expVtx1TrkWeights << 0.8128, 0.7994, 0.8164, 0.8165, 0.8165, 0.8119;
0695   const double expVtx1chi2 = 0.9812;
0696   const double expVtx1ndf = 6.7474;
0697 
0698   // Vertex 2
0699   const Vector3 expVtx2Pos(-0.443_mm, -0.044_mm, -4.829_mm);
0700   // Helper matrix to create const expVtx2Cov below
0701   SquareMatrix3 expVtx2Cov;
0702   expVtx2Cov << 1.088, 0.028, -0.066, 0.028, 0.643, 0.073, -0.066, 0.073, 0.435;
0703 
0704   const Vector3 expVtx2TrkWeights(0.8172, 0.8150, 0.8137);
0705   const double expVtx2chi2 = 0.2114;
0706   const double expVtx2ndf = 1.8920;
0707 
0708   // Compare the results
0709   // Vertex 1
0710   CHECK_CLOSE_ABS(vtx1PosFitted, expVtx1Pos, 0.001_mm);
0711   CHECK_CLOSE_ABS(vtx1CovFitted, expVtx1Cov, 0.001_mm);
0712   int trkCount = 0;
0713   for (const auto& trk : trks1) {
0714     auto& trkAtVtx =
0715         state.tracksAtVerticesMap.at(std::make_pair(trk, vtx1Fitted));
0716     CHECK_CLOSE_ABS(trkAtVtx.trackWeight, expVtx1TrkWeights[trkCount], 0.001);
0717     trkCount++;
0718   }
0719   CHECK_CLOSE_ABS(vtx1FQ.first, expVtx1chi2, 0.001);
0720   CHECK_CLOSE_ABS(vtx1FQ.second, expVtx1ndf, 0.001);
0721 
0722   // Vertex 2
0723   CHECK_CLOSE_ABS(vtx2PosFitted, expVtx2Pos, 0.001_mm);
0724   CHECK_CLOSE_ABS(vtx2CovFitted, expVtx2Cov, 0.001_mm);
0725   trkCount = 0;
0726   for (const auto& trk : trks2) {
0727     auto& trkAtVtx =
0728         state.tracksAtVerticesMap.at(std::make_pair(trk, vtx2Fitted));
0729     CHECK_CLOSE_ABS(trkAtVtx.trackWeight, expVtx2TrkWeights[trkCount], 0.001);
0730     trkCount++;
0731   }
0732   CHECK_CLOSE_ABS(vtx2FQ.first, expVtx2chi2, 0.001);
0733   CHECK_CLOSE_ABS(vtx2FQ.second, expVtx2ndf, 0.001);
0734 }
0735 
0736 }  // namespace Acts::Test