Belle II Software development
metamodules.py
1#!/usr/bin/env python3
2
3
10
11"""This file contains python modules that generally decorate other modules and paths
12to serve a slightly changed purpose or circumstance.
13
14Note:
15 A great deal of the these modules are quite general purpose helper constructs,
16 which might be helpful in other parts of the basf2, and might therefore be better
17 placed in the framework folder.
18"""
19
20import basf2
21from ROOT import Belle2
22
23import cProfile
24import pstats
25
26import logging
27
28
29def get_logger():
30 return logging.getLogger(__name__)
31
32
33class WrapperModule(basf2.Module):
34
35 """Wrapping module base class that wraps a single module to slightly change its behaviour.
36
37 The base implementation does nothing, but forward the relevant methods to the wrapped module.
38
39 Note:
40 Implements the decorator/wrapper pattern.
41
42 Attributes:
43 module (basf2.Module): The wrapped module instance.
44 """
45
46 def __init__(self, module):
47 """Create a new wrapper module around the given module. This base implementation does not change anything,
48 so there is no reason to use this base implementation alone."""
49 super().__init__()
50 name = self.compose_wrapped_module_name(module)
51
52
53 self.module = module
54
55 # Forward the logging parameters
56 self.set_debug_level(self.module.logging.debug_level)
57 self.set_abort_level(self.module.logging.abort_level)
58
59 if self.module.logging.log_level != basf2.LogLevel.default:
60 self.set_log_level(self.module.logging.log_level)
61 self.set_log_info(self.module.logging.log_level,
62 self.module.logging.get_info(self.module.logging.log_level))
63
64 # Forward the name of this module to the C++ world
65 self.set_name(name)
66
67 @property
68 def wrapper_name(self):
69 """Name of the wrapper class."""
70 return self.__class__.__name__
71
72 @property
73 def param(self):
74 """Forwards the parameters"""
75 return self.module.param
76
77 @property
79 """Forwards the available parameters"""
80 return self.module.available_params
81
82 def compose_wrapped_module_name(self, module):
83 """Compose a name that indicates the wrapped module."""
84 return f"{self.wrapper_name}({module.name()})"
85
86 def get_name(self):
87 """Forwards the name()."""
88 return self.name()
89
90 def initialize(self):
91 """Initialize method of the module"""
92 self.module.initialize()
93
94 def beginRun(self):
95 """Begin run method of the module"""
96 self.module.beginRun()
97
98 def event(self):
99 """Event method of the module"""
100 self.module.event()
101
102 def endRun(self):
103 """End run method of the module"""
104 self.module.endRun()
105
106 def terminate(self):
107 """Terminate method of the module"""
108 self.module.terminate()
109
110
112
113 """Wrapper module that evaluates the computational performance of python modules.
114
115 Uses cProfile.
116
117 Attributes:
118 module (basf2.Module): The wrapped module that should be profiled.
119 Should be a module written in Python, since the profile interacts
120 with the interpreter for the measurements, but cannot look into c++ implementations.
121
122 output_file_name (str, optional): Path to the file where the profiling information
123 shall be stored. Defaults to profile.txt.
124
125 profiler (cProfile.Profile): Profiler instance to manage and extract the profiling statistics.
126 """
127
128
129 default_output_file_name = "profile.txt"
130
131 def __init__(self, module, output_file_name=None):
132 """Create a new PyProfilingModule wrapped around the given module
133 which outputs its results to the output_file_name of given (if not, to profile.txt)."""
134 super().__init__(module)
135
136
138
139 if output_file_name is not None:
140 self.output_file_name = output_file_name
141
142 def initialize(self):
143 """Initialize method of the module"""
144
145 self.profiler = cProfile.Profile()
146 super().initialize()
147
148 def event(self):
149 """Event method of the module"""
150 profiler = self.profiler
151 profiler.enable()
152 super().event()
153 profiler.disable()
154
155 def terminate(self):
156 """Terminate method of the module"""
157 super().terminate()
158 sortby = 'cumulative'
159 with open(self.output_file_name, 'w') as profile_output_file:
160 profile_stats = pstats.Stats(self.profiler, stream=profile_output_file)
161 profile_stats.sort_stats(sortby)
162 profile_stats.print_stats()
163
164
166
167 """Wrapper module to conditionally execute module and continue with the normal path afterwards.
168
169 There are two ways to specify the condition.
170 One way is to override the condition(self) method in a subclass.
171 The second way is to give a function as the second argument to the module constructor,
172 which is called each event.
173
174 Attributes:
175 module (basf2.Module): The module executed, if the condition is met.
176
177 condition (function() -> bool, optional): Function executed at each event to determine,
178 if the given module shall be executed.
179 If None call the condition method instead, which can be overridden by subclasses.
180
181 """
182
183 def __init__(self, module, condition=None):
184 """Initialisation method taking the module instance to be wrapped.
185
186 Args:
187 module (basf2.Module): The module executed, if the condition is met.
188 condition (function() -> bool, optional): Function executed at each event to determine,
189 if the given module shall be executed.
190 If None call the condition method instead, which can be overridden by subclasses.
191 """
192
193 super().__init__(module)
194 if condition is not None:
195
197 self.conditioncondition = condition
198
199 def condition(self):
200 """Condition method called if not given a condition function during construction.
201
202 Can be overridden by a concrete subclass to specify
203 under which condition the wrapped module should be executed.
204 It can also be shadowed by a condition function given to the constructor of this module.
205
206 Returns:
207 bool: The indication if the wrapped module should be executed.
208 Always True in the base implementation
209 """
210 return True
211
212 def event(self):
213 """Event method of the module
214
215 Evaluates the condition and sets the return value of this module
216 to trigger the execution of the wrapped module.
217 """
218
219 if self.conditioncondition():
220 super().event()
221
222
223def is_storearray_present(storearray_name,
224 storearray_durability=0):
225 """Checks if a StoreArray with name and durability is present in the DataStore.
226
227 Only works during the event processing phase, but not on initialisation,
228 due to limitation of the python interface to the framework
229 """
230 storearray_list = Belle2.PyStoreArray.list(storearray_durability)
231 return storearray_name in storearray_list
232
233
235
236 """Conditional execution of the wrapped module if a StoreArray is present.
237
238 Attributes:
239 storearray_name (str): The name of the StoreArray which presence has to be checked.
240 storearray_durability (int): The durability of the StoreArray
241 storearray_is_present (bool): The flag whether the StoreArray is present.
242 Set during initialisation.
243 """
244
245 def __init__(self,
246 module,
247 storearray_name,
248 storearray_durability=0):
249 """
250 Args:
251 module (basf2.Module): The module executed, if the condition is met.
252 storearray_name (str): The name of the StoreArray which presence has to be checked.
253 storearray_durability (int, optional): The durability of the StoreArray. Default 0.
254 """
255
256 super().__init__(module)
257
258
259 self.storearray_name = storearray_name
260
261
262 self.storearray_durability = storearray_durability
263
264
266
267 def initialize(self):
268 """Initialize the contained module (only if the condition is true)."""
270 super().initialize()
271
272 def condition(self):
273 """Returns true if the StoreArray is present.
274
275 Checks presence of the StoreArray once and remembers the result for all following events.
276 """
277 if self.storearray_is_present is None:
278 self.storearray_is_present = is_storearray_present(self.storearray_name,
280 return self.storearray_is_present
281
282
284
285 """Conditional execution of the wrapped module based if a StoreArray is not present."""
286
287 def condition(self):
288 """Returns false if the StoreArray is present.
289
290 Checks presence of the StoreArray once and remembers the result for all following events.
291 """
292 return not IfStoreArrayPresentModule.condition(self)
293
294
296
297 """Conditional execution of the wrapped module based on the presence of Monte Carlo information.
298 """
299
300 def __init__(self, module):
301 """
302 Args:
303 module (basf2.Module): The module executed, if the condition is met.
304 """
305 super().__init__(module, "MCParticles")
306
307
308class PathModule(basf2.Module):
309
310 """Wrapper for a basf2 path into a module such that
311 it can be passed around and added to a basf2 path as a basf2 module.
312
313 The wrapper is implement in such a way that it unconditionally executes
314 the contained path by means of a positive return value.
315 The calling path is continued after the execution of the wrapped path.
316
317 Attributes:
318 _path (basf2.Path): The wrapped execution path.
319 """
320
321 def __init__(self, path=None, modules=None):
322 """Initialises the module with a path to be wrapped.
323
324 May also give a list of modules that should be appended to the path.
325 If the path is omitted a new basf2.Path is constructed.
326
327 Args:
328 path (basf2.Path): The execution path to be wrapped.
329 If no path is given create a new one.
330
331 modules (iterable of basf2.Module): Module to be added to the path.
332 """
333
334 super().__init__()
335
336 if path is None:
337 path = basf2.create_path()
338
339
340 self._path = path
341
342 if modules:
343 for module in modules:
344 path.add_module(module)
345
346 self.if_true(path, basf2.AfterConditionPath.CONTINUE)
347
348 # Pass a telling name to the C++ world
349 if modules is None:
350 itemCount = 0
351 else:
352 itemCount = len(modules)
353 self.set_name(f"{self.__class__.__name__} ({itemCount} modules):")
354
355 def event(self):
356 """Event method of the module
357
358 Sets the return value of this module to true and triggers the execution of the wrapped path.
359 """
360
361 self.return_value(True)
static std::vector< std::string > list(DataStore::EDurability durability=DataStore::EDurability::c_Event)
Return list of available arrays for given durability.
Definition: PyStoreArray.cc:28
def __init__(self, module, condition=None)
Definition: metamodules.py:183
condition
Condition function called at each event to determine if wrapped module should be executed.
Definition: metamodules.py:197
storearray_name
Name of the StoreArray to be checked (str).
Definition: metamodules.py:259
storearray_is_present
Flag that the StoreArray is present (bool).
Definition: metamodules.py:265
def __init__(self, module, storearray_name, storearray_durability=0)
Definition: metamodules.py:248
storearray_durability
Durability of the StoreArray to be checked (int).
Definition: metamodules.py:262
def __init__(self, path=None, modules=None)
Definition: metamodules.py:321
str default_output_file_name
The default name for output if none is given.
Definition: metamodules.py:129
profiler
The used profiler instance.
Definition: metamodules.py:145
output_file_name
The output file name the results will be written into.
Definition: metamodules.py:137
def __init__(self, module, output_file_name=None)
Definition: metamodules.py:131
def compose_wrapped_module_name(self, module)
Definition: metamodules.py:82