Belle II Software prerelease-11-00-00d
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/Vector2D.h>
15
16#include <tracking/trackingUtilities/numerics/EigenView.h>
17
18#include <Eigen/Core>
19
20#include <cmath>
21
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 Vector2D getCenterForwardDirection(const Matrix<double, N, 3>& xyl)
33 {
35 Vector2D coordinate(xyl(N - 1, 0) - xyl(0, 0), xyl(N - 1, 1) - xyl(0, 1));
36 return coordinate.unit();
37 }
38
39 template<int N>
40 Vector2D getTangentialForwardDirection(const Matrix<double, N, 3>& xyl)
41 {
43 Vector2D fromPos(xyl(0, 0), xyl(0, 1));
44 double fromL = xyl(0, 2);
45
46 Vector2D 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 Vector2D coordinate = tangentLine.tangential();
51 return coordinate.unit();
52 }
53
54 template<int N>
55 void rotate(Vector2D 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(Vector2D coordinate, Vector2D& 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 = vec.passiveRotatedBy(coordinate.flippedSecond());
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 Vector2D 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 Vector2D 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 (coordinate.hasNAN()) {
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 Vector2D& 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 Vector2D tangential(phiVec(0), phiVec(1));
200 Vector2D n12 = tangential.orthogonal(ERotation::c_Clockwise);
201 double n0 = averages(2) - averages(0) * n12.x() - averages(1) * n12.y();
202 Vector2D 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 Vector2D fillFacetObservations(const CDCFacet& facet,
227 Matrix<double, 3, 3>& xyl,
228 Matrix<double, 3, 1>& w)
229 {
230 const CDCRLWireHit& startRLWireHit = facet.getStartRLWireHit();
231 const CDCRLWireHit& middleRLWireHit = facet.getMiddleRLWireHit();
232 const CDCRLWireHit& endRLWireHit = facet.getEndRLWireHit();
233
234 const Vector2D support = middleRLWireHit.getWireHit().getRefPos2D();
235
236 const double startDriftLengthVar = startRLWireHit.getRefDriftLengthVariance();
237 const Vector2D startWirePos2D = startRLWireHit.getWireHit().getRefPos2D();
238 xyl(0, 0) = startWirePos2D.x() - support.x();
239 xyl(0, 1) = startWirePos2D.y() - support.y();
240 xyl(0, 2) = startRLWireHit.getSignedRefDriftLength();
241 w(0) = 1.0 / startDriftLengthVar;
242
243 const double middleDriftLengthVar = middleRLWireHit.getRefDriftLengthVariance();
244 const Vector2D middleWirePos2D = middleRLWireHit.getWireHit().getRefPos2D();
245 xyl(1, 0) = middleWirePos2D.x() - support.x();
246 xyl(1, 1) = middleWirePos2D.y() - support.y();
247 xyl(1, 2) = middleRLWireHit.getSignedRefDriftLength();
248 w(1) = 1.0 / middleDriftLengthVar;
249
250 const double endDriftLengthVar = endRLWireHit.getRefDriftLengthVariance();
251 const Vector2D endWirePos2D = endRLWireHit.getWireHit().getRefPos2D();
252 xyl(2, 0) = endWirePos2D.x() - support.x();
253 xyl(2, 1) = endWirePos2D.y() - support.y();
254 xyl(2, 2) = endRLWireHit.getSignedRefDriftLength();
255 w(2) = 1.0 / endDriftLengthVar;
256
257 return support;
258 }
259}
260
261double FacetFitter::fit(const CDCFacet& facet, int nSteps, double maxChi2)
262{
263 // Measurement matrix
264 Matrix<double, 3, 3> xyl = Matrix<double, 3, 3>::Zero();
265
266 // Weight matrix
267 Matrix<double, 3, 1> w = Matrix<double, 3, 1>::Zero();
268
269 const Vector2D support = fillFacetObservations(facet, xyl, w);
270
271 LineFitPrecursor precursor = fitPrecursor(std::move(xyl), std::move(w), nSteps);
272
273 // Construct and commit the fit line only when it is wanted: always for an
274 // infinite maxChi2, otherwise only if the fit passes the cut. Skipping it for
275 // failing facets avoids building the line and its covariance matrix. A NaN chi2
276 // fails "chi2 <= maxChi2", so it is committed only in the unbounded case.
277 if (std::isinf(maxChi2) or precursor.chi2 <= maxChi2) {
278 const int ndf = 1;
279 UncertainParameterLine2D fitLine = lineFromPrecursor(precursor, ndf);
280 fitLine.passiveMoveBy(-support);
281 facet.setFitLine(fitLine);
282 }
283 return precursor.chi2;
284}
285
286
287namespace {
289 Vector2D fillFacetPairObservations(const CDCFacet& fromFacet,
290 const CDCFacet& toFacet,
291 Matrix<double, 6, 3>& xyl,
292 Matrix<double, 6, 1>& w)
293 {
294 const Vector2D support = Vector2D::average(fromFacet.getMiddleWireHit().getRefPos2D(),
295 toFacet.getMiddleWireHit().getRefPos2D());
296 {
297 const CDCRLWireHit& startRLWireHit = fromFacet.getStartRLWireHit();
298 const CDCRLWireHit& middleRLWireHit = fromFacet.getMiddleRLWireHit();
299 const CDCRLWireHit& endRLWireHit = fromFacet.getEndRLWireHit();
300
301 const double startDriftLengthVar = startRLWireHit.getRefDriftLengthVariance();
302 const Vector2D startWirePos2D = startRLWireHit.getWireHit().getRefPos2D();
303 xyl(0, 0) = startWirePos2D.x() - support.x();
304 xyl(0, 1) = startWirePos2D.y() - support.y();
305 xyl(0, 2) = startRLWireHit.getSignedRefDriftLength();
306 w(0) = 1.0 / startDriftLengthVar;
307
308 const double middleDriftLengthVar = middleRLWireHit.getRefDriftLengthVariance();
309 const Vector2D middleWirePos2D = middleRLWireHit.getWireHit().getRefPos2D();
310 xyl(1, 0) = middleWirePos2D.x() - support.x();
311 xyl(1, 1) = middleWirePos2D.y() - support.y();
312 xyl(1, 2) = middleRLWireHit.getSignedRefDriftLength();
313 w(1) = 1.0 / middleDriftLengthVar;
314
315 const double endDriftLengthVar = endRLWireHit.getRefDriftLengthVariance();
316 const Vector2D endWirePos2D = endRLWireHit.getWireHit().getRefPos2D();
317 xyl(2, 0) = endWirePos2D.x() - support.x();
318 xyl(2, 1) = endWirePos2D.y() - support.y();
319 xyl(2, 2) = endRLWireHit.getSignedRefDriftLength();
320 w(2) = 1.0 / endDriftLengthVar;
321 }
322
323 {
324 const CDCRLWireHit& startRLWireHit = toFacet.getStartRLWireHit();
325 const CDCRLWireHit& middleRLWireHit = toFacet.getMiddleRLWireHit();
326 const CDCRLWireHit& endRLWireHit = toFacet.getEndRLWireHit();
327
328 const double startDriftLengthVar = startRLWireHit.getRefDriftLengthVariance();
329 const Vector2D startWirePos2D = startRLWireHit.getWireHit().getRefPos2D();
330 xyl(3, 0) = startWirePos2D.x() - support.x();
331 xyl(3, 1) = startWirePos2D.y() - support.y();
332 xyl(3, 2) = startRLWireHit.getSignedRefDriftLength();
333 w(3) = 1.0 / startDriftLengthVar;
334
335 const double middleDriftLengthVar = middleRLWireHit.getRefDriftLengthVariance();
336 const Vector2D middleWirePos2D = middleRLWireHit.getWireHit().getRefPos2D();
337 xyl(4, 0) = middleWirePos2D.x() - support.x();
338 xyl(4, 1) = middleWirePos2D.y() - support.y();
339 xyl(4, 2) = middleRLWireHit.getSignedRefDriftLength();
340 w(4) = 1.0 / middleDriftLengthVar;
341
342 const double endDriftLengthVar = endRLWireHit.getRefDriftLengthVariance();
343 const Vector2D endWirePos2D = endRLWireHit.getWireHit().getRefPos2D();
344 xyl(5, 0) = endWirePos2D.x() - support.x();
345 xyl(5, 1) = endWirePos2D.y() - support.y();
346 xyl(5, 2) = endRLWireHit.getSignedRefDriftLength();
347 w(5) = 1.0 / endDriftLengthVar;
348 }
349
350 return support;
351 }
352}
353
355 const CDCFacet& toFacet,
356 int nSteps)
357{
358 // Observations matrix
359 Matrix<double, 6, 3> xyl = Matrix<double, 6, 3>::Zero();
360
361 // Weight matrix
362 Matrix<double, 6, 1> w = Matrix<double, 6, 1>::Zero();
363
364 const Vector2D support = fillFacetPairObservations(fromFacet, toFacet, xyl, w);
365
366 UncertainParameterLine2D fitLine{ ::fit(std::move(xyl), std::move(w), nSteps) };
367 fitLine.passiveMoveBy(-support);
368 return fitLine;
369}
370
371double FacetFitter::fitChi2(const CDCFacet& fromFacet,
372 const CDCFacet& toFacet)
373{
374 // Observations matrix
375 Matrix<double, 6, 3> xyl = Matrix<double, 6, 3>::Zero();
376
377 // Weight matrix
378 Matrix<double, 6, 1> w = Matrix<double, 6, 1>::Zero();
379
380 fillFacetPairObservations(fromFacet, toFacet, xyl, w);
381
382 // The chi2 is invariant against the translation by the support point
383 constexpr const int nSteps = 0;
384 return fitPrecursor(std::move(xyl), std::move(w), nSteps).chi2;
385}
386
387
388UncertainParameterLine2D FacetFitter::fit(TrackingUtilities::Matrix<double, 3, 3> xyl,
389 TrackingUtilities::Matrix<double, 3, 1> w,
390 int nSteps)
391{
392 return ::fit(std::move(xyl), std::move(w), nSteps);
393}
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:32
void setFitLine(const UncertainParameterLine2D &fitLine) const
Setter for the contained line fit information.
Definition CDCFacet.h:67
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.
static ParameterLine2D touchingCircles(const Vector2D &fromCenter, double fromSignedRadius, const Vector2D &toCenter, double toSignedRadius)
Constructs a line touching two circles in one point each.
const Vector2D & tangential() const
Gives the tangential vector of the line.
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 Vector2D &by)
Moves the coordinate system by the vector by.
A two dimensional vector which is equipped with functions for correct handling of orientation relate...
Definition Vector2D.h:36
Vector2D flippedSecond() const
Makes a copy of the vector with the second coordinate flipped (no difference between active and passi...
Definition Vector2D.h:392
static Vector2D average(const Vector2D &one, const Vector2D &two)
Constructs the average of two vectors.
Definition Vector2D.h:100
double x() const
Getter for the x coordinate.
Definition Vector2D.h:622
Vector2D orthogonal() const
Orthogonal vector to the counterclockwise direction.
Definition Vector2D.h:316
bool hasNAN() const
Checks if one of the coordinates is NAN.
Definition Vector2D.h:165
double y() const
Getter for the y coordinate.
Definition Vector2D.h:637
Vector2D unit() const
Returns a unit vector colaligned with this.
Definition Vector2D.h:348
Vector2D passiveRotatedBy(const Vector2D &phiVec) const
Returns a transformed vector version rotated by the given vector.
Definition Vector2D.h:616
Namespace to hide the contained enum constants.
Abstract base class for different kinds of events.