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