Belle II Software  release-08-01-10
CalibrationAlgorithm.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 <set>
9 #include <utility>
10 #include <filesystem>
11 #include <boost/algorithm/string.hpp>
12 #include <boost/python.hpp>
13 #include <boost/python/list.hpp>
14 #include <TChain.h>
15 #include <calibration/CalibrationAlgorithm.h>
16 #include <framework/logging/Logger.h>
17 #include <framework/core/PyObjConvUtils.h>
18 #include <framework/io/RootIOUtilities.h>
19 
20 using namespace Belle2;
21 using namespace std;
22 using namespace Calibration;
23 namespace fs = std::filesystem;
24 
25 const ExpRun CalibrationAlgorithm::m_allExpRun = make_pair(-1, -1);
26 
29 {
30  // Is it a sequence?
31  if (PySequence_Check(pyObj)) {
32  Py_ssize_t nObj = PySequence_Length(pyObj);
33  // Does it have 2 objects in it?
34  if (nObj != 2) {
35  B2DEBUG(29, "ExpRun was a Python sequence which didn't have exactly 2 entries!");
36  return false;
37  }
38  PyObject* item1, *item2;
39  item1 = PySequence_GetItem(pyObj, 0);
40  item2 = PySequence_GetItem(pyObj, 1);
41  // Did the GetItem work?
42  if ((item1 == NULL) || (item2 == NULL)) {
43  B2DEBUG(29, "A PyObject pointer was NULL in the sequence");
44  return false;
45  }
46  // Are they longs?
47  if (PyLong_Check(item1) && PyLong_Check(item2)) {
48  long value1, value2;
49  value1 = PyLong_AsLong(item1);
50  value2 = PyLong_AsLong(item2);
51  if (((value1 == -1) || (value2 == -1)) && PyErr_Occurred()) {
52  B2DEBUG(29, "An error occurred while converting the PyLong to long");
53  return false;
54  }
55  } else {
56  B2DEBUG(29, "One or more of the PyObjects in the ExpRun wasn't a long");
57  return false;
58  }
59  // Make sure to kill off the reference GetItem gave us responsibility for
60  Py_DECREF(item1);
61  Py_DECREF(item2);
62  } else {
63  B2DEBUG(29, "ExpRun was not a Python sequence.");
64  return false;
65  }
66  return true;
67 }
68 
71 {
72  ExpRun expRun;
73  PyObject* itemExp, *itemRun;
74  itemExp = PySequence_GetItem(pyObj, 0);
75  itemRun = PySequence_GetItem(pyObj, 1);
76  expRun.first = PyLong_AsLong(itemExp);
77  Py_DECREF(itemExp);
78  expRun.second = PyLong_AsLong(itemRun);
79  Py_DECREF(itemRun);
80  return expRun;
81 }
82 
84 {
85  B2DEBUG(29, "Running execute() using Python Object as input argument");
86  // Reset the execution specific data in case the algorithm was previously called
87  m_data.reset();
88  m_data.setIteration(iteration);
89  vector<ExpRun> vecRuns;
90  // Is it a list?
91  if (PySequence_Check(runs)) {
92  boost::python::handle<> handle(boost::python::borrowed(runs));
93  boost::python::list listRuns(handle);
94 
95  int nList = boost::python::len(listRuns);
96  for (int iList = 0; iList < nList; ++iList) {
97  boost::python::object pyExpRun(listRuns[iList]);
98  if (!checkPyExpRun(pyExpRun.ptr())) {
99  B2ERROR("Received Python ExpRuns couldn't be converted to C++");
100  m_data.setResult(c_Failure);
101  return c_Failure;
102  } else {
103  vecRuns.push_back(convertPyExpRun(pyExpRun.ptr()));
104  }
105  }
106  } else {
107  B2ERROR("Tried to set the input runs but we didn't receive a Python sequence object (list,tuple).");
108  m_data.setResult(c_Failure);
109  return c_Failure;
110  }
111  return execute(vecRuns, iteration, iov);
112 }
113 
114 CalibrationAlgorithm::EResult CalibrationAlgorithm::execute(vector<Calibration::ExpRun> runs, int iteration, IntervalOfValidity iov)
115 {
116  // Check if we are calling this function directly and need to reset, or through Python where it was already done.
117  if (m_data.getResult() != c_Undefined) {
118  m_data.reset();
119  m_data.setIteration(iteration);
120  }
121 
122  if (m_inputFileNames.empty()) {
123  B2ERROR("There aren't any input files set. Please use CalibrationAlgorithm::setInputFiles()");
124  m_data.setResult(c_Failure);
125  return c_Failure;
126  }
127 
128  // Did we receive runs to execute over explicitly?
129  if (!(runs.empty())) {
130  for (auto expRun : runs) {
131  B2DEBUG(29, "ExpRun requested = (" << expRun.first << ", " << expRun.second << ")");
132  }
133  // We've asked explicitly for certain runs, but we should check if the data granularity is 'run'
134  if (strcmp(getGranularity().c_str(), "all") == 0) {
135  B2ERROR(("The data is collected with granularity=all (exp=-1,run=-1), but you seem to request calibration for specific runs."
136  " We'll continue but using ALL the input data given instead of the specific runs requested."));
137  }
138  } else {
139  // If no runs are provided, infer the runs from all collected data
140  runs = getRunListFromAllData();
141  // Let's check that we have some now
142  if (runs.empty()) {
143  B2ERROR("No collected data in input files.");
144  m_data.setResult(c_Failure);
145  return c_Failure;
146  }
147  for (auto expRun : runs) {
148  B2DEBUG(29, "ExpRun requested = (" << expRun.first << ", " << expRun.second << ")");
149  }
150  }
151 
152  m_data.setRequestedRuns(runs);
153  if (iov.empty()) {
154  // If no user specified IoV we use the IoV from the executed run list
155  iov = IntervalOfValidity(runs[0].first, runs[0].second, runs[runs.size() - 1].first, runs[runs.size() - 1].second);
156  }
157  m_data.setRequestedIov(iov);
158  // After here, the getObject<...>(...) helpers start to work
159 
160  CalibrationAlgorithm::EResult result = calibrate();
161  m_data.setResult(result);
162  return result;
163 }
164 
166 void CalibrationAlgorithm::setInputFileNames(PyObject* inputFileNames)
167 {
168  // The reasoning for this very 'manual' approach to extending the Python interface
169  // (instead of using boost::python) is down to my fear of putting off final users with
170  // complexity on their side.
171  //
172  // I didn't want users that inherit from this class to be forced to use boost and
173  // to have to define a new python module just to use the CAF. A derived class from
174  // from a boost exposed class would need to have its own boost python module definition
175  // to allow access from a steering file and to the base class functions (I think).
176  // I also couldn't be bothered to write a full framework to get around the issue in a similar
177  // way to Module()...maybe there's an easy way.
178  //
179  // But this way we can allow people to continue using their ROOT implemented classes and inherit
180  // easily from this one. But add in a few helper functions that work with Python objects
181  // created in their steering file i.e. instead of being forced to use STL objects as input
182  // to the algorithm.
183  if (PyList_Check(inputFileNames)) {
184  boost::python::handle<> handle(boost::python::borrowed(inputFileNames));
185  boost::python::list listInputFileNames(handle);
186  auto vecInputFileNames = PyObjConvUtils::convertPythonObject(listInputFileNames, vector<string>());
187  setInputFileNames(vecInputFileNames);
188  } else {
189  B2ERROR("Tried to set the input files but we didn't receive a Python list.");
190  }
191 }
192 
194 void CalibrationAlgorithm::setInputFileNames(vector<string> inputFileNames)
195 {
196  // A lot of code below is tweaked from RootInputModule::initialize,
197  // since we're basically copying the functionality anyway.
198  if (inputFileNames.empty()) {
199  B2WARNING("You have called setInputFileNames() with an empty list. Did you mean to do that?");
200  return;
201  }
202  auto tmpInputFileNames = RootIOUtilities::expandWordExpansions(inputFileNames);
203 
204  // We'll use a set to enforce sorted unique file paths as we check them
205  set<string> setInputFileNames;
206  // Check that files exist and convert to absolute paths
207  for (auto path : tmpInputFileNames) {
208  string fullPath = fs::absolute(path).string();
209  if (fs::exists(fullPath)) {
210  setInputFileNames.insert(fs::canonical(fullPath).string());
211  } else {
212  B2WARNING("Couldn't find the file " << path);
213  }
214  }
215 
216  if (setInputFileNames.empty()) {
217  B2WARNING("No valid files specified!");
218  return;
219  } else {
220  // Reset the run -> files map as our files are likely different
221  m_runsToInputFiles.clear();
222  }
223 
224  // Open TFile to check they can be accessed by ROOT
225  TDirectory* dir = gDirectory;
226  for (const string& fileName : setInputFileNames) {
227  unique_ptr<TFile> f;
228  try {
229  f.reset(TFile::Open(fileName.c_str(), "READ"));
230  } catch (logic_error&) {
231  //this might happen for ~invaliduser/foo.root
232  //actually undefined behaviour per standard, reported as ROOT-8490 in JIRA
233  }
234  if (!f || !f->IsOpen()) {
235  B2FATAL("Couldn't open input file " + fileName);
236  }
237  }
238  dir->cd();
239 
240  // Copy the entries of the set to a vector
241  m_inputFileNames = vector<string>(setInputFileNames.begin(), setInputFileNames.end());
242  m_granularityOfData = getGranularityFromData();
243 }
244 
246 {
247  PyObject* objInputFileNames = PyList_New(m_inputFileNames.size());
248  for (size_t i = 0; i < m_inputFileNames.size(); ++i) {
249  PyList_SetItem(objInputFileNames, i, Py_BuildValue("s", m_inputFileNames[i].c_str()));
250  }
251  return objInputFileNames;
252 }
253 
255 {
256  string expRunString;
257  expRunString += to_string(expRun.first);
258  expRunString += ".";
259  expRunString += to_string(expRun.second);
260  return expRunString;
261 }
262 
263 string CalibrationAlgorithm::getFullObjectPath(const string& name, ExpRun expRun) const
264 {
265  string dirName = getPrefix() + "/" + name;
266  string objName = name + "_" + getExpRunString(expRun);
267  return dirName + "/" + objName;
268 }
269 
270 void CalibrationAlgorithm::saveCalibration(TObject* data, const string& name, const IntervalOfValidity& iov)
271 {
272  B2DEBUG(29, "Saving calibration TObject = '" << name << "' to payloads list.");
273  getPayloads().emplace_back(name, data, iov);
274 }
275 
276 void CalibrationAlgorithm::saveCalibration(TClonesArray* data, const string& name, const IntervalOfValidity& iov)
277 {
278  B2DEBUG(29, "Saving calibration TClonesArray '" << name << "' to payloads list.");
279  getPayloads().emplace_back(name, data, iov);
280 }
281 
283 {
284  saveCalibration(data, DataStore::objectName(data->IsA(), ""), iov);
285 }
286 
288 {
289  saveCalibration(data, DataStore::objectName(data->IsA(), ""));
290 }
291 
292 void CalibrationAlgorithm::saveCalibration(TObject* data, const string& name)
293 {
294  saveCalibration(data, name, m_data.getRequestedIov());
295 }
296 
297 void CalibrationAlgorithm::saveCalibration(TClonesArray* data, const string& name)
298 {
299  saveCalibration(data, name, m_data.getRequestedIov());
300 }
301 
303 {
304  if (getPayloads().empty())
305  return false;
306  list<Database::DBImportQuery> payloads = getPayloads();
307  B2INFO("Committing " << payloads.size() << " payloads to database.");
308  return Database::Instance().storeData(payloads);
309 }
310 
311 bool CalibrationAlgorithm::commit(list<Database::DBImportQuery> payloads)
312 {
313  if (payloads.empty())
314  return false;
315  return Database::Instance().storeData(payloads);
316 }
317 
319 {
320  RunRange runRange = getRunRangeFromAllData();
321  set<ExpRun> expRunSet = runRange.getExpRunSet();
322  return vector<ExpRun>(expRunSet.begin(), expRunSet.end());
323 }
324 
326 {
327  return getRunRangeFromAllData().getIntervalOfValidity();
328 }
329 
331 {
332  m_runsToInputFiles.clear();
333  // Save TDirectory to change back at the end
334  TDirectory* dir = gDirectory;
335  RunRange* runRange;
336  // Construct the TDirectory name where we expect our objects to be
337  string runRangeObjName(getPrefix() + "/" + RUN_RANGE_OBJ_NAME);
338  for (const auto& fileName : m_inputFileNames) {
339  //Open TFile to get the objects
340  unique_ptr<TFile> f;
341  f.reset(TFile::Open(fileName.c_str(), "READ"));
342  runRange = dynamic_cast<RunRange*>(f->Get(runRangeObjName.c_str()));
343  if (runRange) {
344  // Insert or extend the run -> file mapping for this ExpRun
345  auto expRuns = runRange->getExpRunSet();
346  for (const auto& expRun : expRuns) {
347  auto runFiles = m_runsToInputFiles.find(expRun);
348  if (runFiles != m_runsToInputFiles.end()) {
349  (runFiles->second).push_back(fileName);
350  } else {
351  m_runsToInputFiles.insert(std::make_pair(expRun, std::vector<std::string> {fileName}));
352  }
353  }
354  } else {
355  B2WARNING("Missing a RunRange object for file: " << fileName);
356  }
357  }
358  dir->cd();
359 }
360 
362 {
363  // Save TDirectory to change back at the end
364  TDirectory* dir = gDirectory;
365  RunRange runRange;
366  // Construct the TDirectory name where we expect our objects to be
367  string runRangeObjName(getPrefix() + "/" + RUN_RANGE_OBJ_NAME);
368  for (const auto& fileName : m_inputFileNames) {
369  //Open TFile to get the objects
370  unique_ptr<TFile> f;
371  f.reset(TFile::Open(fileName.c_str(), "READ"));
372  RunRange* runRangeOther = dynamic_cast<RunRange*>(f->Get(runRangeObjName.c_str()));
373  if (runRangeOther) {
374  runRange.merge(runRangeOther);
375  } else {
376  B2WARNING("Missing a RunRange object for file: " << fileName);
377  }
378  }
379  dir->cd();
380  return runRange;
381 }
382 
384 {
385  // Save TDirectory to change back at the end
386  TDirectory* dir = gDirectory;
387  RunRange* runRange;
388  string runRangeObjName(getPrefix() + "/" + RUN_RANGE_OBJ_NAME);
389  // We only check the first file
390  string fileName = m_inputFileNames[0];
391  unique_ptr<TFile> f;
392  f.reset(TFile::Open(fileName.c_str(), "READ"));
393  runRange = dynamic_cast<RunRange*>(f->Get(runRangeObjName.c_str()));
394  if (!runRange) {
395  B2FATAL("The input file " << fileName << " does not contain a RunRange object at "
396  << runRangeObjName << ". Please set your input files to exclude it.");
397  return "";
398  }
399  string granularity = runRange->getGranularity();
400  dir->cd();
401  return granularity;
402 }
403 
404 void CalibrationAlgorithm::updateDBObjPtrs(const unsigned int event = 1, const int run = 0, const int experiment = 0)
405 {
406  // Construct an EventMetaData object but NOT in the Datastore
407  EventMetaData emd(event, run, experiment);
408  // Explicitly update while avoiding registering a Datastore object
409  DBStore::Instance().update(emd);
410  // Also update the intra-run objects to the event at the same time (maybe unnessary...)
412 }
413 
414 // Have to put the explicit template specialization in the enclosing namespace
415 namespace Belle2 {
423  template<>
424  shared_ptr<TTree> CalibrationAlgorithm::getObjectPtr<TTree>(const string& name,
425  const vector<ExpRun>& requestedRuns)
426  {
427  // Check if this object already exists
428  RunRange runRangeRequested(requestedRuns);
429  std::shared_ptr<TTree> objOutputPtr = std::dynamic_pointer_cast<TTree>(m_data.getCalibObj(name, runRangeRequested));
430  if (objOutputPtr)
431  return objOutputPtr;
432 
433  // If not we best make a new one
434  shared_ptr<TChain> chain = make_shared<TChain>(name.c_str());
435  chain->SetDirectory(0);
436  // Construct the TDirectory names where we expect our objects to be
437  string runRangeObjName(getPrefix() + "/" + RUN_RANGE_OBJ_NAME);
438 
439  if (strcmp(getGranularity().c_str(), "run") == 0) {
440  // Loop over our runs requested for the right files
441  for (auto expRunRequested : requestedRuns) {
442  // Find the relevant files for this ExpRun
443  auto searchFiles = m_runsToInputFiles.find(expRunRequested);
444  if (searchFiles == m_runsToInputFiles.end()) {
445  B2WARNING("No input file found with data collected from run "
446  "(" << expRunRequested.first << "," << expRunRequested.second << ")");
447  continue;
448  } else {
449  auto files = searchFiles->second;
450  for (auto fileName : files) {
451  RunRange* runRangeData;
452  //Open TFile to get the objects
453  std::unique_ptr<TFile> f;
454  f.reset(TFile::Open(fileName.c_str(), "READ"));
455  runRangeData = dynamic_cast<RunRange*>(f->Get(runRangeObjName.c_str()));
456  // Check that nothing went wrong in the mapping and that this file definitely contains this run's data
457  auto runSet = runRangeData->getExpRunSet();
458  if (runSet.find(expRunRequested) == runSet.end()) {
459  B2WARNING("Something went wrong with the mapping of ExpRun -> Input Files. "
460  "(" << expRunRequested.first << "," << expRunRequested.second << ") not in " << fileName);
461  }
462  // Get the path/directory of the Exp,Run TDirectory that holds the object(s)
463  std::string objDirName = getFullObjectPath(name, expRunRequested);
464  TDirectory* objDir = f->GetDirectory(objDirName.c_str());
465  if (!objDir) {
466  B2ERROR("Directory for requested object " << name << " not found: " << objDirName);
467  return nullptr;
468  }
469  // Find all the objects inside, there may be more than one
470  for (auto key : * (objDir->GetListOfKeys())) {
471  string keyName = key->GetName();
472  string objectPath = fileName + "/" + objDirName + "/" + keyName;
473  B2DEBUG(29, "Adding TTree " << objectPath);
474  chain->Add(objectPath.c_str());
475  }
476  }
477  }
478  }
479  } else {
480  ExpRun allGranExpRun = getAllGranularityExpRun();
481  string objDirName = getFullObjectPath(name, allGranExpRun);
482  for (const auto& fileName : m_inputFileNames) {
483  string objectPath = fileName + "/" + objDirName + "/" + name + "_1"; // Only one index for this granularity
484  B2DEBUG(29, "Adding TTree " << objectPath);
485  chain->Add(objectPath.c_str());
486  }
487  }
488  if (!chain->GetListOfFiles()->GetEntries()) {
489  B2ERROR("No data found for object " << name);
490  return nullptr;
491  }
492  objOutputPtr = static_pointer_cast<TTree>(chain);
493  // make a TNamed version to input to the map of previous calib objects
494  shared_ptr<TNamed> storedObjPtr = static_pointer_cast<TNamed>(objOutputPtr);
495  m_data.setCalibObj(name, runRangeRequested, storedObjPtr);
496  B2DEBUG(29, "Passing back merged data " << name);
497  return objOutputPtr;
498  }
500 }
501 
502 bool CalibrationAlgorithm::loadInputJson(const std::string& jsonString)
503 {
504  try {
505  auto jsonInput = nlohmann::json::parse(jsonString);
506  // Input string has an object (dict) as the top level object?
507  if (jsonInput.is_object()) {
508  m_jsonExecutionInput = jsonInput;
509  return true;
510  } else {
511  B2ERROR("JSON input string isn't an object type i.e. not a '{}' at the top level.");
512  return false;
513  }
514  } catch (nlohmann::json::parse_error&) {
515  B2ERROR("Parsing of JSON input string failed");
516  return false;
517  }
518 }
519 
520 const std::vector<ExpRun> CalibrationAlgorithm::findPayloadBoundaries(std::vector<ExpRun> runs, int iteration)
521 {
522  m_boundaries.clear();
523  if (m_inputFileNames.empty()) {
524  B2ERROR("There aren't any input files set. Please use CalibrationAlgorithm::setInputFiles()");
525  return m_boundaries;
526  }
527  // Reset the internal execution data just in case something is hanging around
528  m_data.reset();
529  if (runs.empty()) {
530  // Want to loop over all runs we could possibly know about
531  runs = getRunListFromAllData();
532  }
533  // Let's check that we have some now
534  if (runs.empty()) {
535  B2ERROR("No collected data in input files.");
536  return m_boundaries;
537  }
538  // In order to find run boundaries we must have collected with data granularity == 'run'
539  if (strcmp(getGranularity().c_str(), "all") == 0) {
540  B2ERROR("The data is collected with granularity='all' (exp=-1,run=-1), and we can't use that to find run boundaries.");
541  return m_boundaries;
542  }
543  m_data.setIteration(iteration);
544  // User defined setup function
545  boundaryFindingSetup(runs, iteration);
546  std::vector<ExpRun> runList;
547  // Loop over run list and call derived class "isBoundaryRequired" member function
548  for (auto currentRun : runs) {
549  runList.push_back(currentRun);
550  m_data.setRequestedRuns(runList);
551  // After here, the getObject<...>(...) helpers start to work
552  if (isBoundaryRequired(currentRun)) {
553  m_boundaries.push_back(currentRun);
554  }
555  // Only want run-by-run
556  runList.clear();
557  // Don't want memory hanging around
558  m_data.clearCalibrationData();
559  }
560  m_data.reset();
561  boundaryFindingTearDown();
562  return m_boundaries;
563 }
std::string getExpRunString(Calibration::ExpRun &expRun) const
Gets the "exp.run" string repr. of (exp,run)
RunRange getRunRangeFromAllData() const
Get the complete RunRange from inspection of collected data.
IntervalOfValidity getIovFromAllData() const
Get the complete IoV from inspection of collected data.
void saveCalibration(TClonesArray *data, const std::string &name)
Store DBArray payload with given name with default IOV.
bool checkPyExpRun(PyObject *pyObj)
Checks that a PyObject can be successfully converted to an ExpRun type.
static const Calibration::ExpRun m_allExpRun
allExpRun
void updateDBObjPtrs(const unsigned int event, const int run, const int experiment)
Updates any DBObjPtrs by calling update(event) for DBStore.
PyObject * getInputFileNames()
Get the input file names used for this algorithm and pass them out as a Python list of unicode string...
EResult execute(std::vector< Calibration::ExpRun > runs={}, int iteration=0, IntervalOfValidity iov=IntervalOfValidity())
Runs calibration over vector of runs for a given iteration.
std::vector< Calibration::ExpRun > getRunListFromAllData() const
Get the complete list of runs from inspection of collected data.
std::string getFullObjectPath(const std::string &name, Calibration::ExpRun expRun) const
constructs the full TDirectory + Key name of an object in a TFile based on its name and exprun
EResult
The result of calibration.
void setInputFileNames(PyObject *inputFileNames)
Set the input file names used for this algorithm from a Python list.
const std::vector< Calibration::ExpRun > findPayloadBoundaries(std::vector< Calibration::ExpRun > runs, int iteration=0)
Used to discover the ExpRun boundaries that you want the Python CAF to execute on....
bool loadInputJson(const std::string &jsonString)
Load the m_inputJson variable from a string (useful from Python interface). The rturn bool indicates ...
std::string getGranularityFromData() const
Get the granularity of collected data.
void fillRunToInputFilesMap()
Fill the mapping of ExpRun -> Files.
bool commit()
Submit constants from last calibration into database.
Calibration::ExpRun convertPyExpRun(PyObject *pyObj)
Performs the conversion of PyObject to ExpRun.
static std::string objectName(const TClass *t, const std::string &name)
Return the storage name for an object of the given TClass and name.
Definition: DataStore.cc:151
Store event, run, and experiment numbers.
Definition: EventMetaData.h:33
A class that describes the interval of experiments/runs for which an object in the database is valid.
Mergeable object holding (unique) set of (exp,run) pairs.
Definition: RunRange.h:25
virtual void merge(const RunRange *other)
Implementation of merging - other is added to the set (union)
Definition: RunRange.h:52
const std::set< Calibration::ExpRun > & getExpRunSet()
Get access to the stored set.
Definition: RunRange.h:64
std::string getGranularity() const
Gets the m_granularity.
Definition: RunRange.h:110
static Database & Instance()
Instance of a singleton Database.
Definition: Database.cc:42
static DBStore & Instance()
Instance of a singleton DBStore.
Definition: DBStore.cc:28
bool storeData(const std::string &name, TObject *object, const IntervalOfValidity &iov)
Store an object in the database.
Definition: Database.cc:141
void updateEvent()
Updates all intra-run dependent objects.
Definition: DBStore.cc:142
void update()
Updates all objects that are outside their interval of validity.
Definition: DBStore.cc:79
Scalar convertPythonObject(const boost::python::object &pyObject, Scalar)
Convert from Python to given type.
std::vector< std::string > expandWordExpansions(const std::vector< std::string > &filenames)
Performs wildcard expansion using wordexp(), returns matches.
Abstract base class for different kinds of events.
Struct containing exp number and run number.
Definition: Splitter.h:51