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.