Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:20:34

0001 #include "TrackFitUtils.h"
0002 
0003 #include "ActsGeometry.h"
0004 #include "ActsSurfaceMaps.h"
0005 #include "MvtxDefs.h"
0006 #include "TrkrClusterContainerv4.h"
0007 #include "TrkrDefs.h"  // for cluskey, getTrkrId, tpcId
0008 
0009 #include <Acts/Definitions/Algebra.hpp>
0010 #include <Acts/Definitions/Units.hpp>
0011 
0012 #include <algorithm>
0013 #include <cmath>
0014 #include <cstdint>
0015 #include <iostream>
0016 #include <iterator>
0017 #include <limits>
0018 #include <ostream>
0019 #include <set>
0020 #include <tuple>
0021 #include <utility>
0022 #include <vector>
0023 
0024 namespace
0025 {
0026 
0027   //! convenience square method
0028   template <class T>
0029   inline constexpr T square(const T& x)
0030   {
0031     return x * x;
0032   }
0033   template <class T>
0034   inline constexpr T r(const T& x, const T& y)
0035   {
0036     return std::sqrt(square(x) + square(y));
0037   }
0038 }  // namespace
0039 
0040 std::pair<Acts::Vector3, Acts::Vector3> TrackFitUtils::get_helix_tangent(const std::vector<float>& fitpars, Acts::Vector3& global)
0041 {
0042   // no analytic solution for the coordinates of the closest approach of a helix to a point
0043   // 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
0044 
0045   float const radius = fitpars[0];
0046   float const x0 = fitpars[1];
0047   float const y0 = fitpars[2];
0048   float const zslope = fitpars[3];
0049   float const z0 = fitpars[4];
0050 
0051   Acts::Vector2 pca_circle = TrackFitUtils::get_circle_point_pca(radius, x0, y0, global);
0052 
0053   // The radius of the PCA determines the z position:
0054   float const pca_circle_radius = pca_circle.norm();  // radius of the PCA of the circle to the point
0055   float const pca_z = pca_circle_radius * zslope + z0;
0056   Acts::Vector3 const pca(pca_circle(0), pca_circle(1), pca_z);
0057 
0058   // now we want a second point on the helix so we can get a local straight line approximation to the track
0059   // Get the angle of the PCA relative to the fitted circle center
0060   float const angle_pca = atan2(pca_circle(1) - y0, pca_circle(0) - x0);
0061   // calculate coords of a point at a slightly larger angle
0062   float const d_angle = 0.005;
0063   float const newx = radius * std::cos(angle_pca + d_angle) + x0;
0064   float const newy = radius * std::sin(angle_pca + d_angle) + y0;
0065   float const newz = std::sqrt(newx * newx + newy * newy) * zslope + z0;
0066   Acts::Vector3 const second_point_pca(newx, newy, newz);
0067 
0068   // pca and second_point_pca define a straight line approximation to the track
0069   Acts::Vector3 tangent = (second_point_pca - pca) / (second_point_pca - pca).norm();
0070 
0071   // Direction is ambiguous, use cluster direction to resolve it
0072   float const phi = atan2(global(1), global(0));
0073   float const tangent_phi = atan2(tangent(1), tangent(0));
0074   if (std::fabs(tangent_phi - phi) > M_PI / 2)
0075   {
0076     tangent = -1.0 * tangent;
0077   }
0078 
0079   // get the PCA of the cluster to that line
0080   // Approximate track with a straight line consisting of the state position posref and the vector (px,py,pz)
0081 
0082   // The position of the closest point on the line to global is:
0083   // posref + projection of difference between the point and posref on the tangent vector
0084   Acts::Vector3 const final_pca = pca + ((global - pca).dot(tangent)) * tangent;
0085 
0086   auto line = std::make_pair(final_pca, tangent);
0087 
0088   return line;
0089 }
0090 Acts::Vector3 TrackFitUtils::surface_3Dline_intersection(const TrkrDefs::cluskey& key,
0091                                                          TrkrCluster* cluster, ActsGeometry* geometry, float& xyslope, float& xyint, float& yzslope, float& yzint)
0092 {
0093   Acts::Vector3 intersection(std::numeric_limits<float>::quiet_NaN(),
0094                              std::numeric_limits<float>::quiet_NaN(),
0095                              std::numeric_limits<float>::quiet_NaN());
0096 
0097   auto surf = geometry->maps().getSurface(key, cluster);
0098 
0099   //! Take two random x points and calculate y and z on the line to find 2
0100   //! 3D points with which to calculate the 3D line
0101   float const x1 = -1;
0102   float const x2 = 5;
0103   float const y1 = xyslope * x1 + xyint;
0104   float const y2 = xyslope * x2 + xyint;
0105 
0106   float const z1 = (y1 - yzint) / yzslope;
0107   float const z2 = (y2 - yzint) / yzslope;
0108 
0109   Acts::Vector3 v1(x1, y1, z1), v2(x2, y2, z2);
0110 
0111   Acts::Vector3 surfcenter = surf->center(geometry->geometry().getGeoContext()) / Acts::UnitConstants::cm;
0112   Acts::Vector3 surfnorm = surf->normal(geometry->geometry().getGeoContext(), Acts::Vector3(1,1,1), Acts::Vector3(1,1,1)) / Acts::UnitConstants::cm;
0113 
0114   Acts::Vector3 u = v2 - v1;
0115   float const dot = surfnorm.dot(u);
0116 
0117   //! If it does not satisfy this, line was parallel to the surface
0118   if (abs(dot) > 1e-6)
0119   {
0120     Acts::Vector3 const w = v1 - surfcenter;
0121     float const fac = -surfnorm.dot(w) / dot;
0122     u *= fac;
0123     intersection = v1 + u;
0124   }
0125 
0126   return intersection;
0127 }
0128 //_________________________________________________________________________________
0129 TrackFitUtils::circle_fit_output_t TrackFitUtils::circle_fit_by_taubin(const TrackFitUtils::position_vector_t& positions)
0130 {
0131   // Compute x- and y- sample means
0132   double meanX = 0;
0133   double meanY = 0;
0134   double weight = 0;
0135 
0136   for (const auto& [x, y] : positions)
0137   {
0138     meanX += x;
0139     meanY += y;
0140     ++weight;
0141   }
0142   meanX /= weight;
0143   meanY /= weight;
0144 
0145   //     computing moments
0146 
0147   double Mxx = 0;
0148   double Myy = 0;
0149   double Mxy = 0;
0150   double Mxz = 0;
0151   double Myz = 0;
0152   double Mzz = 0;
0153 
0154   for (auto& [x, y] : positions)
0155   {
0156     double const Xi = x - meanX;  //  centered x-coordinates
0157     double const Yi = y - meanY;  //  centered y-coordinates
0158     double const Zi = square(Xi) + square(Yi);
0159 
0160     Mxy += Xi * Yi;
0161     Mxx += Xi * Xi;
0162     Myy += Yi * Yi;
0163     Mxz += Xi * Zi;
0164     Myz += Yi * Zi;
0165     Mzz += Zi * Zi;
0166   }
0167   Mxx /= weight;
0168   Myy /= weight;
0169   Mxy /= weight;
0170   Mxz /= weight;
0171   Myz /= weight;
0172   Mzz /= weight;
0173 
0174   //  computing coefficients of the characteristic polynomial
0175 
0176   const double Mz = Mxx + Myy;
0177   const double Cov_xy = Mxx * Myy - Mxy * Mxy;
0178   const double Var_z = Mzz - Mz * Mz;
0179   const double A3 = 4 * Mz;
0180   const double A2 = -3 * Mz * Mz - Mzz;
0181   const double A1 = Var_z * Mz + 4 * Cov_xy * Mz - Mxz * Mxz - Myz * Myz;
0182   const double A0 = Mxz * (Mxz * Myy - Myz * Mxy) + Myz * (Myz * Mxx - Mxz * Mxy) - Var_z * Cov_xy;
0183   const double A22 = A2 + A2;
0184   const double A33 = A3 + A3 + A3;
0185 
0186   //    finding the root of the characteristic polynomial
0187   //    using Newton's method starting at x=0
0188   //    (it is guaranteed to converge to the right root)
0189   static constexpr int iter_max = 99;
0190   double x = 0;
0191   double y = A0;
0192 
0193   // usually, 4-6 iterations are enough
0194   for (int iter = 0; iter < iter_max; ++iter)
0195   {
0196     const double Dy = A1 + x * (A22 + A33 * x);
0197     const double xnew = x - y / Dy;
0198     if ((xnew == x) || (!std::isfinite(xnew)))
0199     {
0200       break;
0201     }
0202 
0203     const double ynew = A0 + xnew * (A1 + xnew * (A2 + xnew * A3));
0204     if (std::abs(ynew) >= std::abs(y))
0205     {
0206       break;
0207     }
0208 
0209     x = xnew;
0210     y = ynew;
0211   }
0212 
0213   //  computing parameters of the fitting circle
0214   const double DET = square(x) - x * Mz + Cov_xy;
0215   const double Xcenter = (Mxz * (Myy - x) - Myz * Mxy) / DET / 2;
0216   const double Ycenter = (Myz * (Mxx - x) - Mxz * Mxy) / DET / 2;
0217 
0218   //  assembling the output
0219   double const X0 = Xcenter + meanX;
0220   double const Y0 = Ycenter + meanY;
0221   double const R = std::sqrt(square(Xcenter) + square(Ycenter) + Mz);
0222   return std::make_tuple(R, X0, Y0);
0223 }
0224 
0225 //_________________________________________________________________________________
0226 TrackFitUtils::circle_fit_output_t TrackFitUtils::circle_fit_by_taubin(const std::vector<Acts::Vector3>& positions)
0227 {
0228   position_vector_t positions_2d;
0229   for (const auto& position : positions)
0230   {
0231     positions_2d.emplace_back(position.x(), position.y());
0232   }
0233 
0234   return circle_fit_by_taubin(positions_2d);
0235 }
0236 
0237 //_________________________________________________________________________________
0238 TrackFitUtils::line_fit_output_t TrackFitUtils::line_fit(const TrackFitUtils::position_vector_t& positions)
0239 {
0240   // calculate the best line fit to an array of x and y points,
0241   // which minimizing the square of the distances orthogonally
0242   // from the points to the line. Assume that the variances of x and y
0243   //  are equal (this is the Deming method)
0244   // get the mean values
0245   double xmean = 0.;
0246   double ymean = 0.;
0247   for (const auto& [x, y] : positions)
0248   {
0249     xmean = xmean + x;
0250     ymean = ymean + y;
0251   }
0252   double const n = positions.size();
0253   xmean /= n;
0254   ymean /= n;
0255 
0256   // calculate the standard deviations
0257   double ssd_x = 0.;
0258   double ssd_y = 0.;
0259   double ssd_xy = 0.;
0260   for (const auto& [x, y] : positions)
0261   {
0262     ssd_x += square(x - xmean);
0263     ssd_y += square(y - ymean);
0264     ssd_xy += (x - xmean) * (y - ymean);
0265   }
0266   const double slope = (ssd_y - ssd_x + sqrt(square(ssd_y - ssd_x) + 4 * square(ssd_xy))) / 2. / ssd_xy;
0267   const double intercept = ymean - slope * xmean;
0268   return std::make_tuple(slope, intercept);
0269 }
0270 
0271 //_________________________________________________________________________________
0272 TrackFitUtils::line_fit_output_t TrackFitUtils::line_fit(const std::vector<Acts::Vector3>& positions)
0273 {
0274   position_vector_t positions_2d;
0275   for (const auto& position : positions)
0276   {
0277     positions_2d.emplace_back(std::sqrt(square(position.x()) + square(position.y())), position.z());
0278   }
0279 
0280   return line_fit(positions_2d);
0281 }
0282 
0283 //_________________________________________________________________________________
0284 TrackFitUtils::line_fit_output_t TrackFitUtils::line_fit_xz(const std::vector<Acts::Vector3>& positions)
0285 {
0286   position_vector_t positions_2d;
0287   for (const auto& position : positions)
0288   {
0289     positions_2d.emplace_back(position.x(), position.z());  // returns dx/dz and z intercept
0290   }
0291 
0292   return line_fit(positions_2d);
0293 }
0294 
0295 //_________________________________________________________________________________
0296 TrackFitUtils::line_fit_output_t TrackFitUtils::line_fit_xy(const std::vector<Acts::Vector3>& positions)
0297 {
0298   position_vector_t positions_2d;
0299   for (const auto& position : positions)
0300   {
0301     positions_2d.emplace_back(position.x(), position.y());  // returns dx/dy and y intercept
0302   }
0303 
0304   return line_fit(positions_2d);
0305 }
0306 
0307 //_________________________________________________________________________________
0308 TrackFitUtils::line_circle_intersection_output_t TrackFitUtils::line_circle_intersection(double r, double m, double b)
0309 {
0310   const double a_coef = 1 + square(m);
0311   const double b_coef = 2 * m * b;
0312   const double c_coef = square(b) - square(r);
0313   const double delta = square(b_coef) - 4 * a_coef * c_coef;
0314 
0315   const double sqdelta = std::sqrt(delta);
0316 
0317   const double xplus = (-b_coef + sqdelta) / (2. * a_coef);
0318   const double xminus = (-b_coef - sqdelta) / (2. * a_coef);
0319 
0320   const double yplus = m * xplus + b;
0321   const double yminus = m * xminus + b;
0322 
0323   return std::make_tuple(xplus, yplus, xminus, yminus);
0324 }
0325 
0326 //_________________________________________________________________________________
0327 TrackFitUtils::circle_circle_intersection_output_t TrackFitUtils::circle_circle_intersection(double r1, double r2, double x2, double y2)
0328 {
0329   const double D = square(r1) - square(r2) + square(x2) + square(y2);
0330   const double a = 1.0 + square(x2 / y2);
0331   const double b = -D * x2 / square(y2);
0332   const double c = square(D / (2. * y2)) - square(r1);
0333   const double delta = square(b) - 4. * a * c;
0334 
0335   const double sqdelta = std::sqrt(delta);
0336 
0337   const double xplus = (-b + sqdelta) / (2. * a);
0338   const double xminus = (-b - sqdelta) / (2. * a);
0339 
0340   const double yplus = -(2 * x2 * xplus - D) / (2. * y2);
0341   const double yminus = -(2 * x2 * xminus - D) / (2. * y2);
0342 
0343   return std::make_tuple(xplus, yplus, xminus, yminus);
0344 }
0345 
0346 unsigned int TrackFitUtils::addClustersOnLine(TrackFitUtils::line_fit_output_t& fitpars,
0347                                               const bool& isXY,
0348                                               const double& dca_cut,
0349                                               ActsGeometry* tGeometry,
0350                                               TrkrClusterContainer* clusterContainer,
0351                                               std::vector<Acts::Vector3>& global_vec,
0352                                               std::vector<TrkrDefs::cluskey>& cluskey_vec,
0353                                               const unsigned int& startLayer,
0354                                               const unsigned int& endLayer)
0355 {
0356   float const slope = std::get<0>(fitpars);
0357   float const intercept = std::get<1>(fitpars);
0358 
0359   int nclusters = 0;
0360   std::set<TrkrDefs::cluskey> keys_to_add;
0361   std::set<TrkrDefs::TrkrId> detectors = {TrkrDefs::TrkrId::mvtxId,
0362                                           TrkrDefs::TrkrId::inttId,
0363                                           TrkrDefs::TrkrId::tpcId,
0364                                           TrkrDefs::TrkrId::micromegasId};
0365 
0366   if (startLayer > 2)
0367   {
0368     detectors.erase(TrkrDefs::TrkrId::mvtxId);
0369     if (startLayer > 6)
0370     {
0371       detectors.erase(TrkrDefs::TrkrId::inttId);
0372       if (startLayer > 54)
0373       {
0374         detectors.erase(TrkrDefs::TrkrId::tpcId);
0375       }
0376     }
0377   }
0378   if (endLayer < 56)
0379   {
0380     detectors.erase(TrkrDefs::TrkrId::micromegasId);
0381     if (endLayer < 7)
0382     {
0383       detectors.erase(TrkrDefs::TrkrId::tpcId);
0384       if (endLayer < 3)
0385       {
0386         detectors.erase(TrkrDefs::TrkrId::inttId);
0387       }
0388     }
0389   }
0390   for (const auto& det : detectors)
0391   {
0392     for (const auto& hitsetkey : clusterContainer->getHitSetKeys(det))
0393     {
0394       if (TrkrDefs::getLayer(hitsetkey) < startLayer ||
0395           TrkrDefs::getLayer(hitsetkey) > endLayer)
0396       {
0397         continue;
0398       }
0399 
0400       auto range = clusterContainer->getClusters(hitsetkey);
0401       for (auto clusIter = range.first; clusIter != range.second; ++clusIter)
0402       {
0403         TrkrDefs::cluskey const cluskey = clusIter->first;
0404 
0405         TrkrCluster* cluster = clusIter->second;
0406 
0407         auto global = tGeometry->getGlobalPosition(cluskey, cluster);
0408         float x, y;
0409         if (isXY)
0410         {
0411           x = global.x();
0412           y = global.y();
0413         }
0414         else
0415         {
0416           //! use r-z
0417           x = global.z();
0418           y = std::sqrt(square(global.x()) + square(global.y()));
0419           if (global.y() < 0)
0420           {
0421             y *= -1;
0422           }
0423         }
0424 
0425         //! Need to find the point on the line closest to the cluster position
0426         //! The line connecting the cluster and the line is necessarily
0427         //! perpendicular, so it has the opposite and reciprocal slope
0428         //! The cluster is on the line so we use it to find the intercept
0429         float const perpSlope = -1 / slope;
0430         float const perpIntercept = y - perpSlope * x;
0431 
0432         //! Now we have two lines, so solve the system of eqns to get the intersection
0433         float const pcax = (perpIntercept - intercept) / (slope - perpSlope);
0434         float const pcay = slope * pcax + intercept;
0435         //! Take the difference to find the distance
0436 
0437         float const dcax = pcax - x;
0438         float const dcay = pcay - y;
0439         float dca = std::sqrt(square(dcax) + square(dcay));
0440         if (!isXY && TrkrDefs::getTrkrId(cluskey) == TrkrDefs::TrkrId::inttId)
0441         {
0442           //! Just ignore the z direction completely for the intt
0443           dca = dcay;
0444         }
0445         if (dca < dca_cut)
0446         {
0447           keys_to_add.insert(cluskey);
0448         }
0449       }
0450     }
0451   }
0452 
0453   for (auto& key : keys_to_add)
0454   {
0455     cluskey_vec.push_back(key);
0456     auto clus = clusterContainer->findCluster(key);
0457     auto global = tGeometry->getGlobalPosition(key, clus);
0458     global_vec.push_back(global);
0459     nclusters++;
0460   }
0461 
0462   return nclusters;
0463 }
0464 
0465 //_________________________________________________________________________________
0466 unsigned int TrackFitUtils::addClusters(std::vector<float>& fitpars,
0467                                         double dca_cut,
0468                                         ActsGeometry* _tGeometry,
0469                                         TrkrClusterContainer* _cluster_map,
0470                                         std::vector<Acts::Vector3>& global_vec,
0471                                         std::vector<TrkrDefs::cluskey>& cluskey_vec,
0472                                         unsigned int startLayer,
0473                                         unsigned int endLayer)
0474 {
0475   // project the fit of the TPC clusters to each silicon layer, and find the nearest silicon cluster
0476   // iterate over the cluster map and find silicon clusters that match this track fit
0477 
0478   unsigned int nclusters = 0;
0479 
0480   // We want the best match in each layer
0481   std::set<TrkrDefs::cluskey> keysToAdd;
0482   std::set<TrkrDefs::TrkrId> detectors = {TrkrDefs::TrkrId::mvtxId,
0483                                           TrkrDefs::TrkrId::inttId,
0484                                           TrkrDefs::TrkrId::tpcId,
0485                                           TrkrDefs::TrkrId::micromegasId};
0486 
0487   if (startLayer > 2)
0488   {
0489     detectors.erase(TrkrDefs::TrkrId::mvtxId);
0490     if (startLayer > 6)
0491     {
0492       detectors.erase(TrkrDefs::TrkrId::inttId);
0493       if (startLayer > 54)
0494       {
0495         detectors.erase(TrkrDefs::TrkrId::tpcId);
0496       }
0497     }
0498   }
0499   if (endLayer < 56)
0500   {
0501     detectors.erase(TrkrDefs::TrkrId::micromegasId);
0502     if (endLayer < 7)
0503     {
0504       detectors.erase(TrkrDefs::TrkrId::tpcId);
0505       if (endLayer < 3)
0506       {
0507         detectors.erase(TrkrDefs::TrkrId::inttId);
0508       }
0509     }
0510   }
0511 
0512   for (const auto& det : detectors)
0513   {
0514     for (const auto& hitsetkey : _cluster_map->getHitSetKeys(det))
0515     {
0516       if (TrkrDefs::getLayer(hitsetkey) < startLayer ||
0517           TrkrDefs::getLayer(hitsetkey) > endLayer)
0518       {
0519         continue;
0520       }
0521 
0522       auto range = _cluster_map->getClusters(hitsetkey);
0523       for (auto clusIter = range.first; clusIter != range.second; ++clusIter)
0524       {
0525         TrkrDefs::cluskey const cluskey = clusIter->first;
0526 
0527         TrkrCluster* cluster = clusIter->second;
0528 
0529         auto global = _tGeometry->getGlobalPosition(cluskey, cluster);
0530 
0531         Acts::Vector3 pca = get_helix_pca(fitpars, global);
0532         float dca = (pca - global).norm();
0533 
0534         Acts::Vector2 const global_xy(global(0), global(1));
0535         Acts::Vector2 const pca_xy(pca(0), pca(1));
0536         Acts::Vector2 const pca_xy_residual = pca_xy - global_xy;
0537         dca = pca_xy_residual.norm();
0538         if (dca < dca_cut)
0539         {
0540           keysToAdd.insert(cluskey);
0541         }
0542       }  // end cluster iteration
0543     }  // end hitsetkey iteration
0544   }
0545 
0546   for (auto& key : keysToAdd)
0547   {
0548     cluskey_vec.push_back(key);
0549     auto clus = _cluster_map->findCluster(key);
0550     auto global = _tGeometry->getGlobalPosition(key, clus);
0551     global_vec.push_back(global);
0552     nclusters++;
0553   }
0554 
0555   return nclusters;
0556 }
0557 
0558 //_________________________________________________________________________________
0559 Acts::Vector3 TrackFitUtils::get_helix_pca(std::vector<float>& fitpars,
0560                                            const Acts::Vector3& global)
0561 {
0562   // no analytic solution for the coordinates of the closest approach of a helix to a point
0563   // 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
0564 
0565   float const radius = fitpars[0];
0566   float const x0 = fitpars[1];
0567   float const y0 = fitpars[2];
0568   float const zslope = fitpars[3];
0569   float const z0 = fitpars[4];
0570 
0571   Acts::Vector2 pca_circle = get_circle_point_pca(radius, x0, y0, global);
0572 
0573   // The radius of the PCA determines the z position:
0574   float const pca_circle_radius = pca_circle.norm();
0575   float const pca_z = pca_circle_radius * zslope + z0;
0576   Acts::Vector3 const pca(pca_circle(0), pca_circle(1), pca_z);
0577 
0578   // now we want a second point on the helix so we can get a local straight line approximation to the track
0579   // project the circle PCA vector an additional small amount and find the helix PCA to that point
0580   float const projection = 0.25;  // cm
0581   Acts::Vector3 const second_point = pca + projection * pca / pca.norm();
0582   Acts::Vector2 second_point_pca_circle = get_circle_point_pca(radius, x0, y0, second_point);
0583   float const second_point_pca_z = second_point_pca_circle.norm() * zslope + z0;
0584   Acts::Vector3 const second_point_pca(second_point_pca_circle(0), second_point_pca_circle(1), second_point_pca_z);
0585 
0586   // pca and second_point_pca define a straight line approximation to the track
0587   Acts::Vector3 const tangent = (second_point_pca - pca) / (second_point_pca - pca).norm();
0588 
0589   // get the PCA of the cluster to that line
0590   Acts::Vector3 final_pca = getPCALinePoint(global, tangent, pca);
0591 
0592   return final_pca;
0593 }
0594 
0595 //_________________________________________________________________________________
0596 Acts::Vector2 TrackFitUtils::get_circle_point_pca(float radius, float x0, float y0, Acts::Vector3 global)
0597 {
0598   // get the PCA of a cluster (x,y) position to a circle
0599   // draw a line from the origin of the circle to the point
0600   // the intersection of the line with the circle is at the distance radius from the origin along that line
0601 
0602   Acts::Vector2 const origin(x0, y0);
0603   Acts::Vector2 const point(global(0), global(1));
0604 
0605   Acts::Vector2 pca = origin + radius * (point - origin) / (point - origin).norm();
0606 
0607   return pca;
0608 }
0609 
0610 //_________________________________________________________________________________
0611 std::vector<float> TrackFitUtils::fitClusters(std::vector<Acts::Vector3>& global_vec,
0612                                               const std::vector<TrkrDefs::cluskey> &cluskey_vec,
0613                                               bool use_intt)
0614 {
0615   std::vector<float> fitpars;
0616 
0617   // make the helical fit using TrackFitUtils
0618   if (global_vec.size() < 3)
0619   {
0620     return fitpars;
0621   }
0622   std::tuple<double, double, double> circle_fit_pars = TrackFitUtils::circle_fit_by_taubin(global_vec);
0623 
0624   // 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
0625   std::vector<Acts::Vector3> global_vec_noINTT;
0626   for (unsigned int ivec = 0; ivec < global_vec.size(); ++ivec)
0627   {
0628     unsigned int const trkrid = TrkrDefs::getTrkrId(cluskey_vec[ivec]);
0629 
0630     if (trkrid != TrkrDefs::inttId && cluskey_vec[ivec] != 0)
0631     {
0632       global_vec_noINTT.push_back(global_vec[ivec]);
0633     }
0634   }
0635   //  std::cout << " use_intt = " << use_intt << std::endl;
0636   if (use_intt)
0637   {
0638     global_vec_noINTT = global_vec;
0639   }
0640   if (global_vec_noINTT.size() < 3)
0641   {
0642     return fitpars;
0643   }
0644   std::tuple<double, double> line_fit_pars = TrackFitUtils::line_fit(global_vec_noINTT);
0645 
0646   fitpars.push_back(std::get<0>(circle_fit_pars));
0647   fitpars.push_back(std::get<1>(circle_fit_pars));
0648   fitpars.push_back(std::get<2>(circle_fit_pars));
0649   fitpars.push_back(std::get<0>(line_fit_pars));
0650   fitpars.push_back(std::get<1>(line_fit_pars));
0651 
0652   return fitpars;
0653 }
0654 
0655 //_________________________________________________________________________________
0656 std::vector<float> TrackFitUtils::fitClustersZeroField(const std::vector<Acts::Vector3>& global_vec,
0657                                                        const std::vector<TrkrDefs::cluskey>& cluskey_vec, bool use_intt, bool mvtx_east, bool mvtx_west)
0658 
0659 {
0660   std::vector<float> fitpars;
0661   std::tuple<double, double> xy_fit_pars;
0662 
0663   // make the helical fit using TrackFitUtils
0664   if (global_vec.size() < 3)
0665   {
0666     std::cout << " TrackFitUtils::fitClustersZeroField failed for <3 cluskeys " << ((int) global_vec.size()) << std::endl;
0667     return fitpars;
0668   }
0669   //  std::tuple<double, double> xy_fit_pars = TrackFitUtils::line_fit_xy(global_vec);
0670 
0671   // 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
0672   // also, for half to half cosmics, allow selection of only 1 half of mvtx (east or west)
0673   std::vector<Acts::Vector3> global_vec_noINTT;
0674   bool cross_mvtx_half = false;
0675   if ((mvtx_east || mvtx_west))
0676   {
0677     cross_mvtx_half = TrackFitUtils::isTrackCrossMvtxHalf(cluskey_vec);
0678   }
0679   else
0680   {
0681     xy_fit_pars = TrackFitUtils::line_fit_xy(global_vec);
0682   }
0683   for (unsigned int ivec = 0; ivec < global_vec.size(); ++ivec)
0684   {
0685     unsigned int const trkrid = TrkrDefs::getTrkrId(cluskey_vec[ivec]);
0686     if ((mvtx_east || mvtx_west) && trkrid == TrkrDefs::mvtxId)
0687     {
0688       if (cross_mvtx_half && TrackFitUtils::includeMvtxHit(cluskey_vec[ivec], mvtx_east, mvtx_west))
0689       {
0690         global_vec_noINTT.push_back(global_vec[ivec]);
0691       }
0692     }
0693     else if (trkrid != TrkrDefs::inttId && cluskey_vec[ivec] != 0)
0694     {
0695       global_vec_noINTT.push_back(global_vec[ivec]);
0696     }
0697   }
0698   //  std::cout << " use_intt = " << use_intt << std::endl;
0699   if (use_intt)
0700   {
0701     global_vec_noINTT = global_vec;
0702   }
0703   if (global_vec_noINTT.size() < 3)
0704   {
0705     // std::cout << " TrackFitUtils::fitClustersZeroField failed for <3 non-INTT cluskeys " << ((int)global_vec_noINTT.size()) << std::endl;
0706     return fitpars;
0707   }
0708   std::tuple<double, double> xz_fit_pars = TrackFitUtils::line_fit_xz(global_vec_noINTT);
0709   if ((mvtx_east || mvtx_west))
0710   {
0711     xy_fit_pars = TrackFitUtils::line_fit_xy(global_vec_noINTT);
0712   }
0713 
0714   fitpars.push_back(std::get<0>(xy_fit_pars));
0715   fitpars.push_back(std::get<1>(xy_fit_pars));
0716   fitpars.push_back(std::get<0>(xz_fit_pars));
0717   fitpars.push_back(std::get<1>(xz_fit_pars));
0718 
0719   /*
0720   std::cout << "   dy/dx " << fitpars[0]
0721             << " y0 " << fitpars[1]
0722             << " dz/dx " << fitpars[2]
0723             << " z0 " << fitpars[3]
0724             << std::endl;
0725   std::cout << " global (layer 0): " << std::endl << global_vec_noINTT[0] << std::endl;
0726   std::cout << " global (layer 2): " << std::endl << global_vec_noINTT[2] << std::endl;
0727   */
0728 
0729   return fitpars;
0730 }
0731 
0732 //_________________________________________________________________________________
0733 void TrackFitUtils::getTrackletClusters(ActsGeometry* _tGeometry,
0734                                         TrkrClusterContainer* _cluster_map,
0735                                         std::vector<Acts::Vector3>& global_vec,
0736                                         const std::vector<TrkrDefs::cluskey>& cluskey_vec)
0737 {
0738   for (unsigned long const key : cluskey_vec)
0739   {
0740     auto cluster = _cluster_map->findCluster(key);
0741     if (!cluster)
0742     {
0743       std::cout << "Failed to get cluster with key " << key << std::endl;
0744       continue;
0745     }
0746 
0747     Acts::Vector3 const global = _tGeometry->getGlobalPosition(key, cluster);
0748 
0749     /*
0750     const unsigned int trkrId = TrkrDefs::getTrkrId(key);
0751     // ASK TONY ABOUT THIS:
0752     // have to add corrections for TPC clusters after transformation to global
0753     if(trkrId == TrkrDefs::tpcId)
0754       {
0755         int crossing = 0;  // for now
0756         makeTpcGlobalCorrections(key, crossing, global);
0757       }
0758     */
0759 
0760     // add the global positions to a vector to return
0761     global_vec.push_back(global);
0762   }  // end loop over clusters for this track
0763 }
0764 
0765 //_________________________________________
0766 Acts::Vector2 TrackFitUtils::get_line_point_pca(double slope, double intercept, Acts::Vector3 global)
0767 {
0768   // return closest point (in xy) on the line to the point global
0769   /* Acts::Vector2 point(global(0), global(1)); */
0770   /* Acts::Vector2 posref(0, intercept);       // arbitrary point on the line */
0771   /* Acts::Vector2 arb_point(2.0, slope*2.0 + intercept); // second arbitrary point on line */
0772   /* Acts::Vector2 tangent = arb_point - posref; */
0773   /* tangent = tangent/tangent.norm();   // +/- the line direction */
0774   /* Acts::Vector2 pca = posref + ((point - posref).dot(tangent))*tangent; */
0775 
0776   const double& m = slope;
0777   const double& b = intercept;
0778   const double& x0 = global(0);
0779   const double& y0 = global(1);
0780   const double xp = (x0 + m * (y0 - b)) / (1 + m * m);
0781   const double yp = m * xp + b;
0782 
0783   return {xp, yp};  // Acts::Vector2(pca;
0784 
0785   /* return std::tuple(xp,yp); */
0786 }
0787 
0788 double TrackFitUtils::z_fit_to_pca(const double xy_slope, const double xy_intercept,
0789                                    const std::vector<Acts::Vector3>& glob_pts)
0790 {
0791   // Project (0,0) to the line, this is the origin (pca)
0792   // Project each point to the line and find the distance along the line to pca
0793   // Collect the distance and Z
0794   // Fit Z=m*dist+b; and return b of the fit
0795   auto pca = get_line_point_pca(xy_slope, xy_intercept, Acts::Vector3(0, 0, 0));
0796   std::vector<std::pair<double, double>> zd_vec;
0797   for (auto& glob : glob_pts)
0798   {
0799     auto point = get_line_point_pca(xy_slope, xy_intercept, glob);  // point = point on line
0800     double const dist = sqrt(square(pca.x() - point.x()) + square(pca.y() + point.y()));
0801     zd_vec.emplace_back(dist, glob.z());
0802   }
0803   auto slope_and_b = line_fit(zd_vec);
0804   /* double zd_slope; */
0805   /* double zd_intercept; */
0806   /* std::tie(zd_slope, zd_intercept) = line_fit(zd_vec); */
0807   return std::get<1>(slope_and_b);
0808 }
0809 
0810 double TrackFitUtils::line_dist_to_pca(const double slope, const double intercept,
0811                                        const Acts::Vector2& pca, const Acts::Vector3& global)
0812 {
0813   auto point_on_line = get_line_point_pca(slope, intercept, global);
0814   return sqrt(square(pca.x() - point_on_line.x()) + square(pca.y() - point_on_line.y()));
0815 }
0816 
0817 //_________________________________________________________________________________
0818 Acts::Vector3 TrackFitUtils::getPCALinePoint(const Acts::Vector3& global, const Acts::Vector3& tangent, const Acts::Vector3& posref)
0819 {
0820   // Approximate track with a straight line consisting of the state position posref and the vector (px,py,pz)
0821 
0822   // The position of the closest point on the line to global is:
0823   // posref + projection of difference between the point and posref on the tangent vector
0824   Acts::Vector3 pca = posref + ((global - posref).dot(tangent)) * tangent;
0825   /*
0826   if( (pca-posref).norm() > 0.001)
0827     {
0828       std::cout << " getPCALinePoint: old pca " << posref(0) << "  " << posref(1) << "  " << posref(2) << std::endl;
0829       std::cout << " getPCALinePoint: new pca " << pca(0) << "  " << pca(1) << "  " << pca(2) << std::endl;
0830       std::cout << " getPCALinePoint: delta pca " << pca(0) - posref(0) << "  " << pca(1)-posref(1) << "  " << pca(2) -posref(2) << std::endl;
0831     }
0832   */
0833 
0834   return pca;
0835 }
0836 
0837 Acts::Vector3 TrackFitUtils::get_helix_surface_intersection(const Surface& surf,
0838                                                             std::vector<float>& fitpars, Acts::Vector3& global, ActsGeometry* _tGeometry)
0839 {
0840   // we want the point where the helix intersects the plane of the surface
0841   // get the plane of the surface
0842   Acts::Vector3 sensorCenter = surf->center(_tGeometry->geometry().getGeoContext()) * 0.1;  // convert to cm
0843   Acts::Vector3 sensorNormal = -surf->normal(_tGeometry->geometry().getGeoContext(), Acts::Vector3(1, 1, 1), Acts::Vector3(1, 1, 1));
0844 
0845   sensorNormal /= sensorNormal.norm();
0846 
0847   // there are analytic solutions for a line-plane intersection.
0848   // to use this, need to get the vector tangent to the helix near the measurement and a point on it.
0849   std::pair<Acts::Vector3, Acts::Vector3> const line = get_helix_tangent(fitpars, global);
0850   Acts::Vector3 const pca = line.first;
0851   Acts::Vector3 const tangent = line.second;
0852 
0853   Acts::Vector3 intersection = get_line_plane_intersection(pca, tangent, sensorCenter, sensorNormal);
0854 
0855   return intersection;
0856 }
0857 Acts::Vector3 TrackFitUtils::get_line_plane_intersection(const Acts::Vector3& pca, const Acts::Vector3& tangent,
0858                                                          const Acts::Vector3& sensorCenter, const Acts::Vector3& sensorNormal)
0859 {
0860   // get the intersection of the line made by PCA and tangent with the plane of the sensor
0861 
0862   // For a point on the line
0863   // p = PCA + d * tangent;
0864   // for a point on the plane
0865   // (p - sensor_center).sensor_normal = 0
0866 
0867   // The solution is:
0868   float const d = (sensorCenter - pca).dot(sensorNormal) / tangent.dot(sensorNormal);
0869   return pca + d * tangent;
0870 }
0871 std::vector<double> TrackFitUtils::getLineClusterResiduals(TrackFitUtils::position_vector_t& rz_pts, float slope, float intercept)
0872 {
0873   std::vector<double> residuals;
0874   // calculate cluster residuals from the fitted circle
0875   std::transform(rz_pts.begin(), rz_pts.end(),
0876                  std::back_inserter(residuals), [slope, intercept](const std::pair<double, double>& point)
0877                  {
0878     double const r = point.first;
0879     double const z = point.second;
0880     
0881     // The shortest distance of a point from a circle is along the radial; line from the circle center to the point
0882     
0883     double const a = -slope;
0884     double const b = 1.0;
0885     double const c = -intercept;
0886     return std::abs(a*r+b*z+c)/sqrt(square(a)+square(b)); });
0887   return residuals;
0888 }
0889 
0890 std::vector<double> TrackFitUtils::getCircleClusterResiduals(TrackFitUtils::position_vector_t& xy_pts, float R, float X0, float Y0)
0891 {
0892   std::vector<double> residuals;
0893   std::transform(xy_pts.begin(), xy_pts.end(),
0894                  std::back_inserter(residuals), [R, X0, Y0](const std::pair<double, double>& point)
0895                  {
0896     double const x = point.first;
0897     double const y = point.second;
0898 
0899     // The shortest distance of a point from a circle is along the radial; line from the circle center to the point
0900     return std::sqrt( square(x-X0) + square(y-Y0) )  -  R; });
0901 
0902   return residuals;
0903 }
0904 
0905 float TrackFitUtils::get_helix_pathlength(std::vector<float>& fitpars, const Acts::Vector3& start_point, const Acts::Vector3& end_point)
0906 {
0907   // caveats:
0908   // - when pz is zero, the helix is degenerate, so this method assumes the pathlength is on the first loop around
0909   // - this method does not check whether the start and end points are actually compatible with the helix;
0910   //   the actual points that this method uses are those points on the helix with the same z and phi as start_point and end_point
0911   if (fitpars.size() != 5)
0912   {
0913     return -1e9;
0914   }
0915   float const R = fitpars[0];
0916   float const x0 = fitpars[1];
0917   float const y0 = fitpars[2];
0918   float const zslope = fitpars[3];
0919   // float z0 = fitpars[4];
0920 
0921   // path length in z per half-turn of helix = dz/dr * (apogee r - perigee r)
0922   // apogee r = sqrt(x0^2+y0^2) + R
0923   // perigee r = sqrt(x0^2+y0^2) - R
0924   float const half_turn_z_pathlength = zslope * 2 * R;
0925 
0926   // find number of turns
0927   float const z_dist = end_point.z() - start_point.z();
0928   int const n_turns = std::floor(std::fabs(z_dist / half_turn_z_pathlength));
0929 
0930   // phi relative to center of circle
0931   float const phic_start = atan2(start_point.y() - y0, start_point.x() - x0);
0932   float const phic_end = atan2(end_point.y() - y0, end_point.x() - x0);
0933   float const phic_dist = std::fabs(phic_end - phic_start) + n_turns * 2 * M_PI;
0934   float const xy_dist = R * phic_dist;
0935 
0936   float const pathlength = std::sqrt(xy_dist * xy_dist + z_dist * z_dist);
0937   return pathlength;
0938 }
0939 
0940 float TrackFitUtils::get_helix_surface_pathlength(const Surface& surf, std::vector<float>& fitpars, const Acts::Vector3& start_point, ActsGeometry* tGeometry)
0941 {
0942   Acts::Vector3 surface_center = surf->center(tGeometry->geometry().getGeoContext()) * 0.1;
0943   Acts::Vector3 const intersection = get_helix_surface_intersection(surf, fitpars, surface_center, tGeometry);
0944   return get_helix_pathlength(fitpars, start_point, intersection);
0945 }
0946 
0947 /* std::tuple<double,double> TrackFitUtils::dca_on_line2D_to_point( */
0948 /*     double x0, double y0, double m, double b) { */
0949 /*   // Find the point on a line y=mx+b closest to the point (x0,y0) */
0950 /*   const double xp = (x0+m*(y0-b))/(1+m*m); */
0951 /*   const double yp = m*xp+b; */
0952 /*   return std::tuple(xp,yp); */
0953 /* } */
0954 
0955 // phi, eta, pT, pos_dca, momentum -- note that momentum is such that pT\equiv1
0956 std::tuple<bool, double, double, double, Acts::Vector3, Acts::Vector3>
0957 TrackFitUtils::zero_field_track_params(
0958     ActsGeometry* _tGeometry,
0959     TrkrClusterContainer* _cluster_map,
0960     const std::vector<TrkrDefs::cluskey>& cluskey_vec)
0961 {
0962   std::vector<Acts::Vector3> global_vec;
0963 
0964   // get the global positions
0965   getTrackletClusters(_tGeometry, _cluster_map, global_vec, cluskey_vec);
0966   if (global_vec.size() < 2)
0967   {
0968     return std::make_tuple(false, 0., 0., 0., Acts::Vector3(0., 0., 0), Acts::Vector3(0., 0., 0.));
0969   }
0970 
0971   // get the y=mx+b and z=mx+b fits
0972   auto params = fitClustersZeroField(global_vec, cluskey_vec, true);
0973   if (params.size() < 4)
0974   {
0975     std::cout << " fit params failed at size : " << ((int) params.size()) << std::endl;
0976     return std::make_tuple(false, 0., 0., 0., Acts::Vector3(0., 0., 0), Acts::Vector3(0., 0., 0.));
0977   }
0978   const double xy_m = params[0];
0979   const double xy_b = params[1];
0980   const double xz_m = params[2];
0981   const double xz_b = params[3];
0982 
0983   // get the DCA in x,y to the line (from y=mx+b)
0984   double x, y, z;
0985   // use the get_line_point_pca
0986   // use the function TrackFitUtils::get_line_point_pca
0987   Acts::Vector2 dca_xy = get_line_point_pca(xy_m, xy_b, {0., 0., 0});
0988   x = dca_xy.x();
0989   y = dca_xy.y();
0990   /* std::tie(x,y) = dca_on_line2D_to_point(0,0,xy_m,xy_b); */
0991   z = xz_m * x + xz_b;  //
0992 
0993   // Need direction for the momentum vector
0994   Acts::Vector3 cluster = global_vec.back();
0995   auto clus_dca_xy = get_line_point_pca(xy_m, xy_b, {cluster.x(), cluster.y(), 0});
0996   double px = clus_dca_xy.x();
0997   double py = clus_dca_xy.y();
0998   double pz = xz_m * px + xz_b;
0999 
1000   // calc the alternative pz value:
1001   double const alt_z = z_fit_to_pca(xy_m, xy_b, global_vec);
1002   bool const PRINT_DEBUG = false;
1003   if (PRINT_DEBUG)
1004   {
1005     std::cout << " zthis " << z << " and alt "
1006               << alt_z << " DELTA: " << (alt_z - z) << " xy-slope " << xy_m << std::endl;
1007   }
1008   z = alt_z;  // this correction is normally quite small
1009 
1010   // correct to direction from the pca to origin
1011   px -= x;
1012   py -= y;
1013   pz -= z;
1014 
1015   // scale momentum vector pT to 5. GeV/c
1016   const double scale = sqrt(px * px + py * py) / 5.;
1017   px /= scale;
1018   py /= scale;
1019   pz /= scale;
1020   auto p = Acts::Vector3(px, py, pz);
1021   const double phi = std::atan2(py, px);
1022   const double eta = atanh(pz / p.norm());
1023   if (PRINT_DEBUG)
1024   {
1025     std::cout << "phi: " << phi << " eta: " << eta << " pT: 1" << " x,y,z: " << x << "<" << y << "," << z << "  P: " << px << "," << py << "," << pz << std::endl;
1026   }
1027   return std::make_tuple(true, phi, eta, 1, Acts::Vector3(x, y, z), p);
1028 }
1029 
1030 bool TrackFitUtils::isTrackCrossMvtxHalf(const std::vector<TrkrDefs::cluskey> &cluskey_vec)
1031 {
1032   bool isWest = false;
1033   bool isEast = false;
1034   for (unsigned long ivec : cluskey_vec)
1035   {
1036     uint32_t const hitsetkey = TrkrDefs::getHitSetKeyFromClusKey(ivec);
1037     unsigned int const layer = TrkrDefs::getLayer(hitsetkey);
1038     unsigned int const stave = MvtxDefs::getStaveId(hitsetkey);
1039     if (stave > (2 + layer) && stave < (9 + layer * 3))
1040     {
1041       isEast = true;
1042     }
1043     else
1044     {
1045       isWest = true;
1046     }
1047   }
1048   if (isEast && isWest)
1049   {
1050     return true;
1051   }
1052   return false;
1053 }
1054 
1055 bool TrackFitUtils::includeMvtxHit(TrkrDefs::cluskey clus_key, bool mvtx_east, bool mvtx_west)
1056 {
1057   uint32_t const hitsetkey = TrkrDefs::getHitSetKeyFromClusKey(clus_key);
1058   bool const is_east = isMvtxEast(hitsetkey);
1059   if ((is_east && mvtx_east) || (!is_east && mvtx_west))
1060   {
1061     return true;
1062   }
1063 
1064   return false;
1065 }
1066 
1067 bool TrackFitUtils::isMvtxEast(uint32_t hitsetkey)
1068 {
1069   unsigned int const layer = TrkrDefs::getLayer(hitsetkey);
1070   unsigned int const stave = MvtxDefs::getStaveId(hitsetkey);
1071   bool is_east = false;
1072   if (stave > (2 + layer) && stave < (9 + layer * 3))
1073   {
1074     is_east = true;
1075   }
1076   return is_east;
1077 }