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