Back to home page

Project CMSSW displayed by LXR

 
 

    


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

0001 #ifndef RecoTracker_PixelTrackFitting_RiemannFit_h
0002 #define RecoTracker_PixelTrackFitting_RiemannFit_h
0003 
0004 #include <Eigen/Core>
0005 #include <Eigen/Eigenvalues>
0006 
0007 #include "RecoTracker/PixelTrackFitting/interface/FitUtils.h"
0008 
0009 namespace riemannFit {
0010 
0011   /*!  Compute the Radiation length in the uniform hypothesis
0012  *
0013  * The Pixel detector, barrel and forward, is considered as an homogeneous
0014  * cylinder of material, whose radiation lengths has been derived from the TDR
0015  * plot that shows that 16cm correspond to 0.06 radiation lengths. Therefore
0016  * one radiation length corresponds to 16cm/0.06 =~ 267 cm. All radiation
0017  * lengths are computed using this unique number, in both regions, barrel and
0018  * endcap.
0019  *
0020  * NB: no angle corrections nor projections are computed inside this routine.
0021  * It is therefore the responsibility of the caller to supply the proper
0022  * lengths in input. These lengths are the path traveled by the particle along
0023  * its trajectory, namely the so called S of the helix in 3D space.
0024  *
0025  * \param length_values vector of incremental distances that will be translated
0026  * into radiation length equivalent. Each radiation length i is computed
0027  * incrementally with respect to the previous length i-1. The first length has
0028  * no reference point (i.e. it has the dca).
0029  *
0030  * \return incremental radiation lengths that correspond to each segment.
0031  */
0032 
0033   template <typename VNd1, typename VNd2>
0034   __host__ __device__ inline void computeRadLenUniformMaterial(const VNd1& length_values, VNd2& rad_lengths) {
0035     // Radiation length of the pixel detector in the uniform assumption, with
0036     // 0.06 rad_len at 16 cm
0037     constexpr double xx_0_inv = 0.06 / 16.;
0038     uint n = length_values.rows();
0039     rad_lengths(0) = length_values(0) * xx_0_inv;
0040     for (uint j = 1; j < n; ++j) {
0041       rad_lengths(j) = std::abs(length_values(j) - length_values(j - 1)) * xx_0_inv;
0042     }
0043   }
0044 
0045   /*!
0046     \brief Compute the covariance matrix along cartesian S-Z of points due to
0047     multiple Coulomb scattering to be used in the line_fit, for the barrel
0048     and forward cases.
0049     The input covariance matrix is in the variables s-z, original and
0050     unrotated.
0051     The multiple scattering component is computed in the usual linear
0052     approximation, using the 3D path which is computed as the squared root of
0053     the squared sum of the s and z components passed in.
0054     Internally a rotation by theta is performed and the covariance matrix
0055     returned is the one in the direction orthogonal to the rotated S3D axis,
0056     i.e. along the rotated Z axis.
0057     The choice of the rotation is not arbitrary, but derived from the fact that
0058     putting the horizontal axis along the S3D direction allows the usage of the
0059     ordinary least squared fitting techiques with the trivial parametrization y
0060     = mx + q, avoiding the patological case with m = +/- inf, that would
0061     correspond to the case at eta = 0.
0062  */
0063 
0064   template <typename V4, typename VNd1, typename VNd2, int N>
0065   __host__ __device__ inline auto scatterCovLine(Matrix2d const* cov_sz,
0066                                                  const V4& fast_fit,
0067                                                  VNd1 const& s_arcs,
0068                                                  VNd2 const& z_values,
0069                                                  const double theta,
0070                                                  const double bField,
0071                                                  MatrixNd<N>& ret) {
0072 #ifdef RFIT_DEBUG
0073     riemannFit::printIt(&s_arcs, "Scatter_cov_line - s_arcs: ");
0074 #endif
0075     constexpr uint n = N;
0076     double p_t = std::min(20., fast_fit(2) * bField);  // limit pt to avoid too small error!!!
0077     double p_2 = p_t * p_t * (1. + 1. / sqr(fast_fit(3)));
0078     VectorNd<N> rad_lengths_S;
0079     // See documentation at http://eigen.tuxfamily.org/dox/group__TutorialArrayClass.html
0080     // Basically, to perform cwise operations on Matrices and Vectors, you need
0081     // to transform them into Array-like objects.
0082     VectorNd<N> s_values = s_arcs.array() * s_arcs.array() + z_values.array() * z_values.array();
0083     s_values = s_values.array().sqrt();
0084     computeRadLenUniformMaterial(s_values, rad_lengths_S);
0085     VectorNd<N> sig2_S;
0086     sig2_S = .000225 / p_2 * (1. + 0.038 * rad_lengths_S.array().log()).abs2() * rad_lengths_S.array();
0087 #ifdef RFIT_DEBUG
0088     riemannFit::printIt(cov_sz, "Scatter_cov_line - cov_sz: ");
0089 #endif
0090     Matrix2Nd<N> tmp = Matrix2Nd<N>::Zero();
0091     for (uint k = 0; k < n; ++k) {
0092       tmp(k, k) = cov_sz[k](0, 0);
0093       tmp(k + n, k + n) = cov_sz[k](1, 1);
0094       tmp(k, k + n) = tmp(k + n, k) = cov_sz[k](0, 1);
0095     }
0096     for (uint k = 0; k < n; ++k) {
0097       for (uint l = k; l < n; ++l) {
0098         for (uint i = 0; i < std::min(k, l); ++i) {
0099           tmp(k + n, l + n) += std::abs(s_values(k) - s_values(i)) * std::abs(s_values(l) - s_values(i)) * sig2_S(i);
0100         }
0101         tmp(l + n, k + n) = tmp(k + n, l + n);
0102       }
0103     }
0104     // We are interested only in the errors orthogonal to the rotated s-axis
0105     // which, in our formalism, are in the lower square matrix.
0106 #ifdef RFIT_DEBUG
0107     riemannFit::printIt(&tmp, "Scatter_cov_line - tmp: ");
0108 #endif
0109     ret = tmp.block(n, n, n, n);
0110   }
0111 
0112   /*!
0113     \brief Compute the covariance matrix (in radial coordinates) of points in
0114     the transverse plane due to multiple Coulomb scattering.
0115     \param p2D 2D points in the transverse plane.
0116     \param fast_fit fast_fit Vector4d result of the previous pre-fit
0117     structured in this form:(X0, Y0, R, Tan(Theta))).
0118     \param B magnetic field use to compute p
0119     \return scatter_cov_rad errors due to multiple scattering.
0120     \warning input points must be ordered radially from the detector center
0121     (from inner layer to outer ones; points on the same layer must ordered too).
0122     \details Only the tangential component is computed (the radial one is
0123     negligible).
0124  */
0125   template <typename M2xN, typename V4, int N>
0126   __host__ __device__ inline MatrixNd<N> scatter_cov_rad(const M2xN& p2D,
0127                                                          const V4& fast_fit,
0128                                                          VectorNd<N> const& rad,
0129                                                          double B) {
0130     constexpr uint n = N;
0131     double p_t = std::min(20., fast_fit(2) * B);  // limit pt to avoid too small error!!!
0132     double p_2 = p_t * p_t * (1. + 1. / sqr(fast_fit(3)));
0133     double theta = atan(fast_fit(3));
0134     theta = theta < 0. ? theta + M_PI : theta;
0135     VectorNd<N> s_values;
0136     VectorNd<N> rad_lengths;
0137     const Vector2d oVec(fast_fit(0), fast_fit(1));
0138 
0139     // associated Jacobian, used in weights and errors computation
0140     for (uint i = 0; i < n; ++i) {  // x
0141       Vector2d pVec = p2D.block(0, i, 2, 1) - oVec;
0142       const double cross = cross2D(-oVec, pVec);
0143       const double dot = (-oVec).dot(pVec);
0144       const double tempAtan2 = atan2(cross, dot);
0145       s_values(i) = std::abs(tempAtan2 * fast_fit(2));
0146     }
0147     computeRadLenUniformMaterial(s_values * sqrt(1. + 1. / sqr(fast_fit(3))), rad_lengths);
0148     MatrixNd<N> scatter_cov_rad = MatrixNd<N>::Zero();
0149     VectorNd<N> sig2 = (1. + 0.038 * rad_lengths.array().log()).abs2() * rad_lengths.array();
0150     sig2 *= 0.000225 / (p_2 * sqr(sin(theta)));
0151     for (uint k = 0; k < n; ++k) {
0152       for (uint l = k; l < n; ++l) {
0153         for (uint i = 0; i < std::min(k, l); ++i) {
0154           scatter_cov_rad(k, l) += (rad(k) - rad(i)) * (rad(l) - rad(i)) * sig2(i);
0155         }
0156         scatter_cov_rad(l, k) = scatter_cov_rad(k, l);
0157       }
0158     }
0159 #ifdef RFIT_DEBUG
0160     riemannFit::printIt(&scatter_cov_rad, "Scatter_cov_rad - scatter_cov_rad: ");
0161 #endif
0162     return scatter_cov_rad;
0163   }
0164 
0165   /*!
0166     \brief Transform covariance matrix from radial (only tangential component)
0167     to Cartesian coordinates (only transverse plane component).
0168     \param p2D 2D points in the transverse plane.
0169     \param cov_rad covariance matrix in radial coordinate.
0170     \return cov_cart covariance matrix in Cartesian coordinates.
0171 */
0172 
0173   template <typename M2xN, int N>
0174   __host__ __device__ inline Matrix2Nd<N> cov_radtocart(const M2xN& p2D,
0175                                                         const MatrixNd<N>& cov_rad,
0176                                                         const VectorNd<N>& rad) {
0177 #ifdef RFIT_DEBUG
0178     printf("Address of p2D: %p\n", &p2D);
0179 #endif
0180     printIt(&p2D, "cov_radtocart - p2D:");
0181     constexpr uint n = N;
0182     Matrix2Nd<N> cov_cart = Matrix2Nd<N>::Zero();
0183     VectorNd<N> rad_inv = rad.cwiseInverse();
0184     printIt(&rad_inv, "cov_radtocart - rad_inv:");
0185     for (uint i = 0; i < n; ++i) {
0186       for (uint j = i; j < n; ++j) {
0187         cov_cart(i, j) = cov_rad(i, j) * p2D(1, i) * rad_inv(i) * p2D(1, j) * rad_inv(j);
0188         cov_cart(i + n, j + n) = cov_rad(i, j) * p2D(0, i) * rad_inv(i) * p2D(0, j) * rad_inv(j);
0189         cov_cart(i, j + n) = -cov_rad(i, j) * p2D(1, i) * rad_inv(i) * p2D(0, j) * rad_inv(j);
0190         cov_cart(i + n, j) = -cov_rad(i, j) * p2D(0, i) * rad_inv(i) * p2D(1, j) * rad_inv(j);
0191         cov_cart(j, i) = cov_cart(i, j);
0192         cov_cart(j + n, i + n) = cov_cart(i + n, j + n);
0193         cov_cart(j + n, i) = cov_cart(i, j + n);
0194         cov_cart(j, i + n) = cov_cart(i + n, j);
0195       }
0196     }
0197     return cov_cart;
0198   }
0199 
0200   /*!
0201     \brief Transform covariance matrix from Cartesian coordinates (only
0202     transverse plane component) to radial coordinates (both radial and
0203     tangential component but only diagonal terms, correlation between different
0204     point are not managed).
0205     \param p2D 2D points in transverse plane.
0206     \param cov_cart covariance matrix in Cartesian coordinates.
0207     \return cov_rad covariance matrix in raidal coordinate.
0208     \warning correlation between different point are not computed.
0209 */
0210   template <typename M2xN, int N>
0211   __host__ __device__ inline VectorNd<N> cov_carttorad(const M2xN& p2D,
0212                                                        const Matrix2Nd<N>& cov_cart,
0213                                                        const VectorNd<N>& rad) {
0214     constexpr uint n = N;
0215     VectorNd<N> cov_rad;
0216     const VectorNd<N> rad_inv2 = rad.cwiseInverse().array().square();
0217     for (uint i = 0; i < n; ++i) {
0218       //!< in case you have (0,0) to avoid dividing by 0 radius
0219       if (rad(i) < 1.e-4)
0220         cov_rad(i) = cov_cart(i, i);
0221       else {
0222         cov_rad(i) = rad_inv2(i) * (cov_cart(i, i) * sqr(p2D(1, i)) + cov_cart(i + n, i + n) * sqr(p2D(0, i)) -
0223                                     2. * cov_cart(i, i + n) * p2D(0, i) * p2D(1, i));
0224       }
0225     }
0226     return cov_rad;
0227   }
0228 
0229   /*!
0230     \brief Transform covariance matrix from Cartesian coordinates (only
0231     transverse plane component) to coordinates system orthogonal to the
0232     pre-fitted circle in each point.
0233     Further information in attached documentation.
0234     \param p2D 2D points in transverse plane.
0235     \param cov_cart covariance matrix in Cartesian coordinates.
0236     \param fast_fit fast_fit Vector4d result of the previous pre-fit
0237     structured in this form:(X0, Y0, R, tan(theta))).
0238     \return cov_rad covariance matrix in the pre-fitted circle's
0239     orthogonal system.
0240 */
0241   template <typename M2xN, typename V4, int N>
0242   __host__ __device__ inline VectorNd<N> cov_carttorad_prefit(const M2xN& p2D,
0243                                                               const Matrix2Nd<N>& cov_cart,
0244                                                               V4& fast_fit,
0245                                                               const VectorNd<N>& rad) {
0246     constexpr uint n = N;
0247     VectorNd<N> cov_rad;
0248     for (uint i = 0; i < n; ++i) {
0249       //!< in case you have (0,0) to avoid dividing by 0 radius
0250       if (rad(i) < 1.e-4)
0251         cov_rad(i) = cov_cart(i, i);  // TO FIX
0252       else {
0253         Vector2d a = p2D.col(i);
0254         Vector2d b = p2D.col(i) - fast_fit.head(2);
0255         const double x2 = a.dot(b);
0256         const double y2 = cross2D(a, b);
0257         const double tan_c = -y2 / x2;
0258         const double tan_c2 = sqr(tan_c);
0259         cov_rad(i) =
0260             1. / (1. + tan_c2) * (cov_cart(i, i) + cov_cart(i + n, i + n) * tan_c2 + 2 * cov_cart(i, i + n) * tan_c);
0261       }
0262     }
0263     return cov_rad;
0264   }
0265 
0266   /*!
0267     \brief Compute the points' weights' vector for the circle fit when multiple
0268     scattering is managed.
0269     Further information in attached documentation.
0270     \param cov_rad_inv covariance matrix inverse in radial coordinated
0271     (or, beter, pre-fitted circle's orthogonal system).
0272     \return weight VectorNd points' weights' vector.
0273     \bug I'm not sure this is the right way to compute the weights for non
0274     diagonal cov matrix. Further investigation needed.
0275 */
0276 
0277   template <int N>
0278   __host__ __device__ inline VectorNd<N> weightCircle(const MatrixNd<N>& cov_rad_inv) {
0279     return cov_rad_inv.colwise().sum().transpose();
0280   }
0281 
0282   /*!
0283     \brief Find particle q considering the  sign of cross product between
0284     particles velocity (estimated by the first 2 hits) and the vector radius
0285     between the first hit and the center of the fitted circle.
0286     \param p2D 2D points in transverse plane.
0287     \param par_uvr result of the circle fit in this form: (X0,Y0,R).
0288     \return q int 1 or -1.
0289 */
0290   template <typename M2xN>
0291   __host__ __device__ inline int32_t charge(const M2xN& p2D, const Vector3d& par_uvr) {
0292     return ((p2D(0, 1) - p2D(0, 0)) * (par_uvr.y() - p2D(1, 0)) - (p2D(1, 1) - p2D(1, 0)) * (par_uvr.x() - p2D(0, 0)) >
0293             0)
0294                ? -1
0295                : 1;
0296   }
0297 
0298   /*!
0299     \brief Compute the eigenvector associated to the minimum eigenvalue.
0300     \param A the Matrix you want to know eigenvector and eigenvalue.
0301     \param chi2 the double were the chi2-related quantity will be stored.
0302     \return the eigenvector associated to the minimum eigenvalue.
0303     \warning double precision is needed for a correct assessment of chi2.
0304     \details The minimus eigenvalue is related to chi2.
0305     We exploit the fact that the matrix is symmetrical and small (2x2 for line
0306     fit and 3x3 for circle fit), so the SelfAdjointEigenSolver from Eigen
0307     library is used, with the computedDirect  method (available only for 2x2
0308     and 3x3 Matrix) wich computes eigendecomposition of given matrix using a
0309     fast closed-form algorithm.
0310     For this optimization the matrix type must be known at compiling time.
0311 */
0312 
0313   __host__ __device__ inline Vector3d min_eigen3D(const Matrix3d& A, double& chi2) {
0314 #ifdef RFIT_DEBUG
0315     printf("min_eigen3D - enter\n");
0316 #endif
0317     Eigen::SelfAdjointEigenSolver<Matrix3d> solver(3);
0318     solver.computeDirect(A);
0319     int min_index;
0320     chi2 = solver.eigenvalues().minCoeff(&min_index);
0321 #ifdef RFIT_DEBUG
0322     printf("min_eigen3D - exit\n");
0323 #endif
0324     return solver.eigenvectors().col(min_index);
0325   }
0326 
0327   /*!
0328     \brief A faster version of min_eigen3D() where double precision is not
0329     needed.
0330     \param A the Matrix you want to know eigenvector and eigenvalue.
0331     \param chi2 the double were the chi2-related quantity will be stored
0332     \return the eigenvector associated to the minimum eigenvalue.
0333     \detail The computedDirect() method of SelfAdjointEigenSolver for 3x3 Matrix
0334     indeed, use trigonometry function (it solves a third degree equation) which
0335     speed up in  single precision.
0336 */
0337 
0338   __host__ __device__ inline Vector3d min_eigen3D_fast(const Matrix3d& A) {
0339     Eigen::SelfAdjointEigenSolver<Matrix3f> solver(3);
0340     solver.computeDirect(A.cast<float>());
0341     int min_index;
0342     solver.eigenvalues().minCoeff(&min_index);
0343     return solver.eigenvectors().col(min_index).cast<double>();
0344   }
0345 
0346   /*!
0347     \brief 2D version of min_eigen3D().
0348     \param aMat the Matrix you want to know eigenvector and eigenvalue.
0349     \param chi2 the double were the chi2-related quantity will be stored
0350     \return the eigenvector associated to the minimum eigenvalue.
0351     \detail The computedDirect() method of SelfAdjointEigenSolver for 2x2 Matrix
0352     do not use special math function (just sqrt) therefore it doesn't speed up
0353     significantly in single precision.
0354 */
0355 
0356   __host__ __device__ inline Vector2d min_eigen2D(const Matrix2d& aMat, double& chi2) {
0357     Eigen::SelfAdjointEigenSolver<Matrix2d> solver(2);
0358     solver.computeDirect(aMat);
0359     int min_index;
0360     chi2 = solver.eigenvalues().minCoeff(&min_index);
0361     return solver.eigenvectors().col(min_index);
0362   }
0363 
0364   /*!
0365     \brief A very fast helix fit: it fits a circle by three points (first, middle
0366     and last point) and a line by two points (first and last).
0367     \param hits points to be fitted
0368     \return result in this form: (X0,Y0,R,tan(theta)).
0369     \warning points must be passed ordered (from internal layer to external) in
0370     order to maximize accuracy and do not mistake tan(theta) sign.
0371     \details This fast fit is used as pre-fit which is needed for:
0372     - weights estimation and chi2 computation in line fit (fundamental);
0373     - weights estimation and chi2 computation in circle fit (useful);
0374     - computation of error due to multiple scattering.
0375 */
0376 
0377   template <typename M3xN, typename V4>
0378   __host__ __device__ inline void fastFit(const M3xN& hits, V4& result) {
0379     constexpr uint32_t N = M3xN::ColsAtCompileTime;
0380     constexpr auto n = N;  // get the number of hits
0381     printIt(&hits, "Fast_fit - hits: ");
0382 
0383     // CIRCLE FIT
0384     // Make segments between middle-to-first(b) and last-to-first(c) hits
0385     const Vector2d bVec = hits.block(0, n / 2, 2, 1) - hits.block(0, 0, 2, 1);
0386     const Vector2d cVec = hits.block(0, n - 1, 2, 1) - hits.block(0, 0, 2, 1);
0387     printIt(&bVec, "Fast_fit - b: ");
0388     printIt(&cVec, "Fast_fit - c: ");
0389     // Compute their lengths
0390     auto b2 = bVec.squaredNorm();
0391     auto c2 = cVec.squaredNorm();
0392     // The algebra has been verified (MR). The usual approach has been followed:
0393     // * use an orthogonal reference frame passing from the first point.
0394     // * build the segments (chords)
0395     // * build orthogonal lines through mid points
0396     // * make a system and solve for X0 and Y0.
0397     // * add the initial point
0398     bool flip = abs(bVec.x()) < abs(bVec.y());
0399     auto bx = flip ? bVec.y() : bVec.x();
0400     auto by = flip ? bVec.x() : bVec.y();
0401     auto cx = flip ? cVec.y() : cVec.x();
0402     auto cy = flip ? cVec.x() : cVec.y();
0403     //!< in case b.x is 0 (2 hits with same x)
0404     auto div = 2. * (cx * by - bx * cy);
0405     // if aligned TO FIX
0406     auto y0 = (cx * b2 - bx * c2) / div;
0407     auto x0 = (0.5 * b2 - y0 * by) / bx;
0408     result(0) = hits(0, 0) + (flip ? y0 : x0);
0409     result(1) = hits(1, 0) + (flip ? x0 : y0);
0410     result(2) = sqrt(sqr(x0) + sqr(y0));
0411     printIt(&result, "Fast_fit - result: ");
0412 
0413     // LINE FIT
0414     const Vector2d dVec = hits.block(0, 0, 2, 1) - result.head(2);
0415     const Vector2d eVec = hits.block(0, n - 1, 2, 1) - result.head(2);
0416     printIt(&eVec, "Fast_fit - e: ");
0417     printIt(&dVec, "Fast_fit - d: ");
0418     // Compute the arc-length between first and last point: L = R * theta = R * atan (tan (Theta) )
0419     auto dr = result(2) * atan2(cross2D(dVec, eVec), dVec.dot(eVec));
0420     // Simple difference in Z between last and first hit
0421     auto dz = hits(2, n - 1) - hits(2, 0);
0422 
0423     result(3) = (dr / dz);
0424 
0425 #ifdef RFIT_DEBUG
0426     printf("Fast_fit: [%f, %f, %f, %f]\n", result(0), result(1), result(2), result(3));
0427 #endif
0428   }
0429 
0430   /*!
0431     \brief Fit a generic number of 2D points with a circle using Riemann-Chernov
0432     algorithm. Covariance matrix of fitted parameter is optionally computed.
0433     Multiple scattering (currently only in barrel layer) is optionally handled.
0434     \param hits2D 2D points to be fitted.
0435     \param hits_cov2D covariance matrix of 2D points.
0436     \param fast_fit pre-fit result in this form: (X0,Y0,R,tan(theta)).
0437     (tan(theta) is not used).
0438     \param bField magnetic field
0439     \param error flag for error computation.
0440     \param scattering flag for multiple scattering
0441     \return circle circle_fit:
0442     -par parameter of the fitted circle in this form (X0,Y0,R); \n
0443     -cov covariance matrix of the fitted parameter (not initialized if
0444     error = false); \n
0445     -q charge of the particle; \n
0446     -chi2.
0447     \warning hits must be passed ordered from inner to outer layer (double hits
0448     on the same layer must be ordered too) so that multiple scattering is
0449     treated properly.
0450     \warning Multiple scattering for barrel is still not tested.
0451     \warning Multiple scattering for endcap hits is not handled (yet). Do not
0452     fit endcap hits with scattering = true !
0453     \bug for small pt (<0.3 Gev/c) chi2 could be slightly underestimated.
0454     \bug further investigation needed for error propagation with multiple
0455     scattering.
0456 */
0457   template <typename M2xN, typename V4, int N>
0458   __host__ __device__ inline CircleFit circleFit(const M2xN& hits2D,
0459                                                  const Matrix2Nd<N>& hits_cov2D,
0460                                                  const V4& fast_fit,
0461                                                  const VectorNd<N>& rad,
0462                                                  const double bField,
0463                                                  const bool error) {
0464 #ifdef RFIT_DEBUG
0465     printf("circle_fit - enter\n");
0466 #endif
0467     // INITIALIZATION
0468     Matrix2Nd<N> vMat = hits_cov2D;
0469     constexpr uint n = N;
0470     printIt(&hits2D, "circle_fit - hits2D:");
0471     printIt(&hits_cov2D, "circle_fit - hits_cov2D:");
0472 
0473 #ifdef RFIT_DEBUG
0474     printf("circle_fit - WEIGHT COMPUTATION\n");
0475 #endif
0476     // WEIGHT COMPUTATION
0477     VectorNd<N> weight;
0478     MatrixNd<N> gMat;
0479     double renorm;
0480     {
0481       MatrixNd<N> cov_rad = cov_carttorad_prefit(hits2D, vMat, fast_fit, rad).asDiagonal();
0482       MatrixNd<N> scatterCovRadMat = scatter_cov_rad(hits2D, fast_fit, rad, bField);
0483       printIt(&scatterCovRadMat, "circle_fit - scatter_cov_rad:");
0484       printIt(&hits2D, "circle_fit - hits2D bis:");
0485 #ifdef RFIT_DEBUG
0486       printf("Address of hits2D: a) %p\n", &hits2D);
0487 #endif
0488       vMat += cov_radtocart(hits2D, scatterCovRadMat, rad);
0489       printIt(&vMat, "circle_fit - V:");
0490       cov_rad += scatterCovRadMat;
0491       printIt(&cov_rad, "circle_fit - cov_rad:");
0492       math::cholesky::invert(cov_rad, gMat);
0493       // gMat = cov_rad.inverse();
0494       renorm = gMat.sum();
0495       gMat *= 1. / renorm;
0496       weight = weightCircle(gMat);
0497     }
0498     printIt(&weight, "circle_fit - weight:");
0499 
0500     // SPACE TRANSFORMATION
0501 #ifdef RFIT_DEBUG
0502     printf("circle_fit - SPACE TRANSFORMATION\n");
0503 #endif
0504 
0505     // center
0506 #ifdef RFIT_DEBUG
0507     printf("Address of hits2D: b) %p\n", &hits2D);
0508 #endif
0509     const Vector2d hCentroid = hits2D.rowwise().mean();  // centroid
0510     printIt(&hCentroid, "circle_fit - h_:");
0511     Matrix3xNd<N> p3D;
0512     p3D.block(0, 0, 2, n) = hits2D.colwise() - hCentroid;
0513     printIt(&p3D, "circle_fit - p3D: a)");
0514     Vector2Nd<N> mc;  // centered hits, used in error computation
0515     mc << p3D.row(0).transpose(), p3D.row(1).transpose();
0516     printIt(&mc, "circle_fit - mc(centered hits):");
0517 
0518     // scale
0519     const double tempQ = mc.squaredNorm();
0520     const double tempS = sqrt(n * 1. / tempQ);  // scaling factor
0521     p3D.block(0, 0, 2, n) *= tempS;
0522 
0523     // project on paraboloid
0524     p3D.row(2) = p3D.block(0, 0, 2, n).colwise().squaredNorm();
0525     printIt(&p3D, "circle_fit - p3D: b)");
0526 
0527 #ifdef RFIT_DEBUG
0528     printf("circle_fit - COST FUNCTION\n");
0529 #endif
0530     // COST FUNCTION
0531 
0532     // compute
0533     Vector3d r0;
0534     r0.noalias() = p3D * weight;  // center of gravity
0535     const Matrix3xNd<N> xMat = p3D.colwise() - r0;
0536     Matrix3d aMat = xMat * gMat * xMat.transpose();
0537     printIt(&aMat, "circle_fit - A:");
0538 
0539 #ifdef RFIT_DEBUG
0540     printf("circle_fit - MINIMIZE\n");
0541 #endif
0542     // minimize
0543     double chi2;
0544     Vector3d vVec = min_eigen3D(aMat, chi2);
0545 #ifdef RFIT_DEBUG
0546     printf("circle_fit - AFTER MIN_EIGEN\n");
0547 #endif
0548     printIt(&vVec, "v BEFORE INVERSION");
0549     vVec *= (vVec(2) > 0) ? 1 : -1;  // TO FIX dovrebbe essere N(3)>0
0550     printIt(&vVec, "v AFTER INVERSION");
0551     // This hack to be able to run on GPU where the automatic assignment to a
0552     // double from the vector multiplication is not working.
0553 #ifdef RFIT_DEBUG
0554     printf("circle_fit - AFTER MIN_EIGEN 1\n");
0555 #endif
0556     Eigen::Matrix<double, 1, 1> cm;
0557 #ifdef RFIT_DEBUG
0558     printf("circle_fit - AFTER MIN_EIGEN 2\n");
0559 #endif
0560     cm = -vVec.transpose() * r0;
0561 #ifdef RFIT_DEBUG
0562     printf("circle_fit - AFTER MIN_EIGEN 3\n");
0563 #endif
0564     const double tempC = cm(0, 0);
0565 
0566 #ifdef RFIT_DEBUG
0567     printf("circle_fit - COMPUTE CIRCLE PARAMETER\n");
0568 #endif
0569     // COMPUTE CIRCLE PARAMETER
0570 
0571     // auxiliary quantities
0572     const double tempH = sqrt(1. - sqr(vVec(2)) - 4. * tempC * vVec(2));
0573     const double v2x2_inv = 1. / (2. * vVec(2));
0574     const double s_inv = 1. / tempS;
0575     Vector3d par_uvr;  // used in error propagation
0576     par_uvr << -vVec(0) * v2x2_inv, -vVec(1) * v2x2_inv, tempH * v2x2_inv;
0577 
0578     CircleFit circle;
0579     circle.par << par_uvr(0) * s_inv + hCentroid(0), par_uvr(1) * s_inv + hCentroid(1), par_uvr(2) * s_inv;
0580     circle.qCharge = charge(hits2D, circle.par);
0581     circle.chi2 = abs(chi2) * renorm / sqr(2 * vVec(2) * par_uvr(2) * tempS);
0582     printIt(&circle.par, "circle_fit - CIRCLE PARAMETERS:");
0583     printIt(&circle.cov, "circle_fit - CIRCLE COVARIANCE:");
0584 #ifdef RFIT_DEBUG
0585     printf("circle_fit - CIRCLE CHARGE: %d\n", circle.qCharge);
0586 #endif
0587 
0588 #ifdef RFIT_DEBUG
0589     printf("circle_fit - ERROR PROPAGATION\n");
0590 #endif
0591     // ERROR PROPAGATION
0592     if (error) {
0593 #ifdef RFIT_DEBUG
0594       printf("circle_fit - ERROR PRPAGATION ACTIVATED\n");
0595 #endif
0596       ArrayNd<N> vcsMat[2][2];  // cov matrix of center & scaled points
0597       MatrixNd<N> cMat[3][3];   // cov matrix of 3D transformed points
0598 #ifdef RFIT_DEBUG
0599       printf("circle_fit - ERROR PRPAGATION ACTIVATED 2\n");
0600 #endif
0601       {
0602         Eigen::Matrix<double, 1, 1> cm;
0603         Eigen::Matrix<double, 1, 1> cm2;
0604         cm = mc.transpose() * vMat * mc;
0605         const double tempC2 = cm(0, 0);
0606         Matrix2Nd<N> tempVcsMat;
0607         tempVcsMat.template triangularView<Eigen::Upper>() =
0608             (sqr(tempS) * vMat + sqr(sqr(tempS)) * 1. / (4. * tempQ * n) *
0609                                      (2. * vMat.squaredNorm() + 4. * tempC2) *  // mc.transpose() * V * mc) *
0610                                      (mc * mc.transpose()));
0611 
0612         printIt(&tempVcsMat, "circle_fit - Vcs:");
0613         cMat[0][0] = tempVcsMat.block(0, 0, n, n).template selfadjointView<Eigen::Upper>();
0614         vcsMat[0][1] = tempVcsMat.block(0, n, n, n);
0615         cMat[1][1] = tempVcsMat.block(n, n, n, n).template selfadjointView<Eigen::Upper>();
0616         vcsMat[1][0] = vcsMat[0][1].transpose();
0617         printIt(&tempVcsMat, "circle_fit - Vcs:");
0618       }
0619 
0620       {
0621         const ArrayNd<N> t0 = (VectorXd::Constant(n, 1.) * p3D.row(0));
0622         const ArrayNd<N> t1 = (VectorXd::Constant(n, 1.) * p3D.row(1));
0623         const ArrayNd<N> t00 = p3D.row(0).transpose() * p3D.row(0);
0624         const ArrayNd<N> t01 = p3D.row(0).transpose() * p3D.row(1);
0625         const ArrayNd<N> t11 = p3D.row(1).transpose() * p3D.row(1);
0626         const ArrayNd<N> t10 = t01.transpose();
0627         vcsMat[0][0] = cMat[0][0];
0628         cMat[0][1] = vcsMat[0][1];
0629         cMat[0][2] = 2. * (vcsMat[0][0] * t0 + vcsMat[0][1] * t1);
0630         vcsMat[1][1] = cMat[1][1];
0631         cMat[1][2] = 2. * (vcsMat[1][0] * t0 + vcsMat[1][1] * t1);
0632         MatrixNd<N> tmp;
0633         tmp.template triangularView<Eigen::Upper>() =
0634             (2. * (vcsMat[0][0] * vcsMat[0][0] + vcsMat[0][0] * vcsMat[0][1] + vcsMat[1][1] * vcsMat[1][0] +
0635                    vcsMat[1][1] * vcsMat[1][1]) +
0636              4. * (vcsMat[0][0] * t00 + vcsMat[0][1] * t01 + vcsMat[1][0] * t10 + vcsMat[1][1] * t11))
0637                 .matrix();
0638         cMat[2][2] = tmp.template selfadjointView<Eigen::Upper>();
0639       }
0640       printIt(&cMat[0][0], "circle_fit - C[0][0]:");
0641 
0642       Matrix3d c0Mat;  // cov matrix of center of gravity (r0.x,r0.y,r0.z)
0643       for (uint i = 0; i < 3; ++i) {
0644         for (uint j = i; j < 3; ++j) {
0645           Eigen::Matrix<double, 1, 1> tmp;
0646           tmp = weight.transpose() * cMat[i][j] * weight;
0647           // Workaround to get things working in GPU
0648           const double tempC = tmp(0, 0);
0649           c0Mat(i, j) = tempC;  //weight.transpose() * C[i][j] * weight;
0650           c0Mat(j, i) = c0Mat(i, j);
0651         }
0652       }
0653       printIt(&c0Mat, "circle_fit - C0:");
0654 
0655       const MatrixNd<N> wMat = weight * weight.transpose();
0656       const MatrixNd<N> hMat = MatrixNd<N>::Identity().rowwise() - weight.transpose();
0657       const MatrixNx3d<N> s_v = hMat * p3D.transpose();
0658       printIt(&wMat, "circle_fit - W:");
0659       printIt(&hMat, "circle_fit - H:");
0660       printIt(&s_v, "circle_fit - s_v:");
0661 
0662       MatrixNd<N> dMat[3][3];  // cov(s_v)
0663       dMat[0][0] = (hMat * cMat[0][0] * hMat.transpose()).cwiseProduct(wMat);
0664       dMat[0][1] = (hMat * cMat[0][1] * hMat.transpose()).cwiseProduct(wMat);
0665       dMat[0][2] = (hMat * cMat[0][2] * hMat.transpose()).cwiseProduct(wMat);
0666       dMat[1][1] = (hMat * cMat[1][1] * hMat.transpose()).cwiseProduct(wMat);
0667       dMat[1][2] = (hMat * cMat[1][2] * hMat.transpose()).cwiseProduct(wMat);
0668       dMat[2][2] = (hMat * cMat[2][2] * hMat.transpose()).cwiseProduct(wMat);
0669       dMat[1][0] = dMat[0][1].transpose();
0670       dMat[2][0] = dMat[0][2].transpose();
0671       dMat[2][1] = dMat[1][2].transpose();
0672       printIt(&dMat[0][0], "circle_fit - D_[0][0]:");
0673 
0674       constexpr uint nu[6][2] = {{0, 0}, {0, 1}, {0, 2}, {1, 1}, {1, 2}, {2, 2}};
0675 
0676       Matrix6d eMat;  // cov matrix of the 6 independent elements of A
0677       for (uint a = 0; a < 6; ++a) {
0678         const uint i = nu[a][0], j = nu[a][1];
0679         for (uint b = a; b < 6; ++b) {
0680           const uint k = nu[b][0], l = nu[b][1];
0681           VectorNd<N> t0(n);
0682           VectorNd<N> t1(n);
0683           if (l == k) {
0684             t0 = 2. * dMat[j][l] * s_v.col(l);
0685             if (i == j)
0686               t1 = t0;
0687             else
0688               t1 = 2. * dMat[i][l] * s_v.col(l);
0689           } else {
0690             t0 = dMat[j][l] * s_v.col(k) + dMat[j][k] * s_v.col(l);
0691             if (i == j)
0692               t1 = t0;
0693             else
0694               t1 = dMat[i][l] * s_v.col(k) + dMat[i][k] * s_v.col(l);
0695           }
0696 
0697           if (i == j) {
0698             Eigen::Matrix<double, 1, 1> cm;
0699             cm = s_v.col(i).transpose() * (t0 + t1);
0700             // Workaround to get things working in GPU
0701             const double tempC = cm(0, 0);
0702             eMat(a, b) = 0. + tempC;
0703           } else {
0704             Eigen::Matrix<double, 1, 1> cm;
0705             cm = (s_v.col(i).transpose() * t0) + (s_v.col(j).transpose() * t1);
0706             // Workaround to get things working in GPU
0707             const double tempC = cm(0, 0);
0708             eMat(a, b) = 0. + tempC;  //(s_v.col(i).transpose() * t0) + (s_v.col(j).transpose() * t1);
0709           }
0710           if (b != a)
0711             eMat(b, a) = eMat(a, b);
0712         }
0713       }
0714       printIt(&eMat, "circle_fit - E:");
0715 
0716       Eigen::Matrix<double, 3, 6> j2Mat;  // Jacobian of min_eigen() (numerically computed)
0717       for (uint a = 0; a < 6; ++a) {
0718         const uint i = nu[a][0], j = nu[a][1];
0719         Matrix3d delta = Matrix3d::Zero();
0720         delta(i, j) = delta(j, i) = abs(aMat(i, j) * epsilon);
0721         j2Mat.col(a) = min_eigen3D_fast(aMat + delta);
0722         const int sign = (j2Mat.col(a)(2) > 0) ? 1 : -1;
0723         j2Mat.col(a) = (j2Mat.col(a) * sign - vVec) / delta(i, j);
0724       }
0725       printIt(&j2Mat, "circle_fit - J2:");
0726 
0727       Matrix4d cvcMat;  // joint cov matrix of (v0,v1,v2,c)
0728       {
0729         Matrix3d t0 = j2Mat * eMat * j2Mat.transpose();
0730         Vector3d t1 = -t0 * r0;
0731         cvcMat.block(0, 0, 3, 3) = t0;
0732         cvcMat.block(0, 3, 3, 1) = t1;
0733         cvcMat.block(3, 0, 1, 3) = t1.transpose();
0734         Eigen::Matrix<double, 1, 1> cm1;
0735         Eigen::Matrix<double, 1, 1> cm3;
0736         cm1 = (vVec.transpose() * c0Mat * vVec);
0737         //      cm2 = (c0Mat.cwiseProduct(t0)).sum();
0738         cm3 = (r0.transpose() * t0 * r0);
0739         // Workaround to get things working in GPU
0740         const double tempC = cm1(0, 0) + (c0Mat.cwiseProduct(t0)).sum() + cm3(0, 0);
0741         cvcMat(3, 3) = tempC;
0742         // (v.transpose() * c0Mat * v) + (c0Mat.cwiseProduct(t0)).sum() + (r0.transpose() * t0 * r0);
0743       }
0744       printIt(&cvcMat, "circle_fit - Cvc:");
0745 
0746       Eigen::Matrix<double, 3, 4> j3Mat;  // Jacobian (v0,v1,v2,c)->(X0,Y0,R)
0747       {
0748         const double t = 1. / tempH;
0749         j3Mat << -v2x2_inv, 0, vVec(0) * sqr(v2x2_inv) * 2., 0, 0, -v2x2_inv, vVec(1) * sqr(v2x2_inv) * 2., 0,
0750             vVec(0) * v2x2_inv * t, vVec(1) * v2x2_inv * t,
0751             -tempH * sqr(v2x2_inv) * 2. - (2. * tempC + vVec(2)) * v2x2_inv * t, -t;
0752       }
0753       printIt(&j3Mat, "circle_fit - J3:");
0754 
0755       const RowVector2Nd<N> Jq = mc.transpose() * tempS * 1. / n;  // var(q)
0756       printIt(&Jq, "circle_fit - Jq:");
0757 
0758       Matrix3d cov_uvr = j3Mat * cvcMat * j3Mat.transpose() * sqr(s_inv)  // cov(X0,Y0,R)
0759                          + (par_uvr * par_uvr.transpose()) * (Jq * vMat * Jq.transpose());
0760 
0761       circle.cov = cov_uvr;
0762     }
0763 
0764     printIt(&circle.cov, "Circle cov:");
0765 #ifdef RFIT_DEBUG
0766     printf("circle_fit - exit\n");
0767 #endif
0768     return circle;
0769   }
0770 
0771   /*!  \brief Perform an ordinary least square fit in the s-z plane to compute
0772  * the parameters cotTheta and Zip.
0773  *
0774  * The fit is performed in the rotated S3D-Z' plane, following the formalism of
0775  * Frodesen, Chapter 10, p. 259.
0776  *
0777  * The system has been rotated to both try to use the combined errors in s-z
0778  * along Z', as errors in the Y direction and to avoid the patological case of
0779  * degenerate lines with angular coefficient m = +/- inf.
0780  *
0781  * The rotation is using the information on the theta angle computed in the
0782  * fast fit. The rotation is such that the S3D axis will be the X-direction,
0783  * while the rotated Z-axis will be the Y-direction. This pretty much follows
0784  * what is done in the same fit in the Broken Line approach.
0785  */
0786 
0787   template <typename M3xN, typename M6xN, typename V4>
0788   __host__ __device__ inline LineFit lineFit(const M3xN& hits,
0789                                              const M6xN& hits_ge,
0790                                              const CircleFit& circle,
0791                                              const V4& fast_fit,
0792                                              const double bField,
0793                                              const bool error) {
0794     constexpr uint32_t N = M3xN::ColsAtCompileTime;
0795     constexpr auto n = N;
0796     double theta = -circle.qCharge * atan(fast_fit(3));
0797     theta = theta < 0. ? theta + M_PI : theta;
0798 
0799     // Prepare the Rotation Matrix to rotate the points
0800     Eigen::Matrix<double, 2, 2> rot;
0801     rot << sin(theta), cos(theta), -cos(theta), sin(theta);
0802 
0803     // PROJECTION ON THE CILINDER
0804     //
0805     // p2D will be:
0806     // [s1, s2, s3, ..., sn]
0807     // [z1, z2, z3, ..., zn]
0808     // s values will be ordinary x-values
0809     // z values will be ordinary y-values
0810 
0811     Matrix2xNd<N> p2D = Matrix2xNd<N>::Zero();
0812     Eigen::Matrix<double, 2, 6> jxMat;
0813 
0814 #ifdef RFIT_DEBUG
0815     printf("Line_fit - B: %g\n", bField);
0816     printIt(&hits, "Line_fit points: ");
0817     printIt(&hits_ge, "Line_fit covs: ");
0818     printIt(&rot, "Line_fit rot: ");
0819 #endif
0820     // x & associated Jacobian
0821     // cfr https://indico.cern.ch/event/663159/contributions/2707659/attachments/1517175/2368189/Riemann_fit.pdf
0822     // Slide 11
0823     // a ==> -o i.e. the origin of the circle in XY plane, negative
0824     // b ==> p i.e. distances of the points wrt the origin of the circle.
0825     const Vector2d oVec(circle.par(0), circle.par(1));
0826 
0827     // associated Jacobian, used in weights and errors computation
0828     Matrix6d covMat = Matrix6d::Zero();
0829     Matrix2d cov_sz[N];
0830     for (uint i = 0; i < n; ++i) {
0831       Vector2d pVec = hits.block(0, i, 2, 1) - oVec;
0832       const double cross = cross2D(-oVec, pVec);
0833       const double dot = (-oVec).dot(pVec);
0834       // atan2(cross, dot) give back the angle in the transverse plane so tha the
0835       // final equation reads: x_i = -q*R*theta (theta = angle returned by atan2)
0836       const double tempQAtan2 = -circle.qCharge * atan2(cross, dot);
0837       //    p2D.coeffRef(1, i) = atan2_ * circle.par(2);
0838       p2D(0, i) = tempQAtan2 * circle.par(2);
0839 
0840       // associated Jacobian, used in weights and errors- computation
0841       const double temp0 = -circle.qCharge * circle.par(2) * 1. / (sqr(dot) + sqr(cross));
0842       double d_X0 = 0., d_Y0 = 0., d_R = 0.;  // good approximation for big pt and eta
0843       if (error) {
0844         d_X0 = -temp0 * ((pVec(1) + oVec(1)) * dot - (pVec(0) - oVec(0)) * cross);
0845         d_Y0 = temp0 * ((pVec(0) + oVec(0)) * dot - (oVec(1) - pVec(1)) * cross);
0846         d_R = tempQAtan2;
0847       }
0848       const double d_x = temp0 * (oVec(1) * dot + oVec(0) * cross);
0849       const double d_y = temp0 * (-oVec(0) * dot + oVec(1) * cross);
0850       jxMat << d_X0, d_Y0, d_R, d_x, d_y, 0., 0., 0., 0., 0., 0., 1.;
0851 
0852       covMat.block(0, 0, 3, 3) = circle.cov;
0853       covMat(3, 3) = hits_ge.col(i)[0];                 // x errors
0854       covMat(4, 4) = hits_ge.col(i)[2];                 // y errors
0855       covMat(5, 5) = hits_ge.col(i)[5];                 // z errors
0856       covMat(3, 4) = covMat(4, 3) = hits_ge.col(i)[1];  // cov_xy
0857       covMat(3, 5) = covMat(5, 3) = hits_ge.col(i)[3];  // cov_xz
0858       covMat(4, 5) = covMat(5, 4) = hits_ge.col(i)[4];  // cov_yz
0859       Matrix2d tmp = jxMat * covMat * jxMat.transpose();
0860       cov_sz[i].noalias() = rot * tmp * rot.transpose();
0861     }
0862     // Math of d_{X0,Y0,R,x,y} all verified by hand
0863     p2D.row(1) = hits.row(2);
0864 
0865     // The following matrix will contain errors orthogonal to the rotated S
0866     // component only, with the Multiple Scattering properly treated!!
0867     MatrixNd<N> cov_with_ms;
0868     scatterCovLine(cov_sz, fast_fit, p2D.row(0), p2D.row(1), theta, bField, cov_with_ms);
0869 #ifdef RFIT_DEBUG
0870     printIt(cov_sz, "line_fit - cov_sz:");
0871     printIt(&cov_with_ms, "line_fit - cov_with_ms: ");
0872 #endif
0873 
0874     // Rotate Points with the shape [2, n]
0875     Matrix2xNd<N> p2D_rot = rot * p2D;
0876 
0877 #ifdef RFIT_DEBUG
0878     printf("Fast fit Tan(theta): %g\n", fast_fit(3));
0879     printf("Rotation angle: %g\n", theta);
0880     printIt(&rot, "Rotation Matrix:");
0881     printIt(&p2D, "Original Hits(s,z):");
0882     printIt(&p2D_rot, "Rotated hits(S3D, Z'):");
0883     printIt(&rot, "Rotation Matrix:");
0884 #endif
0885 
0886     // Build the A Matrix
0887     Matrix2xNd<N> aMat;
0888     aMat << MatrixXd::Ones(1, n), p2D_rot.row(0);  // rotated s values
0889 
0890 #ifdef RFIT_DEBUG
0891     printIt(&aMat, "A Matrix:");
0892 #endif
0893 
0894     // Build A^T V-1 A, where V-1 is the covariance of only the Y components.
0895     MatrixNd<N> vyInvMat;
0896     math::cholesky::invert(cov_with_ms, vyInvMat);
0897     // MatrixNd<N> vyInvMat = cov_with_ms.inverse();
0898     Eigen::Matrix<double, 2, 2> covParamsMat = aMat * vyInvMat * aMat.transpose();
0899     // Compute the Covariance Matrix of the fit parameters
0900     math::cholesky::invert(covParamsMat, covParamsMat);
0901 
0902     // Now Compute the Parameters in the form [2,1]
0903     // The first component is q.
0904     // The second component is m.
0905     Eigen::Matrix<double, 2, 1> sol = covParamsMat * aMat * vyInvMat * p2D_rot.row(1).transpose();
0906 
0907 #ifdef RFIT_DEBUG
0908     printIt(&sol, "Rotated solutions:");
0909 #endif
0910 
0911     // We need now to transfer back the results in the original s-z plane
0912     const auto sinTheta = sin(theta);
0913     const auto cosTheta = cos(theta);
0914     auto common_factor = 1. / (sinTheta - sol(1, 0) * cosTheta);
0915     Eigen::Matrix<double, 2, 2> jMat;
0916     jMat << 0., common_factor * common_factor, common_factor, sol(0, 0) * cosTheta * common_factor * common_factor;
0917 
0918     double tempM = common_factor * (sol(1, 0) * sinTheta + cosTheta);
0919     double tempQ = common_factor * sol(0, 0);
0920     auto cov_mq = jMat * covParamsMat * jMat.transpose();
0921 
0922     VectorNd<N> res = p2D_rot.row(1).transpose() - aMat.transpose() * sol;
0923     double chi2 = res.transpose() * vyInvMat * res;
0924 
0925     LineFit line;
0926     line.par << tempM, tempQ;
0927     line.cov << cov_mq;
0928     line.chi2 = chi2;
0929 
0930 #ifdef RFIT_DEBUG
0931     printf("Common_factor: %g\n", common_factor);
0932     printIt(&jMat, "Jacobian:");
0933     printIt(&sol, "Rotated solutions:");
0934     printIt(&covParamsMat, "Cov_params:");
0935     printIt(&cov_mq, "Rotated Covariance Matrix:");
0936     printIt(&(line.par), "Real Parameters:");
0937     printIt(&(line.cov), "Real Covariance Matrix:");
0938     printf("Chi2: %g\n", chi2);
0939 #endif
0940 
0941     return line;
0942   }
0943 
0944   /*!
0945     \brief Helix fit by three step:
0946     -fast pre-fit (see Fast_fit() for further info); \n
0947     -circle fit of hits projected in the transverse plane by Riemann-Chernov
0948         algorithm (see Circle_fit() for further info); \n
0949     -line fit of hits projected on cylinder surface by orthogonal distance
0950         regression (see Line_fit for further info). \n
0951     Points must be passed ordered (from inner to outer layer).
0952     \param hits Matrix3xNd hits coordinates in this form: \n
0953         |x0|x1|x2|...|xn| \n
0954         |y0|y1|y2|...|yn| \n
0955         |z0|z1|z2|...|zn|
0956     \param hits_cov Matrix3Nd covariance matrix in this form (()->cov()): \n
0957    |(x0,x0)|(x1,x0)|(x2,x0)|.|(y0,x0)|(y1,x0)|(y2,x0)|.|(z0,x0)|(z1,x0)|(z2,x0)| \n
0958    |(x0,x1)|(x1,x1)|(x2,x1)|.|(y0,x1)|(y1,x1)|(y2,x1)|.|(z0,x1)|(z1,x1)|(z2,x1)| \n
0959    |(x0,x2)|(x1,x2)|(x2,x2)|.|(y0,x2)|(y1,x2)|(y2,x2)|.|(z0,x2)|(z1,x2)|(z2,x2)| \n
0960        .       .       .    .    .       .       .    .    .       .       .     \n
0961    |(x0,y0)|(x1,y0)|(x2,y0)|.|(y0,y0)|(y1,y0)|(y2,x0)|.|(z0,y0)|(z1,y0)|(z2,y0)| \n
0962    |(x0,y1)|(x1,y1)|(x2,y1)|.|(y0,y1)|(y1,y1)|(y2,x1)|.|(z0,y1)|(z1,y1)|(z2,y1)| \n
0963    |(x0,y2)|(x1,y2)|(x2,y2)|.|(y0,y2)|(y1,y2)|(y2,x2)|.|(z0,y2)|(z1,y2)|(z2,y2)| \n
0964        .       .       .    .    .       .       .    .    .       .       .     \n
0965    |(x0,z0)|(x1,z0)|(x2,z0)|.|(y0,z0)|(y1,z0)|(y2,z0)|.|(z0,z0)|(z1,z0)|(z2,z0)| \n
0966    |(x0,z1)|(x1,z1)|(x2,z1)|.|(y0,z1)|(y1,z1)|(y2,z1)|.|(z0,z1)|(z1,z1)|(z2,z1)| \n
0967    |(x0,z2)|(x1,z2)|(x2,z2)|.|(y0,z2)|(y1,z2)|(y2,z2)|.|(z0,z2)|(z1,z2)|(z2,z2)|
0968    \param bField magnetic field in the center of the detector in Gev/cm/c
0969    unit, in order to perform pt calculation.
0970    \param error flag for error computation.
0971    \param scattering flag for multiple scattering treatment.
0972    (see Circle_fit() documentation for further info).
0973    \warning see Circle_fit(), Line_fit() and Fast_fit() warnings.
0974    \bug see Circle_fit(), Line_fit() and Fast_fit() bugs.
0975 */
0976 
0977   template <int N>
0978   inline HelixFit helixFit(const Matrix3xNd<N>& hits,
0979                            const Eigen::Matrix<float, 6, N>& hits_ge,
0980                            const double bField,
0981                            const bool error) {
0982     constexpr uint n = N;
0983     VectorNd<4> rad = (hits.block(0, 0, 2, n).colwise().norm());
0984 
0985     // Fast_fit gives back (X0, Y0, R, theta) w/o errors, using only 3 points.
0986     Vector4d fast_fit;
0987     fastFit(hits, fast_fit);
0988     riemannFit::Matrix2Nd<N> hits_cov = MatrixXd::Zero(2 * n, 2 * n);
0989     riemannFit::loadCovariance2D(hits_ge, hits_cov);
0990     CircleFit circle = circleFit(hits.block(0, 0, 2, n), hits_cov, fast_fit, rad, bField, error);
0991     LineFit line = lineFit(hits, hits_ge, circle, fast_fit, bField, error);
0992 
0993     par_uvrtopak(circle, bField, error);
0994 
0995     HelixFit helix;
0996     helix.par << circle.par, line.par;
0997     if (error) {
0998       helix.cov = MatrixXd::Zero(5, 5);
0999       helix.cov.block(0, 0, 3, 3) = circle.cov;
1000       helix.cov.block(3, 3, 2, 2) = line.cov;
1001     }
1002     helix.qCharge = circle.qCharge;
1003     helix.chi2_circle = circle.chi2;
1004     helix.chi2_line = line.chi2;
1005 
1006     return helix;
1007   }
1008 
1009 }  // namespace riemannFit
1010 
1011 #endif  // RecoTracker_PixelTrackFitting_RiemannFit_h