ATLAS Offline Software
STEP_Propagator.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3 */
4 
6 // Implementation file for class Trk::STEP_Propagator
8 // (c) ATLAS Detector software
10 // Based on RungeKuttaPropagator Version 1.0 21/05/2004 I.Gavrilenko
11 // Version 0.1 16/2/2005 Esben Lund
12 // Version 1.0 27/7/2006 Esben Lund
14 
16 //
20 
31 #include "TrkSurfaces/Surface.h"
34 // CLHEP
35 #include "CLHEP/Random/RandFlat.h"
36 #include "CLHEP/Random/RandGauss.h"
37 #include "GaudiKernel/PhysicalConstants.h"
38 // Gaudi
43 //
44 #include <cmath>
45 
47 #include "CxxUtils/vectorize.h"
49 
50 #include "CxxUtils/inline_hints.h"
51 
52 
53 
54 namespace{
55 
56 using Cache = Trk::STEP_Propagator::Cache;
58 // methods for magnetic field information
60 void
61 getField(Cache& cache, const double* ATH_RESTRICT R, double* ATH_RESTRICT H)
62 {
63  if (cache.m_solenoid) {
64  cache.m_fieldCache.getFieldZR(R, H);
65  } else {
66  cache.m_fieldCache.getField(R, H);
67  }
68 }
69 
70 void
71 getFieldGradient(Cache& cache, const double* ATH_RESTRICT R, double* ATH_RESTRICT H, double* ATH_RESTRICT dH)
72 {
73  if (cache.m_solenoid) {
74  cache.m_fieldCache.getFieldZR(R, H, dH);
75  } else {
76  cache.m_fieldCache.getField(R, H, dH);
77  }
78 }
79 
81 // Get the magnetic field and gradients
82 // Input: Globalposition
83 // Output: BG, which contains Bx, By, Bz, dBx/dx, dBx/dy, dBx/dz, dBy/dx,
84 // dBy/dy, dBy/dz, dBz/dx, dBz/dy, dBz/dz
86 
87 void
88 getMagneticField(Cache& cache,
89  const Amg::Vector3D& position,
90  bool getGradients,
91  double* ATH_RESTRICT BG)
92 {
93  constexpr double magScale = 10000.;
94  const double* R = position.data();
95  double H[3];
96  double dH[9];
97 
98  if (getGradients &&
99  cache.m_includeBgradients) { // field gradients needed and available
100 
101  getFieldGradient(cache, R, H, dH);
102  BG[0] = H[0] * magScale;
103  BG[1] = H[1] * magScale;
104  BG[2] = H[2] * magScale;
105  BG[3] = dH[0] * magScale;
106  BG[4] = dH[1] * magScale;
107  BG[5] = dH[2] * magScale;
108  BG[6] = dH[3] * magScale;
109  BG[7] = dH[4] * magScale;
110  BG[8] = dH[5] * magScale;
111  BG[9] = dH[6] * magScale;
112  BG[10] = dH[7] * magScale;
113  BG[11] = dH[8] * magScale;
114 
115  } else { // Homogenous field or no gradients needed, only retrieve the field
116  // strength.
117  getField(cache, R, H);
118  BG[0] = H[0] * magScale;
119  BG[1] = H[1] * magScale;
120  BG[2] = H[2] * magScale;
121 
122  for (int i = 3; i < 12; ++i) { // Set gradients to zero
123  BG[i] = 0.;
124  }
125  }
126 }
127 
129 // Create straight line in case of q/p = 0
131 
132 std::unique_ptr<Trk::TrackParameters>
133 createStraightLine(const Trk::TrackParameters* inputTrackParameters)
134 {
135  AmgVector(5) lp = inputTrackParameters->parameters();
136  lp[Trk::qOverP] = 1. / 1e10;
137 
138  if (inputTrackParameters->type() == Trk::Curvilinear) {
139  return std::make_unique<Trk::CurvilinearParameters>(
140  inputTrackParameters->position(),
141  lp[2],
142  lp[3],
143  lp[4],
144  (inputTrackParameters->covariance() ? std::optional<AmgSymMatrix(5)>(*inputTrackParameters->covariance())
145  : std::nullopt));
146  }
147  return inputTrackParameters->associatedSurface().createUniqueTrackParameters(
148  lp[0],
149  lp[1],
150  lp[2],
151  lp[3],
152  lp[4],
153  (inputTrackParameters->covariance() ? std::optional<AmgSymMatrix(5)>(*inputTrackParameters->covariance())
154  : std::nullopt));
155 }
156 
157 std::unique_ptr<Trk::TrackParameters>
158 propagateNeutral(const Trk::TrackParameters& parm,
159  std::vector<Trk::DestSurf>& targetSurfaces,
160  Trk::PropDirection propDir,
161  std::vector<unsigned int>& solutions,
162  double& path,
163  bool usePathLimit,
164  bool returnCurv)
165 {
166 
167  // find nearest valid intersection
168  double tol = 0.001;
169  solutions.clear();
170  std::vector<std::pair<unsigned int, double>> currentDist;
171  currentDist.clear();
172  std::vector<std::pair<const Trk::Surface*, Trk::BoundaryCheck>>::iterator sIter = targetSurfaces.begin();
173  std::vector<std::pair<unsigned int, double>>::iterator oIter = currentDist.begin();
174  std::vector<Trk::DestSurf>::iterator sBeg = targetSurfaces.begin();
175 
176  const Amg::Vector3D& position(parm.position());
177  Amg::Vector3D direction(parm.momentum().normalized());
178 
179  for (; sIter != targetSurfaces.end(); ++sIter) {
180  Trk::DistanceSolution distSol = (*sIter).first->straightLineDistanceEstimate(position, direction);
181  if (distSol.numberOfSolutions() > 0) {
182  unsigned int numSf = sIter - sBeg;
183  if (distSol.first() > tol) {
184  if (!currentDist.empty()) {
185  oIter = currentDist.end();
186  while (oIter != currentDist.begin() && distSol.first() < (*(oIter - 1)).second)
187  --oIter;
188  oIter = currentDist.insert(oIter, std::pair<unsigned int, double>(numSf, distSol.first()));
189  } else {
190  currentDist.emplace_back(numSf, distSol.first());
191  }
192  }
193  if (distSol.numberOfSolutions() > 1 && distSol.second() > tol) {
194  if (!currentDist.empty()) {
195  oIter = currentDist.end();
196  while (oIter != currentDist.begin() && distSol.second() < (*(oIter - 1)).second)
197  --oIter;
198  oIter = currentDist.insert(oIter, std::pair<unsigned int, double>(numSf, distSol.second()));
199  } else {
200  currentDist.emplace_back(numSf, distSol.second());
201  }
202  }
203  }
204  }
205 
206  // loop over surfaces, find valid intersections
207  for (oIter = currentDist.begin(); oIter != currentDist.end(); ++oIter) {
208  Amg::Vector3D xsct = position + propDir * direction * ((*oIter).second);
209  if (targetSurfaces[(*oIter).first].first->isOnSurface(xsct, (*oIter).second, 0.001, 0.001)) {
210  if (usePathLimit && path > 0. && path < ((*oIter).second)) {
211  Amg::Vector3D endpoint = position + propDir * direction * path;
212  return std::make_unique<Trk::CurvilinearParameters>(endpoint, parm.momentum(), parm.charge());
213  }
214  path = (*oIter).second;
215  solutions.push_back((*oIter).first);
216  const Trk::Surface* sf = targetSurfaces[(*oIter).first].first;
217 
218  if (returnCurv || sf->type() == Trk::SurfaceType::Cone) {
219  return std::make_unique<Trk::CurvilinearParameters>(xsct, parm.momentum(), parm.charge());
220  }
221  return sf->createUniqueTrackParameters(xsct, parm.momentum(), parm.charge(), std::nullopt);
222  }
223  }
224 
225  return nullptr;
226 }
227 
228 void
229 clearMaterialEffects(Trk::STEP_Propagator::Cache& cache)
230 {
231  cache.m_delIoni = 0.;
232  cache.m_delRad = 0.;
233  cache.m_sigmaIoni = 0.;
234  cache.m_sigmaRad = 0.;
235 }
236 
238 // Calculate energy loss in MeV/mm.
240 double
241 dEds(Cache& cache, double p)
242 {
243  cache.m_delIoni = 0.;
244  cache.m_delRad = 0.;
245  cache.m_kazL = 0.;
246 
247  if (cache.m_particle == Trk::geantino || cache.m_particle == Trk::nonInteractingMuon)
248  return 0.;
249  if (cache.m_material->x0() == 0 || cache.m_material->averageZ() == 0)
250  return 0.;
251 
252  cache.m_delIoni =
253  Trk::MaterialInteraction::dEdl_ionization(p, *(cache.m_material), cache.m_particle, cache.m_sigmaIoni, cache.m_kazL);
254 
255  cache.m_delRad = Trk::MaterialInteraction::dEdl_radiation(p, *(cache.m_material), cache.m_particle, cache.m_sigmaRad);
256 
257  double eLoss = cache.m_MPV ? 0.9 * cache.m_delIoni + 0.15 * cache.m_delRad : cache.m_delIoni + cache.m_delRad;
258 
259  return eLoss;
260 }
261 
263 // dg/dlambda for non-electrons (g=dEdX and lambda=q/p).
265 
266 double
267 dgdlambda(Cache& cache, double l)
268 {
269  if (cache.m_particle == Trk::geantino || cache.m_particle == Trk::nonInteractingMuon)
270  return 0.;
271  if (cache.m_material->x0() == 0 || cache.m_material->averageZ() == 0)
272  return 0.;
273  if (cache.m_material->zOverAtimesRho() == 0)
274  return 0.;
275 
276  double p = std::abs(1. / l);
277  double m = Trk::ParticleMasses::mass[cache.m_particle];
279  double E = std::sqrt(p * p + m * m);
280  double beta = p / E;
281  double gamma = E / m;
282  double I = 16.e-6 * std::pow(cache.m_material->averageZ(), 0.9);
283  double kaz = 0.5 * 30.7075 * cache.m_material->zOverAtimesRho();
284 
285  // Bethe-Bloch
286  double lnCore = 4. * me * me / (m * m * m * m * I * I * l * l * l * l) / (1. + 2. * gamma * me / m + me * me / m * m);
287  double lnCore_deriv =
288  -4. * me * me / (m * m * m * m * I * I) *
289  std::pow(l * l * l * l + 2. * gamma * l * l * l * l * me / m + l * l * l * l * me * me / (m * m), -2) *
290  (4. * l * l * l + 8. * me * l * l * l * gamma / m - 2. * me * l / (m * m * m * gamma) +
291  4. * l * l * l * me * me / (m * m));
292  double ln_deriv = 2. * l * m * m * std::log(lnCore) + lnCore_deriv / (lnCore * beta * beta);
293  double Bethe_Bloch_deriv = -kaz * ln_deriv;
294 
295  // density effect, only valid for high energies (gamma > 10 -> p > 1GeV for muons)
296  if (gamma > 10.) {
297  double delta = 2. * std::log(28.816e-6 * std::sqrt(1000. * cache.m_material->zOverAtimesRho()) / I) +
298  2. * std::log(beta * gamma) - 1.;
299  double delta_deriv = -2. / (l * beta * beta) + 2. * delta * l * m * m;
300  Bethe_Bloch_deriv += kaz * delta_deriv;
301  }
302 
303  // Bethe-Heitler
304  double Bethe_Heitler_deriv = me * me / (m * m * cache.m_material->x0() * l * l * l * E);
305 
306  // Radiative corrections (e+e- pair production + photonuclear) for muons at energies above 8 GeV and below 1 TeV
307  double radiative_deriv = 0.;
308  if ((cache.m_particle == Trk::muon) && (E > 8000.)) {
309  if (E < 1.e6) {
310  radiative_deriv = 6.803e-5 / (cache.m_material->x0() * l * l * l * E) +
311  2. * 2.278e-11 / (cache.m_material->x0() * l * l * l) -
312  3. * 9.899e-18 * E / (cache.m_material->x0() * l * l * l);
313  } else {
314  radiative_deriv = 9.253e-5 / (cache.m_material->x0() * l * l * l * E);
315  }
316  }
317 
318  // return the total derivative
319  if (cache.m_MPV) {
320  return 0.9 * Bethe_Bloch_deriv + 0.15 * Bethe_Heitler_deriv + 0.15 * radiative_deriv; // Most probable value
321  } else {
322  return Bethe_Bloch_deriv + Bethe_Heitler_deriv + radiative_deriv; // Mean value
323  }
324 }
325 
327 // update material effects // to follow change of material
329 void
330 updateMaterialEffects(Cache& cache, double mom, double sinTheta, double path)
331 {
332  // collects material effects in between material dumps ( m_covariance )
333 
334  // collect straggling
335  if (cache.m_straggling) {
336  cache.m_covariance(4, 4) += cache.m_stragglingVariance;
337  cache.m_stragglingVariance = 0.;
338  }
339 
340  if (!cache.m_multipleScattering)
341  return;
342 
343  double totalMomentumLoss = mom - cache.m_matupd_lastmom;
344  double pathSinceLastUpdate = path - cache.m_matupd_lastpath;
345 
346  double pathAbs = std::abs(pathSinceLastUpdate);
347 
348  if (pathAbs < 1.e-03)
349  return;
350 
351  double matX0 = cache.m_material->x0();
352 
353  // calculate number of layers : TODO : thickness to be adjusted
354  int msLayers = int(pathAbs / cache.m_layXmax / matX0) + 1;
355 
356  double massSquared = Trk::ParticleMasses::mass[cache.m_particle] * Trk::ParticleMasses::mass[cache.m_particle];
357  double layerThickness = pathAbs / msLayers;
358  double radiationLengths = layerThickness / matX0;
359  sinTheta = std::max(sinTheta, 1e-20); // avoid division by zero
360  double remainingPath = pathAbs;
361  double momentum = 0.;
362  double mom2 = 0.;
363  double E = 0.;
364  double beta = 0.;
365  double dLambdads = 0.;
366  double thetaVariance = 0.;
367 
368  double average_dEds = totalMomentumLoss / pathAbs;
369 
370  double cumulatedVariance = cache.m_inputThetaVariance + cache.m_combinedCovariance(3, 3) + cache.m_covariance(3, 3);
371  if (cumulatedVariance < 0) {
372  cumulatedVariance = 0;
373  }
374  double cumulatedX0 = 0.;
375 
376  bool useCache = cache.m_extrapolationCache != nullptr;
377  if (useCache) {
378  double dX0 = std::abs(cache.m_combinedThickness) - pathAbs / matX0;
379  if (dX0 < 0)
380  dX0 = 0.;
381  if (cache.m_extrapolationCache->x0tot() > 0)
382  cumulatedX0 = cache.m_extrapolationCache->x0tot() + dX0;
383  }
384 
385  // calculate multiple scattering by summing the contributions from the layers
386  for (int layer = 1; layer <= msLayers; ++layer) {
387 
388  // calculate momentum in the middle of the layer by assuming a linear momentum loss
389  momentum = cache.m_matupd_lastmom + totalMomentumLoss * (layer - 0.5) / msLayers;
390 
391  mom2 = momentum * momentum;
392  E = std::sqrt(mom2 + massSquared);
393  beta = momentum / E;
394 
395  double C0 = 13.6 * 13.6 / mom2 / beta / beta;
396  double C1 = 2 * 0.038 * C0;
397  double C2 = 0.038 * 0.038 * C0;
398 
399  double MS2 = radiationLengths;
400 
401  dLambdads = -cache.m_charge * average_dEds * E / (momentum * momentum * momentum);
402  remainingPath -= layerThickness;
403 
404  // simple - step dependent formula
405  // thetaVariance = C0*MS2 + C1*MS2*log(MS2) + C2*MS2*log(MS2)*log(MS2);
406 
407  // replaced by the step-size independent code below
408  if (!useCache) {
409  cumulatedX0 = cumulatedVariance / C0;
410  if (cumulatedX0 > 0.001) {
411  double lX0 = log(cumulatedX0);
412  cumulatedX0 = cumulatedX0 / (1 + 2 * 0.038 * lX0 + 0.038 * 0.038 * lX0 * lX0);
413  }
414  }
415 
416  double MS2s = MS2 + cumulatedX0;
417  thetaVariance = C0 * MS2 + C1 * MS2s * log(MS2s) + C2 * MS2s * log(MS2s) * log(MS2s);
418 
419  if (cumulatedX0 > 0.001) {
420  double lX0 = log(cumulatedX0);
421  thetaVariance += -C1 * cumulatedX0 * lX0 - C2 * cumulatedX0 * lX0 * lX0;
422  }
423 
424  // Calculate ms covariance contributions and scale
425  double varScale = cache.m_scatteringScale * cache.m_scatteringScale;
426  double positionVariance = thetaVariance * (layerThickness * layerThickness / 3. + remainingPath * layerThickness +
427  remainingPath * remainingPath);
428  cache.m_covariance(0, 0) += varScale * positionVariance;
429  cache.m_covariance(1, 1) += varScale * positionVariance;
430  cache.m_covariance.fillSymmetric(
431  2, 0, cache.m_covariance(2, 0) + varScale * thetaVariance / (sinTheta) * (layerThickness / 2. + remainingPath));
432  cache.m_covariance(2, 2) += varScale * thetaVariance / (sinTheta * sinTheta);
433  cache.m_covariance.fillSymmetric(
434  3, 1, cache.m_covariance(3, 1) + varScale * thetaVariance * (-layerThickness / 2. - remainingPath));
435  cache.m_covariance(3, 3) += varScale * thetaVariance;
436  cache.m_covariance(4, 4) +=
437  varScale * 3. * thetaVariance * thetaVariance * layerThickness * layerThickness * dLambdads * dLambdads;
438  cumulatedVariance += varScale * thetaVariance;
439  cumulatedX0 += MS2;
440  }
441 
442  cache.m_matupd_lastmom = mom;
443  cache.m_matupd_lastpath = path;
444 }
445 
447 // Multiple scattering and straggling contribution to the covariance matrix
449 
450 void
451 covarianceContribution(Cache& cache,
452  const Trk::TrackParameters* trackParameters,
453  double path,
454  const Trk::TrackParameters* targetParms,
455  AmgSymMatrix(5)& measurementCovariance)
456 {
457  // kinematics
458  double finalMomentum = targetParms->momentum().mag();
459 
460  // first update to make sure all material counted
461  updateMaterialEffects(cache, finalMomentum, sin(trackParameters->momentum().theta()), path);
462 
463  double Jac[21];
465 
466  // Transform multiple scattering and straggling covariance from curvilinear to local system
467  AmgSymMatrix(5) localMSCov =
468  Trk::RungeKuttaUtils::newCovarianceMatrix(Jac, cache.m_combinedCovariance + cache.m_covariance);
469 
470  measurementCovariance += localMSCov;
471 }
472 
474 // Multiple scattering and straggling contribution to the covariance matrix in curvilinear representation
476 
477 void
478 covarianceContribution(Cache& cache,
479  const Trk::TrackParameters* trackParameters,
480  double path,
481  double finalMomentum,
482  AmgSymMatrix(5)& measurementCovariance)
483 {
484  // first update to make sure all material counted
485  updateMaterialEffects(cache, finalMomentum, sin(trackParameters->momentum().theta()), path);
486 
487  // Add measurement errors and multiple scattering + straggling covariance
488  measurementCovariance += cache.m_combinedCovariance + cache.m_covariance;
489 }
490 
492 // Runge Kutta trajectory model (units->mm,MeV,kGauss)
493 //
494 // Runge Kutta method is adaptive Runge-Kutta-Nystrom.
495 // This is the default STEP method
497 
498 // We compile this package with optimization, even in debug builds; otherwise,
499 // the heavy use of Eigen makes it too slow. However, from here we may call
500 // to out-of-line Eigen code that is linked from other DSOs; in that case,
501 // it would not be optimized. Avoid this by forcing all Eigen code
502 // to be inlined here if possible.
504 bool
505 rungeKuttaStep(Cache& cache,
506  bool errorPropagation,
507  double& h,
508  double* ATH_RESTRICT P,
509  double* ATH_RESTRICT dDir,
510  double* ATH_RESTRICT BG1,
511  bool& firstStep,
512  double& distanceStepped)
513 {
514  constexpr double sol = 0.0299792458; //speed of light * kilogauss in method units
515  double charge = 0;
516  P[6] >= 0. ? charge = 1. : charge = -1.; // Set charge
517  double lambda1 = P[6];
518  double lambda2 = P[6]; // Store inverse momentum for Jacobian transport
519  double lambda3 = P[6];
520  double lambda4 = P[6];
521  double dP1 = 0.;
522  double dP2 = 0.;
523  double dP3 = 0.;
524  double dP4 = 0.; // dp/ds = -g*E/p for positive g=dE/ds
525  double dL1 = 0.;
526  double dL2 = 0.;
527  double dL3 = 0.;
528  double dL4 = 0.; // factor used for calculating dCM/dCM, P[41], in the Jacobian.
529  double initialMomentum = std::abs(1. / P[6]); // Set initial momentum
530  const Amg::Vector3D initialPos(P[0], P[1], P[2]); // Set initial values for position
531  const Amg::Vector3D initialDir(P[3], P[4], P[5]); // Set initial values for direction.
532  if (!Amg::saneVector(initialPos) || !Amg::saneVector(initialDir)) return false;
533  // Directions at the different points. Used by the error propagation
534  Amg::Vector3D dir1;
535  Amg::Vector3D dir2;
536  Amg::Vector3D dir3;
537  Amg::Vector3D dir4;
538  // Bx, By, Bz, dBx/dx, dBx/dy, dBx/dz, dBy/dx, dBy/dy, dBy/dz, dBz/dx, dBz/dy,dBz/dz
539  double BG23[12] = { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. };
540  // The gradients are used by the error propagation
541  double BG4[12] = { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. };
542  double g = 0.; // Energyloss in Mev/mm.
543  double dgdl = 0.; // dg/dlambda in Mev/mm.
544  double particleMass = Trk::ParticleMasses::mass[cache.m_particle]; // Get particle mass from ParticleHypothesis
545  int steps = 0;
546 
547  // POINT 1. Only calculate this once per step, even if step is rejected. This
548  // point is independant of the step length, h
549  double momentum = initialMomentum; // Current momentum
550  if (cache.m_energyLoss && cache.m_matPropOK) {
551  g = dEds(cache, momentum); // Use the same energy loss throughout the step.
552  double E = std::sqrt(momentum * momentum + particleMass * particleMass);
553  dP1 = g * E / momentum;
554  if (errorPropagation) {
555  if (cache.m_includeGgradient) {
556  dgdl = dgdlambda(cache, lambda1); // Use this value throughout the step.
557  }
558  dL1 =
559  -lambda1 * lambda1 * g * E * (3. - (momentum * momentum) / (E * E)) - lambda1 * lambda1 * lambda1 * E * dgdl;
560  }
561  }
562 
563  Amg::Vector3D pos = initialPos; // Coordinate solution
564  Amg::Vector3D dir = initialDir; // Direction solution
565  dir1 = dir;
566  if (firstStep) { // Poll BG1 if this is the first step, else use recycled BG4
567  firstStep = false;
568  // Get the gradients needed for the error propagation if errorPropagation=true
569  getMagneticField(cache,
570  pos,
571  errorPropagation,
572  BG1); // Get the gradients needed for the error propagation if errorPropagation=true
573  }
574  // Lorentz force, d2r/ds2 = lambda * (dir x B)
575  Amg::Vector3D k1(
576  dir.y() * BG1[2] - dir.z() * BG1[1], dir.z() * BG1[0] - dir.x() * BG1[2], dir.x() * BG1[1] - dir.y() * BG1[0]);
577  k1 = sol * lambda1 * k1;
578 
579  while (true) { // Repeat step until error estimate is within the requested tolerance
580  if (steps++ > cache.m_maxSteps)
581  return false; // Abort propagation
582  // POINT 2
583  if (cache.m_energyLoss && cache.m_matPropOK) {
584  momentum = initialMomentum + (h / 2.) * dP1;
585  if (momentum <= cache.m_momentumCutOff)
586  return false; // Abort propagation
587  double E = std::sqrt(momentum * momentum + particleMass * particleMass);
588  dP2 = g * E / momentum;
589  lambda2 = charge / momentum;
590  if (errorPropagation) {
591  dL2 =
592  -lambda2 * lambda2 * g * E * (3. - (momentum * momentum) / (E * E)) - lambda2 * lambda2 * lambda2 * E * dgdl;
593  }
594  }
595  pos = initialPos + (h / 2.) * initialDir + (h * h / 8.) * k1;
596  dir = initialDir + (h / 2.) * k1;
597  dir2 = dir;
598 
599  getMagneticField(cache, pos, errorPropagation, BG23);
600  Amg::Vector3D k2(dir.y() * BG23[2] - dir.z() * BG23[1],
601  dir.z() * BG23[0] - dir.x() * BG23[2],
602  dir.x() * BG23[1] - dir.y() * BG23[0]);
603  k2 = sol * lambda2 * k2;
604 
605  // POINT 3. Same position and B-field as point 2.
606  if (cache.m_energyLoss && cache.m_matPropOK) {
607  momentum = initialMomentum + (h / 2.) * dP2;
608  if (momentum <= cache.m_momentumCutOff)
609  return false; // Abort propagation
610  double E = std::sqrt(momentum * momentum + particleMass * particleMass);
611  dP3 = g * E / momentum;
612  lambda3 = charge / momentum;
613  if (errorPropagation) {
614  dL3 =
615  -lambda3 * lambda3 * g * E * (3. - (momentum * momentum) / (E * E)) - lambda3 * lambda3 * lambda3 * E * dgdl;
616  }
617  }
618  dir = initialDir + (h / 2.) * k2;
619  dir3 = dir;
620  Amg::Vector3D k3(dir.y() * BG23[2] - dir.z() * BG23[1],
621  dir.z() * BG23[0] - dir.x() * BG23[2],
622  dir.x() * BG23[1] - dir.y() * BG23[0]);
623  k3 = sol * lambda3 * k3;
624 
625  // POINT 4
626  if (cache.m_energyLoss && cache.m_matPropOK) {
627  momentum = initialMomentum + h * dP3;
628  if (momentum <= cache.m_momentumCutOff)
629  return false; // Abort propagation
630  double E = std::sqrt(momentum * momentum + particleMass * particleMass);
631  dP4 = g * E / momentum;
632  lambda4 = charge / momentum;
633  if (errorPropagation) {
634  dL4 =
635  -lambda4 * lambda4 * g * E * (3. - (momentum * momentum) / (E * E)) - lambda4 * lambda4 * lambda4 * E * dgdl;
636  }
637  }
638  pos = initialPos + h * initialDir + (h * h / 2.) * k3;
639  dir = initialDir + h * k3;
640  dir4 = dir;
641 
642  // MT Field cache is stored in cache
643  getMagneticField(cache, pos, errorPropagation, BG4);
644  Amg::Vector3D k4(
645  dir.y() * BG4[2] - dir.z() * BG4[1], dir.z() * BG4[0] - dir.x() * BG4[2], dir.x() * BG4[1] - dir.y() * BG4[0]);
646  k4 = sol * lambda4 * k4;
647 
648  // Estimate local error and avoid division by zero
649  Amg::Vector3D errorPos((h * h) * (k1 - k2 - k3 + k4));
650  double errorEstimate = std::max(errorPos.mag(), 1e-20);
651 
652  // Use the error estimate to calculate new step length. h is returned by reference.
653  distanceStepped = h; // Store old step length.
654  h = h * std::min(std::max(0.25, std::sqrt(std::sqrt(cache.m_tolerance / errorEstimate))), 4.);
655 
656  // Repeat step with the new step size if error is too big.
657  if (errorEstimate > 4. * cache.m_tolerance)
658  continue;
659 
660  // Step was ok. Store solutions.
661  // Update positions.
662  pos = initialPos + distanceStepped * initialDir + (distanceStepped * distanceStepped / 6.) * (k1 + k2 + k3);
663  P[0] = pos.x();
664  P[1] = pos.y();
665  P[2] = pos.z();
666 
667  // update directions
668  dir = initialDir + (distanceStepped / 6.) * (k1 + 2. * k2 + 2. * k3 + k4);
669  P[3] = dir.x();
670  P[4] = dir.y();
671  P[5] = dir.z();
672 
673  // Normalize direction
674  double norm = 1. / std::sqrt(P[3] * P[3] + P[4] * P[4] + P[5] * P[5]);
675  P[3] = norm * P[3];
676  P[4] = norm * P[4];
677  P[5] = norm * P[5];
678 
679  // Update inverse momentum if energyloss is switched on
680  if (cache.m_energyLoss && cache.m_matPropOK) {
681  momentum = initialMomentum + (distanceStepped / 6.) * (dP1 + 2. * dP2 + 2. * dP3 + dP4);
682  if (momentum <= cache.m_momentumCutOff)
683  return false; // Abort propagation
684  P[6] = charge / momentum;
685  }
686 
687  // dDir provides a small correction to the final tiny step in PropagateWithJacobian
688  dDir[0] = k4.x();
689  dDir[1] = k4.y();
690  dDir[2] = k4.z();
691 
692  // Transport Jacobian using the same step length, points and magnetic fields as for the track parameters
693  // BG contains Bx, By, Bz, dBx/dx, dBx/dy, dBx/dz, dBy/dx, dBy/dy, dBy/dz, dBz/dx, dBz/dy, dBz/dz
694  // 0 1 2 3 4 5 6 7 8 9 10 11
695  if (errorPropagation) {
696  double initialjLambda = P[41]; // dLambda/dLambda
697  double jLambda = initialjLambda;
698  double jdL1 = 0.;
699  double jdL2 = 0.;
700  double jdL3 = 0.;
701  double jdL4 = 0.;
702 
703  for (int i = 7; i < 42; i += 7) {
704 
705  // POINT 1
706  const Amg::Vector3D initialjPos(P[i], P[i + 1], P[i + 2]);
707  const Amg::Vector3D initialjDir(P[i + 3], P[i + 4], P[i + 5]);
708 
709  if (!Amg::saneVector(initialjPos) || !Amg::saneVector(initialjDir)) return false;
710 
711  Amg::Vector3D jPos = initialjPos;
712  Amg::Vector3D jDir = initialjDir;
713 
714  // B-field terms
715  Amg::Vector3D jk1(jDir.y() * BG1[2] - jDir.z() * BG1[1],
716  jDir.z() * BG1[0] - jDir.x() * BG1[2],
717  jDir.x() * BG1[1] - jDir.y() * BG1[0]);
718 
719  // B-field gradient terms
720  if (cache.m_includeBgradients) {
722  (dir1.y() * BG1[9] - dir1.z() * BG1[6]) * jPos.x() + (dir1.y() * BG1[10] - dir1.z() * BG1[7]) * jPos.y() +
723  (dir1.y() * BG1[11] - dir1.z() * BG1[8]) * jPos.z(),
724  (dir1.z() * BG1[3] - dir1.x() * BG1[9]) * jPos.x() + (dir1.z() * BG1[4] - dir1.x() * BG1[10]) * jPos.y() +
725  (dir1.z() * BG1[5] - dir1.x() * BG1[11]) * jPos.z(),
726  (dir1.x() * BG1[6] - dir1.y() * BG1[3]) * jPos.x() + (dir1.x() * BG1[7] - dir1.y() * BG1[4]) * jPos.y() +
727  (dir1.x() * BG1[8] - dir1.y() * BG1[5]) * jPos.z());
728  jk1 = jk1 + C;
729  }
730  jk1 = sol * lambda1 * jk1;
731 
732  // Last column of the A-matrix
733  if (i == 35) { // Only dLambda/dLambda, in the last row of the jacobian, is different from zero
734  jdL1 = dL1 * jLambda; // Energy loss term. dL1 = 0 if no material effects -> jLambda = P[41] is unchanged
735  jk1 = jk1 + k1 * jLambda / lambda1; // B-field terms
736  }
737 
738  // POINT 2
739  jPos = initialjPos + (distanceStepped / 2.) * initialjDir + (distanceStepped * distanceStepped / 8.) * jk1;
740  jDir = initialjDir + (distanceStepped / 2.) * jk1;
741 
742  Amg::Vector3D jk2(jDir.y() * BG23[2] - jDir.z() * BG23[1],
743  jDir.z() * BG23[0] - jDir.x() * BG23[2],
744  jDir.x() * BG23[1] - jDir.y() * BG23[0]);
745 
746  if (cache.m_includeBgradients) {
747  Amg::Vector3D C((dir2.y() * BG23[9] - dir2.z() * BG23[6]) * jPos.x() +
748  (dir2.y() * BG23[10] - dir2.z() * BG23[7]) * jPos.y() +
749  (dir2.y() * BG23[11] - dir2.z() * BG23[8]) * jPos.z(),
750  (dir2.z() * BG23[3] - dir2.x() * BG23[9]) * jPos.x() +
751  (dir2.z() * BG23[4] - dir2.x() * BG23[10]) * jPos.y() +
752  (dir2.z() * BG23[5] - dir2.x() * BG23[11]) * jPos.z(),
753  (dir2.x() * BG23[6] - dir2.y() * BG23[3]) * jPos.x() +
754  (dir2.x() * BG23[7] - dir2.y() * BG23[4]) * jPos.y() +
755  (dir2.x() * BG23[8] - dir2.y() * BG23[5]) * jPos.z());
756  jk2 = jk2 + C;
757  }
758  jk2 = sol * lambda2 * jk2;
759 
760  if (i == 35) {
761  jLambda = initialjLambda + (distanceStepped / 2.) * jdL1;
762  jdL2 = dL2 * jLambda;
763  jk2 = jk2 + k2 * jLambda / lambda2;
764  }
765 
766  // POINT 3
767  jDir = initialjDir + (distanceStepped / 2.) * jk2;
768 
769  Amg::Vector3D jk3(jDir.y() * BG23[2] - jDir.z() * BG23[1],
770  jDir.z() * BG23[0] - jDir.x() * BG23[2],
771  jDir.x() * BG23[1] - jDir.y() * BG23[0]);
772 
773  if (cache.m_includeBgradients) {
774  Amg::Vector3D C((dir3.y() * BG23[9] - dir3.z() * BG23[6]) * jPos.x() +
775  (dir3.y() * BG23[10] - dir3.z() * BG23[7]) * jPos.y() +
776  (dir3.y() * BG23[11] - dir3.z() * BG23[8]) * jPos.z(),
777  (dir3.z() * BG23[3] - dir3.x() * BG23[9]) * jPos.x() +
778  (dir3.z() * BG23[4] - dir3.x() * BG23[10]) * jPos.y() +
779  (dir3.z() * BG23[5] - dir3.x() * BG23[11]) * jPos.z(),
780  (dir3.x() * BG23[6] - dir3.y() * BG23[3]) * jPos.x() +
781  (dir3.x() * BG23[7] - dir3.y() * BG23[4]) * jPos.y() +
782  (dir3.x() * BG23[8] - dir3.y() * BG23[5]) * jPos.z());
783  jk3 = jk3 + C;
784  }
785  jk3 = sol * lambda3 * jk3;
786 
787  if (i == 35) {
788  jLambda = initialjLambda + (distanceStepped / 2.) * jdL2;
789  jdL3 = dL3 * jLambda;
790  jk3 = jk3 + k3 * jLambda / lambda3;
791  }
792 
793  // POINT 4
794  jPos = initialjPos + distanceStepped * initialjDir + (distanceStepped * distanceStepped / 2.) * jk3;
795  jDir = initialjDir + distanceStepped * jk3;
796 
797  Amg::Vector3D jk4(jDir.y() * BG4[2] - jDir.z() * BG4[1],
798  jDir.z() * BG4[0] - jDir.x() * BG4[2],
799  jDir.x() * BG4[1] - jDir.y() * BG4[0]);
800 
801  if (cache.m_includeBgradients) {
803  (dir4.y() * BG4[9] - dir4.z() * BG4[6]) * jPos.x() + (dir4.y() * BG4[10] - dir4.z() * BG4[7]) * jPos.y() +
804  (dir4.y() * BG4[11] - dir4.z() * BG4[8]) * jPos.z(),
805  (dir4.z() * BG4[3] - dir4.x() * BG4[9]) * jPos.x() + (dir4.z() * BG4[4] - dir4.x() * BG4[10]) * jPos.y() +
806  (dir4.z() * BG4[5] - dir4.x() * BG4[11]) * jPos.z(),
807  (dir4.x() * BG4[6] - dir4.y() * BG4[3]) * jPos.x() + (dir4.x() * BG4[7] - dir4.y() * BG4[4]) * jPos.y() +
808  (dir4.x() * BG4[8] - dir4.y() * BG4[5]) * jPos.z());
809  jk4 = jk4 + C;
810  }
811  jk4 = sol * lambda4 * jk4;
812 
813  if (i == 35) {
814  jLambda = initialjLambda + distanceStepped * jdL3;
815  jdL4 = dL4 * jLambda;
816  jk4 = jk4 + k4 * jLambda / lambda4;
817  }
818 
819  // solution
820  jPos =
821  initialjPos + distanceStepped * initialjDir + (distanceStepped * distanceStepped / 6.) * (jk1 + jk2 + jk3);
822  jDir = initialjDir + (distanceStepped / 6.) * (jk1 + 2. * jk2 + 2. * jk3 + jk4);
823  if (i == 35) {
824  jLambda = initialjLambda + (distanceStepped / 6.) * (jdL1 + 2. * jdL2 + 2. * jdL3 + jdL4);
825  }
826 
827  // Update positions
828  P[i] = jPos.x();
829  P[i + 1] = jPos.y();
830  P[i + 2] = jPos.z();
831 
832  // update directions
833  P[i + 3] = jDir.x();
834  P[i + 4] = jDir.y();
835  P[i + 5] = jDir.z();
836  }
837  P[41] = jLambda; // update dLambda/dLambda
838  }
839 
840  // Store BG4 for use as BG1 in the next step
841  for (int i = 0; i < 12; ++i) {
842  BG1[i] = BG4[i];
843  }
844  return true;
845  }
846 }
848 // Runge Kutta main program for propagation with or without Jacobian
850 bool
851 propagateWithJacobianImpl(Cache& cache,
852  bool errorPropagation,
853  Trk::SurfaceType surfaceType,
854  double* ATH_RESTRICT targetSurface,
855  double* ATH_RESTRICT P,
856  double& path)
857 {
858  double maxPath = cache.m_maxPath; // Max path allowed
859  double* pos = &P[0]; // Start coordinates
860  double* dir = &P[3]; // Start directions
861  double dDir[3] = { 0., 0., 0. }; // Start directions derivs. Zero in case of no RK steps
862  int targetPassed = 0; // how many times have we passed the target?
863  double previousDistance = 0.;
864  double distanceStepped = 0.;
865  bool distanceEstimationSuccessful = false; // Was the distance estimation successful?
866  double BG1[12] = { 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0. }; // Bx, By, Bz, dBx/dx, dBx/dy, dBx/dz,
867  // dBy/dx, dBy/dy, dBy/dz, dBz/dx, dBz/dy, dBz/dz at first point
868  bool firstStep = true; // Poll BG1, else recycle BG4
869  double absolutePath = 0.; // Absolute path to register oscillating behaviour
870  int steps = 0;
871  path = 0.; // signed path of the trajectory
872  double mom = 0.;
873  double beta = 1.;
874 
875  double distanceToTarget = Trk::RungeKuttaUtils::stepEstimator(
876  surfaceType, targetSurface, P, distanceEstimationSuccessful);
877  if (!distanceEstimationSuccessful) {
878  return false;
879  }
880 
881  // Set initial step length to 100mm or the distance to the target surface.
882  double h = 0;
883  distanceToTarget > 100. ? h = 100. : distanceToTarget < -100. ? h = -100. : h = distanceToTarget;
884 
885  // Step to within distanceTolerance, then do the rest as a Taylor expansion.
886  // Keep distanceTolerance within [1 nanometer, 10 microns].
887  // This means that no final Taylor expansions beyond 10 microns and no
888  // Runge-Kutta steps less than 1 nanometer are allowed.
889  double distanceTolerance = std::min(std::max(std::abs(distanceToTarget) * cache.m_tolerance, 1e-6), 1e-2);
890 
891  while (std::abs(distanceToTarget) > distanceTolerance) { // Step until within tolerance
892  // Do the step. Stop the propagation if the energy goes below m_momentumCutOff
893  if (!rungeKuttaStep(cache, errorPropagation, h, P, dDir, BG1, firstStep, distanceStepped))
894  return false;
895  path += distanceStepped;
896  absolutePath += std::abs(distanceStepped);
897 
898  if (std::abs(distanceStepped) > 0.001) {
899  cache.m_sigmaIoni = cache.m_sigmaIoni - cache.m_kazL * log(std::abs(distanceStepped)); // the non-linear term
900  }
901  // update straggling covariance
902  if (errorPropagation && cache.m_straggling) {
903  double sigTot2 = cache.m_sigmaIoni * cache.m_sigmaIoni + cache.m_sigmaRad * cache.m_sigmaRad;
904  // /(beta*beta*p*p*p*p) transforms Var(E) to Var(q/p)
905  mom = std::abs(1. / P[6]);
906  beta = mom / std::sqrt(mom * mom + cache.m_particleMass * cache.m_particleMass);
907  double bp2 = beta * mom * mom;
908  cache.m_stragglingVariance += sigTot2 / (bp2 * bp2) * distanceStepped * distanceStepped;
909  }
910  if (cache.m_matstates || errorPropagation)
911  cache.m_combinedEloss.update(cache.m_delIoni * distanceStepped,
912  cache.m_sigmaIoni * std::abs(distanceStepped),
913  cache.m_delRad * distanceStepped,
914  cache.m_sigmaRad * std::abs(distanceStepped),
915  cache.m_MPV);
916  // Calculate new distance to target
917  previousDistance = std::abs(distanceToTarget);
918  distanceToTarget = Trk::RungeKuttaUtils::stepEstimator(
919  surfaceType, targetSurface, P, distanceEstimationSuccessful);
920  if (!distanceEstimationSuccessful)
921  return false;
922 
923  // If h and distance are in opposite directions, target is passed. Flip propagation direction
924  if (h * distanceToTarget < 0.) {
925  h = -h; // Flip direction
926  ++targetPassed;
927  }
928  // don't step beyond surface
929  if (std::abs(h) > std::abs(distanceToTarget))
930  h = distanceToTarget;
931 
932  // Abort if maxPath is reached or solution is diverging
933  if ((targetPassed > 3 && std::abs(distanceToTarget) >= previousDistance) || (absolutePath > maxPath))
934  return false;
935 
936  if (steps++ > cache.m_maxSteps)
937  return false; // Too many steps, something is wrong
938  }
939 
940  if (cache.m_material && cache.m_material->x0() != 0.)
941  cache.m_combinedThickness += std::abs(path) / cache.m_material->x0();
942 
943  // Use Taylor expansions to step the remaining distance (typically microns).
944  path = path + distanceToTarget;
945 
946  // pos = pos + h*dir + 1/2*h*h*dDir. Second order Taylor expansion.
947  pos[0] = pos[0] + distanceToTarget * (dir[0] + 0.5 * distanceToTarget * dDir[0]);
948  pos[1] = pos[1] + distanceToTarget * (dir[1] + 0.5 * distanceToTarget * dDir[1]);
949  pos[2] = pos[2] + distanceToTarget * (dir[2] + 0.5 * distanceToTarget * dDir[2]);
950 
951  // dir = dir + h*dDir. First order Taylor expansion (Euler).
952  dir[0] = dir[0] + distanceToTarget * dDir[0];
953  dir[1] = dir[1] + distanceToTarget * dDir[1];
954  dir[2] = dir[2] + distanceToTarget * dDir[2];
955 
956  // Normalize dir
957  double norm = 1. / std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
958  dir[0] = norm * dir[0];
959  dir[1] = norm * dir[1];
960  dir[2] = norm * dir[2];
961  P[42] = dDir[0];
962  P[43] = dDir[1];
963  P[44] = dDir[2];
964  return true;
965 }
966 
968 // Main function for track propagation with or without jacobian
970 std::unique_ptr<Trk::TrackParameters>
971 propagateRungeKuttaImpl(Cache& cache,
972  bool errorPropagation,
973  const Trk::TrackParameters& inputTrackParameters,
974  const Trk::Surface& targetSurface,
975  Trk::PropDirection propagationDirection,
976  const Trk::MagneticFieldProperties& mft,
978  const Trk::BoundaryCheck& boundaryCheck,
979  double* Jacobian,
980  bool returnCurv)
981 {
982  // Store for later use
983  cache.m_particle = particle;
984  cache.m_charge = inputTrackParameters.charge();
985 
986  std::unique_ptr<Trk::TrackParameters> trackParameters{};
987 
988  // Bfield mode
989  mft.magneticFieldMode() == Trk::FastField ? cache.m_solenoid = true : cache.m_solenoid = false;
990 
991  // Check inputvalues
992  if (cache.m_tolerance <= 0.)
993  return nullptr;
994  if (cache.m_momentumCutOff < 0.)
995  return nullptr;
996 
997  // Set momentum to 1e10 (straight line) and charge to + if q/p is zero
998  if (inputTrackParameters.parameters()[Trk::qOverP] == 0) {
999  trackParameters = createStraightLine(&inputTrackParameters);
1000  if (!trackParameters) {
1001  return nullptr;
1002  }
1003  } else {
1004  trackParameters.reset(inputTrackParameters.clone());
1005  }
1006 
1007  if (std::abs(1. / trackParameters->parameters()[Trk::qOverP]) <= cache.m_momentumCutOff) {
1008  return nullptr;
1009  }
1010 
1011  // Check for empty volumes. If x != x then x is not a number.
1012  if (cache.m_matPropOK && ((cache.m_material->zOverAtimesRho() == 0.) || (cache.m_material->x0() == 0.) ||
1013  (cache.m_material->zOverAtimesRho() != cache.m_material->zOverAtimesRho()))) {
1014  cache.m_matPropOK = false;
1015  }
1016  if (cache.m_matPropOK && cache.m_straggling)
1017  cache.m_stragglingVariance = 0.;
1018  if (errorPropagation || cache.m_matstates) {
1019  cache.m_combinedCovariance.setZero();
1020  cache.m_covariance.setZero();
1021  // this needs debugging
1022  cache.m_inputThetaVariance = trackParameters->covariance() ? (*trackParameters->covariance())(3, 3) : 0.;
1023  cache.m_combinedEloss.set(0., 0., 0., 0., 0., 0.);
1024  cache.m_combinedThickness = 0.;
1025  }
1026 
1027  if (!Trk::RungeKuttaUtils::transformLocalToGlobal(errorPropagation, *trackParameters, cache.m_P)) {
1028  return nullptr;
1029  }
1030  double path = 0.;
1031 
1032  const Amg::Transform3D& T = targetSurface.transform();
1033  Trk::SurfaceType ty = targetSurface.type();
1034 
1035  if (ty == Trk::SurfaceType::Plane || ty == Trk::SurfaceType::Disc) {
1036  double s[4];
1037  double d = T(0, 3) * T(0, 2) + T(1, 3) * T(1, 2) + T(2, 3) * T(2, 2);
1038 
1039  if (d >= 0.) {
1040  s[0] = T(0, 2);
1041  s[1] = T(1, 2);
1042  s[2] = T(2, 2);
1043  s[3] = d;
1044  } else {
1045  s[0] = -T(0, 2);
1046  s[1] = -T(1, 2);
1047  s[2] = -T(2, 2);
1048  s[3] = -d;
1049  }
1050  if (!propagateWithJacobianImpl(cache, errorPropagation, ty, s, cache.m_P, path)) {
1051  return nullptr;
1052  }
1053  }
1054 
1055  else if (ty == Trk::SurfaceType::Line) {
1056 
1057  double s[6] = { T(0, 3), T(1, 3), T(2, 3), T(0, 2), T(1, 2), T(2, 2) };
1058  if (!propagateWithJacobianImpl(cache, errorPropagation, ty, s, cache.m_P, path)) {
1059  return nullptr;
1060  }
1061  }
1062 
1063  else if (ty == Trk::SurfaceType::Cylinder) {
1064 
1065  const Trk::CylinderSurface* cyl = static_cast<const Trk::CylinderSurface*>(&targetSurface);
1066  double s[9] = { T(0, 3), T(1, 3), T(2, 3), T(0, 2),
1067  T(1, 2), T(2, 2), cyl->bounds().r(), (double)propagationDirection,
1068  0. };
1069  if (!propagateWithJacobianImpl(cache, errorPropagation, ty, s, cache.m_P, path)) {
1070  return nullptr;
1071  }
1072  }
1073 
1074  else if (ty == Trk::SurfaceType::Cone) {
1075 
1076  double k = static_cast<const Trk::ConeSurface*>(&targetSurface)->bounds().tanAlpha();
1077  k = k * k + 1.;
1078  double s[9] = { T(0, 3), T(1, 3), T(2, 3), T(0, 2), T(1, 2), T(2, 2), k, (double)propagationDirection, 0. };
1079  if (!propagateWithJacobianImpl(cache, errorPropagation, ty, s, cache.m_P, path)) {
1080  return nullptr;
1081  }
1082  }
1083 
1084  else if (ty == Trk::SurfaceType::Perigee) {
1085 
1086  double s[6] = { T(0, 3), T(1, 3), T(2, 3), 0., 0., 1. };
1087  if (!propagateWithJacobianImpl(cache, errorPropagation, ty, s, cache.m_P, path)) {
1088  return nullptr;
1089  }
1090  }
1091 
1092  else { // assume curvilinear
1093 
1094  double s[4];
1095  double d = T(0, 3) * T(0, 2) + T(1, 3) * T(1, 2) + T(2, 3) * T(2, 2);
1096 
1097  if (d >= 0.) {
1098  s[0] = T(0, 2);
1099  s[1] = T(1, 2);
1100  s[2] = T(2, 2);
1101  s[3] = d;
1102  } else {
1103  s[0] = -T(0, 2);
1104  s[1] = -T(1, 2);
1105  s[2] = -T(2, 2);
1106  s[3] = -d;
1107  }
1108  if (!propagateWithJacobianImpl(cache, errorPropagation, ty, s, cache.m_P, path)) {
1109  return nullptr;
1110  }
1111  }
1112 
1113  if (propagationDirection * path < 0.) {
1114  return nullptr;
1115  }
1116 
1117  double localp[5];
1118  // output in curvilinear parameters
1119  if (returnCurv || ty == Trk::SurfaceType::Cone) {
1120 
1122  Amg::Vector3D gp(cache.m_P[0], cache.m_P[1], cache.m_P[2]);
1123 
1124  if (boundaryCheck && !targetSurface.isOnSurface(gp)) {
1125  return nullptr;
1126  }
1127 
1128  if (!errorPropagation || !trackParameters->covariance()) {
1129  return std::make_unique<Trk::CurvilinearParameters>(gp, localp[2], localp[3], localp[4]);
1130  }
1131 
1132  double useless[2];
1133  Trk::RungeKuttaUtils::transformGlobalToCurvilinear(true, cache.m_P, useless, Jacobian);
1134  AmgSymMatrix(5) measurementCovariance =
1135  Trk::RungeKuttaUtils::newCovarianceMatrix(Jacobian, *trackParameters->covariance());
1136 
1137  if (cache.m_matPropOK && (cache.m_multipleScattering || cache.m_straggling) && std::abs(path) > 0.)
1138  covarianceContribution(cache, trackParameters.get(), path, std::abs(1. / cache.m_P[6]), measurementCovariance);
1139 
1140  return std::make_unique<Trk::CurvilinearParameters>(
1141  gp, localp[2], localp[3], localp[4], std::move(measurementCovariance));
1142  }
1143 
1144  // Common transformation for all surfaces
1145  Trk::RungeKuttaUtils::transformGlobalToLocal(&targetSurface, errorPropagation, cache.m_P, localp, Jacobian);
1146 
1147  if (boundaryCheck) {
1148  Amg::Vector2D localPosition(localp[0], localp[1]);
1149  if (!targetSurface.insideBounds(localPosition, 0.)) {
1150  return nullptr;
1151  }
1152  }
1153 
1154  auto onTargetSurf =
1155  targetSurface.createUniqueTrackParameters(localp[0], localp[1], localp[2], localp[3], localp[4], std::nullopt);
1156 
1157  if (!errorPropagation || !trackParameters->covariance()) {
1158  return onTargetSurf;
1159  }
1160 
1161  // Errormatrix is included. Use Jacobian to calculate new covariance
1162  AmgSymMatrix(5) measurementCovariance =
1163  Trk::RungeKuttaUtils::newCovarianceMatrix(Jacobian, *trackParameters->covariance());
1164 
1165  // Calculate multiple scattering and straggling covariance contribution.
1166  if (cache.m_matPropOK && (cache.m_multipleScattering || cache.m_straggling) && std::abs(path) > 0.)
1167  covarianceContribution(cache, trackParameters.get(), path, onTargetSurf.get(), measurementCovariance);
1168 
1169  return targetSurface.createUniqueTrackParameters(
1170  localp[0], localp[1], localp[2], localp[3], localp[4], std::move(measurementCovariance));
1171 }
1172 
1173 } // end of anonymous namespace
1174 
1176 // Constructor
1178 
1179 Trk::STEP_Propagator::STEP_Propagator(const std::string& p, const std::string& n, const IInterface* t)
1180  : AthAlgTool(p, n, t),
1181  m_tolerance(1e-5) // Error tolerance of the propagator. A low tolerance gives a high accuracy.
1182  ,m_materialEffects(true) // Switches off all material effects if false. Does nothing when true.
1183  ,m_includeBgradients(true) // Include B-field gradients into the error propagation?
1184  ,m_includeGgradient(false) // Include dg/dlambda into the error propagation? Only relevant when energy loss is true.
1185  ,m_momentumCutOff(50.) // Minimum allowed momentum in MeV.
1186  ,m_multipleScattering(true) // Add multiple scattering to the covariance matrix?
1187  ,m_energyLoss(true) // Include energy loss?
1188  ,m_detailedEloss(true) // Provide the extended EnergyLoss object with MopIonization etc.
1189  ,m_straggling(true) // Add energy loss fluctuations (straggling) to the covariance matrix?
1190  ,m_MPV(false) // Use the most probable value of the energy loss, else use the mean energy loss.
1191  ,m_stragglingScale(1.) // Scale for adjusting the width of the energy loss fluctuations.
1192  ,m_scatteringScale(1.) // Scale for adjusting the multiple scattering contribution to the covariance matrix.
1193  ,m_maxPath(100000.) // Maximum propagation length in mm.
1194  ,m_maxSteps(10000) // Maximum number of allowed steps (to avoid infinite loops).
1195  ,m_layXmax(1.) // maximal layer thickness for multiple scattering calculations
1196  ,m_simulation(false) // flag for simulation mode
1197  ,m_rndGenSvc("AthRNGSvc", n)
1198  ,m_randomEngineName("FatrasRnd") {
1199  declareInterface<Trk::IPropagator>(this);
1200  declareProperty("Tolerance", m_tolerance);
1201  declareProperty("MaterialEffects", m_materialEffects);
1202  declareProperty("IncludeBgradients", m_includeBgradients);
1203  declareProperty("IncludeGgradient", m_includeGgradient);
1204  declareProperty("MomentumCutOff", m_momentumCutOff);
1205  declareProperty("MultipleScattering", m_multipleScattering);
1206  declareProperty("EnergyLoss", m_energyLoss);
1207  declareProperty("Straggling", m_straggling);
1208  declareProperty("MostProbableEnergyLoss", m_MPV);
1209  declareProperty("StragglingScale", m_stragglingScale);
1210  declareProperty("DetailedEloss", m_detailedEloss);
1211  declareProperty("MultipleScatteringScale", m_scatteringScale);
1212  declareProperty("MaxPath", m_maxPath);
1213  declareProperty("MaxSteps", m_maxSteps);
1214  declareProperty("MSstepMax", m_layXmax);
1215  declareProperty("SimulationMode", m_simulation);
1216  declareProperty("SimMatEffUpdator", m_simMatUpdator);
1217  declareProperty("RandomNumberService", m_rndGenSvc, "Random number generator");
1218  declareProperty("RandomStreamName", m_randomEngineName, "Name of the random number stream");
1219 }
1220 
1222 // Destructor
1224 
1226 
1227 // Athena standard methods
1228 // initialize
1230 
1231  // Read handle for AtlasFieldCacheCondObj
1232  ATH_CHECK(m_fieldCacheCondObjInputKey.initialize());
1233 
1234  if (!m_materialEffects) { // override all material interactions
1235  m_multipleScattering = false;
1236  m_energyLoss = false;
1237  m_straggling = false;
1238  } else if (!m_energyLoss) { // override straggling
1239  m_straggling = false;
1240  }
1241 
1242  if (m_simulation && m_simMatUpdator.retrieve().isFailure()) {
1243  ATH_MSG_WARNING("Simulation mode requested but material updator not found - no brem photon emission.");
1244  }
1245 
1246  if (m_simulation) {
1247  // get the random generator serice
1248  ATH_CHECK(m_rndGenSvc.retrieve());
1249  m_rngWrapper = m_rndGenSvc->getEngine(this, m_randomEngineName);
1250  }
1251 
1252  return StatusCode::SUCCESS;
1253 }
1254 
1255 // finalize
1257  return StatusCode::SUCCESS;
1258 }
1259 
1261 std::unique_ptr<Trk::NeutralParameters> Trk::STEP_Propagator::propagate(const Trk::NeutralParameters&,
1262  const Trk::Surface&,
1264  const Trk::BoundaryCheck&,
1265  bool) const {
1266  ATH_MSG_WARNING("[STEP_Propagator] STEP_Propagator does not handle neutral track parameters."
1267  << "Use the StraightLinePropagator instead.");
1268  return nullptr;
1269 }
1270 
1272 // Main function for track parameters and covariance matrix propagation
1274 std::unique_ptr<Trk::TrackParameters> Trk::STEP_Propagator::propagate(
1275  const EventContext& ctx, const Trk::TrackParameters& trackParameters, const Trk::Surface& targetSurface,
1276  Trk::PropDirection propagationDirection, const Trk::BoundaryCheck& boundaryCheck,
1277  const MagneticFieldProperties& magneticFieldProperties, ParticleHypothesis particle, bool returnCurv,
1278  const Trk::TrackingVolume* tVol) const {
1279 
1280  // ATH_MSG_WARNING( "[STEP_Propagator] enter 1");
1281 
1282  double Jacobian[25];
1283  Cache cache(ctx);
1284 
1285  // Get field cache object
1286  getFieldCacheObject(cache, ctx);
1287  setCacheFromProperties(cache);
1288  clearMaterialEffects(cache);
1289 
1290  // Check for tracking volume (materialproperties)
1291  cache.m_trackingVolume = tVol;
1292  cache.m_material = tVol;
1293  cache.m_matPropOK = tVol != nullptr;
1294 
1295  cache.m_matupd_lastmom = trackParameters.momentum().mag();
1296  cache.m_matupd_lastpath = 0.;
1297  cache.m_matdump_lastpath = 0.;
1298 
1299  // no identified intersections needed/ no material dump / no path cache
1300  cache.m_identifiedParameters = nullptr;
1301  cache.m_matstates = nullptr;
1302  cache.m_extrapolationCache = nullptr;
1303  cache.m_hitVector = nullptr;
1304 
1305  return propagateRungeKuttaImpl(cache, true, trackParameters, targetSurface, propagationDirection,
1306  magneticFieldProperties, particle, boundaryCheck, Jacobian, returnCurv);
1307 }
1308 
1310 // Main function for track parameters and covariance matrix propagation
1311 // with search of closest surface (ST)
1313 std::unique_ptr<Trk::TrackParameters> Trk::STEP_Propagator::propagate(
1314  const EventContext& ctx, const Trk::TrackParameters& trackParameters,
1315  std::vector<DestSurf>& targetSurfaces, Trk::PropDirection propagationDirection,
1316  const Trk::MagneticFieldProperties& magneticFieldProperties, ParticleHypothesis particle,
1317  std::vector<unsigned int>& solutions, double& path, bool usePathLimit, bool returnCurv,
1318  const Trk::TrackingVolume* tVol) const {
1319 
1320  Cache cache(ctx);
1321 
1322  // Get field cache object
1323  getFieldCacheObject(cache, ctx);
1324  setCacheFromProperties(cache);
1325  clearMaterialEffects(cache);
1326 
1327  // Check for tracking volume (materialproperties)
1328  cache.m_trackingVolume = tVol;
1329  cache.m_material = tVol;
1330  cache.m_matPropOK = tVol != nullptr;
1331 
1332  // no identified intersections needed/ no material dump
1333  cache.m_identifiedParameters = nullptr;
1334  cache.m_matstates = nullptr;
1335  cache.m_extrapolationCache = nullptr;
1336  cache.m_hitVector = nullptr;
1337 
1338  cache.m_matupd_lastmom = trackParameters.momentum().mag();
1339  cache.m_matupd_lastpath = 0.;
1340  cache.m_matdump_lastpath = 0.;
1341 
1342  // resolve path limit input
1343  if (path > 0.) {
1344  cache.m_propagateWithPathLimit = usePathLimit ? 1 : 0;
1345  cache.m_pathLimit = path;
1346  path = 0.;
1347  } else {
1348  cache.m_propagateWithPathLimit = 0;
1349  cache.m_pathLimit = -1.;
1350  path = 0.;
1351  }
1353  return propagateNeutral(trackParameters, targetSurfaces, propagationDirection, solutions, path,
1354  usePathLimit, returnCurv);
1355 
1356  return propagateRungeKutta(cache, true, trackParameters, targetSurfaces, propagationDirection,
1357  magneticFieldProperties, particle, solutions, path, returnCurv);
1358 }
1359 
1361 // Main function for track parameters and covariance matrix propagation
1362 // with search of closest surface and time info (ST)
1364 std::unique_ptr<Trk::TrackParameters> Trk::STEP_Propagator::propagateT(
1365  const EventContext& ctx, const Trk::TrackParameters& trackParameters,
1366  std::vector<DestSurf>& targetSurfaces, Trk::PropDirection propagationDirection,
1367  const Trk::MagneticFieldProperties& magneticFieldProperties, ParticleHypothesis particle,
1368  std::vector<unsigned int>& solutions, PathLimit& pathLim, TimeLimit& timeLim, bool returnCurv,
1369  const Trk::TrackingVolume* tVol, std::vector<Trk::HitInfo>*& hitVector) const {
1370 
1371  Cache cache(ctx);
1372 
1373  // Get field cache object
1374  getFieldCacheObject(cache, ctx);
1375  setCacheFromProperties(cache);
1376  clearMaterialEffects(cache);
1377 
1378  // cache particle mass
1379  cache.m_particleMass = Trk::ParticleMasses::mass[particle]; // Get particle mass from ParticleHypothesis
1380 
1381  // cache input timing - for secondary track emission
1382  cache.m_timeIn = timeLim.time;
1383 
1384  // Check for tracking volume (materialproperties)
1385  cache.m_trackingVolume = tVol;
1386  cache.m_material = tVol;
1387  cache.m_matPropOK = tVol != nullptr;
1388 
1389  // no identified intersections needed/ no material dump
1390  cache.m_identifiedParameters = nullptr;
1391  cache.m_matstates = nullptr;
1392  cache.m_extrapolationCache = nullptr;
1393  cache.m_hitVector = hitVector;
1394 
1395  cache.m_matupd_lastmom = trackParameters.momentum().mag();
1396  cache.m_matupd_lastpath = 0.;
1397  cache.m_matdump_lastpath = 0.;
1398 
1399  // convert time/path limits into trajectory limit (in mm)
1400  double dMat = pathLim.x0Max - pathLim.x0Collected;
1401  double path =
1402  dMat > 0 && cache.m_matPropOK && cache.m_material->x0() > 0. ? dMat * cache.m_material->x0() : -1.;
1403 
1404  double dTim = timeLim.tMax - timeLim.time;
1405  double beta = 1.;
1406  if (dTim > 0.) {
1407  double mom = trackParameters.momentum().mag();
1408  beta = mom / std::sqrt(mom * mom + cache.m_particleMass * cache.m_particleMass);
1409  }
1410  double timMax = dTim > 0 ? dTim * beta * Gaudi::Units::c_light : -1.;
1411 
1412  if (timMax > 0. && timMax < path)
1413  path = timMax;
1414  bool usePathLimit = (path > 0.);
1415 
1416  // resolve path limit input
1417  if (path > 0.) {
1418  cache.m_propagateWithPathLimit = usePathLimit ? 1 : 0;
1419  cache.m_pathLimit = path;
1420  path = 0.;
1421  } else {
1422  cache.m_propagateWithPathLimit = 0;
1423  cache.m_pathLimit = -1.;
1424  path = 0.;
1425  }
1426 
1427  std::unique_ptr<Trk::TrackParameters> nextPar{};
1428 
1430  nextPar = propagateNeutral(trackParameters, targetSurfaces, propagationDirection, solutions, path,
1431  usePathLimit, returnCurv);
1432  } else {
1433  nextPar = propagateRungeKutta(cache, true, trackParameters, targetSurfaces, propagationDirection,
1434  magneticFieldProperties, particle, solutions, path, returnCurv);
1435  }
1436  // update material path
1437  if (cache.m_matPropOK && cache.m_material->x0() > 0. && path > 0.) {
1438  pathLim.updateMat(path / cache.m_material->x0(), cache.m_material->averageZ(), 0.);
1439  }
1440  // return value
1441  timeLim.time += cache.m_timeOfFlight;
1442  return nextPar;
1443 }
1444 
1446 // Main function for track parameters and covariance matrix propagation
1447 // with search of closest surface and material collection (ST)
1449 
1450 std::unique_ptr<Trk::TrackParameters> Trk::STEP_Propagator::propagateM(
1451  const EventContext& ctx, const Trk::TrackParameters& trackParameters,
1452  std::vector<DestSurf>& targetSurfaces, Trk::PropDirection propagationDirection,
1453  const Trk::MagneticFieldProperties& magneticFieldProperties, ParticleHypothesis particle,
1454  std::vector<unsigned int>& solutions, std::vector<const Trk::TrackStateOnSurface*>*& matstates,
1455  std::vector<std::pair<std::unique_ptr<Trk::TrackParameters>, int>>* intersections, double& path,
1456  bool usePathLimit, bool returnCurv, const Trk::TrackingVolume* tVol,
1457  Trk::ExtrapolationCache* extrapCache) const {
1458 
1459  Cache cache(ctx);
1460 
1461  // Get field cache object
1462  getFieldCacheObject(cache, ctx);
1463  setCacheFromProperties(cache);
1464  clearMaterialEffects(cache);
1465 
1466  // Check for tracking volume (materialproperties)
1467  cache.m_trackingVolume = tVol;
1468  cache.m_material = tVol;
1469  cache.m_matPropOK = tVol != nullptr;
1470 
1471  cache.m_matstates = matstates;
1472  cache.m_identifiedParameters = intersections;
1473  cache.m_extrapolationCache = extrapCache;
1474  cache.m_hitVector = nullptr;
1475 
1476  cache.m_matupd_lastmom = trackParameters.momentum().mag();
1477  cache.m_matupd_lastpath = 0.;
1478  cache.m_matdump_lastpath = 0.;
1479  cache.m_extrapolationCache = extrapCache;
1480 
1481  // switch on the detailed energy loss
1482  if (cache.m_extrapolationCache) {
1483  cache.m_detailedElossFlag = true;
1484  }
1485  // resolve path limit input
1486  if (path > 0.) {
1487  cache.m_propagateWithPathLimit = usePathLimit ? 1 : 0;
1488  cache.m_pathLimit = path;
1489  path = 0.;
1490  } else {
1491  cache.m_propagateWithPathLimit = 0;
1492  cache.m_pathLimit = -1.;
1493  path = 0.;
1494  }
1496  return propagateNeutral(trackParameters, targetSurfaces, propagationDirection, solutions, path,
1497  usePathLimit, returnCurv);
1498  }
1499  return propagateRungeKutta(cache, true, trackParameters, targetSurfaces, propagationDirection,
1500  magneticFieldProperties, particle, solutions, path, returnCurv);
1501 }
1502 
1504 // Main function for track parameters and covariance matrix propagation.
1506 std::unique_ptr<Trk::TrackParameters> Trk::STEP_Propagator::propagate(
1507  const EventContext& ctx,
1508  const Trk::TrackParameters& trackParameters,
1509  const Trk::Surface& targetSurface,
1510  Trk::PropDirection propagationDirection,
1511  const Trk::BoundaryCheck& boundaryCheck,
1512  const Trk::MagneticFieldProperties& magneticFieldProperties,
1513  std::optional<Trk::TransportJacobian>& jacobian,
1514  double&,
1516  bool returnCurv,
1517  const Trk::TrackingVolume* tVol) const {
1518 
1519  double Jacobian[25];
1520  Cache cache(ctx);
1521 
1522  // Get field cache object
1523  getFieldCacheObject(cache, ctx);
1524  setCacheFromProperties(cache);
1525  clearMaterialEffects(cache);
1526 
1527  // Check for tracking volume (materialproperties)
1528  cache.m_trackingVolume = tVol;
1529  cache.m_material = tVol;
1530  cache.m_matPropOK = tVol != nullptr;
1531 
1532  // no identified intersections needed/ no material dump
1533  cache.m_identifiedParameters = nullptr;
1534  cache.m_matstates = nullptr;
1535  cache.m_extrapolationCache = nullptr;
1536  cache.m_hitVector = nullptr;
1537 
1538  cache.m_matupd_lastmom = trackParameters.momentum().mag();
1539  cache.m_matupd_lastpath = 0.;
1540  cache.m_matdump_lastpath = 0.;
1541 
1542  std::unique_ptr<Trk::TrackParameters> parameters =
1543  propagateRungeKuttaImpl(cache, true, trackParameters, targetSurface, propagationDirection,
1544  magneticFieldProperties, particle, boundaryCheck, Jacobian, returnCurv);
1545 
1546  if (parameters) {
1547  Jacobian[24] = Jacobian[20];
1548  Jacobian[23] = 0.;
1549  Jacobian[22] = 0.;
1550  Jacobian[21] = 0.;
1551  Jacobian[20] = 0.;
1552  jacobian = std::make_optional<Trk::TransportJacobian>(Jacobian);
1553  } else {
1554  jacobian.reset();
1555  }
1556 
1557  return parameters;
1558 }
1559 
1561 // Main function for track parameters propagation without covariance matrix
1563 
1564 std::unique_ptr<Trk::TrackParameters> Trk::STEP_Propagator::propagateParameters(
1565  const EventContext& ctx, const Trk::TrackParameters& trackParameters, const Trk::Surface& targetSurface,
1566  Trk::PropDirection propagationDirection, const Trk::BoundaryCheck& boundaryCheck,
1567  const Trk::MagneticFieldProperties& magneticFieldProperties, ParticleHypothesis particle, bool returnCurv,
1568  const Trk::TrackingVolume* tVol) const {
1569 
1570  double Jacobian[25];
1571  Cache cache(ctx);
1572 
1573  // Get field cache object
1574  getFieldCacheObject(cache, ctx);
1575  setCacheFromProperties(cache);
1576  clearMaterialEffects(cache);
1577 
1578  // Check for tracking volume (materialproperties)
1579  cache.m_trackingVolume = tVol;
1580  cache.m_material = tVol;
1581  cache.m_matPropOK = tVol != nullptr;
1582 
1583  // no identified intersections needed/ no material dump
1584  cache.m_identifiedParameters = nullptr;
1585  cache.m_matstates = nullptr;
1586  cache.m_hitVector = nullptr;
1587 
1588  return propagateRungeKuttaImpl(cache, false, trackParameters, targetSurface, propagationDirection,
1589  magneticFieldProperties, particle, boundaryCheck, Jacobian, returnCurv);
1590 }
1591 
1593 // Main function for track parameters propagation without covariance matrix.
1595 std::unique_ptr<Trk::TrackParameters> Trk::STEP_Propagator::propagateParameters(
1596  const EventContext& ctx,
1597  const Trk::TrackParameters& trackParameters,
1598  const Trk::Surface& targetSurface,
1599  Trk::PropDirection propagationDirection,
1600  const Trk::BoundaryCheck& boundaryCheck,
1601  const Trk::MagneticFieldProperties& magneticFieldProperties,
1602  std::optional<Trk::TransportJacobian>& jacobian,
1604  bool returnCurv,
1605  const Trk::TrackingVolume* tVol) const {
1606 
1607  double Jacobian[25];
1608 
1609  Cache cache(ctx);
1610 
1611  // Get field cache object
1612  getFieldCacheObject(cache, ctx);
1613  setCacheFromProperties(cache);
1614  clearMaterialEffects(cache);
1615 
1616  // Check for tracking volume (materialproperties)
1617  cache.m_trackingVolume = tVol;
1618  cache.m_material = tVol;
1619  cache.m_matPropOK = tVol != nullptr;
1620 
1621  // no identified intersections needed/ no material dump
1622  cache.m_identifiedParameters = nullptr;
1623  cache.m_matstates = nullptr;
1624  cache.m_extrapolationCache = nullptr;
1625  cache.m_hitVector = nullptr;
1626 
1627  std::unique_ptr<Trk::TrackParameters> parameters =
1628  propagateRungeKuttaImpl(cache, true, trackParameters, targetSurface, propagationDirection,
1629  magneticFieldProperties, particle, boundaryCheck, Jacobian, returnCurv);
1630 
1631  if (parameters) {
1632  Jacobian[24] = Jacobian[20];
1633  Jacobian[23] = 0.;
1634  Jacobian[22] = 0.;
1635  Jacobian[21] = 0.;
1636  Jacobian[20] = 0.;
1637  jacobian = std::make_optional<Trk::TransportJacobian>(Jacobian);
1638  } else {
1639  jacobian.reset();
1640  }
1641 
1642  return parameters;
1643 }
1644 
1646 // Function for finding the intersection point with a surface
1648 std::optional<Trk::TrackSurfaceIntersection> Trk::STEP_Propagator::intersect(
1649  const EventContext& ctx,
1650  const Trk::TrackParameters& trackParameters,
1651  const Trk::Surface& targetSurface,
1652  const Trk::MagneticFieldProperties& mft,
1654  const Trk::TrackingVolume* tVol) const {
1655 
1656  Cache cache(ctx);
1657 
1658  // Get field cache object
1659  getFieldCacheObject(cache, ctx);
1660  setCacheFromProperties(cache);
1661  clearMaterialEffects(cache);
1662 
1663  cache.m_particle = particle; // Store for later use
1664 
1665  // Check for tracking volume (materialproperties)
1666  cache.m_trackingVolume = tVol;
1667  cache.m_material = tVol;
1668  cache.m_matPropOK = tVol != nullptr;
1669 
1670  // no identified intersections needed/ no material dump
1671  cache.m_identifiedParameters = nullptr;
1672  cache.m_matstates = nullptr;
1673  cache.m_extrapolationCache = nullptr;
1674  cache.m_hitVector = nullptr;
1675 
1676  // Bfield mode
1677  mft.magneticFieldMode() == Trk::FastField ? cache.m_solenoid = true : cache.m_solenoid = false;
1678 
1679  // Check inputvalues
1680  if (cache.m_tolerance <= 0.) {
1681  return std::nullopt;
1682  }
1683  if (cache.m_momentumCutOff < 0.) {
1684  return std::nullopt;
1685  }
1686  if (std::abs(1. / trackParameters.parameters()[Trk::qOverP]) <= cache.m_momentumCutOff) {
1687  return std::nullopt;
1688  }
1689 
1690  // Check for empty volumes. If x != x then x is not a number.
1691  if (cache.m_matPropOK && ((cache.m_material->zOverAtimesRho() == 0.) || (cache.m_material->x0() == 0.) ||
1692  (cache.m_material->zOverAtimesRho() != cache.m_material->zOverAtimesRho()))) {
1693  cache.m_matPropOK = false;
1694  }
1695 
1696  // double P[45];
1697  if (!Trk::RungeKuttaUtils::transformLocalToGlobal(false, trackParameters, cache.m_P)) {
1698  return std::nullopt;
1699  }
1700  double path = 0.;
1701 
1702  const Amg::Transform3D& T = targetSurface.transform();
1703  Trk::SurfaceType ty = targetSurface.type();
1704 
1705  if (ty == Trk::SurfaceType::Plane || ty == Trk::SurfaceType::Disc) {
1706  double s[4];
1707  double d = T(0, 3) * T(0, 2) + T(1, 3) * T(1, 2) + T(2, 3) * T(2, 2);
1708 
1709  if (d >= 0.) {
1710  s[0] = T(0, 2);
1711  s[1] = T(1, 2);
1712  s[2] = T(2, 2);
1713  s[3] = d;
1714  } else {
1715  s[0] = -T(0, 2);
1716  s[1] = -T(1, 2);
1717  s[2] = -T(2, 2);
1718  s[3] = -d;
1719  }
1720  if (!propagateWithJacobianImpl(cache, false, ty, s, cache.m_P, path)){
1721  return std::nullopt;
1722  }
1723  }
1724 
1725  else if (ty == Trk::SurfaceType::Line) {
1726 
1727  double s[6] = {T(0, 3), T(1, 3), T(2, 3), T(0, 2), T(1, 2), T(2, 2)};
1728  if (!propagateWithJacobianImpl(cache, false, ty, s, cache.m_P, path)) {
1729  return std::nullopt;
1730  }
1731  }
1732 
1733  else if (ty == Trk::SurfaceType::Cylinder) {
1734 
1735  const Trk::CylinderSurface* cyl = static_cast<const Trk::CylinderSurface*>(&targetSurface);
1736  double s[9] = {
1737  T(0, 3), T(1, 3), T(2, 3), T(0, 2), T(1, 2), T(2, 2), cyl->bounds().r(), Trk::alongMomentum, 0.};
1738  if (!propagateWithJacobianImpl(cache, false, ty, s, cache.m_P, path)) {
1739  return std::nullopt;
1740  }
1741  }
1742 
1743  else if (ty == Trk::SurfaceType::Cone) {
1744 
1745  double k = static_cast<const Trk::ConeSurface*>(&targetSurface)->bounds().tanAlpha();
1746  k = k * k + 1.;
1747  double s[9] = {T(0, 3), T(1, 3), T(2, 3), T(0, 2), T(1, 2), T(2, 2), k, Trk::alongMomentum, 0.};
1748  if (!propagateWithJacobianImpl(cache, false, ty, s, cache.m_P, path)) {
1749  return std::nullopt;
1750  }
1751  }
1752 
1753  else if (ty == Trk::SurfaceType::Perigee) {
1754 
1755  double s[6] = {T(0, 3), T(1, 3), T(2, 3), 0., 0., 1.};
1756  if (!propagateWithJacobianImpl(cache, false, ty, s, cache.m_P, path)) {
1757  return std::nullopt;
1758  }
1759  }
1760 
1761  else { // presumably curvilinear
1762 
1763  double s[4];
1764  double d = T(0, 3) * T(0, 2) + T(1, 3) * T(1, 2) + T(2, 3) * T(2, 2);
1765 
1766  if (d >= 0.) {
1767  s[0] = T(0, 2);
1768  s[1] = T(1, 2);
1769  s[2] = T(2, 2);
1770  s[3] = d;
1771  } else {
1772  s[0] = -T(0, 2);
1773  s[1] = -T(1, 2);
1774  s[2] = -T(2, 2);
1775  s[3] = -d;
1776  }
1777  if (!propagateWithJacobianImpl(cache, false, ty, s, cache.m_P, path)) {
1778  return std::nullopt;
1779  }
1780  }
1781 
1782  Amg::Vector3D globalPosition(cache.m_P[0], cache.m_P[1], cache.m_P[2]);
1783  Amg::Vector3D direction(cache.m_P[3], cache.m_P[4], cache.m_P[5]);
1784  return std::make_optional<Trk::TrackSurfaceIntersection>(globalPosition, direction, path);
1785 }
1786 
1787 std::optional<Trk::TrackSurfaceIntersection>
1789  const EventContext& ctx, const Trk::Surface& surface,
1790  const Trk::TrackSurfaceIntersection& trackIntersection, const double qOverP,
1791  const Trk::MagneticFieldProperties& mft,
1792  ParticleHypothesis particle) const {
1793 
1794  const Amg::Vector3D& origin = trackIntersection.position();
1795  const Amg::Vector3D& direction = trackIntersection.direction();
1796 
1797  auto perigeeSurface = PerigeeSurface(origin);
1798  perigeeSurface.setOwner(Trk::userOwn); // tmp ones
1799 
1800  auto tmpTrackParameters =
1801  Trk::Perigee(0., 0., direction.phi(), direction.theta(), qOverP, perigeeSurface, std::nullopt);
1802 
1803  std::optional<Trk::TrackSurfaceIntersection> solution =
1804  qOverP == 0
1805  ? intersect(ctx, tmpTrackParameters, surface,
1807  : intersect(ctx, tmpTrackParameters, surface, mft, particle, nullptr);
1808 
1809  return solution;
1810 }
1811 
1813 // Global positions calculation inside CylinderBounds
1814 // if max step allowed > 0 -> propagate along momentum else propagate opposite momentum
1816 void Trk::STEP_Propagator::globalPositions(const EventContext& ctx, std::deque<Amg::Vector3D>& positionsList,
1817  const Trk::TrackParameters& trackParameters,
1818  const Trk::MagneticFieldProperties& mft,
1819  const Trk::CylinderBounds& cylinderBounds, double maxStepSize,
1821  const Trk::TrackingVolume* tVol) const {
1822  Cache cache(ctx);
1823 
1824  // Get field cache object
1825  getFieldCacheObject(cache, ctx);
1826  setCacheFromProperties(cache);
1827  clearMaterialEffects(cache);
1828 
1829  cache.m_particle = particle; // Store for later use
1830 
1831  // Check for tracking volume (materialproperties)
1832  cache.m_trackingVolume = tVol;
1833  cache.m_material = tVol;
1834  cache.m_matPropOK = tVol != nullptr;
1835 
1836  // Check for empty volumes. If x != x then x is not a number.
1837  if (cache.m_matPropOK && ((cache.m_material->zOverAtimesRho() == 0.) || (cache.m_material->x0() == 0.) ||
1838  (cache.m_material->zOverAtimesRho() != cache.m_material->zOverAtimesRho()))) {
1839  cache.m_matPropOK = false;
1840  }
1841 
1842  mft.magneticFieldMode() == Trk::FastField ? cache.m_solenoid = true : cache.m_solenoid = false;
1843 
1844  // Check inputvalues
1845  if (cache.m_tolerance <= 0.)
1846  return;
1847 
1848  double PP[7];
1849  if (!Trk::RungeKuttaUtils::transformLocalToGlobal(false, trackParameters, PP))
1850  return;
1851 
1852  double maxPath = cache.m_maxPath; // Max path allowed
1853  double dDir[3] = {0., 0., 0.}; // Start directions derivs. Zero in case of no RK steps
1854  double distanceStepped = 0.;
1855  double BG1[12] = {0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; // Bx, By, Bz, dBx/dx, dBx/dy, dBx/dz,
1856  // dBy/dx, dBy/dy, dBy/dz, dBz/dx, dBz/dy, dBz/dz
1857  bool firstStep = true; // Poll B1, else recycle B4
1858  double path = 0.; // path of the trajectory
1859  double radius2Max = cylinderBounds.r() * cylinderBounds.r(); // max. radius**2 of region
1860  double zMax = cylinderBounds.halflengthZ(); // max. Z of region
1861  double radius2 = PP[0] * PP[0] + PP[1] * PP[1]; // Start radius**2
1862  double direction = PP[0] * PP[3] + PP[1] * PP[4]; // Direction
1863  double h = maxStepSize; // max step allowed
1864 
1865  // Test position of the track
1866  if ((std::abs(PP[2]) > zMax) || (radius2 > radius2Max))
1867  return;
1868 
1869  // Store initial position
1870  Amg::Vector3D initialPosition(PP[0], PP[1], PP[2]);
1871  positionsList.push_back(initialPosition);
1872 
1873  bool perigee = false;
1874  if (std::abs(direction) < 0.00001) {
1875  perigee = true;
1876  }
1877 
1878  for (int i = 0; i != 2; ++i) {
1879  if (i) {
1880  if (perigee)
1881  return;
1882  h = -h;
1883  }
1884  double p[7] = {PP[0], PP[1], PP[2], PP[3], PP[4], PP[5], PP[6]};
1885 
1886  while (std::abs(path) < maxPath) {
1887  // Do the step.
1888  if (!rungeKuttaStep(cache, false, h, p, dDir, BG1, firstStep, distanceStepped))
1889  break;
1890  path = path + distanceStepped;
1891 
1892  // Keep h within max stepsize
1893  if (h > maxStepSize) {
1894  h = maxStepSize;
1895  } else if (h < -maxStepSize) {
1896  h = -maxStepSize;
1897  }
1898 
1899  // store current step
1900  Amg::Vector3D globalPosition(p[0], p[1], p[2]);
1901  if (!i) {
1902  positionsList.push_back(globalPosition);
1903  } else {
1904  positionsList.push_front(globalPosition);
1905  }
1906 
1907  // Test position of the track
1908  radius2 = p[0] * p[0] + p[1] * p[1];
1909  if ((std::abs(p[2]) > zMax) || (radius2 > radius2Max))
1910  break;
1911 
1912  // Test perigee
1913  if ((p[0] * p[3] + p[1] * p[4]) * direction < 0.) {
1914  if (i)
1915  return;
1916  perigee = true;
1917  }
1918  }
1919  }
1920 }
1921 
1923 // Main function for track propagation with or without jacobian
1924 // with search of closest surface
1926 std::unique_ptr<Trk::TrackParameters> Trk::STEP_Propagator::propagateRungeKutta(
1927  Cache& cache, bool errorPropagation, const Trk::TrackParameters& inputTrackParameters,
1928  std::vector<DestSurf>& targetSurfaces, Trk::PropDirection propagationDirection,
1930  std::vector<unsigned int>& solutions, double& totalPath, bool returnCurv) const {
1931  // Store for later use
1932  cache.m_particle = particle;
1933  cache.m_charge = inputTrackParameters.charge();
1934  cache.m_inputThetaVariance = 0.;
1935 
1936  std::unique_ptr<Trk::TrackParameters> trackParameters{};
1937 
1938  // Bfield mode
1939  mft.magneticFieldMode() == Trk::FastField ? cache.m_solenoid = true : cache.m_solenoid = false;
1940 
1941  // Check inputvalues
1942  if (cache.m_tolerance <= 0.)
1943  return nullptr;
1944  if (cache.m_momentumCutOff < 0.)
1945  return nullptr;
1946 
1947  // Set momentum to 1e10 (straight line) and charge to + if q/p is zero
1948  if (inputTrackParameters.parameters()[Trk::qOverP] == 0) {
1949  trackParameters = createStraightLine(&inputTrackParameters);
1950  if (!trackParameters) {
1951  return nullptr;
1952  }
1953  } else {
1954  trackParameters.reset(inputTrackParameters.clone());
1955  }
1956 
1957  if (std::abs(1. / trackParameters->parameters()[Trk::qOverP]) <= cache.m_momentumCutOff) {
1958  return nullptr;
1959  }
1960 
1961  // Check for empty volumes. If x != x then x is not a number.
1962  if (cache.m_matPropOK && ((cache.m_material->zOverAtimesRho() == 0.) || (cache.m_material->x0() == 0.) ||
1963  (cache.m_material->zOverAtimesRho() != cache.m_material->zOverAtimesRho()))) {
1964  cache.m_matPropOK = false;
1965  }
1966 
1967  if (errorPropagation && !trackParameters->covariance()) {
1968  errorPropagation = false;
1969  }
1970 
1971  if (cache.m_matPropOK && errorPropagation && cache.m_straggling)
1972  cache.m_stragglingVariance = 0.;
1973  cache.m_combinedCovariance.setZero();
1974  cache.m_covariance.setZero();
1975 
1976  if (errorPropagation || cache.m_matstates) {
1977  // this needs debugging
1978  cache.m_inputThetaVariance = trackParameters->covariance() ? (*trackParameters->covariance())(3, 3) : 0.;
1979  cache.m_combinedEloss.set(0., 0., 0., 0., 0., 0.);
1980  cache.m_combinedThickness = 0.;
1981  }
1982 
1983  // double P[45]; // Track parameters and jacobian
1984  if (!Trk::RungeKuttaUtils::transformLocalToGlobal(errorPropagation, *trackParameters, cache.m_P)) {
1985  return nullptr;
1986  }
1987 
1988  double path = 0.;
1989 
1990  // activate brem photon emission if required
1991  cache.m_brem = m_simulation && particle == Trk::electron && m_simMatUpdator;
1992 
1993  // loop while valid solutions
1994  bool validStep = true;
1995  totalPath = 0.;
1996  cache.m_timeOfFlight = 0.;
1997  // Common transformation for all surfaces (angles and momentum)
1998  double localp[5];
1999  double Jacobian[21];
2000  while (validStep) {
2001  // propagation to next surface
2002  validStep = propagateWithJacobian(cache, errorPropagation, targetSurfaces, cache.m_P,
2003  propagationDirection, solutions, path, totalPath);
2004  if (!validStep) {
2005  return nullptr;
2006  }
2007  if (propagationDirection * path <= 0.) {
2008  return nullptr;
2009  }
2010  totalPath += path;
2011  cache.m_timeOfFlight += cache.m_timeStep;
2012  if (cache.m_propagateWithPathLimit > 1 || cache.m_binMat) {
2013  // make sure that for sliding surfaces the result does not get distorted
2014  // return curvilinear parameters
2015  std::unique_ptr<Trk::CurvilinearParameters> cPar = nullptr;
2017  if (!errorPropagation) {
2018  cPar = std::make_unique<Trk::CurvilinearParameters>(
2019  Amg::Vector3D(cache.m_P[0], cache.m_P[1], cache.m_P[2]), localp[2], localp[3], localp[4]);
2020  } else {
2021  double useless[2];
2022  Trk::RungeKuttaUtils::transformGlobalToCurvilinear(true, cache.m_P, useless, Jacobian);
2023  AmgSymMatrix(5) measurementCovariance =
2024  Trk::RungeKuttaUtils::newCovarianceMatrix(Jacobian, *trackParameters->covariance());
2025  // Calculate multiple scattering and straggling covariance contribution.
2026  if (cache.m_matPropOK && (cache.m_multipleScattering || cache.m_straggling) &&
2027  std::abs(totalPath) > 0.) {
2028  covarianceContribution(cache, trackParameters.get(), totalPath, std::abs(1. / cache.m_P[6]),
2029  measurementCovariance);
2030  }
2031  cPar = std::make_unique<Trk::CurvilinearParameters>(
2032  Amg::Vector3D(cache.m_P[0], cache.m_P[1], cache.m_P[2]), localp[2], localp[3], localp[4],
2033  std::move(measurementCovariance));
2034  }
2035  // material collection : first iteration, bin material averaged
2036  // collect material
2037  if (cache.m_binMat && (cache.m_matstates || (errorPropagation && cache.m_extrapolationCache)) &&
2038  std::abs(totalPath - cache.m_matdump_lastpath) > 1.) {
2039  dumpMaterialEffects(cache, cPar.get(), totalPath);
2040  }
2041  return cPar;
2042  }
2043  if (cache.m_propagateWithPathLimit > 0)
2044  cache.m_pathLimit -= path;
2045  // boundary check
2046  // take into account that there may be many identical surfaces with different boundaries
2047  Amg::Vector3D gp(cache.m_P[0], cache.m_P[1], cache.m_P[2]);
2048  bool solution = false;
2049  std::vector<unsigned int> valid_solutions;
2050  valid_solutions.reserve(solutions.size());
2051 
2052  std::vector<unsigned int>::iterator iSol = solutions.begin();
2053  while (iSol != solutions.end()) {
2054  if (targetSurfaces[*iSol].first->isOnSurface(gp, targetSurfaces[*iSol].second, 0.001, 0.001)) {
2055  if (!solution) {
2057  if (returnCurv || targetSurfaces[*iSol].first->type() == Trk::SurfaceType::Cone) {
2058  Trk::RungeKuttaUtils::transformGlobalToCurvilinear(errorPropagation, cache.m_P, localp, Jacobian);
2059  } else
2060  Trk::RungeKuttaUtils::transformGlobalToLocal(targetSurfaces[*iSol].first, errorPropagation,
2061  cache.m_P, localp, Jacobian);
2062  solution = true;
2063  }
2064  valid_solutions.push_back(*iSol);
2065  }
2066  ++iSol;
2067  }
2068  solutions = std::move(valid_solutions);
2069  if (solution)
2070  break;
2071  }
2072 
2073  if (solutions.empty()) {
2074  return nullptr;
2075  }
2076 
2077  // simulation mode : smear momentum
2078  if (m_simulation && cache.m_matPropOK) {
2079  double radDist = totalPath / cache.m_material->x0();
2080  smear(cache, localp[2], localp[3], trackParameters.get(), radDist);
2081  }
2082 
2083  std::unique_ptr<Trk::TrackParameters> onTargetSurf =
2084  (returnCurv || targetSurfaces[solutions[0]].first->type() == Trk::SurfaceType::Cone)
2085  ? nullptr
2086  : targetSurfaces[solutions[0]].first->createUniqueTrackParameters(
2087  localp[0], localp[1], localp[2], localp[3], localp[4], std::nullopt);
2088 
2089  if (!errorPropagation) {
2090  if (returnCurv || targetSurfaces[solutions[0]].first->type() == Trk::SurfaceType::Cone) {
2091  Amg::Vector3D gp(cache.m_P[0], cache.m_P[1], cache.m_P[2]);
2092  return std::make_unique<Trk::CurvilinearParameters>(gp, localp[2], localp[3], localp[4]);
2093  }
2094  return onTargetSurf;
2095  }
2096 
2097  // Errormatrix is included. Use Jacobian to calculate new covariance
2099  for (double i : Jacobian) {
2100  if (!Amg::saneCovarianceElement(i)) {
2101  return nullptr;
2102  }
2103  }
2104 
2105  AmgSymMatrix(5) measurementCovariance =
2106  Trk::RungeKuttaUtils::newCovarianceMatrix(Jacobian, *trackParameters->covariance());
2107  if (!Amg::hasPositiveOrZeroDiagElems(measurementCovariance))
2108  return nullptr;
2109 
2110  // Calculate multiple scattering and straggling covariance contribution.
2111  if (cache.m_matPropOK && (cache.m_multipleScattering || cache.m_straggling) && std::abs(totalPath) > 0.) {
2112  if (returnCurv || targetSurfaces[solutions[0]].first->type() == Trk::SurfaceType::Cone) {
2113  covarianceContribution(cache, trackParameters.get(), totalPath, std::abs(1. / cache.m_P[6]),
2114  measurementCovariance);
2115  } else {
2116  covarianceContribution(cache, trackParameters.get(), totalPath, onTargetSurf.get(),
2117  measurementCovariance);
2118  }
2119  }
2120 
2121  if (returnCurv || targetSurfaces[solutions[0]].first->type() == Trk::SurfaceType::Cone) {
2122  Amg::Vector3D gp(cache.m_P[0], cache.m_P[1], cache.m_P[2]);
2123  return std::make_unique<Trk::CurvilinearParameters>(gp, localp[2], localp[3], localp[4],
2124  std::move(measurementCovariance));
2125  }
2126 
2127  // delete onTargetSurf; // the covariance matrix can be just added instead of recreating ?
2128  return targetSurfaces[solutions[0]].first->createUniqueTrackParameters(
2129  localp[0], localp[1], localp[2], localp[3], localp[4], std::move(measurementCovariance));
2130 }
2131 
2133 // Runge Kutta main program for propagation with or without Jacobian
2134 // with search of closest surface (ST)
2136 bool Trk::STEP_Propagator::propagateWithJacobian(Cache& cache, bool errorPropagation,
2137  std::vector<DestSurf>& sfs, double* P,
2138  Trk::PropDirection propDir,
2139  std::vector<unsigned int>& solutions, double& path,
2140  double sumPath) const {
2141  double maxPath = cache.m_maxPath; // Max path allowed
2142  double* pos = &P[0]; // Start coordinates
2143  double* dir = &P[3]; // Start directions
2144  double dDir[3] = {0., 0., 0.}; // Start directions derivs. Zero in case of no RK steps
2145  // int targetPassed = 0; // how many times have we passed the target?
2146  double previousDistance = 0.;
2147  double distanceStepped = 0.;
2148  double BG1[12] = {0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; // Bx, By, Bz, dBx/dx, dBx/dy, dBx/dz,
2149  // dBy/dx, dBy/dy, dBy/dz, dBz/dx, dBz/dy, dBz/dz at first point
2150  bool firstStep = true; // Poll BG1, else recycle BG4
2151  int steps = 0;
2152  path = 0.; // path of the trajectory
2153  cache.m_timeStep = 0.; // time separation corresponding to the trajectory
2154  double mom = 0.; // need momentum and beta for timing
2155  double beta = 1.; // need momentum and beta for timing
2156 
2157  // factor to stabilize iteration for soft tracks
2158  double helpSoft = 1.;
2159 
2160  // limit number of recovery attempts
2161  int restartLimit = 10;
2162 
2163  Amg::Vector3D position(P[0], P[1], P[2]);
2164  Amg::Vector3D direction0(P[3], P[4], P[5]);
2165 
2166  // binned material ?
2167  cache.m_binMat = nullptr;
2168  if (cache.m_trackingVolume && cache.m_trackingVolume->isAlignable()) {
2169  const Trk::AlignableTrackingVolume* aliTV =
2170  static_cast<const Trk::AlignableTrackingVolume*>(cache.m_trackingVolume);
2171  cache.m_binMat = aliTV->binnedMaterial();
2172  }
2173 
2174  // closest distance estimate
2175  // maintain count of oscilations and previous distance for each surface;
2176  // skip initial trivial solutions (input parameters at surface) - should be treated before call to the
2177  // propagator
2178  // !
2179  double tol = 0.001;
2180  solutions.clear();
2181  double distanceToTarget = propDir * maxPath;
2182  cache.m_currentDist.resize(sfs.size()); // keep size through the call
2183 
2184  int nextSf = sfs.size();
2185  int nextSfCand = nextSf;
2186  std::vector<DestSurf>::iterator sIter = sfs.begin();
2187  std::vector<DestSurf>::iterator sBeg = sfs.begin();
2188  unsigned int numSf = 0;
2189  unsigned int iCurr = 0; // index for m_currentDist
2190  int startSf = -99;
2191  for (; sIter != sfs.end(); ++sIter) {
2192  Trk::DistanceSolution distSol = (*sIter).first->straightLineDistanceEstimate(position, direction0);
2193  double distEst = -propDir * maxPath;
2194  double dist1Est = distEst;
2195  if (distSol.numberOfSolutions() > 0) {
2196  distEst = distSol.first();
2197  dist1Est = distSol.first();
2198  if (distSol.numberOfSolutions() > 1 &&
2199  (std::abs(distEst) < tol || (propDir * distEst < -tol && propDir * distSol.second() > tol)))
2200  distEst = distSol.second();
2201  }
2202  // select input surfaces;
2203  // do not accept trivial solutions (at the surface)
2204  // but include them into further distance estimate (aca return to the same surface)
2205  if (distEst * propDir > -tol) {
2206  if (distSol.currentDistance() > 500.) {
2207  cache.m_currentDist[iCurr] = std::pair<int, std::pair<double, double>>(
2208  0, std::pair<double, double>(distEst, distSol.currentDistance(true)));
2209  } else {
2210  cache.m_currentDist[iCurr] = std::pair<int, std::pair<double, double>>(
2211  1, std::pair<double, double>(distEst, distSol.currentDistance(true)));
2212  }
2213  if (tol < propDir * distEst && propDir * distEst < propDir * distanceToTarget) {
2214  distanceToTarget = distEst;
2215  nextSf = iCurr;
2216  }
2217  ++numSf;
2218  } else {
2219  // save the nearest distance to surface
2220  cache.m_currentDist[iCurr] = std::pair<int, std::pair<double, double>>(
2221  -1, std::pair<double, double>(distSol.currentDistance(), distSol.currentDistance(true)));
2222  }
2223  if (std::abs(dist1Est) < tol)
2224  startSf = (int)iCurr;
2225  ++iCurr;
2226  }
2227 
2228  if (distanceToTarget == maxPath || numSf == 0)
2229  return false;
2230 
2231  // these do not change
2232  std::vector<std::pair<int, std::pair<double, double>>>::iterator vsBeg = cache.m_currentDist.begin();
2233  std::vector<std::pair<int, std::pair<double, double>>>::iterator vsEnd = cache.m_currentDist.end();
2234  const int num_vs_dist = cache.m_currentDist.size();
2235 
2236  // Set initial step length to 100mm or the distance to the target surface.
2237  double h = 0;
2238  double absPath = 0.;
2239  distanceToTarget > 100. ? h = 100. : distanceToTarget < -100. ? h = -100. : h = distanceToTarget;
2240 
2241  const Trk::IdentifiedMaterial* binIDMat = nullptr;
2242  // Adapt step size to the material binning : change of bin layer triggers dump of material effects
2243  if (cache.m_binMat) {
2244  const Trk::BinUtility* lbu = cache.m_binMat->layerBinUtility(position);
2245  if (lbu) {
2246  cache.m_currentLayerBin = cache.m_binMat->layerBin(position);
2247  binIDMat = cache.m_binMat->material(position);
2248  std::pair<size_t, float> dist2next = lbu->distanceToNext(position, propDir * direction0);
2249  if (dist2next.first < lbu->bins() && std::abs(dist2next.second) > 1. &&
2250  std::abs(dist2next.second) < std::abs(h)) {
2251  h = dist2next.second * propDir;
2252  }
2253  if (binIDMat)
2254  cache.m_material = binIDMat->first;
2255  }
2256  }
2257 
2258  // Step to within distanceTolerance, then do the rest as a Taylor expansion.
2259  // Keep distanceTolerance within [1 nanometer, 10 microns].
2260  // This means that no final Taylor expansions beyond 10 microns and no
2261  // Runge-Kutta steps less than 1 nanometer are allowed.
2262  double distanceTolerance = std::min(std::max(std::abs(distanceToTarget) * cache.m_tolerance, 1e-6), 1e-2);
2263 
2264  // bremstrahlung : sample if activated
2265  if (cache.m_brem) {
2266  mom = std::abs(1. / P[6]);
2267  sampleBrem(cache, mom);
2268  }
2269 
2270  while (numSf > 0 && (std::abs(distanceToTarget) > distanceTolerance ||
2271  std::abs(path + distanceStepped) < tol)) { // Step until within tolerance
2272  // Do the step. Stop the propagation if the energy goes below m_momentumCutOff
2273  if (!rungeKuttaStep(cache, errorPropagation, h, P, dDir, BG1, firstStep, distanceStepped)) {
2274  // emit brem photon before stopped ?
2275  if (cache.m_brem) {
2276  if (m_momentumCutOff < cache.m_bremEmitThreshold && m_simMatUpdator) {
2277  Amg::Vector3D position(P[0], P[1], P[2]);
2278  Amg::Vector3D direction(P[3], P[4], P[5]);
2279  m_simMatUpdator->recordBremPhoton(cache.m_timeIn + cache.m_timeOfFlight + cache.m_timeStep,
2280  m_momentumCutOff, cache.m_bremMom, position, direction,
2281  cache.m_particle);
2282  // the recoil can be skipped here
2283  for (int i = 0; i < 3; ++i)
2284  P[3 + i] = direction[i];
2285  // end recoil ( momentum not adjusted here ! continuous energy loss maintained for the moment)
2286  }
2287  }
2288  // collect material and update timing
2289  path = path + distanceStepped;
2290  // timing
2291  mom = std::abs(1. / P[6]);
2292  beta = mom / std::sqrt(mom * mom + cache.m_particleMass * cache.m_particleMass);
2293  cache.m_timeStep += distanceStepped / beta / Gaudi::Units::c_light;
2294 
2295  if (std::abs(distanceStepped) > 0.001) {
2296  cache.m_sigmaIoni = cache.m_sigmaIoni - cache.m_kazL * log(std::abs(distanceStepped));
2297  }
2298  // update straggling covariance
2299  if (errorPropagation && cache.m_straggling) {
2300  // 15% of the Radition moves the MOP value thus only 85% is accounted for by the Mean-MOP shift
2301  double sigTot2 = cache.m_sigmaIoni * cache.m_sigmaIoni + cache.m_sigmaRad * cache.m_sigmaRad;
2302  // /(beta*beta*p*p*p*p) transforms Var(E) to Var(q/p)
2303  double bp2 = beta * mom * mom;
2304  cache.m_stragglingVariance += sigTot2 / (bp2 * bp2) * distanceStepped * distanceStepped;
2305  }
2306  if (cache.m_matstates || errorPropagation) {
2307  cache.m_combinedEloss.update(
2308  cache.m_delIoni * distanceStepped, cache.m_sigmaIoni * std::abs(distanceStepped),
2309  cache.m_delRad * distanceStepped, cache.m_sigmaRad * std::abs(distanceStepped), cache.m_MPV);
2310  }
2311  if (cache.m_material && cache.m_material->x0() != 0.) {
2312  cache.m_combinedThickness += propDir * distanceStepped / cache.m_material->x0();
2313  }
2314 
2315  return false;
2316  }
2317  path = path + distanceStepped;
2318  absPath += std::abs(distanceStepped);
2319 
2320  // timing
2321  mom = std::abs(1. / P[6]);
2322  beta = mom / std::sqrt(mom * mom + cache.m_particleMass * cache.m_particleMass);
2323  cache.m_timeStep += distanceStepped / beta / Gaudi::Units::c_light;
2324 
2325  if (std::abs(distanceStepped) > 0.001)
2326  cache.m_sigmaIoni = cache.m_sigmaIoni - cache.m_kazL * log(std::abs(distanceStepped));
2327  // update straggling covariance
2328  if (errorPropagation && cache.m_straggling) {
2329  // 15% of the Radition moves the MOP value thus only 85% is accounted for by the Mean-MOP shift
2330  double sigTot2 = cache.m_sigmaIoni * cache.m_sigmaIoni + cache.m_sigmaRad * cache.m_sigmaRad;
2331  // /(beta*beta*p*p*p*p) transforms Var(E) to Var(q/p)
2332  double bp2 = beta * mom * mom;
2333  cache.m_stragglingVariance += sigTot2 / (bp2 * bp2) * distanceStepped * distanceStepped;
2334  }
2335  if (cache.m_matstates || errorPropagation) {
2336  cache.m_combinedEloss.update(
2337  cache.m_delIoni * distanceStepped, cache.m_sigmaIoni * std::abs(distanceStepped),
2338  cache.m_delRad * distanceStepped, cache.m_sigmaRad * std::abs(distanceStepped), cache.m_MPV);
2339  }
2340  if (cache.m_material && cache.m_material->x0() != 0.) {
2341  cache.m_combinedThickness += propDir * distanceStepped / cache.m_material->x0();
2342  }
2343 
2344  if (absPath > maxPath)
2345  return false;
2346 
2347  // path limit implemented
2348  if (cache.m_propagateWithPathLimit > 0 && cache.m_pathLimit <= path) {
2349  ++cache.m_propagateWithPathLimit;
2350  return true;
2351  }
2352 
2353  bool restart = false;
2354  // in case of problems, make shorter steps
2355  if (propDir * path < -tol || absPath - std::abs(path) > 10.) {
2356  helpSoft = std::abs(path) / absPath > 0.5 ? std::abs(path) / absPath : 0.5;
2357  }
2358 
2359  Amg::Vector3D position(P[0], P[1], P[2]);
2360  Amg::Vector3D direction(P[3], P[4], P[5]);
2361 
2362  // Adapt step size to the material binning : change of bin layer triggers dump of material effects
2363  float distanceToNextBin = h; // default
2364  if (cache.m_binMat) {
2365  const Trk::BinUtility* lbu = cache.m_binMat->layerBinUtility(position);
2366  if (lbu) {
2367  size_t layerBin = cache.m_binMat->layerBin(position);
2368  const Trk::IdentifiedMaterial* iMat = cache.m_binMat->material(position);
2369  std::pair<size_t, float> dist2next = lbu->distanceToNext(position, propDir * direction);
2370  distanceToNextBin = dist2next.second;
2371  if (layerBin != cache.m_currentLayerBin) { // step into new bin
2372  // check the overshoot
2373  std::pair<size_t, float> dist2previous = lbu->distanceToNext(position, -propDir * direction);
2374  float stepOver = dist2previous.second;
2375  double localp[5];
2377  auto cPar = std::make_unique<Trk::CurvilinearParameters>(Amg::Vector3D(P[0], P[1], P[2]), localp[2],
2378  localp[3], localp[4]);
2379  if (cache.m_identifiedParameters) {
2380  if (binIDMat && binIDMat->second > 0 && !iMat) { // exit from active layer
2381  cache.m_identifiedParameters->push_back(
2382  std::pair<std::unique_ptr<Trk::TrackParameters>, int>(cPar->clone(), -binIDMat->second));
2383  } else if (binIDMat && binIDMat->second > 0 &&
2384  (iMat->second == 0 || iMat->second == binIDMat->second)) { // exit from active layer
2385  cache.m_identifiedParameters->push_back(
2386  std::pair<std::unique_ptr<Trk::TrackParameters>, int>(cPar->clone(), -binIDMat->second));
2387  } else if (iMat && iMat->second > 0) { // entry active layer
2388  cache.m_identifiedParameters->push_back(
2389  std::pair<std::unique_ptr<Trk::TrackParameters>, int>(cPar->clone(), iMat->second));
2390  }
2391  }
2392  if (cache.m_hitVector) {
2393  double hitTiming = cache.m_timeIn + cache.m_timeOfFlight + cache.m_timeStep;
2394  if (binIDMat && binIDMat->second > 0 && !iMat) { // exit from active layer
2395  cache.m_hitVector->push_back(
2396  Trk::HitInfo(cPar->uniqueClone(), hitTiming, -binIDMat->second, 0.));
2397  } else if (binIDMat && binIDMat->second > 0 &&
2398  (iMat->second == 0 || iMat->second == binIDMat->second)) { // exit from active layer
2399  cache.m_hitVector->push_back(
2400  Trk::HitInfo(cPar->uniqueClone(), hitTiming, -binIDMat->second, 0.));
2401  } else if (iMat && iMat->second > 0) { // entry active layer
2402  cache.m_hitVector->push_back(Trk::HitInfo(cPar->uniqueClone(), hitTiming, iMat->second, 0.));
2403  }
2404  }
2405 
2406  cache.m_currentLayerBin = layerBin;
2407  binIDMat = iMat;
2408  if (binIDMat) {
2409  // change of material triggers update of the cache
2410  // @TODO Coverity complains about a possible NULL pointer dereferencing here
2411  // because the code above does not explicitly forbid m_material to be NULL and m_material is used
2412  // unchecked inside updateMaterialEffects.
2413  // Can m_material be NULL at this point ?
2414  if (cache.m_material) {
2415  updateMaterialEffects(cache, mom, sin(direction.theta()), sumPath + path - stepOver);
2416  }
2417  cache.m_material = binIDMat->first;
2418  }
2419  // recalculate distance to next bin
2420  if (distanceToNextBin < h) {
2421  Amg::Vector3D probe = position + (distanceToNextBin + h) * propDir * direction;
2422  std::pair<size_t, float> d2n = lbu->distanceToNext(probe, propDir * direction);
2423  distanceToNextBin += d2n.second + h;
2424  }
2425  } else if (dist2next.first < lbu->bins() && std::abs(distanceToNextBin) < 0.01 &&
2426  h > 0.01) { // tolerance 10 microns ?
2427  double localp[5];
2429  auto cPar = std::make_unique<Trk::CurvilinearParameters>(Amg::Vector3D(P[0], P[1], P[2]), localp[2],
2430  localp[3], localp[4]);
2431 
2432  const Trk::IdentifiedMaterial* nextMat = binIDMat;
2433  // need to know what comes next
2434  Amg::Vector3D probe = position + (distanceToNextBin + 0.01) * propDir * direction.normalized();
2435  nextMat = cache.m_binMat->material(probe);
2436 
2437  if (cache.m_identifiedParameters) {
2438  if (binIDMat && binIDMat->second > 0 && !nextMat) { // exit from active layer
2439  cache.m_identifiedParameters->push_back(
2440  std::pair<std::unique_ptr<Trk::TrackParameters>, int>(cPar->clone(), -binIDMat->second));
2441  } else if (binIDMat && binIDMat->second > 0 &&
2442  (nextMat->second == 0 || nextMat->second == binIDMat->second)) {
2443  // exit from active layer
2444  cache.m_identifiedParameters->push_back(
2445  std::pair<std::unique_ptr<Trk::TrackParameters>, int>(cPar->clone(), -binIDMat->second));
2446  } else if (nextMat && nextMat->second > 0) { // entry active layer
2447  cache.m_identifiedParameters->push_back(
2448  std::pair<std::unique_ptr<Trk::TrackParameters>, int>(cPar->clone(), nextMat->second));
2449  }
2450  }
2451  if (cache.m_hitVector) {
2452  double hitTiming = cache.m_timeIn + cache.m_timeOfFlight + cache.m_timeStep;
2453  if (binIDMat && binIDMat->second > 0 && !nextMat) { // exit from active layer
2454  cache.m_hitVector->push_back(
2455  Trk::HitInfo(cPar->uniqueClone(), hitTiming, -binIDMat->second, 0.));
2456  } else if (binIDMat && binIDMat->second > 0 &&
2457  (nextMat->second == 0 ||
2458  nextMat->second == binIDMat->second)) { // exit from active layer
2459  cache.m_hitVector->push_back(
2460  Trk::HitInfo(cPar->uniqueClone(), hitTiming, -binIDMat->second, 0.));
2461  } else if (nextMat && nextMat->second > 0) { // entry active layer
2462  cache.m_hitVector->push_back(Trk::HitInfo(cPar->uniqueClone(), hitTiming, nextMat->second, 0.));
2463  }
2464  }
2465 
2466  cache.m_currentLayerBin = dist2next.first;
2467  if (binIDMat != nextMat) { // change of material triggers update of the cache
2468  binIDMat = nextMat;
2469  if (binIDMat) {
2470  assert(cache.m_material);
2471  updateMaterialEffects(cache, mom, sin(direction.theta()), sumPath + path);
2472  cache.m_material = binIDMat->first;
2473  }
2474  }
2475  // recalculate distance to next bin
2476  std::pair<size_t, float> d2n = lbu->distanceToNext(probe, propDir * direction.normalized());
2477  distanceToNextBin += d2n.second + 0.01;
2478  }
2479  // TODO: trigger the update of material properties and recalculation of distance to the target sliding
2480  // surface
2481  }
2482  }
2483 
2484  // Calculate new distance to targets
2485  bool flipDirection = false;
2486  numSf = 0;
2487  nextSfCand = nextSf;
2488  double dev = direction0.dot(direction);
2489  std::vector<DestSurf>::iterator sIter = sBeg;
2490  std::vector<std::pair<int, std::pair<double, double>>>::iterator vsIter = vsBeg;
2491  int ic = 0;
2492  int numRestart = 0;
2493 
2494  if (cache.m_brem) {
2495  if (mom < cache.m_bremEmitThreshold && m_simMatUpdator) {
2496  // ST : strictly speaking, the emission point should be shifted backwards a bit
2497  // (mom-m_bremEmitThreshold) this seems to be a minor point
2498  m_simMatUpdator->recordBremPhoton(cache.m_timeIn + cache.m_timeOfFlight + cache.m_timeStep, mom,
2499  cache.m_bremMom, position, direction, cache.m_particle);
2500  cache.m_bremEmitThreshold = 0.;
2501  }
2502  if (mom < cache.m_bremSampleThreshold)
2503  sampleBrem(cache, cache.m_bremSampleThreshold);
2504  }
2505 
2506  for (; vsIter != vsEnd; ++vsIter) {
2507  if (restart) {
2508  ++numRestart;
2509  if (numRestart > restartLimit)
2510  return false;
2511 
2512  vsIter = vsBeg;
2513  ic = 0;
2514  sIter = sBeg;
2515  distanceToTarget = propDir * maxPath;
2516  nextSf = -1;
2517  nextSfCand = -1;
2518  restart = false;
2519  helpSoft = 1.;
2520  }
2521  if ((*vsIter).first != -1 &&
2522  (ic == nextSf || (*vsIter).first == 1 || nextSf < 0 || std::abs((*vsIter).second.first) < 500. ||
2523  std::abs(path) > 0.5 * std::abs((*vsIter).second.second))) {
2524  previousDistance = (*vsIter).second.first;
2525  Trk::DistanceSolution distSol =
2526  (*sIter).first->straightLineDistanceEstimate(position, propDir * direction);
2527  double distanceEst = -propDir * maxPath;
2528  if (distSol.numberOfSolutions() > 0) {
2529  distanceEst = distSol.first();
2530  if (distSol.numberOfSolutions() > 1 &&
2531  std::abs(distSol.first() * propDir + distanceStepped - previousDistance) >
2532  std::abs(distSol.second() * propDir + distanceStepped - previousDistance)) {
2533  distanceEst = distSol.second();
2534  }
2535  // Peter Kluit: avoid jumping into other (distSol.first->second) distance solution for start surface
2536  // with negative distance solution
2537  // negative distanceEst will trigger flipDirection = true and will iterate to the start
2538  // surface this will lead to very similar positions for multiple propagator calls and
2539  // many tiny X0 scatterers
2540  if (ic == startSf && distanceEst < 0 && distSol.first() > 0)
2541  distanceEst = distSol.first();
2542  }
2543  // eliminate close surface if path too small
2544  if (ic == nextSf && std::abs(distanceEst) < tol && std::abs(path) < tol) {
2545  (*vsIter).first = -1;
2546  vsIter = vsBeg;
2547  restart = true;
2548  distanceToTarget = maxPath;
2549  nextSf = -1;
2550  continue;
2551  }
2552 
2553  // If h and distance are in opposite directions, target is passed. Flip propagation direction
2554  // Verify if true intersection
2555  // if ( h * propDir * distanceEst < 0. && std::abs(distanceEst)>distanceTolerance ) {
2556  if ((*vsIter).second.first * propDir * distanceEst < 0. &&
2557  std::abs(distanceEst) > distanceTolerance) {
2558  // verify change of sign in signedDistance ( after eliminating situations where this is meaningless
2559  // )
2560  if (!distSol.signedDistance() || std::abs(distSol.currentDistance(true)) < tol ||
2561  std::abs((*vsIter).second.second) < tol ||
2562  (*vsIter).second.second * distSol.currentDistance(true) < 0) { // true intersection
2563  if (ic == nextSf) {
2564  ((*vsIter).first)++;
2565  // eliminate surface if diverging
2566  if ((*vsIter).first > 3) {
2567  helpSoft = fmax(0.05, 1. - 0.05 * (*vsIter).first);
2568  if ((*vsIter).first > 20)
2569  helpSoft = 1. / (*vsIter).first;
2570  }
2571  // take care of eliminating when number of flips even - otherwise it may end up at the start !
2572  if ((*vsIter).first > 50 && h * propDir > 0) {
2573  // std::abs(distanceEst) >= std::abs(previousDistance) ) {
2574  (*vsIter).first = -1;
2575  vsIter = vsBeg;
2576  restart = true;
2577  continue;
2578  }
2579  if ((*vsIter).first != -1)
2580  flipDirection = true;
2581  } else if (std::abs((*vsIter).second.second) > tol &&
2582  std::abs(distSol.currentDistance(true)) > tol) {
2583  // here we need to compare with distance from current closest
2584  if (ic > nextSf && nextSf != -1) { // easy case, already calculated
2585  if (propDir * distanceEst < (cache.m_currentDist.at(nextSf)).second.first - tol) {
2586  if ((*vsIter).first != -1) {
2587  ((*vsIter).first)++;
2588  flipDirection = true;
2589  nextSf = ic;
2590  }
2591  }
2592  } else if (distanceToTarget >
2593  0.) { // set as nearest (if not iterating already), will be rewritten later
2594  if ((*vsIter).first != -1) {
2595  ((*vsIter).first)++;
2596  flipDirection = true;
2597  nextSf = ic;
2598  }
2599  }
2600  }
2601  } else if (ic == nextSf) {
2602  vsIter = vsBeg;
2603  restart = true;
2604  continue;
2605  }
2606  }
2607 
2608  // save current distance to surface
2609  (*vsIter).second.first = propDir * distanceEst;
2610  (*vsIter).second.second = distSol.currentDistance(true);
2611 
2612  // find closest surface: the step may have been beyond several surfaces
2613  // from all surfaces with 'negative' distance, consider only the one currently designed as 'closest'
2614  // mw if ((*vsIter).first!=-1 && ( distanceEst>-tol || ic==nextSf ) ) {
2615  if ((*vsIter).first != -1 && (distanceEst > 0. || ic == nextSf)) {
2616  ++numSf;
2617  if (distanceEst < std::abs(distanceToTarget)) {
2618  distanceToTarget = propDir * distanceEst;
2619  nextSfCand = ic;
2620  }
2621  }
2622  } else if (std::abs(path) > std::abs((*vsIter).second.second) || dev < 0.985 ||
2623  nextSf < 0) { // keep an eye on surfaces with negative distance; tracks are curved !
2624  Trk::DistanceSolution distSol =
2625  (*sIter).first->straightLineDistanceEstimate(position, propDir * direction);
2626  double distanceEst = -propDir * maxPath;
2627  if (distSol.numberOfSolutions() > 0) {
2628  distanceEst = distSol.first();
2629  }
2630  // save current distance to surface
2631  (*vsIter).second.first = propDir * distanceEst;
2632  (*vsIter).second.second = distSol.currentDistance(true);
2633  // reactivate surface
2634  if (distanceEst > tol && distanceEst < maxPath) {
2635  (*vsIter).first = 0;
2636  } else {
2637  (*vsIter).second.first = distSol.currentDistance() + std::abs(path);
2638  }
2639  if ((*vsIter).first != -1 && distanceEst > 0.) {
2640  ++numSf;
2641  if (distanceEst < std::abs(distanceToTarget)) {
2642  distanceToTarget = propDir * distanceEst;
2643  nextSfCand = ic;
2644  }
2645  }
2646  }
2647  // additional protection - return to the same surface
2648  // eliminate the surface and restart the search
2649  // 04/10/10 ST:infinite loop due to distanceTolerance>tol fixed;
2650  if (std::abs(distanceToTarget) <= distanceTolerance && path * propDir < distanceTolerance) {
2651  (*vsIter).first = -1;
2652  vsIter = vsBeg;
2653  restart = true;
2654  continue;
2655  }
2656  ++sIter;
2657  ++ic;
2658  }
2659  // if next closest not found, propagation failed
2660  if (nextSf < 0 && nextSfCand < 0)
2661  return false;
2662  // flip direction
2663  if (flipDirection) {
2664  // Out of bounds protection
2665  if (nextSf < 0 || nextSf >= num_vs_dist)
2666  return false;
2667  distanceToTarget = (*(vsBeg + nextSf)).second.first;
2668  h = -h;
2669  } else if (nextSfCand != nextSf) {
2670  nextSf = nextSfCand;
2671  // Out of bounds protection
2672  if (nextSf < 0 || nextSf >= num_vs_dist)
2673  return false;
2674  if (cache.m_currentDist[nextSf].first < 3)
2675  helpSoft = 1.;
2676  }
2677 
2678  // don't step beyond surfaces - adjust step
2679  if (std::abs(h) > std::abs(distanceToTarget))
2680  h = distanceToTarget;
2681 
2682  // don't step beyond bin boundary - adjust step
2683  if (cache.m_binMat && std::abs(h) > std::abs(distanceToNextBin) + 0.001) {
2684  if (distanceToNextBin > 0) { // TODO : investigate source of negative distance in BinningData
2685  h = distanceToNextBin * propDir;
2686  }
2687  }
2688 
2689  if (helpSoft < 1.)
2690  h *= helpSoft;
2691 
2692  // don't step much beyond path limit
2693  if (cache.m_propagateWithPathLimit > 0 && h > cache.m_pathLimit)
2694  h = cache.m_pathLimit + tol;
2695 
2696  // Abort if maxPath is reached
2697  if (std::abs(path) > maxPath)
2698  return false;
2699 
2700  if (steps++ > cache.m_maxSteps)
2701  return false; // Too many steps, something is wrong
2702  }
2703 
2704  if (!numSf)
2705  return false;
2706 
2707  // Use Taylor expansions to step the remaining distance (typically microns).
2708  path = path + distanceToTarget;
2709 
2710  // timing
2711  mom = std::abs(1. / P[6]);
2712  beta = mom / std::sqrt(mom * mom + cache.m_particleMass * cache.m_particleMass);
2713  cache.m_timeStep += distanceToTarget / beta / Gaudi::Units::c_light;
2714 
2715  // pos = pos + h*dir + 1/2*h*h*dDir. Second order Taylor expansion.
2716  pos[0] = pos[0] + distanceToTarget * (dir[0] + 0.5 * distanceToTarget * dDir[0]);
2717  pos[1] = pos[1] + distanceToTarget * (dir[1] + 0.5 * distanceToTarget * dDir[1]);
2718  pos[2] = pos[2] + distanceToTarget * (dir[2] + 0.5 * distanceToTarget * dDir[2]);
2719 
2720  // dir = dir + h*dDir. First order Taylor expansion (Euler).
2721  dir[0] = dir[0] + distanceToTarget * dDir[0];
2722  dir[1] = dir[1] + distanceToTarget * dDir[1];
2723  dir[2] = dir[2] + distanceToTarget * dDir[2];
2724 
2725  // Normalize dir
2726  double norm = 1. / std::sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
2727  dir[0] = norm * dir[0];
2728  dir[1] = norm * dir[1];
2729  dir[2] = norm * dir[2];
2730  P[42] = dDir[0];
2731  P[43] = dDir[1];
2732  P[44] = dDir[2];
2733 
2734  // collect all surfaces with distance below tolerance
2735  std::vector<std::pair<int, std::pair<double, double>>>::iterator vsIter = vsBeg;
2736 
2737  int index = 0;
2738  for (; vsIter != vsEnd; ++vsIter) {
2739  if ((*vsIter).first != -1 && propDir * (*vsIter).second.first >= propDir * distanceToTarget - tol &&
2740  propDir * (*vsIter).second.first < 0.01 && index != nextSf) {
2741  solutions.push_back(index);
2742  }
2743  if (index == nextSf)
2744  solutions.push_back(index);
2745  ++index;
2746  }
2747 
2748  return true;
2749 }
2750 
2752 // dump material effects
2755  double path) const {
2756 
2757  // kinematics
2758  double mom = parms->momentum().mag();
2759 
2760  // first update to make sure all material counted
2761  updateMaterialEffects(cache, mom, sin(parms->momentum().theta()), path);
2762 
2763  if (cache.m_extrapolationCache) {
2767  cache.m_combinedEloss.sigmaRad());
2768  }
2769  // output
2770  if (cache.m_matstates) {
2771  auto eloss = !m_detailedEloss
2772  ? std::make_unique<Trk::EnergyLoss>(cache.m_combinedEloss.deltaE(),
2773  cache.m_combinedEloss.sigmaDeltaE())
2774  : std::make_unique<Trk::EnergyLoss>(
2779 
2780  auto sa = Trk::ScatteringAngles(0., 0., std::sqrt(cache.m_covariance(2, 2)),
2781  std::sqrt(cache.m_covariance(3, 3)));
2782 
2783  auto cvlTP = parms->uniqueClone();
2784  auto mefot = std::make_unique<Trk::MaterialEffectsOnTrack>(cache.m_combinedThickness, sa,
2785  std::move(eloss), cvlTP->associatedSurface());
2786 
2787  cache.m_matstates->push_back(new TrackStateOnSurface(nullptr, std::move(cvlTP), std::move(mefot)));
2788  }
2789 
2790  cache.m_matdump_lastpath = path;
2791 
2792  // clean-up
2793  cache.m_combinedCovariance += cache.m_covariance;
2794  cache.m_covariance.setZero();
2795  cache.m_combinedThickness = 0.;
2796  cache.m_combinedEloss.set(0., 0., 0., 0., 0., 0.);
2797 }
2798 
2799 // Smear momentum ( simulation mode )
2801 void Trk::STEP_Propagator::smear(Cache& cache, double& phi, double& theta, const Trk::TrackParameters* parms,
2802  double radDist) const {
2803  if (cache.m_particle == Trk::geantino)
2804  return;
2805  if (!parms)
2806  return;
2807 
2808  if (!cache.m_randomEngine) {
2809  cache.m_randomEngine = getRandomEngine(cache.m_ctx);
2810  }
2811 
2812  // Calculate polar angle
2813  double particleMass = Trk::ParticleMasses::mass[cache.m_particle]; // Get particle mass from
2814  // ParticleHypothesis
2815  double momentum = parms->momentum().mag();
2816  double energy = std::sqrt(momentum * momentum + particleMass * particleMass);
2817  double beta = momentum / energy;
2818  double th = std::sqrt(2.) * 15. * std::sqrt(radDist) / (beta * momentum) *
2819  CLHEP::RandGauss::shoot(cache.m_randomEngine); // Moliere
2820  // double th = (sqrt(2.)*13.6*std::sqrt(radDist)/(beta*momentum)) *
2821  // (1.+0.038*log(radDist/(beta*beta))) * m_gaussian->shoot(); //Highland
2822 
2823  // Calculate azimuthal angle
2824  double ph = 2. * M_PI * CLHEP::RandFlat::shoot(cache.m_randomEngine);
2825 
2827  Amg::AngleAxis3D(-phi, Amg::Vector3D(0., 0., 1.)));
2828  Amg::Vector3D dir0(0., 0., 1.);
2829  Amg::Vector3D rotated = rot.inverse() * Amg::AngleAxis3D(ph, Amg::Vector3D(0., 0., 1.)) *
2830  Amg::AngleAxis3D(th, Amg::Vector3D(0., 1., 0.)) * dir0;
2831 
2832  theta = rotated.theta();
2833  phi = rotated.phi();
2834 }
2835 
2836 // Sample momentum of brem photon ( simulation mode )
2838 void Trk::STEP_Propagator::sampleBrem(Cache& cache, double mom) const {
2839  if (!cache.m_randomEngine) {
2840  cache.m_randomEngine = getRandomEngine(cache.m_ctx);
2841  }
2842  double rndx = CLHEP::RandFlat::shoot(cache.m_randomEngine);
2843  double rnde = CLHEP::RandFlat::shoot(cache.m_randomEngine);
2844 
2845  // sample visible fraction of the mother momentum taken according to 1/f
2846  double eps = cache.m_momentumCutOff / mom;
2847  cache.m_bremMom = pow(eps, pow(rndx, exp(1.))) * mom; // adjustment here ?
2848  cache.m_bremSampleThreshold = mom - cache.m_bremMom;
2849  cache.m_bremEmitThreshold = mom - rnde * cache.m_bremMom;
2850 }
2851 
2852 void Trk::STEP_Propagator::getFieldCacheObject(Cache& cache, const EventContext& ctx) const {
2853  SG::ReadCondHandle<AtlasFieldCacheCondObj> readHandle{m_fieldCacheCondObjInputKey, ctx};
2854  const AtlasFieldCacheCondObj* fieldCondObj{*readHandle};
2855  if (fieldCondObj == nullptr) {
2856  ATH_MSG_ERROR("extrapolate: Failed to retrieve AtlasFieldCacheCondObj with key "
2857  << m_fieldCacheCondObjInputKey.key());
2858  return;
2859  }
2860  fieldCondObj->getInitializedCache(cache.m_fieldCache);
2861 }
2862 
2863 CLHEP::HepRandomEngine*
2864 Trk::STEP_Propagator::getRandomEngine (const EventContext& ctx) const
2865 {
2866  if (!m_simulation || !m_rngWrapper) {
2867  return nullptr;
2868  }
2869  if (m_rngWrapper->evtSeeded(ctx) != ctx.evt()) {
2870  // Ok, the wrappers are unique to this algorithm and a given slot,
2871  // so cannot be accessed concurrently.
2872  ATHRNG::RNGWrapper* wrapper_nc ATLAS_THREAD_SAFE = m_rngWrapper;
2873  wrapper_nc->setSeed (this->name(), ctx);
2874  }
2875  return m_rngWrapper->getEngine (ctx);
2876 }
BinnedMaterial.h
Trk::ExtrapolationCache::updateX0
void updateX0(double x0)
xAOD::iterator
JetConstituentVector::iterator iterator
Definition: JetConstituentVector.cxx:68
Trk::Material::averageZ
float averageZ() const
Definition: Material.h:227
Trk::STEP_Propagator::m_includeBgradients
bool m_includeBgradients
Include B-gradients in the error propagation.
Definition: STEP_Propagator.h:498
Trk::DistanceSolution::currentDistance
double currentDistance(bool signedDist=false) const
Current distance to surface (spatial), signed (along/opposite to surface normal) if input argument tr...
Trk::RungeKuttaUtils::jacobianTransformCurvilinearToLocal
void jacobianTransformCurvilinearToLocal(const Trk::TrackParameters &, double *)
Definition: RungeKuttaUtils.cxx:1690
Trk::STEP_Propagator::dumpMaterialEffects
void dumpMaterialEffects(Cache &cache, const Trk::CurvilinearParameters *trackParameters, double path) const
Definition: STEP_Propagator.cxx:2754
Trk::STEP_Propagator::Cache::m_kazL
double m_kazL
Definition: STEP_Propagator.h:375
Trk::EnergyLoss::update
void update(double ioni, double sigi, double rad, double sigr, bool mpv=false)
Trk::DistanceSolution::signedDistance
bool signedDistance() const
This method indicates availability of signed current distance (false for Perigee and StraighLineSurfa...
Trk::STEP_Propagator::Cache::m_bremEmitThreshold
double m_bremEmitThreshold
Definition: STEP_Propagator.h:395
Trk::STEP_Propagator::Cache::m_momentumCutOff
double m_momentumCutOff
Definition: STEP_Propagator.h:387
PlotCalibFromCool.norm
norm
Definition: PlotCalibFromCool.py:100
ScatteringAngles.h
Trk::STEP_Propagator::m_energyLoss
bool m_energyLoss
Definition: STEP_Propagator.h:502
Trk::MaterialInteraction::dEdl_ionization
static double dEdl_ionization(double p, const Material &mat, ParticleHypothesis particle, double &sigma, double &kazL)
dE/dl ionization energy loss per path unit
Definition: MaterialInteraction.cxx:117
Trk::STEP_Propagator::Cache::m_bremSampleThreshold
double m_bremSampleThreshold
Definition: STEP_Propagator.h:396
Trk::ParticleSwitcher::particle
constexpr ParticleHypothesis particle[PARTICLEHYPOTHESES]
the array of masses
Definition: ParticleHypothesis.h:76
Trk::ParametersCommon::m_covariance
std::optional< AmgSymMatrix(DIM)> m_covariance
charge definition for this track
Definition: ParametersCommon.h:147
Trk::k0
@ k0
Definition: ParticleHypothesis.h:35
python.SystemOfUnits.s
int s
Definition: SystemOfUnits.py:131
Trk::STEP_Propagator::m_stragglingScale
double m_stragglingScale
Definition: STEP_Propagator.h:506
inline_hints.h
python.SystemOfUnits.m
int m
Definition: SystemOfUnits.py:91
python.PerfMonSerializer.p
def p
Definition: PerfMonSerializer.py:743
athena.path
path
python interpreter configuration --------------------------------------—
Definition: athena.py:126
max
#define max(a, b)
Definition: cfImp.cxx:41
Trk::RungeKuttaUtils::transformGlobalToCurvilinear
void transformGlobalToCurvilinear(bool, double *ATH_RESTRICT, double *ATH_RESTRICT, double *ATH_RESTRICT)
Trk::STEP_Propagator::Cache::m_multipleScattering
bool m_multipleScattering
Definition: STEP_Propagator.h:366
Trk::STEP_Propagator::m_momentumCutOff
double m_momentumCutOff
Stop propagation below this momentum.
Definition: STEP_Propagator.h:500
SG::ReadCondHandle
Definition: ReadCondHandle.h:44
Trk::STEP_Propagator::m_includeGgradient
bool m_includeGgradient
Include g-gradient in the error propagation.
Definition: STEP_Propagator.h:499
Trk::MagneticFieldProperties
Definition: MagneticFieldProperties.h:31
IDTPM::R
float R(const U &p)
Definition: TrackParametersHelper.h:101
Trk::ParametersBase::charge
double charge() const
Returns the charge.
Surface.h
AtlasFieldCacheCondObj
Definition: AtlasFieldCacheCondObj.h:19
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
Trk::STEP_Propagator::Cache::m_ctx
const EventContext & m_ctx
Definition: STEP_Propagator.h:423
Trk::STEP_Propagator::Cache::m_matPropOK
bool m_matPropOK
Definition: STEP_Propagator.h:361
Trk::DistanceSolution
Definition: DistanceSolution.h:25
Trk::STEP_Propagator::Cache::m_P
double m_P[45]
Definition: STEP_Propagator.h:397
MagField::AtlasFieldCache::getFieldZR
void getFieldZR(const double *ATH_RESTRICT xyz, double *ATH_RESTRICT bxyz, double *ATH_RESTRICT deriv=nullptr)
get B field valaue on the z-r plane at given position works only inside the solenoid.
Definition: AtlasFieldCache.cxx:86
Trk::PerigeeSurface
Definition: PerigeeSurface.h:43
Trk::ParametersBase::position
const Amg::Vector3D & position() const
Access method for the position.
Amg::Vector2D
Eigen::Matrix< double, 2, 1 > Vector2D
Definition: GeoPrimitives.h:48
Trk::DistanceSolution::numberOfSolutions
int numberOfSolutions() const
Number of intersection solutions.
index
Definition: index.py:1
Trk::PathLimit::updateMat
void updateMat(float dX0, float Z, float dL0)
collected material update
Definition: HelperStructs.h:51
Trk::ParametersBase::associatedSurface
virtual const Surface & associatedSurface() const override=0
Access to the Surface associated to the Parameters.
AthCommonDataStore< AthCommonMsg< AlgTool > >::declareProperty
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T > &t)
Definition: AthCommonDataStore.h:145
Trk::ParametersBase::uniqueClone
std::unique_ptr< ParametersBase< DIM, T > > uniqueClone() const
clone method for polymorphic deep copy returning unique_ptr; it is not overriden, but uses the existi...
Definition: ParametersBase.h:97
Trk::STEP_Propagator::Cache::m_stragglingVariance
double m_stragglingVariance
Definition: STEP_Propagator.h:379
hist_file_dump.d
d
Definition: hist_file_dump.py:137
Trk::pi0
@ pi0
Definition: ParticleHypothesis.h:34
Trk::STEP_Propagator::Cache::m_charge
double m_charge
Definition: STEP_Propagator.h:384
EventPrimitivesHelpers.h
Trk::STEP_Propagator::Cache::m_bremMom
double m_bremMom
Definition: STEP_Propagator.h:394
conifer::pow
constexpr int pow(int x)
Definition: conifer.h:20
DMTest::C
C_v1 C
Definition: C.h:26
hitVector
std::vector< FPGATrackSimHit > hitVector
Definition: FPGATrackSimCluster.h:23
Amg::saneCovarianceElement
bool saneCovarianceElement(double ele)
A covariance matrix formally needs to be positive semi definite.
Definition: EventPrimitivesCovarianceHelpers.h:63
BinUtility.h
vectorize.h
Helper to enable auto-vectorization.
Trk::BinUtility::distanceToNext
std::pair< size_t, float > distanceToNext(const Amg::Vector3D &position, const Amg::Vector3D &direction, size_t ba=0) const
Distance estimate to next bin
Definition: BinUtility.h:161
Trk::STEP_Propagator::globalPositions
virtual void globalPositions(const EventContext &ctx, std::deque< Amg::Vector3D > &positionsList, const TrackParameters &trackParameters, const MagneticFieldProperties &magneticFieldProperties, const CylinderBounds &cylinderBounds, double maxStepSize, ParticleHypothesis particle, const Trk::TrackingVolume *tVol=0) const override final
Return a list of positions along the track.
Definition: STEP_Propagator.cxx:1816
Trk::Material::zOverAtimesRho
float zOverAtimesRho() const
access to members
Definition: Material.h:225
Trk::EnergyLoss::sigmaDeltaE
double sigmaDeltaE() const
returns the symmatric error
Trk::STEP_Propagator::m_MPV
bool m_MPV
Definition: STEP_Propagator.h:505
M_PI
#define M_PI
Definition: ActiveFraction.h:11
Trk::STEP_Propagator::Cache::m_solenoid
bool m_solenoid
Switch for turning off material effects temporarily.
Definition: STEP_Propagator.h:359
Trk::Perigee
ParametersT< 5, Charged, PerigeeSurface > Perigee
Definition: Tracking/TrkEvent/TrkParameters/TrkParameters/TrackParameters.h:29
Trk::STEP_Propagator::Cache::m_hitVector
std::vector< Trk::HitInfo > * m_hitVector
Definition: STEP_Propagator.h:407
Trk::ScatteringAngles
represents a deflection of the track caused through multiple scattering in material.
Definition: ScatteringAngles.h:26
Trk::CylinderSurface::bounds
virtual const CylinderBounds & bounds() const override final
This method returns the CylinderBounds by reference (NoBounds is not possible for cylinder)
Amg::saneVector
bool saneVector(const AmgVector(N) &vec)
Definition: EventPrimitivesHelpers.h:28
Trk::SurfaceType
SurfaceType
Definition: SurfaceTypes.h:17
UploadAMITag.l
list l
Definition: UploadAMITag.larcaf.py:158
STEP_Propagator.h
Trk::TrackSurfaceIntersection
Definition: TrackSurfaceIntersection.h:32
Trk::alongMomentum
@ alongMomentum
Definition: PropDirection.h:20
Trk::STEP_Propagator::m_materialEffects
bool m_materialEffects
Switch material effects on or off.
Definition: STEP_Propagator.h:497
Trk::EnergyLoss::sigmaRad
double sigmaRad() const
Trk::Curvilinear
@ Curvilinear
Definition: ParametersCommon.h:29
read_hist_ntuple.t
t
Definition: read_hist_ntuple.py:5
Trk::DistanceSolution::first
double first() const
Distance to first intersection solution along direction.
TransportJacobian.h
Trk::EnergyLoss::meanIoni
double meanIoni() const
drawFromPickle.exp
exp
Definition: drawFromPickle.py:36
Trk::STEP_Propagator::Cache::m_sigmaIoni
double m_sigmaIoni
Definition: STEP_Propagator.h:374
Trk::RungeKuttaUtils::transformLocalToGlobal
bool transformLocalToGlobal(bool, const Trk::TrackParameters &, double *ATH_RESTRICT)
python.copyTCTOutput.dDir
dDir
Definition: copyTCTOutput.py:61
Trk::STEP_Propagator::Cache::m_tolerance
double m_tolerance
Definition: STEP_Propagator.h:386
Trk::AlignableTrackingVolume::binnedMaterial
const BinnedMaterial * binnedMaterial() const
access to binned material
Definition: AlignableTrackingVolume.h:74
ParamDefs.h
Trk::STEP_Propagator::Cache::m_timeStep
double m_timeStep
Definition: STEP_Propagator.h:382
AmgSymMatrix
#define AmgSymMatrix(dim)
Definition: EventPrimitives.h:52
Trk::STEP_Propagator::Cache::m_combinedThickness
double m_combinedThickness
Definition: STEP_Propagator.h:385
Trk::RungeKuttaUtils::transformGlobalToLocal
void transformGlobalToLocal(double *ATH_RESTRICT, double *ATH_RESTRICT)
Trk::BinUtility::bins
size_t bins(size_t ba=0) const
Number of bins.
Definition: BinUtility.h:223
Trk::STEP_Propagator::Cache::m_timeIn
double m_timeIn
Definition: STEP_Propagator.h:393
ATH_RESTRICT
#define ATH_RESTRICT
Definition: restrict.h:31
ExtrapolationCache.h
MagneticFieldProperties.h
MaterialEffectsOnTrack.h
Trk::STEP_Propagator::m_simulation
bool m_simulation
Definition: STEP_Propagator.h:514
Trk::FastField
@ FastField
call the fast field access method of the FieldSvc
Definition: MagneticFieldMode.h:20
Trk::AmgSymMatrix
AmgSymMatrix(5) &GXFTrackState
Definition: GXFTrackState.h:156
Trk::ParticleHypothesis
ParticleHypothesis
Definition: ParticleHypothesis.h:25
Trk::STEP_Propagator::Cache::m_straggling
bool m_straggling
Definition: STEP_Propagator.h:358
Trk::PropDirection
PropDirection
Definition: PropDirection.h:19
Trk::TimeLimit::tMax
float tMax
Definition: HelperStructs.h:59
Trk::STEP_Propagator::Cache::m_particleMass
double m_particleMass
cache
Definition: STEP_Propagator.h:383
H
#define H(x, y, z)
Definition: MD5.cxx:114
Trk::Surface::isOnSurface
virtual bool isOnSurface(const Amg::Vector3D &glopo, const BoundaryCheck &bchk=true, double tol1=0., double tol2=0.) const
This method returns true if the GlobalPosition is on the Surface for both, within or without check of...
Definition: Surface.cxx:123
Trk::Material::x0
float x0() const
Definition: Material.h:226
Trk::STEP_Propagator::sampleBrem
void sampleBrem(Cache &cache, double mom) const
Definition: STEP_Propagator.cxx:2838
ParticleGun_EoverP_Config.mom
mom
Definition: ParticleGun_EoverP_Config.py:63
beamspotman.steps
int steps
Definition: beamspotman.py:505
Trk::STEP_Propagator::Cache::m_matdump_lastpath
double m_matdump_lastpath
Definition: STEP_Propagator.h:371
python.TriggerHandler.th
th
Definition: TriggerHandler.py:296
Trk::PathLimit::x0Collected
float x0Collected
Definition: HelperStructs.h:36
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
ParticleGun_FastCalo_ChargeFlip_Config.energy
energy
Definition: ParticleGun_FastCalo_ChargeFlip_Config.py:78
EventPrimitivesToStringConverter.h
Trk::STEP_Propagator::m_scatteringScale
double m_scatteringScale
Definition: STEP_Propagator.h:507
ParticleGun_EoverP_Config.momentum
momentum
Definition: ParticleGun_EoverP_Config.py:63
TrigVtx::gamma
@ gamma
Definition: TrigParticleTable.h:26
Trk::STEP_Propagator::m_layXmax
double m_layXmax
Definition: STEP_Propagator.h:510
lumiFormat.i
int i
Definition: lumiFormat.py:92
Trk::TrackSurfaceIntersection::position
const Amg::Vector3D & position() const
Method to retrieve the position of the Intersection.
Definition: TrackSurfaceIntersection.h:80
Trk::Surface::createUniqueTrackParameters
virtual ChargedTrackParametersUniquePtr createUniqueTrackParameters(double l1, double l2, double phi, double theat, double qop, std::optional< AmgSymMatrix(5)> cov=std::nullopt) const =0
Use the Surface as a ParametersBase constructor, from local parameters - charged.
Trk::STEP_Propagator::propagateWithJacobian
bool propagateWithJacobian(Cache &cache, bool errorPropagation, std::vector< DestSurf > &sfs, double *P, Trk::PropDirection propDir, std::vector< unsigned int > &solutions, double &path, double sumPath) const
Definition: STEP_Propagator.cxx:2136
ATH_FLATTEN
#define ATH_FLATTEN
Definition: inline_hints.h:52
beamspotman.n
n
Definition: beamspotman.py:731
python.CaloCondTools.g
g
Definition: CaloCondTools.py:15
Trk::theta
@ theta
Definition: ParamDefs.h:72
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
Trk::STEP_Propagator::Cache
stuct to pass information to the heavy lifting calculation internal methods
Definition: STEP_Propagator.h:355
Trk::electron
@ electron
Definition: ParticleHypothesis.h:27
extractSporadic.h
list h
Definition: extractSporadic.py:97
Trk::CylinderSurface
Definition: CylinderSurface.h:55
AmgVector
AmgVector(4) T2BSTrackFilterTool
Definition: T2BSTrackFilterTool.cxx:114
Trk::STEP_Propagator::m_maxPath
double m_maxPath
Definition: STEP_Propagator.h:508
Trk::STEP_Propagator::propagateParameters
virtual std::unique_ptr< Trk::TrackParameters > propagateParameters(const EventContext &ctx, const Trk::TrackParameters &trackParameters, const Trk::Surface &targetSurface, Trk::PropDirection propagationDirection, const Trk::BoundaryCheck &boundaryCheck, const MagneticFieldProperties &magneticFieldProperties, ParticleHypothesis particle, bool returnCurv=false, const Trk::TrackingVolume *tVol=nullptr) const override final
Propagate parameters only.
Definition: STEP_Propagator.cxx:1564
Trk::CylinderBounds
Definition: CylinderBounds.h:46
TRT::Hit::layer
@ layer
Definition: HitInfo.h:79
Trk::DistanceSolution::second
double second() const
Distance to second intersection solution along direction (for a cylinder surface)
Trk::STEP_Propagator::m_detailedEloss
bool m_detailedEloss
Definition: STEP_Propagator.h:503
Trk::EnergyLoss::set
void set(double eLoss, double sigde, double ioni, double sigi, double rad, double sigr)
Amg::Transform3D
Eigen::Affine3d Transform3D
Definition: GeoPrimitives.h:46
Trk::STEP_Propagator::Cache::m_inputThetaVariance
double m_inputThetaVariance
Definition: STEP_Propagator.h:378
Trk::geantino
@ geantino
Definition: ParticleHypothesis.h:26
Trk::EnergyLoss::deltaE
double deltaE() const
returns the
Trk::STEP_Propagator::getFieldCacheObject
void getFieldCacheObject(Cache &cache, const EventContext &ctx) const
Definition: STEP_Propagator.cxx:2852
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
Trk::STEP_Propagator::Cache::m_pathLimit
double m_pathLimit
Definition: STEP_Propagator.h:380
Trk::STEP_Propagator::propagateT
virtual std::unique_ptr< Trk::TrackParameters > propagateT(const EventContext &ctx, const Trk::TrackParameters &trackParameters, std::vector< Trk::DestSurf > &targetSurfaces, Trk::PropDirection propagationDirection, const MagneticFieldProperties &magneticFieldProperties, ParticleHypothesis particle, std::vector< unsigned int > &solutions, Trk::PathLimit &path, Trk::TimeLimit &time, bool returnCurv, const Trk::TrackingVolume *tVol, std::vector< Trk::HitInfo > *&hitVector) const override final
Propagate parameters and covariance with search of closest surface time included.
Definition: STEP_Propagator.cxx:1364
Trk::STEP_Propagator::getRandomEngine
CLHEP::HepRandomEngine * getRandomEngine(const EventContext &ctx) const
Definition: STEP_Propagator.cxx:2864
Trk::ParametersBase
Definition: ParametersBase.h:55
Trk::CurvilinearParametersT
Definition: CurvilinearParametersT.h:48
Trk::STEP_Propagator::Cache::m_binMat
const Trk::BinnedMaterial * m_binMat
cache of TrackStateOnSurfaces
Definition: STEP_Propagator.h:399
xAOD::double
double
Definition: CompositeParticle_v1.cxx:159
Trk::SurfaceType::Cone
@ Cone
Trk::STEP_Propagator::m_simMatUpdator
ToolHandle< ITimedMatEffUpdator > m_simMatUpdator
secondary interactions (brem photon emission)
Definition: STEP_Propagator.h:516
Trk::STEP_Propagator::Cache::m_particle
ParticleHypothesis m_particle
Definition: STEP_Propagator.h:409
Trk::ParametersBase::type
constexpr virtual ParametersType type() const override=0
Return the ParametersType enum.
Trk::TimeLimit
Definition: HelperStructs.h:58
Trk::muon
@ muon
Definition: ParticleHypothesis.h:28
Trk::neutron
@ neutron
Definition: ParticleHypothesis.h:33
ParticleHypothesis.h
Trk::EnergyLoss::meanRad
double meanRad() const
Trk::STEP_Propagator::m_multipleScattering
bool m_multipleScattering
Switch multiple scattering on or off.
Definition: STEP_Propagator.h:501
Trk::STEP_Propagator::Cache::m_delIoni
double m_delIoni
Definition: STEP_Propagator.h:373
Trk::STEP_Propagator::intersect
virtual std::optional< TrackSurfaceIntersection > intersect(const EventContext &ctx, const Trk::TrackParameters &trackParameters, const Trk::Surface &targetSurface, const Trk::MagneticFieldProperties &magneticFieldProperties, ParticleHypothesis particle, const Trk::TrackingVolume *tVol=nullptr) const override final
Propagate parameters and return path (Similar to propagateParameters.
Definition: STEP_Propagator.cxx:1648
Trk::STEP_Propagator::intersectSurface
virtual std::optional< TrackSurfaceIntersection > intersectSurface(const EventContext &ctx, const Surface &surface, const TrackSurfaceIntersection &trackIntersection, const double qOverP, const MagneticFieldProperties &mft, ParticleHypothesis particle) const override final
Intersection and propagation:
Definition: STEP_Propagator.cxx:1788
beamspotman.dir
string dir
Definition: beamspotman.py:623
min
#define min(a, b)
Definition: cfImp.cxx:40
Trk::BinUtility
Definition: BinUtility.h:39
grepfile.ic
int ic
Definition: grepfile.py:33
Trk::STEP_Propagator::Cache::m_currentLayerBin
size_t m_currentLayerBin
Definition: STEP_Propagator.h:368
Trk::MaterialInteraction::dEdl_radiation
static double dEdl_radiation(double p, const Material &mat, ParticleHypothesis particle, double &sigma)
dE/dl radiation energy loss per path unit
Definition: MaterialInteraction.cxx:229
Trk::ParticleMasses::mass
constexpr double mass[PARTICLEHYPOTHESES]
the array of masses
Definition: ParticleHypothesis.h:53
Trk::NoField
@ NoField
Field is set to 0., 0., 0.,.
Definition: MagneticFieldMode.h:18
EventPrimitives.h
Trk::STEP_Propagator::m_randomEngineName
std::string m_randomEngineName
Definition: STEP_Propagator.h:521
Trk::SurfaceType::Perigee
@ Perigee
Trk::STEP_Propagator::Cache::m_currentDist
std::vector< std::pair< int, std::pair< double, double > > > m_currentDist
Definition: STEP_Propagator.h:420
Trk::STEP_Propagator::propagateRungeKutta
std::unique_ptr< Trk::TrackParameters > propagateRungeKutta(Cache &cache, bool errorPropagation, const Trk::TrackParameters &trackParameters, std::vector< DestSurf > &targetSurfaces, Trk::PropDirection propagationDirection, const MagneticFieldProperties &magneticFieldProperties, ParticleHypothesis particle, std::vector< unsigned int > &solutions, double &path, bool returnCurv=false) const
Definition: STEP_Propagator.cxx:1926
Trk::TrackStateOnSurface
represents the track state (measurement, material, fit parameters and quality) at a surface.
Definition: TrackStateOnSurface.h:71
Trk::STEP_Propagator::Cache::m_MPV
bool m_MPV
Definition: STEP_Propagator.h:365
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:192
ATHRNG::RNGWrapper
A wrapper class for event-slot-local random engines.
Definition: RNGWrapper.h:56
VP1PartSpect::E
@ E
Definition: VP1PartSpectFlags.h:21
Trk::STEP_Propagator::Cache::m_detailedElossFlag
bool m_detailedElossFlag
Definition: STEP_Propagator.h:357
charge
double charge(const T &p)
Definition: AtlasPID.h:494
Trk::STEP_Propagator::Cache::m_combinedEloss
Trk::EnergyLoss m_combinedEloss
Definition: STEP_Propagator.h:419
Trk::PathLimit
Definition: HelperStructs.h:34
python.PhysicalConstants.c_light
float c_light
Definition: PhysicalConstants.py:63
Trk::Surface::insideBounds
virtual bool insideBounds(const Amg::Vector2D &locpos, double tol1=0., double tol2=0.) const =0
virtual methods to be overwritten by the inherited surfaces
Amg::Vector3D
Eigen::Matrix< double, 3, 1 > Vector3D
Definition: GeoPrimitives.h:47
Trk::ExtrapolationCache::updateEloss
void updateEloss(double ioni, double sigi, double rad, double sigr)
Trk::MagneticFieldProperties::magneticFieldMode
MagneticFieldMode magneticFieldMode() const
Returns the MagneticFieldMode as specified.
Trk::STEP_Propagator::Cache::m_brem
bool m_brem
Definition: STEP_Propagator.h:362
Trk::TrackSurfaceIntersection::direction
const Amg::Vector3D & direction() const
Method to retrieve the direction at the Intersection.
Definition: TrackSurfaceIntersection.h:88
Trk::STEP_Propagator::smear
void smear(Cache &cache, double &phi, double &theta, const Trk::TrackParameters *parms, double radDist) const
Definition: STEP_Propagator.cxx:2801
RNGWrapper.h
python.LumiBlobConversion.pos
pos
Definition: LumiBlobConversion.py:18
Trk::STEP_Propagator::Cache::m_extrapolationCache
Trk::ExtrapolationCache * m_extrapolationCache
Definition: STEP_Propagator.h:413
Trk::STEP_Propagator::Cache::m_matupd_lastpath
double m_matupd_lastpath
Definition: STEP_Propagator.h:370
EventPrimitivesCovarianceHelpers.h
Trk::STEP_Propagator::Cache::m_material
const Material * m_material
cache for collecting the total X0 ans Elos
Definition: STEP_Propagator.h:411
Trk::ParametersBase::momentum
const Amg::Vector3D & momentum() const
Access method for the momentum.
Trk::BinnedMaterial::layerBinUtility
const Trk::BinUtility * layerBinUtility(const Amg::Vector3D &position) const
access to layer bin utility
Definition: BinnedMaterial.h:91
Trk::STEP_Propagator::Cache::m_sigmaRad
double m_sigmaRad
Definition: STEP_Propagator.h:376
DeMoScan.index
string index
Definition: DeMoScan.py:362
DiTauMassTools::MaxHistStrategyV2::e
e
Definition: PhysicsAnalysis/TauID/DiTauMassTools/DiTauMassTools/HelperFunctions.h:26
Trk::STEP_Propagator::m_maxSteps
double m_maxSteps
Definition: STEP_Propagator.h:509
h
ITimedMatEffUpdator.h
Trk::STEP_Propagator::Cache::m_matstates
std::vector< const Trk::TrackStateOnSurface * > * m_matstates
cache of intersections
Definition: STEP_Propagator.h:401
Trk::STEP_Propagator::Cache::m_identifiedParameters
std::vector< std::pair< std::unique_ptr< Trk::TrackParameters >, int > > * m_identifiedParameters
cache of intersections/hit info
Definition: STEP_Propagator.h:404
mapkey::sf
@ sf
Definition: TElectronEfficiencyCorrectionTool.cxx:38
Trk::STEP_Propagator::Cache::m_fieldCache
MagField::AtlasFieldCache m_fieldCache
Definition: STEP_Propagator.h:421
Amg::intersect
std::optional< double > intersect(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
Calculates the closest approach of two lines.
Definition: GeoPrimitivesHelpers.h:302
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
Trk::STEP_Propagator::Cache::m_delRad
double m_delRad
Definition: STEP_Propagator.h:372
Trk::STEP_Propagator::Cache::m_maxSteps
double m_maxSteps
Definition: STEP_Propagator.h:390
Trk::STEP_Propagator::Cache::m_trackingVolume
const TrackingVolume * m_trackingVolume
Definition: STEP_Propagator.h:410
Trk::userOwn
@ userOwn
Definition: Tracking/TrkDetDescr/TrkSurfaces/TrkSurfaces/Surface.h:57
Trk::BoundaryCheck
Definition: BoundaryCheck.h:51
Trk::STEP_Propagator::STEP_Propagator
STEP_Propagator(const std::string &, const std::string &, const IInterface *)
Definition: STEP_Propagator.cxx:1179
RungeKuttaUtils.h
Trk::BinnedMaterial::material
const IdentifiedMaterial * material(const Amg::Vector3D &position) const
access to material/id per bin
Definition: BinnedMaterial.cxx:65
Trk::STEP_Propagator::m_rndGenSvc
ServiceHandle< IAthRNGSvc > m_rndGenSvc
Random Generator service.
Definition: STEP_Propagator.h:518
DeMoScan.first
bool first
Definition: DeMoScan.py:534
Trk::STEP_Propagator::Cache::m_propagateWithPathLimit
int m_propagateWithPathLimit
Definition: STEP_Propagator.h:367
Trk::STEP_Propagator::Cache::m_randomEngine
CLHEP::HepRandomEngine * m_randomEngine
Definition: STEP_Propagator.h:422
Trk::qOverP
@ qOverP
perigee
Definition: ParamDefs.h:73
Trk::SurfaceType::Disc
@ Disc
python.CaloCondTools.log
log
Definition: CaloCondTools.py:20
Trk::RungeKuttaUtils::stepEstimator
double stepEstimator(int kind, double *ATH_RESTRICT Su, const double *ATH_RESTRICT P, bool &Q)
Definition: RungeKuttaUtils.cxx:1233
Trk::SurfaceType::Cylinder
@ Cylinder
Trk::STEP_Propagator::propagateM
virtual std::unique_ptr< Trk::TrackParameters > propagateM(const EventContext &ctx, const Trk::TrackParameters &trackParameters, std::vector< Trk::DestSurf > &targetSurfaces, Trk::PropDirection propagationDirection, const MagneticFieldProperties &magneticFieldProperties, ParticleHypothesis particle, std::vector< unsigned int > &solutions, std::vector< const Trk::TrackStateOnSurface * > *&matstates, std::vector< std::pair< std::unique_ptr< Trk::TrackParameters >, int >> *intersections, double &path, bool usePathLimit=false, bool returnCurv=false, const Trk::TrackingVolume *tVol=nullptr, Trk::ExtrapolationCache *=nullptr) const override final
Propagate parameters and covariance with search of closest surface and material collection.
Definition: STEP_Propagator.cxx:1450
Trk::TimeLimit::time
float time
Definition: HelperStructs.h:60
Trk::photon
@ photon
Definition: ParticleHypothesis.h:32
Trk::TrackingVolume::isAlignable
virtual bool isAlignable() const
physics_parameters.parameters
parameters
Definition: physics_parameters.py:144
Trk::ConeSurface
Definition: ConeSurface.h:51
Amg::AngleAxis3D
Eigen::AngleAxisd AngleAxis3D
Definition: GeoPrimitives.h:45
Trk::STEP_Propagator::m_tolerance
double m_tolerance
Error tolerance.
Definition: STEP_Propagator.h:496
Trk::SurfaceType::Plane
@ Plane
Trk::HitInfo
Definition: HelperStructs.h:12
Trk::phi
@ phi
Definition: ParamDefs.h:81
Trk::STEP_Propagator::m_straggling
bool m_straggling
Definition: STEP_Propagator.h:504
Trk::PathLimit::x0Max
float x0Max
Definition: HelperStructs.h:35
ATLAS_THREAD_SAFE
#define ATLAS_THREAD_SAFE
Definition: checker_macros.h:211
ATH_ENABLE_VECTORIZATION
ATH_ENABLE_VECTORIZATION
enables -ftree-vectorize in gcc prior to 12
Definition: STEP_Propagator.cxx:48
Trk::BinnedMaterial::layerBin
size_t layerBin(const Amg::Vector3D &position) const
layer bin
Definition: BinnedMaterial.h:96
Trk::nonInteractingMuon
@ nonInteractingMuon
Definition: ParticleHypothesis.h:36
I
#define I(x, y, z)
Definition: MD5.cxx:116
drawFromPickle.sin
sin
Definition: drawFromPickle.py:36
AthAlgTool
Definition: AthAlgTool.h:26
Trk::SurfaceType::Line
@ Line
Amg::hasPositiveOrZeroDiagElems
bool hasPositiveOrZeroDiagElems(const AmgSymMatrix(N) &mat)
Returns true if all diagonal elements of the covariance matrix are finite aka sane in the above defin...
Definition: EventPrimitivesCovarianceHelpers.h:73
MuonParameters::beta
@ beta
Definition: MuonParamDefs.h:144
Trk::STEP_Propagator::Cache::m_matupd_lastmom
double m_matupd_lastmom
Definition: STEP_Propagator.h:369
TrackSurfaceIntersection.h
Trk::Surface
Definition: Tracking/TrkDetDescr/TrkSurfaces/TrkSurfaces/Surface.h:75
Trk::STEP_Propagator::~STEP_Propagator
virtual ~STEP_Propagator()
Trk::STEP_Propagator::Cache::m_timeOfFlight
double m_timeOfFlight
Definition: STEP_Propagator.h:381
Trk::IdentifiedMaterial
std::pair< const Material *, int > IdentifiedMaterial
Definition: BinnedMaterial.h:28
Trk::TrackingVolume
Definition: TrackingVolume.h:121
Trk::Surface::transform
const Amg::Transform3D & transform() const
Returns HepGeom::Transform3D by reference.
AlignableTrackingVolume.h
Trk::ExtrapolationCache
Definition: ExtrapolationCache.h:26
Trk::STEP_Propagator::initialize
virtual StatusCode initialize() override final
AlgTool initialize method.
Definition: STEP_Propagator.cxx:1229
Trk::Surface::type
constexpr virtual SurfaceType type() const =0
Returns the Surface type to avoid dynamic casts.
Trk::STEP_Propagator::Cache::m_maxPath
double m_maxPath
Definition: STEP_Propagator.h:389
TrackStateOnSurface.h
TSU::T
unsigned long long T
Definition: L1TopoDataTypes.h:35
fitman.k
k
Definition: fitman.py:528
Trk::STEP_Propagator::finalize
virtual StatusCode finalize() override final
AlgTool finalize method.
Definition: STEP_Propagator.cxx:1256
Trk::ParametersBase::clone
virtual ParametersBase< DIM, T > * clone() const override=0
clone method for polymorphic deep copy
Trk::CylinderBounds::halflengthZ
double halflengthZ() const
This method returns the halflengthZ.
Trk::EnergyLoss::sigmaIoni
double sigmaIoni() const
Trk::CylinderBounds::r
virtual double r() const override final
This method returns the radius.
Trk::AlignableTrackingVolume
Definition: AlignableTrackingVolume.h:36
Trk::STEP_Propagator::propagate
virtual std::unique_ptr< Trk::NeutralParameters > propagate(const Trk::NeutralParameters &, const Trk::Surface &, Trk::PropDirection, const Trk::BoundaryCheck &, bool rC=false) const override final
Main propagation method NeutralParameters.
Definition: STEP_Propagator.cxx:1261