Belle II Software development
FacetFitter.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#include <tracking/trackFindingCDC/fitting/FacetFitter.h>
9
10#include <tracking/trackingUtilities/eventdata/hits/CDCFacet.h>
11#include <tracking/trackingUtilities/eventdata/hits/CDCWireHit.h>
12#include <tracking/trackingUtilities/geometry/UncertainParameterLine2D.h>
13#include <tracking/trackingUtilities/geometry/ParameterLine2D.h>
14#include <tracking/trackingUtilities/geometry/VectorUtil.h>
15#include <tracking/trackingUtilities/numerics/EigenView.h>
16
17#include <Eigen/Core>
18
19#include <cmath>
20
21#include <Math/Vector2D.h>
22#include <Math/Functor.h>
23#include <Math/BrentMinimizer1D.h>
24
25using namespace Belle2;
26using namespace TrackFindingCDC;
27using namespace TrackingUtilities;
28
29namespace {
30
31 template<int N>
32 ROOT::Math::XYVector getCenterForwardDirection(const Matrix<double, N, 3>& xyl)
33 {
35 ROOT::Math::XYVector coordinate(xyl(N - 1, 0) - xyl(0, 0), xyl(N - 1, 1) - xyl(0, 1));
36 return VectorUtil::unit(coordinate);
37 }
38
39 template<int N>
40 ROOT::Math::XYVector getTangentialForwardDirection(const Matrix<double, N, 3>& xyl)
41 {
43 ROOT::Math::XYVector fromPos(xyl(0, 0), xyl(0, 1));
44 double fromL = xyl(0, 2);
45
46 ROOT::Math::XYVector toPos(xyl(N - 1, 0), xyl(N - 1, 1));
47 double toL = xyl(N - 1, 2);
48
49 ParameterLine2D tangentLine = ParameterLine2D::touchingCircles(fromPos, fromL, toPos, toL);
50 ROOT::Math::XYVector coordinate = tangentLine.tangential();
51 return VectorUtil::unit(coordinate);
52 }
53
54 template<int N>
55 void rotate(ROOT::Math::XYVector coordinate, Matrix<double, N, 3>& xyl)
56 {
57 Matrix<double, 3, 3> rot = Matrix<double, 3, 3>::Identity();
58 rot(0, 0) = coordinate.x();
59 rot(0, 1) = -coordinate.y();
60 rot(1, 0) = coordinate.y();
61 rot(1, 1) = coordinate.x();
62 rot(2, 2) = 1; // Drift length remains the same.
63 xyl = xyl * rot;
64 }
65
66 void unrotate(ROOT::Math::XYVector coordinate, ROOT::Math::XYVector& vec)
67 {
68 // Inverse rotation is accomblished by taking the angle to the opposite
69 // which is equivalent to flipping the second coordinate.
70 vec = VectorUtil::passiveRotatedBy(vec, ROOT::Math::XYVector(coordinate.X(), -coordinate.Y()));
71 }
72
73 Eigen::Vector2d fitPhiVecZeroSteps(const Eigen::Matrix<double, 3, 3>& xylCov, double& chi2)
74 {
75 chi2 = xylCov(1, 1) + 2 * xylCov(1, 2) + xylCov(2, 2);
76 return Eigen::Vector2d(1, 0);
77 }
78
79 Eigen::Vector2d fitPhiVecOneStep(const Eigen::Matrix<double, 3, 3>& xylCov, double& chi2)
80 {
81 const double phi = (xylCov(0, 1) + xylCov(0, 2)) / xylCov(0, 0);
82 chi2 = xylCov(1, 1) + 2 * xylCov(1, 2) + xylCov(2, 2) - phi * (xylCov(0, 1) + xylCov(0, 2));
83 return Eigen::Vector2d(std::cos(phi), std::sin(phi));
84 }
85
86 Eigen::Vector2d fitPhiVecBrent(const Eigen::Matrix<double, 3, 3>& xylCov, int nIter, double& chi2)
87 {
88 const Eigen::Matrix< double, 2, 2> A = xylCov.topLeftCorner<2, 2>();
89 const Eigen::Matrix< double, 2, 1> b = xylCov.topRightCorner<2, 1>();
90 const double c = xylCov(2, 2);
91
92 auto calcReducedChi2 = [&A, &b](double phi) -> double {
93 Eigen::Matrix<double, 2, 1> normal(std::sin(phi), -std::cos(phi));
94 return ((normal.transpose() * A - 2 * b.transpose()) * normal)[0];
95 };
96
97 ROOT::Math::Functor1D functor(calcReducedChi2);
98 ROOT::Math::BrentMinimizer1D bm;
99 bm.SetFunction(functor, -M_PI / 2, M_PI / 2);
100 bm.Minimize(nIter); // #iterations, abs. error, rel. error
101
102 chi2 = bm.FValMinimum() + c;
103 const double phi = bm.XMinimum();
104 return Eigen::Vector2d(std::cos(phi), std::sin(phi));
105 }
106
113 struct LineFitPrecursor {
115 ROOT::Math::XYVector coordinate;
116
118 Eigen::Array<double, 1, 3> averages;
119
121 Eigen::Matrix<double, 3, 3> covariances;
122
124 Eigen::Vector2d phiVec;
125
127 double chi2 = 0.0;
128
130 double sumW = 0.0;
131 };
132
133 template<int N>
134 LineFitPrecursor fitPrecursor(Matrix<double, N, 3> xylIn,
135 Matrix<double, N, 1> wIn,
136 int nSteps)
137 {
138 LineFitPrecursor precursor;
139
141 ROOT::Math::XYVector coordinate = getTangentialForwardDirection(xylIn);
142 // Sometimes the calculation of the tangent fails due to misestimated dirft lengths
143 // Make best effort the continue the calculation
144 if (VectorUtil::hasNAN(coordinate)) {
145 coordinate = getCenterForwardDirection(xylIn);
146 }
147
148 rotate(coordinate, xylIn);
149
150 auto xyl = mapToEigen(xylIn);
151 auto w = mapToEigen(wIn).array();
152
153 Eigen::Array< double, 1, 3> averages = (xyl.array().colwise() * w).colwise().sum() / w.sum();
154 Eigen::Matrix< double, N, 3> deltas = xyl.array().rowwise() - averages;
155 Eigen::Matrix< double, N, 3> weightedDeltas = deltas.array().colwise() * w;
156 Eigen::Matrix< double, 3, 3> covariances = deltas.transpose() * weightedDeltas / w.sum();
157
158 Eigen::Vector2d phiVec;
159 double chi2 = 0.0;
160 if (nSteps == 0) {
161 phiVec = fitPhiVecZeroSteps(covariances, chi2);
162 } else if (nSteps == 1) {
163 phiVec = fitPhiVecOneStep(covariances, chi2);
164 } else {
165 phiVec = fitPhiVecBrent(covariances, nSteps, chi2);
166 }
167 chi2 *= w.sum();
168
169 precursor.coordinate = coordinate;
170 precursor.averages = averages;
171 precursor.covariances = covariances;
172 precursor.phiVec = phiVec;
173 precursor.chi2 = chi2;
174 precursor.sumW = w.sum();
175 return precursor;
176 }
177
179 UncertainParameterLine2D lineFromPrecursor(const LineFitPrecursor& precursor, int ndf)
180 {
181 const ROOT::Math::XYVector& coordinate = precursor.coordinate;
182 const Eigen::Array<double, 1, 3>& averages = precursor.averages;
183 const Eigen::Matrix<double, 3, 3>& covariances = precursor.covariances;
184 const Eigen::Vector2d& phiVec = precursor.phiVec;
185 const double chi2 = precursor.chi2;
186
187 double meanArcLength = averages.topLeftCorner<1, 2>().matrix() * phiVec;
188 double varArcLength = phiVec.transpose() * covariances.topLeftCorner<2, 2>() * phiVec;
189 double p = precursor.sumW;
190
191 using namespace NLineParameterIndices;
192 LinePrecision linePrecision;
193 linePrecision(c_Phi0, c_Phi0) = p * (varArcLength + meanArcLength * meanArcLength);
194 linePrecision(c_Phi0, c_I) = p * meanArcLength;
195 linePrecision(c_I, c_Phi0) = p * meanArcLength;
196 linePrecision(c_I, c_I) = p;
197 LineCovariance lineCovariance = LineUtil::covarianceFromFullPrecision(linePrecision);
198
199 ROOT::Math::XYVector tangential(phiVec(0), phiVec(1));
200 ROOT::Math::XYVector n12 = VectorUtil::Orthogonal(tangential, ERotation::c_Clockwise);
201 double n0 = averages(2) - averages(0) * n12.x() - averages(1) * n12.y();
202 ROOT::Math::XYVector support = -n12 * n0;
203
204 // Transform the normal vector back into the original coordinate system.
205 unrotate(coordinate, support);
206 unrotate(coordinate, tangential);
207
208 ParameterLine2D parameterLine2D(support, tangential);
209 return UncertainParameterLine2D(parameterLine2D, lineCovariance, chi2, ndf);
210 }
211
212 template<int N>
213 UncertainParameterLine2D fit(Matrix<double, N, 3> xylIn,
214 Matrix<double, N, 1> wIn,
215 int nSteps)
216 {
217 LineFitPrecursor precursor = fitPrecursor(std::move(xylIn), std::move(wIn), nSteps);
218 int ndf = N - 2;
219 return lineFromPrecursor(precursor, ndf);
220 }
221
222}
223
224namespace {
226 ROOT::Math::XYVector fillFacetObservations(const CDCFacet& facet,
227 // cppcheck-suppress constParameterReference ; xyl and w are output matrices filled below
228 Matrix<double, 3, 3>& xyl,
229 // cppcheck-suppress constParameterReference ; xyl and w are output matrices filled below
230 Matrix<double, 3, 1>& w)
231 {
232 const CDCRLWireHit& startRLWireHit = facet.getStartRLWireHit();
233 const CDCRLWireHit& middleRLWireHit = facet.getMiddleRLWireHit();
234 const CDCRLWireHit& endRLWireHit = facet.getEndRLWireHit();
235
236 const ROOT::Math::XYVector support = middleRLWireHit.getWireHit().getRefPos2D();
237
238 const double startDriftLengthVar = startRLWireHit.getRefDriftLengthVariance();
239 const ROOT::Math::XYVector startWirePos2D = startRLWireHit.getWireHit().getRefPos2D();
240 xyl(0, 0) = startWirePos2D.x() - support.x();
241 xyl(0, 1) = startWirePos2D.y() - support.y();
242 xyl(0, 2) = startRLWireHit.getSignedRefDriftLength();
243 w(0) = 1.0 / startDriftLengthVar;
244
245 const double middleDriftLengthVar = middleRLWireHit.getRefDriftLengthVariance();
246 const ROOT::Math::XYVector middleWirePos2D = middleRLWireHit.getWireHit().getRefPos2D();
247 xyl(1, 0) = middleWirePos2D.x() - support.x();
248 xyl(1, 1) = middleWirePos2D.y() - support.y();
249 xyl(1, 2) = middleRLWireHit.getSignedRefDriftLength();
250 w(1) = 1.0 / middleDriftLengthVar;
251
252 const double endDriftLengthVar = endRLWireHit.getRefDriftLengthVariance();
253 const ROOT::Math::XYVector endWirePos2D = endRLWireHit.getWireHit().getRefPos2D();
254 xyl(2, 0) = endWirePos2D.x() - support.x();
255 xyl(2, 1) = endWirePos2D.y() - support.y();
256 xyl(2, 2) = endRLWireHit.getSignedRefDriftLength();
257 w(2) = 1.0 / endDriftLengthVar;
258
259 return support;
260 }
261}
262
263double FacetFitter::fit(const CDCFacet& facet, int nSteps, double maxChi2)
264{
265 // Measurement matrix
266 Matrix<double, 3, 3> xyl = Matrix<double, 3, 3>::Zero();
267
268 // Weight matrix
269 Matrix<double, 3, 1> w = Matrix<double, 3, 1>::Zero();
270
271 const ROOT::Math::XYVector support = fillFacetObservations(facet, xyl, w);
272
273 LineFitPrecursor precursor = fitPrecursor(std::move(xyl), std::move(w), nSteps);
274
275 // Construct and commit the fit line only when it is wanted: always for an
276 // infinite maxChi2, otherwise only if the fit passes the cut. Skipping it for
277 // failing facets avoids building the line and its covariance matrix. A NaN chi2
278 // fails "chi2 <= maxChi2", so it is committed only in the unbounded case.
279 if (std::isinf(maxChi2) or precursor.chi2 <= maxChi2) {
280 const int ndf = 1;
281 UncertainParameterLine2D fitLine = lineFromPrecursor(precursor, ndf);
282 fitLine.passiveMoveBy(-support);
283 facet.setFitLine(fitLine);
284 }
285 return precursor.chi2;
286}
287
288
289namespace {
291 ROOT::Math::XYVector fillFacetPairObservations(const CDCFacet& fromFacet,
292 const CDCFacet& toFacet,
293 // cppcheck-suppress constParameterReference ; xyl and w are output matrices filled below
294 Matrix<double, 6, 3>& xyl,
295 // cppcheck-suppress constParameterReference ; xyl and w are output matrices filled below
296 Matrix<double, 6, 1>& w)
297 {
298 const ROOT::Math::XYVector support = VectorUtil::average(fromFacet.getMiddleWireHit().getRefPos2D(),
299 toFacet.getMiddleWireHit().getRefPos2D());
300 {
301 const CDCRLWireHit& startRLWireHit = fromFacet.getStartRLWireHit();
302 const CDCRLWireHit& middleRLWireHit = fromFacet.getMiddleRLWireHit();
303 const CDCRLWireHit& endRLWireHit = fromFacet.getEndRLWireHit();
304
305 const double startDriftLengthVar = startRLWireHit.getRefDriftLengthVariance();
306 const ROOT::Math::XYVector startWirePos2D = startRLWireHit.getWireHit().getRefPos2D();
307 xyl(0, 0) = startWirePos2D.x() - support.x();
308 xyl(0, 1) = startWirePos2D.y() - support.y();
309 xyl(0, 2) = startRLWireHit.getSignedRefDriftLength();
310 w(0) = 1.0 / startDriftLengthVar;
311
312 const double middleDriftLengthVar = middleRLWireHit.getRefDriftLengthVariance();
313 const ROOT::Math::XYVector middleWirePos2D = middleRLWireHit.getWireHit().getRefPos2D();
314 xyl(1, 0) = middleWirePos2D.x() - support.x();
315 xyl(1, 1) = middleWirePos2D.y() - support.y();
316 xyl(1, 2) = middleRLWireHit.getSignedRefDriftLength();
317 w(1) = 1.0 / middleDriftLengthVar;
318
319 const double endDriftLengthVar = endRLWireHit.getRefDriftLengthVariance();
320 const ROOT::Math::XYVector endWirePos2D = endRLWireHit.getWireHit().getRefPos2D();
321 xyl(2, 0) = endWirePos2D.x() - support.x();
322 xyl(2, 1) = endWirePos2D.y() - support.y();
323 xyl(2, 2) = endRLWireHit.getSignedRefDriftLength();
324 w(2) = 1.0 / endDriftLengthVar;
325 }
326
327 {
328 const CDCRLWireHit& startRLWireHit = toFacet.getStartRLWireHit();
329 const CDCRLWireHit& middleRLWireHit = toFacet.getMiddleRLWireHit();
330 const CDCRLWireHit& endRLWireHit = toFacet.getEndRLWireHit();
331
332 const double startDriftLengthVar = startRLWireHit.getRefDriftLengthVariance();
333 const ROOT::Math::XYVector startWirePos2D = startRLWireHit.getWireHit().getRefPos2D();
334 xyl(3, 0) = startWirePos2D.x() - support.x();
335 xyl(3, 1) = startWirePos2D.y() - support.y();
336 xyl(3, 2) = startRLWireHit.getSignedRefDriftLength();
337 w(3) = 1.0 / startDriftLengthVar;
338
339 const double middleDriftLengthVar = middleRLWireHit.getRefDriftLengthVariance();
340 const ROOT::Math::XYVector middleWirePos2D = middleRLWireHit.getWireHit().getRefPos2D();
341 xyl(4, 0) = middleWirePos2D.x() - support.x();
342 xyl(4, 1) = middleWirePos2D.y() - support.y();
343 xyl(4, 2) = middleRLWireHit.getSignedRefDriftLength();
344 w(4) = 1.0 / middleDriftLengthVar;
345
346 const double endDriftLengthVar = endRLWireHit.getRefDriftLengthVariance();
347 const ROOT::Math::XYVector endWirePos2D = endRLWireHit.getWireHit().getRefPos2D();
348 xyl(5, 0) = endWirePos2D.x() - support.x();
349 xyl(5, 1) = endWirePos2D.y() - support.y();
350 xyl(5, 2) = endRLWireHit.getSignedRefDriftLength();
351 w(5) = 1.0 / endDriftLengthVar;
352 }
353
354 return support;
355 }
356}
357
359 const CDCFacet& toFacet,
360 int nSteps)
361{
362 // Observations matrix
363 Matrix<double, 6, 3> xyl = Matrix<double, 6, 3>::Zero();
364
365 // Weight matrix
366 Matrix<double, 6, 1> w = Matrix<double, 6, 1>::Zero();
367
368 const ROOT::Math::XYVector support = fillFacetPairObservations(fromFacet, toFacet, xyl, w);
369
370 UncertainParameterLine2D fitLine{ ::fit(std::move(xyl), std::move(w), nSteps) };
371 fitLine.passiveMoveBy(-support);
372 return fitLine;
373}
374
375double FacetFitter::fitChi2(const CDCFacet& fromFacet,
376 const CDCFacet& toFacet)
377{
378 // Observations matrix
379 Matrix<double, 6, 3> xyl = Matrix<double, 6, 3>::Zero();
380
381 // Weight matrix
382 Matrix<double, 6, 1> w = Matrix<double, 6, 1>::Zero();
383
384 fillFacetPairObservations(fromFacet, toFacet, xyl, w);
385
386 // The chi2 is invariant against the translation by the support point
387 constexpr const int nSteps = 0;
388 return fitPrecursor(std::move(xyl), std::move(w), nSteps).chi2;
389}
390
391
392UncertainParameterLine2D FacetFitter::fit(TrackingUtilities::Matrix<double, 3, 3> xyl,
393 TrackingUtilities::Matrix<double, 3, 1> w,
394 int nSteps)
395{
396 return ::fit(std::move(xyl), std::move(w), nSteps);
397}
static double fitChi2(const TrackingUtilities::CDCFacet &fromFacet, const TrackingUtilities::CDCFacet &toFacet)
Calculate only the chi2 of a line fitted to the hits of the two facets.
static double fit(const TrackingUtilities::CDCFacet &facet, int nSteps=100, double maxChi2=std::numeric_limits< double >::infinity())
Fits a proper line to the facet and returns the chi2.
Class representing a triple of neighboring oriented wire with additional trajectory information.
Definition CDCFacet.h:33
void setFitLine(const UncertainParameterLine2D &fitLine) const
Setter for the contained line fit information.
Definition CDCFacet.h:69
const CDCWireHit & getMiddleWireHit() const
Getter for the hit wire of the second oriented wire hit.
CDCRLWireHit & getStartRLWireHit()
Getter for the first oriented wire hit.
CDCRLWireHit & getEndRLWireHit()
Getter for the third oriented wire hit.
CDCRLWireHit & getMiddleRLWireHit()
Getter for the second oriented wire hit.
Class representing an oriented hit wire including a hypotheses whether the causing track passes left ...
const CDCWireHit & getWireHit() const
Getter for the wire hit associated with the oriented hit.
double getRefDriftLengthVariance() const
Getter for the variance of the drift length at the reference position of the wire.
double getSignedRefDriftLength() const
Getter for the drift length at the reference position of the wire.
const ROOT::Math::XYVector & getRefPos2D() const
The two dimensional reference position (z=0) of the underlying wire.
A line with a support point and tangential vector.
const ROOT::Math::XYVector & tangential() const
Gives the tangential vector of the line.
static ParameterLine2D touchingCircles(const ROOT::Math::XYVector &fromCenter, double fromSignedRadius, const ROOT::Math::XYVector &toCenter, double toSignedRadius)
Constructs a line touching two circles in one point each.
static PlainMatrix< T, M, N > Identity()
Construct an identity matrix.
Definition PlainMatrix.h:74
static PlainMatrix< T, M, N > Zero()
Construct a matrix initialized with zeros.
Definition PlainMatrix.h:67
A parameter line including including an line covariance matrix which is interpreted as located in the...
void passiveMoveBy(const ROOT::Math::XYVector &by)
Moves the coordinate system by the vector by.
Namespace to hide the contained enum constants.
Abstract base class for different kinds of events.