File indexing completed on 2025-11-17 08:55:30
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Definitions/TrackParametrization.hpp"
0012 #include "Acts/EventData/Charge.hpp"
0013 #include "Acts/EventData/GenericBoundTrackParameters.hpp"
0014 #include "Acts/EventData/MultiTrajectory.hpp"
0015 #include "Acts/EventData/TrackStatePropMask.hpp"
0016 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0017 #include "Acts/EventData/VectorTrackContainer.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/Propagator/detail/CovarianceEngine.hpp"
0020 #include "Acts/Propagator/detail/JacobianEngine.hpp"
0021 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0022 #include "Acts/Surfaces/PerigeeSurface.hpp"
0023 #include "Acts/Surfaces/PlaneSurface.hpp"
0024 #include "Acts/Surfaces/Surface.hpp"
0025 #include "Acts/Utilities/Logger.hpp"
0026 #include "Acts/Utilities/TrackHelpers.hpp"
0027 #include "Acts/Utilities/Zip.hpp"
0028 #include "ActsPlugins/EDM4hep/EDM4hepUtil.hpp"
0029 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0030
0031 #include <algorithm>
0032 #include <iostream>
0033 #include <numbers>
0034 #include <random>
0035
0036 #include <edm4hep/TrackCollection.h>
0037
0038 using namespace Acts;
0039 using namespace Acts::UnitLiterals;
0040 using namespace ActsPlugins;
0041
0042 namespace ActsTests {
0043
0044 BOOST_AUTO_TEST_SUITE(EDM4HepSuite)
0045
0046 BOOST_AUTO_TEST_CASE(JacobianRoundtrip) {
0047 BoundVector par;
0048 par << 1_mm, 5_mm, 0.1, std::numbers::pi / 2. * 0.9, -1 / 1_GeV, 5_ns;
0049
0050 BoundMatrix cov;
0051 cov.setIdentity();
0052
0053 double Bz = 2_T;
0054
0055 double tanLambda = std::tan(std::numbers::pi / 2. - par[eBoundTheta]);
0056 double omega = par[eBoundQOverP] / std::sin(par[eBoundTheta]) * Bz;
0057
0058 auto J1 = EDM4hepUtil::detail::jacobianToEdm4hep(par[eBoundTheta],
0059 par[eBoundQOverP], Bz);
0060
0061 BoundMatrix cov2 = J1 * cov * J1.transpose();
0062
0063 auto J2 = EDM4hepUtil::detail::jacobianFromEdm4hep(tanLambda, omega, Bz);
0064
0065 BoundMatrix cov3 = J2 * cov2 * J2.transpose();
0066
0067 CHECK_CLOSE_ABS(cov, cov3, 1e-9);
0068 }
0069
0070 BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithPerigee) {
0071 auto refSurface = Surface::makeShared<PerigeeSurface>(Vector3{50, 30, 20});
0072
0073 BoundVector par;
0074 par << 1_mm, 5_mm, 0, std::numbers::pi / 2., -1 / 1_GeV,
0075 5_ns;
0076
0077 BoundMatrix cov;
0078 cov.setIdentity();
0079 cov(5, 5) = 25_ns;
0080
0081 BoundTrackParameters boundPar{refSurface, par, cov,
0082 ParticleHypothesis::pion()};
0083
0084 double Bz = 2_T;
0085
0086 GeometryContext gctx;
0087
0088 EDM4hepUtil::detail::Parameters converted =
0089 EDM4hepUtil::detail::convertTrackParametersToEdm4hep(gctx, Bz, boundPar);
0090
0091 BOOST_CHECK(converted.covariance.has_value());
0092 BOOST_CHECK(converted.surface);
0093
0094
0095 BOOST_CHECK_EQUAL(par.template head<2>(),
0096 converted.values.template head<2>());
0097 BOOST_CHECK_EQUAL(
0098 (converted.covariance.value().template topLeftCorner<4, 4>()),
0099 ActsSquareMatrix<4>::Identity());
0100 BOOST_CHECK_GT(converted.covariance.value()(4, 4), 0);
0101 BOOST_CHECK_EQUAL(converted.covariance.value()(5, 5), 25_ns);
0102
0103
0104
0105 BoundTrackParameters roundtripPar =
0106 EDM4hepUtil::detail::convertTrackParametersFromEdm4hep(Bz, converted);
0107
0108 BOOST_CHECK(roundtripPar.parameters().isApprox(boundPar.parameters()));
0109 BOOST_CHECK(roundtripPar.covariance().value().isApprox(
0110 boundPar.covariance().value()));
0111 }
0112
0113 BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithOutPerigee) {
0114 std::shared_ptr<PlaneSurface> planeSurface =
0115 CurvilinearSurface(Vector3{50, 30, 20}, Vector3{1, 1, 0.3}.normalized())
0116 .planeSurface();
0117
0118 BoundVector par;
0119 par << 1_mm, 5_mm, std::numbers::pi / 4., std::numbers::pi / 2. * 0.9,
0120 -1 / 1_GeV, 5_ns;
0121
0122 BoundMatrix cov;
0123 cov.setIdentity();
0124 cov(5, 5) = 25_ns;
0125
0126 BoundTrackParameters planePar{planeSurface, par, cov,
0127 ParticleHypothesis::pion()};
0128
0129 double Bz = 2_T;
0130
0131 GeometryContext gctx;
0132
0133 EDM4hepUtil::detail::Parameters converted =
0134 EDM4hepUtil::detail::convertTrackParametersToEdm4hep(gctx, Bz, planePar);
0135
0136 BOOST_CHECK(converted.covariance.has_value());
0137 BOOST_CHECK(converted.surface);
0138
0139
0140 BOOST_CHECK_EQUAL(converted.values.template head<2>(), (Vector2{0, 0}));
0141 CHECK_CLOSE_ABS(converted.values[2], par[2], 1e-6);
0142
0143 BOOST_CHECK_EQUAL(converted.covariance.value()(0, 0), 1);
0144
0145 BOOST_CHECK_LT(converted.covariance.value()(1, 1), 1.2);
0146 BOOST_CHECK_GT(converted.covariance.value()(1, 1), 1);
0147
0148 CHECK_CLOSE_ABS(converted.covariance.value()(2, 2), 1, 1e-6);
0149
0150 BOOST_CHECK_GT(converted.covariance.value()(3, 3), 1);
0151 BOOST_CHECK_LT(converted.covariance.value()(3, 3), 1.2);
0152
0153 BOOST_CHECK_GT(converted.covariance.value()(4, 4), 0);
0154 BOOST_CHECK_EQUAL(converted.covariance.value()(5, 5), 25_ns);
0155
0156
0157 BoundTrackParameters roundtripPar =
0158 EDM4hepUtil::detail::convertTrackParametersFromEdm4hep(Bz, converted);
0159
0160 BOOST_CHECK_NE(
0161 dynamic_cast<const PerigeeSurface*>(&roundtripPar.referenceSurface()),
0162 nullptr);
0163
0164 BOOST_CHECK((converted.covariance.value().topLeftCorner<3, 3>().isApprox(
0165 roundtripPar.covariance().value().topLeftCorner<3, 3>())));
0166 CHECK_CLOSE_ABS(roundtripPar.covariance().value()(3, 3), 1, 1e-6);
0167 CHECK_CLOSE_ABS(roundtripPar.covariance().value()(4, 4), 1, 1e-6);
0168 BOOST_CHECK_EQUAL(roundtripPar.covariance().value()(5, 5), 25_ns);
0169
0170 auto roundtripPlaneBoundParams =
0171 detail::boundToBoundConversion(gctx, roundtripPar, *planeSurface).value();
0172
0173 BOOST_CHECK(roundtripPlaneBoundParams.parameters().isApprox(par));
0174
0175 CHECK_CLOSE_COVARIANCE(roundtripPlaneBoundParams.covariance().value(),
0176 planePar.covariance().value(), 1e-3);
0177 }
0178
0179 BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithPerigeeNoCov) {
0180 auto refSurface = Surface::makeShared<PerigeeSurface>(Vector3{50, 30, 20});
0181
0182 BoundVector par;
0183 par << 1_mm, 5_mm, 0, std::numbers::pi / 2., -1 / 1_GeV,
0184 5_ns;
0185
0186 BoundTrackParameters boundPar{refSurface, par, std::nullopt,
0187 ParticleHypothesis::pion()};
0188
0189 double Bz = 2_T;
0190
0191 GeometryContext gctx;
0192
0193 EDM4hepUtil::detail::Parameters converted =
0194 EDM4hepUtil::detail::convertTrackParametersToEdm4hep(gctx, Bz, boundPar);
0195
0196 BOOST_CHECK(!converted.covariance.has_value());
0197 BOOST_CHECK(converted.surface);
0198
0199
0200 BOOST_CHECK_EQUAL(par.template head<2>(),
0201 converted.values.template head<2>());
0202
0203
0204
0205 BoundTrackParameters roundtripPar =
0206 EDM4hepUtil::detail::convertTrackParametersFromEdm4hep(Bz, converted);
0207
0208 BOOST_CHECK(roundtripPar.parameters().isApprox(boundPar.parameters()));
0209 BOOST_CHECK(!roundtripPar.covariance().has_value());
0210 }
0211
0212 BOOST_AUTO_TEST_CASE(ConvertTrackParametersToEdm4hepWithOutPerigeeNoCov) {
0213 std::shared_ptr<PlaneSurface> refSurface =
0214 CurvilinearSurface(Vector3{50, 30, 20}, Vector3{1, 1, 0.3}.normalized())
0215 .planeSurface();
0216
0217 BoundVector par;
0218 par << 1_mm, 5_mm, std::numbers::pi / 4., std::numbers::pi / 2., -1 / 1_GeV,
0219 5_ns;
0220
0221 BoundTrackParameters boundPar{refSurface, par, std::nullopt,
0222 ParticleHypothesis::pion()};
0223
0224 double Bz = 2_T;
0225
0226 GeometryContext gctx;
0227
0228 EDM4hepUtil::detail::Parameters converted =
0229 EDM4hepUtil::detail::convertTrackParametersToEdm4hep(gctx, Bz, boundPar);
0230
0231 BOOST_CHECK(!converted.covariance.has_value());
0232 BOOST_CHECK(converted.surface);
0233
0234
0235 BOOST_CHECK_EQUAL(converted.values.template head<2>(), (Vector2{0, 0}));
0236 CHECK_CLOSE_ABS(converted.values[2], par[2], 1e-6);
0237
0238
0239 BoundTrackParameters roundtripPar =
0240 EDM4hepUtil::detail::convertTrackParametersFromEdm4hep(Bz, converted);
0241
0242 BOOST_CHECK_EQUAL(roundtripPar.parameters().template head<2>(),
0243 (Vector2{0, 0}));
0244 BOOST_CHECK(roundtripPar.parameters().tail<4>().isApprox(par.tail<4>()));
0245 BOOST_CHECK(!roundtripPar.covariance().has_value());
0246 }
0247
0248 BOOST_AUTO_TEST_CASE(CovariancePacking) {
0249 BoundMatrix m;
0250
0251 m << 1, 2, 3, 4, 5, 6,
0252 2, 2, 3, 4, 5, 6,
0253 3, 3, 3, 4, 5, 6,
0254 4, 4, 4, 4, 5, 6,
0255 5, 5, 5, 5, 5, 6,
0256 6, 6, 6, 6, 6, 6;
0257
0258
0259 std::array<float, 21> values{};
0260 EDM4hepUtil::detail::packCovariance(m, values.data());
0261
0262 BoundMatrix m2;
0263 m2.setZero();
0264 EDM4hepUtil::detail::unpackCovariance(values.data(), m2);
0265
0266 CHECK_CLOSE_ABS(m, m2, 1e-9);
0267 }
0268
0269 BOOST_AUTO_TEST_CASE(RoundTripTests) {
0270 auto trackContainer = std::make_shared<VectorTrackContainer>();
0271 auto trackStateContainer = std::make_shared<VectorMultiTrajectory>();
0272 TrackContainer tracks(trackContainer, trackStateContainer);
0273
0274 std::mt19937 rng{42};
0275 std::normal_distribution<double> gauss(0., 1.);
0276 std::uniform_real_distribution<double> f(-1, 1);
0277 std::uniform_real_distribution<double> r(0, 1);
0278 std::uniform_int_distribution<std::uint32_t> nTracks(2, 20);
0279 std::uniform_int_distribution<std::uint32_t> nTs(1, 20);
0280 std::uniform_real_distribution<double> phiDist(-std::numbers::pi,
0281 std::numbers::pi);
0282 std::uniform_real_distribution<double> etaDist(-4, 4);
0283 std::uniform_real_distribution<double> ptDist(1_MeV, 10_GeV);
0284 std::uniform_real_distribution<double> qDist(0., 1.);
0285
0286 auto genParams = [&]() -> std::pair<BoundVector, BoundMatrix> {
0287 double d0 = 20_um * gauss(rng);
0288 double z0 = 20_mm * gauss(rng);
0289 double phi = phiDist(rng);
0290 double eta = etaDist(rng);
0291 double theta = 2 * atan(exp(-eta));
0292 double pt = ptDist(rng);
0293 double p = pt / sin(theta);
0294 double charge = qDist(rng) > 0.5 ? 1. : -1.;
0295 double qop = charge / p;
0296 double t = 5_ns * gauss(rng);
0297
0298 BoundVector par;
0299 par << d0, z0, phi, theta, qop, t;
0300 BoundMatrix cov;
0301 cov = BoundMatrix::Identity();
0302 cov.diagonal() << 20_um * 20_um, 20_mm * 20_mm, 0.1, 0.1, 1_GeV, 25_ns;
0303 return {par, cov};
0304 };
0305
0306 std::uint32_t numT = nTracks(rng);
0307 for (std::uint32_t t = 0; t < numT; t++) {
0308 auto track = tracks.makeTrack();
0309 {
0310 auto [par, cov] = genParams();
0311 track.parameters() = par;
0312 track.covariance() = cov;
0313 }
0314 track.setReferenceSurface(
0315 Surface::makeShared<PerigeeSurface>(Vector3{0, 0, 0}));
0316
0317 std::uint32_t numTs = nTs(rng);
0318 for (std::uint32_t i = 0; i < numTs; i++) {
0319 auto ts = track.appendTrackState(TrackStatePropMask::Smoothed);
0320 double crit = r(rng);
0321 if (crit < 0.1) {
0322 ts.typeFlags().set(TrackStateFlag::HoleFlag);
0323 continue;
0324 } else if (crit < 0.2) {
0325 ts.typeFlags().set(TrackStateFlag::OutlierFlag);
0326 continue;
0327 } else if (crit < 0.3) {
0328 ts.typeFlags().set(TrackStateFlag::SharedHitFlag);
0329 } else if (crit < 0.4) {
0330 ts.typeFlags().set(TrackStateFlag::MaterialFlag);
0331 continue;
0332 }
0333
0334 ts.typeFlags().set(TrackStateFlag::MeasurementFlag);
0335
0336 auto [par, cov] = genParams();
0337 ts.smoothed() = par;
0338 ts.smoothedCovariance() = cov;
0339 Vector3 pos;
0340 pos << 1000 * f(rng), 1000 * f(rng), 3000 * f(rng);
0341 ts.setReferenceSurface(Surface::makeShared<PerigeeSurface>(pos));
0342 }
0343
0344 calculateTrackQuantities(track);
0345 }
0346
0347 edm4hep::TrackCollection edm4hepTracks;
0348
0349 GeometryContext gctx;
0350
0351 double Bz = 3_T;
0352
0353 auto logger = getDefaultLogger("EDM4hep", Logging::INFO);
0354
0355 for (const auto& track : tracks) {
0356 auto to = edm4hepTracks.create();
0357 EDM4hepUtil::writeTrack(gctx, track, to, Bz, *logger);
0358 }
0359
0360 BOOST_CHECK_EQUAL(edm4hepTracks.size(), tracks.size());
0361
0362 auto tIt = tracks.begin();
0363 for (auto edm4hepTrack : edm4hepTracks) {
0364 auto track = *tIt;
0365 BOOST_CHECK_EQUAL(track.nMeasurements(),
0366 edm4hepTrack.trackStates_size() - 1);
0367
0368 ++tIt;
0369 }
0370
0371 const edm4hep::TrackCollection& edm4hepTracksConst = edm4hepTracks;
0372
0373 TrackContainer readTracks(std::make_shared<VectorTrackContainer>(),
0374 std::make_shared<VectorMultiTrajectory>());
0375
0376 for (const auto edm4hepTrack : edm4hepTracksConst) {
0377 auto track = readTracks.makeTrack();
0378 EDM4hepUtil::readTrack(edm4hepTrack, track, Bz, *logger);
0379 }
0380
0381 BOOST_CHECK_EQUAL(tracks.size(), readTracks.size());
0382 std::size_t t = 0;
0383
0384 auto origTrackIt = tracks.begin();
0385 auto readTrackIt = readTracks.begin();
0386 while (origTrackIt != tracks.end() && readTrackIt != readTracks.end()) {
0387 BOOST_TEST_INFO_SCOPE("Track #" << t);
0388 auto orig = *origTrackIt;
0389 auto read = *readTrackIt;
0390
0391 CHECK_CLOSE_OR_SMALL(orig.parameters(), read.parameters(), 1e-5, 1e-8);
0392 CHECK_CLOSE_OR_SMALL(orig.covariance(), read.covariance(), 1e-5, 1e-8);
0393 BOOST_CHECK_EQUAL(orig.referenceSurface().center(gctx),
0394 read.referenceSurface().center(gctx));
0395
0396 auto origTsIt = orig.trackStatesReversed().begin();
0397 auto readTsIt = read.trackStatesReversed().begin();
0398
0399 std::size_t tsi = 0;
0400 while (origTsIt != orig.trackStatesReversed().end() &&
0401 readTsIt != read.trackStatesReversed().end()) {
0402 BOOST_TEST_INFO_SCOPE("TS: #" << tsi);
0403 auto nextMeas = std::find_if(
0404 origTsIt, orig.trackStatesReversed().end(), [](const auto& ts) {
0405 return ts.typeFlags().test(TrackStateFlag::MeasurementFlag);
0406 });
0407 BOOST_CHECK(nextMeas != orig.trackStatesReversed().end());
0408 origTsIt = nextMeas;
0409 auto origTs = *origTsIt;
0410 auto readTs = *readTsIt;
0411
0412 BOOST_TEST_INFO_SCOPE(
0413 "orig parameters: " << origTs.parameters().transpose());
0414 BOOST_TEST_INFO_SCOPE(
0415 "read parameters: " << readTs.parameters().transpose());
0416 CHECK_CLOSE_OR_SMALL(origTs.smoothedCovariance(),
0417 readTs.smoothedCovariance(), 1e-5, 1e-6);
0418 Vector3 newCenter = readTs.referenceSurface().center(
0419 gctx);
0420
0421 BOOST_CHECK(origTs.referenceSurface().isOnSurface(gctx, newCenter,
0422 Vector3::Zero()));
0423
0424
0425 Vector3 readGlobal = readTs.referenceSurface().localToGlobal(
0426 gctx, readTs.parameters().template head<2>(), Vector3::Zero());
0427 Vector3 origGlobal = origTs.referenceSurface().localToGlobal(
0428 gctx, origTs.parameters().template head<2>(), Vector3::Zero());
0429 CHECK_CLOSE_ABS(readGlobal, origGlobal, 1e-3);
0430 ++origTsIt;
0431 ++readTsIt;
0432 tsi++;
0433 }
0434 ++origTrackIt;
0435 ++readTrackIt;
0436
0437 t++;
0438 }
0439 }
0440
0441 BOOST_AUTO_TEST_SUITE_END()
0442
0443 }