EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PropagationTests.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file PropagationTests.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2020 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 
19 #include "Acts/Utilities/Units.hpp"
21 
22 #include <utility>
23 
24 // parameter construction helpers
25 
28  double phi, double theta, double absMom, double charge) {
29  using namespace Acts;
30  using namespace Acts::UnitLiterals;
31 
32  // phi is ill-defined in forward/backward tracks. normalize the value to
33  // ensure parameter comparisons give correct answers.
34  if (not((0 < theta) and (theta < M_PI))) {
35  phi = 0;
36  }
37 
38  Vector4D pos4 = Vector4D::Zero();
39  return CurvilinearTrackParameters(pos4, phi, theta, absMom, charge);
40 }
41 
44  double phi, double theta, double absMom, double charge) {
45  using namespace Acts;
46  using namespace Acts::UnitLiterals;
47 
48  // phi is ill-defined in forward/backward tracks. normalize the value to
49  // ensure parameter comparisons give correct answers.
50  if (not((0 < theta) and (theta < M_PI))) {
51  phi = 0;
52  }
53 
54  BoundVector stddev = BoundVector::Zero();
55  // TODO use momentum-dependent resolutions
56  stddev[eBoundLoc0] = 15_um;
57  stddev[eBoundLoc1] = 80_um;
58  stddev[eBoundTime] = 25_ns;
59  stddev[eBoundPhi] = 1_degree;
60  stddev[eBoundTheta] = 1.5_degree;
61  stddev[eBoundQOverP] = 1_e / 10_GeV;
62  BoundSymMatrix corr = BoundSymMatrix::Identity();
63  corr(eBoundLoc0, eBoundLoc1) = corr(eBoundLoc1, eBoundLoc0) = 0.125;
64  corr(eBoundLoc0, eBoundPhi) = corr(eBoundPhi, eBoundLoc0) = 0.25;
65  corr(eBoundLoc1, eBoundTheta) = corr(eBoundTheta, eBoundLoc1) = -0.25;
66  corr(eBoundTime, eBoundQOverP) = corr(eBoundQOverP, eBoundTime) = 0.125;
67  corr(eBoundPhi, eBoundTheta) = corr(eBoundTheta, eBoundPhi) = -0.25;
68  corr(eBoundPhi, eBoundQOverP) = corr(eBoundPhi, eBoundQOverP) = -0.125;
69  corr(eBoundTheta, eBoundQOverP) = corr(eBoundTheta, eBoundQOverP) = 0.5;
70  BoundSymMatrix cov = stddev.asDiagonal() * corr * stddev.asDiagonal();
71 
72  Vector4D pos4 = Vector4D::Zero();
73  return CurvilinearTrackParameters(pos4, phi, theta, absMom, charge, cov);
74 }
75 
78  double phi, double theta, double absMom) {
79  using namespace Acts;
80  using namespace Acts::UnitLiterals;
81 
82  // phi is ill-defined in forward/backward tracks. normalize the value to
83  // ensure parameter comparisons give correct answers.
84  if (not((0 < theta) and (theta < M_PI))) {
85  phi = 0;
86  }
87 
88  Vector4D pos4 = Vector4D::Zero();
89  return NeutralCurvilinearTrackParameters(pos4, phi, theta, 1 / absMom);
90 }
91 
92 // helpers to compare track parameters
93 
97 template <typename charge_t>
101  const Acts::GeometryContext& geoCtx, double epsPos, double epsDir,
102  double epsMom) {
103  using namespace Acts;
104 
105  // check stored parameters
106  CHECK_CLOSE_ABS(cmp.template get<eBoundLoc0>(),
107  ref.template get<eBoundLoc0>(), epsPos);
108  CHECK_CLOSE_ABS(cmp.template get<eBoundLoc1>(),
109  ref.template get<eBoundLoc1>(), epsPos);
110  CHECK_CLOSE_ABS(cmp.template get<eBoundTime>(),
111  ref.template get<eBoundTime>(), epsPos);
112  // check phi equivalence with circularity
113  CHECK_SMALL(detail::radian_sym(cmp.template get<eBoundPhi>() -
114  ref.template get<eBoundPhi>()),
115  epsDir);
116  CHECK_CLOSE_ABS(cmp.template get<eBoundTheta>(),
117  ref.template get<eBoundTheta>(), epsDir);
118  CHECK_CLOSE_ABS(cmp.template get<eBoundQOverP>(),
119  ref.template get<eBoundQOverP>(), epsMom);
120  // check derived parameters
121  CHECK_CLOSE_ABS(cmp.position(geoCtx), ref.position(geoCtx), epsPos);
122  CHECK_CLOSE_ABS(cmp.time(), ref.time(), epsPos);
123  CHECK_CLOSE_ABS(cmp.unitDirection(), ref.unitDirection(), epsDir);
124  CHECK_CLOSE_ABS(cmp.absoluteMomentum(), ref.absoluteMomentum(), epsMom);
125  // charge should be identical not just similar
126  BOOST_CHECK_EQUAL(cmp.charge(), ref.charge());
127 }
128 
132 template <typename charge_t>
136  double relativeTolerance) {
137  // either both or none have covariance set
138  if (cmp.covariance().has_value()) {
139  // comparison parameters have covariance but the reference does not
140  BOOST_CHECK(ref.covariance().has_value());
141  }
142  if (ref.covariance().has_value()) {
143  // reference parameters have covariance but the comparison does not
144  BOOST_CHECK(cmp.covariance().has_value());
145  }
146  if (cmp.covariance().has_value() and ref.covariance().has_value()) {
147  CHECK_CLOSE_COVARIANCE(cmp.covariance().value(), ref.covariance().value(),
148  relativeTolerance);
149  }
150 }
151 
152 // helpers to construct target surfaces from track states
153 
155 template <typename charge_t>
158  const Acts::GeometryContext& geoCtx) {
159  Acts::Vector3D unitW = params.unitDirection();
160  auto [unitU, unitV] = Acts::makeCurvilinearUnitVectors(unitW);
161 
162  Acts::RotationMatrix3D rotation = Acts::RotationMatrix3D::Zero();
163  rotation.col(0) = unitU;
164  rotation.col(1) = unitV;
165  rotation.col(2) = unitW;
166  Acts::Translation3D offset(params.position(geoCtx));
167  Acts::Transform3D toGlobal = offset * rotation;
168 
169  return toGlobal;
170 }
171 
174  template <typename charge_t>
175  std::shared_ptr<Acts::CylinderSurface> operator()(
177  const Acts::GeometryContext& geoCtx) {
178  auto radius = params.position(geoCtx).template head<2>().norm();
179  auto halfz = std::numeric_limits<double>::max();
180  return Acts::Surface::makeShared<Acts::CylinderSurface>(
181  Acts::Transform3D::Identity(), radius, halfz);
182  }
183 };
184 
187  template <typename charge_t>
188  std::shared_ptr<Acts::DiscSurface> operator()(
190  const Acts::GeometryContext& geoCtx) {
191  using namespace Acts;
192  using namespace Acts::UnitLiterals;
193 
194  auto cl = makeCurvilinearTransform(params, geoCtx);
195  // shift the origin of the plane so the local particle position does not
196  // sit directly at the rho=0,phi=undefined singularity
197  // TODO this is a hack do avoid issues with the numerical covariance
198  // transport that does not work well at rho=0,
199  Acts::Vector3D localOffset = Acts::Vector3D::Zero();
200  localOffset[Acts::ePos0] = 1_cm;
201  localOffset[Acts::ePos1] = -1_cm;
202  Acts::Vector3D globalOriginDelta = cl.linear() * localOffset;
203  cl.pretranslate(globalOriginDelta);
204 
205  return Acts::Surface::makeShared<Acts::DiscSurface>(cl);
206  }
207 };
208 
211  template <typename charge_t>
212  std::shared_ptr<Acts::PlaneSurface> operator()(
214  const Acts::GeometryContext& geoCtx) {
215  return Acts::Surface::makeShared<Acts::PlaneSurface>(
216  makeCurvilinearTransform(params, geoCtx));
217  }
218 };
219 
222  template <typename charge_t>
223  std::shared_ptr<Acts::StrawSurface> operator()(
225  const Acts::GeometryContext& geoCtx) {
226  return Acts::Surface::makeShared<Acts::StrawSurface>(
228  }
229 };
230 
231 // helper functions to run the propagation with additional checks
232 
236 template <typename propagator_t, typename charge_t,
237  template <typename, typename>
238  class options_t = Acts::PropagatorOptions>
239 inline std::pair<Acts::CurvilinearTrackParameters, double> transportFreely(
240  const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
243  double pathLength) {
244  using namespace Acts::UnitLiterals;
245 
246  using Actions = Acts::ActionList<>;
247  using Aborts = Acts::AbortList<>;
248 
249  // setup propagation options
250  options_t<Actions, Aborts> options(geoCtx, magCtx, Acts::getDummyLogger());
251  options.direction = (0 <= pathLength) ? Acts::forward : Acts::backward;
252  options.pathLimit = pathLength;
253  options.maxStepSize = 1_cm;
254 
255  auto result = propagator.propagate(initialParams, options);
256  BOOST_CHECK(result.ok());
257  BOOST_CHECK(result.value().endParameters);
258 
259  return {*result.value().endParameters, result.value().pathLength};
260 }
261 
263 template <typename propagator_t, typename charge_t,
264  template <typename, typename>
265  class options_t = Acts::PropagatorOptions>
266 inline std::pair<Acts::BoundTrackParameters, double> transportToSurface(
267  const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
270  const Acts::Surface& targetSurface, double pathLimit) {
271  using namespace Acts::UnitLiterals;
272 
273  using Actions = Acts::ActionList<>;
274  using Aborts = Acts::AbortList<>;
275 
276  // setup propagation options
277  options_t<Actions, Aborts> options(geoCtx, magCtx, Acts::getDummyLogger());
278  options.direction = Acts::forward;
279  options.pathLimit = pathLimit;
280  options.maxStepSize = 1_cm;
281 
282  auto result = propagator.propagate(initialParams, targetSurface, options);
283  BOOST_CHECK(result.ok());
284  BOOST_CHECK(result.value().endParameters);
285 
286  return {*result.value().endParameters, result.value().pathLength};
287 }
288 
289 // self-consistency tests for a single propagator
290 
294 template <typename propagator_t, typename charge_t,
295  template <typename, typename>
296  class options_t = Acts::PropagatorOptions>
298  const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
301  double pathLength, double epsPos, double epsDir, double epsMom) {
302  // propagate parameters forward
303  auto [fwdParams, fwdPathLength] =
304  transportFreely<propagator_t, charge_t, options_t>(
305  propagator, geoCtx, magCtx, initialParams, pathLength);
306  CHECK_CLOSE_ABS(fwdPathLength, pathLength, epsPos);
307  // propagate propagated parameters back again
308  auto [bwdParams, bwdPathLength] =
309  transportFreely<propagator_t, charge_t, options_t>(
310  propagator, geoCtx, magCtx, fwdParams, -pathLength);
311  CHECK_CLOSE_ABS(bwdPathLength, -pathLength, epsPos);
312  // check that initial and back-propagated parameters match
313  checkParametersConsistency(initialParams, bwdParams, geoCtx, epsPos, epsDir,
314  epsMom);
315 }
316 
321 template <typename propagator_t, typename charge_t, typename surface_builder_t,
322  template <typename, typename>
323  class options_t = Acts::PropagatorOptions>
324 inline void runToSurfaceTest(
325  const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
328  double pathLength, surface_builder_t&& buildTargetSurface, double epsPos,
329  double epsDir, double epsMom) {
330  // free propagation for the given path length
331  auto [freeParams, freePathLength] =
332  transportFreely<propagator_t, charge_t, options_t>(
333  propagator, geoCtx, magCtx, initialParams, pathLength);
334  CHECK_CLOSE_ABS(freePathLength, pathLength, epsPos);
335  // build a target surface at the propagated position
336  auto surface = buildTargetSurface(freeParams, geoCtx);
337  BOOST_CHECK(surface);
338 
339  // bound propagation onto the target surface
340  // increase path length limit to ensure the surface can be reached
341  auto [surfParams, surfPathLength] =
342  transportToSurface<propagator_t, charge_t, options_t>(
343  propagator, geoCtx, magCtx, initialParams, *surface,
344  1.5 * pathLength);
345  CHECK_CLOSE_ABS(surfPathLength, pathLength, epsPos);
346 
347  // check that the to-surface propagation matches the defining free parameters
348  CHECK_CLOSE_ABS(surfParams.position(geoCtx), freeParams.position(geoCtx),
349  epsPos);
350  CHECK_CLOSE_ABS(surfParams.time(), freeParams.time(), epsPos);
351  CHECK_CLOSE_ABS(surfParams.unitDirection(), freeParams.unitDirection(),
352  epsDir);
353  CHECK_CLOSE_ABS(surfParams.absoluteMomentum(), freeParams.absoluteMomentum(),
354  epsMom);
355  CHECK_CLOSE_ABS(surfPathLength, freePathLength, epsPos);
356 }
357 
358 // consistency tests between two propagators
359 
362 template <
363  typename cmp_propagator_t, typename ref_propagator_t, typename charge_t,
364  template <typename, typename> class options_t = Acts::PropagatorOptions>
366  const cmp_propagator_t& cmpPropagator,
367  const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx,
370  double pathLength, double epsPos, double epsDir, double epsMom,
371  double tolCov) {
372  // propagate twice using the two different propagators
373  auto [cmpParams, cmpPath] =
374  transportFreely<cmp_propagator_t, charge_t, options_t>(
375  cmpPropagator, geoCtx, magCtx, initialParams, pathLength);
376  auto [refParams, refPath] =
377  transportFreely<ref_propagator_t, charge_t, options_t>(
378  refPropagator, geoCtx, magCtx, initialParams, pathLength);
379  // check parameter comparison
380  checkParametersConsistency(cmpParams, refParams, geoCtx, epsPos, epsDir,
381  epsMom);
382  checkCovarianceConsistency(cmpParams, refParams, tolCov);
383  CHECK_CLOSE_ABS(cmpPath, pathLength, epsPos);
384  CHECK_CLOSE_ABS(refPath, pathLength, epsPos);
385  CHECK_CLOSE_ABS(cmpPath, refPath, epsPos);
386 }
387 
392 template <typename cmp_propagator_t, typename ref_propagator_t,
393  typename charge_t, typename surface_builder_t,
394  template <typename, typename>
395  class options_t = Acts::PropagatorOptions>
397  const cmp_propagator_t& cmpPropagator,
398  const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx,
401  double pathLength, surface_builder_t&& buildTargetSurface, double epsPos,
402  double epsDir, double epsMom, double tolCov) {
403  // free propagation with the reference propagator for the given path length
404  auto [freeParams, freePathLength] =
405  transportFreely<ref_propagator_t, charge_t, options_t>(
406  refPropagator, geoCtx, magCtx, initialParams, pathLength);
407  CHECK_CLOSE_ABS(freePathLength, pathLength, epsPos);
408 
409  // build a target surface at the propagated position
410  auto surface = buildTargetSurface(freeParams, geoCtx);
411  BOOST_CHECK(surface);
412 
413  // propagate twice to the surface using the two different propagators
414  // increase path length limit to ensure the surface can be reached
415  auto [cmpParams, cmpPath] =
416  transportToSurface<cmp_propagator_t, charge_t, options_t>(
417  cmpPropagator, geoCtx, magCtx, initialParams, *surface,
418  1.5 * pathLength);
419  auto [refParams, refPath] =
420  transportToSurface<ref_propagator_t, charge_t, options_t>(
421  refPropagator, geoCtx, magCtx, initialParams, *surface,
422  1.5 * pathLength);
423  // check parameter comparison
424  checkParametersConsistency(cmpParams, refParams, geoCtx, epsPos, epsDir,
425  epsMom);
426  checkCovarianceConsistency(cmpParams, refParams, tolCov);
427  CHECK_CLOSE_ABS(cmpPath, pathLength, epsPos);
428  CHECK_CLOSE_ABS(refPath, pathLength, epsPos);
429  CHECK_CLOSE_ABS(cmpPath, refPath, epsPos);
430 }