Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MultiTrajectoryTestsCommon.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file MultiTrajectoryTestsCommon.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2023 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
11 #include <boost/test/data/test_case.hpp>
12 #include <boost/test/unit_test.hpp>
13 
21 
22 #include <random>
23 
24 namespace Acts::Test {
25 
26 template <typename factory_t>
31 
32  using trajectory_t = typename factory_t::trajectory_t;
33  using const_trajectory_t = typename factory_t::const_trajectory_t;
34 
35  private:
36  factory_t m_factory;
37 
38  public:
39  void testBuild() {
40  constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted;
41 
42  // construct trajectory w/ multiple components
43  trajectory_t t = m_factory.create();
44 
45  auto i0 = t.addTrackState(kMask);
46  // trajectory bifurcates here into multiple hypotheses
47  auto i1a = t.addTrackState(kMask, i0);
48  auto i1b = t.addTrackState(kMask, i0);
49  auto i2a = t.addTrackState(kMask, i1a);
50  auto i2b = t.addTrackState(kMask, i1b);
51 
52  // print each trajectory component
53  std::vector<size_t> act;
54  auto collect = [&](auto p) {
55  act.push_back(p.index());
56  BOOST_CHECK(!p.hasCalibrated());
57  BOOST_CHECK(!p.hasFiltered());
58  BOOST_CHECK(!p.hasSmoothed());
59  BOOST_CHECK(!p.hasJacobian());
60  BOOST_CHECK(!p.hasProjector());
61  };
62 
63  std::vector<size_t> exp = {i2a, i1a, i0};
64  t.visitBackwards(i2a, collect);
65  BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(),
66  exp.end());
67 
68  act.clear();
69  for (const auto& p : t.reverseTrackStateRange(i2a)) {
70  act.push_back(p.index());
71  }
72  BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(),
73  exp.end());
74 
75  act.clear();
76  exp = {i2b, i1b, i0};
77  t.visitBackwards(i2b, collect);
78  BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(),
79  exp.end());
80 
81  act.clear();
82  for (const auto& p : t.reverseTrackStateRange(i2b)) {
83  act.push_back(p.index());
84  }
85  BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(),
86  exp.end());
87 
88  act.clear();
89  t.applyBackwards(i2b, collect);
90  BOOST_CHECK_EQUAL_COLLECTIONS(act.begin(), act.end(), exp.begin(),
91  exp.end());
92 
93  auto r = t.reverseTrackStateRange(i2b);
94  BOOST_CHECK_EQUAL(std::distance(r.begin(), r.end()), 3);
95 
96  // check const-correctness
97  const auto& ct = t;
98  std::vector<BoundVector> predicteds;
99  // mutation in this loop works!
100  for (auto p : t.reverseTrackStateRange(i2b)) {
101  predicteds.push_back(BoundVector::Random());
102  p.predicted() = predicteds.back();
103  }
104  std::vector<BoundVector> predictedsAct;
105  for (const auto& p : ct.reverseTrackStateRange(i2b)) {
106  predictedsAct.push_back(p.predicted());
107  // mutation in this loop doesn't work: does not compile
108  // p.predicted() = BoundVector::Random();
109  }
110  BOOST_CHECK_EQUAL_COLLECTIONS(predictedsAct.begin(), predictedsAct.end(),
111  predicteds.begin(), predicteds.end());
112  }
113 
114  void testClear() {
115  constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted;
116  trajectory_t t = m_factory.create();
117  BOOST_CHECK_EQUAL(t.size(), 0);
118 
119  auto i0 = t.addTrackState(kMask);
120  // trajectory bifurcates here into multiple hypotheses
121  auto i1a = t.addTrackState(kMask, i0);
122  auto i1b = t.addTrackState(kMask, i0);
123  t.addTrackState(kMask, i1a);
124  t.addTrackState(kMask, i1b);
125 
126  BOOST_CHECK_EQUAL(t.size(), 5);
127  t.clear();
128  BOOST_CHECK_EQUAL(t.size(), 0);
129  }
130 
132  constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted;
133 
134  // construct trajectory with three components
135  trajectory_t t = m_factory.create();
136  auto i0 = t.addTrackState(kMask);
137  auto i1 = t.addTrackState(kMask, i0);
138  auto i2 = t.addTrackState(kMask, i1);
139 
140  size_t n = 0;
141  t.applyBackwards(i2, [&](const auto&) {
142  n++;
143  return false;
144  });
145  BOOST_CHECK_EQUAL(n, 1u);
146 
147  n = 0;
148  t.applyBackwards(i2, [&](const auto& ts) {
149  n++;
150  if (ts.index() == i1) {
151  return false;
152  }
153  return true;
154  });
155  BOOST_CHECK_EQUAL(n, 2u);
156 
157  n = 0;
158  t.applyBackwards(i2, [&](const auto&) {
159  n++;
160  return true;
161  });
162  BOOST_CHECK_EQUAL(n, 3u);
163  }
164 
166  using PM = TrackStatePropMask;
167  using namespace Acts::HashedStringLiteral;
168 
169  trajectory_t t = m_factory.create();
170 
171  auto alwaysPresent = [](auto& ts) {
172  BOOST_CHECK(ts.template has<"referenceSurface"_hash>());
173  BOOST_CHECK(ts.template has<"measdim"_hash>());
174  BOOST_CHECK(ts.template has<"chi2"_hash>());
175  BOOST_CHECK(ts.template has<"pathLength"_hash>());
176  BOOST_CHECK(ts.template has<"typeFlags"_hash>());
177  };
178 
179  auto ts = t.getTrackState(t.addTrackState(PM::All));
180  BOOST_CHECK(ts.hasPredicted());
181  BOOST_CHECK(ts.hasFiltered());
182  BOOST_CHECK(ts.hasSmoothed());
183  BOOST_CHECK(!ts.hasCalibrated());
184  BOOST_CHECK(ts.hasProjector());
185  BOOST_CHECK(ts.hasJacobian());
186  alwaysPresent(ts);
187  ts.allocateCalibrated(5);
188  BOOST_CHECK(ts.hasCalibrated());
189 
190  ts = t.getTrackState(t.addTrackState(PM::None));
191  BOOST_CHECK(!ts.hasPredicted());
192  BOOST_CHECK(!ts.hasFiltered());
193  BOOST_CHECK(!ts.hasSmoothed());
194  BOOST_CHECK(!ts.hasCalibrated());
195  BOOST_CHECK(!ts.hasProjector());
196  BOOST_CHECK(!ts.hasJacobian());
197  alwaysPresent(ts);
198 
199  ts = t.getTrackState(t.addTrackState(PM::Predicted));
200  BOOST_CHECK(ts.hasPredicted());
201  BOOST_CHECK(!ts.hasFiltered());
202  BOOST_CHECK(!ts.hasSmoothed());
203  BOOST_CHECK(!ts.hasCalibrated());
204  BOOST_CHECK(!ts.hasProjector());
205  BOOST_CHECK(!ts.hasJacobian());
206  alwaysPresent(ts);
207 
208  ts = t.getTrackState(t.addTrackState(PM::Filtered));
209  BOOST_CHECK(!ts.hasPredicted());
210  BOOST_CHECK(ts.hasFiltered());
211  BOOST_CHECK(!ts.hasSmoothed());
212  BOOST_CHECK(!ts.hasCalibrated());
213  BOOST_CHECK(!ts.hasProjector());
214  BOOST_CHECK(!ts.hasJacobian());
215  alwaysPresent(ts);
216 
217  ts = t.getTrackState(t.addTrackState(PM::Smoothed));
218  BOOST_CHECK(!ts.hasPredicted());
219  BOOST_CHECK(!ts.hasFiltered());
220  BOOST_CHECK(ts.hasSmoothed());
221  BOOST_CHECK(!ts.hasCalibrated());
222  BOOST_CHECK(!ts.hasProjector());
223  BOOST_CHECK(!ts.hasJacobian());
224  alwaysPresent(ts);
225 
226  ts = t.getTrackState(t.addTrackState(PM::Calibrated));
227  BOOST_CHECK(!ts.hasPredicted());
228  BOOST_CHECK(!ts.hasFiltered());
229  BOOST_CHECK(!ts.hasSmoothed());
230  BOOST_CHECK(!ts.hasCalibrated());
231  BOOST_CHECK(ts.hasProjector());
232  BOOST_CHECK(!ts.hasJacobian());
233  ts.allocateCalibrated(5);
234  BOOST_CHECK(ts.hasCalibrated());
235 
236  ts = t.getTrackState(t.addTrackState(PM::Jacobian));
237  BOOST_CHECK(!ts.hasPredicted());
238  BOOST_CHECK(!ts.hasFiltered());
239  BOOST_CHECK(!ts.hasSmoothed());
240  BOOST_CHECK(!ts.hasCalibrated());
241  BOOST_CHECK(!ts.hasProjector());
242  BOOST_CHECK(ts.hasJacobian());
243  alwaysPresent(ts);
244  }
245 
246  void testTrackStateProxyCrossTalk(std::default_random_engine& rng) {
247  TestTrackState pc(rng, 2u);
248 
249  // multi trajectory w/ a single, fully set, track state
250  trajectory_t traj = m_factory.create();
251  size_t index = traj.addTrackState();
252  {
253  auto ts = traj.getTrackState(index);
254  fillTrackState<trajectory_t>(pc, TrackStatePropMask::All, ts);
255  }
256  // get two TrackStateProxies that reference the same data
257  auto tsa = traj.getTrackState(index);
258  auto tsb = traj.getTrackState(index);
259  // then modify one and check that the other was modified as well
260  {
261  auto [par, cov] = generateBoundParametersCovariance(rng);
262  tsb.predicted() = par;
263  tsb.predictedCovariance() = cov;
264  BOOST_CHECK_EQUAL(tsa.predicted(), par);
265  BOOST_CHECK_EQUAL(tsa.predictedCovariance(), cov);
266  BOOST_CHECK_EQUAL(tsb.predicted(), par);
267  BOOST_CHECK_EQUAL(tsb.predictedCovariance(), cov);
268  }
269  {
270  auto [par, cov] = generateBoundParametersCovariance(rng);
271  tsb.filtered() = par;
272  tsb.filteredCovariance() = cov;
273  BOOST_CHECK_EQUAL(tsa.filtered(), par);
274  BOOST_CHECK_EQUAL(tsa.filteredCovariance(), cov);
275  BOOST_CHECK_EQUAL(tsb.filtered(), par);
276  BOOST_CHECK_EQUAL(tsb.filteredCovariance(), cov);
277  }
278  {
279  auto [par, cov] = generateBoundParametersCovariance(rng);
280  tsb.smoothed() = par;
281  tsb.smoothedCovariance() = cov;
282  BOOST_CHECK_EQUAL(tsa.smoothed(), par);
283  BOOST_CHECK_EQUAL(tsa.smoothedCovariance(), cov);
284  BOOST_CHECK_EQUAL(tsb.smoothed(), par);
285  BOOST_CHECK_EQUAL(tsb.smoothedCovariance(), cov);
286  }
287  {
288  // create a new (invalid) source link
289  TestSourceLink invalid;
290  invalid.sourceId = -1;
291  BOOST_CHECK_NE(
292  tsa.getUncalibratedSourceLink().template get<TestSourceLink>(),
293  invalid);
294  BOOST_CHECK_NE(
295  tsb.getUncalibratedSourceLink().template get<TestSourceLink>(),
296  invalid);
297  tsb.setUncalibratedSourceLink(SourceLink{invalid});
298  BOOST_CHECK_EQUAL(
299  tsa.getUncalibratedSourceLink().template get<TestSourceLink>(),
300  invalid);
301  BOOST_CHECK_EQUAL(
302  tsb.getUncalibratedSourceLink().template get<TestSourceLink>(),
303  invalid);
304  }
305  {
306  // reset measurements w/ full parameters
307  auto [measPar, measCov] = generateBoundParametersCovariance(rng);
308  tsb.allocateCalibrated(eBoundSize);
309  tsb.template calibrated<eBoundSize>() = measPar;
310  tsb.template calibratedCovariance<eBoundSize>() = measCov;
311  BOOST_CHECK_EQUAL(tsa.template calibrated<eBoundSize>(), measPar);
312  BOOST_CHECK_EQUAL(tsa.template calibratedCovariance<eBoundSize>(),
313  measCov);
314  BOOST_CHECK_EQUAL(tsb.template calibrated<eBoundSize>(), measPar);
315  BOOST_CHECK_EQUAL(tsb.template calibratedCovariance<eBoundSize>(),
316  measCov);
317  }
318  {
319  // reset only the effective measurements
320  auto [measPar, measCov] = generateBoundParametersCovariance(rng);
321  size_t nMeasurements = tsb.effectiveCalibrated().rows();
322  auto effPar = measPar.head(nMeasurements);
323  auto effCov = measCov.topLeftCorner(nMeasurements, nMeasurements);
324  tsb.allocateCalibrated(eBoundSize);
325  tsb.effectiveCalibrated() = effPar;
326  tsb.effectiveCalibratedCovariance() = effCov;
327  BOOST_CHECK_EQUAL(tsa.effectiveCalibrated(), effPar);
328  BOOST_CHECK_EQUAL(tsa.effectiveCalibratedCovariance(), effCov);
329  BOOST_CHECK_EQUAL(tsb.effectiveCalibrated(), effPar);
330  BOOST_CHECK_EQUAL(tsb.effectiveCalibratedCovariance(), effCov);
331  }
332  {
333  Jacobian jac = Jacobian::Identity();
334  BOOST_CHECK_NE(tsa.jacobian(), jac);
335  BOOST_CHECK_NE(tsb.jacobian(), jac);
336  tsb.jacobian() = jac;
337  BOOST_CHECK_EQUAL(tsa.jacobian(), jac);
338  BOOST_CHECK_EQUAL(tsb.jacobian(), jac);
339  }
340  {
341  tsb.chi2() = 98.0;
342  BOOST_CHECK_EQUAL(tsa.chi2(), 98.0);
343  BOOST_CHECK_EQUAL(tsb.chi2(), 98.0);
344  }
345  {
346  tsb.pathLength() = 66.0;
347  BOOST_CHECK_EQUAL(tsa.pathLength(), 66.0);
348  BOOST_CHECK_EQUAL(tsb.pathLength(), 66.0);
349  }
350  }
351 
352  void testTrackStateReassignment(std::default_random_engine& rng) {
353  TestTrackState pc(rng, 1u);
354 
355  trajectory_t t = m_factory.create();
356  size_t index = t.addTrackState();
357  auto ts = t.getTrackState(index);
358  fillTrackState<trajectory_t>(pc, TrackStatePropMask::All, ts);
359 
360  // assert contents of original measurement (just to be safe)
361  BOOST_CHECK_EQUAL(ts.calibratedSize(), 1u);
362  BOOST_CHECK_EQUAL(ts.effectiveCalibrated(),
363  (pc.sourceLink.parameters.head<1>()));
364  BOOST_CHECK_EQUAL(ts.effectiveCalibratedCovariance(),
365  (pc.sourceLink.covariance.topLeftCorner<1, 1>()));
366 
367  // use temporary measurement to reset calibrated data
368  TestTrackState ttsb(rng, 2u);
371  BOOST_CHECK_EQUAL(
372  ts.getUncalibratedSourceLink().template get<TestSourceLink>().sourceId,
373  pc.sourceLink.sourceId);
374  auto meas = testSourceLinkCalibratorReturn<trajectory_t>(
375  gctx, cctx, SourceLink{ttsb.sourceLink}, ts);
376  BOOST_CHECK_EQUAL(
377  ts.getUncalibratedSourceLink().template get<TestSourceLink>().sourceId,
378  ttsb.sourceLink.sourceId);
379  auto m2 = std::get<Measurement<BoundIndices, 2u>>(meas);
380 
381  BOOST_CHECK_EQUAL(ts.calibratedSize(), 2);
382  BOOST_CHECK_EQUAL(ts.effectiveCalibrated(), m2.parameters());
383  BOOST_CHECK_EQUAL(ts.effectiveCalibratedCovariance(), m2.covariance());
384  BOOST_CHECK_EQUAL(ts.effectiveProjector(), m2.projector());
385  }
386 
387  void testTrackStateProxyStorage(std::default_random_engine& rng,
388  size_t nMeasurements) {
389  TestTrackState pc(rng, nMeasurements);
390 
391  // create trajectory with a single fully-filled random track state
392  trajectory_t t = m_factory.create();
393  size_t index = t.addTrackState();
394  auto ts = t.getTrackState(index);
395  fillTrackState<trajectory_t>(pc, TrackStatePropMask::All, ts);
396 
397  // check that the surface is correctly set
398  BOOST_CHECK_EQUAL(&ts.referenceSurface(), pc.surface.get());
399  BOOST_CHECK_EQUAL(ts.referenceSurface().geometryId(),
400  pc.sourceLink.m_geometryId);
401 
402  // check that the track parameters are set
403  BOOST_CHECK(ts.hasPredicted());
404  BOOST_CHECK_EQUAL(ts.predicted(), pc.predicted.parameters());
405  BOOST_CHECK(pc.predicted.covariance().has_value());
406  BOOST_CHECK_EQUAL(ts.predictedCovariance(), *pc.predicted.covariance());
407  BOOST_CHECK(ts.hasFiltered());
408  BOOST_CHECK_EQUAL(ts.filtered(), pc.filtered.parameters());
409  BOOST_CHECK(pc.filtered.covariance().has_value());
410  BOOST_CHECK_EQUAL(ts.filteredCovariance(), *pc.filtered.covariance());
411  BOOST_CHECK(ts.hasSmoothed());
412  BOOST_CHECK_EQUAL(ts.smoothed(), pc.smoothed.parameters());
413  BOOST_CHECK(pc.smoothed.covariance().has_value());
414  BOOST_CHECK_EQUAL(ts.smoothedCovariance(), *pc.smoothed.covariance());
415 
416  // check that the jacobian is set
417  BOOST_CHECK(ts.hasJacobian());
418  BOOST_CHECK_EQUAL(ts.jacobian(), pc.jacobian);
419  BOOST_CHECK_EQUAL(ts.pathLength(), pc.pathLength);
420  // check that chi2 is set
421  BOOST_CHECK_EQUAL(ts.chi2(), pc.chi2);
422 
423  // check that the uncalibratedSourceLink source link is set
424  BOOST_CHECK_EQUAL(
425  ts.getUncalibratedSourceLink().template get<TestSourceLink>(),
426  pc.sourceLink);
427 
428  // check that the calibrated measurement is set
429  BOOST_CHECK(ts.hasCalibrated());
430  BOOST_CHECK_EQUAL(ts.effectiveCalibrated(),
431  pc.sourceLink.parameters.head(nMeasurements));
432  BOOST_CHECK_EQUAL(
433  ts.effectiveCalibratedCovariance(),
434  pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements));
435  {
436  ParametersVector mParFull = ParametersVector::Zero();
437  CovarianceMatrix mCovFull = CovarianceMatrix::Zero();
438  mParFull.head(nMeasurements) =
439  pc.sourceLink.parameters.head(nMeasurements);
440  mCovFull.topLeftCorner(nMeasurements, nMeasurements) =
441  pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements);
442 
443  auto expMeas = pc.sourceLink.parameters.head(nMeasurements);
444  auto expCov =
445  pc.sourceLink.covariance.topLeftCorner(nMeasurements, nMeasurements);
446 
447  visit_measurement(ts.calibratedSize(), [&](auto N) {
448  constexpr size_t measdim = decltype(N)::value;
449  BOOST_CHECK_EQUAL(ts.template calibrated<measdim>(), expMeas);
450  BOOST_CHECK_EQUAL(ts.template calibratedCovariance<measdim>(), expCov);
451  });
452  }
453 
454  BOOST_CHECK(ts.hasProjector());
456  fullProj.setZero();
457  {
460  // create a temporary measurement to extract the projector matrix
461  auto meas = testSourceLinkCalibratorReturn<trajectory_t>(
462  gctx, cctx, SourceLink{pc.sourceLink}, ts);
463  std::visit(
464  [&](const auto& m) {
465  fullProj.topLeftCorner(nMeasurements, eBoundSize) = m.projector();
466  },
467  meas);
468  }
469  BOOST_CHECK_EQUAL(ts.effectiveProjector(),
470  fullProj.topLeftCorner(nMeasurements, eBoundSize));
471  BOOST_CHECK_EQUAL(ts.projector(), fullProj);
472  }
473 
474  void testTrackStateProxyAllocations(std::default_random_engine& rng) {
475  using namespace Acts::HashedStringLiteral;
476 
477  TestTrackState pc(rng, 2u);
478 
479  // this should allocate for all components in the trackstate, plus filtered
480  trajectory_t t = m_factory.create();
481  size_t i = t.addTrackState(TrackStatePropMask::Predicted |
482  TrackStatePropMask::Filtered |
484  auto tso = t.getTrackState(i);
485  fillTrackState<trajectory_t>(pc, TrackStatePropMask::Predicted, tso);
486  fillTrackState<trajectory_t>(pc, TrackStatePropMask::Filtered, tso);
487  fillTrackState<trajectory_t>(pc, TrackStatePropMask::Jacobian, tso);
488 
489  BOOST_CHECK(tso.hasPredicted());
490  BOOST_CHECK(tso.hasFiltered());
491  BOOST_CHECK(!tso.hasSmoothed());
492  BOOST_CHECK(!tso.hasCalibrated());
493  BOOST_CHECK(tso.hasJacobian());
494 
495  auto tsnone = t.getTrackState(t.addTrackState(TrackStatePropMask::None));
496  BOOST_CHECK(!tsnone.template has<"predicted"_hash>());
497  BOOST_CHECK(!tsnone.template has<"filtered"_hash>());
498  BOOST_CHECK(!tsnone.template has<"smoothed"_hash>());
499  BOOST_CHECK(!tsnone.template has<"jacobian"_hash>());
500  BOOST_CHECK(!tsnone.template has<"calibrated"_hash>());
501  BOOST_CHECK(!tsnone.template has<"projector"_hash>());
502  BOOST_CHECK(
503  !tsnone.template has<"uncalibratedSourceLink"_hash>()); // separate
504  // optional
505  // mechanism
506  BOOST_CHECK(tsnone.template has<"referenceSurface"_hash>());
507  BOOST_CHECK(tsnone.template has<"measdim"_hash>());
508  BOOST_CHECK(tsnone.template has<"chi2"_hash>());
509  BOOST_CHECK(tsnone.template has<"pathLength"_hash>());
510  BOOST_CHECK(tsnone.template has<"typeFlags"_hash>());
511 
512  auto tsall = t.getTrackState(t.addTrackState(TrackStatePropMask::All));
513  BOOST_CHECK(tsall.template has<"predicted"_hash>());
514  BOOST_CHECK(tsall.template has<"filtered"_hash>());
515  BOOST_CHECK(tsall.template has<"smoothed"_hash>());
516  BOOST_CHECK(tsall.template has<"jacobian"_hash>());
517  BOOST_CHECK(!tsall.template has<"calibrated"_hash>());
518  tsall.allocateCalibrated(5);
519  BOOST_CHECK(tsall.template has<"calibrated"_hash>());
520  BOOST_CHECK(tsall.template has<"projector"_hash>());
521  BOOST_CHECK(!tsall.template has<
522  "uncalibratedSourceLink"_hash>()); // separate optional
523  // mechanism: nullptr
524  BOOST_CHECK(tsall.template has<"referenceSurface"_hash>());
525  BOOST_CHECK(tsall.template has<"measdim"_hash>());
526  BOOST_CHECK(tsall.template has<"chi2"_hash>());
527  BOOST_CHECK(tsall.template has<"pathLength"_hash>());
528  BOOST_CHECK(tsall.template has<"typeFlags"_hash>());
529 
530  tsall.unset(TrackStatePropMask::Predicted);
531  BOOST_CHECK(!tsall.template has<"predicted"_hash>());
532  tsall.unset(TrackStatePropMask::Filtered);
533  BOOST_CHECK(!tsall.template has<"filtered"_hash>());
534  tsall.unset(TrackStatePropMask::Smoothed);
535  BOOST_CHECK(!tsall.template has<"smoothed"_hash>());
536  tsall.unset(TrackStatePropMask::Jacobian);
537  BOOST_CHECK(!tsall.template has<"jacobian"_hash>());
538  tsall.unset(TrackStatePropMask::Calibrated);
539  BOOST_CHECK(!tsall.template has<"calibrated"_hash>());
540  }
541 
543  using PM = TrackStatePropMask;
544 
545  std::array<PM, 5> values{PM::Predicted, PM::Filtered, PM::Smoothed,
546  PM::Jacobian, PM::Calibrated};
547  PM all = std::accumulate(values.begin(), values.end(), PM::None,
548  [](auto a, auto b) { return a | b; });
549 
550  trajectory_t mj = m_factory.create();
551  {
552  auto ts = mj.getTrackState(mj.addTrackState(PM::All));
553  // Calibrated is ignored because we haven't allocated yet
554  BOOST_CHECK_EQUAL(ts.getMask(), (all & ~PM::Calibrated));
555  ts.allocateCalibrated(4);
556  BOOST_CHECK_EQUAL(ts.getMask(), all);
557  }
558  {
559  auto ts =
560  mj.getTrackState(mj.addTrackState(PM::Filtered | PM::Calibrated));
561  // Calibrated is ignored because we haven't allocated yet
562  BOOST_CHECK_EQUAL(ts.getMask(), PM::Filtered);
563  ts.allocateCalibrated(4);
564  BOOST_CHECK_EQUAL(ts.getMask(), (PM::Filtered | PM::Calibrated));
565  }
566  {
567  auto ts = mj.getTrackState(
568  mj.addTrackState(PM::Filtered | PM::Smoothed | PM::Predicted));
569  BOOST_CHECK(ts.getMask() ==
570  (PM::Filtered | PM::Smoothed | PM::Predicted));
571  }
572  {
573  for (PM mask : values) {
574  auto ts = mj.getTrackState(mj.addTrackState(mask));
575  // Calibrated is ignored because we haven't allocated yet
576  BOOST_CHECK_EQUAL(ts.getMask(), (mask & ~PM::Calibrated));
577  }
578  }
579  }
580 
581  void testTrackStateProxyCopy(std::default_random_engine& rng) {
582  using PM = TrackStatePropMask;
583 
584  std::array<PM, 4> values{PM::Predicted, PM::Filtered, PM::Smoothed,
585  PM::Jacobian};
586 
587  trajectory_t mj = m_factory.create();
588  auto mkts = [&](PM mask) {
589  auto r = mj.getTrackState(mj.addTrackState(mask));
590  return r;
591  };
592 
593  // orthogonal ones
594  for (PM a : values) {
595  for (PM b : values) {
596  auto tsa = mkts(a);
597  auto tsb = mkts(b);
598  // doesn't work
599  if (a != b) {
600  BOOST_CHECK_THROW(tsa.copyFrom(tsb), std::runtime_error);
601  BOOST_CHECK_THROW(tsb.copyFrom(tsa), std::runtime_error);
602  } else {
603  tsa.copyFrom(tsb);
604  tsb.copyFrom(tsa);
605  }
606  }
607  }
608 
609  {
610  BOOST_TEST_CHECKPOINT("Calib auto alloc");
611  auto tsa = mkts(PM::All);
612  auto tsb = mkts(PM::All);
613  tsb.allocateCalibrated(5);
614  tsb.template calibrated<5>().setRandom();
615  tsb.template calibratedCovariance<5>().setRandom();
616  tsa.copyFrom(tsb, PM::All);
617  BOOST_CHECK_EQUAL(tsa.template calibrated<5>(),
618  tsb.template calibrated<5>());
619  BOOST_CHECK_EQUAL(tsa.template calibratedCovariance<5>(),
620  tsb.template calibratedCovariance<5>());
621  }
622 
623  {
624  BOOST_TEST_CHECKPOINT("Copy none");
625  auto tsa = mkts(PM::All);
626  auto tsb = mkts(PM::All);
627  tsa.copyFrom(tsb, PM::None);
628  }
629 
630  auto ts1 = mkts(PM::Filtered | PM::Predicted); // this has both
631  ts1.filtered().setRandom();
632  ts1.filteredCovariance().setRandom();
633  ts1.predicted().setRandom();
634  ts1.predictedCovariance().setRandom();
635 
636  // ((src XOR dst) & src) == 0
637  auto ts2 = mkts(PM::Predicted);
638  ts2.predicted().setRandom();
639  ts2.predictedCovariance().setRandom();
640 
641  // they are different before
642  BOOST_CHECK(ts1.predicted() != ts2.predicted());
643  BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance());
644 
645  // ts1 -> ts2 fails
646  BOOST_CHECK_THROW(ts2.copyFrom(ts1), std::runtime_error);
647  BOOST_CHECK(ts1.predicted() != ts2.predicted());
648  BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance());
649 
650  // ts2 -> ts1 is ok
651  ts1.copyFrom(ts2);
652  BOOST_CHECK(ts1.predicted() == ts2.predicted());
653  BOOST_CHECK(ts1.predictedCovariance() == ts2.predictedCovariance());
654 
655  size_t i0 = mj.addTrackState();
656  size_t i1 = mj.addTrackState();
657  ts1 = mj.getTrackState(i0);
658  ts2 = mj.getTrackState(i1);
659  TestTrackState rts1(rng, 1u);
660  TestTrackState rts2(rng, 2u);
661  fillTrackState<trajectory_t>(rts1, TrackStatePropMask::All, ts1);
662  fillTrackState<trajectory_t>(rts2, TrackStatePropMask::All, ts2);
663 
664  auto ots1 = mkts(PM::All);
665  auto ots2 = mkts(PM::All);
666  // make full copy for later. We prove full copy works right below
667  ots1.copyFrom(ts1);
668  ots2.copyFrom(ts2);
669 
670  BOOST_CHECK_NE(ts1.predicted(), ts2.predicted());
671  BOOST_CHECK_NE(ts1.predictedCovariance(), ts2.predictedCovariance());
672  BOOST_CHECK_NE(ts1.filtered(), ts2.filtered());
673  BOOST_CHECK_NE(ts1.filteredCovariance(), ts2.filteredCovariance());
674  BOOST_CHECK_NE(ts1.smoothed(), ts2.smoothed());
675  BOOST_CHECK_NE(ts1.smoothedCovariance(), ts2.smoothedCovariance());
676 
677  BOOST_CHECK_NE(
678  ts1.getUncalibratedSourceLink().template get<TestSourceLink>(),
679  ts2.getUncalibratedSourceLink().template get<TestSourceLink>());
680 
681  visit_measurement(ts1.calibratedSize(), [&](auto N) {
682  constexpr size_t measdim = decltype(N)::value;
683  BOOST_CHECK_NE(ts1.template calibrated<measdim>(),
684  ts2.template calibrated<measdim>());
685  BOOST_CHECK_NE(ts1.template calibratedCovariance<measdim>(),
686  ts2.template calibratedCovariance<measdim>());
687  });
688 
689  BOOST_CHECK_NE(ts1.calibratedSize(), ts2.calibratedSize());
690  BOOST_CHECK_NE(ts1.projector(), ts2.projector());
691 
692  BOOST_CHECK_NE(ts1.jacobian(), ts2.jacobian());
693  BOOST_CHECK_NE(ts1.chi2(), ts2.chi2());
694  BOOST_CHECK_NE(ts1.pathLength(), ts2.pathLength());
695  BOOST_CHECK_NE(&ts1.referenceSurface(), &ts2.referenceSurface());
696 
697  ts1.copyFrom(ts2);
698 
699  BOOST_CHECK_EQUAL(ts1.predicted(), ts2.predicted());
700  BOOST_CHECK_EQUAL(ts1.predictedCovariance(), ts2.predictedCovariance());
701  BOOST_CHECK_EQUAL(ts1.filtered(), ts2.filtered());
702  BOOST_CHECK_EQUAL(ts1.filteredCovariance(), ts2.filteredCovariance());
703  BOOST_CHECK_EQUAL(ts1.smoothed(), ts2.smoothed());
704  BOOST_CHECK_EQUAL(ts1.smoothedCovariance(), ts2.smoothedCovariance());
705 
706  BOOST_CHECK_EQUAL(
707  ts1.getUncalibratedSourceLink().template get<TestSourceLink>(),
708  ts2.getUncalibratedSourceLink().template get<TestSourceLink>());
709 
710  visit_measurement(ts1.calibratedSize(), [&](auto N) {
711  constexpr size_t measdim = decltype(N)::value;
712  BOOST_CHECK_EQUAL(ts1.template calibrated<measdim>(),
713  ts2.template calibrated<measdim>());
714  BOOST_CHECK_EQUAL(ts1.template calibratedCovariance<measdim>(),
715  ts2.template calibratedCovariance<measdim>());
716  });
717 
718  BOOST_CHECK_EQUAL(ts1.calibratedSize(), ts2.calibratedSize());
719  BOOST_CHECK_EQUAL(ts1.projector(), ts2.projector());
720 
721  BOOST_CHECK_EQUAL(ts1.jacobian(), ts2.jacobian());
722  BOOST_CHECK_EQUAL(ts1.chi2(), ts2.chi2());
723  BOOST_CHECK_EQUAL(ts1.pathLength(), ts2.pathLength());
724  BOOST_CHECK_EQUAL(&ts1.referenceSurface(), &ts2.referenceSurface());
725 
726  // full copy proven to work. now let's do partial copy
727  ts2 = mkts(PM::Predicted | PM::Jacobian | PM::Calibrated);
728  ts2.copyFrom(ots2, PM::Predicted | PM::Jacobian | PM::Calibrated);
729  // copy into empty ts, only copy some
730  ts1.copyFrom(ots1); // reset to original
731  // is different again
732  BOOST_CHECK_NE(ts1.predicted(), ts2.predicted());
733  BOOST_CHECK_NE(ts1.predictedCovariance(), ts2.predictedCovariance());
734 
735  visit_measurement(ts1.calibratedSize(), [&](auto N) {
736  constexpr size_t measdim = decltype(N)::value;
737  BOOST_CHECK_NE(ts1.template calibrated<measdim>(),
738  ts2.template calibrated<measdim>());
739  BOOST_CHECK_NE(ts1.template calibratedCovariance<measdim>(),
740  ts2.template calibratedCovariance<measdim>());
741  });
742 
743  BOOST_CHECK_NE(ts1.calibratedSize(), ts2.calibratedSize());
744  BOOST_CHECK_NE(ts1.projector(), ts2.projector());
745 
746  BOOST_CHECK_NE(ts1.jacobian(), ts2.jacobian());
747  BOOST_CHECK_NE(ts1.chi2(), ts2.chi2());
748  BOOST_CHECK_NE(ts1.pathLength(), ts2.pathLength());
749  BOOST_CHECK_NE(&ts1.referenceSurface(), &ts2.referenceSurface());
750 
751  ts1.copyFrom(ts2);
752 
753  // some components are same now
754  BOOST_CHECK_EQUAL(ts1.predicted(), ts2.predicted());
755  BOOST_CHECK_EQUAL(ts1.predictedCovariance(), ts2.predictedCovariance());
756 
757  visit_measurement(ts1.calibratedSize(), [&](auto N) {
758  constexpr size_t measdim = decltype(N)::value;
759  BOOST_CHECK_EQUAL(ts1.template calibrated<measdim>(),
760  ts2.template calibrated<measdim>());
761  BOOST_CHECK_EQUAL(ts1.template calibratedCovariance<measdim>(),
762  ts2.template calibratedCovariance<measdim>());
763  });
764 
765  BOOST_CHECK_EQUAL(ts1.calibratedSize(), ts2.calibratedSize());
766  BOOST_CHECK_EQUAL(ts1.projector(), ts2.projector());
767 
768  BOOST_CHECK_EQUAL(ts1.jacobian(), ts2.jacobian());
769  BOOST_CHECK_EQUAL(ts1.chi2(), ts2.chi2()); // always copied
770  BOOST_CHECK_EQUAL(ts1.pathLength(), ts2.pathLength()); // always copied
771  BOOST_CHECK_EQUAL(&ts1.referenceSurface(),
772  &ts2.referenceSurface()); // always copied
773  }
774 
776  using PM = TrackStatePropMask;
777 
778  std::array<PM, 4> values{PM::Predicted, PM::Filtered, PM::Smoothed,
779  PM::Jacobian};
780 
781  trajectory_t mj = m_factory.create();
782  trajectory_t mj2 = m_factory.create();
783  auto mkts = [&](PM mask) {
784  auto r = mj.getTrackState(mj.addTrackState(mask));
785  return r;
786  };
787  auto mkts2 = [&](PM mask) {
788  auto r = mj2.getTrackState(mj2.addTrackState(mask));
789  return r;
790  };
791 
792  // orthogonal ones
793  for (PM a : values) {
794  for (PM b : values) {
795  auto tsa = mkts(a);
796  auto tsb = mkts2(b);
797  // doesn't work
798  if (a != b) {
799  BOOST_CHECK_THROW(tsa.copyFrom(tsb), std::runtime_error);
800  BOOST_CHECK_THROW(tsb.copyFrom(tsa), std::runtime_error);
801  } else {
802  tsa.copyFrom(tsb);
803  tsb.copyFrom(tsa);
804  }
805  }
806  }
807 
808  // make sure they are actually on different MultiTrajectories
809  BOOST_CHECK_EQUAL(mj.size(), values.size() * values.size());
810  BOOST_CHECK_EQUAL(mj2.size(), values.size() * values.size());
811 
812  auto ts1 = mkts(PM::Filtered | PM::Predicted); // this has both
813  ts1.filtered().setRandom();
814  ts1.filteredCovariance().setRandom();
815  ts1.predicted().setRandom();
816  ts1.predictedCovariance().setRandom();
817 
818  // ((src XOR dst) & src) == 0
819  auto ts2 = mkts2(PM::Predicted);
820  ts2.predicted().setRandom();
821  ts2.predictedCovariance().setRandom();
822 
823  // they are different before
824  BOOST_CHECK(ts1.predicted() != ts2.predicted());
825  BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance());
826 
827  // ts1 -> ts2 fails
828  BOOST_CHECK_THROW(ts2.copyFrom(ts1), std::runtime_error);
829  BOOST_CHECK(ts1.predicted() != ts2.predicted());
830  BOOST_CHECK(ts1.predictedCovariance() != ts2.predictedCovariance());
831 
832  // ts2 -> ts1 is ok
833  ts1.copyFrom(ts2);
834  BOOST_CHECK(ts1.predicted() == ts2.predicted());
835  BOOST_CHECK(ts1.predictedCovariance() == ts2.predictedCovariance());
836 
837  {
838  BOOST_TEST_CHECKPOINT("Calib auto alloc");
839  auto tsa = mkts(PM::All);
840  auto tsb = mkts(PM::All);
841  tsb.allocateCalibrated(5);
842  tsb.template calibrated<5>().setRandom();
843  tsb.template calibratedCovariance<5>().setRandom();
844  tsa.copyFrom(tsb, PM::All);
845  BOOST_CHECK_EQUAL(tsa.template calibrated<5>(),
846  tsb.template calibrated<5>());
847  BOOST_CHECK_EQUAL(tsa.template calibratedCovariance<5>(),
848  tsb.template calibratedCovariance<5>());
849  }
850 
851  {
852  BOOST_TEST_CHECKPOINT("Copy none");
853  auto tsa = mkts(PM::All);
854  auto tsb = mkts(PM::All);
855  tsa.copyFrom(tsb, PM::None);
856  }
857  }
858 
860  constexpr TrackStatePropMask kMask = TrackStatePropMask::Predicted;
861  trajectory_t t = m_factory.create();
862  auto i0 = t.addTrackState(kMask);
863 
864  typename trajectory_t::TrackStateProxy tp = t.getTrackState(i0); // mutable
865  typename trajectory_t::TrackStateProxy tp2{tp}; // mutable to mutable
866  typename trajectory_t::ConstTrackStateProxy tp3{tp}; // mutable to const
867  // const to mutable: this won't compile
868  // MultiTrajectory::TrackStateProxy tp4{tp3};
869  }
870 
872  // Check if the copy from const does compile, assume the copy is done
873  // correctly
874 
875  using PM = TrackStatePropMask;
876  trajectory_t mj = m_factory.create();
877 
878  const auto idx_a = mj.addTrackState(PM::All);
879  const auto idx_b = mj.addTrackState(PM::All);
880 
881  typename trajectory_t::TrackStateProxy mutableProxy =
882  mj.getTrackState(idx_a);
883 
884  const trajectory_t& cmj = mj;
885  typename trajectory_t::ConstTrackStateProxy constProxy =
886  cmj.getTrackState(idx_b);
887 
888  mutableProxy.copyFrom(constProxy);
889 
890  // copy mutable to const: this won't compile
891  // constProxy.copyFrom(mutableProxy);
892  }
893 
894  void testTrackStateProxyShare(std::default_random_engine& rng) {
895  TestTrackState pc(rng, 2u);
896 
897  {
898  trajectory_t traj = m_factory.create();
899  size_t ia = traj.addTrackState(TrackStatePropMask::All);
900  size_t ib = traj.addTrackState(TrackStatePropMask::None);
901 
902  auto tsa = traj.getTrackState(ia);
903  auto tsb = traj.getTrackState(ib);
904 
905  fillTrackState<trajectory_t>(pc, TrackStatePropMask::All, tsa);
906 
907  BOOST_CHECK(tsa.hasPredicted());
908  BOOST_CHECK(!tsb.hasPredicted());
909  tsb.shareFrom(tsa, TrackStatePropMask::Predicted);
910  BOOST_CHECK(tsa.hasPredicted());
911  BOOST_CHECK(tsb.hasPredicted());
912  BOOST_CHECK_EQUAL(tsa.predicted(), tsb.predicted());
913  BOOST_CHECK_EQUAL(tsa.predictedCovariance(), tsb.predictedCovariance());
914 
915  BOOST_CHECK(tsa.hasFiltered());
916  BOOST_CHECK(!tsb.hasFiltered());
917  tsb.shareFrom(tsa, TrackStatePropMask::Filtered);
918  BOOST_CHECK(tsa.hasFiltered());
919  BOOST_CHECK(tsb.hasFiltered());
920  BOOST_CHECK_EQUAL(tsa.filtered(), tsb.filtered());
921  BOOST_CHECK_EQUAL(tsa.filteredCovariance(), tsb.filteredCovariance());
922 
923  BOOST_CHECK(tsa.hasSmoothed());
924  BOOST_CHECK(!tsb.hasSmoothed());
925  tsb.shareFrom(tsa, TrackStatePropMask::Smoothed);
926  BOOST_CHECK(tsa.hasSmoothed());
927  BOOST_CHECK(tsb.hasSmoothed());
928  BOOST_CHECK_EQUAL(tsa.smoothed(), tsb.smoothed());
929  BOOST_CHECK_EQUAL(tsa.smoothedCovariance(), tsb.smoothedCovariance());
930 
931  BOOST_CHECK(tsa.hasJacobian());
932  BOOST_CHECK(!tsb.hasJacobian());
933  tsb.shareFrom(tsa, TrackStatePropMask::Jacobian);
934  BOOST_CHECK(tsa.hasJacobian());
935  BOOST_CHECK(tsb.hasJacobian());
936  BOOST_CHECK_EQUAL(tsa.jacobian(), tsb.jacobian());
937  }
938 
939  {
940  trajectory_t traj = m_factory.create();
941  size_t i = traj.addTrackState(TrackStatePropMask::All &
942  ~TrackStatePropMask::Filtered &
943  ~TrackStatePropMask::Smoothed);
944 
945  auto ts = traj.getTrackState(i);
946 
947  BOOST_CHECK(ts.hasPredicted());
948  BOOST_CHECK(!ts.hasFiltered());
949  BOOST_CHECK(!ts.hasSmoothed());
950  ts.predicted().setRandom();
951  ts.predictedCovariance().setRandom();
952 
953  ts.shareFrom(TrackStatePropMask::Predicted, TrackStatePropMask::Filtered);
954  BOOST_CHECK(ts.hasPredicted());
955  BOOST_CHECK(ts.hasFiltered());
956  BOOST_CHECK(!ts.hasSmoothed());
957  BOOST_CHECK_EQUAL(ts.predicted(), ts.filtered());
958  BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.filteredCovariance());
959 
960  ts.shareFrom(TrackStatePropMask::Predicted, TrackStatePropMask::Smoothed);
961  BOOST_CHECK(ts.hasPredicted());
962  BOOST_CHECK(ts.hasFiltered());
963  BOOST_CHECK(ts.hasSmoothed());
964  BOOST_CHECK_EQUAL(ts.predicted(), ts.filtered());
965  BOOST_CHECK_EQUAL(ts.predicted(), ts.smoothed());
966  BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.filteredCovariance());
967  BOOST_CHECK_EQUAL(ts.predictedCovariance(), ts.smoothedCovariance());
968  }
969  }
970 
972  using namespace HashedStringLiteral;
973 
974  auto test = [&](const std::string& col, auto value) {
975  using T = decltype(value);
976  std::string col2 = col + "_2";
978  HashedString h2{hashString(col2)};
979 
980  trajectory_t traj = m_factory.create();
981  BOOST_CHECK(!traj.hasColumn(h));
982  traj.template addColumn<T>(col);
983  BOOST_CHECK(traj.hasColumn(h));
984 
985  BOOST_CHECK(!traj.hasColumn(h2));
986  traj.template addColumn<T>(col2);
987  BOOST_CHECK(traj.hasColumn(h2));
988 
989  auto ts1 = traj.getTrackState(traj.addTrackState());
990  auto ts2 = traj.getTrackState(
991  traj.addTrackState(TrackStatePropMask::All, ts1.index()));
992  auto ts3 = traj.getTrackState(
993  traj.addTrackState(TrackStatePropMask::All, ts2.index()));
994 
995  BOOST_CHECK(ts1.has(h));
996  BOOST_CHECK(ts2.has(h));
997  BOOST_CHECK(ts3.has(h));
998 
999  BOOST_CHECK(ts1.has(h2));
1000  BOOST_CHECK(ts2.has(h2));
1001  BOOST_CHECK(ts3.has(h2));
1002 
1003  ts1.template component<T>(col) = value;
1004  BOOST_CHECK_EQUAL(ts1.template component<T>(col), value);
1005  };
1006 
1007  test("uint32_t", uint32_t(1));
1008  test("uint64_t", uint64_t(2));
1009  test("int32_t", int32_t(-3));
1010  test("int64_t", int64_t(-4));
1011  test("float", float(8.9));
1012  test("double", double(656.2));
1013 
1014  trajectory_t traj = m_factory.create();
1015  traj.template addColumn<int>("extra_column");
1016  traj.template addColumn<float>("another_column");
1017 
1018  auto ts1 = traj.getTrackState(traj.addTrackState());
1019  auto ts2 = traj.getTrackState(
1020  traj.addTrackState(TrackStatePropMask::All, ts1.index()));
1021  auto ts3 = traj.getTrackState(
1022  traj.addTrackState(TrackStatePropMask::All, ts2.index()));
1023 
1024  BOOST_CHECK(ts1.template has<"extra_column"_hash>());
1025  BOOST_CHECK(ts2.template has<"extra_column"_hash>());
1026  BOOST_CHECK(ts3.template has<"extra_column"_hash>());
1027 
1028  BOOST_CHECK(ts1.template has<"another_column"_hash>());
1029  BOOST_CHECK(ts2.template has<"another_column"_hash>());
1030  BOOST_CHECK(ts3.template has<"another_column"_hash>());
1031 
1032  ts2.template component<int, "extra_column"_hash>() = 6;
1033 
1034  BOOST_CHECK_EQUAL((ts2.template component<int, "extra_column"_hash>()), 6);
1035 
1036  ts3.template component<float, "another_column"_hash>() = 7.2f;
1037  BOOST_CHECK_EQUAL((ts3.template component<float, "another_column"_hash>()),
1038  7.2f);
1039  }
1040 
1042  auto runTest = [&](auto&& fn) {
1043  trajectory_t mt = m_factory.create();
1044  std::vector<std::string> columns = {"one", "two", "three", "four"};
1045  for (const auto& c : columns) {
1046  BOOST_CHECK(!mt.hasColumn(fn(c)));
1047  mt.template addColumn<int>(c);
1048  BOOST_CHECK(mt.hasColumn(fn(c)));
1049  }
1050  for (const auto& c : columns) {
1051  auto ts1 = mt.getTrackState(mt.addTrackState());
1052  auto ts2 = mt.getTrackState(mt.addTrackState());
1053  BOOST_CHECK(ts1.has(fn(c)));
1054  BOOST_CHECK(ts2.has(fn(c)));
1055  ts1.template component<int>(fn(c)) = 674;
1056  ts2.template component<int>(fn(c)) = 421;
1057  BOOST_CHECK_EQUAL(ts1.template component<int>(fn(c)), 674);
1058  BOOST_CHECK_EQUAL(ts2.template component<int>(fn(c)), 421);
1059  }
1060  };
1061 
1062  runTest([](const std::string& c) { return hashString(c.c_str()); });
1063  // runTest([](const std::string& c) { return c.c_str(); });
1064  // runTest([](const std::string& c) { return c; });
1065  // runTest([](std::string_view c) { return c; });
1066  }
1067 };
1068 } // namespace Acts::Test