Belle II Software  release-08-01-10
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 
23 using namespace Belle2;
24 
25 
26 namespace {
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 
76 void 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 
103 std::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 
140 std::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 
199 std::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];
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 measurment 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];
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];
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
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
bool wasFitSuccessful(const genfit::AbsTrackRep *representation=nullptr) const
Returns true if the last fit with the given representation was successful.
Definition: RecoTrack.cc:336
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
unsigned int getNumberOfCDCHits() const
Return the number of cdc hits.
Definition: RecoTrack.h:427
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
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:116
bool fit(RecoTrack &recoTrack, genfit::AbsTrackRep *trackRepresentation) const
Fit a reco track with a given non-default track representation.
Definition: TrackFitter.cc:107
Contains the measurement and covariance in raw detector coordinates.
virtual const AbsHMatrix * constructHMatrix(const AbsTrackRep *) const =0
Returns a new AbsHMatrix object.
Abstract base class for a track representation.
Definition: AbsTrackRep.h:66
virtual unsigned int getDim() const =0
Get the dimension of the state vector used by the track representation.
Class where important numbers and properties of a fit can be stored.
Definition: FitStatus.h:80
double getChi2() const
Get chi^2 of the fit.
Definition: FitStatus.h:120
bool isTrackPruned() const
Has the track been pruned after the fit?
Definition: FitStatus.h:116
double getNdf() const
Get the degrees of freedom of the fit.
Definition: FitStatus.h:122
FitStatus for use with AbsKalmanFitter implementations.
Collects information needed and produced by a AbsKalmanFitter implementations and is specific to one ...
#StateOnPlane with additional covariance matrix.
Measured coordinates on a plane.
#StateOnPlane with linearized transport to that #ReferenceStateOnPlane from previous and next #Refere...
Object containing AbsMeasurement and AbsFitterInfo objects.
Definition: TrackPoint.h:46
KalmanFitterInfo * getKalmanFitterInfo(const AbsTrackRep *rep=nullptr) const
Helper to avoid casting.
Definition: TrackPoint.cc:180
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
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