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");
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]);
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);
119 m_data.setIteration(iteration);
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 <<
")");
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);
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.");
314 if (payloads.empty())
323 return vector<ExpRun>(expRunSet.begin(), expRunSet.end());
335 TDirectory* dir = gDirectory;
338 string runRangeObjName(
getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
342 f.reset(TFile::Open(fileName.c_str(),
"READ"));
343 runRange =
dynamic_cast<RunRange*
>(f->Get(runRangeObjName.c_str()));
347 for (
const auto& expRun : expRuns) {
350 (runFiles->second).push_back(fileName);
352 m_runsToInputFiles.insert(std::make_pair(expRun, std::vector<std::string> {fileName}));
356 B2WARNING(
"Missing a RunRange object for file: " << fileName);
365 TDirectory* dir = gDirectory;
368 string runRangeObjName(
getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
372 f.reset(TFile::Open(fileName.c_str(),
"READ"));
373 const RunRange* runRangeOther =
dynamic_cast<RunRange*
>(f->Get(runRangeObjName.c_str()));
375 runRange.
merge(runRangeOther);
377 B2WARNING(
"Missing a RunRange object for file: " << fileName);
387 TDirectory* dir = gDirectory;
389 string runRangeObjName(
getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
393 f.reset(TFile::Open(fileName.c_str(),
"READ"));
394 runRange =
dynamic_cast<RunRange*
>(f->Get(runRangeObjName.c_str()));
396 B2FATAL(
"The input file " << fileName <<
" does not contain a RunRange object at "
397 << runRangeObjName <<
". Please set your input files to exclude it.");
426 const vector<ExpRun>& requestedRuns)
429 RunRange runRangeRequested(requestedRuns);
430 std::shared_ptr<TTree> objOutputPtr = std::dynamic_pointer_cast<TTree>(
m_data.
getCalibObj(name, runRangeRequested));
435 shared_ptr<TChain> chain = make_shared<TChain>(name.c_str());
436 chain->SetDirectory(0);
438 string runRangeObjName(
getPrefix() +
"/" + RUN_RANGE_OBJ_NAME);
442 for (
auto expRunRequested : requestedRuns) {
446 B2WARNING(
"No input file found with data collected from run "
447 "(" << expRunRequested.first <<
"," << expRunRequested.second <<
")");
450 auto files = searchFiles->second;
451 for (
auto fileName : files) {
454 std::unique_ptr<TFile> f;
455 f.reset(TFile::Open(fileName.c_str(),
"READ"));
456 runRangeData =
dynamic_cast<RunRange*
>(f->Get(runRangeObjName.c_str()));
459 if (runSet.find(expRunRequested) == runSet.end()) {
460 B2WARNING(
"Something went wrong with the mapping of ExpRun -> Input Files. "
461 "(" << expRunRequested.first <<
"," << expRunRequested.second <<
") not in " << fileName);
465 TDirectory* objDir = f->GetDirectory(objDirName.c_str());
467 B2ERROR(
"Directory for requested object " << name <<
" not found: " << objDirName);
471 for (
auto key : * (objDir->GetListOfKeys())) {
472 string keyName = key->GetName();
473 string objectPath = fileName +
"/" + objDirName +
"/" + keyName;
474 B2DEBUG(29,
"Adding TTree " << objectPath);
475 chain->Add(objectPath.c_str());
484 string objectPath = fileName +
"/" + objDirName +
"/" + name +
"_1";
485 B2DEBUG(29,
"Adding TTree " << objectPath);
486 chain->Add(objectPath.c_str());
489 if (!chain->GetListOfFiles()->GetEntries()) {
490 B2ERROR(
"No data found for object " << name);
493 objOutputPtr = static_pointer_cast<TTree>(chain);
495 shared_ptr<TNamed> storedObjPtr = static_pointer_cast<TNamed>(objOutputPtr);
496 m_data.setCalibObj(name, runRangeRequested, storedObjPtr);
497 B2DEBUG(29,
"Passing back merged data " << name);
506 auto jsonInput = nlohmann::json::parse(jsonString);
508 if (jsonInput.is_object()) {
512 B2ERROR(
"JSON input string isn't an object type i.e. not a '{}' at the top level.");
515 }
catch (nlohmann::json::parse_error&) {
516 B2ERROR(
"Parsing of JSON input string failed");
525 B2ERROR(
"There aren't any input files set. Please use CalibrationAlgorithm::setInputFiles()");
536 B2ERROR(
"No collected data in input files.");
541 B2ERROR(
"The data is collected with granularity='all' (exp=-1,run=-1), and we can't use that to find run boundaries.");
544 m_data.setIteration(iteration);
547 std::vector<ExpRun> runList;
549 for (
auto currentRun : runs) {
550 runList.push_back(currentRun);
551 m_data.setRequestedRuns(runList);
559 m_data.clearCalibrationData();
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)
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.
static bool checkPyExpRun(PyObject *pyObj)
Checks that a PyObject can be successfully converted to an ExpRun type.
static const Calibration::ExpRun m_allExpRun
allExpRun
static 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...
const std::string & getPrefix() const
Get the prefix used for getting calibration data.
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.
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
static Calibration::ExpRun getAllGranularityExpRun()
Returns the Exp,Run pair that means 'Everything'. Currently unused.
virtual void boundaryFindingSetup(std::vector< Calibration::ExpRun >, int)
If you need to make some changes to your algorithm class before 'findPayloadBoundaries' is run,...
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.
const std::string & getGranularity() 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.
static Calibration::ExpRun convertPyExpRun(PyObject *pyObj)
Performs the conversion of PyObject to ExpRun.
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)
const 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.
std::shared_ptr< T > getObjectPtr(const std::string &name, const std::vector< Calibration::ExpRun > &requestedRuns)
Get calibration data object by name and list of runs, the Merge function will be called to generate t...
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.