Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-16 08:14:25

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