Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
LinearizedTrackFactoryTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file LinearizedTrackFactoryTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019-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 #include <boost/test/data/test_case.hpp>
10 #include <boost/test/tools/output_test_stream.hpp>
11 #include <boost/test/unit_test.hpp>
12 
33 
34 #include <algorithm>
35 #include <array>
36 #include <cmath>
37 #include <memory>
38 #include <random>
39 #include <tuple>
40 #include <utility>
41 #include <vector>
42 
43 namespace bdata = boost::unit_test::data;
44 using namespace Acts::UnitLiterals;
45 
46 namespace Acts {
47 namespace Test {
48 
50 // We will compare analytical and numerical computations in the case of a
51 // (non-zero) constant B-field and a zero B-field.
59 
60 // Create a test context
63 
64 // Vertex x/y position distribution
65 std::uniform_real_distribution<> vXYDist(-0.1_mm, 0.1_mm);
66 // Vertex z position distribution
67 std::uniform_real_distribution<> vZDist(-20_mm, 20_mm);
68 // Vertex time distribution
69 std::uniform_real_distribution<> vTDist(-1_ns, 1_ns);
70 // Track d0 distribution
71 std::uniform_real_distribution<> d0Dist(-0.01_mm, 0.01_mm);
72 // Track z0 distribution
73 std::uniform_real_distribution<> z0Dist(-0.2_mm, 0.2_mm);
74 // Track pT distribution
75 std::uniform_real_distribution<> pTDist(0.4_GeV, 10_GeV);
76 // Track phi distribution
77 std::uniform_real_distribution<> phiDist(-M_PI, M_PI);
78 // Track theta distribution
79 std::uniform_real_distribution<> thetaDist(1.0, M_PI - 1.0);
80 // Track charge helper distribution
81 std::uniform_real_distribution<> qDist(-1, 1);
82 // Track time distribution
83 std::uniform_real_distribution<> tDist(-0.002_ns, 0.002_ns);
84 // Track IP resolution distribution
85 std::uniform_real_distribution<> resIPDist(0., 100_um);
86 // Track angular distribution
87 std::uniform_real_distribution<> resAngDist(0., 0.1);
88 // Track q/p resolution distribution
89 std::uniform_real_distribution<> resQoPDist(0.0, 0.1);
90 // Track time resolution distribution
91 std::uniform_real_distribution<> resTDist(0.1_ns, 0.2_ns);
92 
100 BOOST_AUTO_TEST_CASE(linearized_track_factory_test) {
101  // Number of tracks to linearize
102  unsigned int nTracks = 100;
103 
104  // Set up RNG
105  int seed = 31415;
106  std::mt19937 gen(seed);
107 
108  // Constant B-Field and 0 B-field
109  auto constField = std::make_shared<ConstantBField>(Vector3{0.0, 0.0, 2_T});
110  auto zeroField = std::make_shared<NullBField>();
111 
112  // Set up stepper and propagator for constant B-field
113  EigenStepper<> stepper(constField);
114  auto propagator = std::make_shared<HelicalPropagator>(stepper);
115 
116  // Set up stepper and propagator for 0 B-field
117  StraightLineStepper straightStepper;
118  auto straightPropagator =
119  std::make_shared<StraightPropagator>(straightStepper);
120 
121  // Create perigee surface, initial track parameters will be relative to it
122  std::shared_ptr<PerigeeSurface> perigeeSurface{
123  Surface::makeShared<PerigeeSurface>(Vector3{0., 0., 0.})};
124 
125  // Vertex position and corresponding d0 and z0
126  Vector4 vtxPos;
127  double d0v{};
128  double z0v{};
129  double t0v{};
130  {
131  double x = vXYDist(gen);
132  double y = vXYDist(gen);
133  double z = vZDist(gen);
134  double t = vTDist(gen);
135  d0v = std::hypot(x, y);
136  z0v = z;
137  t0v = t;
138  vtxPos << x, y, z, t;
139  }
140 
141  // Vector storing the tracks that we linearize
142  std::vector<BoundTrackParameters> tracks;
143 
144  // Construct random track emerging from vicinity of vertex position
145  for (unsigned int iTrack = 0; iTrack < nTracks; iTrack++) {
146  // Random charge
147  double q = qDist(gen) < 0 ? -1. : 1.;
148 
149  // Random track parameters
150  BoundVector paramVec;
151  paramVec << d0v + d0Dist(gen), z0v + z0Dist(gen), phiDist(gen),
152  thetaDist(gen), q / pTDist(gen), t0v + tDist(gen);
153 
154  // Resolutions
155  double resD0 = resIPDist(gen);
156  double resZ0 = resIPDist(gen);
157  double resPh = resAngDist(gen);
158  double resTh = resAngDist(gen);
159  double resQp = resQoPDist(gen);
160  double resT = resTDist(gen);
161 
162  // Fill vector of track objects with simple covariance matrix
163  Covariance covMat;
164 
165  covMat << resD0 * resD0, 0., 0., 0., 0., 0., 0., resZ0 * resZ0, 0., 0., 0.,
166  0., 0., 0., resPh * resPh, 0., 0., 0., 0., 0., 0., resTh * resTh, 0.,
167  0., 0., 0., 0., 0., resQp * resQp, 0., 0., 0., 0., 0., 0., resT * resT;
168  tracks.emplace_back(perigeeSurface, paramVec, std::move(covMat),
170  }
171 
172  // Linearizer for constant field and corresponding state
173  AnalyticalLinearizer::Config linConfig(constField, propagator);
174  AnalyticalLinearizer linFactory(linConfig);
175  AnalyticalLinearizer::State linState(constField->makeCache(magFieldContext));
176 
177  NumericalLinearizer::Config numLinConfig(constField, propagator);
178  NumericalLinearizer numLinFactory(numLinConfig);
179  NumericalLinearizer::State numLinState(
180  constField->makeCache(magFieldContext));
181 
182  // Linearizer for 0 field and corresponding state
183  StraightAnalyticalLinearizer::Config straightLinConfig(straightPropagator);
184  StraightAnalyticalLinearizer straightLinFactory(straightLinConfig);
185  StraightAnalyticalLinearizer::State straightLinState(
186  zeroField->makeCache(magFieldContext));
187 
188  StraightNumericalLinearizer::Config numStraightLinConfig(straightPropagator);
189  StraightNumericalLinearizer numStraightLinFactory(numStraightLinConfig);
190  StraightNumericalLinearizer::State numStraightLinState(
191  zeroField->makeCache(magFieldContext));
192 
193  // Lambda for comparing outputs of the two linearization methods
194  // We compare the linearization result at the PCA to "linPoint"
195  auto checkLinearizers = [](auto& lin1, auto& linState1, auto& lin2,
196  auto& linState2, const BoundTrackParameters& track,
197  const Vector4& linPoint,
198  const auto& geometryContext,
199  const auto& fieldContext) {
200  // In addition to comparing the output of the linearizers, we check that
201  // they return non-zero quantities
202  BoundVector vecBoundZero = BoundVector::Zero();
203  BoundSquareMatrix matBoundZero = BoundSquareMatrix::Zero();
204  ActsMatrix<eBoundSize, 4> matBound2SPZero =
206  ActsMatrix<eBoundSize, 3> matBound2MomZero =
208 
209  // We check that the entries of the output quantities either
210  // -) have a relative difference of less than "relTol"
211  // or
212  // -) are both smaller than "small"
213  double relTol = 5e-4;
214  double small = 5e-4;
215 
216  std::shared_ptr<PerigeeSurface> perigee =
217  Surface::makeShared<PerigeeSurface>(VectorHelpers::position(linPoint));
218 
219  const LinearizedTrack linTrack1 =
220  lin1.linearizeTrack(track, linPoint[3], *perigee, geometryContext,
221  fieldContext, linState1)
222  .value();
223  const LinearizedTrack linTrack2 =
224  lin2.linearizeTrack(track, linPoint[3], *perigee, geometryContext,
225  fieldContext, linState2)
226  .value();
227 
228  // There should be no problem here because both linearizers compute
229  // "parametersAtPCA" the same way
230  CHECK_CLOSE_OR_SMALL(linTrack1.parametersAtPCA, linTrack2.parametersAtPCA,
231  relTol, small);
232  BOOST_CHECK_NE(linTrack1.parametersAtPCA, vecBoundZero);
233  BOOST_CHECK_NE(linTrack2.parametersAtPCA, vecBoundZero);
234 
235  // Compare position Jacobians
236  CHECK_CLOSE_OR_SMALL((linTrack1.positionJacobian),
237  (linTrack2.positionJacobian), relTol, small);
238  BOOST_CHECK_NE(linTrack1.positionJacobian, matBound2SPZero);
239  BOOST_CHECK_NE(linTrack2.positionJacobian, matBound2SPZero);
240 
241  // Compare momentum Jacobians
242  CHECK_CLOSE_OR_SMALL((linTrack1.momentumJacobian),
243  (linTrack2.momentumJacobian), relTol, small);
244  BOOST_CHECK_NE(linTrack1.momentumJacobian, matBound2MomZero);
245  BOOST_CHECK_NE(linTrack2.momentumJacobian, matBound2MomZero);
246 
247  // Again, both methods compute "covarianceAtPCA" the same way => this
248  // check should always work
249  CHECK_CLOSE_OR_SMALL(linTrack1.covarianceAtPCA, linTrack2.covarianceAtPCA,
250  relTol, small);
251  BOOST_CHECK_NE(linTrack1.covarianceAtPCA, matBoundZero);
252  BOOST_CHECK_NE(linTrack2.covarianceAtPCA, matBoundZero);
253 
254  // Check whether "linPoint" is saved correctly in the LinearizerTrack
255  // objects
256  BOOST_CHECK_EQUAL(linTrack1.linearizationPoint, linPoint);
257  BOOST_CHECK_EQUAL(linTrack2.linearizationPoint, linPoint);
258 
259  CHECK_CLOSE_OR_SMALL(linTrack1.constantTerm, linTrack2.constantTerm, relTol,
260  small);
261  BOOST_CHECK_NE(linTrack1.constantTerm, vecBoundZero);
262  BOOST_CHECK_NE(linTrack2.constantTerm, vecBoundZero);
263  };
264 
265  // Compare linearizers for all tracks
266  for (const BoundTrackParameters& trk : tracks) {
267  BOOST_TEST_CONTEXT("Linearization in constant magnetic field") {
268  checkLinearizers(linFactory, linState, numLinFactory, numLinState, trk,
269  vtxPos, geoContext, magFieldContext);
270  }
271  BOOST_TEST_CONTEXT("Linearization without magnetic field") {
272  checkLinearizers(straightLinFactory, straightLinState,
273  numStraightLinFactory, numStraightLinState, trk, vtxPos,
275  }
276  }
277 }
278 
279 } // namespace Test
280 } // namespace Acts