Belle II Software  release-06-02-00
calibTools.h
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 
10 
11 #pragma once
12 
13 #include <tracking/calibration/Splitter.h>
14 #include <TMatrixDSym.h>
15 #include <TVector3.h>
16 #include <functional>
17 #include <map>
18 
19 #include <framework/database/EventDependency.h>
20 #include <framework/datastore/StoreArray.h>
21 #include <calibration/CalibrationAlgorithm.h>
22 
23 #include <Eigen/Dense>
24 
25 namespace Belle2 {
31  // General functions to perform the calibration
32  // Notice that the goal of the calibration is to estimate the parameters
33  // of the Gaussian distribution: center + covariance matrix describing the spread.
34  // In general it requires more data to determine the spread, so there can be
35  // several calib. subintervals with different values of
36  // the center position of the Gaussian (mean) but with identical spread parameters.
37  // The longer intervals of constant spread are called "intervals"
38  // The shorter intervals of constant mean value are called "subintervals"
39  // In general there are several subintervals withing single interval
40  // By definition a subinterval always belongs only to single interval.
41 
42 
44  inline TMatrixDSym toTMatrixDSym(Eigen::MatrixXd mIn)
45  {
46  TMatrixDSym mOut(mIn.rows());
47  for (int i = 0; i < mIn.rows(); ++i)
48  for (int j = 0; j < mIn.cols(); ++j)
49  mOut(i, j) = (mIn(i, j) + mIn(j, i)) / 2.;
50  return mOut;
51  }
52 
54  inline TVector3 toTVector3(Eigen::VectorXd vIn)
55  {
56  return TVector3(vIn(0), vIn(1), vIn(2));
57  }
58 
60  inline int getID(const std::vector<double>& breaks, double t)
61  {
62  for (int i = 0; i < int(breaks.size()) + 1; ++i) {
63  double s = (i == 0) ? 0 : breaks[i - 1];
64  double e = (i == int(breaks.size())) ? 1e20 : breaks[i];
65  if (s <= t && t < e)
66  return i;
67  }
68  return -1;
69  }
70 
72  struct CalibPars {
73  std::vector<Eigen::VectorXd> cnt;
74  std::vector<Eigen::MatrixXd> cntUnc;
75  Eigen::MatrixXd spreadMat;
76 
77  double spreadUnc;
78  double shift;
79  double shiftUnc;
80  std::vector<double> pulls;
81  int size() const {return cnt.size();}
82  };
83 
84 
86  struct CalibrationData {
88  std::vector<std::map<ExpRun, std::pair<double, double>>> subIntervals;
89 
90  std::vector<ExpRunEvt> breakPoints;
91 
93 
94  bool isCalibrated = false;
95 
96  };
97 
98 
99 
101  inline void extrapolateCalibration(std::vector<CalibrationData>& calVec)
102  {
103  //put closest neighbor, where the statistic was low or algo failed
104  for (unsigned i = 0; i < calVec.size(); ++i) {
105  if (calVec[i].pars.cnt.size() != 0) continue;
106  const auto& r = calVec[i].subIntervals;
107  double Start, End;
108  std::tie(Start, End) = Splitter::getStartEnd(r);
109 
110  Eigen::Vector3d ipNow;
111  Eigen::MatrixXd ipeNow;
112  Eigen::MatrixXd sizeMatNow;
113 
114  double distMin = 1e20;
115  //Find the closest calibrated interval
116  for (unsigned j = 0; j < calVec.size(); ++j) {
117  if (calVec[j].isCalibrated == false) continue; //skip not-calibrated intervals
118  const auto& rJ = calVec[j].subIntervals;
119  for (unsigned jj = 0; jj < rJ.size(); ++jj) { //loop over subintervals
120  const auto& rNow = rJ[jj];
121  double s = rNow.begin()->second.first;
122  double e = rNow.rbegin()->second.second;
123 
124  double dist1 = (s - End >= 0) ? (s - End) : 1e20;
125  double dist2 = (Start - e >= 0) ? (Start - e) : 1e20;
126  double dist = std::min(dist1, dist2);
127 
128  if (dist < distMin) {
129  ipNow = calVec[j].pars.cnt.at(jj);
130  ipeNow = calVec[j].pars.cntUnc.at(jj);
131  sizeMatNow = calVec[j].pars.spreadMat;
132  distMin = dist;
133  }
134  }
135  }
136 
137  //Store it to vectors
138  calVec[i].pars.cnt.resize(r.size());
139  calVec[i].pars.cntUnc.resize(r.size());
140  for (unsigned ii = 0; ii < r.size(); ++ii) {
141  calVec[i].pars.cnt.at(ii) = ipNow;
142  calVec[i].pars.cntUnc.at(ii) = ipeNow;
143  }
144  calVec[i].pars.spreadMat = sizeMatNow;
145  }
146 
147  }
148 
150  inline void addShortRun(std::vector<CalibrationData>& calVec, std::pair<ExpRun, std::pair<double, double>> shortRun)
151  {
152  double shortStart = shortRun.second.first;
153  double shortEnd = shortRun.second.second;
154 
155  double distMin = 1e20;
156  int iMin = -1, jMin = -1;
157 
158  for (unsigned i = 0; i < calVec.size(); ++i) {
159  if (calVec[i].isCalibrated == false)
160  continue;
161  for (unsigned j = 0; j < calVec[i].subIntervals.size(); ++j) {
162  for (auto I : calVec[i].subIntervals[j]) {
163  double s = I.second.first;
164  double e = I.second.second;
165 
166  double dist1 = (s - shortEnd >= 0) ? (s - shortEnd) : 1e20;
167  double dist2 = (shortStart - e >= 0) ? (shortStart - e) : 1e20;
168  double dist = std::min(dist1, dist2);
169 
170  if (dist < distMin) {
171  distMin = dist;
172  iMin = i;
173  jMin = j;
174  }
175  }
176  }
177  }
178 
179  B2ASSERT("Must be found", iMin != -1 && jMin != -1);
180  calVec[iMin].subIntervals[jMin].insert(shortRun);
181  }
182 
185  inline double encodeNumber(double val, unsigned num)
186  {
187  double factor = pow(FLT_RADIX, DBL_MANT_DIG);
188  static const long long fEnc = pow(2, 32); //32 binary digits for encoded number
189 
190  int e; //exponent of the number
191  double mantisa = std::frexp(val, &e);
192  long long mantisaI = mantisa * factor; //mantissa as integer
193 
194  if (val != 0)
195  mantisaI = (mantisaI / fEnc) * fEnc + num; //adding encoded number to last digits of mantissa
196  else {
197  mantisaI = factor / 2 + num;
198  e = -100; //if the val is zero, ensure very small number by the exponent
199  }
200 
201  double newVal = ldexp(mantisaI / factor, e);
202 
203  return newVal;
204  }
205 
207  inline unsigned decodeNumber(double val)
208  {
209  double factor = pow(FLT_RADIX, DBL_MANT_DIG);
210  static const long long fEnc = pow(2, 32); //32 binary digits for encoded number
211 
212  int e;
213  double mantisa = std::frexp(val, &e);
214  long long mantisaI = mantisa * factor;
215 
216  return (mantisaI % fEnc);
217  }
218 
219 
220 
221 
223  template<typename Evt>
224  inline void storePayloads(const std::vector<Evt>& evts, const std::vector<CalibrationData>& calVecConst, std::string objName,
225  std::function<TObject*(Eigen::VectorXd, Eigen::MatrixXd, Eigen::MatrixXd) > getCalibObj)
226  {
227  auto calVec = calVecConst;
228 
229  // Loop to store payloads
230  ExpRun exprunLast(-1, -1); //last exprun
231  EventDependency* intraRun = nullptr;
232 
233  // Loop over calibration intervals
234  for (unsigned i = 0; i < calVec.size(); ++i) {
235  const auto& r = calVec[i].subIntervals; // splits[i];
236  // Loop over calibration subintervals
237  for (int k = 0; k < int(r.size()); ++k) {
238 
239  for (auto I : r[k]) { //interval required to be within single run
240  ExpRun exprun = I.first;
241 
242  //Encode Start+End time in seconds of the payload
243  if (calVec[i].pars.cntUnc.at(k).rows() == 3) {
244  calVec[i].pars.cntUnc.at(k)(0, 1) = calVec[i].pars.cntUnc.at(k)(1, 0) = encodeNumber(calVec[i].pars.cntUnc.at(k)(0, 1),
245  round(I.second.first * 3600));
246  calVec[i].pars.cntUnc.at(k)(0, 2) = calVec[i].pars.cntUnc.at(k)(2, 0) = encodeNumber(calVec[i].pars.cntUnc.at(k)(0, 2),
247  round(I.second.second * 3600));
248  } else {
249  calVec[i].pars.cntUnc.at(k)(0, 0) = encodeNumber(calVec[i].pars.cntUnc.at(k)(0, 0), round(I.second.first * 3600));
250  calVec[i].pars.spreadMat(0, 0) = encodeNumber(calVec[i].pars.spreadMat(0, 0), round(I.second.second * 3600));
251  }
252 
253  TObject* obj = getCalibObj(calVec[i].pars.cnt.at(k), calVec[i].pars.cntUnc.at(k), calVec[i].pars.spreadMat);
254  if (exprun != exprunLast) { //if new run
255  if (intraRun) { //if not first -> store
256  auto m_iov = IntervalOfValidity(exprunLast.exp, exprunLast.run, exprunLast.exp, exprunLast.run);
257  Database::Instance().storeData(objName, intraRun, m_iov);
258  }
259 
260  intraRun = new EventDependency(obj);
261  } else {
262  int breakPoint;
263  if (k - 1 >= 0) {
264  breakPoint = calVec[i].breakPoints.at(k - 1).evt;
265  B2ASSERT("Payload saving consistency", calVec[i].breakPoints.at(k - 1).run == exprun.run);
266  } else {
267  B2ASSERT("Payload saving consistency", i != 0);
268  double rStart, rEnd;
269  std::tie(rStart, rEnd) = Splitter::getStartEnd(r);
270  auto pos = getPosition(evts, rStart);
271  breakPoint = pos.evt;
272  B2ASSERT("Payload saving consistency", pos.run == exprun.run);
273  }
274  intraRun->add(breakPoint, obj);
275  }
276  exprunLast = exprun;
277  }
278  } //end loop over calibration subintervals
279 
280  } //end loop over calibration intervals
281 
282  //Store the last entry
283  auto m_iov = IntervalOfValidity(exprunLast.exp, exprunLast.run, exprunLast.exp, exprunLast.run);
284  Database::Instance().storeData(objName, intraRun, m_iov);
285  }
286 
287 
289  inline void storePayloadsNoIntraRun(const std::vector<CalibrationData>& calVecConst, std::string objName,
290  std::function<TObject*(Eigen::VectorXd, Eigen::MatrixXd, Eigen::MatrixXd) > getCalibObj)
291  {
292  auto calVec = calVecConst;
293 
294  // Check that there is no intra-run dependence
295  std::set<ExpRun> existingRuns;
296  for (unsigned i = 0; i < calVec.size(); ++i) {
297  const auto& r = calVec[i].subIntervals;
298  // Loop over calibration subintervals
299  for (int k = 0; k < int(r.size()); ++k) {
300 
301  for (auto I : r[k]) {
302  ExpRun exprun = I.first;
303  // make sure that the run isn't already in the list, to avoid duplicity
304  if (existingRuns.count(exprun) != 0)
305  B2FATAL("Intra-run dependence exists");
306  existingRuns.insert(exprun);
307  }
308  }
309  }
310 
311 
312  // Loop over calibration intervals
313  for (unsigned i = 0; i < calVec.size(); ++i) {
314  const auto& r = calVec[i].subIntervals; // splits[i];
315  // Loop over calibration subintervals
316  for (unsigned k = 0; k < r.size(); ++k) {
317 
318  TObject* obj = getCalibObj(calVec[i].pars.cnt.at(k), calVec[i].pars.cntUnc.at(k), calVec[i].pars.spreadMat);
319 
320  ExpRun start = (r[k].cbegin()->first);
321  ExpRun last = (r[k].crbegin()->first);
322 
323  auto iov = IntervalOfValidity(start.exp, start.run, last.exp, last.run);
324  Database::Instance().storeData(objName, obj, iov);
325 
326 
327  } //end loop over calibration subintervals
328  } //end loop over calibration intervals
329 
330  }
331 
333  template<typename Evt, typename Fun>
334  inline CalibrationData runAlgorithm(const std::vector<Evt>& evts, std::vector<std::map<ExpRun, std::pair<double, double>>> range,
335  Fun runCalibAnalysis
336  )
337  {
338  CalibrationData calD;
339  auto& r = range;
340  double rStart, rEnd;
341  std::tie(rStart, rEnd) = Splitter::getStartEnd(r);
342  B2INFO("Start of loop startTime endTime : " << rStart << " " << rEnd);
343 
344  auto breaks = Splitter::getBreaks(r);
345 
346  std::vector<Evt> evtsNow;
347 
348  std::vector<int> Counts(breaks.size() + 1, 0);
349  // Select events belonging to the interval
350  for (const auto& ev : evts) {
351  if (rStart <= ev.t && ev.t < rEnd) {
352  evtsNow.push_back(ev);
353  ++Counts.at(getID(breaks, ev.t));
354  }
355  }
356 
357  B2ASSERT("Number of intervals vs number of breakPoints", r.size() == breaks.size() + 1);
358 
359  //Merge smallest interval if with low stat (try it 10times)
360  for (int k = 0; k < 10; ++k) {
361  int iMin = min_element(Counts.begin(), Counts.end()) - Counts.begin();
362  if (Counts.size() >= 2 && Counts[iMin] < 50) { //merge with neighbor if possible
363  auto iM = -1;
364  if (iMin == 0)
365  iM = iMin + 1;
366  else if (iMin == int(Counts.size()) - 1)
367  iM = iMin - 1;
368  else {
369  if (Counts[iMin + 1] < Counts[iMin - 1])
370  iM = iMin + 1;
371  else
372  iM = iMin - 1;
373  }
374  B2ASSERT("Number of intervals equal to size of counters", r.size() == Counts.size());
375 
376  r.at(iM) = Splitter::mergeIntervals(r[iM], r[iMin]);
377  r.erase(r.begin() + iMin);
378  breaks = Splitter::getBreaks(r);
379  Counts[iM] += Counts[iMin];
380  Counts.erase(Counts.begin() + iMin);
381  }
382  }
383 
384  B2INFO("#events " << " : " << evtsNow.size());
385  B2INFO("Breaks size " << " : " << breaks.size());
386 
387  calD.breakPoints = convertSplitPoints(evtsNow, breaks);
388 
389  calD.subIntervals = r;
390 
391  if (breaks.size() > 0)
392  B2INFO("StartOfCalibInterval (run,evtNo,vtxIntervalsSize) " << calD.breakPoints.at(0).run << " " <<
393  calD.breakPoints.at(0).evt << " " << calD.breakPoints.size());
394 
395 
396  //If too few events, let have the output empty
397  //Will be filled with the closest neighbor at the next stage
398  if (evtsNow.size() < 50) {
399  return calD;
400  }
401 
402  // Run the calibration
403  B2INFO("Start of running calibration over calibration interval");
404  tie(calD.pars.cnt, calD.pars.cntUnc, calD.pars.spreadMat) = runCalibAnalysis(evtsNow, breaks);
405  calD.pars.pulls.resize(calD.pars.cnt.size());
406  B2INFO("End of running analysis - SpreadMatX : " << sqrt(abs(calD.pars.spreadMat(0, 0))));
407  B2ASSERT("All subintervals have calibration of the mean value", calD.pars.cnt.size() == r.size());
408  B2ASSERT("All subintervals have calibration of the unc. of mean", calD.pars.cntUnc.size() == r.size());
409 
410  calD.isCalibrated = true;
411 
412  return calD;
413  }
414 
415 
426  template<typename Fun1, typename Fun2>
427  CalibrationAlgorithm::EResult runCalibration(TTree* tracks, const std::string& calibName, Fun1 GetEvents, Fun2 calibAnalysis,
428  std::function<TObject*(Eigen::VectorXd, Eigen::MatrixXd, Eigen::MatrixXd)> calibObjCreator,
429  TString m_lossFunctionOuter, TString m_lossFunctionInner)
430  {
431  // Check that there are at least some data
432  if (!tracks || tracks->GetEntries() < 15) {
433  if (tracks)
434  B2WARNING("Too few data : " << tracks->GetEntries());
435  return CalibrationAlgorithm::EResult::c_NotEnoughData;
436  }
437  B2INFO("Number of tracks: " << tracks->GetEntries());
438 
439  // Tree to vector of Events
440  auto evts = GetEvents(tracks);
441 
442  //Time range for each ExpRun
443  std::map<ExpRun, std::pair<double, double>> runsInfoOrg = getRunInfo(evts);
444  std::map<ExpRun, std::pair<double, double>> runsRemoved; //map with time intervals of very short runs
445  auto runsInfo = filter(runsInfoOrg, 2. / 60, runsRemoved); //include only runs longer than 2mins
446 
447  // If nothing remains
448  if (runsInfo.size() == 0) {
449  B2WARNING("Too short run");
450  return CalibrationAlgorithm::EResult::c_NotEnoughData;
451  }
452 
453  // Get intervals based on the input loss functions
454  Splitter splt;
455  auto splits = splt.getIntervals(runsInfo, evts, m_lossFunctionOuter, m_lossFunctionInner);
456 
457  //Loop over all calibration intervals
458  std::vector<CalibrationData> calVec;
459  for (auto s : splits) {
460  CalibrationData calD = runAlgorithm(evts, s, calibAnalysis); // run the calibration over the interval s
461  calVec.push_back(calD);
462  }
463 
464  // extrapolate results to the low-stat intervals
465  extrapolateCalibration(calVec);
466 
467  // Include removed short runs
468  for (auto shortRun : runsRemoved) {
469  addShortRun(calVec, shortRun);
470  }
471 
472  // Store Payloads to files
473  storePayloads(evts, calVec, calibName, calibObjCreator);
474 
475  return CalibrationAlgorithm::EResult::c_OK;
476  }
477 
478 
480 }
EResult
The result of calibration.
Class for handling changing conditions as a function of event number.
void add(unsigned int event, TObject *object)
Add an object to the intra run dependency.
A class that describes the interval of experiments/runs for which an object in the database is valid.
Class that allows to split runs into the intervals of intended properties given by the lossFunction.
Definition: Splitter.h:108
std::vector< std::vector< std::map< ExpRun, std::pair< double, double > > > > getIntervals(const std::map< ExpRun, std::pair< double, double >> &runs, std::vector< Evt > evts, TString lossFunctionOuter, TString lossFunctionInner, double atomSize=3./60)
Function to merge/divide runs into the calibration intervals of given characteristic length.
Definition: Splitter.h:163
static std::vector< double > getBreaks(std::vector< std::map< ExpRun, std::pair< double, double >>> res)
Get vector with breaks of the calib.
Definition: Splitter.h:131
static std::pair< double, double > getStartEnd(std::vector< std::map< ExpRun, std::pair< double, double >>> res)
Get the start/end time of the calibration interval (vector of the calib.
Definition: Splitter.h:120
static Database & Instance()
Instance of a singleton Database.
Definition: Database.cc:41
bool storeData(const std::string &name, TObject *object, const IntervalOfValidity &iov)
Store an object in the database.
Definition: Database.cc:140
std::map< ExpRun, std::pair< double, double > > filter(const std::map< ExpRun, std::pair< double, double >> &runs, double cut, std::map< ExpRun, std::pair< double, double >> &runsRemoved)
filter events to remove runs shorter than cut, it stores removed runs in runsRemoved
Definition: Splitter.cc:40
ExpRunEvt getPosition(const std::vector< Evt > &events, double tEdge)
Get the exp-run-evt number from the event time [hours].
Definition: Splitter.h:341
void storePayloadsNoIntraRun(const std::vector< CalibrationData > &calVecConst, std::string objName, std::function< TObject *(Eigen::VectorXd, Eigen::MatrixXd, Eigen::MatrixXd) > getCalibObj)
Store payloads to files, where calib data have no intra-run dependence.
Definition: calibTools.h:289
void addShortRun(std::vector< CalibrationData > &calVec, std::pair< ExpRun, std::pair< double, double >> shortRun)
Extrapolate calibration to the very short runs which were filtered before.
Definition: calibTools.h:150
unsigned decodeNumber(double val)
Decode the integer number encoded in val.
Definition: calibTools.h:207
double encodeNumber(double val, unsigned num)
Encode integer num into double val such that val is nearly not changed (maximally by a relative shift...
Definition: calibTools.h:185
static std::map< ExpRun, std::pair< double, double > > mergeIntervals(std::map< ExpRun, std::pair< double, double >> I1, std::map< ExpRun, std::pair< double, double >> I2)
Merge two subintervals into one subinterval.
Definition: Splitter.cc:307
int getID(const std::vector< double > &breaks, double t)
get id of the time point t
Definition: calibTools.h:60
CalibrationData runAlgorithm(const std::vector< Evt > &evts, std::vector< std::map< ExpRun, std::pair< double, double >>> range, Fun runCalibAnalysis)
run calibration algorithm for single calibration interval
Definition: calibTools.h:334
TMatrixDSym toTMatrixDSym(Eigen::MatrixXd mIn)
Function that converts Eigen symmetric matrix to ROOT matrix.
Definition: calibTools.h:44
CalibrationAlgorithm::EResult runCalibration(TTree *tracks, const std::string &calibName, Fun1 GetEvents, Fun2 calibAnalysis, std::function< TObject *(Eigen::VectorXd, Eigen::MatrixXd, Eigen::MatrixXd)> calibObjCreator, TString m_lossFunctionOuter, TString m_lossFunctionInner)
Run the the calibration over the whole event sample.
Definition: calibTools.h:427
void extrapolateCalibration(std::vector< CalibrationData > &calVec)
Extrapolate calibration to intervals where it failed.
Definition: calibTools.h:101
void storePayloads(const std::vector< Evt > &evts, const std::vector< CalibrationData > &calVecConst, std::string objName, std::function< TObject *(Eigen::VectorXd, Eigen::MatrixXd, Eigen::MatrixXd) > getCalibObj)
Store payloads to files.
Definition: calibTools.h:224
std::map< ExpRun, std::pair< double, double > > getRunInfo(const std::vector< Evt > &evts)
Get the map of runs, where each run contains pair with start/end time [hours].
Definition: Splitter.h:312
std::vector< ExpRunEvt > convertSplitPoints(const std::vector< Evt > &events, std::vector< double > splitPoints)
Convert splitPoints [hours] to breakPoints in ExpRunEvt.
Definition: Splitter.h:363
TVector3 toTVector3(Eigen::VectorXd vIn)
Function that converts Eigen vector to ROOT vector.
Definition: calibTools.h:54
Abstract base class for different kinds of events.
The parameters related to single calibration interval.
Definition: calibTools.h:72
std::vector< Eigen::MatrixXd > cntUnc
vector of uncertainties of means for each calib. subinterval
Definition: calibTools.h:74
std::vector< double > pulls
vector of pulls between mumu and hadB methods (for eCMS)
Definition: calibTools.h:80
Eigen::MatrixXd spreadMat
spread CovMatrix
Definition: calibTools.h:75
double shift
difference between eCMS for hadronic B decay method and mumu method, i.e. hadB - mumu
Definition: calibTools.h:78
std::vector< Eigen::VectorXd > cnt
vector of means for each calib. subinterval
Definition: calibTools.h:73
double shiftUnc
stat uncertainty of the shift
Definition: calibTools.h:79
int size() const
number of the subintervals
Definition: calibTools.h:81
double spreadUnc
stat uncertainty of the spread (for eCMS)
Definition: calibTools.h:77
Parameters and data relevant for single calibration interval.
Definition: calibTools.h:86
std::vector< std::map< ExpRun, std::pair< double, double > > > subIntervals
vector of the start and end times of the calibration subintervals
Definition: calibTools.h:88
CalibPars pars
The parameters of the calibration itself.
Definition: calibTools.h:92
bool isCalibrated
true if calibration run was successful
Definition: calibTools.h:94
std::vector< ExpRunEvt > breakPoints
vector with break points positions
Definition: calibTools.h:90
Struct containing exp number and run number.
Definition: Splitter.h:51
int exp
experiment number
Definition: Splitter.h:52
int run
run number
Definition: Splitter.h:53