Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TrackFitUtils.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file TrackFitUtils.cc
1 #include "TrackFitUtils.h"
2 
3 #include <trackbase/MvtxDefs.h>
4 #include "ActsGeometry.h"
5 #include "TpcDefs.h"
7 #include "TrkrClusterv5.h"
8 #include "TrkrDefs.h" // for cluskey, getTrkrId, tpcId
9 
10 #include <cmath>
11 
12 namespace
13 {
14 
16  template <class T>
17  inline constexpr T square(const T& x)
18  {
19  return x * x;
20  }
21 } // namespace
22 
23 std::pair<Acts::Vector3, Acts::Vector3> TrackFitUtils::get_helix_tangent(const std::vector<float>& fitpars, Acts::Vector3& global)
24 {
25  // no analytic solution for the coordinates of the closest approach of a helix to a point
26  // Instead, we get the PCA in x and y to the circle, and the PCA in z to the z vs R line at the R of the PCA
27 
28  float radius = fitpars[0];
29  float x0 = fitpars[1];
30  float y0 = fitpars[2];
31  float zslope = fitpars[3];
32  float z0 = fitpars[4];
33 
34  Acts::Vector2 pca_circle = TrackFitUtils::get_circle_point_pca(radius, x0, y0, global);
35 
36  // The radius of the PCA determines the z position:
37  float pca_circle_radius = pca_circle.norm(); // radius of the PCA of the circle to the point
38  float pca_z = pca_circle_radius * zslope + z0;
39  Acts::Vector3 pca(pca_circle(0), pca_circle(1), pca_z);
40 
41  // now we want a second point on the helix so we can get a local straight line approximation to the track
42  // Get the angle of the PCA relative to the fitted circle center
43  float angle_pca = atan2(pca_circle(1) - y0, pca_circle(0) - x0);
44  // calculate coords of a point at a slightly larger angle
45  float d_angle = 0.005;
46  float newx = radius * cos(angle_pca + d_angle) + x0;
47  float newy = radius * sin(angle_pca + d_angle) + y0;
48  float newz = sqrt(newx * newx + newy * newy) * zslope + z0;
49  Acts::Vector3 second_point_pca(newx, newy, newz);
50 
51  // pca and second_point_pca define a straight line approximation to the track
52  Acts::Vector3 tangent = (second_point_pca - pca) / (second_point_pca - pca).norm();
53 
54  // get the PCA of the cluster to that line
55  // Approximate track with a straight line consisting of the state position posref and the vector (px,py,pz)
56 
57  // The position of the closest point on the line to global is:
58  // posref + projection of difference between the point and posref on the tangent vector
59  Acts::Vector3 final_pca = pca + ((global - pca).dot(tangent)) * tangent;
60 
61  auto line = std::make_pair(final_pca, tangent);
62 
63  return line;
64 }
65 
66 //_________________________________________________________________________________
68 {
69  // Compute x- and y- sample means
70  double meanX = 0;
71  double meanY = 0;
72  double weight = 0;
73 
74  for (const auto& [x, y] : positions)
75  {
76  meanX += x;
77  meanY += y;
78  ++weight;
79  }
80  meanX /= weight;
81  meanY /= weight;
82 
83  // computing moments
84 
85  double Mxx = 0;
86  double Myy = 0;
87  double Mxy = 0;
88  double Mxz = 0;
89  double Myz = 0;
90  double Mzz = 0;
91 
92  for (auto& [x, y] : positions)
93  {
94  double Xi = x - meanX; // centered x-coordinates
95  double Yi = y - meanY; // centered y-coordinates
96  double Zi = square(Xi) + square(Yi);
97 
98  Mxy += Xi * Yi;
99  Mxx += Xi * Xi;
100  Myy += Yi * Yi;
101  Mxz += Xi * Zi;
102  Myz += Yi * Zi;
103  Mzz += Zi * Zi;
104  }
105  Mxx /= weight;
106  Myy /= weight;
107  Mxy /= weight;
108  Mxz /= weight;
109  Myz /= weight;
110  Mzz /= weight;
111 
112  // computing coefficients of the characteristic polynomial
113 
114  const double Mz = Mxx + Myy;
115  const double Cov_xy = Mxx * Myy - Mxy * Mxy;
116  const double Var_z = Mzz - Mz * Mz;
117  const double A3 = 4 * Mz;
118  const double A2 = -3 * Mz * Mz - Mzz;
119  const double A1 = Var_z * Mz + 4 * Cov_xy * Mz - Mxz * Mxz - Myz * Myz;
120  const double A0 = Mxz * (Mxz * Myy - Myz * Mxy) + Myz * (Myz * Mxx - Mxz * Mxy) - Var_z * Cov_xy;
121  const double A22 = A2 + A2;
122  const double A33 = A3 + A3 + A3;
123 
124  // finding the root of the characteristic polynomial
125  // using Newton's method starting at x=0
126  // (it is guaranteed to converge to the right root)
127  static constexpr int iter_max = 99;
128  double x = 0;
129  double y = A0;
130 
131  // usually, 4-6 iterations are enough
132  for (int iter = 0; iter < iter_max; ++iter)
133  {
134  const double Dy = A1 + x * (A22 + A33 * x);
135  const double xnew = x - y / Dy;
136  if ((xnew == x) || (!std::isfinite(xnew))) break;
137 
138  const double ynew = A0 + xnew * (A1 + xnew * (A2 + xnew * A3));
139  if (std::abs(ynew) >= std::abs(y)) break;
140 
141  x = xnew;
142  y = ynew;
143  }
144 
145  // computing parameters of the fitting circle
146  const double DET = square(x) - x * Mz + Cov_xy;
147  const double Xcenter = (Mxz * (Myy - x) - Myz * Mxy) / DET / 2;
148  const double Ycenter = (Myz * (Mxx - x) - Mxz * Mxy) / DET / 2;
149 
150  // assembling the output
151  double X0 = Xcenter + meanX;
152  double Y0 = Ycenter + meanY;
153  double R = std::sqrt(square(Xcenter) + square(Ycenter) + Mz);
154  return std::make_tuple(R, X0, Y0);
155 }
156 
157 //_________________________________________________________________________________
159 {
160  position_vector_t positions_2d;
161  for (const auto& position : positions)
162  {
163  positions_2d.emplace_back(position.x(), position.y());
164  }
165 
166  return circle_fit_by_taubin(positions_2d);
167 }
168 
169 //_________________________________________________________________________________
171 {
172  double xsum = 0;
173  double x2sum = 0;
174  double ysum = 0;
175  double xysum = 0;
176  double y2sum = 0;
177  for (const auto& [r, z] : positions)
178  {
179  xsum = xsum + r; // calculate sigma(xi)
180  ysum = ysum + z; // calculate sigma(yi)
181  x2sum = x2sum + square(r); // calculate sigma(x^2i)
182  y2sum = y2sum + square(z); // calculate sigma(y^2i)
183  xysum = xysum + r * z; // calculate sigma(xi*yi)
184  }
185 
186  const auto npts = positions.size();
187  const double denominator = (x2sum * npts - square(xsum));
188  const double a = (xysum * npts - xsum * ysum) / denominator; // calculate slope
189  const double b = (x2sum * ysum - xsum * xysum) / denominator; // calculate intercept
190 
196  const double denominatoryx = (y2sum * npts - square(ysum));
197  const double inva = (xysum * npts - xsum * ysum) / denominatoryx;
198  const double invb = (y2sum * xsum - ysum * xysum) / denominatoryx;
199 
201  float sumresid = 0;
202  float suminvresid = 0;
203  for (const auto& [r, z] : positions)
204  {
205  sumresid += square(z - (a * r + b));
206  suminvresid += square(r - (inva * z + invb));
207  }
208 
209  if (sumresid < suminvresid)
210  {
211  return std::make_tuple(a, b);
212  }
213 
215  return std::make_tuple(1. / inva, -invb / inva);
216 }
217 
218 //_________________________________________________________________________________
220 {
221  position_vector_t positions_2d;
222  for (const auto& position : positions)
223  {
224  positions_2d.emplace_back(std::sqrt(square(position.x()) + square(position.y())), position.z());
225  }
226 
227  return line_fit(positions_2d);
228 }
229 
230 //_________________________________________________________________________________
232 {
233  const double D = square(r1) - square(r2) + square(x2) + square(y2);
234  const double a = 1.0 + square(x2 / y2);
235  const double b = -D * x2 / square(y2);
236  const double c = square(D / (2. * y2)) - square(r1);
237  const double delta = square(b) - 4. * a * c;
238 
239  const double sqdelta = std::sqrt(delta);
240 
241  const double xplus = (-b + sqdelta) / (2. * a);
242  const double xminus = (-b - sqdelta) / (2. * a);
243 
244  const double yplus = -(2 * x2 * xplus - D) / (2. * y2);
245  const double yminus = -(2 * x2 * xminus - D) / (2. * y2);
246 
247  return std::make_tuple(xplus, yplus, xminus, yminus);
248 }
249 
251  const bool& isXY,
252  const double& dca_cut,
254  TrkrClusterContainer* clusterContainer,
255  std::vector<Acts::Vector3>& global_vec,
256  std::vector<TrkrDefs::cluskey>& cluskey_vec,
257  const unsigned int& startLayer,
258  const unsigned int& endLayer)
259 {
260  float slope = std::get<0>(fitpars);
261  float intercept = std::get<1>(fitpars);
262 
263  int nclusters = 0;
264  std::set<TrkrDefs::cluskey> keys_to_add;
265  std::set<TrkrDefs::TrkrId> detectors = {TrkrDefs::TrkrId::mvtxId,
269 
270  if (startLayer > 2)
271  {
272  detectors.erase(TrkrDefs::TrkrId::mvtxId);
273  if (startLayer > 6)
274  {
275  detectors.erase(TrkrDefs::TrkrId::inttId);
276  if (startLayer > 54)
277  {
278  detectors.erase(TrkrDefs::TrkrId::tpcId);
279  }
280  }
281  }
282  if (endLayer < 56)
283  {
284  detectors.erase(TrkrDefs::TrkrId::micromegasId);
285  if (endLayer < 7)
286  {
287  detectors.erase(TrkrDefs::TrkrId::tpcId);
288  if (endLayer < 3)
289  {
290  detectors.erase(TrkrDefs::TrkrId::inttId);
291  }
292  }
293  }
294  for (const auto& det : detectors)
295  {
296  for (const auto& hitsetkey : clusterContainer->getHitSetKeys(det))
297  {
298  if (TrkrDefs::getLayer(hitsetkey) < startLayer ||
299  TrkrDefs::getLayer(hitsetkey) > endLayer)
300  {
301  continue;
302  }
303 
304  auto range = clusterContainer->getClusters(hitsetkey);
305  for (auto clusIter = range.first; clusIter != range.second; ++clusIter)
306  {
307  TrkrDefs::cluskey cluskey = clusIter->first;
308 
309  TrkrCluster* cluster = clusIter->second;
310 
311  auto global = tGeometry->getGlobalPosition(cluskey, cluster);
312  float x, y;
313  if (isXY)
314  {
315  x = global.x();
316  y = global.y();
317  }
318  else
319  {
321  x = global.z();
322  y = std::sqrt(square(global.x()) + square(global.y()));
323  if (global.y() < 0) y *= -1;
324  }
325 
330  float perpSlope = -1 / slope;
331  float perpIntercept = y - perpSlope * x;
332 
334  float pcax = (perpIntercept - intercept) / (slope - perpSlope);
335  float pcay = slope * pcax + intercept;
337 
338  float dcax = pcax - x;
339  float dcay = pcay - y;
340  float dca = std::sqrt(square(dcax) + square(dcay));
341 
342  if (dca < dca_cut)
343  {
344  keys_to_add.insert(cluskey);
345  }
346  }
347  }
348  }
349 
350  for (auto& key : keys_to_add)
351  {
352  cluskey_vec.push_back(key);
353  auto clus = clusterContainer->findCluster(key);
354  auto global = tGeometry->getGlobalPosition(key, clus);
355  global_vec.push_back(global);
356  nclusters++;
357  }
358 
359  return nclusters;
360 }
361 
362 //_________________________________________________________________________________
363 unsigned int TrackFitUtils::addClusters(std::vector<float>& fitpars,
364  double dca_cut,
365  ActsGeometry* _tGeometry,
366  TrkrClusterContainer* _cluster_map,
367  std::vector<Acts::Vector3>& global_vec,
368  std::vector<TrkrDefs::cluskey>& cluskey_vec,
369  unsigned int startLayer,
370  unsigned int endLayer)
371 {
372  // project the fit of the TPC clusters to each silicon layer, and find the nearest silicon cluster
373  // iterate over the cluster map and find silicon clusters that match this track fit
374 
375  unsigned int nclusters = 0;
376 
377  // We want the best match in each layer
378  std::vector<float> best_layer_dca;
379  best_layer_dca.assign(endLayer + 1, 999.0);
380  std::vector<TrkrDefs::cluskey> best_layer_cluskey;
381  best_layer_cluskey.assign(endLayer + 1, 0);
382  std::set<TrkrDefs::TrkrId> detectors = {TrkrDefs::TrkrId::mvtxId,
386 
387  if (startLayer > 2)
388  {
389  detectors.erase(TrkrDefs::TrkrId::mvtxId);
390  if (startLayer > 6)
391  {
392  detectors.erase(TrkrDefs::TrkrId::inttId);
393  if (startLayer > 54)
394  {
395  detectors.erase(TrkrDefs::TrkrId::tpcId);
396  }
397  }
398  }
399  if (endLayer < 56)
400  {
401  detectors.erase(TrkrDefs::TrkrId::micromegasId);
402  if (endLayer < 7)
403  {
404  detectors.erase(TrkrDefs::TrkrId::tpcId);
405  if (endLayer < 3)
406  {
407  detectors.erase(TrkrDefs::TrkrId::inttId);
408  }
409  }
410  }
411 
412  for (const auto& det : detectors)
413  {
414  for (const auto& hitsetkey : _cluster_map->getHitSetKeys(det))
415  {
416  if (TrkrDefs::getLayer(hitsetkey) < startLayer ||
417  TrkrDefs::getLayer(hitsetkey) > endLayer)
418  {
419  continue;
420  }
421 
422  auto range = _cluster_map->getClusters(hitsetkey);
423  for (auto clusIter = range.first; clusIter != range.second; ++clusIter)
424  {
425  TrkrDefs::cluskey cluskey = clusIter->first;
426  unsigned int layer = TrkrDefs::getLayer(cluskey);
427 
428  TrkrCluster* cluster = clusIter->second;
429 
430  auto global = _tGeometry->getGlobalPosition(cluskey, cluster);
431 
432  Acts::Vector3 pca = get_helix_pca(fitpars, global);
433  float dca = (pca - global).norm();
434 
435  Acts::Vector2 global_xy(global(0), global(1));
436  Acts::Vector2 pca_xy(pca(0), pca(1));
437  Acts::Vector2 pca_xy_residual = pca_xy - global_xy;
438  dca = pca_xy_residual.norm();
439 
440  if (dca < best_layer_dca[layer])
441  {
442  best_layer_dca[layer] = dca;
443  best_layer_cluskey[layer] = cluskey;
444  }
445  } // end cluster iteration
446  } // end hitsetkey iteration
447  }
448  for (unsigned int layer = startLayer; layer <= endLayer; ++layer)
449  {
451  if (best_layer_cluskey[layer] == 0)
452  {
453  continue;
454  }
455  if (best_layer_dca[layer] < dca_cut)
456  {
457  cluskey_vec.push_back(best_layer_cluskey[layer]);
458  auto clus = _cluster_map->findCluster(best_layer_cluskey[layer]);
459  auto global = _tGeometry->getGlobalPosition(best_layer_cluskey[layer], clus);
460  global_vec.push_back(global);
461  nclusters++;
462  }
463  }
464 
465  return nclusters;
466 }
467 
468 //_________________________________________________________________________________
469 Acts::Vector3 TrackFitUtils::get_helix_pca(std::vector<float>& fitpars,
470  Acts::Vector3 global)
471 {
472  // no analytic solution for the coordinates of the closest approach of a helix to a point
473  // Instead, we get the PCA in x and y to the circle, and the PCA in z to the z vs R line at the R of the PCA
474 
475  float radius = fitpars[0];
476  float x0 = fitpars[1];
477  float y0 = fitpars[2];
478  float zslope = fitpars[3];
479  float z0 = fitpars[4];
480 
481  Acts::Vector2 pca_circle = get_circle_point_pca(radius, x0, y0, global);
482 
483  // The radius of the PCA determines the z position:
484  float pca_circle_radius = pca_circle.norm();
485  float pca_z = pca_circle_radius * zslope + z0;
486  Acts::Vector3 pca(pca_circle(0), pca_circle(1), pca_z);
487 
488  // now we want a second point on the helix so we can get a local straight line approximation to the track
489  // project the circle PCA vector an additional small amount and find the helix PCA to that point
490  float projection = 0.25; // cm
491  Acts::Vector3 second_point = pca + projection * pca / pca.norm();
492  Acts::Vector2 second_point_pca_circle = get_circle_point_pca(radius, x0, y0, second_point);
493  float second_point_pca_z = pca_circle_radius * zslope + z0;
494  Acts::Vector3 second_point_pca(second_point_pca_circle(0), second_point_pca_circle(1), second_point_pca_z);
495 
496  // pca and second_point_pca define a straight line approximation to the track
497  Acts::Vector3 tangent = (second_point_pca - pca) / (second_point_pca - pca).norm();
498 
499  // get the PCA of the cluster to that line
500  Acts::Vector3 final_pca = getPCALinePoint(global, tangent, pca);
501 
502  return final_pca;
503 }
504 
505 //_________________________________________________________________________________
507 {
508  // get the PCA of a cluster (x,y) position to a circle
509  // draw a line from the origin of the circle to the point
510  // the intersection of the line with the circle is at the distance radius from the origin along that line
511 
512  Acts::Vector2 origin(x0, y0);
513  Acts::Vector2 point(global(0), global(1));
514 
515  Acts::Vector2 pca = origin + radius * (point - origin) / (point - origin).norm();
516 
517  return pca;
518 }
519 
520 //_________________________________________________________________________________
521 std::vector<float> TrackFitUtils::fitClusters(std::vector<Acts::Vector3>& global_vec,
522  std::vector<TrkrDefs::cluskey> cluskey_vec)
523 {
524  std::vector<float> fitpars;
525 
526  // make the helical fit using TrackFitUtils
527  if (global_vec.size() < 3)
528  {
529  return fitpars;
530  }
531  std::tuple<double, double, double> circle_fit_pars = TrackFitUtils::circle_fit_by_taubin(global_vec);
532 
533  // It is problematic that the large errors on the INTT strip z values are not allowed for - drop the INTT from the z line fit
534  std::vector<Acts::Vector3> global_vec_noINTT;
535  for (unsigned int ivec = 0; ivec < global_vec.size(); ++ivec)
536  {
537  unsigned int trkrid = TrkrDefs::getTrkrId(cluskey_vec[ivec]);
538 
539  if (trkrid != TrkrDefs::inttId and cluskey_vec[ivec] != 0)
540  {
541  global_vec_noINTT.push_back(global_vec[ivec]);
542  }
543  }
544 
545  if (global_vec_noINTT.size() < 3)
546  {
547  return fitpars;
548  }
549  std::tuple<double, double> line_fit_pars = TrackFitUtils::line_fit(global_vec_noINTT);
550 
551  fitpars.push_back(std::get<0>(circle_fit_pars));
552  fitpars.push_back(std::get<1>(circle_fit_pars));
553  fitpars.push_back(std::get<2>(circle_fit_pars));
554  fitpars.push_back(std::get<0>(line_fit_pars));
555  fitpars.push_back(std::get<1>(line_fit_pars));
556 
557  return fitpars;
558 }
559 
560 //_________________________________________________________________________________
562  TrkrClusterContainer* _cluster_map,
563  std::vector<Acts::Vector3>& global_vec,
564  std::vector<TrkrDefs::cluskey>& cluskey_vec)
565 {
566  for (unsigned int iclus = 0; iclus < cluskey_vec.size(); ++iclus)
567  {
568  auto key = cluskey_vec[iclus];
569  auto cluster = _cluster_map->findCluster(key);
570  if (!cluster)
571  {
572  std::cout << "Failed to get cluster with key " << key << std::endl;
573  continue;
574  }
575 
576  Acts::Vector3 global = _tGeometry->getGlobalPosition(key, cluster);
577 
578  /*
579  const unsigned int trkrId = TrkrDefs::getTrkrId(key);
580  // have to add corrections for TPC clusters after transformation to global
581  if(trkrId == TrkrDefs::tpcId)
582  {
583  int crossing = 0; // for now
584  makeTpcGlobalCorrections(key, crossing, global);
585  }
586  */
587 
588  // add the global positions to a vector to return
589  global_vec.push_back(global);
590 
591  } // end loop over clusters for this track
592 }
593 
594 //_________________________________________________________________________________
596 {
597  // Approximate track with a straight line consisting of the state position posref and the vector (px,py,pz)
598 
599  // The position of the closest point on the line to global is:
600  // posref + projection of difference between the point and posref on the tangent vector
601  Acts::Vector3 pca = posref + ((global - posref).dot(tangent)) * tangent;
602  /*
603  if( (pca-posref).norm() > 0.001)
604  {
605  std::cout << " getPCALinePoint: old pca " << posref(0) << " " << posref(1) << " " << posref(2) << std::endl;
606  std::cout << " getPCALinePoint: new pca " << pca(0) << " " << pca(1) << " " << pca(2) << std::endl;
607  std::cout << " getPCALinePoint: delta pca " << pca(0) - posref(0) << " " << pca(1)-posref(1) << " " << pca(2) -posref(2) << std::endl;
608  }
609  */
610 
611  return pca;
612 }
613 
615 {
616  std::vector<double> residuals;
617  // calculate cluster residuals from the fitted circle
618  std::transform(rz_pts.begin(), rz_pts.end(),
619  std::back_inserter(residuals), [slope, intercept](const std::pair<double, double>& point)
620  {
621  double r = point.first;
622  double z = point.second;
623 
624  // The shortest distance of a point from a circle is along the radial; line from the circle center to the point
625 
626  double a = -slope;
627  double b = 1.0;
628  double c = -intercept;
629  return std::abs(a*r+b*z+c)/sqrt(square(a)+square(b)); });
630  return residuals;
631 }
632 
633 std::vector<double> TrackFitUtils::getCircleClusterResiduals(TrackFitUtils::position_vector_t& xy_pts, float R, float X0, float Y0)
634 {
635  std::vector<double> residuals;
636  std::transform(xy_pts.begin(), xy_pts.end(),
637  std::back_inserter(residuals), [R, X0, Y0](const std::pair<double, double>& point)
638  {
639  double x = point.first;
640  double y = point.second;
641 
642  // The shortest distance of a point from a circle is along the radial; line from the circle center to the point
643  return std::sqrt( square(x-X0) + square(y-Y0) ) - R; });
644 
645  return residuals;
646 }