Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:28:36

0001 #ifndef RecoTracker_PixelTrackFitting_interface_alpaka_BrokenLine_h
0002 #define RecoTracker_PixelTrackFitting_interface_alpaka_BrokenLine_h
0003 
0004 #include <alpaka/alpaka.hpp>
0005 
0006 #include <Eigen/Core>
0007 
0008 #include "HeterogeneousCore/AlpakaInterface/interface/config.h"
0009 #include "RecoTracker/PixelTrackFitting/interface/alpaka/FitUtils.h"
0010 
0011 namespace ALPAKA_ACCELERATOR_NAMESPACE::brokenline {
0012 
0013   using namespace cms::alpakatools;
0014   using namespace ::riemannFit;
0015 
0016   //!< Karimäki's parameters: (phi, d, k=1/R)
0017   /*!< covariance matrix: \n
0018     |cov(phi,phi)|cov( d ,phi)|cov( k ,phi)| \n
0019     |cov(phi, d )|cov( d , d )|cov( k , d )| \n
0020     |cov(phi, k )|cov( d , k )|cov( k , k )| \n
0021     as defined in Karimäki V., 1990, Effective circle fitting for particle trajectories, 
0022     Nucl. Instr. and Meth. A305 (1991) 187.
0023   */
0024   using karimaki_circle_fit = riemannFit::CircleFit;
0025 
0026   /*!
0027     \brief data needed for the Broken Line fit procedure.
0028   */
0029   template <int n>
0030   struct PreparedBrokenLineData {
0031     int qCharge;                          //!< particle charge
0032     riemannFit::Matrix2xNd<n> radii;      //!< xy data in the system in which the pre-fitted center is the origin
0033     riemannFit::VectorNd<n> sTransverse;  //!< total distance traveled in the transverse plane
0034                                           //   starting from the pre-fitted closest approach
0035     riemannFit::VectorNd<n> sTotal;       //!< total distance traveled (three-dimensional)
0036     riemannFit::VectorNd<n> zInSZplane;   //!< orthogonal coordinate to the pre-fitted line in the sz plane
0037     riemannFit::VectorNd<n> varBeta;      //!< kink angles in the SZ plane
0038   };
0039 
0040   /*!
0041     \brief Computes the Coulomb multiple scattering variance of the planar angle.
0042     
0043     \param length length of the track in the material.
0044     \param bField magnetic field in Gev/cm/c.
0045     \param radius radius of curvature (needed to evaluate p).
0046     \param layer denotes which of the four layers of the detector is the endpoint of the 
0047    *             multiple scattered track. For example, if Layer=3, then the particle has 
0048    *             just gone through the material between the second and the third layer.
0049     
0050     \todo add another Layer variable to identify also the start point of the track, 
0051    *      so if there are missing hits or multiple hits, the part of the detector that 
0052    *      the particle has traversed can be exactly identified.
0053     
0054     \warning the formula used here assumes beta=1, and so neglects the dependence 
0055    *         of theta_0 on the mass of the particle at fixed momentum.
0056     
0057     \return the variance of the planar angle ((theta_0)^2 /3).
0058   */
0059   template <typename TAcc>
0060   ALPAKA_FN_ACC ALPAKA_FN_INLINE double multScatt(
0061       const TAcc& acc, const double& length, const double bField, const double radius, int layer, double slope) {
0062     // limit R to 20GeV...
0063     auto pt2 = alpaka::math::min(acc, 20., bField * radius);
0064     pt2 *= pt2;
0065     constexpr double inv_X0 = 0.06 / 16.;  //!< inverse of radiation length of the material in cm
0066     //if(Layer==1) XXI_0=0.06/16.;
0067     // else XXI_0=0.06/16.;
0068     //XX_0*=1;
0069 
0070     //! number between 1/3 (uniform material) and 1 (thin scatterer) to be manually tuned
0071     constexpr double geometry_factor = 0.7;
0072     constexpr double fact = geometry_factor * riemannFit::sqr(13.6 / 1000.);
0073     return fact / (pt2 * (1. + riemannFit::sqr(slope))) * (alpaka::math::abs(acc, length) * inv_X0) *
0074            riemannFit::sqr(1. + 0.038 * log(alpaka::math::abs(acc, length) * inv_X0));
0075   }
0076 
0077   /*!
0078     \brief Computes the 2D rotation matrix that transforms the line y=slope*x into the line y=0.
0079     
0080     \param slope tangent of the angle of rotation.
0081     
0082     \return 2D rotation matrix.
0083   */
0084   template <typename TAcc>
0085   ALPAKA_FN_ACC ALPAKA_FN_INLINE riemannFit::Matrix2d rotationMatrix(const TAcc& acc, double slope) {
0086     riemannFit::Matrix2d rot;
0087     rot(0, 0) = 1. / alpaka::math::sqrt(acc, 1. + riemannFit::sqr(slope));
0088     rot(0, 1) = slope * rot(0, 0);
0089     rot(1, 0) = -rot(0, 1);
0090     rot(1, 1) = rot(0, 0);
0091     return rot;
0092   }
0093 
0094   /*!
0095     \brief Changes the Karimäki parameters (and consequently their covariance matrix) under a 
0096    *       translation of the coordinate system, such that the old origin has coordinates (x0,y0) 
0097    *       in the new coordinate system. The formulas are taken from Karimäki V., 1990, Effective 
0098    *       circle fitting for particle trajectories, Nucl. Instr. and Meth. A305 (1991) 187.
0099     
0100     \param circle circle fit in the old coordinate system. circle.par(0) is phi, circle.par(1) is d and circle.par(2) is rho. 
0101     \param x0 x coordinate of the translation vector.
0102     \param y0 y coordinate of the translation vector.
0103     \param jacobian passed by reference in order to save stack.
0104   */
0105   template <typename TAcc>
0106   ALPAKA_FN_ACC ALPAKA_FN_INLINE void translateKarimaki(
0107       const TAcc& acc, karimaki_circle_fit& circle, double x0, double y0, riemannFit::Matrix3d& jacobian) {
0108     // Avoid multiple access to the circle.par vector.
0109     using scalar = typename std::remove_reference<decltype(circle.par(0))>::type;
0110     scalar phi = circle.par(0);
0111     scalar dee = circle.par(1);
0112     scalar rho = circle.par(2);
0113 
0114     // Avoid repeated trig. computations
0115     scalar sinPhi = alpaka::math::sin(acc, phi);
0116     scalar cosPhi = alpaka::math::cos(acc, phi);
0117 
0118     // Intermediate computations for the circle parameters
0119     scalar deltaPara = x0 * cosPhi + y0 * sinPhi;
0120     scalar deltaOrth = x0 * sinPhi - y0 * cosPhi + dee;
0121     scalar tempSmallU = 1 + rho * dee;
0122     scalar tempC = -rho * y0 + tempSmallU * cosPhi;
0123     scalar tempB = rho * x0 + tempSmallU * sinPhi;
0124     scalar tempA = 2. * deltaOrth + rho * (riemannFit::sqr(deltaOrth) + riemannFit::sqr(deltaPara));
0125     scalar tempU = alpaka::math::sqrt(acc, 1. + rho * tempA);
0126 
0127     // Intermediate computations for the error matrix transform
0128     scalar xi = 1. / (riemannFit::sqr(tempB) + riemannFit::sqr(tempC));
0129     scalar tempV = 1. + rho * deltaOrth;
0130     scalar lambda = (0.5 * tempA) / (riemannFit::sqr(1. + tempU) * tempU);
0131     scalar mu = 1. / (tempU * (1. + tempU)) + rho * lambda;
0132     scalar zeta = riemannFit::sqr(deltaOrth) + riemannFit::sqr(deltaPara);
0133     jacobian << xi * tempSmallU * tempV, -xi * riemannFit::sqr(rho) * deltaOrth, xi * deltaPara,
0134         2. * mu * tempSmallU * deltaPara, 2. * mu * tempV, mu * zeta - lambda * tempA, 0, 0, 1.;
0135 
0136     // translated circle parameters
0137     // phi
0138     circle.par(0) = alpaka::math::atan2(acc, tempB, tempC);
0139     // d
0140     circle.par(1) = tempA / (1 + tempU);
0141     // rho after translation. It is invariant, so noop
0142     // circle.par(2)= rho;
0143 
0144     // translated error matrix
0145     circle.cov = jacobian * circle.cov * jacobian.transpose();
0146   }
0147 
0148   /*!
0149     \brief Computes the data needed for the Broken Line fit procedure that are mainly common for the circle and the line fit.
0150     
0151     \param hits hits coordinates.
0152     \param fast_fit pre-fit result in the form (X0,Y0,R,tan(theta)).
0153     \param bField magnetic field in Gev/cm/c.
0154     \param results PreparedBrokenLineData to be filled (see description of PreparedBrokenLineData).
0155   */
0156   template <typename TAcc, typename M3xN, typename V4, int n>
0157   ALPAKA_FN_ACC ALPAKA_FN_INLINE void __attribute__((always_inline)) prepareBrokenLineData(
0158       const TAcc& acc, const M3xN& hits, const V4& fast_fit, const double bField, PreparedBrokenLineData<n>& results) {
0159     riemannFit::Vector2d dVec;
0160     riemannFit::Vector2d eVec;
0161 
0162     int mId = 1;
0163 
0164     if constexpr (n > 3) {
0165       riemannFit::Vector2d middle = 0.5 * (hits.block(0, n - 1, 2, 1) + hits.block(0, 0, 2, 1));
0166       auto d1 = (hits.block(0, n / 2, 2, 1) - middle).squaredNorm();
0167       auto d2 = (hits.block(0, n / 2 - 1, 2, 1) - middle).squaredNorm();
0168       mId = d1 < d2 ? n / 2 : n / 2 - 1;
0169     }
0170 
0171     dVec = hits.block(0, mId, 2, 1) - hits.block(0, 0, 2, 1);
0172     eVec = hits.block(0, n - 1, 2, 1) - hits.block(0, mId, 2, 1);
0173     results.qCharge = riemannFit::cross2D(acc, dVec, eVec) > 0 ? -1 : 1;
0174 
0175     const double slope = -results.qCharge / fast_fit(3);
0176 
0177     riemannFit::Matrix2d rotMat = rotationMatrix(acc, slope);
0178 
0179     // calculate radii and s
0180     results.radii = hits.block(0, 0, 2, n) - fast_fit.head(2) * riemannFit::MatrixXd::Constant(1, n, 1);
0181     eVec = -fast_fit(2) * fast_fit.head(2) / fast_fit.head(2).norm();
0182     for (u_int i = 0; i < n; i++) {
0183       dVec = results.radii.block(0, i, 2, 1);
0184       results.sTransverse(i) =
0185           results.qCharge * fast_fit(2) *
0186           alpaka::math::atan2(acc, riemannFit::cross2D(acc, dVec, eVec), dVec.dot(eVec));  // calculates the arc length
0187     }
0188     riemannFit::VectorNd<n> zVec = hits.block(2, 0, 1, n).transpose();
0189 
0190     //calculate sTotal and zVec
0191     riemannFit::Matrix2xNd<n> pointsSZ = riemannFit::Matrix2xNd<n>::Zero();
0192     for (u_int i = 0; i < n; i++) {
0193       pointsSZ(0, i) = results.sTransverse(i);
0194       pointsSZ(1, i) = zVec(i);
0195       pointsSZ.block(0, i, 2, 1) = rotMat * pointsSZ.block(0, i, 2, 1);
0196     }
0197     results.sTotal = pointsSZ.block(0, 0, 1, n).transpose();
0198     results.zInSZplane = pointsSZ.block(1, 0, 1, n).transpose();
0199 
0200     //calculate varBeta
0201     results.varBeta(0) = results.varBeta(n - 1) = 0;
0202     for (u_int i = 1; i < n - 1; i++) {
0203       results.varBeta(i) =
0204           multScatt(acc, results.sTotal(i + 1) - results.sTotal(i), bField, fast_fit(2), i + 2, slope) +
0205           multScatt(acc, results.sTotal(i) - results.sTotal(i - 1), bField, fast_fit(2), i + 1, slope);
0206     }
0207   }
0208 
0209   /*!
0210     \brief Computes the n-by-n band matrix obtained minimizing the Broken Line's cost function w.r.t u. 
0211    *       This is the whole matrix in the case of the line fit and the main n-by-n block in the case 
0212    *       of the circle fit.
0213     
0214     \param weights weights of the first part of the cost function, the one with the measurements 
0215    *         and not the angles (\sum_{i=1}^n w*(y_i-u_i)^2).
0216     \param sTotal total distance traveled by the particle from the pre-fitted closest approach.
0217     \param varBeta kink angles' variance.
0218     
0219     \return the n-by-n matrix of the linear system
0220   */
0221   template <typename TAcc, int n>
0222   ALPAKA_FN_ACC ALPAKA_FN_INLINE riemannFit::MatrixNd<n> matrixC_u(const TAcc& acc,
0223                                                                    const riemannFit::VectorNd<n>& weights,
0224                                                                    const riemannFit::VectorNd<n>& sTotal,
0225                                                                    const riemannFit::VectorNd<n>& varBeta) {
0226     riemannFit::MatrixNd<n> c_uMat = riemannFit::MatrixNd<n>::Zero();
0227     for (u_int i = 0; i < n; i++) {
0228       c_uMat(i, i) = weights(i);
0229       if (i > 1)
0230         c_uMat(i, i) += 1. / (varBeta(i - 1) * riemannFit::sqr(sTotal(i) - sTotal(i - 1)));
0231       if (i > 0 && i < n - 1)
0232         c_uMat(i, i) +=
0233             (1. / varBeta(i)) * riemannFit::sqr((sTotal(i + 1) - sTotal(i - 1)) /
0234                                                 ((sTotal(i + 1) - sTotal(i)) * (sTotal(i) - sTotal(i - 1))));
0235       if (i < n - 2)
0236         c_uMat(i, i) += 1. / (varBeta(i + 1) * riemannFit::sqr(sTotal(i + 1) - sTotal(i)));
0237 
0238       if (i > 0 && i < n - 1)
0239         c_uMat(i, i + 1) =
0240             1. / (varBeta(i) * (sTotal(i + 1) - sTotal(i))) *
0241             (-(sTotal(i + 1) - sTotal(i - 1)) / ((sTotal(i + 1) - sTotal(i)) * (sTotal(i) - sTotal(i - 1))));
0242       if (i < n - 2)
0243         c_uMat(i, i + 1) +=
0244             1. / (varBeta(i + 1) * (sTotal(i + 1) - sTotal(i))) *
0245             (-(sTotal(i + 2) - sTotal(i)) / ((sTotal(i + 2) - sTotal(i + 1)) * (sTotal(i + 1) - sTotal(i))));
0246 
0247       if (i < n - 2)
0248         c_uMat(i, i + 2) = 1. / (varBeta(i + 1) * (sTotal(i + 2) - sTotal(i + 1)) * (sTotal(i + 1) - sTotal(i)));
0249 
0250       c_uMat(i, i) *= 0.5;
0251     }
0252     return c_uMat + c_uMat.transpose();
0253   }
0254 
0255   /*!
0256     \brief A very fast helix fit.
0257     
0258     \param hits the measured hits.
0259     
0260     \return (X0,Y0,R,tan(theta)).
0261     
0262     \warning sign of theta is (intentionally, for now) mistaken for negative charges.
0263   */
0264 
0265   template <typename TAcc, typename M3xN, typename V4>
0266   ALPAKA_FN_ACC ALPAKA_FN_INLINE void fastFit(const TAcc& acc, const M3xN& hits, V4& result) {
0267     constexpr uint32_t n = M3xN::ColsAtCompileTime;
0268 
0269     int mId = 1;
0270 
0271     if constexpr (n > 3) {
0272       riemannFit::Vector2d middle = 0.5 * (hits.block(0, n - 1, 2, 1) + hits.block(0, 0, 2, 1));
0273       auto d1 = (hits.block(0, n / 2, 2, 1) - middle).squaredNorm();
0274       auto d2 = (hits.block(0, n / 2 - 1, 2, 1) - middle).squaredNorm();
0275       mId = d1 < d2 ? n / 2 : n / 2 - 1;
0276     }
0277 
0278     const riemannFit::Vector2d a = hits.block(0, mId, 2, 1) - hits.block(0, 0, 2, 1);
0279     const riemannFit::Vector2d b = hits.block(0, n - 1, 2, 1) - hits.block(0, mId, 2, 1);
0280     const riemannFit::Vector2d c = hits.block(0, 0, 2, 1) - hits.block(0, n - 1, 2, 1);
0281 
0282     auto tmp = 0.5 / riemannFit::cross2D(acc, c, a);
0283     result(0) = hits(0, 0) - (a(1) * c.squaredNorm() + c(1) * a.squaredNorm()) * tmp;
0284     result(1) = hits(1, 0) + (a(0) * c.squaredNorm() + c(0) * a.squaredNorm()) * tmp;
0285     // check Wikipedia for these formulas
0286 
0287     result(2) = alpaka::math::sqrt(acc, a.squaredNorm() * b.squaredNorm() * c.squaredNorm()) /
0288                 (2. * alpaka::math::abs(acc, riemannFit::cross2D(acc, b, a)));
0289     // Using Math Olympiad's formula R=abc/(4A)
0290 
0291     const riemannFit::Vector2d d = hits.block(0, 0, 2, 1) - result.head(2);
0292     const riemannFit::Vector2d e = hits.block(0, n - 1, 2, 1) - result.head(2);
0293 
0294     result(3) = result(2) * atan2(riemannFit::cross2D(acc, d, e), d.dot(e)) / (hits(2, n - 1) - hits(2, 0));
0295     // ds/dz slope between last and first point
0296   }
0297 
0298   /*!
0299     \brief Performs the Broken Line fit in the curved track case (that is, the fit 
0300    *       parameters are the interceptions u and the curvature correction \Delta\kappa).
0301     
0302     \param hits hits coordinates.
0303     \param hits_cov hits covariance matrix.
0304     \param fast_fit pre-fit result in the form (X0,Y0,R,tan(theta)).
0305     \param bField magnetic field in Gev/cm/c.
0306     \param data PreparedBrokenLineData.
0307     \param circle_results struct to be filled with the results in this form:
0308     -par parameter of the line in this form: (phi, d, k); \n
0309     -cov covariance matrix of the fitted parameter; \n
0310     -chi2 value of the cost function in the minimum.
0311     
0312     \details The function implements the steps 2 and 3 of the Broken Line fit 
0313    *         with the curvature correction.\n
0314    * The step 2 is the least square fit, done by imposing the minimum constraint on 
0315    * the cost function and solving the consequent linear system. It determines the 
0316    * fitted parameters u and \Delta\kappa and their covariance matrix.
0317    * The step 3 is the correction of the fast pre-fitted parameters for the innermost 
0318    * part of the track. It is first done in a comfortable coordinate system (the one 
0319    * in which the first hit is the origin) and then the parameters and their 
0320    * covariance matrix are transformed to the original coordinate system.
0321   */
0322   template <typename TAcc, typename M3xN, typename M6xN, typename V4, int n>
0323   ALPAKA_FN_ACC ALPAKA_FN_INLINE void circleFit(const TAcc& acc,
0324                                                 const M3xN& hits,
0325                                                 const M6xN& hits_ge,
0326                                                 const V4& fast_fit,
0327                                                 const double bField,
0328                                                 PreparedBrokenLineData<n>& data,
0329                                                 karimaki_circle_fit& circle_results) {
0330     circle_results.qCharge = data.qCharge;
0331     auto& radii = data.radii;
0332     const auto& sTransverse = data.sTransverse;
0333     const auto& sTotal = data.sTotal;
0334     auto& zInSZplane = data.zInSZplane;
0335     auto& varBeta = data.varBeta;
0336     const double slope = -circle_results.qCharge / fast_fit(3);
0337     varBeta *= 1. + riemannFit::sqr(slope);  // the kink angles are projected!
0338 
0339     for (u_int i = 0; i < n; i++) {
0340       zInSZplane(i) = radii.block(0, i, 2, 1).norm() - fast_fit(2);
0341     }
0342 
0343     riemannFit::Matrix2d vMat;           // covariance matrix
0344     riemannFit::VectorNd<n> weightsVec;  // weights
0345     riemannFit::Matrix2d rotMat;         // rotation matrix point by point
0346     for (u_int i = 0; i < n; i++) {
0347       vMat(0, 0) = hits_ge.col(i)[0];               // x errors
0348       vMat(0, 1) = vMat(1, 0) = hits_ge.col(i)[1];  // cov_xy
0349       vMat(1, 1) = hits_ge.col(i)[2];               // y errors
0350       rotMat = rotationMatrix(acc, -radii(0, i) / radii(1, i));
0351       weightsVec(i) =
0352           1. / ((rotMat * vMat * rotMat.transpose())(1, 1));  // compute the orthogonal weight point by point
0353     }
0354 
0355     riemannFit::VectorNplusONEd<n> r_uVec;
0356     r_uVec(n) = 0;
0357     for (u_int i = 0; i < n; i++) {
0358       r_uVec(i) = weightsVec(i) * zInSZplane(i);
0359     }
0360 
0361     riemannFit::MatrixNplusONEd<n> c_uMat;
0362     c_uMat.block(0, 0, n, n) = matrixC_u(acc, weightsVec, sTransverse, varBeta);
0363     c_uMat(n, n) = 0;
0364     //add the border to the c_uMat matrix
0365     for (u_int i = 0; i < n; i++) {
0366       c_uMat(i, n) = 0;
0367       if (i > 0 && i < n - 1) {
0368         c_uMat(i, n) +=
0369             -(sTransverse(i + 1) - sTransverse(i - 1)) * (sTransverse(i + 1) - sTransverse(i - 1)) /
0370             (2. * varBeta(i) * (sTransverse(i + 1) - sTransverse(i)) * (sTransverse(i) - sTransverse(i - 1)));
0371       }
0372       if (i > 1) {
0373         c_uMat(i, n) +=
0374             (sTransverse(i) - sTransverse(i - 2)) / (2. * varBeta(i - 1) * (sTransverse(i) - sTransverse(i - 1)));
0375       }
0376       if (i < n - 2) {
0377         c_uMat(i, n) +=
0378             (sTransverse(i + 2) - sTransverse(i)) / (2. * varBeta(i + 1) * (sTransverse(i + 1) - sTransverse(i)));
0379       }
0380       c_uMat(n, i) = c_uMat(i, n);
0381       if (i > 0 && i < n - 1)
0382         c_uMat(n, n) += riemannFit::sqr(sTransverse(i + 1) - sTransverse(i - 1)) / (4. * varBeta(i));
0383     }
0384 
0385 #ifdef CPP_DUMP
0386     std::cout << "CU5\n" << c_uMat << std::endl;
0387 #endif
0388     riemannFit::MatrixNplusONEd<n> iMat;
0389     math::cholesky::invert(c_uMat, iMat);
0390 #ifdef CPP_DUMP
0391     std::cout << "I5\n" << iMat << std::endl;
0392 #endif
0393     riemannFit::VectorNplusONEd<n> uVec = iMat * r_uVec;  // obtain the fitted parameters by solving the linear system
0394 
0395     // compute (phi, d_ca, k) in the system in which the midpoint of the first two corrected hits is the origin...
0396 
0397     radii.block(0, 0, 2, 1) /= radii.block(0, 0, 2, 1).norm();
0398     radii.block(0, 1, 2, 1) /= radii.block(0, 1, 2, 1).norm();
0399 
0400     riemannFit::Vector2d dVec = hits.block(0, 0, 2, 1) + (-zInSZplane(0) + uVec(0)) * radii.block(0, 0, 2, 1);
0401     riemannFit::Vector2d eVec = hits.block(0, 1, 2, 1) + (-zInSZplane(1) + uVec(1)) * radii.block(0, 1, 2, 1);
0402     auto eMinusd = eVec - dVec;
0403     auto eMinusd2 = eMinusd.squaredNorm();
0404     auto tmp1 = 1. / eMinusd2;
0405     auto tmp2 = alpaka::math::sqrt(acc, riemannFit::sqr(fast_fit(2)) - 0.25 * eMinusd2);
0406 
0407     circle_results.par << atan2(eMinusd(1), eMinusd(0)), circle_results.qCharge * (tmp2 - fast_fit(2)),
0408         circle_results.qCharge * (1. / fast_fit(2) + uVec(n));
0409 
0410     tmp2 = 1. / tmp2;
0411 
0412     riemannFit::Matrix3d jacobian;
0413     jacobian << (radii(1, 0) * eMinusd(0) - eMinusd(1) * radii(0, 0)) * tmp1,
0414         (radii(1, 1) * eMinusd(0) - eMinusd(1) * radii(0, 1)) * tmp1, 0,
0415         circle_results.qCharge * (eMinusd(0) * radii(0, 0) + eMinusd(1) * radii(1, 0)) * tmp2,
0416         circle_results.qCharge * (eMinusd(0) * radii(0, 1) + eMinusd(1) * radii(1, 1)) * tmp2, 0, 0, 0,
0417         circle_results.qCharge;
0418 
0419     circle_results.cov << iMat(0, 0), iMat(0, 1), iMat(0, n), iMat(1, 0), iMat(1, 1), iMat(1, n), iMat(n, 0),
0420         iMat(n, 1), iMat(n, n);
0421 
0422     circle_results.cov = jacobian * circle_results.cov * jacobian.transpose();
0423 
0424     //...Translate in the system in which the first corrected hit is the origin, adding the m.s. correction...
0425 
0426     translateKarimaki(acc, circle_results, 0.5 * eMinusd(0), 0.5 * eMinusd(1), jacobian);
0427     circle_results.cov(0, 0) +=
0428         (1 + riemannFit::sqr(slope)) * multScatt(acc, sTotal(1) - sTotal(0), bField, fast_fit(2), 2, slope);
0429 
0430     //...And translate back to the original system
0431 
0432     translateKarimaki(acc, circle_results, dVec(0), dVec(1), jacobian);
0433 
0434     // compute chi2
0435     circle_results.chi2 = 0;
0436     for (u_int i = 0; i < n; i++) {
0437       circle_results.chi2 += weightsVec(i) * riemannFit::sqr(zInSZplane(i) - uVec(i));
0438       if (i > 0 && i < n - 1)
0439         circle_results.chi2 +=
0440             riemannFit::sqr(uVec(i - 1) / (sTransverse(i) - sTransverse(i - 1)) -
0441                             uVec(i) * (sTransverse(i + 1) - sTransverse(i - 1)) /
0442                                 ((sTransverse(i + 1) - sTransverse(i)) * (sTransverse(i) - sTransverse(i - 1))) +
0443                             uVec(i + 1) / (sTransverse(i + 1) - sTransverse(i)) +
0444                             (sTransverse(i + 1) - sTransverse(i - 1)) * uVec(n) / 2) /
0445             varBeta(i);
0446     }
0447   }
0448 
0449   /*!
0450     \brief Performs the Broken Line fit in the straight track case (that is, the fit parameters are only the interceptions u).
0451     
0452     \param hits hits coordinates.
0453     \param fast_fit pre-fit result in the form (X0,Y0,R,tan(theta)).
0454     \param bField magnetic field in Gev/cm/c.
0455     \param data PreparedBrokenLineData.
0456     \param line_results struct to be filled with the results in this form:
0457     -par parameter of the line in this form: (cot(theta), Zip); \n
0458     -cov covariance matrix of the fitted parameter; \n
0459     -chi2 value of the cost function in the minimum.
0460     
0461     \details The function implements the steps 2 and 3 of the Broken Line fit without 
0462    *        the curvature correction.\n
0463    * The step 2 is the least square fit, done by imposing the minimum constraint 
0464    * on the cost function and solving the consequent linear system. It determines 
0465    * the fitted parameters u and their covariance matrix.
0466    * The step 3 is the correction of the fast pre-fitted parameters for the innermost 
0467    * part of the track. It is first done in a comfortable coordinate system (the one 
0468    * in which the first hit is the origin) and then the parameters and their covariance 
0469    * matrix are transformed to the original coordinate system.
0470    */
0471   template <typename TAcc, typename V4, typename M6xN, int n>
0472   ALPAKA_FN_ACC ALPAKA_FN_INLINE void lineFit(const TAcc& acc,
0473                                               const M6xN& hits_ge,
0474                                               const V4& fast_fit,
0475                                               const double bField,
0476                                               const PreparedBrokenLineData<n>& data,
0477                                               riemannFit::LineFit& line_results) {
0478     const auto& radii = data.radii;
0479     const auto& sTotal = data.sTotal;
0480     const auto& zInSZplane = data.zInSZplane;
0481     const auto& varBeta = data.varBeta;
0482 
0483     const double slope = -data.qCharge / fast_fit(3);
0484     riemannFit::Matrix2d rotMat = rotationMatrix(acc, slope);
0485 
0486     riemannFit::Matrix3d vMat = riemannFit::Matrix3d::Zero();  // covariance matrix XYZ
0487     riemannFit::Matrix2x3d jacobXYZtosZ =
0488         riemannFit::Matrix2x3d::Zero();  // jacobian for computation of the error on s (xyz -> sz)
0489     riemannFit::VectorNd<n> weights = riemannFit::VectorNd<n>::Zero();
0490     for (u_int i = 0; i < n; i++) {
0491       vMat(0, 0) = hits_ge.col(i)[0];               // x errors
0492       vMat(0, 1) = vMat(1, 0) = hits_ge.col(i)[1];  // cov_xy
0493       vMat(0, 2) = vMat(2, 0) = hits_ge.col(i)[3];  // cov_xz
0494       vMat(1, 1) = hits_ge.col(i)[2];               // y errors
0495       vMat(2, 1) = vMat(1, 2) = hits_ge.col(i)[4];  // cov_yz
0496       vMat(2, 2) = hits_ge.col(i)[5];               // z errors
0497       auto tmp = 1. / radii.block(0, i, 2, 1).norm();
0498       jacobXYZtosZ(0, 0) = radii(1, i) * tmp;
0499       jacobXYZtosZ(0, 1) = -radii(0, i) * tmp;
0500       jacobXYZtosZ(1, 2) = 1.;
0501       weights(i) = 1. / ((rotMat * jacobXYZtosZ * vMat * jacobXYZtosZ.transpose() * rotMat.transpose())(
0502                             1, 1));  // compute the orthogonal weight point by point
0503     }
0504 
0505     riemannFit::VectorNd<n> r_u;
0506     for (u_int i = 0; i < n; i++) {
0507       r_u(i) = weights(i) * zInSZplane(i);
0508     }
0509 #ifdef CPP_DUMP
0510     std::cout << "CU4\n" << matrixC_u(w, sTotal, varBeta) << std::endl;
0511 #endif
0512     riemannFit::MatrixNd<n> iMat;
0513     math::cholesky::invert(matrixC_u(acc, weights, sTotal, varBeta), iMat);
0514 #ifdef CPP_DUMP
0515     std::cout << "I4\n" << iMat << std::endl;
0516 #endif
0517 
0518     riemannFit::VectorNd<n> uVec = iMat * r_u;  // obtain the fitted parameters by solving the linear system
0519 
0520     // line parameters in the system in which the first hit is the origin and with axis along SZ
0521     line_results.par << (uVec(1) - uVec(0)) / (sTotal(1) - sTotal(0)), uVec(0);
0522     auto idiff = 1. / (sTotal(1) - sTotal(0));
0523     line_results.cov << (iMat(0, 0) - 2 * iMat(0, 1) + iMat(1, 1)) * riemannFit::sqr(idiff) +
0524                             multScatt(acc, sTotal(1) - sTotal(0), bField, fast_fit(2), 2, slope),
0525         (iMat(0, 1) - iMat(0, 0)) * idiff, (iMat(0, 1) - iMat(0, 0)) * idiff, iMat(0, 0);
0526 
0527     // translate to the original SZ system
0528     riemannFit::Matrix2d jacobian;
0529     jacobian(0, 0) = 1.;
0530     jacobian(0, 1) = 0;
0531     jacobian(1, 0) = -sTotal(0);
0532     jacobian(1, 1) = 1.;
0533     line_results.par(1) += -line_results.par(0) * sTotal(0);
0534     line_results.cov = jacobian * line_results.cov * jacobian.transpose();
0535 
0536     // rotate to the original sz system
0537     auto tmp = rotMat(0, 0) - line_results.par(0) * rotMat(0, 1);
0538     jacobian(1, 1) = 1. / tmp;
0539     jacobian(0, 0) = jacobian(1, 1) * jacobian(1, 1);
0540     jacobian(0, 1) = 0;
0541     jacobian(1, 0) = line_results.par(1) * rotMat(0, 1) * jacobian(0, 0);
0542     line_results.par(1) = line_results.par(1) * jacobian(1, 1);
0543     line_results.par(0) = (rotMat(0, 1) + line_results.par(0) * rotMat(0, 0)) * jacobian(1, 1);
0544     line_results.cov = jacobian * line_results.cov * jacobian.transpose();
0545 
0546     // compute chi2
0547     line_results.chi2 = 0;
0548     for (u_int i = 0; i < n; i++) {
0549       line_results.chi2 += weights(i) * riemannFit::sqr(zInSZplane(i) - uVec(i));
0550       if (i > 0 && i < n - 1)
0551         line_results.chi2 += riemannFit::sqr(uVec(i - 1) / (sTotal(i) - sTotal(i - 1)) -
0552                                              uVec(i) * (sTotal(i + 1) - sTotal(i - 1)) /
0553                                                  ((sTotal(i + 1) - sTotal(i)) * (sTotal(i) - sTotal(i - 1))) +
0554                                              uVec(i + 1) / (sTotal(i + 1) - sTotal(i))) /
0555                              varBeta(i);
0556     }
0557   }
0558 
0559   /*!
0560     \brief Helix fit by three step:
0561     -fast pre-fit (see Fast_fit() for further info); \n
0562     -circle fit of the hits projected in the transverse plane by Broken Line algorithm (see BL_Circle_fit() for further info); \n
0563     -line fit of the hits projected on the (pre-fitted) cilinder surface by Broken Line algorithm (see BL_Line_fit() for further info); \n
0564     Points must be passed ordered (from inner to outer layer).
0565     
0566     \param hits Matrix3xNd hits coordinates in this form: \n
0567     |x1|x2|x3|...|xn| \n
0568     |y1|y2|y3|...|yn| \n
0569     |z1|z2|z3|...|zn|
0570     \param hits_cov Matrix3Nd covariance matrix in this form (()->cov()): \n
0571     |(x1,x1)|(x2,x1)|(x3,x1)|(x4,x1)|.|(y1,x1)|(y2,x1)|(y3,x1)|(y4,x1)|.|(z1,x1)|(z2,x1)|(z3,x1)|(z4,x1)| \n
0572     |(x1,x2)|(x2,x2)|(x3,x2)|(x4,x2)|.|(y1,x2)|(y2,x2)|(y3,x2)|(y4,x2)|.|(z1,x2)|(z2,x2)|(z3,x2)|(z4,x2)| \n
0573     |(x1,x3)|(x2,x3)|(x3,x3)|(x4,x3)|.|(y1,x3)|(y2,x3)|(y3,x3)|(y4,x3)|.|(z1,x3)|(z2,x3)|(z3,x3)|(z4,x3)| \n
0574     |(x1,x4)|(x2,x4)|(x3,x4)|(x4,x4)|.|(y1,x4)|(y2,x4)|(y3,x4)|(y4,x4)|.|(z1,x4)|(z2,x4)|(z3,x4)|(z4,x4)| \n
0575     .       .       .       .       . .       .       .       .       . .       .       .       .       . \n
0576     |(x1,y1)|(x2,y1)|(x3,y1)|(x4,y1)|.|(y1,y1)|(y2,y1)|(y3,x1)|(y4,y1)|.|(z1,y1)|(z2,y1)|(z3,y1)|(z4,y1)| \n
0577     |(x1,y2)|(x2,y2)|(x3,y2)|(x4,y2)|.|(y1,y2)|(y2,y2)|(y3,x2)|(y4,y2)|.|(z1,y2)|(z2,y2)|(z3,y2)|(z4,y2)| \n
0578     |(x1,y3)|(x2,y3)|(x3,y3)|(x4,y3)|.|(y1,y3)|(y2,y3)|(y3,x3)|(y4,y3)|.|(z1,y3)|(z2,y3)|(z3,y3)|(z4,y3)| \n
0579     |(x1,y4)|(x2,y4)|(x3,y4)|(x4,y4)|.|(y1,y4)|(y2,y4)|(y3,x4)|(y4,y4)|.|(z1,y4)|(z2,y4)|(z3,y4)|(z4,y4)| \n
0580     .       .       .    .          . .       .       .       .       . .       .       .       .       . \n
0581     |(x1,z1)|(x2,z1)|(x3,z1)|(x4,z1)|.|(y1,z1)|(y2,z1)|(y3,z1)|(y4,z1)|.|(z1,z1)|(z2,z1)|(z3,z1)|(z4,z1)| \n
0582     |(x1,z2)|(x2,z2)|(x3,z2)|(x4,z2)|.|(y1,z2)|(y2,z2)|(y3,z2)|(y4,z2)|.|(z1,z2)|(z2,z2)|(z3,z2)|(z4,z2)| \n
0583     |(x1,z3)|(x2,z3)|(x3,z3)|(x4,z3)|.|(y1,z3)|(y2,z3)|(y3,z3)|(y4,z3)|.|(z1,z3)|(z2,z3)|(z3,z3)|(z4,z3)| \n
0584     |(x1,z4)|(x2,z4)|(x3,z4)|(x4,z4)|.|(y1,z4)|(y2,z4)|(y3,z4)|(y4,z4)|.|(z1,z4)|(z2,z4)|(z3,z4)|(z4,z4)|
0585     \param bField magnetic field in the center of the detector in Gev/cm/c, in order to perform the p_t calculation.
0586     
0587     \warning see BL_Circle_fit(), BL_Line_fit() and Fast_fit() warnings.
0588     
0589     \bug see BL_Circle_fit(), BL_Line_fit() and Fast_fit() bugs.
0590     
0591     \return (phi,Tip,p_t,cot(theta)),Zip), their covariance matrix and the chi2's of the circle and line fits.
0592   */
0593 
0594   template <int n>
0595   class helixFit {
0596   public:
0597     template <typename TAcc>
0598     ALPAKA_FN_ACC ALPAKA_FN_INLINE void operator()(const TAcc& acc,
0599                                                    const riemannFit::Matrix3xNd<n>* hits,
0600                                                    const Eigen::Matrix<float, 6, 4>* hits_ge,
0601                                                    const double bField,
0602                                                    riemannFit::HelixFit* helix) const {
0603       riemannFit::Vector4d fast_fit;
0604       fastFit(acc, *hits, fast_fit);
0605 
0606       PreparedBrokenLineData<n> data;
0607       karimaki_circle_fit circle;
0608       riemannFit::LineFit line;
0609       riemannFit::Matrix3d jacobian;
0610 
0611       prepareBrokenLineData(acc, *hits, fast_fit, bField, data);
0612       lineFit(acc, *hits_ge, fast_fit, bField, data, line);
0613       circleFit(acc, *hits, *hits_ge, fast_fit, bField, data, circle);
0614 
0615       // the circle fit gives k, but here we want p_t, so let's change the parameter and the covariance matrix
0616       jacobian << 1., 0, 0, 0, 1., 0, 0, 0,
0617           -alpaka::math::abs(acc, circle.par(2)) * bField / (riemannFit::sqr(circle.par(2)) * circle.par(2));
0618       circle.par(2) = bField / alpaka::math::abs(acc, circle.par(2));
0619       circle.cov = jacobian * circle.cov * jacobian.transpose();
0620 
0621       helix->par << circle.par, line.par;
0622       helix->cov = riemannFit::MatrixXd::Zero(5, 5);
0623       helix->cov.block(0, 0, 3, 3) = circle.cov;
0624       helix->cov.block(3, 3, 2, 2) = line.cov;
0625       helix->qCharge = circle.qCharge;
0626       helix->chi2_circle = circle.chi2;
0627       helix->chi2_line = line.chi2;
0628     }
0629   };
0630 
0631 }  // namespace ALPAKA_ACCELERATOR_NAMESPACE::brokenline
0632 
0633 #endif  // RecoTracker_PixelTrackFitting_interface_alpaka_BrokenLine_h