Analysis Software
Documentation for sPHENIX simulation 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 
9 #pragma once
10 
23 
24 #include <utility>
25 
26 // parameter construction helpers
27 
30  double phi, double theta, double absMom, double charge) {
31  using namespace Acts;
32  using namespace Acts::UnitLiterals;
33 
34  // phi is ill-defined in forward/backward tracks. normalize the value to
35  // ensure parameter comparisons give correct answers.
36  if (not((0 < theta) and (theta < M_PI))) {
37  phi = 0;
38  }
39 
40  Vector4 pos4 = Vector4::Zero();
41  auto particleHypothesis = ParticleHypothesis::pionLike(std::abs(charge));
42  return CurvilinearTrackParameters(pos4, phi, theta,
43  particleHypothesis.qOverP(absMom, charge),
44  std::nullopt, particleHypothesis);
45 }
46 
49  double phi, double theta, double absMom, double charge) {
50  using namespace Acts;
51  using namespace Acts::UnitLiterals;
52 
53  // phi is ill-defined in forward/backward tracks. normalize the value to
54  // ensure parameter comparisons give correct answers.
55  if (not((0 < theta) and (theta < M_PI))) {
56  phi = 0;
57  }
58 
59  BoundVector stddev = BoundVector::Zero();
60  // TODO use momentum-dependent resolutions
61  stddev[eBoundLoc0] = 15_um;
62  stddev[eBoundLoc1] = 80_um;
63  stddev[eBoundTime] = 25_ns;
64  stddev[eBoundPhi] = 1_degree;
65  stddev[eBoundTheta] = 1.5_degree;
66  stddev[eBoundQOverP] = 1_e / 10_GeV;
67  BoundSquareMatrix corr = BoundSquareMatrix::Identity();
75  BoundSquareMatrix cov = stddev.asDiagonal() * corr * stddev.asDiagonal();
76 
77  Vector4 pos4 = Vector4::Zero();
78  auto particleHypothesis = ParticleHypothesis::pionLike(std::abs(charge));
79  return CurvilinearTrackParameters(pos4, phi, theta,
80  particleHypothesis.qOverP(absMom, charge),
82 }
83 
86  double phi, double theta, double absMom) {
87  using namespace Acts;
88  using namespace Acts::UnitLiterals;
89 
90  // phi is ill-defined in forward/backward tracks. normalize the value to
91  // ensure parameter comparisons give correct answers.
92  if (not((0 < theta) and (theta < M_PI))) {
93  phi = 0;
94  }
95 
96  Vector4 pos4 = Vector4::Zero();
97  return CurvilinearTrackParameters(pos4, phi, theta, 1 / absMom, std::nullopt,
99 }
100 
101 // helpers to compare track parameters
102 
109  double epsPos, double epsDir,
110  double epsMom) {
111  using namespace Acts;
112 
113  // check stored parameters
114  CHECK_CLOSE_ABS(cmp.template get<eBoundLoc0>(),
115  ref.template get<eBoundLoc0>(), epsPos);
116  CHECK_CLOSE_ABS(cmp.template get<eBoundLoc1>(),
117  ref.template get<eBoundLoc1>(), epsPos);
118  CHECK_CLOSE_ABS(cmp.template get<eBoundTime>(),
119  ref.template get<eBoundTime>(), epsPos);
120  // check phi equivalence with circularity
121  CHECK_SMALL(detail::radian_sym(cmp.template get<eBoundPhi>() -
122  ref.template get<eBoundPhi>()),
123  epsDir);
124  CHECK_CLOSE_ABS(cmp.template get<eBoundTheta>(),
125  ref.template get<eBoundTheta>(), epsDir);
126  CHECK_CLOSE_ABS(cmp.template get<eBoundQOverP>(),
127  ref.template get<eBoundQOverP>(), epsMom);
128  // check derived parameters
129  CHECK_CLOSE_ABS(cmp.position(geoCtx), ref.position(geoCtx), epsPos);
130  CHECK_CLOSE_ABS(cmp.time(), ref.time(), epsPos);
131  CHECK_CLOSE_ABS(cmp.direction(), ref.direction(), epsDir);
132  CHECK_CLOSE_ABS(cmp.absoluteMomentum(), ref.absoluteMomentum(), epsMom);
133  // charge should be identical not just similar
134  BOOST_CHECK_EQUAL(cmp.charge(), ref.charge());
135 }
136 
142  double relativeTolerance) {
143  // either both or none have covariance set
144  if (cmp.covariance().has_value()) {
145  // comparison parameters have covariance but the reference does not
146  BOOST_CHECK(ref.covariance().has_value());
147  }
148  if (ref.covariance().has_value()) {
149  // reference parameters have covariance but the comparison does not
150  BOOST_CHECK(cmp.covariance().has_value());
151  }
152  if (cmp.covariance().has_value() and ref.covariance().has_value()) {
153  CHECK_CLOSE_COVARIANCE(cmp.covariance().value(), ref.covariance().value(),
154  relativeTolerance);
155  }
156 }
157 
158 // helpers to construct target surfaces from track states
159 
162  const Acts::BoundTrackParameters& params,
163  const Acts::GeometryContext& geoCtx) {
164  Acts::Vector3 unitW = params.direction();
165  auto [unitU, unitV] = Acts::makeCurvilinearUnitVectors(unitW);
166 
167  Acts::RotationMatrix3 rotation = Acts::RotationMatrix3::Zero();
168  rotation.col(0) = unitU;
169  rotation.col(1) = unitV;
170  rotation.col(2) = unitW;
171  Acts::Translation3 offset(params.position(geoCtx));
172  Acts::Transform3 toGlobal = offset * rotation;
173 
174  return toGlobal;
175 }
176 
179  std::shared_ptr<Acts::CylinderSurface> operator()(
180  const Acts::BoundTrackParameters& params,
181  const Acts::GeometryContext& geoCtx) {
182  auto radius = params.position(geoCtx).template head<2>().norm();
183  auto halfz = std::numeric_limits<double>::max();
184  return Acts::Surface::makeShared<Acts::CylinderSurface>(
185  Acts::Transform3::Identity(), radius, halfz);
186  }
187 };
188 
191  std::shared_ptr<Acts::DiscSurface> operator()(
192  const Acts::BoundTrackParameters& params,
193  const Acts::GeometryContext& geoCtx) {
194  using namespace Acts;
195  using namespace Acts::UnitLiterals;
196 
197  auto cl = makeCurvilinearTransform(params, geoCtx);
198  // shift the origin of the plane so the local particle position does not
199  // sit directly at the rho=0,phi=undefined singularity
200  // TODO this is a hack do avoid issues with the numerical covariance
201  // transport that does not work well at rho=0,
202  Acts::Vector3 localOffset = Acts::Vector3::Zero();
203  localOffset[Acts::ePos0] = 1_cm;
204  localOffset[Acts::ePos1] = -1_cm;
205  Acts::Vector3 globalOriginDelta = cl.linear() * localOffset;
206  cl.pretranslate(globalOriginDelta);
207 
208  return Acts::Surface::makeShared<Acts::DiscSurface>(cl);
209  }
210 };
211 
214  std::shared_ptr<Acts::PlaneSurface> operator()(
215  const Acts::BoundTrackParameters& params,
216  const Acts::GeometryContext& geoCtx) {
217  return Acts::Surface::makeShared<Acts::PlaneSurface>(
218  makeCurvilinearTransform(params, geoCtx));
219  }
220 };
221 
224  std::shared_ptr<Acts::StrawSurface> operator()(
225  const Acts::BoundTrackParameters& params,
226  const Acts::GeometryContext& geoCtx) {
227  return Acts::Surface::makeShared<Acts::StrawSurface>(
228  Acts::Transform3(Acts::Translation3(params.position(geoCtx))));
229  }
230 };
231 
232 // helper functions to run the propagation with additional checks
233 
237 template <typename propagator_t, 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,
242  const Acts::CurvilinearTrackParameters& initialParams, double pathLength) {
243  using namespace Acts::UnitLiterals;
244 
245  using Actions = Acts::ActionList<>;
246  using Aborts = Acts::AbortList<>;
247 
248  // setup propagation options
249  options_t<Actions, Aborts> options(geoCtx, magCtx);
250  options.direction = Acts::Direction::fromScalar(pathLength);
251  options.pathLimit = pathLength;
252  options.targetTolerance = 1_nm;
253  options.tolerance = 1_nm;
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, template <typename, typename>
264  class options_t = Acts::PropagatorOptions>
265 inline std::pair<Acts::BoundTrackParameters, double> transportToSurface(
266  const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
268  const Acts::CurvilinearTrackParameters& initialParams,
269  const Acts::Surface& targetSurface, double pathLimit) {
270  using namespace Acts::UnitLiterals;
271 
272  using Actions = Acts::ActionList<>;
273  using Aborts = Acts::AbortList<>;
274 
275  // setup propagation options
276  options_t<Actions, Aborts> options(geoCtx, magCtx);
277  options.direction = Acts::Direction::Forward;
278  options.pathLimit = pathLimit;
279  options.targetTolerance = 1_nm;
280  options.tolerance = 1_nm;
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, template <typename, typename>
295  class options_t = Acts::PropagatorOptions>
297  const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
299  const Acts::CurvilinearTrackParameters& initialParams, double pathLength,
300  double epsPos, double epsDir, double epsMom) {
301  // propagate parameters Acts::Direction::Forward
302  auto [fwdParams, fwdPathLength] = transportFreely<propagator_t, options_t>(
303  propagator, geoCtx, magCtx, initialParams, pathLength);
304  CHECK_CLOSE_ABS(fwdPathLength, pathLength, epsPos);
305  // propagate propagated parameters back again
306  auto [bwdParams, bwdPathLength] = transportFreely<propagator_t, options_t>(
307  propagator, geoCtx, magCtx, fwdParams, -pathLength);
308  CHECK_CLOSE_ABS(bwdPathLength, -pathLength, epsPos);
309  // check that initial and back-propagated parameters match
310  checkParametersConsistency(initialParams, bwdParams, geoCtx, epsPos, epsDir,
311  epsMom);
312 }
313 
318 template <typename propagator_t, typename surface_builder_t,
319  template <typename, typename>
320  class options_t = Acts::PropagatorOptions>
321 inline void runToSurfaceTest(
322  const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
324  const Acts::CurvilinearTrackParameters& initialParams, double pathLength,
325  surface_builder_t&& buildTargetSurface, double epsPos, double epsDir,
326  double epsMom) {
327  // free propagation for the given path length
328  auto [freeParams, freePathLength] = transportFreely<propagator_t, options_t>(
329  propagator, geoCtx, magCtx, initialParams, pathLength);
330  CHECK_CLOSE_ABS(freePathLength, pathLength, epsPos);
331  // build a target surface at the propagated position
332  auto surface = buildTargetSurface(freeParams, geoCtx);
333  BOOST_CHECK(surface);
334 
335  // bound propagation onto the target surface
336  // increase path length limit to ensure the surface can be reached
337  auto [surfParams, surfPathLength] =
338  transportToSurface<propagator_t, options_t>(propagator, geoCtx, magCtx,
339  initialParams, *surface,
340  1.5 * pathLength);
341  CHECK_CLOSE_ABS(surfPathLength, pathLength, epsPos);
342 
343  // check that the to-surface propagation matches the defining free parameters
344  CHECK_CLOSE_ABS(surfParams.position(geoCtx), freeParams.position(geoCtx),
345  epsPos);
346  CHECK_CLOSE_ABS(surfParams.time(), freeParams.time(), epsPos);
347  CHECK_CLOSE_ABS(surfParams.direction(), freeParams.direction(), epsDir);
348  CHECK_CLOSE_ABS(surfParams.absoluteMomentum(), freeParams.absoluteMomentum(),
349  epsMom);
350  CHECK_CLOSE_ABS(surfPathLength, freePathLength, epsPos);
351 }
352 
353 // consistency tests between two propagators
354 
357 template <typename cmp_propagator_t, typename ref_propagator_t,
358  template <typename, typename>
359  class options_t = Acts::PropagatorOptions>
361  const cmp_propagator_t& cmpPropagator,
362  const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx,
364  const Acts::CurvilinearTrackParameters& initialParams, double pathLength,
365  double epsPos, double epsDir, double epsMom, double tolCov) {
366  // propagate twice using the two different propagators
367  auto [cmpParams, cmpPath] = transportFreely<cmp_propagator_t, options_t>(
368  cmpPropagator, geoCtx, magCtx, initialParams, pathLength);
369  auto [refParams, refPath] = transportFreely<ref_propagator_t, options_t>(
370  refPropagator, geoCtx, magCtx, initialParams, pathLength);
371  // check parameter comparison
372  checkParametersConsistency(cmpParams, refParams, geoCtx, epsPos, epsDir,
373  epsMom);
374  checkCovarianceConsistency(cmpParams, refParams, tolCov);
375  CHECK_CLOSE_ABS(cmpPath, pathLength, epsPos);
376  CHECK_CLOSE_ABS(refPath, pathLength, epsPos);
377  CHECK_CLOSE_ABS(cmpPath, refPath, epsPos);
378 }
379 
384 template <typename cmp_propagator_t, typename ref_propagator_t,
385  typename surface_builder_t,
386  template <typename, typename>
387  class options_t = Acts::PropagatorOptions>
389  const cmp_propagator_t& cmpPropagator,
390  const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx,
392  const Acts::CurvilinearTrackParameters& initialParams, double pathLength,
393  surface_builder_t&& buildTargetSurface, double epsPos, double epsDir,
394  double epsMom, double tolCov) {
395  // free propagation with the reference propagator for the given path length
396  auto [freeParams, freePathLength] =
397  transportFreely<ref_propagator_t, options_t>(
398  refPropagator, geoCtx, magCtx, initialParams, pathLength);
399  CHECK_CLOSE_ABS(freePathLength, pathLength, epsPos);
400 
401  // build a target surface at the propagated position
402  auto surface = buildTargetSurface(freeParams, geoCtx);
403  BOOST_CHECK(surface);
404 
405  // propagate twice to the surface using the two different propagators
406  // increase path length limit to ensure the surface can be reached
407  auto [cmpParams, cmpPath] = transportToSurface<cmp_propagator_t, options_t>(
408  cmpPropagator, geoCtx, magCtx, initialParams, *surface, 1.5 * pathLength);
409  auto [refParams, refPath] = transportToSurface<ref_propagator_t, options_t>(
410  refPropagator, geoCtx, magCtx, initialParams, *surface, 1.5 * pathLength);
411  // check parameter comparison
412  checkParametersConsistency(cmpParams, refParams, geoCtx, epsPos, epsDir,
413  epsMom);
414  checkCovarianceConsistency(cmpParams, refParams, tolCov);
415  CHECK_CLOSE_ABS(cmpPath, pathLength, epsPos);
416  CHECK_CLOSE_ABS(refPath, pathLength, epsPos);
417  CHECK_CLOSE_ABS(cmpPath, refPath, epsPos);
418 }