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>
22using namespace Calibration;
23namespace 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");
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]);
99 B2ERROR(
"Received Python ExpRuns couldn't be converted to C++");
107 B2ERROR(
"Tried to set the input runs but we didn't receive a Python sequence object (list,tuple).");
111 return execute(vecRuns, iteration, iov);
123 B2ERROR(
"There aren't any input files set. Please use CalibrationAlgorithm::setInputFiles()");
129 if (!(runs.empty())) {
130 for (
auto expRun : runs) {
131 B2DEBUG(29,
"ExpRun requested = (" << expRun.first <<
", " << expRun.second <<
")");
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."));
143 B2ERROR(
"No collected data in input files.");
147 for (
auto expRun : runs) {
148 B2DEBUG(29,
"ExpRun requested = (" << expRun.first <<
", " << expRun.second <<
")");
155 iov =
IntervalOfValidity(runs[0].first, runs[0].second, runs[runs.size() - 1].first, runs[runs.size() - 1].second);
183 if (PyList_Check(inputFileNames)) {
184 boost::python::handle<> handle(boost::python::borrowed(inputFileNames));
185 boost::python::list listInputFileNames(handle);
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?");
207 for (
auto path : tmpInputFileNames) {
208 string fullPath = fs::absolute(path).string();
209 if (fs::exists(fullPath)) {
212 B2WARNING(
"Couldn't find the file " << path);
217 B2WARNING(
"No valid files specified!");
225 TDirectory* dir = gDirectory;
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);
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;
267 return dirName +
"/" + objName;
272 B2DEBUG(29,
"Saving calibration TObject = '" << name <<
"' to payloads list.");
278 B2DEBUG(29,
"Saving calibration TClonesArray '" << name <<
"' to payloads list.");
306 list<Database::DBImportQuery> payloads =
getPayloads();
307 B2INFO(
"Committing " << payloads.size() <<
" payloads to database.");
313 if (payloads.empty())
322 return vector<ExpRun>(expRunSet.begin(), expRunSet.end());
334 TDirectory* dir = gDirectory;
337 string runRangeObjName(
getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
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) {
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);
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);
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);
441 for (
auto expRunRequested : requestedRuns) {
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);
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());
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);
496 B2DEBUG(29,
"Passing back merged data " << name);
505 auto jsonInput = nlohmann::json::parse(jsonString);
507 if (jsonInput.is_object()) {
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");
524 B2ERROR(
"There aren't any input files set. Please use CalibrationAlgorithm::setInputFiles()");
535 B2ERROR(
"No collected data in input files.");
540 B2ERROR(
"The data is collected with granularity='all' (exp=-1,run=-1), and we can't use that to find run boundaries.");
546 std::vector<ExpRun> runList;
548 for (
auto currentRun : runs) {
549 runList.push_back(currentRun);
void setResult(EResult result)
Setter for current iteration.
void setRequestedIov(const IntervalOfValidity &iov=IntervalOfValidity(0, 0, -1, -1))
Sets the requested IoV for this execution, based on the.
void setCalibObj(const std::string &name, const RunRange &runRange, const std::shared_ptr< TNamed > &objectPtr)
Insert a newly created object in m_mapCalibData. Overwrites a previous entry if one exists.
void setRequestedRuns(const std::vector< Calibration::ExpRun > &requestedRuns)
Sets the vector of ExpRuns.
void setIteration(int iteration)
Setter for current iteration.
EResult getResult() const
Getter for current result.
void reset()
Resets this class back to what is needed at the beginning of an execution.
std::shared_ptr< TNamed > getCalibObj(const std::string &name, const RunRange &runRange) const
Get a previously created object in m_mapCalibData if one exists, otherwise return shared_ptr(nullptr)
void clearCalibrationData()
Clear calibration data.
const IntervalOfValidity & getRequestedIov() const
Getter for requested IOV.
std::list< Database::DBImportQuery > & getPayloads()
Get constants (in TObjects) for database update from last execution.
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.
std::vector< Calibration::ExpRun > m_boundaries
When using the boundaries functionality from isBoundaryRequired, this is used to store the boundaries...
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.
Calibration::ExpRun getAllGranularityExpRun() const
Returns the Exp,Run pair that means 'Everything'. Currently unused.
virtual void boundaryFindingTearDown()
Put your algorithm back into a state ready for normal execution if you need to.
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
virtual void boundaryFindingSetup(std::vector< Calibration::ExpRun >, int)
If you need to make some changes to your algorithm class before 'findPayloadBoundaries' is run,...
std::string getPrefix() const
Get the prefix used for getting calibration data.
std::vector< std::string > m_inputFileNames
List of input files to the Algorithm, will initially be user defined but then gets the wildcards expa...
EResult
The result of calibration.
@ c_Failure
Failed =3 in Python.
@ c_Undefined
Not yet known (before execution) =4 in Python.
std::string m_granularityOfData
Granularity of input data. This only changes when the input files change so it isn't specific to an e...
nlohmann::json m_jsonExecutionInput
Optional input JSON object used to make decisions about how to execute the algorithm code.
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....
virtual bool isBoundaryRequired(const Calibration::ExpRun &)
Given the current collector data, make a decision about whether or not this run should be the start o...
std::map< Calibration::ExpRun, std::vector< std::string > > m_runsToInputFiles
Map of Runs to input files. Gets filled when you call getRunRangeFromAllData, gets cleared when setti...
bool loadInputJson(const std::string &jsonString)
Load the m_inputJson variable from a string (useful from Python interface). The return 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.
virtual EResult calibrate()=0
Run algo on data - pure virtual: needs to be implemented.
Calibration::ExpRun convertPyExpRun(PyObject *pyObj)
Performs the conversion of PyObject to ExpRun.
std::string getGranularity() const
Get the granularity of collected data.
ExecutionData m_data
Data specific to a SINGLE execution of the algorithm. Gets reset at the beginning of execution.
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)
std::string getGranularity() const
Gets the m_granularity.
IntervalOfValidity getIntervalOfValidity()
Make IntervalOfValidity from the set, spanning all runs. Works because sets are sorted by default.
const std::set< Calibration::ExpRun > & getExpRunSet()
Get access to the stored set.
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.