Belle II Software  release-08-01-10
QualityEstimatorRiemannHelixFit.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include "tracking/trackFindingVXD/trackQualityEstimators/QualityEstimatorRiemannHelixFit.h"
10 #include <tracking/trackFindingVXD/utilities/CalcCurvatureSignum.h>
11 #include <math.h>
12 #include <Eigen/Dense>
13 #include <framework/logging/Logger.h>
14 #include <TMath.h>
15 
16 using namespace Belle2;
17 // Set precision to be used for Eigen Library
18 typedef double Precision;
19 
20 double QualityEstimatorRiemannHelixFit::estimateQuality(std::vector<SpacePoint const*> const& measurements)
21 {
22  const int nHits = measurements.size();
23  if (nHits < 3) return 0;
24 
25  // Circle Fit
26 
27  Eigen::Matrix<Precision, Eigen::Dynamic, Eigen::Dynamic> W = Eigen::Matrix<Precision, Eigen::Dynamic, Eigen::Dynamic>::Zero(nHits,
28  nHits);
29  Eigen::Matrix<Precision, Eigen::Dynamic, Eigen::Dynamic> Wz = Eigen::Matrix<Precision, Eigen::Dynamic, Eigen::Dynamic>::Zero(nHits,
30  nHits);
31  Eigen::Matrix<Precision, Eigen::Dynamic, 3> X = Eigen::Matrix<Precision, Eigen::Dynamic, 3>::Zero(nHits, 3);
32  Eigen::Matrix<Precision, Eigen::Dynamic, 1> Z = Eigen::Matrix<Precision, Eigen::Dynamic, 1>::Zero(nHits, 1);
33  Precision traceOfW = 0.;
34 
35  short index = 0;
36  for (SpacePoint const* hit : measurements) {
37  auto position = hit->getPosition();
38  auto positionError = hit->getPositionError();
39  double x = position.X();
40  double y = position.Y();
41  double z = position.Z();
42  double sigmaX = positionError.X();
43  double sigmaY = positionError.Y();
44  double sigmaZ = positionError.Z();
45 
46  double r2 = x * x + y * y;
47  double inverseVarianceXY = 1. / sqrt(sigmaX * sigmaX + sigmaY * sigmaY);
48 
49  // The following weight matrix W can be improved for cases with multiple scattering
50  // by adding a correction term which will make the matrix non-diagonal.
51  // However, this requires prior knowledge of the curvature of the track and thus a
52  // second iteration (see page 368 of above mentioned source).
53  W(index, index) = inverseVarianceXY;
54  traceOfW += inverseVarianceXY;
55 
56  X(index, 0) = x;
57  X(index, 1) = y;
58  X(index, 2) = r2;
59 
60  // Values for z line fit for extrended Riemann fit
61  Wz(index, index) = 1. / sigmaZ;
62  Z(index, 0) = z;
63 
64  index++;
65  }
66 
67  Eigen::Matrix<Precision, 1, 3> xBar = Eigen::Matrix<Precision, Eigen::Dynamic, 1>::Ones(nHits, 1).transpose() * W * X / traceOfW;
68  Eigen::Matrix<Precision, 3, 3> Vx = X.transpose() * W * X - xBar.transpose() * xBar * traceOfW;
69 
70  // Find eigenvector to smallest eigenvalue
71  Eigen::EigenSolver<Eigen::Matrix<Precision, 3, 3>> eigencollection(Vx);
72  Eigen::Matrix<Precision, 3, 1> eigenvalues = eigencollection.eigenvalues().real();
73  Eigen::Matrix<std::complex<Precision>, 3, 3> eigenvectors = eigencollection.eigenvectors();
74  Eigen::Matrix<Precision, 3, 1>::Index minRow, minCol;
75  eigenvalues.minCoeff(&minRow, &minCol);
76 
77  Eigen::Matrix<Precision, 3, 1> n = eigenvectors.col(minRow).real();
78 
79  // Calculate results with this eigenvector
80  Precision c = - xBar * n;
81  Precision x0 = - 0.5 * n(0) / n(2);
82  Precision y0 = - 0.5 * n(1) / n(2);
83  Precision rho2 = (1 - n(2) * (n(2) + 4 * c)) / (4 * n(2) * n(2));
84  Precision rho = sqrt(rho2);
85 
86  // calculation of chi2 for circle fit using Karimaeki circle fit
87  Precision divisor = 1. / traceOfW;
88  Eigen::Matrix<Precision, Eigen::Dynamic, 1> unitvec = Eigen::Matrix<Precision, Eigen::Dynamic, 1>::Ones(nHits, 1);
89  Precision meanX = unitvec.transpose() * W * X.col(0);
90  meanX *= divisor;
91  Precision meanY = unitvec.transpose() * W * X.col(1);
92  meanY *= divisor;
93  Precision meanXY = unitvec.transpose() * W * (X.col(0).cwiseProduct(X.col(1)));
94  meanXY *= divisor;
95  Precision meanX2 = unitvec.transpose() * W * (X.col(0).cwiseProduct(X.col(0)));
96  meanX2 *= divisor;
97  Precision meanY2 = unitvec.transpose() * W * (X.col(1).cwiseProduct(X.col(1)));
98  meanY2 *= divisor;
99  Precision meanXR2 = unitvec.transpose() * W * (X.col(0).cwiseProduct(X.col(2)));
100  meanXR2 *= divisor;
101  Precision meanYR2 = unitvec.transpose() * W * (X.col(1).cwiseProduct(X.col(2)));
102  meanYR2 *= divisor;
103  Precision meanR2 = unitvec.transpose() * W * X.col(2);
104  meanR2 *= divisor;
105  Precision meanR4 = unitvec.transpose() * W * (X.col(2).cwiseProduct(X.col(2)));
106  meanR4 *= divisor;
107 
108  // covariances:
109  Precision covXX = meanX2 - meanX * meanX;
110  Precision covXY = meanXY - meanX * meanY;
111  Precision covYY = meanY2 - meanY * meanY;
112  Precision covXR2 = meanXR2 - meanX * meanR2;
113  Precision covYR2 = meanYR2 - meanY * meanR2;
114  Precision covR2R2 = meanR4 - meanR2 * meanR2;
115 
116  // q1, q2: helping variables, to make the code more readable
117  Precision q1 = covR2R2 * covXY - covXR2 * covYR2;
118  Precision q2 = covR2R2 * (covXX - covYY) - covXR2 * covXR2 + covYR2 * covYR2;
119 
120  Precision pocaPhi = 0.5 * atan2(2. * q1, q2);
121 
122  Precision sinPhi = sin(pocaPhi);
123  Precision cosPhi = cos(pocaPhi);
124  Precision kappa = (sinPhi * covXR2 - cosPhi * covYR2) / covR2R2;
125  Precision delta = -kappa * meanR2 + sinPhi * meanX - cosPhi * meanY;
126  Precision rootTerm = sqrt(1. - 4.*delta * kappa);
127  Precision curvature = 2.*kappa / (rootTerm);
128  Precision pocaD = 2.*delta / (1. + rootTerm);
129  short curvatureSign = calcCurvatureSignum(measurements);
130 
131  if ((curvature < 0 && curvatureSign >= 0) || (curvature > 0 && curvatureSign < 0)) {
132  curvature = -curvature;
133  // pocaPhi = pocaPhi + M_PI; //
134  pocaD = -pocaD;
135  }
136 
137  Precision chi2 = traceOfW * (1. + pocaD / rho) * (1. + curvature * pocaD) *
138  (sinPhi * sinPhi * covXX - 2.*sinPhi * cosPhi * covXY + cosPhi * cosPhi * covYY - kappa * kappa * covR2R2);
139 
140  // Arc length calculation
141  Precision x_first = X.col(0)(0) - x0;
142  Precision y_first = X.col(1)(0) - y0;
143  Precision r_mag_first = sqrt(x_first * x_first + y_first * y_first);
144  Eigen::Matrix<Precision, Eigen::Dynamic, 1> x0s = Eigen::Matrix<Precision, Eigen::Dynamic, 1>::Ones(nHits, 1) * x0;
145  Eigen::Matrix<Precision, Eigen::Dynamic, 1> y0s = Eigen::Matrix<Precision, Eigen::Dynamic, 1>::Ones(nHits, 1) * y0;
146  Eigen::Matrix<Precision, Eigen::Dynamic, 1> r_mags = ((X.col(0) - x0s).cwiseProduct(X.col(0) - x0s) + (X.col(1) - y0s).cwiseProduct(
147  X.col(1) - y0s)).cwiseSqrt();
148 
149  Eigen::Matrix<Precision, Eigen::Dynamic, 1> arc_angles = (x_first * (X.col(0) - x0s) + y_first * (X.col(1) - y0s)).cwiseQuotient(
150  r_mags) / r_mag_first;
151  Eigen::Matrix<Precision, Eigen::Dynamic, 1> arc_lengths = rho * arc_angles.array().acos().matrix();
152 
153  Eigen::Matrix<Precision, Eigen::Dynamic, 2> A = Eigen::Matrix<Precision, Eigen::Dynamic, 2>::Ones(nHits, 2);
154  arc_lengths(0) = 0.;
155  A.col(1) = arc_lengths;
156 
157  // Linear regression of z on arg_lengths with weight matrix Wz
158  Eigen::Matrix<Precision, 2, 1> p = (A.transpose() * Wz * A).inverse() * A.transpose() * Wz * Z;
159 
160  // Construction of momentum vector with innermost hit and reconstructed circle center
161  Eigen::Matrix<Precision, 3, 1> momVec = Eigen::Matrix<Precision, 3, 1>::Zero();
162  momVec(0) = y0 - X.col(1)(0);
163  momVec(1) = - (x0 - X.col(0)(0));
164 
165  Precision pT = Precision(calcPt(rho));
166  momVec = pT * momVec.normalized();
167 
168  Eigen::Matrix<Precision, 3, 1> vec01 = X.row(1) - X.row(0);
169  vec01(2) = Z(1) - Z(0);
170  Precision angle01 = std::acos(vec01.dot(momVec) / momVec.norm() / vec01.norm());
171  if (angle01 > 0.5 * M_PI) { momVec *= -1.; }
172 
173  // Calculation of a chi2 distributed quantity for the quality of fit of the z component fit.
174  Eigen::Matrix<Precision, Eigen::Dynamic, 1> ones = Eigen::Matrix<Precision, Eigen::Dynamic, 1>::Ones(nHits, 1);
175  Precision chi2_z = ((Z - p(0) * ones - p(1) * arc_lengths).cwiseQuotient(Wz * ones)).transpose() * ((Z - p(0) * ones - p(
176  1) * arc_lengths).cwiseQuotient(Wz * ones));
177 
178  // Adding chi2 and chi2_z, thus creating a chi2 distribution with (n-3) + (n-2) = 2n-5 degrees of freedom
179  m_results.chiSquared = chi2 + chi2_z;
180  B2DEBUG(25, "Chi Squared of extended Riemann = " << * (m_results.chiSquared) << std::endl);
181 
182  Precision pZ = pT * p(1);
183  momVec(2) = pZ;
184  m_results.pt = pT;
185  m_results.p = B2Vector3D(momVec(0), momVec(1), momVec(2));
186  m_results.curvatureSign = curvatureSign;
187  m_results.pocaD = pocaD;
188 
189  return TMath::Prob(*(m_results.chiSquared), 2 * measurements.size() - 5);
190 }
191 
QualityEstimationResults m_results
Result of the quality estimation This is stored as a member variable, because some values may be calc...
double calcPt(double const radius) const
Returns a value for the transverse momentum in GeV calculated from a provided radius.
virtual double estimateQuality(std::vector< SpacePoint const * > const &measurements) final
Minimal implementation of the quality estimation Calculates quality indicator in range [0,...
SpacePoint typically is build from 1 PXDCluster or 1-2 SVDClusters.
Definition: SpacePoint.h:42
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:516
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
short calcCurvatureSignum(std::vector< SpacePoint const * > const &measurements)
Calculate curvature based on triplets of measurements.
Abstract base class for different kinds of events.
std::optional< double > pt
transverse momentum estimate from the QE
std::optional< short > curvatureSign
direction of curvature as obtained by the QE
std::optional< double > pocaD
distance to the z-axis of the POCA
std::optional< double > chiSquared
chi squared value obtained by the fit of the QE
std::optional< B2Vector3D > p
momentum vector estimate from the QE