File indexing completed on 2025-07-09 07:51:10
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/TrackParametrization.hpp"
0014 #include "Acts/EventData/ParticleHypothesis.hpp"
0015 #include "Acts/EventData/TrackParameters.hpp"
0016 #include "Acts/EventData/TransformationHelpers.hpp"
0017 #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/MagneticField/ConstantBField.hpp"
0020 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0021 #include "Acts/Propagator/EigenStepper.hpp"
0022 #include "Acts/Propagator/Propagator.hpp"
0023 #include "Acts/Propagator/VoidNavigator.hpp"
0024 #include "Acts/Propagator/detail/CovarianceEngine.hpp"
0025 #include "Acts/Surfaces/PerigeeSurface.hpp"
0026 #include "Acts/Surfaces/PlaneSurface.hpp"
0027 #include "Acts/Surfaces/Surface.hpp"
0028 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0029 #include "Acts/Utilities/Result.hpp"
0030
0031 #include <cmath>
0032 #include <memory>
0033 #include <numbers>
0034 #include <optional>
0035 #include <random>
0036 #include <tuple>
0037 #include <utility>
0038
0039 namespace bdata = boost::unit_test::data;
0040
0041 namespace Acts::Test {
0042
0043 Acts::GeometryContext gctx;
0044 Acts::MagneticFieldContext mctx;
0045
0046 using namespace Acts::UnitLiterals;
0047
0048 using Covariance = BoundSquareMatrix;
0049 using Jacobian = BoundMatrix;
0050
0051
0052
0053
0054 BOOST_AUTO_TEST_CASE(covariance_engine_test) {
0055
0056 GeometryContext tgContext = GeometryContext();
0057
0058 auto particleHypothesis = ParticleHypothesis::pion();
0059
0060
0061 Vector3 position{1., 2., 3.};
0062 double time = 4.;
0063 Vector3 direction{sqrt(5. / 22.), 3. * sqrt(2. / 55.), 7. / sqrt(110.)};
0064 double qop = 0.125;
0065 FreeVector parameters, startParameters;
0066 parameters << position[0], position[1], position[2], time, direction[0],
0067 direction[1], direction[2], qop;
0068 startParameters = parameters;
0069
0070
0071 Covariance covariance = Covariance::Identity();
0072 Jacobian jacobian = 2. * Jacobian::Identity();
0073 FreeMatrix transportJacobian = 3. * FreeMatrix::Identity();
0074 FreeVector derivatives;
0075 derivatives << 9., 10., 11., 12., 13., 14., 15., 16.;
0076 BoundToFreeMatrix boundToFreeJacobian = 4. * BoundToFreeMatrix::Identity();
0077 std::optional<FreeMatrix> additionalFreeCovariance;
0078
0079
0080 detail::transportCovarianceToCurvilinear(
0081 covariance, jacobian, transportJacobian, derivatives, boundToFreeJacobian,
0082 additionalFreeCovariance, direction);
0083
0084
0085 BOOST_CHECK_NE(covariance, Covariance::Identity());
0086 BOOST_CHECK_NE(jacobian, 2. * Jacobian::Identity());
0087 BOOST_CHECK_EQUAL(transportJacobian, FreeMatrix::Identity());
0088 BOOST_CHECK_EQUAL(derivatives, FreeVector::Zero());
0089 BOOST_CHECK_NE(boundToFreeJacobian, 4. * BoundToFreeMatrix::Identity());
0090 BOOST_CHECK_EQUAL(
0091 direction, Vector3(sqrt(5. / 22.), 3. * sqrt(2. / 55.), 7. / sqrt(110.)));
0092
0093
0094 covariance = Covariance::Identity();
0095 jacobian = 2. * Jacobian::Identity();
0096 transportJacobian = 3. * FreeMatrix::Identity();
0097 derivatives << 9., 10., 11., 12., 13., 14., 15., 16.;
0098 boundToFreeJacobian = 4. * BoundToFreeMatrix::Identity();
0099
0100
0101 FreeToBoundCorrection freeToBoundCorrection(false);
0102 std::shared_ptr<PlaneSurface> surface =
0103 CurvilinearSurface(position, direction).planeSurface();
0104 detail::transportCovarianceToBound(
0105 tgContext, *surface, covariance, jacobian, transportJacobian, derivatives,
0106 boundToFreeJacobian, additionalFreeCovariance, parameters,
0107 freeToBoundCorrection);
0108
0109 BOOST_CHECK_NE(covariance, Covariance::Identity());
0110 BOOST_CHECK_NE(jacobian, 2. * Jacobian::Identity());
0111 BOOST_CHECK_EQUAL(transportJacobian, FreeMatrix::Identity());
0112 BOOST_CHECK_EQUAL(derivatives, FreeVector::Zero());
0113 BOOST_CHECK_NE(boundToFreeJacobian, 4. * BoundToFreeMatrix::Identity());
0114 BOOST_CHECK_EQUAL(parameters, startParameters);
0115
0116
0117 auto curvResult = detail::curvilinearState(
0118 covariance, jacobian, transportJacobian, derivatives, boundToFreeJacobian,
0119 std::nullopt, parameters, particleHypothesis, false, 1337.);
0120 BOOST_CHECK(!std::get<0>(curvResult).covariance().has_value());
0121 BOOST_CHECK_EQUAL(std::get<2>(curvResult), 1337.);
0122
0123
0124 covariance = Covariance::Identity();
0125 jacobian = 2. * Jacobian::Identity();
0126 transportJacobian = 3. * FreeMatrix::Identity();
0127 derivatives << 9., 10., 11., 12., 13., 14., 15., 16.;
0128 boundToFreeJacobian = 4. * BoundToFreeMatrix::Identity();
0129
0130
0131 curvResult = detail::curvilinearState(
0132 covariance, jacobian, transportJacobian, derivatives, boundToFreeJacobian,
0133 additionalFreeCovariance, parameters, particleHypothesis, true, 1337.);
0134 BOOST_CHECK(std::get<0>(curvResult).covariance().has_value());
0135 BOOST_CHECK_NE(*(std::get<0>(curvResult).covariance()),
0136 Covariance::Identity());
0137 BOOST_CHECK_NE(std::get<1>(curvResult), 2. * Jacobian::Identity());
0138 BOOST_CHECK_EQUAL(std::get<2>(curvResult), 1337.);
0139
0140
0141 auto covarianceBefore = covariance;
0142 auto boundResult =
0143 detail::boundState(
0144 tgContext, *surface, covariance, jacobian, transportJacobian,
0145 derivatives, boundToFreeJacobian, additionalFreeCovariance,
0146 parameters, particleHypothesis, false, 1337., freeToBoundCorrection)
0147 .value();
0148 BOOST_CHECK(std::get<0>(curvResult).covariance().has_value());
0149 BOOST_CHECK_EQUAL(*(std::get<0>(curvResult).covariance()), covarianceBefore);
0150 BOOST_CHECK_EQUAL(std::get<2>(boundResult), 1337.);
0151
0152
0153 covariance = Covariance::Identity();
0154 jacobian = 2. * Jacobian::Identity();
0155 transportJacobian = 3. * FreeMatrix::Identity();
0156 derivatives << 9., 10., 11., 12., 13., 14., 15., 16.;
0157 boundToFreeJacobian = 4. * BoundToFreeMatrix::Identity();
0158
0159
0160 boundResult =
0161 detail::boundState(tgContext, *surface, covariance, jacobian,
0162 transportJacobian, derivatives, boundToFreeJacobian,
0163 additionalFreeCovariance, parameters,
0164 ParticleHypothesis::pion(), true, 1337.,
0165 freeToBoundCorrection)
0166 .value();
0167 BOOST_CHECK(std::get<0>(boundResult).covariance().has_value());
0168 BOOST_CHECK_NE(*(std::get<0>(boundResult).covariance()),
0169 Covariance::Identity());
0170 BOOST_CHECK_NE(std::get<1>(boundResult), 2. * Jacobian::Identity());
0171 BOOST_CHECK_EQUAL(std::get<2>(boundResult), 1337.);
0172
0173
0174 freeToBoundCorrection.apply = true;
0175
0176
0177 boundResult =
0178 detail::boundState(tgContext, *surface, covariance, jacobian,
0179 transportJacobian, derivatives, boundToFreeJacobian,
0180 additionalFreeCovariance, parameters,
0181 ParticleHypothesis::pion(), true, 1337.,
0182 freeToBoundCorrection)
0183 .value();
0184 BOOST_CHECK(std::get<0>(boundResult).covariance().has_value());
0185 BOOST_CHECK_NE(*(std::get<0>(boundResult).covariance()),
0186 Covariance::Identity());
0187 }
0188
0189 std::pair<BoundVector, BoundMatrix> boundToBound(const BoundVector& parIn,
0190 const BoundMatrix& covIn,
0191 const Surface& srfA,
0192 const Surface& srfB,
0193 const Vector3& bField) {
0194 Acts::BoundTrackParameters boundParamIn{srfA.getSharedPtr(), parIn, covIn,
0195 ParticleHypothesis::pion()};
0196
0197 auto converted =
0198 detail::boundToBoundConversion(gctx, boundParamIn, srfB, bField).value();
0199
0200 return {converted.parameters(), converted.covariance().value()};
0201 }
0202
0203 using propagator_t = Propagator<EigenStepper<>, VoidNavigator>;
0204
0205 BoundVector localToLocal(const propagator_t& prop, const BoundVector& local,
0206 const Surface& src, const Surface& dst) {
0207 using PropagatorOptions = typename propagator_t::template Options<>;
0208 PropagatorOptions options{gctx, mctx};
0209 options.stepping.stepTolerance = 1e-10;
0210 options.surfaceTolerance = 1e-10;
0211
0212 BoundTrackParameters start{src.getSharedPtr(), local, std::nullopt,
0213 ParticleHypothesis::pion()};
0214
0215 auto res = prop.propagate(start, dst, options).value();
0216 auto endParameters = res.endParameters.value();
0217
0218 BOOST_CHECK_EQUAL(&endParameters.referenceSurface(), &dst);
0219
0220 BoundVector out = endParameters.parameters();
0221 out[eBoundTime] = local[eBoundTime];
0222 return out;
0223 }
0224
0225 propagator_t makePropagator(const Vector3& bField) {
0226 return propagator_t{EigenStepper<>{std::make_shared<ConstantBField>(bField)},
0227 VoidNavigator{}};
0228 }
0229
0230 BoundMatrix numericalBoundToBoundJacobian(const propagator_t& prop,
0231 const BoundVector& parA,
0232 const Surface& srfA,
0233 const Surface& srfB) {
0234 double h = 1e-4;
0235 BoundMatrix J;
0236 for (std::size_t i = 0; i < 6; i++) {
0237 for (std::size_t j = 0; j < 6; j++) {
0238 BoundVector parInitial1 = parA;
0239 BoundVector parInitial2 = parA;
0240 parInitial1[i] -= h;
0241 parInitial2[i] += h;
0242 BoundVector parFinal1 = localToLocal(prop, parInitial1, srfA, srfB);
0243 BoundVector parFinal2 = localToLocal(prop, parInitial2, srfA, srfB);
0244
0245 J(j, i) = (parFinal2[j] - parFinal1[j]) / (2 * h);
0246 }
0247 }
0248 return J;
0249 }
0250
0251 unsigned int getNextSeed() {
0252 static unsigned int seed = 10;
0253 return ++seed;
0254 }
0255
0256 auto makeDist(double a, double b) {
0257 return bdata::random(
0258 (bdata::engine = std::mt19937{}, bdata::seed = getNextSeed(),
0259 bdata::distribution = std::uniform_real_distribution<double>(a, b)));
0260 }
0261
0262 const auto locDist = makeDist(-5_mm, 5_mm);
0263 const auto bFieldDist = makeDist(0, 3_T);
0264 const auto angleDist = makeDist(-2 * std::numbers::pi, 2 * std::numbers::pi);
0265 const auto posDist = makeDist(-50_mm, 50_mm);
0266
0267 #define MAKE_SURFACE() \
0268 [&]() { \
0269 Transform3 transformA = Transform3::Identity(); \
0270 transformA = AngleAxis3(Rx, Vector3::UnitX()); \
0271 transformA = AngleAxis3(Ry, Vector3::UnitY()); \
0272 transformA = AngleAxis3(Rz, Vector3::UnitZ()); \
0273 transformA.translation() << gx, gy, gz; \
0274 return Surface::makeShared<PlaneSurface>(transformA); \
0275 }()
0276
0277 BOOST_DATA_TEST_CASE(CovarianceConversionSamePlane,
0278 (bFieldDist ^ bFieldDist ^ bFieldDist ^ angleDist ^
0279 angleDist ^ angleDist ^ posDist ^ posDist ^ posDist ^
0280 locDist ^ locDist) ^
0281 bdata::xrange(100),
0282 Bx, By, Bz, Rx, Ry, Rz, gx, gy, gz, l0, l1, index) {
0283 (void)index;
0284 const Vector3 bField{Bx, By, Bz};
0285
0286 auto planeSurfaceA = MAKE_SURFACE();
0287 auto planeSurfaceB =
0288 Surface::makeShared<PlaneSurface>(planeSurfaceA->transform(gctx));
0289
0290 BoundMatrix covA;
0291 covA.setZero();
0292 covA.diagonal() << 1, 2, 3, 4, 5, 6;
0293
0294 BoundVector parA;
0295 parA << l0, l1, std::numbers::pi / 4., std::numbers::pi / 2. * 0.9,
0296 -1 / 1_GeV, 5_ns;
0297
0298
0299 auto [parB, covB] =
0300 boundToBound(parA, covA, *planeSurfaceA, *planeSurfaceB, bField);
0301
0302
0303 CHECK_CLOSE_ABS(parA, parB, 1e-9);
0304 CHECK_CLOSE_COVARIANCE(covA, covB, 1e-9);
0305
0306
0307 auto [parA2, covA2] =
0308 boundToBound(parB, covB, *planeSurfaceB, *planeSurfaceA, bField);
0309 CHECK_CLOSE_ABS(parA, parA2, 1e-9);
0310 CHECK_CLOSE_COVARIANCE(covA, covA2, 1e-9);
0311
0312 auto prop = makePropagator(bField);
0313
0314 BoundMatrix J =
0315 numericalBoundToBoundJacobian(prop, parA, *planeSurfaceA, *planeSurfaceB);
0316 BoundMatrix covC = J * covA * J.transpose();
0317 CHECK_CLOSE_COVARIANCE(covB, covC, 1e-6);
0318 }
0319
0320 BOOST_DATA_TEST_CASE(CovarianceConversionRotatedPlane,
0321 (bFieldDist ^ bFieldDist ^ bFieldDist ^ angleDist ^
0322 angleDist ^ angleDist ^ posDist ^ posDist ^ posDist ^
0323 locDist ^ locDist ^ angleDist) ^
0324 bdata::xrange(100),
0325 Bx, By, Bz, Rx, Ry, Rz, gx, gy, gz, l0, l1, angle, index) {
0326 (void)index;
0327 const Vector3 bField{Bx, By, Bz};
0328
0329 auto planeSurfaceA = MAKE_SURFACE();
0330
0331 Transform3 transform;
0332 transform = planeSurfaceA->transform(gctx).rotation();
0333 transform = AngleAxis3(angle, planeSurfaceA->normal(gctx)) * transform;
0334 transform.translation() = planeSurfaceA->transform(gctx).translation();
0335 auto planeSurfaceB = Surface::makeShared<PlaneSurface>(transform);
0336
0337
0338 CHECK_CLOSE_ABS(planeSurfaceA->normal(gctx), planeSurfaceB->normal(gctx),
0339 1e-9);
0340
0341 BoundMatrix covA;
0342 covA.setZero();
0343 covA.diagonal() << 1, 2, 3, 4, 5, 6;
0344
0345 BoundVector parA;
0346 parA << l0, l1, std::numbers::pi / 4., std::numbers::pi / 2. * 0.9,
0347 -1 / 1_GeV, 5_ns;
0348
0349 auto [parB, covB] =
0350 boundToBound(parA, covA, *planeSurfaceA, *planeSurfaceB, bField);
0351 BoundVector exp = parA;
0352
0353 exp.head<2>() = Eigen::Rotation2D<double>(-angle) * parA.head<2>();
0354
0355 CHECK_CLOSE_ABS(exp, parB, 1e-9);
0356
0357
0358 auto [parA2, covA2] =
0359 boundToBound(parB, covB, *planeSurfaceB, *planeSurfaceA, bField);
0360 CHECK_CLOSE_ABS(parA, parA2, 1e-9);
0361 CHECK_CLOSE_COVARIANCE(covA, covA2, 1e-9);
0362
0363 auto prop = makePropagator(bField);
0364 BoundMatrix J =
0365 numericalBoundToBoundJacobian(prop, parA, *planeSurfaceA, *planeSurfaceB);
0366 BoundMatrix covC = J * covA * J.transpose();
0367 CHECK_CLOSE_COVARIANCE(covB, covC, 1e-6);
0368 }
0369
0370 BOOST_DATA_TEST_CASE(CovarianceConversionL0TiltedPlane,
0371 (bFieldDist ^ bFieldDist ^ bFieldDist ^ angleDist ^
0372 angleDist ^ angleDist ^ posDist ^ posDist ^ posDist ^
0373 locDist ^ angleDist) ^
0374 bdata::xrange(100),
0375 Bx, By, Bz, Rx, Ry, Rz, gx, gy, gz, l1, angle, index) {
0376 (void)index;
0377 const Vector3 bField{Bx, By, Bz};
0378
0379 auto planeSurfaceA = MAKE_SURFACE();
0380
0381
0382 Transform3 transform;
0383 transform = planeSurfaceA->transform(gctx).rotation();
0384
0385
0386 Vector3 axis = planeSurfaceA->transform(gctx).rotation() * Vector3::UnitY();
0387 transform = AngleAxis3(angle, axis) * transform;
0388
0389 transform.translation() = planeSurfaceA->transform(gctx).translation();
0390
0391 auto planeSurfaceB = Surface::makeShared<PlaneSurface>(transform);
0392
0393 BoundVector parA;
0394
0395 parA << 0, l1, std::numbers::pi / 4., std::numbers::pi / 2. * 0.9, -1 / 1_GeV,
0396 5_ns;
0397
0398 BoundMatrix covA;
0399 covA.setZero();
0400 covA.diagonal() << 1, 2, 3, 4, 5, 6;
0401
0402 auto [parB, covB] =
0403 boundToBound(parA, covA, *planeSurfaceA, *planeSurfaceB, bField);
0404
0405
0406 auto [parA2, covA2] =
0407 boundToBound(parB, covB, *planeSurfaceB, *planeSurfaceA, bField);
0408 CHECK_CLOSE_ABS(parA, parA2, 1e-9);
0409 CHECK_CLOSE_COVARIANCE(covA, covA2, 1e-7);
0410
0411 auto prop = makePropagator(bField);
0412 BoundMatrix J =
0413 numericalBoundToBoundJacobian(prop, parA, *planeSurfaceA, *planeSurfaceB);
0414 BoundMatrix covC = J * covA * J.transpose();
0415 CHECK_CLOSE_OR_SMALL((covB.template topLeftCorner<2, 2>()),
0416 (covC.template topLeftCorner<2, 2>()), 1e-7, 1e-9);
0417 CHECK_CLOSE_OR_SMALL(covB.diagonal(), covC.diagonal(), 1e-7, 1e-9);
0418 }
0419
0420 BOOST_DATA_TEST_CASE(CovarianceConversionL1TiltedPlane,
0421 (bFieldDist ^ bFieldDist ^ bFieldDist ^ angleDist ^
0422 angleDist ^ angleDist ^ posDist ^ posDist ^ posDist ^
0423 locDist ^ angleDist) ^
0424 bdata::xrange(100),
0425 Bx, By, Bz, Rx, Ry, Rz, gx, gy, gz, l0, angle, index) {
0426 (void)index;
0427 const Vector3 bField{Bx, By, Bz};
0428
0429 auto planeSurfaceA = MAKE_SURFACE();
0430
0431
0432 Transform3 transform;
0433 transform = planeSurfaceA->transform(gctx).rotation();
0434
0435 Vector3 axis = planeSurfaceA->transform(gctx).rotation() * Vector3::UnitX();
0436 transform = AngleAxis3(angle, axis) * transform;
0437
0438 transform.translation() = planeSurfaceA->transform(gctx).translation();
0439
0440 auto planeSurfaceB = Surface::makeShared<PlaneSurface>(transform);
0441
0442 BoundVector parA;
0443
0444 parA << l0, 0, std::numbers::pi / 4., std::numbers::pi / 2. * 0.9, -1 / 1_GeV,
0445 5_ns;
0446
0447 BoundMatrix covA;
0448 covA.setZero();
0449 covA.diagonal() << 1, 2, 3, 4, 5, 6;
0450
0451 auto [parB, covB] =
0452 boundToBound(parA, covA, *planeSurfaceA, *planeSurfaceB, bField);
0453
0454
0455 auto [parA2, covA2] =
0456 boundToBound(parB, covB, *planeSurfaceB, *planeSurfaceA, bField);
0457 CHECK_CLOSE_ABS(parA, parA2, 1e-9);
0458
0459 CHECK_CLOSE_COVARIANCE(covA, covA2, 1e-6);
0460
0461 auto prop = makePropagator(bField);
0462 BoundMatrix J =
0463 numericalBoundToBoundJacobian(prop, parA, *planeSurfaceA, *planeSurfaceB);
0464 BoundMatrix covC = J * covA * J.transpose();
0465 CHECK_CLOSE_OR_SMALL((covB.template topLeftCorner<2, 2>()),
0466 (covC.template topLeftCorner<2, 2>()), 1e-6, 1e-9);
0467 CHECK_CLOSE_OR_SMALL(covB.diagonal(), covC.diagonal(), 1e-6, 1e-9);
0468 }
0469
0470 BOOST_DATA_TEST_CASE(CovarianceConversionPerigee,
0471 (bFieldDist ^ bFieldDist ^ bFieldDist ^ angleDist ^
0472 angleDist ^ angleDist ^ posDist ^ posDist ^ posDist ^
0473 locDist ^ locDist ^ angleDist ^ angleDist ^ angleDist) ^
0474 bdata::xrange(100),
0475 Bx, By, Bz, Rx, Ry, Rz, gx, gy, gz, l0, l1, pRx, pRy, pRz,
0476 index) {
0477 (void)index;
0478 const Vector3 bField{Bx, By, Bz};
0479
0480 auto planeSurfaceA = MAKE_SURFACE();
0481
0482 BoundVector parA;
0483 parA << l0, l1, std::numbers::pi / 4., std::numbers::pi / 2. * 0.9,
0484 -1 / 1_GeV, 5_ns;
0485
0486 BoundMatrix covA;
0487 covA.setZero();
0488 covA.diagonal() << 1, 2, 3, 4, 5, 6;
0489
0490 Vector3 global = planeSurfaceA->localToGlobal(gctx, parA.head<2>());
0491
0492 Transform3 transform;
0493 transform.setIdentity();
0494 transform.rotate(AngleAxis3(pRx, Vector3::UnitX()));
0495 transform.rotate(AngleAxis3(pRy, Vector3::UnitY()));
0496 transform.rotate(AngleAxis3(pRz, Vector3::UnitZ()));
0497 transform.translation() = global;
0498
0499 auto perigee = Surface::makeShared<PerigeeSurface>(transform);
0500
0501 auto [parB, covB] =
0502 boundToBound(parA, covA, *planeSurfaceA, *perigee, bField);
0503
0504
0505 auto [parA2, covA2] =
0506 boundToBound(parB, covB, *perigee, *planeSurfaceA, bField);
0507 CHECK_CLOSE_ABS(parA, parA2, 1e-9);
0508 CHECK_CLOSE_COVARIANCE(covA, covA2, 1e-9);
0509
0510 auto prop = makePropagator(bField);
0511 BoundMatrix J =
0512 numericalBoundToBoundJacobian(prop, parA, *planeSurfaceA, *perigee);
0513 BoundMatrix covC = J * covA * J.transpose();
0514 CHECK_CLOSE_ABS(covB, covC, 1e-7);
0515 }
0516
0517 }