9 #include <framework/pybasf2/Framework.h>
11 #include <framework/core/PyObjConvUtils.h>
12 #include <framework/core/Environment.h>
13 #include <framework/core/RandomNumbers.h>
14 #include <framework/core/EventProcessor.h>
15 #include <framework/core/ModuleManager.h>
16 #include <framework/datastore/DataStore.h>
17 #include <framework/database/DBStore.h>
18 #include <framework/database/Database.h>
19 #include <framework/pcore/pEventProcessor.h>
20 #include <framework/pcore/ZMQEventProcessor.h>
21 #include <framework/pcore/zmq/utils/ZMQAddressUtils.h>
22 #include <framework/utilities/FileSystem.h>
23 #include <framework/database/Configuration.h>
25 #include <framework/logging/Logger.h>
26 #include <framework/logging/LogSystem.h>
28 #include <boost/algorithm/string.hpp>
29 #include <boost/algorithm/string/join.hpp>
30 #include <boost/python.hpp>
35 using namespace boost::python;
39 Framework::Framework()
41 DataStore::s_DoCleanup =
true;
42 LogSystem::Instance().enableErrorSummary(
true);
44 if (!RandomNumbers::isInitialized()) {
45 RandomNumbers::initialize();
47 Environment::Instance();
51 Framework::~Framework()
57 ModuleManager::Instance().reset();
58 DataStore::s_DoCleanup =
false;
60 Conditions::Configuration::getInstance().reset();
64 void Framework::addModuleSearchPath(
const std::string& path)
66 ModuleManager::Instance().addModuleSearchPath(path);
70 void Framework::setExternalsPath(
const std::string& path)
72 Environment::Instance().setExternalsPath(path);
76 ModulePtr Framework::registerModule(
const std::string& moduleName)
78 return ModuleManager::Instance().registerModule(moduleName);
82 ModulePtr Framework::registerModule(
const std::string& moduleName,
const std::string& sharedLibPath)
84 return ModuleManager::Instance().registerModule(moduleName, sharedLibPath);
88 void Framework::process(
PathPtr startPath,
long maxEvent)
90 if (Environment::Instance().getDryRun()) {
91 Environment::Instance().setJobInformation(startPath);
95 static bool already_executed =
false;
96 static std::set<const Module*> previously_run_modules;
97 static int errors_from_previous_run = 0;
98 const auto moduleListUnique = startPath->buildModulePathList(
true);
99 if (already_executed) {
100 B2WARNING(
"Calling process() more than once per steering file is still experimental, please check results carefully! Python modules especially should reinitialise their state in initialise() to avoid problems");
101 if (startPath->buildModulePathList(
true) != startPath->buildModulePathList(
false)) {
102 B2FATAL(
"Your path contains the same module instance in multiple places. Calling process() multiple times is not implemented for this case.");
106 for (
const auto& m : moduleListUnique) {
107 if (previously_run_modules.count(m.get()) > 0) {
109 startPath = std::static_pointer_cast<Path>(startPath->clone());
114 for (
const auto& m : moduleListUnique) {
115 previously_run_modules.insert(m.get());
118 int numLogError = LogSystem::Instance().getMessageCounter(LogConfig::c_Error);
119 if (numLogError != errors_from_previous_run) {
120 B2FATAL(numLogError <<
" ERROR(S) occurred! The processing of events will not be started.");
124 LogSystem::Instance().resetMessageCounter();
125 DataStore::Instance().reset();
126 DataStore::Instance().setInitializeActive(
true);
128 auto& environment = Environment::Instance();
130 already_executed =
true;
131 if (environment.getNumberProcesses() == 0) {
134 processor.
process(startPath, maxEvent);
136 if (environment.getUseZMQ()) {
138 if (environment.getZMQSocketAddress().empty()) {
139 environment.setZMQSocketAddress(ZMQAddressUtils::randomSocketName());
142 processor.
process(startPath, maxEvent);
145 processor.
process(startPath, maxEvent);
148 errors_from_previous_run = LogSystem::Instance().getMessageCounter(LogConfig::c_Error);
150 DBStore::Instance().reset();
154 Database::Instance().reset(
true);
155 }
catch (std::exception& e) {
156 B2ERROR(
"Uncaught exception encountered: " << e.what());
157 DataStore::Instance().reset();
160 B2ERROR(
"Uncaught exception encountered!");
161 DataStore::Instance().reset();
168 void Framework::setNumberProcesses(
int numProcesses)
170 Environment::Instance().setNumberProcesses(numProcesses);
174 int Framework::getNumberProcesses()
176 return Environment::Instance().getNumberProcesses();
180 void Framework::setPicklePath(
const std::string& path)
182 Environment::Instance().setPicklePath(path);
186 std::string Framework::getPicklePath()
188 return Environment::Instance().getPicklePath();
191 void Framework::setStreamingObjects(
const boost::python::list& streamingObjects)
193 auto vec = PyObjConvUtils::convertPythonObject(streamingObjects, std::vector<std::string>());
194 Environment::Instance().setStreamingObjects(vec);
197 void Framework::setRealm(
const std::string& realm)
200 std::vector<std::string> realms;
201 for (
int i = LogConfig::c_None; i <= LogConfig::c_Production; i++) {
203 realms.push_back(thisRealm);
204 if (boost::iequals(realm, thisRealm)) {
210 B2ERROR(
"Invalid realm! Needs to be one of " << boost::join(realms,
", "));
216 void Framework::writeSimulationSteps()
218 B2WARNING(
"basf2 will write the simulation steps of each event into output csv files. "
219 "This is fine if you are producing events for the Belle II Virtual Reality application, "
220 "otherwise this function should not be used since the exeuction time will significantly increase.");
221 Environment::Instance().setWriteSimSteps(
true);
224 std::string Framework::findFile(
const std::string& filename,
const std::string& type,
bool ignore_errors)
229 result = FileSystem::findFile(filename, ignore_errors);
231 result = FileSystem::findFile(filename, type, ignore_errors);
233 if (!ignore_errors and result.empty()) {
238 PyErr_SetFromErrnoWithFilename(PyExc_FileNotFoundError, filename.c_str());
239 boost::python::throw_error_already_set();
248 boost::python::list Framework::getModuleSearchPathsPython()
250 boost::python::list returnList;
252 for (
const std::string& path : ModuleManager::Instance().getModuleSearchPaths())
253 returnList.append(boost::python::object(path));
258 boost::python::dict Framework::getAvailableModulesPython()
260 boost::python::dict returnDict;
261 for (
const auto& modulePair : ModuleManager::Instance().getAvailableModules())
262 returnDict[boost::python::object(modulePair.first)] = boost::python::object(modulePair.second);
267 boost::python::list Framework::getRegisteredModulesPython()
269 boost::python::list returnList;
271 for (
const ModulePtr& mod : ModuleManager::Instance().getCreatedModules())
272 returnList.append(boost::python::object(mod));
277 #if !defined(__GNUG__) || defined(__ICC)
279 #pragma GCC diagnostic push
280 #pragma GCC diagnostic ignored "-Wunused-local-typedefs"
282 BOOST_PYTHON_FUNCTION_OVERLOADS(process_overloads, Framework::process, 1, 2)
283 #if !defined(__GNUG__) || defined(__ICC)
285 #pragma GCC diagnostic pop
289 PyObject* PyExc_ModuleNotCreatedError{
nullptr};
292 void moduleNotCreatedTranslator(
const ModuleManager::ModuleNotCreatedError& e)
294 PyErr_SetString(PyExc_ModuleNotCreatedError, e.what());
298 void Framework::exposePythonAPI()
300 PyExc_ModuleNotCreatedError = PyErr_NewExceptionWithDoc(
"basf2.ModuleNotCreatedError",
301 "This exception is raised when a basf2 module could not be created for any reason",
302 PyExc_RuntimeError,
nullptr);
303 scope().attr(
"ModuleNotCreatedError") = handle<>(borrowed(PyExc_ModuleNotCreatedError));
304 register_exception_translator<ModuleManager::ModuleNotCreatedError>(moduleNotCreatedTranslator);
306 ModulePtr(*registerModule1)(
const std::string&) = &Framework::registerModule;
307 ModulePtr(*registerModule2)(
const std::string&,
const std::string&) = &Framework::registerModule;
310 docstring_options options(
true,
true,
false);
313 class_<Framework, std::shared_ptr<Framework>, boost::noncopyable>(
"Framework",
"Initialize and Cleanup functions", no_init);
314 std::shared_ptr<Framework> initguard{
new Framework()};
315 scope().attr(
"__framework") = initguard;
317 def(
"add_module_search_path", &Framework::addModuleSearchPath, R
"DOCSTRING(
318 Add a directory in which to search for compiled basf2 C++ `Modules <Module>`.
320 This directory needs to contain the shared libraries containing the compiled
321 modules as well as companion files ending in ``.b2modmap`` which contain a list
322 of the module names contained in each library.
325 The newly added path will not override existing modules
328 path (str): directory containing the modules.
329 )DOCSTRING", args("path"));
330 def(
"set_externals_path", &Framework::setExternalsPath, R
"DOCSTRING(
331 Set the path to the externals to be used.
334 This will not change the library and executable paths but will just change
335 the directory where to look for certain data files like the Evtgen particle
336 definition file. Don't use this unless you really know what you are doing.
339 path (str): new top level directory for the externals
340 )DOCSTRING", args("path"));
341 def(
"list_module_search_paths", &Framework::getModuleSearchPathsPython, R
"DOCSTRING(
342 Return a python list containing all the directories included in the module
346 `add_module_search_path`
348 def("list_available_modules", &Framework::getAvailableModulesPython, R
"DOCSTRING(
349 Return a dictionary containing the names of all known modules
350 as keys and the name of the shared library containing these modules as values.
352 def("list_registered_modules", &Framework::getRegisteredModulesPython, R
"DOCSTRING(
353 Return a list with pointers to all previously created module instances by calling `register_module()`
355 def("get_pickle_path", &Framework::getPicklePath, R
"DOCSTRING(
356 Return the filename where the pickled path is or should be stored
358 def("set_pickle_path", &Framework::setPicklePath, R
"DOCSTRING(
359 Set the filename where the pickled path should be stored or retrieved from
360 )DOCSTRING", args("path"));
361 def(
"set_nprocesses", &Framework::setNumberProcesses, R
"DOCSTRING(
362 Sets number of worker processes for parallel processing.
364 Can be overridden using the ``-p`` argument to basf2.
367 Setting this to 1 will have one parallel worker job which is almost always
368 slower than just running without parallel processing but is still provided to
369 allow debugging of parallel execution.
372 nproc (int): number of worker processes. 0 to disable parallel processing.
374 def("get_nprocesses", &Framework::getNumberProcesses, R
"DOCSTRING(
375 Gets number of worker processes for parallel processing. 0 disables parallel processing
377 def("set_streamobjs", &Framework::setStreamingObjects, R
"DOCSTRING(
378 Set the names of all DataStore objects which should be sent between the
379 parallel processes. This can be used to improve parallel processing performance
380 by removing objects not required.
385 docstring_options param_options(
true,
false,
false);
386 def(
"_register_module", registerModule1);
387 def(
"_register_module", registerModule2, R
"DOCSTRING(register_module(name, library=None)
388 Register a new Module.
390 This function will try to create a new instance of a module with the given name. If no library is given it will try to find the module by itself from the module search path. Optionally one can specify the name of a shared library containing the module code then this library will be loaded
393 `list_module_search_paths()`, `add_module_search_path()`
396 name (str): Type of the module to create
397 library (str): Optional, name of a shared library containing the module
400 An instance of the module if successful.
403 will raise a `ModuleNotCreatedError` if there is any problem creating the module.
405 def("set_realm", &Framework::setRealm, R
"DOCSTRING(
406 Set the basf2 execution realm.
408 The severity of log messages sometimes depends on where basf2 runs. This is controlled by the execution realm.
410 Usually the realm does not have to be set explicitly. On the HLT or express reco it should be set to 'online' and for official productions to 'production'.
411 )DOCSTRING", args("realm"));
412 def(
"write_simulation_steps", &Framework::writeSimulationSteps, R
"DOCSTRING(
413 Allow basf2 to write the simulation steps of each event into csv files.
415 This function should not be used in production jobs because the exeuction time will significantly increase.
417 def("_process", &Framework::process, process_overloads(R
"DOCSTRING(process(path, num_events=0)
418 Processes up to max_events events by starting with the first module in the specified path.
420 This method starts processing events only if there is a module in the path
421 which is capable of specifying the end of the data flow.
424 path (Path): The processing starts with the first module of this path.
425 max_events (int): The maximum number of events that will be processed.
426 If the number is smaller than 1, all events will be processed (default).
431 def("find_file", &Framework::findFile, (arg(
"filename"), arg(
"data_type") =
"", arg(
"silent") =
false), R
"DOC(
432 Try to find a file and return its full path
434 If ``data_type`` is empty this function will try to find the file
436 1. in ``$BELLE2_LOCAL_DIR``,
437 2. in ``$BELLE2_RELEASE_DIR``
438 3. relative to the current working directory.
440 Other known ``data_type`` values are
443 Example data for examples and tutorials. Will try to find the file
445 1. in ``$BELLE2_EXAMPLES_DATA_DIR``
446 2. relative to the current working directory
449 Data for Validation purposes. Will try to find the file in
451 1. in ``$BELLE2_VALIDATION_DATA_DIR``
452 2. relative to the current working directory
454 .. versionadded:: release-03-00-00
457 filename (str): relative filename to look for, either in a central place or
458 in the current working directory
459 data_type (str): case insensitive data type to find. Either empty string or
460 one of ``"examples"`` or ``"validation"``
461 silent (bool): If True don't print any errors and just return an empty
462 string if the file cannot be found
provides the core event processing loop.
void setProfileModuleName(const std::string &name)
Set the name of the module we want to profile.
void process(const PathPtr &startPath, long maxEvent=0)
Processes the full module chain, starting with the first module in the given path.
This class combines all subsystems of the framework, and exports the main interface to Python.
ELogRealm
Definition of the supported execution realms.
This class provides the core event processing loop for parallel processing with ZMQ.
void process(const PathPtr &spath, long maxEvent)
Processes the full module chain using parallel processing, starting with the first module in the give...
This class provides the core event processing loop for parallel processing.
void process(const PathPtr &spath, long maxEvent)
Processes the full module chain, starting with the first module in the given path.
std::shared_ptr< Path > PathPtr
Defines a pointer to a path object as a boost shared pointer.
std::shared_ptr< Module > ModulePtr
Defines a pointer to a module object as a boost shared pointer.
Abstract base class for different kinds of events.