Belle II Software  release-06-02-00
core.py
1 #!/usr/bin/env python3
2 
3 
10 
11 """
12 The core module of the Belle II Analysis Software Framework.
13 """
14 
15 import sys as _sys
16 import signal as _signal
17 
18 # now let's make sure we actually run in python 3
19 if _sys.version_info[0] < 3:
20  print("basf2 requires python3. Please run the steering files using basf2 "
21  "(or python3), not python")
22  _sys.exit(1)
23 
24 # import to override print function
25 from basf2 import _override_print # noqa
26 
27 # import the C++ library with the exported functions
28 import pybasf2 # noqa
29 # and also import all of them in current scope for ease of use
30 from pybasf2 import * # noqa
31 
32 # make sure conditions objects are read only
33 from basf2 import _constwrapper # noqa
34 
35 
36 
37 basf2label = 'basf2 (Belle II Analysis Software Framework)'
38 
39 basf2copyright = 'Copyright(C) 2010-2021 Members of the Belle II Collaboration'
40 
41 basf2license = '(See "basf2 --license" for more information.)'
42 
43 # -----------------------------------------------
44 # Prepare basf2
45 # -----------------------------------------------
46 
47 # Reset the signal handler to allow the framework execution
48 # to be stopped with Ctrl-c (Python installs own handler)
49 # This will again be replaced once process() is called.
50 _signal.signal(_signal.SIGINT, _signal.SIG_DFL)
51 
52 
53 def register_module(name_or_module, shared_lib_path=None, logLevel=None, debugLevel=None, **kwargs):
54  """
55  Register the module 'name' and return it (e.g. for adding to a path). This
56  function is intended to instantiate existing modules. To find out which
57  modules exist you can run :program:`basf2 -m` and to get details about the
58  parameters for each module you can use :program:`basf2 -m {modulename}`
59 
60  Parameters can be passed directly to the module as keyword parameters or can
61  be set later using `Module.param`
62 
63  >>> module = basf2.register_module('EventInfoSetter', evtNumList=100, logLevel=LogLevel.ERROR)
64  >>> module.param("evtNumList", 100)
65 
66  Parameters:
67  name_or_module: The name of the module type, may also be an existing
68  `Module` instance for which parameters should be set
69  shared_lib_path (str): An optional path to a shared library from which the
70  module should be loaded
71  logLevel (LogLevel): indicates the minimum severity of log messages
72  to be shown from this module. See `Module.set_log_level`
73  debugLevel (int): Number indicating the detail of debug messages, the
74  default level is 100. See `Module.set_debug_level`
75  kwargs: Additional parameters to be passed to the module.
76 
77  Note:
78  You can also use `Path.add_module()` directly,
79  which accepts the same name, logging and module parameter arguments. There
80  is no need to register the module by hand if you will add it to the path in
81  any case.
82  """
83 
84  if isinstance(name_or_module, pybasf2.Module):
85  module = name_or_module
86  else:
87  module_name = name_or_module
88  if shared_lib_path is not None:
89  module = pybasf2._register_module(module_name, shared_lib_path)
90  else:
91  module = pybasf2._register_module(module_name)
92 
93  if kwargs:
94  module.param(kwargs)
95  if logLevel is not None:
96  module.set_log_level(logLevel)
97  if debugLevel is not None:
98  module.set_debug_level(debugLevel)
99 
100  return module
101 
102 
103 def set_module_parameters(path, name=None, type=None, recursive=False, **kwargs):
104  """Set the given set of parameters for all `modules <Module>` in a path which
105  have the given ``name`` (see `Module.set_name`)
106 
107  Usage is similar to `register_module()` but this function will not create
108  new modules but just adjust parameters for modules already in a `Path`
109 
110  >>> set_module_parameters(path, "Geometry", components=["PXD"], logLevel=LogLevel.WARNING)
111 
112  Parameters:
113  path (basf2.Path): The path to search for the modules
114  name (str): Then name of the module to set parameters for
115  type (str): The type of the module to set parameters for.
116  recursive (bool): if True also look in paths connected by conditions or `Path.for_each()`
117  kwargs: Named parameters to be set for the module, see `register_module()`
118  """
119 
120  if name is None and type is None:
121  raise ValueError("At least one of name or type has to be given")
122 
123  if not kwargs:
124  raise ValueError("no module parameters given")
125 
126  found = False
127  for module in path.modules():
128  if (name is None or module.name() == name) and (type is None or module.type() == type):
129  # use register_module as this automatically takes care of logLevel
130  # and debugLevel parameters
131  register_module(module, **kwargs)
132  found = True
133 
134  if recursive:
135  if module.has_condition():
136  for condition_path in module.get_all_condition_paths():
137  set_module_parameters(condition_path, name, type, recursive, **kwargs)
138  if module.type() == "SubEvent":
139  for subpath in [p.values for p in module.available_params() if p.name == "path"]:
140  set_module_parameters(subpath, name, type, recursive, **kwargs)
141 
142  if not found:
143  raise KeyError("No module with given name found anywhere in the path")
144 
145 
146 def remove_module(old_path, name=None):
147  """Provides a new path with all modules that were in the ``old_path`` \
148  except the one with the given ``name`` (see `Module.set_name`)
149 
150  Usage is very simple, in this example we remove Geometry the path:
151 
152  >>> main = remove_module(main, "Geometry")
153 
154  Parameters:
155  old_path (basf2.Path): The path to search for the module
156  name (str): Then name of the module you want to remove
157  """
158 
159  if name is None:
160  raise ValueError("You should provide the module name")
161 
162  new_path = create_path()
163 
164  for module in old_path.modules():
165  if name != module.name():
166  new_path.add_module(module)
167 
168  return new_path
169 
170 
171 def create_path():
172  """
173  Creates a new path and returns it. You can also instantiate `basf2.Path` directly.
174  """
175  return pybasf2.Path()
176 
177 
178 def process(path, max_event=0):
179  """
180  Start processing events using the modules in the given `basf2.Path` object.
181 
182  Can be called multiple times in one steering file (some restrictions apply:
183  modules need to perform proper cleanup & reinitialisation, if Geometry is
184  involved this might be difficult to achieve.)
185 
186  When used in a Jupyter notebook this function will automatically print a
187  nice progress bar and display the log messages in an advanced way once the
188  processing is complete.
189 
190  Note:
191  This also means that in a Jupyter Notebook, modifications to class members
192  or global variables will not be visible after processing is complete as
193  the processing is performed in a subprocess.
194 
195  To restore the old behavior you can use ``basf2.core.process()`` which
196  will behave exactly identical in Jupyter notebooks as it does in normal
197  python scripts ::
198 
199  from basf2 import core
200  core.process(path)
201 
202 
203  Parameters:
204  path: The path with which the processing starts
205  max_event: The maximal number of events which will be processed,
206  0 for no limit
207 
208  .. versionchanged:: release-03-00-00
209  automatic Jupyter integration
210  """
211 
212  # if we are running in an ipython session set the steering file to the
213  # current history
214  try:
215  ipython = get_ipython() # noqa
216  history = "\n".join(e[2] for e in ipython.history_manager.get_range())
217  from ROOT import Belle2
218  Belle2.Environment.Instance().setSteering(history)
219  except NameError:
220  pass
221 
222  # If a pickle path is set via --dump-path or --execute-path we do something special
223  if pybasf2.get_pickle_path() != "":
224  from basf2.pickle_path import check_pickle_path
225  path = check_pickle_path(path)
226 
227  # apparently nothing to do
228  if path is None:
229  return
230 
231  pybasf2.B2INFO("Starting event processing, random seed is set to '" + pybasf2.get_random_seed() + "'")
232 
233  if max_event != 0:
234  pybasf2._process(path, max_event)
235  else:
236  pybasf2._process(path)
237 
238 
239 def set_log_level(level):
240  """
241  Sets the global log level which specifies up to which level the
242  logging messages will be shown
243 
244  Parameters:
245  level (basf2.LogLevel): minimum severity of messages to be logged
246  """
247 
248  logging.log_level = level
249 
250 
251 def set_debug_level(level):
252  """
253  Sets the global debug level which specifies up to which level the
254  debug messages should be shown
255 
256  Parameters:
257  level (int): The debug level. The default value is 100
258  """
259 
260  logging.debug_level = level
261 
262 
263 def log_to_console(color=False):
264  """
265  Adds the standard output stream to the list of logging destinations.
266  The shell logging destination is
267  added to the list by the framework by default.
268  """
269 
270  logging.add_console(color)
271 
272 
273 def log_to_file(filename, append=False):
274  """
275  Adds a text file to the list of logging destinations.
276 
277  Parameters:
278  filename: The path and filename of the text file
279  append: Should the logging system append the messages to the end of the
280  file (True) or create a new file for each event processing session (False).
281  Default is False.
282  """
283 
284  logging.add_file(filename, append)
285 
286 
287 def reset_log():
288  """
289  Resets the logging by removing all logging destinations
290  """
291 
292  logging.reset()
293 
294 
295 def _add_module(self, module, logLevel=None, debugLevel=None, **kwargs):
296  """
297  Add given module (either object or name) at the end of this path.
298  All unknown arguments are passed as module parameters.
299 
300  >>> path = create_path()
301  >>> path.add_module('EventInfoSetter', evtNumList=100, logLevel=LogLevel.ERROR)
302  <pybasf2.Module at 0x1e356e0>
303 
304  >>> path = create_path()
305  >>> eventinfosetter = register_module('EventInfoSetter')
306  >>> path.add_module(eventinfosetter)
307  <pybasf2.Module at 0x2289de8>
308  """
309  module = register_module(module, logLevel=logLevel, debugLevel=debugLevel, **kwargs)
310  self._add_module_object(module)
311  return module
312 
313 
314 def _add_independent_path(self, skim_path, ds_ID='', merge_back_event=None):
315  """
316  Add given path at the end of this path and ensure all modules there
317  do not influence the main DataStore. You can thus use modules in
318  skim_path to clean up e.g. the list of particles, save a skimmed uDST file,
319  and continue working with the unmodified DataStore contents outside of
320  skim_path.
321 
322  Parameters:
323  ds_ID: can be specified to give a defined ID to the temporary DataStore,
324  otherwise, a random name will be generated.
325  merge_back_event: is a list of object/array names (of event durability)
326  that will be merged back into the main path.
327  """
328  if merge_back_event is None:
329  merge_back_event = []
330  self._add_independent_path(skim_path, ds_ID, merge_back_event)
331 
332 
333 pybasf2.Path.add_module = _add_module
334 pybasf2.Path.add_independent_path = _add_independent_path
335 
336 
337 def get_default_global_tags():
338  """
339  Return the list of default globaltags in one string separated with comma
340 
341  .. deprecated:: release-04-00-00
342  Please use `basf2.conditions.default_globaltags <ConditionsConfiguration.default_globaltags>` instead
343  """
344  B2WARNING("basf2.get_default_global_tags() is deprecated, please use basf2.conditions.default_globaltags")
345  return ",".join(conditions.default_globaltags)
346 
347 
348 def set_central_database_networkparams(**argk):
349  """
350  Set some expert database connection details
351 
352  .. deprecated:: release-04-00-00
353  Please use `basf2.conditions.expert_settings <ConditionsConfiguration.expert_settings>` instead
354  """
355  B2WARNING("basf2.set_central_database_networkparams() is deprecated, please use basf2.conditions.expert_settings()")
356  return conditions.expert_settings(**argk)
357 
358 
359 def set_central_serverlist(serverlist):
360  """
361  Set the list of database servers
362 
363  .. deprecated:: release-04-00-00
364  Please use `basf2.conditions.metadata_providers <ConditionsConfiguration.metadata_providers>` instead
365  """
366  B2WARNING("basf2.set_central_serverlist() is deprecated, please use basf2.conditions.metadata_providers instead")
367  conditions.metadata_providers = serverlist + [e for e in conditions.metadata_providers if not e.startswith("http")]
static Environment & Instance()
Static method to get a reference to the Environment instance.
Definition: Environment.cc:29