11 #include <boost/python/register_ptr_to_python.hpp>
12 #include <boost/python/class.hpp>
13 #include <boost/python/list.hpp>
14 #include <boost/python/docstring_options.hpp>
17 #include <framework/core/Path.h>
18 #include <framework/core/Module.h>
19 #include <framework/core/ModuleManager.h>
20 #include <framework/core/SubEventModule.h>
21 #include <framework/core/SwitchDataStoreModule.h>
22 #include <framework/core/PyObjConvUtils.h>
25 using namespace boost::python;
33 m_elements.push_back(module);
38 if (path.get() ==
this) {
39 B2FATAL(
"Attempting to add a path to itself!");
41 m_elements.push_back(path);
46 return m_elements.empty();
51 std::list<ModulePtr> modules;
52 for (
const std::shared_ptr<PathElement>& elem : m_elements) {
53 if (
dynamic_cast<Module*
>(elem.get()) !=
nullptr) {
55 modules.push_back(std::static_pointer_cast<Module>(elem));
58 const std::list<ModulePtr>& modulesInElem = elem->getModules();
59 modules.insert(modules.end(), modulesInElem.begin(), modulesInElem.end());
69 for (
const ModulePtr& module : getModules()) {
70 if (!unique or find(modList.begin(), modList.end(), module) == modList.end()) {
71 modList.push_back(module);
74 if (module->hasCondition()) {
75 for (
const auto& conditionPath : module->getAllConditionPaths()) {
77 if (conditionPath.get() ==
this) B2FATAL(
"Found recursion in conditional path");
78 const std::list<ModulePtr>& modulesInElem = conditionPath->buildModulePathList(unique);
79 modList.insert(modList.end(), modulesInElem.begin(), modulesInElem.end());
90 m_elements.assign(mlist.begin(), mlist.end());
111 static int dscount = 1;
112 ds_ID =
"DS " + std::to_string(dscount++);
119 switchStart->setName(
"SwitchDataStore ('' -> '" + ds_ID +
"')");
120 switchEnd->setName(
"SwitchDataStore ('' <- '" + ds_ID +
"')");
125 switchStart->setPropertyFlags(flag);
126 switchEnd->setPropertyFlags(flag);
129 addModule(switchStart);
130 addPath(independent_path);
131 addModule(switchEnd);
136 const std::list<ModulePtr>& modules = getModules();
138 auto sameTypeFunc = [moduleType](
const ModulePtr & m) ->
bool {
return m->getType() == moduleType; };
139 return std::find_if(modules.begin(), modules.end(), sameTypeFunc) != modules.end();
145 for (
const auto& elem : m_elements) {
146 const auto* m =
dynamic_cast<const Module*
>(elem.get());
147 if (m and m->getType() ==
"PyModule") {
149 path->addModule(std::static_pointer_cast<Module>(elem));
151 path->m_elements.push_back(elem->clone());
165 bool firstElem =
true;
166 for (
const std::shared_ptr<PathElement>& elem : m_elements) {
173 out += elem->getPathString();
175 return "[" + out +
"]";
183 boost::python::list _getModulesPython(
const Path* path)
185 boost::python::list returnList;
187 for (
const ModulePtr& module : path->getModules())
188 returnList.append(boost::python::object(
ModulePtr(module)));
196 docstring_options options(
true,
false,
false);
197 using bparg = boost::python::arg;
200 R
"(Implements a path consisting of Module and/or Path objects (arranged in a linear order).
202 .. seealso:: :func:`basf2.process`)")
205 .def(
"add_path", &
Path::addPath, args(
"path"), R
"(add_path(path)
207 Insert another path at the end of this one.
210 >>> path.add_module('A')
211 >>> path.add_path(otherPath)
212 >>> path.add_module('B')
214 would create a path [ A -> [ contents of otherPath ] -> B ].)
217 path (Path): path to add to this path)")
218 .def("modules", &_getModulesPython, R
"(modules()
220 Returns an ordered list of all modules in this path.)")
221 .def("for_each", &
Path::forEach, R
"(for_each(loop_object_name, array_name, path)
223 Similar to `add_path()`, this will
224 execute the given ``path`` at the current position, but in each event it will
225 execute it once for each object in the given StoreArray ``arrayName``. It will
226 create a StoreObject named ``loop_object_name`` of same type as array which will
227 point to each element in turn for each execution.
229 This has the effect of calling the ``event()`` methods of modules in ``path``
230 for each entry in ``arrayName``.
232 The main use case is to use it after using the `RestOfEventBuilder` on a
233 ``ParticeList``, where you can use this feature to perform actions on only a part
234 of the event for a given list of candidates:
236 >>> path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
240 "for each ``RestOfEvent`` in the array of "RestOfEvents", execute ``roe_path``"
242 For example, if 'RestOfEvents' contains two elements then ``roe_path`` will be
243 executed twice and during the execution a StoreObjectPtr 'RestOfEvent' will be
244 available, which will point to the first element in the first execution, and
245 the second element in the second execution.
248 A working example of this `for_each` RestOfEvent is to build a veto against
249 photons from :math:`\pi^0\to\gamma\gamma`. It is described in `HowToVeto`.
251 .. note:: This feature is used by both the `FlavorTagger` and :ref:`FullEventInterpretation` algorithms.
253 Changes to existing arrays / objects will be available to all modules after the
254 `for_each()`, including those made to the loop object itself (it will simply modify
255 the i'th item in the array looped over.)
257 StoreArrays / StoreObjects (of event durability) *created* inside the loop will
258 be removed at the end of each iteration. So if you create a new particle list
259 inside a `for_each()` path execution the particle list will not exist for the
260 next iteration or after the `for_each()` is complete.
263 loop_object_name (str): The name of the object in the datastore during each execution
264 array_name (str): The name of the StoreArray to loop over where the i-th
265 element will be available as ``loop_object_name`` during the i-th execution
267 path (basf2.Path): The path to execute for each element in ``array_name``)",
268 args("loop_object_name",
"array_name",
"path"))
269 .def(
"do_while", &
Path::doWhile, R
"(do_while(path, condition='<1', max_iterations=10000)
271 Similar to `add_path()` this will execute a path at the current position but it
272 will repeat execution of this path as long as the return value of the last
273 module in the path fulfills the given ``condition``.
275 This is useful for event generation with special cuts like inclusive particle generation.
278 `Module.if_value` for an explanation of the condition expression.
281 path (basf2.Path): sub path to execute repeatedly
282 condition (str): condition on the return value of the last module in ``path``.
283 The execution will be repeated as long as this condition is fulfilled.
284 max_iterations (int): Maximum number of iterations per event. If this number is exceeded
285 the execution is aborted.
286 )", (bparg("path"), bparg(
"condition") =
"<1", bparg(
"max_iterations") = 10000))
288 .def(
"__contains__", &
Path::contains, R
"(Does this Path contain a module of the given type?
290 >>> path = basf2.Path()
291 >>> 'RootInput' in path
293 >>> path.add_module('RootInput')
294 >>> 'RootInput' in path
295 True)", args("moduleType"))
298 register_ptr_to_python<PathPtr>();