Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-26 07:35:35

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/Units.hpp"
0012 #include "Acts/EventData/ProxyAccessor.hpp"
0013 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0014 #include "Acts/EventData/VectorTrackContainer.hpp"
0015 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0016 #include "Acts/Surfaces/PlaneSurface.hpp"
0017 #include "Acts/Utilities/Diagnostics.hpp"
0018 #include "Acts/Utilities/Zip.hpp"
0019 
0020 #include <algorithm>
0021 #include <numeric>
0022 
0023 using namespace Acts;
0024 using namespace Acts::HashedStringLiteral;
0025 using IndexType = TrackIndexType;
0026 constexpr auto kInvalid = kTrackIndexInvalid;
0027 using namespace Acts::UnitLiterals;
0028 
0029 template <typename track_container_t, typename traj_t,
0030           template <typename> class holder_t>
0031 struct Factory {};
0032 
0033 template <typename track_container_t, typename traj_t>
0034 struct Factory<track_container_t, traj_t, RefHolder> {
0035   using track_container_type =
0036       TrackContainer<track_container_t, traj_t, RefHolder>;
0037 
0038   track_container_t vtc;
0039   traj_t mtj;
0040   track_container_type tc{vtc, mtj};
0041 
0042   auto& trackContainer() { return tc; }
0043   auto& trackStateContainer() { return mtj; }
0044   auto& backend() { return vtc; }
0045 };
0046 
0047 template <typename track_container_t, typename traj_t>
0048 struct Factory<track_container_t, traj_t, ValueHolder> {
0049   using track_container_type =
0050       TrackContainer<track_container_t, traj_t, ValueHolder>;
0051 
0052   track_container_type tc{track_container_t{}, traj_t{}};
0053 
0054   auto& trackContainer() { return tc; }
0055   auto& trackStateContainer() { return tc.trackStateContainer(); }
0056   auto& backend() { return tc.container(); }
0057 };
0058 
0059 template <typename track_container_t, typename traj_t>
0060 struct Factory<track_container_t, traj_t, std::shared_ptr> {
0061   using track_container_type =
0062       TrackContainer<track_container_t, traj_t, std::shared_ptr>;
0063 
0064   std::shared_ptr<track_container_t> vtc{std::make_shared<track_container_t>()};
0065   std::shared_ptr<traj_t> mtj{std::make_shared<traj_t>()};
0066   track_container_type tc{vtc, mtj};
0067 
0068   auto& trackContainer() { return tc; }
0069   auto& trackStateContainer() { return *mtj; }
0070   auto& backend() { return *vtc; }
0071 };
0072 
0073 template <typename track_container_t, typename traj_t,
0074           template <typename> class... holders>
0075 using holder_types_t =
0076     std::tuple<Factory<track_container_t, traj_t, holders>...>;
0077 
0078 using holder_types = holder_types_t<VectorTrackContainer, VectorMultiTrajectory,
0079                                     // detail_tc::ValueHolder,
0080                                     // detail_tc::RefHolder,
0081                                     std::shared_ptr>;
0082 
0083 using const_holder_types =
0084     holder_types_t<ConstVectorTrackContainer, ConstVectorMultiTrajectory,
0085                    ValueHolder, RefHolder, std::shared_ptr>;
0086 
0087 namespace ActsTests {
0088 
0089 BOOST_AUTO_TEST_SUITE(EventDataSuite)
0090 
0091 BOOST_AUTO_TEST_CASE(BuildDefaultHolder) {
0092   VectorMultiTrajectory mtj{};
0093   VectorTrackContainer vtc{};
0094   TrackContainer tc{vtc, mtj};
0095 
0096   static_assert(
0097       std::is_same_v<decltype(tc),
0098                      TrackContainer<VectorTrackContainer, VectorMultiTrajectory,
0099                                     RefHolder>>,
0100       "Incorrect deduced type");
0101   BOOST_CHECK_EQUAL(&mtj, &tc.trackStateContainer());
0102   BOOST_CHECK_EQUAL(&vtc, &tc.container());
0103   tc.addTrack();
0104 
0105   std::decay_t<decltype(tc)> copy = tc;
0106   BOOST_CHECK_EQUAL(&mtj, &copy.trackStateContainer());
0107   BOOST_CHECK_EQUAL(&vtc, &copy.container());
0108 }
0109 
0110 BOOST_AUTO_TEST_CASE(BuildValueHolder) {
0111   {
0112     VectorMultiTrajectory mtj{};
0113     VectorTrackContainer vtc{};
0114     TrackContainer tc{std::move(vtc), std::move(mtj)};
0115     static_assert(
0116         std::is_same_v<decltype(tc),
0117                        TrackContainer<VectorTrackContainer,
0118                                       VectorMultiTrajectory, ValueHolder>>,
0119         "Incorrect deduced type");
0120     std::decay_t<decltype(tc)> copy = tc;
0121     BOOST_CHECK_NE(&tc.trackStateContainer(), &copy.trackStateContainer());
0122     BOOST_CHECK_NE(&tc.container(), &copy.container());
0123   }
0124   {
0125     TrackContainer tc{VectorTrackContainer{}, VectorMultiTrajectory{}};
0126 
0127     static_assert(
0128         std::is_same_v<decltype(tc),
0129                        TrackContainer<VectorTrackContainer,
0130                                       VectorMultiTrajectory, ValueHolder>>,
0131         "Incorrect deduced type");
0132     tc.addTrack();
0133     std::decay_t<decltype(tc)> copy = tc;
0134     BOOST_CHECK_NE(&tc.trackStateContainer(), &copy.trackStateContainer());
0135     BOOST_CHECK_NE(&tc.container(), &copy.container());
0136   }
0137 }
0138 
0139 BOOST_AUTO_TEST_CASE(BuildRefHolder) {
0140   VectorMultiTrajectory mtj{};
0141   VectorTrackContainer vtc{};
0142   TrackContainer<VectorTrackContainer, VectorMultiTrajectory, RefHolder> tc{
0143       vtc, mtj};
0144 
0145   static_assert(
0146       std::is_same_v<decltype(tc),
0147                      TrackContainer<VectorTrackContainer, VectorMultiTrajectory,
0148                                     RefHolder>>,
0149       "Incorrect deduced type");
0150   BOOST_CHECK_EQUAL(&mtj, &tc.trackStateContainer());
0151   BOOST_CHECK_EQUAL(&vtc, &tc.container());
0152   tc.addTrack();
0153   std::decay_t<decltype(tc)> copy = tc;
0154   BOOST_CHECK_EQUAL(&mtj, &copy.trackStateContainer());
0155   BOOST_CHECK_EQUAL(&vtc, &copy.container());
0156 }
0157 
0158 BOOST_AUTO_TEST_CASE(BuildSharedPtr) {
0159   auto mtj = std::make_shared<VectorMultiTrajectory>();
0160   auto vtc = std::make_shared<VectorTrackContainer>();
0161   TrackContainer<VectorTrackContainer, VectorMultiTrajectory, std::shared_ptr>
0162       tc{vtc, mtj};
0163 
0164   static_assert(
0165       std::is_same_v<decltype(tc),
0166                      TrackContainer<VectorTrackContainer, VectorMultiTrajectory,
0167                                     std::shared_ptr>>,
0168       "Incorrect deduced type");
0169   BOOST_CHECK_EQUAL(mtj.get(), &tc.trackStateContainer());
0170   BOOST_CHECK_EQUAL(vtc.get(), &tc.container());
0171   tc.addTrack();
0172   std::decay_t<decltype(tc)> copy = tc;
0173   BOOST_CHECK_EQUAL(mtj.get(), &copy.trackStateContainer());
0174   BOOST_CHECK_EQUAL(vtc.get(), &copy.container());
0175 }
0176 
0177 BOOST_AUTO_TEST_CASE(BuildConvenience) {
0178   VectorMultiTrajectory mtj{};
0179   VectorTrackContainer vtc{};
0180   TrackContainer tc{vtc, mtj};
0181 
0182   BOOST_CHECK_EQUAL(tc.size(), 0);
0183   auto track1 = tc.makeTrack();
0184   BOOST_CHECK_EQUAL(tc.size(), 1);
0185   auto track2 = tc.makeTrack();
0186   BOOST_CHECK_EQUAL(tc.size(), 2);
0187 
0188   BOOST_CHECK_EQUAL(track1.index(), 0);
0189   BOOST_CHECK_EQUAL(track2.index(), 1);
0190 }
0191 
0192 BOOST_AUTO_TEST_CASE_TEMPLATE(Build, factory_t, holder_types) {
0193   factory_t factory;
0194 
0195   auto& tc = factory.trackContainer();
0196 
0197   static_assert(std::is_same_v<std::decay_t<decltype(tc)>,
0198                                typename factory_t::track_container_type>,
0199                 "Incorrect deduction");
0200 
0201   static_assert(!std::decay_t<decltype(tc)>::ReadOnly,
0202                 "Should not be read only");
0203   BOOST_CHECK(!tc.ReadOnly);
0204 
0205   auto idx = tc.addTrack();
0206   auto t = tc.getTrack(idx);
0207   auto t2 = tc.getTrack(idx);
0208   t.template component<IndexType, "tipIndex"_hash>() = 5;
0209 
0210   BOOST_CHECK_EQUAL((t.template component<IndexType, "tipIndex"_hash>()), 5);
0211   BOOST_CHECK_EQUAL(t.tipIndex(), 5);
0212   t.tipIndex() = 6;
0213   BOOST_CHECK_EQUAL(t.tipIndex(), 6);
0214 
0215   BoundVector pars;
0216   pars.setRandom();
0217   t.parameters() = pars;
0218   BOOST_CHECK_EQUAL(t.parameters(), pars);
0219 
0220   BoundMatrix cov;
0221   cov.setRandom();
0222   t.covariance() = cov;
0223   BOOST_CHECK_EQUAL(t.covariance(), cov);
0224 
0225   std::shared_ptr<PlaneSurface> surface =
0226       CurvilinearSurface(Vector3{-3_m, 0., 0.}, Vector3{1., 0., 0})
0227           .planeSurface();
0228 
0229   t.setReferenceSurface(surface);
0230   BOOST_CHECK_EQUAL(surface.get(), &t.referenceSurface());
0231 
0232   ProxyAccessor<unsigned int> accNMeasuements("nMeasurements");
0233   ConstProxyAccessor<unsigned int> caccNMeasuements("nMeasurements");
0234 
0235   t.nMeasurements() = 42;
0236   BOOST_CHECK_EQUAL(t2.nMeasurements(), 42);
0237   BOOST_CHECK_EQUAL(accNMeasuements(t), 42);
0238   accNMeasuements(t) = 89;
0239   BOOST_CHECK_EQUAL(t2.nMeasurements(), 89);
0240   BOOST_CHECK_EQUAL(caccNMeasuements(t), 89);
0241 
0242   // Test hasColumn functionality
0243   BOOST_CHECK(accNMeasuements.hasColumn(t));
0244   BOOST_CHECK(caccNMeasuements.hasColumn(t));
0245 
0246   ProxyAccessor<std::string> accNonExistent("nonExistentColumn");
0247   ConstProxyAccessor<std::string> caccNonExistent("nonExistentColumn");
0248   BOOST_CHECK(!accNonExistent.hasColumn(t));
0249   BOOST_CHECK(!caccNonExistent.hasColumn(t));
0250 
0251   // does not compile
0252   // caccNMeasuements(t) = 66;
0253 
0254   t2.nHoles() = 67;
0255   BOOST_CHECK_EQUAL(t.nHoles(), 67);
0256 
0257   t2.nOutliers() = 68;
0258   BOOST_CHECK_EQUAL(t.nOutliers(), 68);
0259 
0260   t2.nSharedHits() = 69;
0261   BOOST_CHECK_EQUAL(t.nSharedHits(), 69);
0262 
0263   t2.chi2() = 555.0;
0264   BOOST_CHECK_EQUAL(t2.chi2(), 555.0);
0265 
0266   t2.nDoF() = 123;
0267   BOOST_CHECK_EQUAL(t2.nDoF(), 123);
0268 
0269   // const checks: should not compile
0270   // const auto& ctc = tc;
0271   // ctc.getTrack(idx).covariance().setRandom();
0272   // const auto& ctp = t;
0273   // ctp.covariance().setRandom();
0274 }
0275 
0276 BOOST_AUTO_TEST_CASE(CopyTracksIncludingDynamicColumns) {
0277   // mutable source
0278   VectorTrackContainer vtc{};
0279   VectorMultiTrajectory mtj{};
0280   TrackContainer tc{vtc, mtj};
0281   tc.addColumn<std::size_t>("counter");
0282   tc.addColumn<bool>("odd");
0283   mtj.addColumn<std::size_t>("ts_counter");
0284   mtj.addColumn<bool>("ts_odd");
0285 
0286   TrackContainer tc2{VectorTrackContainer{}, VectorMultiTrajectory{}};
0287   // doesn't have the dynamic column
0288 
0289   VectorTrackContainer vtc3{};
0290   VectorMultiTrajectory mtj3{};
0291   mtj3.addColumn<std::size_t>("ts_counter");
0292   mtj3.addColumn<bool>("ts_odd");
0293 
0294   TrackContainer tc3{vtc3, mtj3};
0295 
0296   tc3.addColumn<std::size_t>("counter");
0297   tc3.addColumn<bool>("odd");
0298 
0299   for (std::size_t i = 0; i < 10; i++) {
0300     auto t = tc.makeTrack();
0301     auto ts = t.appendTrackState();
0302     ts.predicted() = BoundVector::Ones();
0303     ts.component<std::size_t, "ts_counter"_hash>() = i;
0304 
0305     ts = t.appendTrackState();
0306     ts.predicted().setOnes();
0307     ts.predicted() *= 2;
0308     ts.component<std::size_t, "ts_counter"_hash>() = i + 1;
0309 
0310     ts = t.appendTrackState();
0311     ts.predicted().setOnes();
0312     ts.predicted() *= 3;
0313     ts.component<std::size_t, "ts_counter"_hash>() = i + 2;
0314 
0315     t.template component<std::size_t>("counter") = i;
0316     t.template component<bool>("odd") = i % 2 == 0;
0317 
0318     auto t2 = tc2.makeTrack();
0319     BOOST_CHECK_THROW(t2.copyFrom(t),
0320                       std::invalid_argument);  // this should fail
0321 
0322     auto t3 = tc3.makeTrack();
0323     t3.copyFrom(t);  // this should work
0324 
0325     BOOST_CHECK_NE(t3.tipIndex(), kInvalid);
0326     BOOST_CHECK_GT(t3.nTrackStates(), 0);
0327     BOOST_REQUIRE_EQUAL(t.nTrackStates(), t3.nTrackStates());
0328 
0329     for (auto [tsa, tsb] :
0330          zip(t.trackStatesReversed(), t3.trackStatesReversed())) {
0331       BOOST_CHECK_EQUAL(tsa.predicted(), tsb.predicted());
0332 
0333       BOOST_CHECK_EQUAL(
0334           (tsa.template component<std::size_t, "ts_counter"_hash>()),
0335           (tsb.template component<std::size_t, "ts_counter"_hash>()));
0336 
0337       BOOST_CHECK_EQUAL((tsa.template component<bool, "ts_odd"_hash>()),
0338                         (tsb.template component<bool, "ts_odd"_hash>()));
0339     }
0340 
0341     BOOST_CHECK_EQUAL(t.template component<std::size_t>("counter"),
0342                       t3.template component<std::size_t>("counter"));
0343     BOOST_CHECK_EQUAL(t.template component<bool>("odd"),
0344                       t3.template component<bool>("odd"));
0345   }
0346 
0347   std::size_t before = mtj.size();
0348   TrackContainer tc4{ConstVectorTrackContainer{vtc},
0349                      ConstVectorMultiTrajectory{mtj}};
0350 
0351   BOOST_REQUIRE_EQUAL(tc4.trackStateContainer().size(), before);
0352 
0353   VectorTrackContainer vtc5{};
0354   VectorMultiTrajectory mtj5{};
0355   mtj5.addColumn<std::size_t>("ts_counter");
0356   mtj5.addColumn<bool>("ts_odd");
0357 
0358   TrackContainer tc5{vtc5, mtj5};
0359   tc5.addColumn<std::size_t>("counter");
0360   tc5.addColumn<bool>("odd");
0361 
0362   for (std::size_t i = 0; i < 10; i++) {
0363     auto t4 = tc4.getTrack(i);  // const source!
0364     BOOST_CHECK_NE(t4.nTrackStates(), 0);
0365 
0366     auto t5 = tc5.makeTrack();
0367     t5.copyFrom(t4);  // this should work
0368 
0369     BOOST_CHECK_NE(t5.tipIndex(), kInvalid);
0370     BOOST_CHECK_GT(t5.nTrackStates(), 0);
0371     BOOST_REQUIRE_EQUAL(t4.nTrackStates(), t5.nTrackStates());
0372 
0373     for (auto [tsa, tsb] :
0374          zip(t4.trackStatesReversed(), t5.trackStatesReversed())) {
0375       BOOST_CHECK_EQUAL(tsa.predicted(), tsb.predicted());
0376     }
0377 
0378     BOOST_CHECK_EQUAL(t4.template component<std::size_t>("counter"),
0379                       t5.template component<std::size_t>("counter"));
0380     BOOST_CHECK_EQUAL(t4.template component<bool>("odd"),
0381                       t5.template component<bool>("odd"));
0382   }
0383 }
0384 
0385 BOOST_AUTO_TEST_CASE(ReverseTrackStates) {
0386   VectorTrackContainer vtc{};
0387   VectorMultiTrajectory mtj{};
0388   TrackContainer tc{vtc, mtj};
0389 
0390   auto t = tc.makeTrack();
0391 
0392   for (std::size_t i = 0; i < 4; i++) {
0393     auto ts = t.appendTrackState();
0394     ts.jacobian() = BoundMatrix::Identity() * i;
0395   }
0396 
0397   std::vector<IndexType> exp;
0398   exp.resize(t.nTrackStates());
0399   std::iota(exp.rbegin(), exp.rend(), 0);
0400   std::vector<IndexType> act;
0401   std::transform(t.trackStatesReversed().begin(), t.trackStatesReversed().end(),
0402                  std::back_inserter(act),
0403                  [](const auto& ts) { return ts.index(); });
0404 
0405   // jacobians count up
0406   for (const auto [e, ts] : zip(exp, t.trackStatesReversed())) {
0407     BOOST_CHECK_EQUAL(ts.jacobian(), BoundMatrix::Identity() * e);
0408   }
0409 
0410   BOOST_CHECK_EQUAL_COLLECTIONS(exp.begin(), exp.end(), act.begin(), act.end());
0411 
0412   // reverse!
0413   t.reverseTrackStates();
0414 
0415   std::iota(exp.begin(), exp.end(), 0);
0416   act.clear();
0417   std::transform(t.trackStatesReversed().begin(), t.trackStatesReversed().end(),
0418                  std::back_inserter(act),
0419                  [](const auto& ts) { return ts.index(); });
0420   BOOST_CHECK_EQUAL_COLLECTIONS(exp.begin(), exp.end(), act.begin(), act.end());
0421 
0422   // jacobians stay with their track states
0423   for (const auto [e, ts] : zip(exp, t.trackStatesReversed())) {
0424     BOOST_CHECK_EQUAL(ts.jacobian(), BoundMatrix::Identity() * e);
0425   }
0426 
0427   // back to original!
0428   t.reverseTrackStates();
0429 
0430   // jacobians stay with their track states
0431   for (const auto [e, ts] : zip(exp, t.trackStates())) {
0432     BOOST_CHECK_EQUAL(ts.jacobian(), BoundMatrix::Identity() * e);
0433   }
0434 
0435   // reverse with jacobians
0436   t.reverseTrackStates(true);
0437 
0438   std::ranges::reverse(exp);
0439   std::rotate(exp.rbegin(), std::next(exp.rbegin()), exp.rend());
0440 
0441   for (const auto [e, ts] : zip(exp, t.trackStates())) {
0442     Acts::BoundMatrix expJac;
0443     if (e == 0) {
0444       expJac = BoundMatrix::Zero();
0445     } else {
0446       expJac = (BoundMatrix::Identity() * e).inverse();
0447     }
0448 
0449     BOOST_CHECK_EQUAL(ts.jacobian(), expJac);
0450   }
0451 
0452   // now back to original order, revert jacobians again
0453   t.reverseTrackStates(true);
0454 
0455   // reset exp to range(0, N)
0456   std::iota(exp.begin(), exp.end(), 0);
0457 
0458   for (const auto [e, ts] : zip(exp, t.trackStates())) {
0459     BOOST_CHECK_EQUAL(ts.jacobian(), BoundMatrix::Identity() * e);
0460   }
0461 }
0462 
0463 BOOST_AUTO_TEST_CASE(CopyTrackProxyCalibrated) {
0464   VectorTrackContainer vtc{};
0465   VectorMultiTrajectory mtj{};
0466   TrackContainer tc{vtc, mtj};
0467 
0468   constexpr static std::size_t kMeasurementSize = 3;
0469 
0470   auto track1 = tc.makeTrack();
0471   auto ts = track1.appendTrackState(TrackStatePropMask::Calibrated);
0472   ts.allocateCalibrated(kMeasurementSize);
0473   ts.setProjectorSubspaceIndices(BoundSubspaceIndices{});
0474 
0475   auto tsCopy = track1.appendTrackState(TrackStatePropMask::Calibrated);
0476   tsCopy.copyFrom(ts, TrackStatePropMask::Calibrated, false);
0477 
0478   BOOST_CHECK_EQUAL(ts.calibratedSize(), tsCopy.calibratedSize());
0479 }
0480 
0481 BOOST_AUTO_TEST_CASE(ProxyAccessorHasColumn) {
0482   VectorTrackContainer vtc{};
0483   VectorMultiTrajectory mtj{};
0484   TrackContainer tc{vtc, mtj};
0485 
0486   // Add a custom column to the track state container
0487   mtj.addColumn<float>("customFloat");
0488   // Add a custom column to the track container
0489   tc.addColumn<std::string>("customString");
0490 
0491   auto track = tc.makeTrack();
0492   auto ts = track.appendTrackState(TrackStatePropMask::Predicted);
0493   ts.predicted() = BoundVector::Zero();
0494 
0495   // Set values in the custom columns
0496   ts.component<float>("customFloat") = 42.5f;
0497   track.component<std::string>("customString") = "test";
0498 
0499   // Test ALL known track container columns
0500   // From VectorTrackContainer.hpp hasColumn_impl
0501   BOOST_CHECK(ConstProxyAccessor<IndexType>("tipIndex").hasColumn(track));
0502   BOOST_CHECK(ConstProxyAccessor<IndexType>("stemIndex").hasColumn(track));
0503   BOOST_CHECK(ConstProxyAccessor<BoundVector>("params").hasColumn(track));
0504   BOOST_CHECK(ConstProxyAccessor<BoundMatrix>("cov").hasColumn(track));
0505   BOOST_CHECK(
0506       ConstProxyAccessor<unsigned int>("nMeasurements").hasColumn(track));
0507   BOOST_CHECK(ConstProxyAccessor<unsigned int>("nHoles").hasColumn(track));
0508   BOOST_CHECK(ConstProxyAccessor<float>("chi2").hasColumn(track));
0509   BOOST_CHECK(ConstProxyAccessor<unsigned int>("ndf").hasColumn(track));
0510   BOOST_CHECK(ConstProxyAccessor<unsigned int>("nOutliers").hasColumn(track));
0511   BOOST_CHECK(ConstProxyAccessor<unsigned int>("nSharedHits").hasColumn(track));
0512 
0513   // Test custom track container column
0514   BOOST_CHECK(ConstProxyAccessor<std::string>("customString").hasColumn(track));
0515 
0516   // Test ALL known track state columns
0517   // From VectorMultiTrajectory.hpp hasColumn_impl
0518   BOOST_CHECK(ConstProxyAccessor<IndexType>("previous").hasColumn(ts));
0519   BOOST_CHECK(ConstProxyAccessor<IndexType>("next").hasColumn(ts));
0520   BOOST_CHECK(ConstProxyAccessor<IndexType>("predicted").hasColumn(ts));
0521   BOOST_CHECK(ConstProxyAccessor<IndexType>("filtered").hasColumn(ts));
0522   BOOST_CHECK(ConstProxyAccessor<IndexType>("smoothed").hasColumn(ts));
0523   BOOST_CHECK(ConstProxyAccessor<IndexType>("calibrated").hasColumn(ts));
0524   BOOST_CHECK(ConstProxyAccessor<IndexType>("calibratedCov").hasColumn(ts));
0525   BOOST_CHECK(ConstProxyAccessor<IndexType>("jacobian").hasColumn(ts));
0526   BOOST_CHECK(ConstProxyAccessor<IndexType>("projector").hasColumn(ts));
0527   BOOST_CHECK(
0528       ConstProxyAccessor<std::optional<SourceLink>>("uncalibratedSourceLink")
0529           .hasColumn(ts));
0530   BOOST_CHECK(
0531       ConstProxyAccessor<std::shared_ptr<const Surface>>("referenceSurface")
0532           .hasColumn(ts));
0533   BOOST_CHECK(ConstProxyAccessor<std::uint8_t>("measdim").hasColumn(ts));
0534   BOOST_CHECK(ConstProxyAccessor<float>("chi2").hasColumn(ts));
0535   BOOST_CHECK(ConstProxyAccessor<double>("pathLength").hasColumn(ts));
0536   BOOST_CHECK(
0537       ConstProxyAccessor<TrackStateType::raw_type>("typeFlags").hasColumn(ts));
0538 
0539   // Test custom track state column
0540   BOOST_CHECK(ConstProxyAccessor<float>("customFloat").hasColumn(ts));
0541 
0542   // Test with non-existent columns
0543   BOOST_CHECK(!ConstProxyAccessor<int>("nonExistentColumn").hasColumn(ts));
0544   BOOST_CHECK(!ConstProxyAccessor<std::string>("nonExistentTrackColumn")
0545                    .hasColumn(track));
0546 
0547   // Test that we can actually access the custom columns
0548   ProxyAccessor<float> accCustomFloat("customFloat");
0549   ConstProxyAccessor<float> caccCustomFloat("customFloat");
0550   BOOST_CHECK_EQUAL(accCustomFloat(ts), 42.5f);
0551   BOOST_CHECK_EQUAL(caccCustomFloat(ts), 42.5f);
0552 
0553   ProxyAccessor<std::string> accCustomString("customString");
0554   ConstProxyAccessor<std::string> caccCustomString("customString");
0555   BOOST_CHECK_EQUAL(accCustomString(track), "test");
0556   BOOST_CHECK_EQUAL(caccCustomString(track), "test");
0557 }
0558 
0559 consteval ProxyAccessor<int> makeProxyAccessor() {
0560   return ProxyAccessor<int>("test_consteval");
0561 }
0562 
0563 BOOST_AUTO_TEST_CASE(ProxyAccessorConstexprConstruction) {
0564   static_assert(ProxyAccessor<int>("test").key == "test"_hash,
0565                 "ProxyAccessor should be constructible with a string key");
0566 
0567   constexpr auto acc = makeProxyAccessor();
0568 
0569   static_assert(acc.key == "test_consteval"_hash,
0570                 "ProxyAccessor should be constructible at compile time");
0571 }
0572 
0573 BOOST_AUTO_TEST_CASE(CopyFromWithoutStatesInvalidatesIndices) {
0574   VectorTrackContainer vtc{};
0575   VectorMultiTrajectory mtj{};
0576   TrackContainer tc{vtc, mtj};
0577 
0578   // Create source track with track states
0579   auto sourceTrack = tc.makeTrack();
0580   sourceTrack.appendTrackState();
0581   sourceTrack.appendTrackState();
0582   sourceTrack.appendTrackState();
0583   sourceTrack.linkForward();
0584 
0585   // Verify source track has valid indices
0586   BOOST_CHECK_NE(sourceTrack.tipIndex(), kInvalid);
0587   BOOST_CHECK_NE(sourceTrack.stemIndex(), kInvalid);
0588   BOOST_CHECK_EQUAL(sourceTrack.nTrackStates(), 3);
0589 
0590   // Set some track properties
0591   sourceTrack.nMeasurements() = 42;
0592   sourceTrack.nHoles() = 5;
0593   sourceTrack.chi2() = 123.45f;
0594 
0595   // Create destination track that also has track states initially
0596   auto destTrack = tc.makeTrack();
0597   destTrack.appendTrackState();
0598   destTrack.appendTrackState();
0599   destTrack.linkForward();
0600 
0601   // Verify destination has valid indices before copy
0602   BOOST_CHECK_NE(destTrack.tipIndex(), kInvalid);
0603   BOOST_CHECK_NE(destTrack.stemIndex(), kInvalid);
0604   BOOST_CHECK_EQUAL(destTrack.nTrackStates(), 2);
0605 
0606   // Copy without states
0607   destTrack.copyFromWithoutStates(sourceTrack);
0608 
0609   // Verify that tip and stem indices are now invalid
0610   BOOST_CHECK_EQUAL(destTrack.tipIndex(), kInvalid);
0611   BOOST_CHECK_EQUAL(destTrack.stemIndex(), kInvalid);
0612 
0613   // Verify that track properties were copied
0614   BOOST_CHECK_EQUAL(destTrack.nMeasurements(), 42);
0615   BOOST_CHECK_EQUAL(destTrack.nHoles(), 5);
0616   BOOST_CHECK_EQUAL(destTrack.chi2(), 123.45f);
0617 
0618   // Verify that nTrackStates returns 0 since indices are invalid
0619   BOOST_CHECK_EQUAL(destTrack.nTrackStates(), 0);
0620 
0621   // Verify that the original track states are still in the container
0622   // but not accessible through the destination track
0623   BOOST_CHECK_EQUAL(sourceTrack.nTrackStates(), 3);
0624   BOOST_CHECK_NE(sourceTrack.tipIndex(), kInvalid);
0625 }
0626 
0627 BOOST_AUTO_TEST_CASE(CopyFromDeepCopyFunctionality) {
0628   VectorTrackContainer vtc{};
0629   VectorMultiTrajectory mtj{};
0630   TrackContainer tc{vtc, mtj};
0631 
0632   // Create source track with track states and set unique data
0633   auto sourceTrack = tc.makeTrack();
0634 
0635   // Set track-level properties
0636   sourceTrack.nMeasurements() = 15;
0637   sourceTrack.nHoles() = 3;
0638   sourceTrack.nOutliers() = 2;
0639   sourceTrack.chi2() = 42.5f;
0640   sourceTrack.nDoF() = 10;
0641 
0642   // Create track states with distinctive predicted parameters
0643   auto ts1 = sourceTrack.appendTrackState();
0644   ts1.predicted() = BoundVector::Ones() * 1.0;
0645 
0646   auto ts2 = sourceTrack.appendTrackState();
0647   ts2.predicted() = BoundVector::Ones() * 2.0;
0648 
0649   auto ts3 = sourceTrack.appendTrackState();
0650   ts3.predicted() = BoundVector::Ones() * 3.0;
0651 
0652   // Verify source track setup
0653   BOOST_CHECK_EQUAL(sourceTrack.nTrackStates(), 3);
0654 
0655   // Create destination track and perform deep copy
0656   auto destTrack = tc.makeTrack();
0657   destTrack.copyFrom(sourceTrack);
0658 
0659   // Verify track-level properties were copied
0660   BOOST_CHECK_EQUAL(destTrack.nMeasurements(), 15);
0661   BOOST_CHECK_EQUAL(destTrack.nHoles(), 3);
0662   BOOST_CHECK_EQUAL(destTrack.nOutliers(), 2);
0663   BOOST_CHECK_EQUAL(destTrack.chi2(), 42.5f);
0664   BOOST_CHECK_EQUAL(destTrack.nDoF(), 10);
0665 
0666   // Verify track state structure
0667   BOOST_CHECK_EQUAL(destTrack.nTrackStates(), 3);
0668   BOOST_CHECK_NE(destTrack.tipIndex(), kInvalid);
0669   BOOST_CHECK_NE(destTrack.stemIndex(), kInvalid);
0670 
0671   // Verify track is forward-linked (can iterate forward)
0672   BOOST_CHECK(destTrack.innermostTrackState().has_value());
0673 
0674   // Verify track state data was copied correctly (order preserved)
0675   std::vector<BoundVector> sourceParams;
0676   for (const auto& ts : sourceTrack.trackStatesReversed()) {
0677     sourceParams.insert(sourceParams.begin(), ts.predicted());
0678   }
0679 
0680   std::vector<BoundVector> destParams;
0681   for (const auto& ts : destTrack.trackStatesReversed()) {
0682     destParams.insert(destParams.begin(), ts.predicted());
0683   }
0684 
0685   BOOST_REQUIRE_EQUAL(sourceParams.size(), destParams.size());
0686   for (std::size_t i = 0; i < sourceParams.size(); ++i) {
0687     BOOST_CHECK_EQUAL(sourceParams[i], destParams[i]);
0688   }
0689 
0690   // Verify track states have different indices (new track states were created)
0691   std::vector<IndexType> sourceIndices;
0692   for (const auto& ts : sourceTrack.trackStatesReversed()) {
0693     sourceIndices.push_back(ts.index());
0694   }
0695 
0696   std::vector<IndexType> destIndices;
0697   for (const auto& ts : destTrack.trackStatesReversed()) {
0698     destIndices.push_back(ts.index());
0699   }
0700 
0701   BOOST_REQUIRE_EQUAL(sourceIndices.size(), destIndices.size());
0702   for (std::size_t i = 0; i < sourceIndices.size(); ++i) {
0703     BOOST_CHECK_NE(sourceIndices[i], destIndices[i]);
0704   }
0705 
0706   // Verify forward iteration works (result of forward-linking)
0707   std::vector<BoundVector> forwardParams;
0708   for (const auto& ts : destTrack.trackStates()) {
0709     forwardParams.push_back(ts.predicted());
0710   }
0711 
0712   // Forward iteration should give same parameters in same order
0713   BOOST_REQUIRE_EQUAL(destParams.size(), forwardParams.size());
0714   for (std::size_t i = 0; i < destParams.size(); ++i) {
0715     BOOST_CHECK_EQUAL(destParams[i], forwardParams[i]);
0716   }
0717 
0718   // Verify tracks are independent (modifying dest doesn't affect source)
0719   destTrack.nMeasurements() = 99;
0720   BOOST_CHECK_EQUAL(sourceTrack.nMeasurements(), 15);
0721   BOOST_CHECK_EQUAL(destTrack.nMeasurements(), 99);
0722 }
0723 
0724 BOOST_AUTO_TEST_SUITE_END()
0725 
0726 }  // namespace ActsTests