File indexing completed on 2025-01-18 09:13:02
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/unit_test.hpp>
0011
0012 #include "Acts/Definitions/Algebra.hpp"
0013 #include "Acts/Definitions/Common.hpp"
0014 #include "Acts/Definitions/Direction.hpp"
0015 #include "Acts/Definitions/TrackParametrization.hpp"
0016 #include "Acts/Definitions/Units.hpp"
0017 #include "Acts/EventData/Charge.hpp"
0018 #include "Acts/EventData/GenericBoundTrackParameters.hpp"
0019 #include "Acts/EventData/TrackParameters.hpp"
0020 #include "Acts/Geometry/GeometryContext.hpp"
0021 #include "Acts/Geometry/GeometryIdentifier.hpp"
0022 #include "Acts/MagneticField/ConstantBField.hpp"
0023 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0024 #include "Acts/MagneticField/MagneticFieldProvider.hpp"
0025 #include "Acts/MagneticField/NullBField.hpp"
0026 #include "Acts/Propagator/EigenStepper.hpp"
0027 #include "Acts/Propagator/Propagator.hpp"
0028 #include "Acts/Propagator/PropagatorOptions.hpp"
0029 #include "Acts/Propagator/StraightLineStepper.hpp"
0030 #include "Acts/Propagator/VoidNavigator.hpp"
0031 #include "Acts/Surfaces/PerigeeSurface.hpp"
0032 #include "Acts/Surfaces/PlaneSurface.hpp"
0033 #include "Acts/Surfaces/Surface.hpp"
0034 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0035 #include "Acts/Utilities/Logger.hpp"
0036 #include "Acts/Utilities/Result.hpp"
0037 #include "Acts/Vertexing/ImpactPointEstimator.hpp"
0038 #include "Acts/Vertexing/TrackAtVertex.hpp"
0039 #include "Acts/Vertexing/Vertex.hpp"
0040
0041 #include <algorithm>
0042 #include <array>
0043 #include <cmath>
0044 #include <limits>
0045 #include <memory>
0046 #include <numbers>
0047 #include <optional>
0048 #include <tuple>
0049 #include <utility>
0050 #include <vector>
0051
0052 namespace {
0053
0054 namespace bd = boost::unit_test::data;
0055
0056 using namespace Acts;
0057 using namespace Acts::UnitLiterals;
0058 using Acts::VectorHelpers::makeVector4;
0059
0060 using MagneticField = Acts::ConstantBField;
0061 using StraightPropagator = Acts::Propagator<StraightLineStepper>;
0062 using Stepper = Acts::EigenStepper<>;
0063 using Propagator = Acts::Propagator<Stepper>;
0064 using Estimator = Acts::ImpactPointEstimator;
0065 using StraightLineEstimator = Acts::ImpactPointEstimator;
0066
0067 const Acts::GeometryContext geoContext;
0068 const Acts::MagneticFieldContext magFieldContext;
0069
0070 Acts::MagneticFieldProvider::Cache magFieldCache() {
0071 return NullBField{}.makeCache(magFieldContext);
0072 }
0073
0074
0075
0076 auto d0s = bd::make({-25_um, 25_um});
0077 auto l0s = bd::make({-1_mm, 1_mm});
0078 auto t0s = bd::make({-2_ns, 2_ns});
0079 auto phis = bd::make({0_degree, -45_degree, 45_degree});
0080 auto thetas = bd::make({90_degree, 20_degree, 160_degree});
0081 auto ps = bd::make({0.4_GeV, 1_GeV, 10_GeV});
0082 auto qs = bd::make({-1_e, 1_e});
0083
0084 auto tracksWithoutIPs = t0s * phis * thetas * ps * qs;
0085 auto IPs = d0s * l0s;
0086 auto tracks = IPs * tracksWithoutIPs;
0087
0088
0089 auto vx0s = bd::make({0_um, -10_um, 10_um});
0090 auto vy0s = bd::make({0_um, -10_um, 10_um});
0091 auto vz0s = bd::make({0_mm, -25_mm, 25_mm});
0092 auto vt0s = bd::make({0_ns, -2_ns, 2_ns});
0093
0094 auto vertices = vx0s * vy0s * vz0s * vt0s;
0095
0096
0097 Estimator makeEstimator(double bZ) {
0098 auto field = std::make_shared<MagneticField>(Vector3(0, 0, bZ));
0099 Stepper stepper(field);
0100 Estimator::Config cfg(field,
0101 std::make_shared<Propagator>(
0102 std::move(stepper), VoidNavigator(),
0103 getDefaultLogger("Prop", Logging::Level::WARNING)));
0104 return Estimator(cfg);
0105 }
0106
0107
0108 Acts::BoundSquareMatrix makeBoundParametersCovariance(
0109 double stdDevTime = 30_ps) {
0110 BoundVector stddev;
0111 stddev[eBoundLoc0] = 15_um;
0112 stddev[eBoundLoc1] = 100_um;
0113 stddev[eBoundTime] = stdDevTime;
0114 stddev[eBoundPhi] = 1_degree;
0115 stddev[eBoundTheta] = 1_degree;
0116 stddev[eBoundQOverP] = 1_e / 100_GeV;
0117 return stddev.cwiseProduct(stddev).asDiagonal();
0118 }
0119
0120
0121 Acts::SquareMatrix4 makeVertexCovariance() {
0122 Vector4 stddev;
0123 stddev[ePos0] = 10_um;
0124 stddev[ePos1] = 10_um;
0125 stddev[ePos2] = 75_um;
0126 stddev[eTime] = 1_ns;
0127 return stddev.cwiseProduct(stddev).asDiagonal();
0128 }
0129
0130
0131 std::uniform_real_distribution<double> uniformDist(0.0, 1.0);
0132
0133 std::uniform_real_distribution<double> signDist(-1, 1);
0134 }
0135
0136 BOOST_AUTO_TEST_SUITE(VertexingImpactPointEstimator)
0137
0138
0139
0140 BOOST_DATA_TEST_CASE(SingleTrackDistanceParametersCompatibility3D, tracks, d0,
0141 l0, t0, phi, theta, p, q) {
0142 auto particleHypothesis = ParticleHypothesis::pion();
0143
0144 BoundVector par;
0145 par[eBoundLoc0] = d0;
0146 par[eBoundLoc1] = l0;
0147 par[eBoundTime] = t0;
0148 par[eBoundPhi] = phi;
0149 par[eBoundTheta] = theta;
0150 par[eBoundQOverP] = particleHypothesis.qOverP(p, q);
0151
0152 Estimator ipEstimator = makeEstimator(2_T);
0153 Estimator::State state{magFieldCache()};
0154
0155 Vector3 refPosition(0., 0., 0.);
0156 auto perigeeSurface = Surface::makeShared<PerigeeSurface>(refPosition);
0157
0158 BoundTrackParameters myTrack(
0159 perigeeSurface, par, makeBoundParametersCovariance(), particleHypothesis);
0160
0161
0162 double distT = std::hypot(d0, l0);
0163 double dist3 =
0164 ipEstimator.calculateDistance(geoContext, myTrack, refPosition, state)
0165 .value();
0166
0167
0168
0169
0170 BOOST_CHECK((dist3 < distT) ||
0171 (theta == 90_degree && std::abs(dist3 - distT) < 1_nm));
0172
0173
0174 auto res = ipEstimator.estimate3DImpactParameters(
0175 geoContext, magFieldContext, myTrack, refPosition, state);
0176 BoundTrackParameters trackAtIP3d = *res;
0177 const auto& atPerigee = myTrack.parameters();
0178 const auto& atIp3d = trackAtIP3d.parameters();
0179
0180
0181 BOOST_CHECK_NE(atPerigee[eBoundLoc0], atIp3d[eBoundLoc0]);
0182 BOOST_CHECK_NE(atPerigee[eBoundLoc1], atIp3d[eBoundLoc1]);
0183
0184
0185 CHECK_CLOSE_ABS(atPerigee[eBoundTheta], atIp3d[eBoundTheta], 0.01_mrad);
0186 CHECK_CLOSE_REL(atPerigee[eBoundQOverP], atIp3d[eBoundQOverP],
0187 std::numeric_limits<double>::epsilon());
0188
0189
0190
0191 auto compatibility =
0192 ipEstimator.getVertexCompatibility(geoContext, &trackAtIP3d, refPosition)
0193 .value();
0194 BOOST_CHECK_GT(compatibility, 0);
0195 }
0196
0197 BOOST_DATA_TEST_CASE(TimeAtPca, tracksWithoutIPs* vertices, t0, phi, theta, p,
0198 q, vx0, vy0, vz0, vt0) {
0199 using Propagator = Acts::Propagator<Stepper>;
0200 using PropagatorOptions = Propagator::Options<>;
0201 using StraightPropagator = Acts::Propagator<StraightLineStepper>;
0202
0203
0204 auto field = std::make_shared<MagneticField>(Vector3(0, 0, 2_T));
0205 Stepper stepper(field);
0206 auto propagator = std::make_shared<Propagator>(std::move(stepper));
0207 Estimator::Config cfg(field, propagator);
0208 Estimator ipEstimator(cfg);
0209 Estimator::State ipState{magFieldCache()};
0210
0211
0212 auto zeroField = std::make_shared<MagneticField>(Vector3(0, 0, 0));
0213 StraightLineStepper straightLineStepper;
0214 auto straightLinePropagator =
0215 std::make_shared<StraightPropagator>(straightLineStepper);
0216 StraightLineEstimator::Config zeroFieldCfg(zeroField, straightLinePropagator);
0217 StraightLineEstimator zeroFieldIPEstimator(zeroFieldCfg);
0218 StraightLineEstimator::State zeroFieldIPState{magFieldCache()};
0219
0220
0221 Vector4 vtxPos(vx0, vy0, vz0, vt0);
0222 Vertex vtx(vtxPos, makeVertexCovariance(), {});
0223
0224
0225 auto vtxPerigeeSurface =
0226 Surface::makeShared<PerigeeSurface>(vtxPos.head<3>());
0227
0228
0229
0230
0231 BoundVector paramVec;
0232 paramVec[eBoundLoc0] = 0.;
0233 paramVec[eBoundLoc1] = 0.;
0234 paramVec[eBoundTime] = t0;
0235 paramVec[eBoundPhi] = phi;
0236 paramVec[eBoundTheta] = theta;
0237 paramVec[eBoundQOverP] = q / p;
0238
0239 BoundTrackParameters params(vtxPerigeeSurface, paramVec,
0240 makeBoundParametersCovariance(),
0241 ParticleHypothesis::pion());
0242
0243
0244
0245 double corrTimeDiff = t0 - vt0;
0246
0247
0248 double cosPhi = std::cos(phi);
0249 double sinPhi = std::sin(phi);
0250 double sinTheta = std::sin(theta);
0251 Vector3 corrMomDir =
0252 Vector3(cosPhi * sinTheta, sinPhi * sinTheta, std::cos(theta));
0253
0254
0255 Vector3 refPoint(2_mm, -2_mm, -5_mm);
0256
0257
0258 auto refPerigeeSurface = Surface::makeShared<PerigeeSurface>(refPoint);
0259
0260
0261 PropagatorOptions pOptions(geoContext, magFieldContext);
0262 auto intersection =
0263 refPerigeeSurface
0264 ->intersect(geoContext, params.position(geoContext),
0265 params.direction(), BoundaryTolerance::Infinite())
0266 .closest();
0267 pOptions.direction =
0268 Direction::fromScalarZeroAsPositive(intersection.pathLength());
0269
0270 StraightPropagator::Options<> straightPOptions(geoContext, magFieldContext);
0271 straightPOptions.direction = pOptions.direction;
0272
0273
0274 auto result = propagator->propagate(params, *refPerigeeSurface, pOptions);
0275 BOOST_CHECK(result.ok());
0276 const auto& refParams = *result->endParameters;
0277
0278
0279 auto zeroFieldResult = straightLinePropagator->propagate(
0280 params, *refPerigeeSurface, straightPOptions);
0281 BOOST_CHECK(zeroFieldResult.ok());
0282 const auto& zeroFieldRefParams = *zeroFieldResult->endParameters;
0283
0284 BOOST_TEST_CONTEXT(
0285 "Check time at 2D PCA (i.e., function getImpactParameters) for helical "
0286 "tracks") {
0287
0288 auto ipParams = ipEstimator
0289 .getImpactParameters(refParams, vtx, geoContext,
0290 magFieldContext, true)
0291 .value();
0292
0293
0294 CHECK_CLOSE_ABS(ipParams.d0, 0., 30_nm);
0295 CHECK_CLOSE_ABS(ipParams.z0, 0., 100_nm);
0296
0297
0298 CHECK_CLOSE_OR_SMALL(ipParams.deltaT.value(), std::abs(corrTimeDiff), 1e-5,
0299 1e-3);
0300 }
0301
0302 auto checkGetDistanceAndMomentum = [&vtxPos, &corrMomDir, corrTimeDiff](
0303 const auto& ipe, const auto& rParams,
0304 auto& state) {
0305
0306
0307 auto distAndMom = ipe.template getDistanceAndMomentum<4>(
0308 geoContext, rParams, vtxPos, state)
0309 .value();
0310
0311 Vector4 distVec = distAndMom.first;
0312 Vector3 momDir = distAndMom.second;
0313
0314
0315
0316 double dist = distVec.head<3>().norm();
0317 CHECK_CLOSE_ABS(dist, 0., 30_nm);
0318
0319
0320
0321 CHECK_CLOSE_OR_SMALL(distVec[3], corrTimeDiff, 1e-5, 1e-4);
0322
0323
0324 CHECK_CLOSE_OR_SMALL(momDir, corrMomDir, 1e-5, 1e-4);
0325 };
0326
0327 BOOST_TEST_CONTEXT(
0328 "Check time at 3D PCA (i.e., function getDistanceAndMomentum) for "
0329 "straight tracks") {
0330 checkGetDistanceAndMomentum(zeroFieldIPEstimator, zeroFieldRefParams,
0331 zeroFieldIPState);
0332 }
0333 BOOST_TEST_CONTEXT(
0334 "Check time at 3D PCA (i.e., function getDistanceAndMomentum) for "
0335 "helical tracks") {
0336 checkGetDistanceAndMomentum(ipEstimator, refParams, ipState);
0337 }
0338 }
0339
0340 BOOST_DATA_TEST_CASE(VertexCompatibility4D, IPs* vertices, d0, l0, vx0, vy0,
0341 vz0, vt0) {
0342
0343 int seed = 31415;
0344 std::mt19937 gen(seed);
0345
0346
0347 Estimator ipEstimator = makeEstimator(2_T);
0348
0349
0350 Vector4 vtxPos(vx0, vy0, vz0, vt0);
0351
0352
0353 Transform3 coordinateSystem;
0354
0355 coordinateSystem.matrix().block<3, 3>(0, 0) = ActsSquareMatrix<3>::Identity();
0356
0357 coordinateSystem.matrix().block<3, 1>(0, 3) = vtxPos.head<3>();
0358
0359
0360 std::shared_ptr<PlaneSurface> planeSurface =
0361 Surface::makeShared<PlaneSurface>(coordinateSystem);
0362
0363
0364
0365
0366
0367 double timeDiffFactor = uniformDist(gen);
0368 double timeDiffClose = timeDiffFactor * 0.1_ps;
0369 double timeDiffFar = timeDiffFactor * 0.11_ps;
0370
0371
0372 double sgnClose = signDist(gen) < 0 ? -1. : 1.;
0373 double sgnFar = signDist(gen) < 0 ? -1. : 1.;
0374
0375 BoundVector paramVecClose = BoundVector::Zero();
0376 paramVecClose[eBoundLoc0] = d0;
0377 paramVecClose[eBoundLoc1] = l0;
0378 paramVecClose[eBoundPhi] = 0;
0379 paramVecClose[eBoundTheta] = std::numbers::pi / 2;
0380 paramVecClose[eBoundQOverP] = 0;
0381 paramVecClose[eBoundTime] = vt0 + sgnClose * timeDiffClose;
0382
0383 BoundVector paramVecFar = paramVecClose;
0384 paramVecFar[eBoundTime] = vt0 + sgnFar * timeDiffFar;
0385
0386
0387 BoundTrackParameters paramsClose(planeSurface, paramVecClose,
0388 makeBoundParametersCovariance(30_ns),
0389 ParticleHypothesis::pion());
0390
0391
0392
0393 BoundTrackParameters paramsCloseLargerCov(
0394 planeSurface, paramVecClose, makeBoundParametersCovariance(31_ns),
0395 ParticleHypothesis::pion());
0396
0397
0398 BoundTrackParameters paramsFar(planeSurface, paramVecFar,
0399 makeBoundParametersCovariance(30_ns),
0400 ParticleHypothesis::pion());
0401
0402
0403 double compatibilityClose =
0404 ipEstimator.getVertexCompatibility(geoContext, ¶msClose, vtxPos)
0405 .value();
0406 double compatibilityCloseLargerCov =
0407 ipEstimator
0408 .getVertexCompatibility(geoContext, ¶msCloseLargerCov, vtxPos)
0409 .value();
0410 double compatibilityFar =
0411 ipEstimator.getVertexCompatibility(geoContext, ¶msFar, vtxPos)
0412 .value();
0413
0414
0415
0416 BOOST_CHECK_LT(compatibilityClose, compatibilityFar);
0417
0418 BOOST_CHECK_LT(compatibilityCloseLargerCov, compatibilityClose);
0419 }
0420
0421
0422
0423
0424
0425
0426
0427
0428 BOOST_AUTO_TEST_CASE(SingleTrackDistanceParametersAthenaRegression) {
0429 Estimator ipEstimator = makeEstimator(1.9971546939_T);
0430 Estimator::State state{magFieldCache()};
0431
0432
0433 Vector4 pos1(2_mm, 1_mm, -10_mm, 0_ns);
0434 Vector3 mom1(400_MeV, 600_MeV, 200_MeV);
0435 Vector3 vtxPos(1.2_mm, 0.8_mm, -7_mm);
0436
0437
0438 auto perigeeSurface =
0439 Surface::makeShared<PerigeeSurface>(pos1.segment<3>(ePos0));
0440
0441 auto params1 = BoundTrackParameters::create(
0442 perigeeSurface, geoContext, pos1, mom1, 1_e / mom1.norm(),
0443 BoundTrackParameters::CovarianceMatrix::Identity(),
0444 ParticleHypothesis::pion())
0445 .value();
0446
0447
0448 auto distance =
0449 ipEstimator.calculateDistance(geoContext, params1, vtxPos, state).value();
0450 CHECK_CLOSE_ABS(distance, 3.10391_mm, 10_nm);
0451
0452 auto res2 = ipEstimator.estimate3DImpactParameters(
0453 geoContext, magFieldContext, params1, vtxPos, state);
0454 BOOST_CHECK(res2.ok());
0455 BoundTrackParameters endParams = *res2;
0456 Vector3 surfaceCenter = endParams.referenceSurface().center(geoContext);
0457
0458 BOOST_CHECK_EQUAL(surfaceCenter, vtxPos);
0459 }
0460
0461
0462
0463
0464 BOOST_AUTO_TEST_CASE(Lifetimes2d3d) {
0465 Estimator ipEstimator = makeEstimator(2_T);
0466
0467
0468 BoundVector trk_par;
0469 trk_par[eBoundLoc0] = 200_um;
0470 trk_par[eBoundLoc1] = 300_um;
0471 trk_par[eBoundTime] = 1_ns;
0472 trk_par[eBoundPhi] = 45_degree;
0473 trk_par[eBoundTheta] = 45_degree;
0474 trk_par[eBoundQOverP] = 1_e / 10_GeV;
0475
0476 Vector4 ip_pos{0., 0., 0., 0.};
0477 Vertex ip_vtx(ip_pos, makeVertexCovariance(), {});
0478
0479
0480 auto perigeeSurface = Surface::makeShared<PerigeeSurface>(ip_pos.head<3>());
0481 BoundTrackParameters track(perigeeSurface, trk_par,
0482 makeBoundParametersCovariance(),
0483 ParticleHypothesis::pion());
0484
0485 Vector3 direction{0., 1., 0.};
0486 auto lifetimes_signs = ipEstimator.getLifetimeSignOfTrack(
0487 track, ip_vtx, direction, geoContext, magFieldContext);
0488
0489
0490 BOOST_CHECK(lifetimes_signs.ok());
0491
0492
0493 BOOST_CHECK_GT((*lifetimes_signs).first, 0.);
0494
0495
0496 BOOST_CHECK_LT((*lifetimes_signs).second, 0.);
0497
0498
0499
0500 auto sign3d = ipEstimator.get3DLifetimeSignOfTrack(
0501 track, ip_vtx, direction, geoContext, magFieldContext);
0502
0503
0504 BOOST_CHECK(sign3d.ok());
0505
0506
0507 BOOST_CHECK_GT((*sign3d), 0.);
0508 }
0509
0510
0511 BOOST_DATA_TEST_CASE(SingeTrackImpactParameters, tracks* vertices, d0, l0, t0,
0512 phi, theta, p, q, vx0, vy0, vz0, vt0) {
0513 BoundVector par;
0514 par[eBoundLoc0] = d0;
0515 par[eBoundLoc1] = l0;
0516 par[eBoundTime] = t0;
0517 par[eBoundPhi] = phi;
0518 par[eBoundTheta] = theta;
0519 par[eBoundQOverP] = q / p;
0520 Vector4 vtxPos;
0521 vtxPos[ePos0] = vx0;
0522 vtxPos[ePos1] = vy0;
0523 vtxPos[ePos2] = vz0;
0524 vtxPos[eTime] = vt0;
0525
0526 Estimator ipEstimator = makeEstimator(1_T);
0527 Estimator::State state{magFieldCache()};
0528
0529
0530 Vector3 refPosition(0., 0., 0.);
0531 auto perigeeSurface = Surface::makeShared<PerigeeSurface>(refPosition);
0532
0533 BoundTrackParameters track(perigeeSurface, par,
0534 makeBoundParametersCovariance(),
0535 ParticleHypothesis::pionLike(std::abs(q)));
0536 Vertex myConstraint(vtxPos, makeVertexCovariance(), {});
0537
0538
0539 ImpactParametersAndSigma output =
0540 ipEstimator
0541 .getImpactParameters(track, myConstraint, geoContext, magFieldContext)
0542 .value();
0543 BOOST_CHECK_NE(output.d0, 0.);
0544 BOOST_CHECK_NE(output.z0, 0.);
0545
0546
0547 }
0548
0549 BOOST_AUTO_TEST_SUITE_END()