Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RiddersPropagator.ipp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file RiddersPropagator.ipp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2017-2023 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
11 template <typename propagator_t>
12 template <typename parameters_t, typename propagator_options_t>
14  const parameters_t& start, const propagator_options_t& options) const
17  typename propagator_options_t::action_list_type>> {
18  using ThisResult = Result<
20  typename propagator_options_t::action_list_type>>;
21 
22  // Remove the covariance from our start parameters in order to skip jacobian
23  // transport for the nominal propagation
24  CurvilinearTrackParameters startWithoutCov = start;
25  startWithoutCov.covariance() = std::nullopt;
26 
27  // Propagate the nominal parameters
28  auto result = m_propagator.propagate(startWithoutCov, options);
29  if (not result.ok()) {
30  return ThisResult::failure(result.error());
31  }
32  // Extract results from the nominal propagation
33  auto nominalResult = result.value();
34  assert(nominalResult.endParameters);
35  const auto& nominalFinalParameters = *nominalResult.endParameters;
36 
37  Jacobian jacobian = wiggleAndCalculateJacobian(options, start, nominalResult);
38  nominalResult.transportJacobian = jacobian;
39 
40  if (start.covariance()) {
41  // use nominal parameters and Ridders covariance
42  auto cov = jacobian * (*start.covariance()) * jacobian.transpose();
43  // replace the covariance of the nominal result w/ the ridders covariance
44  nominalResult.endParameters = CurvilinearTrackParameters(
45  nominalFinalParameters.fourPosition(options.geoContext),
46  nominalFinalParameters.direction(), nominalFinalParameters.qOverP(),
47  std::move(cov), nominalFinalParameters.particleHypothesis());
48  }
49 
50  return ThisResult::success(std::move(nominalResult));
51 }
52 
53 template <typename propagator_t>
54 template <typename parameters_t, typename propagator_options_t>
56  const parameters_t& start, const Surface& target,
57  const propagator_options_t& options) const
60  typename propagator_options_t::action_list_type>> {
61  using ThisResult = Result<action_list_t_result_t<
62  BoundTrackParameters, typename propagator_options_t::action_list_type>>;
63 
64  // Remove the covariance from our start parameters in order to skip jacobian
65  // transport for the nominal propagation
66  BoundTrackParameters startWithoutCov = start;
67  startWithoutCov.covariance() = std::nullopt;
68 
69  // Propagate the nominal parameters
70  auto result = m_propagator.propagate(startWithoutCov, target, options);
71  if (not result.ok()) {
72  return ThisResult::failure(result.error());
73  }
74  // Extract results from the nominal propagation
75  auto nominalResult = result.value();
76  assert(nominalResult.endParameters);
77  const auto& nominalFinalParameters = *nominalResult.endParameters;
78 
79  Jacobian jacobian = wiggleAndCalculateJacobian(options, start, nominalResult);
80  nominalResult.transportJacobian = jacobian;
81 
82  if (start.covariance()) {
83  // use nominal parameters and Ridders covariance
84  auto cov = jacobian * (*start.covariance()) * jacobian.transpose();
85  // replace the covariance of the nominal result w/ the ridders covariance
86  nominalResult.endParameters = BoundTrackParameters(
87  nominalFinalParameters.referenceSurface().getSharedPtr(),
88  nominalFinalParameters.parameters(), std::move(cov),
89  nominalFinalParameters.particleHypothesis());
90  }
91 
92  return ThisResult::success(std::move(nominalResult));
93 }
94 
95 template <typename propagator_t>
96 template <typename propagator_options_t, typename parameters_t,
97  typename result_t>
99  const propagator_options_t& options, const parameters_t& start,
100  result_t& nominalResult) const -> Jacobian {
101  auto nominalFinalParameters = *nominalResult.endParameters;
102  // Use the curvilinear surface of the propagated parameters as target
103  const Surface& target = nominalFinalParameters.referenceSurface();
104 
105  // TODO add to propagator options
106  // Steps for estimating derivatives
107  const std::vector<double>* deviations = &m_config.deviations;
108  if (target.type() == Surface::Disc) {
109  deviations = &m_config.deviationsDisc;
110  }
111 
112  // - for planar surfaces the dest surface is a perfect destination
113  // surface for the numerical propagation, as reference frame
114  // aligns with the referenceSurface.transform().rotation() at
115  // at any given time
116  //
117  // - for straw & cylinder, where the error is given
118  // in the reference frame that re-aligns with a slightly different
119  // intersection solution
120 
121  // Allow larger distances for the oscillation
122  propagator_options_t opts = options;
123  opts.pathLimit *= 2.;
124 
125  // Derivations of each parameter around the nominal parameters
126  std::array<std::vector<BoundVector>, eBoundSize> derivatives;
127 
128  // Wiggle each dimension individually
129  for (unsigned int i = 0; i < eBoundSize; i++) {
130  derivatives[i] =
131  wiggleParameter(opts, start, i, target,
132  nominalFinalParameters.parameters(), *deviations);
133  }
134 
135  // Test if target is disc - this may lead to inconsistent results
136  if (target.type() == Surface::Disc) {
137  for ([[maybe_unused]] const auto& d : derivatives) {
138  assert(!inconsistentDerivativesOnDisc(d));
139  }
140  }
141 
142  return calculateJacobian(*deviations, derivatives);
143 }
144 
145 template <typename propagator_t>
147  const std::vector<Acts::BoundVector>& derivatives) {
148  // Test each component with each other
149  for (unsigned int i = 0; i < derivatives.size(); i++) {
150  bool jumpedAngle = true;
151  for (unsigned int j = 0; j < derivatives.size(); j++) {
152  // If there is at least one with a similar angle then it seems to work
153  // properly
154  if (i != j &&
155  std::abs(derivatives[i](1) - derivatives[j](1)) < 0.5 * M_PI) {
156  jumpedAngle = false;
157  break;
158  }
159  }
160  // Break if a jump was detected
161  if (jumpedAngle) {
162  return true;
163  }
164  }
165  return false;
166 }
167 
168 template <typename propagator_t>
169 template <typename propagator_options_t, typename parameters_t>
170 std::vector<Acts::BoundVector>
172  const propagator_options_t& options, const parameters_t& start,
173  const unsigned int param, const Surface& target,
174  const Acts::BoundVector& nominal,
175  const std::vector<double>& deviations) const {
176  // Storage of the results
177  std::vector<BoundVector> derivatives;
178  derivatives.reserve(deviations.size());
179  for (double h : deviations) {
180  // Treatment for theta
181  if (param == eBoundTheta) {
182  const double current_theta = start.template get<eBoundTheta>();
183  if (current_theta + h > M_PI) {
184  h = M_PI - current_theta;
185  }
186  if (current_theta + h < 0) {
187  h = -current_theta;
188  }
189  }
190 
191  // Modify start parameter
192  BoundVector values = start.parameters();
193  values[param] += h;
194 
195  // Propagate with updated start parameters
196  BoundTrackParameters tp(start.referenceSurface().getSharedPtr(), values,
197  start.covariance(), start.particleHypothesis());
198  const auto& r = m_propagator.propagate(tp, target, options).value();
199  // Collect the slope
200  derivatives.push_back((r.endParameters->parameters() - nominal) / h);
201 
202  // Correct for a possible variation of phi around
203  if (param == eBoundPhi) {
204  double phi0 = nominal(Acts::eBoundPhi);
205  double phi1 = r.endParameters->parameters()(Acts::eBoundPhi);
206  if (std::abs(phi1 + 2. * M_PI - phi0) < std::abs(phi1 - phi0)) {
207  derivatives.back()[Acts::eBoundPhi] = (phi1 + 2. * M_PI - phi0) / h;
208  } else if (std::abs(phi1 - 2. * M_PI - phi0) < std::abs(phi1 - phi0)) {
209  derivatives.back()[Acts::eBoundPhi] = (phi1 - 2. * M_PI - phi0) / h;
210  }
211  }
212  }
213 
214  return derivatives;
215 }
216 
217 template <typename propagator_t>
219  const std::vector<double>& deviations,
220  const std::array<std::vector<Acts::BoundVector>, Acts::eBoundSize>&
221  derivatives) -> Jacobian {
222  Jacobian jacobian;
223  jacobian.col(eBoundLoc0) = fitLinear(deviations, derivatives[eBoundLoc0]);
224  jacobian.col(eBoundLoc1) = fitLinear(deviations, derivatives[eBoundLoc1]);
225  jacobian.col(eBoundPhi) = fitLinear(deviations, derivatives[eBoundPhi]);
226  jacobian.col(eBoundTheta) = fitLinear(deviations, derivatives[eBoundTheta]);
227  jacobian.col(eBoundQOverP) = fitLinear(deviations, derivatives[eBoundQOverP]);
228  jacobian.col(eBoundTime) = fitLinear(deviations, derivatives[eBoundTime]);
229  return jacobian;
230 }
231 
232 template <typename propagator_t>
234  const std::vector<double>& deviations,
235  const std::vector<Acts::BoundVector>& derivatives) {
236  assert(!derivatives.empty() && "no fit without data");
237  if (derivatives.size() == 1) {
238  return derivatives[0];
239  }
240 
241  BoundVector A = BoundVector::Zero();
242  BoundVector C = BoundVector::Zero();
243  double B = 0;
244  double D = 0;
245  const unsigned int N = deviations.size();
246 
247  for (unsigned int i = 0; i < N; ++i) {
248  A += deviations.at(i) * derivatives.at(i);
249  B += deviations.at(i);
250  C += derivatives.at(i);
251  D += deviations.at(i) * deviations.at(i);
252  }
253 
254  BoundVector b = (N * A - B * C) / (N * D - B * B);
255  BoundVector a = (C - B * b) / N;
256 
257  return a;
258 }