11 #include <boost/algorithm/string.hpp>
12 #include <boost/python.hpp>
13 #include <boost/python/list.hpp>
15 #include <calibration/CalibrationAlgorithm.h>
16 #include <framework/logging/Logger.h>
17 #include <framework/core/PyObjConvUtils.h>
18 #include <framework/io/RootIOUtilities.h>
22 using namespace Calibration;
23 namespace fs = std::filesystem;
31 if (PySequence_Check(pyObj)) {
32 Py_ssize_t nObj = PySequence_Length(pyObj);
35 B2DEBUG(29,
"ExpRun was a Python sequence which didn't have exactly 2 entries!");
38 PyObject* item1, *item2;
39 item1 = PySequence_GetItem(pyObj, 0);
40 item2 = PySequence_GetItem(pyObj, 1);
42 if ((item1 == NULL) || (item2 == NULL)) {
43 B2DEBUG(29,
"A PyObject pointer was NULL in the sequence");
47 if (PyLong_Check(item1) && PyLong_Check(item2)) {
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");
56 B2DEBUG(29,
"One or more of the PyObjects in the ExpRun wasn't a long");
63 B2DEBUG(29,
"ExpRun was not a Python sequence.");
73 PyObject* itemExp, *itemRun;
74 itemExp = PySequence_GetItem(pyObj, 0);
75 itemRun = PySequence_GetItem(pyObj, 1);
76 expRun.first = PyLong_AsLong(itemExp);
78 expRun.second = PyLong_AsLong(itemRun);
85 B2DEBUG(29,
"Running execute() using Python Object as input argument");
88 m_data.setIteration(iteration);
89 vector<ExpRun> vecRuns;
91 if (PySequence_Check(runs)) {
92 boost::python::handle<> handle(boost::python::borrowed(runs));
93 boost::python::list listRuns(handle);
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);
103 vecRuns.push_back(convertPyExpRun(pyExpRun.ptr()));
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);
111 return execute(vecRuns, iteration, iov);
117 if (m_data.getResult() != c_Undefined) {
119 m_data.setIteration(iteration);
122 if (m_inputFileNames.empty()) {
123 B2ERROR(
"There aren't any input files set. Please use CalibrationAlgorithm::setInputFiles()");
124 m_data.setResult(c_Failure);
129 if (!(runs.empty())) {
130 for (
auto expRun : runs) {
131 B2DEBUG(29,
"ExpRun requested = (" << expRun.first <<
", " << expRun.second <<
")");
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."));
140 runs = getRunListFromAllData();
143 B2ERROR(
"No collected data in input files.");
144 m_data.setResult(c_Failure);
147 for (
auto expRun : runs) {
148 B2DEBUG(29,
"ExpRun requested = (" << expRun.first <<
", " << expRun.second <<
")");
152 m_data.setRequestedRuns(runs);
155 iov =
IntervalOfValidity(runs[0].first, runs[0].second, runs[runs.size() - 1].first, runs[runs.size() - 1].second);
157 m_data.setRequestedIov(iov);
161 m_data.setResult(result);
183 if (PyList_Check(inputFileNames)) {
184 boost::python::handle<> handle(boost::python::borrowed(inputFileNames));
185 boost::python::list listInputFileNames(handle);
187 setInputFileNames(vecInputFileNames);
189 B2ERROR(
"Tried to set the input files but we didn't receive a Python list.");
198 if (inputFileNames.empty()) {
199 B2WARNING(
"You have called setInputFileNames() with an empty list. Did you mean to do that?");
205 set<string> setInputFileNames;
207 for (
auto path : tmpInputFileNames) {
208 string fullPath = fs::absolute(path).string();
209 if (fs::exists(fullPath)) {
210 setInputFileNames.insert(fs::canonical(fullPath).
string());
212 B2WARNING(
"Couldn't find the file " << path);
216 if (setInputFileNames.empty()) {
217 B2WARNING(
"No valid files specified!");
221 m_runsToInputFiles.clear();
225 TDirectory* dir = gDirectory;
226 for (
const string& fileName : setInputFileNames) {
229 f.reset(TFile::Open(fileName.c_str(),
"READ"));
230 }
catch (logic_error&) {
234 if (!f || !f->IsOpen()) {
235 B2FATAL(
"Couldn't open input file " + fileName);
241 m_inputFileNames = vector<string>(setInputFileNames.begin(), setInputFileNames.end());
242 m_granularityOfData = getGranularityFromData();
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()));
251 return objInputFileNames;
257 expRunString += to_string(expRun.first);
259 expRunString += to_string(expRun.second);
265 string dirName = getPrefix() +
"/" + name;
266 string objName = name +
"_" + getExpRunString(expRun);
267 return dirName +
"/" + objName;
272 B2DEBUG(29,
"Saving calibration TObject = '" << name <<
"' to payloads list.");
273 getPayloads().emplace_back(name, data, iov);
278 B2DEBUG(29,
"Saving calibration TClonesArray '" << name <<
"' to payloads list.");
279 getPayloads().emplace_back(name, data, iov);
294 saveCalibration(data, name, m_data.getRequestedIov());
299 saveCalibration(data, name, m_data.getRequestedIov());
304 if (getPayloads().empty())
306 list<Database::DBImportQuery> payloads = getPayloads();
307 B2INFO(
"Committing " << payloads.size() <<
" payloads to database.");
313 if (payloads.empty())
320 RunRange runRange = getRunRangeFromAllData();
322 return vector<ExpRun>(expRunSet.begin(), expRunSet.end());
327 return getRunRangeFromAllData().getIntervalOfValidity();
332 m_runsToInputFiles.clear();
334 TDirectory* dir = gDirectory;
337 string runRangeObjName(getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
338 for (
const auto& fileName : m_inputFileNames) {
341 f.reset(TFile::Open(fileName.c_str(),
"READ"));
342 runRange =
dynamic_cast<RunRange*
>(f->Get(runRangeObjName.c_str()));
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);
351 m_runsToInputFiles.insert(std::make_pair(expRun, std::vector<std::string> {fileName}));
355 B2WARNING(
"Missing a RunRange object for file: " << fileName);
364 TDirectory* dir = gDirectory;
367 string runRangeObjName(getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
368 for (
const auto& fileName : m_inputFileNames) {
371 f.reset(TFile::Open(fileName.c_str(),
"READ"));
372 RunRange* runRangeOther =
dynamic_cast<RunRange*
>(f->Get(runRangeObjName.c_str()));
374 runRange.
merge(runRangeOther);
376 B2WARNING(
"Missing a RunRange object for file: " << fileName);
386 TDirectory* dir = gDirectory;
388 string runRangeObjName(getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
390 string fileName = m_inputFileNames[0];
392 f.reset(TFile::Open(fileName.c_str(),
"READ"));
393 runRange =
dynamic_cast<RunRange*
>(f->Get(runRangeObjName.c_str()));
395 B2FATAL(
"The input file " << fileName <<
" does not contain a RunRange object at "
396 << runRangeObjName <<
". Please set your input files to exclude it.");
424 shared_ptr<TTree> CalibrationAlgorithm::getObjectPtr<TTree>(
const string& name,
425 const vector<ExpRun>& requestedRuns)
428 RunRange runRangeRequested(requestedRuns);
429 std::shared_ptr<TTree> objOutputPtr = std::dynamic_pointer_cast<TTree>(m_data.getCalibObj(name, runRangeRequested));
434 shared_ptr<TChain> chain = make_shared<TChain>(name.c_str());
435 chain->SetDirectory(0);
437 string runRangeObjName(getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
439 if (strcmp(getGranularity().c_str(),
"run") == 0) {
441 for (
auto expRunRequested : requestedRuns) {
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 <<
")");
449 auto files = searchFiles->second;
450 for (
auto fileName : files) {
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()));
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);
463 std::string objDirName = getFullObjectPath(name, expRunRequested);
464 TDirectory* objDir = f->GetDirectory(objDirName.c_str());
466 B2ERROR(
"Directory for requested object " << name <<
" not found: " << objDirName);
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());
480 ExpRun allGranExpRun = getAllGranularityExpRun();
481 string objDirName = getFullObjectPath(name, allGranExpRun);
482 for (
const auto& fileName : m_inputFileNames) {
483 string objectPath = fileName +
"/" + objDirName +
"/" + name +
"_1";
484 B2DEBUG(29,
"Adding TTree " << objectPath);
485 chain->Add(objectPath.c_str());
488 if (!chain->GetListOfFiles()->GetEntries()) {
489 B2ERROR(
"No data found for object " << name);
492 objOutputPtr = static_pointer_cast<TTree>(chain);
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);
505 auto jsonInput = nlohmann::json::parse(jsonString);
507 if (jsonInput.is_object()) {
508 m_jsonExecutionInput = jsonInput;
511 B2ERROR(
"JSON input string isn't an object type i.e. not a '{}' at the top level.");
514 }
catch (nlohmann::json::parse_error&) {
515 B2ERROR(
"Parsing of JSON input string failed");
522 m_boundaries.clear();
523 if (m_inputFileNames.empty()) {
524 B2ERROR(
"There aren't any input files set. Please use CalibrationAlgorithm::setInputFiles()");
531 runs = getRunListFromAllData();
535 B2ERROR(
"No collected data in input files.");
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.");
543 m_data.setIteration(iteration);
545 boundaryFindingSetup(runs, iteration);
546 std::vector<ExpRun> runList;
548 for (
auto currentRun : runs) {
549 runList.push_back(currentRun);
550 m_data.setRequestedRuns(runList);
552 if (isBoundaryRequired(currentRun)) {
553 m_boundaries.push_back(currentRun);
558 m_data.clearCalibrationData();
561 boundaryFindingTearDown();
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.
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.
virtual void merge(const RunRange *other)
Implementation of merging - other is added to the set (union)
const std::set< Calibration::ExpRun > & getExpRunSet()
Get access to the stored set.
std::string getGranularity() const
Gets the m_granularity.
static Database & Instance()
Instance of a singleton Database.
static DBStore & Instance()
Instance of a singleton DBStore.
bool storeData(const std::string &name, TObject *object, const IntervalOfValidity &iov)
Store an object in the database.
void updateEvent()
Updates all intra-run dependent objects.
void update()
Updates all objects that are outside their interval of validity.
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.