Belle II Software development
TimeExtractionUtils.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/eventTimeExtraction/utilities/TimeExtractionUtils.h>
9#include <tracking/trackFitting/fitter/base/TrackFitter.h>
10#include <tracking/dataobjects/RecoTrack.h>
11
12#include <genfit/KalmanFitStatus.h>
13#include <genfit/KalmanFitterInfo.h>
14
15#include <genfit/Tools.h>
16
17#include <cdc/dataobjects/CDCRecoHit.h>
18#include <framework/logging/Logger.h>
19#include <TDecompChol.h>
20
21#include <numeric>
22
23using namespace Belle2;
24
25
26namespace {
30 template<typename T>
31 void setSubOnDiagonal(TMatrixTSym <T>& target, int iRow,
32 const TMatrixTSym <T>& source)
33 {
34 const int nColsTarget = target.GetNrows();
35 const int nColsSource = source.GetNrows();
36
37 T* pt = target.GetMatrixArray();
38 const T* ps = source.GetMatrixArray();
39
40 for (int i = 0; i < nColsSource; ++i) {
41 for (int j = 0; j < nColsSource; ++j) {
42 pt[(iRow + i) * nColsTarget + (iRow + j)] = ps[i * nColsSource + j];
43 }
44 }
45 }
46
51 template<typename T>
52 void setSubOffDiagonal(TMatrixTSym <T>& target, int iRow, int iCol,
53 const TMatrixT <T>& source)
54 {
55 const int nColsTarget = target.GetNcols();
56 const int nRowsSource = source.GetNrows();
57 const int nColsSource = source.GetNcols();
58
59 T* pt = target.GetMatrixArray();
60 const T* ps = source.GetMatrixArray();
61
62 for (int i = 0; i < nRowsSource; ++i) {
63 for (int j = 0; j < nColsSource; ++j) {
64 pt[(iRow + i) * nColsTarget + (iCol + j)] = ps[i * nColsSource + j];
65 }
66 }
67 for (int i = 0; i < nRowsSource; ++i) {
68 for (int j = 0; j < nColsSource; ++j) {
69 pt[(iCol + j) * nColsTarget + (iRow + i)] = ps[i * nColsSource + j];
70 }
71 }
72 }
73}
74
75
76void TimeExtractionUtils::addEventT0WithQuality(const std::vector<RecoTrack*>& recoTracks,
77 StoreObjPtr<EventT0>& eventT0,
78 std::vector<EventT0::EventT0Component>& eventT0WithQualityIndex)
79{
80 if (not eventT0->hasEventT0()) {
81 B2DEBUG(25, "No event t0 is set. Not testing.");
82 return;
83 }
84
85 // As we do not know what happened before, we have to set the dirty flag here to force a refit.
86 const auto& chi2_with_ndf = TimeExtractionUtils::getChi2WithFit(recoTracks, true);
87 const double quality = chi2_with_ndf.second;
88
89 if (std::isnan(quality)) {
90 B2DEBUG(25, "The calculated quality is nan. Not using this EventT0 of " << eventT0->getEventT0());
91 return;
92 }
93
94 B2DEBUG(25, "The iteration gave a result of " << quality << " for " << eventT0->getEventT0());
95 EventT0::EventT0Component eventT0Component = *(eventT0->getEventT0Component());
96 eventT0Component.quality = quality;
97 eventT0WithQualityIndex.emplace_back(eventT0Component);
98 eventT0->addTemporaryEventT0(eventT0Component);
99 eventT0->setEventT0(eventT0Component);
100}
101
102
103std::pair<double, double> TimeExtractionUtils::getChi2WithFit(const std::vector<RecoTrack*>& recoTracks, bool setDirtyFlag)
104{
105 TrackFitter trackFitter;
106
107 double summedChi2 = 0;
108 double summedNDF = 0;
109 unsigned int numberOfFittableRecoTracks = 0;
110
111 for (RecoTrack* recoTrack : recoTracks) {
112 if (setDirtyFlag) {
113 recoTrack->setDirtyFlag();
114 }
115
116 if (not trackFitter.fit(*recoTrack)) {
117 continue;
118 }
119
120 const double chi2 = TimeExtractionUtils::extractReducedChi2(*recoTrack);
121 const double ndf = recoTrack->getTrackFitStatus()->getNdf();
122
123 if (std::isnan(chi2)) {
124 continue;
125 }
126
127 numberOfFittableRecoTracks++;
128 summedChi2 += chi2;
129 summedNDF += ndf;
130 }
131
132 if (numberOfFittableRecoTracks == 0) {
133 return {NAN, NAN};
134 }
135
136 return {summedChi2 / numberOfFittableRecoTracks, summedNDF / numberOfFittableRecoTracks};
137}
138
139
140std::pair<double, double> TimeExtractionUtils::getExtractedTimeAndUncertaintyWithFit(const std::vector<RecoTrack*>& recoTracks,
141 bool setDirtyFlag)
142{
143 TrackFitter trackFitter;
144
145 double sumFirstDerivatives = 0;
146 double sumSecondDerivatives = 0;
147 unsigned int numberOfFittableRecoTracks = 0;
148
149 for (RecoTrack* recoTrack : recoTracks) {
150 if (setDirtyFlag) {
151 recoTrack->setDirtyFlag();
152 }
153
154 if (not trackFitter.fit(*recoTrack)) {
155 continue;
156 }
157
158 const auto& chi2Derivatives = TimeExtractionUtils::getChi2Derivatives(*recoTrack);
159 const double dchi2da = chi2Derivatives.first;
160 const double d2chi2da2 = chi2Derivatives.second;
161
162 if (std::isnan(dchi2da) or std::isnan(d2chi2da2)) {
163 continue;
164 }
165
166 if (d2chi2da2 > 20) {
167 B2DEBUG(25, "Track with bad second derivative");
168 continue;
169 }
170
171 numberOfFittableRecoTracks++;
172 sumFirstDerivatives += dchi2da;
173 sumSecondDerivatives += d2chi2da2;
174 }
175
176 if (numberOfFittableRecoTracks == 0) {
177 return {NAN, NAN};
178 }
179
180 const double extractedEventT0 = sumFirstDerivatives / sumSecondDerivatives;
181 const double extractedEventT0Uncertainty = std::sqrt(2 / sumSecondDerivatives);
182
183 return {extractedEventT0, extractedEventT0Uncertainty};
184}
185
187{
188 const double chi2 = recoTrack.getTrackFitStatus()->getChi2();
189 const double ndf = recoTrack.getTrackFitStatus()->getNdf();
190
191 if (ndf == 0) {
192 return NAN;
193 }
194
195 return chi2 / ndf;
196}
197
198
199std::pair<double, double> TimeExtractionUtils::getChi2Derivatives(const RecoTrack& recoTrack)
200{
201 // Check if track is ready to be used.
202 if (recoTrack.getNumberOfCDCHits() == 0) {
203 B2DEBUG(200, "No CDC hits in track.");
204 return {NAN, NAN};
205 }
206
207 if (recoTrack.wasFitSuccessful()) {
208 const genfit::FitStatus* fs = recoTrack.getTrackFitStatus();
209 if (!fs)
210 return {NAN, NAN};
211 if (fs->isTrackPruned()) {
212 B2WARNING("Skipping pruned track");
213 return {NAN, NAN};
214 }
215 }
216
217 try {
218 const std::vector<int>& vDimMeas = TimeExtractionUtils::getMeasurementDimensions(recoTrack);
219
220 TMatrixDSym fullCovariance;
221 bool success = buildFullCovarianceMatrix(recoTrack, fullCovariance);
222 if (!success) {
223 // Error printed inside.
224 return {NAN, NAN};
225 }
226 TMatrixDSym fullResidualCovariance;
227 TMatrixDSym inverseFullMeasurementCovariance;
228 buildFullResidualCovarianceMatrix(recoTrack, vDimMeas, fullCovariance, fullResidualCovariance,
229 inverseFullMeasurementCovariance);
230
231 TVectorD residuals;
232 TVectorD residualsTimeDerivative;
233 buildResidualsAndTimeDerivative(recoTrack, vDimMeas, residuals, residualsTimeDerivative);
234
235 // Equations with their numbers from 0810.2241:
236 // 2 At V^-1 (V - HCHt) V^-1 r (9)
237 // = 2 At V^-1 r for fitted tracks (12)
238 const double dchi2da = 2. * residualsTimeDerivative * (inverseFullMeasurementCovariance * residuals);
239
240 // (2 At V^-1 (V - HCHt) V^-1 A)^-1, note that this should be (10)
241 // SimilarityT(...) if (...) were a matrix.
242 const double d2chi2da2 = 2. * fullResidualCovariance.Similarity(inverseFullMeasurementCovariance * residualsTimeDerivative);
243
244
245 if (d2chi2da2 > 20) {
246 B2DEBUG(200, "Track with bad second derivative");
247 return {NAN, NAN};
248 }
249 return {dchi2da, d2chi2da2};
250 } catch (...) {
251 B2DEBUG(25, "Failed time extraction - skipping track");
252 return {NAN, NAN};
253 }
254}
255
256
258{
259 const auto& hitPoints = recoTrack.getHitPointsWithMeasurement();
260
261 std::vector<int> vDimMeas;
262 vDimMeas.reserve(hitPoints.size());
263
264 for (const auto& hit : hitPoints) {
265 vDimMeas.push_back(hit->getRawMeasurement(0)->getDim());
266 }
267
268 return vDimMeas;
269}
270
271
273 TMatrixDSym& fullCovariance)
274{
275 const auto* kfs = dynamic_cast<const genfit::KalmanFitStatus*>(recoTrack.getTrackFitStatus());
276
277 if (!kfs) {
278 B2ERROR("Track not fitted with a Kalman fitter.");
279 return false;
280 }
281
282 if (!kfs->isFitConverged()) {
283 B2ERROR("Track fit didn't converge.");
284 return false;
285 }
286
287 if (!kfs->isFittedWithReferenceTrack()) {
288 B2ERROR("No reference track.");
289 return false;
290 }
291
292 const auto& hitPoints = recoTrack.getHitPointsWithMeasurement();
293 const unsigned int nPoints = hitPoints.size();
294 const genfit::AbsTrackRep* rep = recoTrack.getCardinalRepresentation();
295 const int nDim = rep->getDim();
296
297 fullCovariance.ResizeTo(nPoints * nDim, nPoints * nDim);
298 std::vector<TMatrixD> vFitterGain;
299 vFitterGain.reserve(nPoints);
300 for (unsigned int i = 0; i < nPoints; ++i) {
301 const genfit::TrackPoint* tp = hitPoints[i];
302 const genfit::KalmanFitterInfo* fi = tp->getKalmanFitterInfo();
303 if (!fi) {
304 B2DEBUG(25, "Missing KalmanFitterInfo - skipping track");
305 return false;
306 }
307
308 // Diagonal part of the full covariance matrix are the covariances
309 // of the smoothed states.
310 const genfit::MeasuredStateOnPlane& mop = fi->getFittedState();
311 setSubOnDiagonal(fullCovariance, i * nDim, mop.getCov());
312
313 // Build the corresponding smoother gain matrix.
314 if (i + 1 < nPoints) {
315 const genfit::TrackPoint* tpNext = hitPoints[i + 1];
316 const genfit::KalmanFitterInfo* fiNext = tpNext->getKalmanFitterInfo();
317 if (!fiNext) {
318 B2DEBUG(25, "Missing next KalmanFitterInfo - skipping track");
319 return false;
320 }
321
322 // update at i
323 const genfit::MeasuredStateOnPlane* update = fi->getForwardUpdate();
324 // transport to i+1 prediction at i+1
325 const genfit::ReferenceStateOnPlane* rsop = fiNext->getReferenceState();
326 if (rsop) {
327 const genfit::MeasuredStateOnPlane* pred = fiNext->getForwardPrediction();
328
329 // Evaluate (C^i_i+1)^-1 F_i
330 TDecompChol decomp(pred->getCov());
331 TMatrixD F = rsop->getForwardTransportMatrix();
332 decomp.MultiSolve(F);
333
334 // Calculate gain matrix as
335 // C_i F_i^T (C^i_i+1)^-1 = C_i ((C^i_i+1)^-1 F_i)^T
336 // in the notation of 0810.2241
337 vFitterGain.emplace_back(TMatrixD(update->getCov(),
338 TMatrixD::kMultTranspose, F));
339 } else {
340 B2DEBUG(150, "No reference state, substituting empty fitter gain matrix.");
341 vFitterGain.emplace_back(TMatrixD(5, 5));
342 }
343 }
344
345 // Build the off-diagonal elements.
346 TMatrixD offDiag = mop.getCov();
347 for (int j = i - 1; j >= 0; --j) {
348 offDiag = TMatrixD(vFitterGain[j], TMatrixD::kMult, offDiag);
349 setSubOffDiagonal(fullCovariance, j * nDim, i * nDim, offDiag);
350 }
351 }
352
353 return true;
354}
355
357 const std::vector<int>& vDimMeas,
358 const TMatrixDSym& fullCovariance,
359 TMatrixDSym& fullResidualCovariance,
360 TMatrixDSym& inverseFullMeasurementCovariance)
361{
362 const auto& hitPoints = recoTrack.getHitPointsWithMeasurement();
363 const unsigned int nPoints = hitPoints.size();
364 const genfit::AbsTrackRep* rep = recoTrack.getCardinalRepresentation();
365 const int nDim = rep->getDim();
366 int measurementDimensions = std::accumulate(vDimMeas.begin(), vDimMeas.end(), 0);
367 fullResidualCovariance.ResizeTo(measurementDimensions, measurementDimensions);
368 inverseFullMeasurementCovariance.ResizeTo(measurementDimensions, measurementDimensions);
369
370 // Put together the parts containing the track covariances.
371 std::vector<TMatrixD> HMatrices;
372 HMatrices.reserve(nPoints);
373 for (unsigned int i = 0, index = 0; i < nPoints; ++i) {
374 const genfit::TrackPoint* tp = hitPoints[i];
375 const genfit::AbsMeasurement* meas = tp->getRawMeasurement(0);
376 std::unique_ptr<const genfit::AbsHMatrix> pH(meas->constructHMatrix(rep));
377 const TMatrixD& H = pH->getMatrix();
378 int nDimMeas = vDimMeas[i];
379 TMatrixDSym cov = fullCovariance.GetSub(i * nDim, (i + 1) * nDim - 1,
380 i * nDim, (i + 1) * nDim - 1);
381 pH->HMHt(cov);
382 setSubOnDiagonal(fullResidualCovariance, index, cov);
383
384 int indexOffDiag = index;
385 for (int j = i - 1; j >= 0; --j) {
386 int nDimMeasJ = HMatrices[j].GetNrows();
387 indexOffDiag -= nDimMeasJ;
388
389 TMatrixD offDiag(HMatrices[j], TMatrixD::kMult,
390 TMatrixD(fullCovariance.GetSub(nDim * j, nDim * (j + 1) - 1,
391 nDim * i, nDim * (i + 1) - 1),
392 TMatrixD::kMultTranspose, H));
393
394 setSubOffDiagonal(fullResidualCovariance, indexOffDiag, index, offDiag);
395 }
396
397 index += nDimMeas;
398 HMatrices.push_back(H);
399 }
400
401 // Add the measurement covariances, also calculate their full
402 // (block-diagonal) inverse covariance matrix.
403 fullResidualCovariance *= -1;
404 inverseFullMeasurementCovariance = 0;
405 for (unsigned int i = 0, index = 0; i < nPoints; ++i) {
406 const genfit::TrackPoint* tp = hitPoints[i];
407 const genfit::KalmanFitterInfo* fi = tp->getKalmanFitterInfo();
408 const genfit::MeasurementOnPlane& mop = fi->getAvgWeightedMeasurementOnPlane();
409 TMatrixDSym cov = mop.getCov();
410 const int dim = cov.GetNrows();
411 setSubOnDiagonal(fullResidualCovariance, index,
412 cov + fullResidualCovariance.GetSub(index, index + dim - 1,
413 index, index + dim - 1));
414
415 genfit::tools::invertMatrix(cov);
416 setSubOnDiagonal(inverseFullMeasurementCovariance, index, cov);
417
418 index += dim;
419 }
420
421 return true;
422}
423
424
426 const std::vector<int>& vDimMeas,
427 TVectorD& residuals,
428 TVectorD& residualTimeDerivative)
429{
430 // The residuals and their derivatives WRT the event time.
431
432 int measurementDimensions = std::accumulate(vDimMeas.begin(), vDimMeas.end(), 0);
433 residuals.ResizeTo(measurementDimensions);
434 residualTimeDerivative.ResizeTo(measurementDimensions);
435
436 const auto& hitPoints = recoTrack.getHitPointsWithMeasurement();
437 const unsigned int nPoints = hitPoints.size();
438 for (unsigned int i = 0, index = 0; i < nPoints; ++i) {
439 const genfit::TrackPoint* tp = hitPoints[i];
440 const genfit::KalmanFitterInfo* fi = tp->getKalmanFitterInfo();
441
442 const std::vector<double>& weights = fi->getWeights();
443 TVectorD weightedResidual(vDimMeas[i]);
444 for (size_t iMeas = 0; iMeas < fi->getNumMeasurements(); ++iMeas) {
445 weightedResidual += weights[iMeas] * fi->getResidual(iMeas).getState();
446 }
447 residuals.SetSub(index, weightedResidual);
448 if (dynamic_cast<const CDCRecoHit*>(tp->getRawMeasurement(0))) {
449 const CDCRecoHit* hit = static_cast<const CDCRecoHit*>(tp->getRawMeasurement(0));
450 std::vector<double> deriv = hit->timeDerivativesMeasurementsOnPlane(fi->getFittedState());
451
452 double weightedDeriv = weights[0] * deriv[0] + weights[1] * deriv[1];
453 residualTimeDerivative[index] = weightedDeriv;
454 }
455 index += vDimMeas[i];
456 }
457}
This class is used to transfer CDC information to the track fit.
Definition: CDCRecoHit.h:32
This is the Reconstruction Event-Data Model Track.
Definition: RecoTrack.h:79
bool wasFitSuccessful(const genfit::AbsTrackRep *representation=nullptr) const
Returns true if the last fit with the given representation was successful.
Definition: RecoTrack.cc:336
genfit::AbsTrackRep * getCardinalRepresentation() const
Get a pointer to the cardinal track representation. You are not allowed to modify or delete it!
Definition: RecoTrack.h:631
unsigned int getNumberOfCDCHits() const
Return the number of cdc hits.
Definition: RecoTrack.h:427
const genfit::FitStatus * getTrackFitStatus(const genfit::AbsTrackRep *representation=nullptr) const
Return the track fit status for the given representation or for the cardinal one. You are not allowed...
Definition: RecoTrack.h:621
const std::vector< genfit::TrackPoint * > & getHitPointsWithMeasurement() const
Return a list of measurements and track points, which can be used e.g. to extrapolate....
Definition: RecoTrack.h:708
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:96
static std::pair< double, double > getExtractedTimeAndUncertaintyWithFit(const std::vector< RecoTrack * > &recoTracks, bool setDirtyFlag)
Fit the tracks and extract the chi2 derivatives.
static std::pair< double, double > getChi2WithFit(const std::vector< RecoTrack * > &recoTracks, bool setDirtyFlag)
Fit the tracks and extract the reduced chi2.
static void buildResidualsAndTimeDerivative(const RecoTrack &recoTrack, const std::vector< int > &vDimMeas, TVectorD &residuals, TVectorD &residualTimeDerivative)
Calculate the weighted residuals (weighted with the DAF weight) of each hit and the time derivative o...
static std::pair< double, double > getChi2Derivatives(const RecoTrack &recoTrack)
Extract the derivatives d chi^2 / d alpha and d^2 chi^2 / (d alpha)^2 from the reco track by calculat...
static bool buildFullCovarianceMatrix(const RecoTrack &recoTrack, TMatrixDSym &fullCovariance)
Build the full covariance matrix of the given reco track and fills it into the given TMatrixDSym.
static double extractReducedChi2(const RecoTrack &recoTrack)
Small helper function to extract the reduced chi^2 (chi^2/ndf). Returns NaN if ndf is 0.
static void addEventT0WithQuality(const std::vector< RecoTrack * > &recoTracks, StoreObjPtr< EventT0 > &eventT0, std::vector< EventT0::EventT0Component > &eventT0WithQualityIndex)
Append an event-t0 value with quality information.
static std::vector< int > getMeasurementDimensions(const RecoTrack &recoTrack)
Get a list of dimensions for each measurement in the reco track.
static bool buildFullResidualCovarianceMatrix(const RecoTrack &recoTrack, const std::vector< int > &vDimMeas, const TMatrixDSym &fullCovariance, TMatrixDSym &fullResidualCovariance, TMatrixDSym &inverseFullMeasurementCovariance)
Build the full covariance matrix of the residuals of the measurements out of the full covariance matr...
Algorithm class to handle the fitting of RecoTrack objects.
Definition: TrackFitter.h:121
bool fit(RecoTrack &recoTrack, genfit::AbsTrackRep *trackRepresentation, bool resortHits=false) const
Fit a reco track with a given non-default track representation.
Definition: TrackFitter.cc:108
Abstract base class for different kinds of events.
Structure for storing the extracted event t0s together with its detector and its uncertainty.
Definition: EventT0.h:33
double quality
Storage for the internal quality estimation for a single algorithm. Only comparable for all temporari...
Definition: EventT0.h:56