Belle II Software  release-05-01-25
GlobalTimeLine.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2019 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Tadeas Bilka *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <alignment/GlobalTimeLine.h>
12 
13 #include <alignment/Manager.h>
14 #include <framework/core/PyObjConvUtils.h>
15 #include <framework/database/EventDependency.h>
16 
17 namespace Belle2 {
22  namespace alignment {
23  namespace timeline {
24 
25  EventMetaData gotoNextChangeRunWise(TimeTable& timeTable, int uid, int& timeid)
26  {
27  auto& row = std::get<TableData>(timeTable).at(uid);
28 
29  auto lastIntervalStartEvent = std::get<EventHeader>(timeTable).at(row.size() - 1);
30 
31  if (timeid >= int(row.size())) {
32  return lastIntervalStartEvent;
33  }
34 
35  auto cell = row.at(timeid);
36  auto cellRun = std::get<RunHeader>(timeTable).at(timeid);
37  for (long unsigned int iCol = timeid + 1; iCol < row.size(); ++iCol) {
38  if (row.at(iCol) != cell && std::get<RunHeader>(timeTable).at(iCol) != cellRun) {
39  timeid = iCol - 1;
40  return std::get<EventHeader>(timeTable).at(iCol);
41  //return std::get<EventHeader>(timeTable).at(iCol - 1);
42  }
43  }
44  timeid = row.size() - 1;
45 
46  return lastIntervalStartEvent;
47  }
48 
49  EventMetaData gotoNextChangeInRun(TimeTable& timeTable, int uid, int& timeid)
50  {
51  auto& row = std::get<TableData>(timeTable).at(uid);
52  auto cell = row.at(timeid);
53  auto cellRun = std::get<RunHeader>(timeTable).at(timeid);
54  for (long unsigned int iCol = timeid + 1; iCol < row.size(); ++iCol) {
55  if (std::get<RunHeader>(timeTable).at(iCol) != cellRun) {
56  timeid = iCol - 1;
57  return std::get<EventHeader>(timeTable).at(iCol - 1);
58  }
59  if (row.at(iCol) != cell) {
60  timeid = iCol;
61  return std::get<EventHeader>(timeTable).at(iCol);
62  }
63  }
64  return std::get<EventHeader>(timeTable).at(timeid);
65  }
66 
67  PayloadsTable TimeIdsTable2PayloadsTable(TimeTable& timeTable, const GlobalParamVector& vector)
68  {
69  PayloadsTable payloadsTable;
70 
71  for (auto& uid_obj : vector.getGlobalParamSets()) {
72  auto uid = uid_obj.first;
73  auto& obj = uid_obj.second;
74 
75  payloadsTable[uid] = {};
76 
77  if (std::get<TableData>(timeTable).find(uid) == std::get<TableData>(timeTable).end()) {
78  auto firstEvent = std::get<EventHeader>(timeTable).at(0);
79 // auto lastEvent = std::get<EventHeader>(timeTable).at(std::get<EventHeader>(timeTable).size() - 1);
80 
81  auto iov = IntervalOfValidity(firstEvent.getExperiment(), firstEvent.getRun(), -1, -1);
82  auto objCopy = std::shared_ptr<GlobalParamSetAccess>(obj->clone());
83  payloadsTable[uid].push_back({ iov, {{firstEvent, objCopy}} });
84 
85  continue;
86  }
87  int iCol = 0;
88  // Now add PayloadIovBlockRow with run-spanning IoVs
89  for (; iCol < int(std::get<EventHeader>(timeTable).size()); ++iCol) {
90  auto event = std::get<EventHeader>(timeTable).at(iCol);
91  auto exprun = std::get<RunHeader>(timeTable).at(iCol);
92  auto exp = exprun.first;
93  auto run = exprun.second;
94 
95  // Prepare intra run objects
96  // 1st is always there (even for non-intra-run)
97  auto objCopy = std::shared_ptr<GlobalParamSetAccess>(obj->clone());
98  IntraIoVPayloads intraRunEntries;
99  intraRunEntries.push_back({event, objCopy});
100  // At each change in run, add new entry
101  auto lastEvent = event;
102  for (; iCol < int(std::get<EventHeader>(timeTable).size());) {
103  auto nextEvent = gotoNextChangeInRun(timeTable, uid, iCol);
104  if (nextEvent != lastEvent) {
105  auto objIntraRunCopy = std::shared_ptr<GlobalParamSetAccess>(obj->clone());
106  intraRunEntries.push_back({nextEvent, objIntraRunCopy});
107  lastEvent = nextEvent;
108  } else {
109  break;
110  }
111  }
112 
113  // Move to next IoV block (for intra-run deps in just processed block, next block is always the next run)
114  auto endEvent = gotoNextChangeRunWise(timeTable, uid, iCol);
115  int endExp = endEvent.getExperiment();
116  //int endRun = endEvent.getRun();
117  int endRun = std::max(0, endEvent.getRun() - 1);
118  // Last IoV open:
119  if (iCol == static_cast<int>(std::get<EventHeader>(timeTable).size()) - 1) {
120  endRun = -1;
121  endExp = -1;
122  }
123  // Store finished block
124  payloadsTable[uid].push_back({IntervalOfValidity(exp, run, endExp, endRun), intraRunEntries});
125 
126  }
127 
128  }
129 
130  return payloadsTable;
131  }
132 
133  TimeTable makeInitialTimeTable(std::vector< EventMetaData > events, GlobalLabel& label)
134  {
135  TimeTable table;
136  std::vector<int> nullRow(events.size(), 0);
137 
138  // event header
139  std::get<EventHeader>(table) = events;
140 
141  // run header
142  RunHeader runs;
143  for (auto event : events) {
144  runs.push_back({event.getExperiment(), event.getRun()});
145  }
146  std::get<RunHeader>(table) = runs;
147 
148  for (auto& eidpid_intervals : label.getTimeIntervals()) {
149  auto uid = GlobalLabel(eidpid_intervals.first).getUniqueId();
150  if (std::get<TableData>(table).find(uid) == std::get<TableData>(table).end()) {
151  std::get<TableData>(table)[uid] = nullRow;
152  }
153  unsigned int lastTime = 0;
154  for (long unsigned int timeid = 0; timeid < events.size(); ++timeid) {
155  if (lastTime != eidpid_intervals.second.get(timeid)) {
156  std::get<TableData>(table)[uid][timeid] = 1;
157  lastTime = timeid;
158  }
159  }
160  }
161 
162  return table;
163  }
164 
165  void finalizeTimeTable(TimeTable& table)
166  {
167  for (auto& row : std::get<TableData>(table)) {
168  auto& cells = row.second;
169 
170  int currIndex = 0;
171  for (long unsigned int iCell = 0; iCell < cells.size(); ++iCell) {
172  auto cell = cells.at(iCell);
173  if (iCell == 0) {
174  if (cell != 0) {
175  B2FATAL("First cell (index 0) has to be zero (time id for const objects or 1st instance of time-dep objects) for each row.");
176  }
177  continue;
178  }
179  if (cell != 0 && cell != 1) {
180  B2FATAL("In initial time table, only cells with 0 (=no change of object at beginning of cell) or 1 (object can change at beginning of this cell) are allowed");
181  }
182  // Now cell is not first and is either 0 or 1 (-> increment index)
183  if (cell == 1) {
184  ++currIndex;
185  }
186  cells.at(iCell) = currIndex;
187  }
188  }
189  }
190 
191  std::pair< EventMetaData, std::shared_ptr< GlobalParamSetAccess > > getPayloadByContinuousIndex(PayloadsTable& payloadsTable,
192  int uid, long unsigned int index)
193  {
194  auto& row = payloadsTable.at(uid);
195 
196  long unsigned int currentIndex = 0;
197  for (long unsigned int iIovBlock = 0; iIovBlock < row.size(); ++iIovBlock) {
198  if (currentIndex + row.at(iIovBlock).second.size() > index) {
199  return row.at(iIovBlock).second.at(index - currentIndex);
200  }
201  currentIndex += row.at(iIovBlock).second.size();
202  }
203 
204  return {EventMetaData(), {}};
205  }
206 
207  int getContinuousIndexByTimeID(const TimeTable& timeTable, int uid, int timeid)
208  {
209  if (timeid <= 0)
210  return 0;
211  if (std::get<TableData>(timeTable).find(uid) == std::get<TableData>(timeTable).end())
212  return 0;
213  if (timeid >= int(std::get<TableData>(timeTable).at(uid).size()))
214  return std::get<TableData>(timeTable).at(uid).size() - 1;
215 
216  auto cIndex = std::get<TableData>(timeTable).at(uid)[timeid];
217  return cIndex;
218  }
219 
220  // GlobalParamTimeLine class -------------------------------------------------------------------------------------
221 
222  GlobalParamTimeLine::GlobalParamTimeLine(const std::vector< EventMetaData >& events, GlobalLabel& label,
223  const GlobalParamVector& vector) : timeTable(makeInitialTimeTable(events, label))
224  {
225  finalizeTimeTable(timeTable);
226  payloadsTable = TimeIdsTable2PayloadsTable(timeTable, vector);
227 
228  }
229 
230  void GlobalParamTimeLine::loadFromDB()
231  {
232  std::map<std::tuple<int, int, int>, std::vector<std::shared_ptr<GlobalParamSetAccess>>> eventPayloads{};
233  for (auto& row : payloadsTable) {
234  for (auto& iovBlock : row.second) {
235  for (auto& payload : iovBlock.second) {
236  auto eventTuple = std::make_tuple((int)payload.first.getExperiment(), (int)payload.first.getRun(), (int)payload.first.getEvent());
237  auto iter_and_inserted = eventPayloads.insert(
238  {eventTuple, std::vector<std::shared_ptr<GlobalParamSetAccess>>()}
239  );
240  iter_and_inserted.first->second.push_back(payload.second);
241  }
242  }
243  }
244  for (auto event_payloads : eventPayloads) {
245  auto event = EventMetaData(std::get<2>(event_payloads.first), std::get<1>(event_payloads.first), std::get<0>(event_payloads.first));
246  DBStore::Instance().update(event);
247  DBStore::Instance().updateEvent(event.getEvent());
248  for (auto& payload : event_payloads.second) {
249  payload->loadFromDBObjPtr();
250  }
251  }
252  }
253 
254  void GlobalParamTimeLine::updateGlobalParam(GlobalLabel label, double correction, bool resetParam)
255  {
256  auto timeid = label.getTimeId();
257  auto eov = label.getEndOfValidity();
258  auto uid = label.getUniqueId();
259 
260  std::set<int> payloadIndices;
261  // this is probably dangerous if we do not impose additional invariant
262  //TODO: better to always loop over whole event header?
263  for (int i = timeid; i < std::min(eov + 1, int(std::get<EventHeader>(timeTable).size())); ++i) {
264  payloadIndices.insert(getContinuousIndexByTimeID(timeTable, uid, i));
265  }
266 
267  for (auto payloadIndex : payloadIndices) {
268  auto payload = getPayloadByContinuousIndex(payloadsTable, label.getUniqueId(), payloadIndex).second;
269  // If not found, we get an empty payload shared ptr
270  if (payload) {
271  if (resetParam) {
272  payload->setGlobalParam(correction, label.getElementId(), label.getParameterId());
273  } else {
274  payload->updateGlobalParam(correction, label.getElementId(), label.getParameterId());
275  }
276  }
277  }
278 
279  }
280 
281  std::vector< std::pair< IntervalOfValidity, TObject* > > GlobalParamTimeLine::releaseObjects()
282  {
283  std::vector<std::pair<IntervalOfValidity, TObject*>> result;
284 
285  for (auto& row : payloadsTable) {
286  for (auto& iovBlock : row.second) {
287  auto iov = iovBlock.first;
288  auto obj = iovBlock.second.at(0).second->releaseObject();
289 
290  // non-intra-run
291  if (iovBlock.second.size() == 1) {
292  if (obj)
293  result.push_back({iov, obj});
294 
295  continue;
296  }
297 
298  // First obj in event dependency
299  //TODO: how the lifetime of EventDependency is handled?
300  // both work now -> have to check data storage in DB in real life scenario
301  auto payloads = new EventDependency(obj);
302  //auto payloads = EventDependency(obj);
303  // Add others
304  for (long unsigned int iObj = 1; iObj < iovBlock.second.size(); ++iObj) {
305  auto nextEvent = iovBlock.second.at(iObj).first.getEvent();
306  auto nextObj = iovBlock.second.at(iObj).second->releaseObject();
307 
308  if (nextObj)
309  payloads->add(nextEvent, nextObj);
310  //payloads.add(nextEvent, nextObj);
311  }
312  result.push_back({iov, payloads});
313  //result.push_back({iov, &payloads});
314  }
315  }
316  return result;
317  }
318 
319  std::vector< EventMetaData > setupTimedepGlobalLabels(PyObject* config)
320  {
321  boost::python::handle<> handle(boost::python::borrowed(config));
322  boost::python::list configList(handle);
323  auto newConfig = PyObjConvUtils::convertPythonObject(configList,
324  std::vector< std::tuple< std::vector< int >, std::vector< std::tuple< int, int, int > > > >());
325  return setupTimedepGlobalLabels(newConfig);
326 
327  }
328 
329 
330  std::vector<EventMetaData> setupTimedepGlobalLabels(
331  std::vector< std::tuple< std::vector< int >, std::vector< std::tuple< int, int, int > > > >& config)
332  {
333  std::vector< std::tuple< std::set< int >, std::set< std::tuple< int, int, int > > > > myConfig = {};
334  std::set<std::tuple<int, int, int>> events;
335  for (auto& params_events : config) {
336  auto myRow = std::make_tuple(std::set<int>(), std::set<std::tuple< int, int, int>>());
337 
338  for (auto& param : std::get<0>(params_events))
339  std::get<0>(myRow).insert(param);
340  for (auto& event : std::get<1>(params_events)) {
341  // WARNING: The function expect event metadata tuple in form (event, run, exp) while the implementation internally
342  // reverses this for proper sorting of event metadata in sets!
343  std::get<1>(myRow).insert(std::make_tuple(std::get<2>(event), std::get<1>(event), std::get<0>(event)));
344  }
345 
346  for (auto& event : std::get<1>(params_events)) {
347  int eventNum = std::get<0>(event);
348  int runNum = std::get<1>(event);
349  int expNum = std::get<2>(event);
350 
351  // WARNING: here we also need reversed order for the set to be sorted in ascending order
352  auto emd = std::make_tuple(expNum, runNum, eventNum);
353  events.insert(emd);
354 
355 
356  if (eventNum != 0) {
357  // Automatically add start of this run and start of next run as points where params can change
358  //NOTE: this is the main invariant we need to keep - if something can change inside run, it is expected
359  // it did change since last run and will change for next run, too... (i.e. if there is an event depencency,
360  // the IoV of the payload has to span only single run)
361  auto firstEventThisRun = std::make_tuple(expNum, runNum, 0);
362  auto firstEventNextRun = std::make_tuple(expNum, runNum + 1, 0);
363 
364  events.insert(firstEventThisRun);
365  events.insert(firstEventNextRun);
366 
367  std::get<1>(myRow).insert(firstEventThisRun);
368  std::get<1>(myRow).insert(firstEventNextRun);
369  }
370  }
371  myConfig.push_back(myRow);
372  }
373 
374  std::vector<EventMetaData> eventsVect;
375  std::map<std::tuple<int, int, int>, int> eventIndices;
376 
377 
378  for (auto& event : events) {
379  // WARNING: here we reverse order of exp,run,event back to "normal"
380  eventIndices[event] = eventsVect.size();
381  eventsVect.push_back(EventMetaData(std::get<2>(event), std::get<1>(event), std::get<0>(event)));
382  }
383 
385 
386  for (auto& params_events : myConfig) {
387  for (auto& param : std::get<0>(params_events)) {
388  GlobalLabel label(param);
389 
390  for (auto& event : std::get<1>(params_events)) {
391  auto eventIndex = eventIndices[event];
392  //if (eventIndex > 0)
393  label.registerTimeDependent(eventIndex);
394 
395  }
396  }
397  }
398 
399  return eventsVect;
400  }
401  }
402  }
404 }
Belle2::alignment::timeline::GlobalParamTimeLine::GlobalParamTimeLine
GlobalParamTimeLine(const std::vector< EventMetaData > &events, GlobalLabel &label, const GlobalParamVector &vector)
Constructor.
Definition: GlobalTimeLine.cc:230
Belle2::EventDependency
Class for handling changing conditions as a function of event number.
Definition: EventDependency.h:32
Belle2::GlobalLabel::clearTimeDependentParamaters
static void clearTimeDependentParamaters()
Forget all previously registered time dependent parameters.
Definition: GlobalLabel.cc:68
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::DBStore::Instance
static DBStore & Instance()
Instance of a singleton DBStore.
Definition: DBStore.cc:36
Belle2::DBStore::updateEvent
void updateEvent()
Updates all intra-run dependent objects.
Definition: DBStore.cc:150
Belle2::DBStore::update
void update()
Updates all objects that are outside their interval of validity.
Definition: DBStore.cc:87
Belle2::PyObjConvUtils::convertPythonObject
Scalar convertPythonObject(const boost::python::object &pyObject, Scalar)
Convert from Python to given type.
Definition: PyObjConvUtils.h:510