Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:10:24

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2020 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 http://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 #include "Acts/Definitions/Direction.hpp"
0012 #include "Acts/Definitions/Units.hpp"
0013 #include "Acts/EventData/TrackParameters.hpp"
0014 #include "Acts/Geometry/GeometryContext.hpp"
0015 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0016 #include "Acts/Surfaces/CylinderSurface.hpp"
0017 #include "Acts/Surfaces/DiscSurface.hpp"
0018 #include "Acts/Surfaces/PlaneSurface.hpp"
0019 #include "Acts/Surfaces/StrawSurface.hpp"
0020 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0021 #include "Acts/Utilities/UnitVectors.hpp"
0022 #include "Acts/Utilities/detail/periodic.hpp"
0023 
0024 #include <utility>
0025 
0026 // parameter construction helpers
0027 
0028 /// Construct (initial) curvilinear parameters.
0029 inline Acts::CurvilinearTrackParameters makeParametersCurvilinear(
0030     double phi, double theta, double absMom, double charge) {
0031   using namespace Acts;
0032   using namespace Acts::UnitLiterals;
0033 
0034   // phi is ill-defined in forward/backward tracks. normalize the value to
0035   // ensure parameter comparisons give correct answers.
0036   if (!((0 < theta) && (theta < M_PI))) {
0037     phi = 0;
0038   }
0039 
0040   Vector4 pos4 = Vector4::Zero();
0041   auto particleHypothesis = ParticleHypothesis::pionLike(std::abs(charge));
0042   return CurvilinearTrackParameters(pos4, phi, theta,
0043                                     particleHypothesis.qOverP(absMom, charge),
0044                                     std::nullopt, particleHypothesis);
0045 }
0046 
0047 /// Construct (initial) curvilinear parameters with covariance.
0048 inline Acts::CurvilinearTrackParameters makeParametersCurvilinearWithCovariance(
0049     double phi, double theta, double absMom, double charge) {
0050   using namespace Acts;
0051   using namespace Acts::UnitLiterals;
0052 
0053   // phi is ill-defined in forward/backward tracks. normalize the value to
0054   // ensure parameter comparisons give correct answers.
0055   if (!((0 < theta) && (theta < M_PI))) {
0056     phi = 0;
0057   }
0058 
0059   BoundVector stddev = BoundVector::Zero();
0060   // TODO use momentum-dependent resolutions
0061   stddev[eBoundLoc0] = 15_um;
0062   stddev[eBoundLoc1] = 80_um;
0063   stddev[eBoundTime] = 25_ns;
0064   stddev[eBoundPhi] = 1_degree;
0065   stddev[eBoundTheta] = 1.5_degree;
0066   stddev[eBoundQOverP] = 1_e / 10_GeV;
0067   BoundSquareMatrix corr = BoundSquareMatrix::Identity();
0068   corr(eBoundLoc0, eBoundLoc1) = corr(eBoundLoc1, eBoundLoc0) = 0.125;
0069   corr(eBoundLoc0, eBoundPhi) = corr(eBoundPhi, eBoundLoc0) = 0.25;
0070   corr(eBoundLoc1, eBoundTheta) = corr(eBoundTheta, eBoundLoc1) = -0.25;
0071   corr(eBoundTime, eBoundQOverP) = corr(eBoundQOverP, eBoundTime) = 0.125;
0072   corr(eBoundPhi, eBoundTheta) = corr(eBoundTheta, eBoundPhi) = -0.25;
0073   corr(eBoundPhi, eBoundQOverP) = corr(eBoundPhi, eBoundQOverP) = -0.125;
0074   corr(eBoundTheta, eBoundQOverP) = corr(eBoundTheta, eBoundQOverP) = 0.5;
0075   BoundSquareMatrix cov = stddev.asDiagonal() * corr * stddev.asDiagonal();
0076 
0077   Vector4 pos4 = Vector4::Zero();
0078   auto particleHypothesis = ParticleHypothesis::pionLike(std::abs(charge));
0079   return CurvilinearTrackParameters(pos4, phi, theta,
0080                                     particleHypothesis.qOverP(absMom, charge),
0081                                     cov, particleHypothesis);
0082 }
0083 
0084 /// Construct (initial) neutral curvilinear parameters.
0085 inline Acts::CurvilinearTrackParameters makeParametersCurvilinearNeutral(
0086     double phi, double theta, double absMom) {
0087   using namespace Acts;
0088   using namespace Acts::UnitLiterals;
0089 
0090   // phi is ill-defined in forward/backward tracks. normalize the value to
0091   // ensure parameter comparisons give correct answers.
0092   if (!((0 < theta) && (theta < M_PI))) {
0093     phi = 0;
0094   }
0095 
0096   Vector4 pos4 = Vector4::Zero();
0097   return CurvilinearTrackParameters(pos4, phi, theta, 1 / absMom, std::nullopt,
0098                                     ParticleHypothesis::pion0());
0099 }
0100 
0101 // helpers to compare track parameters
0102 
0103 /// Check that two parameters object are consistent within the tolerances.
0104 ///
0105 /// \warning Does not check that they are defined on the same surface.
0106 inline void checkParametersConsistency(const Acts::BoundTrackParameters& cmp,
0107                                        const Acts::BoundTrackParameters& ref,
0108                                        const Acts::GeometryContext& geoCtx,
0109                                        double epsPos, double epsDir,
0110                                        double epsMom) {
0111   using namespace Acts;
0112 
0113   // check stored parameters
0114   CHECK_CLOSE_ABS(cmp.template get<eBoundLoc0>(),
0115                   ref.template get<eBoundLoc0>(), epsPos);
0116   CHECK_CLOSE_ABS(cmp.template get<eBoundLoc1>(),
0117                   ref.template get<eBoundLoc1>(), epsPos);
0118   CHECK_CLOSE_ABS(cmp.template get<eBoundTime>(),
0119                   ref.template get<eBoundTime>(), epsPos);
0120   // check phi equivalence with circularity
0121   CHECK_SMALL(detail::radian_sym(cmp.template get<eBoundPhi>() -
0122                                  ref.template get<eBoundPhi>()),
0123               epsDir);
0124   CHECK_CLOSE_ABS(cmp.template get<eBoundTheta>(),
0125                   ref.template get<eBoundTheta>(), epsDir);
0126   CHECK_CLOSE_ABS(cmp.template get<eBoundQOverP>(),
0127                   ref.template get<eBoundQOverP>(), epsMom);
0128   // check derived parameters
0129   CHECK_CLOSE_ABS(cmp.position(geoCtx), ref.position(geoCtx), epsPos);
0130   CHECK_CLOSE_ABS(cmp.time(), ref.time(), epsPos);
0131   CHECK_CLOSE_ABS(cmp.direction(), ref.direction(), epsDir);
0132   CHECK_CLOSE_ABS(cmp.absoluteMomentum(), ref.absoluteMomentum(), epsMom);
0133   // charge should be identical not just similar
0134   BOOST_CHECK_EQUAL(cmp.charge(), ref.charge());
0135 }
0136 
0137 /// Check that two parameters covariances are consistent within the tolerances.
0138 ///
0139 /// \warning Does not check that the parameters value itself are consistent.
0140 inline void checkCovarianceConsistency(const Acts::BoundTrackParameters& cmp,
0141                                        const Acts::BoundTrackParameters& ref,
0142                                        double relativeTolerance) {
0143   // either both or none have covariance set
0144   if (cmp.covariance().has_value()) {
0145     // comparison parameters have covariance but the reference does not
0146     BOOST_CHECK(ref.covariance().has_value());
0147   }
0148   if (ref.covariance().has_value()) {
0149     // reference parameters have covariance but the comparison does not
0150     BOOST_CHECK(cmp.covariance().has_value());
0151   }
0152   if (cmp.covariance().has_value() && ref.covariance().has_value()) {
0153     CHECK_CLOSE_COVARIANCE(cmp.covariance().value(), ref.covariance().value(),
0154                            relativeTolerance);
0155   }
0156 }
0157 
0158 // helpers to construct target surfaces from track states
0159 
0160 /// Construct the transformation from the curvilinear to the global coordinates.
0161 inline Acts::Transform3 makeCurvilinearTransform(
0162     const Acts::BoundTrackParameters& params,
0163     const Acts::GeometryContext& geoCtx) {
0164   Acts::Vector3 unitW = params.direction();
0165   auto [unitU, unitV] = Acts::makeCurvilinearUnitVectors(unitW);
0166 
0167   Acts::RotationMatrix3 rotation = Acts::RotationMatrix3::Zero();
0168   rotation.col(0) = unitU;
0169   rotation.col(1) = unitV;
0170   rotation.col(2) = unitW;
0171   Acts::Translation3 offset(params.position(geoCtx));
0172   Acts::Transform3 toGlobal = offset * rotation;
0173 
0174   return toGlobal;
0175 }
0176 
0177 /// Construct a z-cylinder centered at zero with the track on its surface.
0178 struct ZCylinderSurfaceBuilder {
0179   std::shared_ptr<Acts::CylinderSurface> operator()(
0180       const Acts::BoundTrackParameters& params,
0181       const Acts::GeometryContext& geoCtx) {
0182     auto radius = params.position(geoCtx).template head<2>().norm();
0183     auto halfz = std::numeric_limits<double>::max();
0184     return Acts::Surface::makeShared<Acts::CylinderSurface>(
0185         Acts::Transform3::Identity(), radius, halfz);
0186   }
0187 };
0188 
0189 /// Construct a disc at track position with plane normal along track tangent.
0190 struct DiscSurfaceBuilder {
0191   std::shared_ptr<Acts::DiscSurface> operator()(
0192       const Acts::BoundTrackParameters& params,
0193       const Acts::GeometryContext& geoCtx) {
0194     using namespace Acts;
0195     using namespace Acts::UnitLiterals;
0196 
0197     auto cl = makeCurvilinearTransform(params, geoCtx);
0198     // shift the origin of the plane so the local particle position does not
0199     // sit directly at the rho=0,phi=undefined singularity
0200     // TODO this is a hack do avoid issues with the numerical covariance
0201     //      transport that does not work well at rho=0,
0202     Acts::Vector3 localOffset = Acts::Vector3::Zero();
0203     localOffset[Acts::ePos0] = 1_cm;
0204     localOffset[Acts::ePos1] = -1_cm;
0205     Acts::Vector3 globalOriginDelta = cl.linear() * localOffset;
0206     cl.pretranslate(globalOriginDelta);
0207 
0208     return Acts::Surface::makeShared<Acts::DiscSurface>(cl);
0209   }
0210 };
0211 
0212 /// Construct a plane at track position with plane normal along track tangent.
0213 struct PlaneSurfaceBuilder {
0214   std::shared_ptr<Acts::PlaneSurface> operator()(
0215       const Acts::BoundTrackParameters& params,
0216       const Acts::GeometryContext& geoCtx) {
0217     return Acts::Surface::makeShared<Acts::PlaneSurface>(
0218         makeCurvilinearTransform(params, geoCtx));
0219   }
0220 };
0221 
0222 /// Construct a z-straw at the track position.
0223 struct ZStrawSurfaceBuilder {
0224   std::shared_ptr<Acts::StrawSurface> operator()(
0225       const Acts::BoundTrackParameters& params,
0226       const Acts::GeometryContext& geoCtx) {
0227     return Acts::Surface::makeShared<Acts::StrawSurface>(
0228         Acts::Transform3(Acts::Translation3(params.position(geoCtx))));
0229   }
0230 };
0231 
0232 // helper functions to run the propagation with additional checks
0233 
0234 /// Propagate the initial parameters for the given pathlength in space.
0235 ///
0236 /// Use a negative path length to indicate backward propagation.
0237 template <typename propagator_t, template <typename, typename>
0238                                  class options_t = Acts::PropagatorOptions>
0239 inline std::pair<Acts::CurvilinearTrackParameters, double> transportFreely(
0240     const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
0241     const Acts::MagneticFieldContext& magCtx,
0242     const Acts::CurvilinearTrackParameters& initialParams, double pathLength) {
0243   using namespace Acts::UnitLiterals;
0244 
0245   using Actions = Acts::ActionList<>;
0246   using Aborts = Acts::AbortList<>;
0247 
0248   // setup propagation options
0249   options_t<Actions, Aborts> options(geoCtx, magCtx);
0250   options.direction = Acts::Direction::fromScalar(pathLength);
0251   options.pathLimit = pathLength;
0252   options.surfaceTolerance = 1_nm;
0253   options.stepTolerance = 1_nm;
0254 
0255   auto result = propagator.propagate(initialParams, options);
0256   BOOST_CHECK(result.ok());
0257   BOOST_CHECK(result.value().endParameters);
0258 
0259   return {*result.value().endParameters, result.value().pathLength};
0260 }
0261 
0262 /// Propagate the initial parameters to the target surface.
0263 template <typename propagator_t, template <typename, typename>
0264                                  class options_t = Acts::PropagatorOptions>
0265 inline std::pair<Acts::BoundTrackParameters, double> transportToSurface(
0266     const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
0267     const Acts::MagneticFieldContext& magCtx,
0268     const Acts::CurvilinearTrackParameters& initialParams,
0269     const Acts::Surface& targetSurface, double pathLimit) {
0270   using namespace Acts::UnitLiterals;
0271 
0272   using Actions = Acts::ActionList<>;
0273   using Aborts = Acts::AbortList<>;
0274 
0275   // setup propagation options
0276   options_t<Actions, Aborts> options(geoCtx, magCtx);
0277   options.direction = Acts::Direction::Forward;
0278   options.pathLimit = pathLimit;
0279   options.surfaceTolerance = 1_nm;
0280   options.stepTolerance = 1_nm;
0281 
0282   auto result = propagator.propagate(initialParams, targetSurface, options);
0283   BOOST_CHECK(result.ok());
0284   BOOST_CHECK(result.value().endParameters);
0285 
0286   return {*result.value().endParameters, result.value().pathLength};
0287 }
0288 
0289 // self-consistency tests for a single propagator
0290 
0291 /// Propagate the initial parameters the given path length along its
0292 /// trajectory and then propagate the final parameters back. Verify that the
0293 /// propagated parameters match the initial ones.
0294 template <typename propagator_t, template <typename, typename>
0295                                  class options_t = Acts::PropagatorOptions>
0296 inline void runForwardBackwardTest(
0297     const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
0298     const Acts::MagneticFieldContext& magCtx,
0299     const Acts::CurvilinearTrackParameters& initialParams, double pathLength,
0300     double epsPos, double epsDir, double epsMom) {
0301   // propagate parameters Acts::Direction::Forward
0302   auto [fwdParams, fwdPathLength] = transportFreely<propagator_t, options_t>(
0303       propagator, geoCtx, magCtx, initialParams, pathLength);
0304   CHECK_CLOSE_ABS(fwdPathLength, pathLength, epsPos);
0305   // propagate propagated parameters back again
0306   auto [bwdParams, bwdPathLength] = transportFreely<propagator_t, options_t>(
0307       propagator, geoCtx, magCtx, fwdParams, -pathLength);
0308   CHECK_CLOSE_ABS(bwdPathLength, -pathLength, epsPos);
0309   // check that initial and back-propagated parameters match
0310   checkParametersConsistency(initialParams, bwdParams, geoCtx, epsPos, epsDir,
0311                              epsMom);
0312 }
0313 
0314 /// Propagate the initial parameters once for the given path length and
0315 /// use the propagated parameters to define a target surface. Propagate the
0316 /// initial parameters again to the target surface. Verify that the surface has
0317 /// been found and the parameters are consistent.
0318 template <typename propagator_t, typename surface_builder_t,
0319           template <typename, typename>
0320           class options_t = Acts::PropagatorOptions>
0321 inline void runToSurfaceTest(
0322     const propagator_t& propagator, const Acts::GeometryContext& geoCtx,
0323     const Acts::MagneticFieldContext& magCtx,
0324     const Acts::CurvilinearTrackParameters& initialParams, double pathLength,
0325     surface_builder_t&& buildTargetSurface, double epsPos, double epsDir,
0326     double epsMom) {
0327   // free propagation for the given path length
0328   auto [freeParams, freePathLength] = transportFreely<propagator_t, options_t>(
0329       propagator, geoCtx, magCtx, initialParams, pathLength);
0330   CHECK_CLOSE_ABS(freePathLength, pathLength, epsPos);
0331   // build a target surface at the propagated position
0332   auto surface = buildTargetSurface(freeParams, geoCtx);
0333   BOOST_CHECK(surface);
0334 
0335   // bound propagation onto the target surface
0336   // increase path length limit to ensure the surface can be reached
0337   auto [surfParams, surfPathLength] =
0338       transportToSurface<propagator_t, options_t>(propagator, geoCtx, magCtx,
0339                                                   initialParams, *surface,
0340                                                   1.5 * pathLength);
0341   CHECK_CLOSE_ABS(surfPathLength, pathLength, epsPos);
0342 
0343   // check that the to-surface propagation matches the defining free parameters
0344   CHECK_CLOSE_ABS(surfParams.position(geoCtx), freeParams.position(geoCtx),
0345                   epsPos);
0346   CHECK_CLOSE_ABS(surfParams.time(), freeParams.time(), epsPos);
0347   CHECK_CLOSE_ABS(surfParams.direction(), freeParams.direction(), epsDir);
0348   CHECK_CLOSE_ABS(surfParams.absoluteMomentum(), freeParams.absoluteMomentum(),
0349                   epsMom);
0350   CHECK_CLOSE_ABS(surfPathLength, freePathLength, epsPos);
0351 }
0352 
0353 // consistency tests between two propagators
0354 
0355 /// Propagate the initial parameters along their trajectory for the given path
0356 /// length using two different propagators and verify consistent output.
0357 template <typename cmp_propagator_t, typename ref_propagator_t,
0358           template <typename, typename>
0359           class options_t = Acts::PropagatorOptions>
0360 inline void runForwardComparisonTest(
0361     const cmp_propagator_t& cmpPropagator,
0362     const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx,
0363     const Acts::MagneticFieldContext& magCtx,
0364     const Acts::CurvilinearTrackParameters& initialParams, double pathLength,
0365     double epsPos, double epsDir, double epsMom, double tolCov) {
0366   // propagate twice using the two different propagators
0367   auto [cmpParams, cmpPath] = transportFreely<cmp_propagator_t, options_t>(
0368       cmpPropagator, geoCtx, magCtx, initialParams, pathLength);
0369   auto [refParams, refPath] = transportFreely<ref_propagator_t, options_t>(
0370       refPropagator, geoCtx, magCtx, initialParams, pathLength);
0371   // check parameter comparison
0372   checkParametersConsistency(cmpParams, refParams, geoCtx, epsPos, epsDir,
0373                              epsMom);
0374   checkCovarianceConsistency(cmpParams, refParams, tolCov);
0375   CHECK_CLOSE_ABS(cmpPath, pathLength, epsPos);
0376   CHECK_CLOSE_ABS(refPath, pathLength, epsPos);
0377   CHECK_CLOSE_ABS(cmpPath, refPath, epsPos);
0378 }
0379 
0380 /// Propagate the initial parameters along their trajectory for the given path
0381 /// length using the reference propagator. Use the propagated track parameters
0382 /// to define a target plane. Propagate the initial parameters using two
0383 /// different propagators and verify consistent output.
0384 template <typename cmp_propagator_t, typename ref_propagator_t,
0385           typename surface_builder_t,
0386           template <typename, typename>
0387           class options_t = Acts::PropagatorOptions>
0388 inline void runToSurfaceComparisonTest(
0389     const cmp_propagator_t& cmpPropagator,
0390     const ref_propagator_t& refPropagator, const Acts::GeometryContext& geoCtx,
0391     const Acts::MagneticFieldContext& magCtx,
0392     const Acts::CurvilinearTrackParameters& initialParams, double pathLength,
0393     surface_builder_t&& buildTargetSurface, double epsPos, double epsDir,
0394     double epsMom, double tolCov) {
0395   // free propagation with the reference propagator for the given path length
0396   auto [freeParams, freePathLength] =
0397       transportFreely<ref_propagator_t, options_t>(
0398           refPropagator, geoCtx, magCtx, initialParams, pathLength);
0399   CHECK_CLOSE_ABS(freePathLength, pathLength, epsPos);
0400 
0401   // build a target surface at the propagated position
0402   auto surface = buildTargetSurface(freeParams, geoCtx);
0403   BOOST_CHECK(surface);
0404 
0405   // propagate twice to the surface using the two different propagators
0406   // increase path length limit to ensure the surface can be reached
0407   auto [cmpParams, cmpPath] = transportToSurface<cmp_propagator_t, options_t>(
0408       cmpPropagator, geoCtx, magCtx, initialParams, *surface, 1.5 * pathLength);
0409   auto [refParams, refPath] = transportToSurface<ref_propagator_t, options_t>(
0410       refPropagator, geoCtx, magCtx, initialParams, *surface, 1.5 * pathLength);
0411   // check parameter comparison
0412   checkParametersConsistency(cmpParams, refParams, geoCtx, epsPos, epsDir,
0413                              epsMom);
0414   checkCovarianceConsistency(cmpParams, refParams, tolCov);
0415   CHECK_CLOSE_ABS(cmpPath, pathLength, epsPos);
0416   CHECK_CLOSE_ABS(refPath, pathLength, epsPos);
0417   CHECK_CLOSE_ABS(cmpPath, refPath, epsPos);
0418 }