Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EstimateTrackParamsFromSeed.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file EstimateTrackParamsFromSeed.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
14 #include "Acts/Seeding/Seed.hpp"
17 
18 #include <array>
19 #include <cmath>
20 #include <iostream>
21 #include <iterator>
22 #include <optional>
23 #include <vector>
24 
25 namespace Acts {
30 
51 template <typename spacepoint_iterator_t>
52 std::optional<BoundVector> estimateTrackParamsFromSeed(
53  spacepoint_iterator_t spBegin, spacepoint_iterator_t spEnd,
54  const Logger& logger = getDummyLogger()) {
55  // Check the number of provided space points
56  size_t numSP = std::distance(spBegin, spEnd);
57  if (numSP < 3) {
58  ACTS_ERROR("At least three space points are required.")
59  return std::nullopt;
60  }
61 
62  ActsScalar x2m = 0., xm = 0.;
63  ActsScalar xym = 0.;
64  ActsScalar y2m = 0., ym = 0.;
65  ActsScalar r2m = 0., r4m = 0.;
66  ActsScalar xr2m = 0., yr2m = 0.;
67 
68  for (spacepoint_iterator_t it = spBegin; it != spEnd; it++) {
69  if (*it == nullptr) {
70  ACTS_ERROR("Empty space point found. This should not happen.")
71  return std::nullopt;
72  }
73  const auto& sp = *it;
74 
75  ActsScalar x = sp->x();
76  ActsScalar y = sp->y();
77  ActsScalar r2 = x * x + y * y;
78  x2m += x * x;
79  xm += x;
80  xym += x * y;
81  y2m += y * y;
82  ym += y;
83  r2m += r2;
84  r4m += r2 * r2;
85  xr2m += x * r2;
86  yr2m += y * r2;
87  numSP++;
88  }
89  x2m = x2m / numSP;
90  xm = xm / numSP;
91  xym = xym / numSP;
92  y2m = y2m / numSP;
93  ym = ym / numSP;
94  r2m = r2m / numSP;
95  r4m = r4m / numSP;
96  xr2m = xr2m / numSP;
97  yr2m = yr2m / numSP;
98 
99  ActsScalar Cxx = x2m - xm * xm;
100  ActsScalar Cxy = xym - xm * ym;
101  ActsScalar Cyy = y2m - ym * ym;
102  ActsScalar Cxr2 = xr2m - xm * r2m;
103  ActsScalar Cyr2 = yr2m - ym * r2m;
104  ActsScalar Cr2r2 = r4m - r2m * r2m;
105 
106  ActsScalar q1 = Cr2r2 * Cxy - Cxr2 * Cyr2;
107  ActsScalar q2 = Cr2r2 * (Cxx - Cyy) - Cxr2 * Cxr2 + Cyr2 * Cyr2;
108 
109  ActsScalar phi = 0.5 * std::atan(2 * q1 / q2);
110  ActsScalar k = (std::sin(phi) * Cxr2 - std::cos(phi) * Cyr2) * (1. / Cr2r2);
111  ActsScalar delta = -k * r2m + std::sin(phi) * xm - std::cos(phi) * ym;
112 
113  ActsScalar rho = (2 * k) / (std::sqrt(1 - 4 * delta * k));
114  ActsScalar d0 = (2 * delta) / (1 + std::sqrt(1 - 4 * delta * k));
115 
116  // Initialize the bound parameters vector
117  BoundVector params = BoundVector::Zero();
118  params[eBoundLoc0] = d0;
119  params[eBoundPhi] = phi;
120  params[eBoundQOverP] = rho;
121 
122  return params;
123 }
124 
154 template <typename spacepoint_iterator_t>
155 std::optional<BoundVector> estimateTrackParamsFromSeed(
156  const GeometryContext& gctx, spacepoint_iterator_t spBegin,
157  spacepoint_iterator_t spEnd, const Surface& surface, const Vector3& bField,
158  ActsScalar bFieldMin, const Acts::Logger& logger = getDummyLogger(),
159  ActsScalar mass = 139.57018 * UnitConstants::MeV) {
160  // Check the number of provided space points
161  size_t numSP = std::distance(spBegin, spEnd);
162  if (numSP != 3) {
163  ACTS_ERROR("There should be exactly three space points provided.")
164  return std::nullopt;
165  }
166 
167  // Convert bField to Tesla
168  ActsScalar bFieldInTesla = bField.norm() / UnitConstants::T;
169  ActsScalar bFieldMinInTesla = bFieldMin / UnitConstants::T;
170  // Check if magnetic field is too small
171  if (bFieldInTesla < bFieldMinInTesla) {
172  // @todo shall we use straight-line estimation and use default q/pt in such
173  // case?
174  ACTS_WARNING("The magnetic field at the bottom space point: B = "
175  << bFieldInTesla << " T is smaller than |B|_min = "
176  << bFieldMinInTesla << " T. Estimation is not performed.")
177  return std::nullopt;
178  }
179 
180  // The global positions of the bottom, middle and space points
181  std::array<Vector3, 3> spGlobalPositions = {Vector3::Zero(), Vector3::Zero(),
182  Vector3::Zero()};
183  // The first, second and third space point are assumed to be bottom, middle
184  // and top space point, respectively
185  for (size_t isp = 0; isp < 3; ++isp) {
186  spacepoint_iterator_t it = std::next(spBegin, isp);
187  if (*it == nullptr) {
188  ACTS_ERROR("Empty space point found. This should not happen.")
189  return std::nullopt;
190  }
191  const auto& sp = *it;
192  spGlobalPositions[isp] = Vector3(sp->x(), sp->y(), sp->z());
193  }
194 
195  // Define a new coordinate frame with its origin at the bottom space point, z
196  // axis long the magnetic field direction and y axis perpendicular to vector
197  // from the bottom to middle space point. Hence, the projection of the middle
198  // space point on the transverse plane will be located at the x axis of the
199  // new frame.
200  Vector3 relVec = spGlobalPositions[1] - spGlobalPositions[0];
201  Vector3 newZAxis = bField.normalized();
202  Vector3 newYAxis = newZAxis.cross(relVec).normalized();
203  Vector3 newXAxis = newYAxis.cross(newZAxis);
204  RotationMatrix3 rotation;
205  rotation.col(0) = newXAxis;
206  rotation.col(1) = newYAxis;
207  rotation.col(2) = newZAxis;
208  // The center of the new frame is at the bottom space point
209  Translation3 trans(spGlobalPositions[0]);
210  // The transform which constructs the new frame
211  Transform3 transform(trans * rotation);
212 
213  // The coordinate of the middle and top space point in the new frame
214  Vector3 local1 = transform.inverse() * spGlobalPositions[1];
215  Vector3 local2 = transform.inverse() * spGlobalPositions[2];
216 
217  // Lambda to transform the coordinates to the (u, v) space
218  auto uvTransform = [](const Vector3& local) -> Vector2 {
219  Vector2 uv;
220  ActsScalar denominator = local.x() * local.x() + local.y() * local.y();
221  uv.x() = local.x() / denominator;
222  uv.y() = local.y() / denominator;
223  return uv;
224  };
225  // The uv1.y() should be zero
226  Vector2 uv1 = uvTransform(local1);
227  Vector2 uv2 = uvTransform(local2);
228 
229  // A,B are slope and intercept of the straight line in the u,v plane
230  // connecting the three points
231  ActsScalar A = (uv2.y() - uv1.y()) / (uv2.x() - uv1.x());
232  ActsScalar B = uv2.y() - A * uv2.x();
233  // Curvature (with a sign) estimate
234  ActsScalar rho = -2.0 * B / std::hypot(1., A);
235  // The projection of the top space point on the transverse plane of the new
236  // frame
237  ActsScalar rn = local2.x() * local2.x() + local2.y() * local2.y();
238  // The (1/tanTheta) of momentum in the new frame,
239  static constexpr ActsScalar G = static_cast<ActsScalar>(1. / 24.);
240  ActsScalar invTanTheta =
241  local2.z() * std::sqrt(1. / rn) / (1. + G * rho * rho * rn);
242  // The momentum direction in the new frame (the center of the circle has the
243  // coordinate (-1.*A/(2*B), 1./(2*B)))
244  Vector3 transDirection(1., A, std::hypot(1, A) * invTanTheta);
245  // Transform it back to the original frame
246  Vector3 direction = rotation * transDirection.normalized();
247 
248  // Initialize the bound parameters vector
249  BoundVector params = BoundVector::Zero();
250 
251  // The estimated phi and theta
252  params[eBoundPhi] = VectorHelpers::phi(direction);
253  params[eBoundTheta] = VectorHelpers::theta(direction);
254 
255  // Transform the bottom space point to local coordinates of the provided
256  // surface
257  auto lpResult = surface.globalToLocal(gctx, spGlobalPositions[0], direction);
258  if (not lpResult.ok()) {
259  ACTS_ERROR(
260  "Global to local transformation did not succeed. Please make sure the "
261  "bottom space point lies on the provided surface.");
262  return std::nullopt;
263  }
264  Vector2 bottomLocalPos = lpResult.value();
265  // The estimated loc0 and loc1
266  params[eBoundLoc0] = bottomLocalPos.x();
267  params[eBoundLoc1] = bottomLocalPos.y();
268 
269  // The estimated q/pt in [GeV/c]^-1 (note that the pt is the projection of
270  // momentum on the transverse plane of the new frame)
271  ActsScalar qOverPt = rho * (UnitConstants::m) / (0.3 * bFieldInTesla);
272  // The estimated q/p in [GeV/c]^-1
273  params[eBoundQOverP] = qOverPt / std::hypot(1., invTanTheta);
274 
275  // The estimated momentum, and its projection along the magnetic field
276  // diretion
277  ActsScalar pInGeV = std::abs(1.0 / params[eBoundQOverP]);
278  ActsScalar pzInGeV = 1.0 / std::abs(qOverPt) * invTanTheta;
279  ActsScalar massInGeV = mass / UnitConstants::GeV;
280  // The estimated velocity, and its projection along the magnetic field
281  // diretion
282  ActsScalar v = pInGeV / std::hypot(pInGeV, massInGeV);
283  ActsScalar vz = pzInGeV / std::hypot(pInGeV, massInGeV);
284  // The z coordinate of the bottom space point along the magnetic field
285  // direction
286  ActsScalar pathz = spGlobalPositions[0].dot(bField) / bField.norm();
287  // The estimated time (use path length along magnetic field only if it's not
288  // zero)
289  if (pathz != 0 && vz != 0) {
290  params[eBoundTime] = pathz / vz;
291  } else {
292  params[eBoundTime] = spGlobalPositions[0].norm() / v;
293  }
294 
295  if (params.hasNaN()) {
296  ACTS_ERROR(
297  "The NaN value exists at the estimated track parameters from seed with"
298  << "\nbottom sp: " << spGlobalPositions[0] << "\nmiddle sp: "
299  << spGlobalPositions[1] << "\ntop sp: " << spGlobalPositions[2]);
300  return std::nullopt;
301  }
302  return params;
303 }
304 
305 } // namespace Acts