Source code for basf2.core

#!/usr/bin/env python3

##########################################################################
# basf2 (Belle II Analysis Software Framework)                           #
# Author: The Belle II Collaboration                                     #
#                                                                        #
# See git log for contributors and copyright holders.                    #
# This file is licensed under LGPL-3.0, see LICENSE.md.                  #
##########################################################################

"""
The core module of the Belle II Analysis Software Framework.
"""

import sys as _sys
import signal as _signal

# now let's make sure we actually run in python 3
if _sys.version_info[0] < 3:
    print("basf2 requires python3. Please run the steering files using basf2 "
          "(or python3), not python")
    _sys.exit(1)

# import to override print function
from basf2 import _override_print  # noqa

# import the C++ library with the exported functions
import pybasf2  # noqa
# and also import all of them in current scope for ease of use
from pybasf2 import logging
from pybasf2 import *  # noqa

# make sure conditions objects are read only
from basf2 import _constwrapper  # noqa


#: name of the framework
basf2label = 'basf2 (Belle II Analysis Software Framework)'
#: and copyright notice
basf2copyright = 'Copyright(C) 2010-2024 Members of the Belle II Collaboration'
#: license details
basf2license = '(See "basf2 --license" for more information.)'

# -----------------------------------------------
#               Prepare basf2
# -----------------------------------------------

# Reset the signal handler to allow the framework execution
# to be stopped with Ctrl-c (Python installs own handler)
# This will again be replaced once process() is called.
_signal.signal(_signal.SIGINT, _signal.SIG_DFL)


[docs]def register_module(name_or_module, shared_lib_path=None, logLevel=None, debugLevel=None, **kwargs): """ Register the module 'name' and return it (e.g. for adding to a path). This function is intended to instantiate existing modules. To find out which modules exist you can run :program:`basf2 -m` and to get details about the parameters for each module you can use :program:`basf2 -m {modulename}` Parameters can be passed directly to the module as keyword parameters or can be set later using `Module.param` >>> module = basf2.register_module('EventInfoSetter', evtNumList=100, logLevel=LogLevel.ERROR) >>> module.param("evtNumList", 100) Parameters: name_or_module: The name of the module type, may also be an existing `Module` instance for which parameters should be set shared_lib_path (str): An optional path to a shared library from which the module should be loaded logLevel (LogLevel): indicates the minimum severity of log messages to be shown from this module. See `Module.set_log_level` debugLevel (int): Number indicating the detail of debug messages, the default level is 100. See `Module.set_debug_level` kwargs: Additional parameters to be passed to the module. Note: You can also use `Path.add_module()` directly, which accepts the same name, logging and module parameter arguments. There is no need to register the module by hand if you will add it to the path in any case. """ if isinstance(name_or_module, pybasf2.Module): module = name_or_module else: module_name = name_or_module if shared_lib_path is not None: module = pybasf2._register_module(module_name, shared_lib_path) else: module = pybasf2._register_module(module_name) if kwargs: module.param(kwargs) if logLevel is not None: module.set_log_level(logLevel) if debugLevel is not None: module.set_debug_level(debugLevel) return module
[docs]def set_module_parameters(path, name=None, type=None, recursive=False, **kwargs): """Set the given set of parameters for all `modules <Module>` in a path which have the given ``name`` (see `Module.set_name`) Usage is similar to `register_module()` but this function will not create new modules but just adjust parameters for modules already in a `Path` >>> set_module_parameters(path, "Geometry", components=["PXD"], logLevel=LogLevel.WARNING) Parameters: path (basf2.Path): The path to search for the modules name (str): Then name of the module to set parameters for type (str): The type of the module to set parameters for. recursive (bool): if True also look in paths connected by conditions or `Path.for_each()` kwargs: Named parameters to be set for the module, see `register_module()` """ if name is None and type is None: raise ValueError("At least one of name or type has to be given") if not kwargs: raise ValueError("no module parameters given") found = False for module in path.modules(): if (name is None or module.name() == name) and (type is None or module.type() == type): # use register_module as this automatically takes care of logLevel # and debugLevel parameters register_module(module, **kwargs) found = True if recursive: if module.has_condition(): for condition_path in module.get_all_condition_paths(): set_module_parameters(condition_path, name, type, recursive, **kwargs) if module.type() == "SubEvent": for subpath in [p.values for p in module.available_params() if p.name == "path"]: set_module_parameters(subpath, name, type, recursive, **kwargs) if not found: raise KeyError("No module with given name found anywhere in the path")
def remove_module(old_path, name=None): """Provides a new path with all modules that were in the ``old_path`` \ except the one with the given ``name`` (see `Module.set_name`) Usage is very simple, in this example we remove Geometry the path: >>> main = remove_module(main, "Geometry") Parameters: old_path (basf2.Path): The path to search for the module name (str): Then name of the module you want to remove """ if name is None: raise ValueError("You should provide the module name") new_path = create_path() for module in old_path.modules(): if name != module.name(): new_path.add_module(module) return new_path
[docs]def create_path(): """ Creates a new path and returns it. You can also instantiate `basf2.Path` directly. """ return pybasf2.Path()
[docs]def process(path, max_event=0): """ Start processing events using the modules in the given `basf2.Path` object. Can be called multiple times in one steering file (some restrictions apply: modules need to perform proper cleanup & reinitialisation, if Geometry is involved this might be difficult to achieve.) When used in a Jupyter notebook this function will automatically print a nice progress bar and display the log messages in an advanced way once the processing is complete. Note: This also means that in a Jupyter Notebook, modifications to class members or global variables will not be visible after processing is complete as the processing is performed in a subprocess. To restore the old behavior you can use ``basf2.core.process()`` which will behave exactly identical in Jupyter notebooks as it does in normal python scripts :: from basf2 import core core.process(path) Parameters: path: The path with which the processing starts max_event: The maximal number of events which will be processed, 0 for no limit .. versionchanged:: release-03-00-00 automatic Jupyter integration """ # if we are running in an ipython session set the steering file to the # current history try: ipython = get_ipython() # noqa history = "\n".join(e[2] for e in ipython.history_manager.get_range()) from ROOT import Belle2 Belle2.Environment.Instance().setSteering(history) except NameError: pass # If a pickle path is set via --dump-path or --execute-path we do something special if pybasf2.get_pickle_path() != "": from basf2.pickle_path import check_pickle_path path = check_pickle_path(path) # apparently nothing to do if path is None: return pybasf2.B2INFO("Starting event processing, random seed is set to '" + pybasf2.get_random_seed() + "'") if max_event != 0: pybasf2._process(path, max_event) else: pybasf2._process(path)
[docs]def set_log_level(level): """ Sets the global log level which specifies up to which level the logging messages will be shown Parameters: level (basf2.LogLevel): minimum severity of messages to be logged """ logging.log_level = level
[docs]def set_debug_level(level): """ Sets the global debug level which specifies up to which level the debug messages should be shown Parameters: level (int): The debug level. The default value is 100 """ logging.debug_level = level
[docs]def log_to_console(color=False): """ Adds the standard output stream to the list of logging destinations. The shell logging destination is added to the list by the framework by default. """ logging.add_console(color)
[docs]def log_to_file(filename, append=False): """ Adds a text file to the list of logging destinations. Parameters: filename: The path and filename of the text file append: Should the logging system append the messages to the end of the file (True) or create a new file for each event processing session (False). Default is False. """ logging.add_file(filename, append)
[docs]def reset_log(): """ Resets the logging by removing all logging destinations """ logging.reset()
def _add_module(self, module, logLevel=None, debugLevel=None, **kwargs): """ Add given module (either object or name) at the end of this path. All unknown arguments are passed as module parameters. >>> path = create_path() >>> path.add_module('EventInfoSetter', evtNumList=100, logLevel=LogLevel.ERROR) <pybasf2.Module at 0x1e356e0> >>> path = create_path() >>> eventinfosetter = register_module('EventInfoSetter') >>> path.add_module(eventinfosetter) <pybasf2.Module at 0x2289de8> """ module = register_module(module, logLevel=logLevel, debugLevel=debugLevel, **kwargs) self._add_module_object(module) return module def _add_independent_path(self, skim_path, ds_ID='', merge_back_event=None): """ Add given path at the end of this path and ensure all modules there do not influence the main DataStore. You can thus use modules in skim_path to clean up e.g. the list of particles, save a skimmed uDST file, and continue working with the unmodified DataStore contents outside of skim_path. Parameters: ds_ID: can be specified to give a defined ID to the temporary DataStore, otherwise, a random name will be generated. merge_back_event: is a list of object/array names (of event durability) that will be merged back into the main path. """ if merge_back_event is None: merge_back_event = [] self._add_independent_path(skim_path, ds_ID, merge_back_event) def _add_independent_merge_path( self, skim_path, ds_ID='', merge_back_event=None, consistency_check=None, event_mixing=False, merge_same_file=False): """ Merge specified content of DataStore of independent path into DataStore of main path on a per event level (add tracks/cluster from both events,...). Parameters: skim_path: independent path to be merged ds_ID: can be specified to give a defined ID to the temporary DataStore, otherwise, a random name will be generated (option for developers). merge_back_event: is a list of object/array names (of event durability) that will be merged back into the main path. consistency_check: perform additional consistency checks on the objects from two paths. If they are not satisfied, the skim_path proceeds to the next event on the path. Currently supported value is "charge" that uses EventExtraInfo "charge" of the two paths, that must be specified by the user, ensuring correct configuration of the combined event. See CheckMergingConsistencyModule for more details. event_mixing: apply event mixing (merge each event from first path with each event of second path) merge_same_file: merge events from single file (useful for mixing) """ if merge_back_event is None: merge_back_event = [] if consistency_check is None: consistency_check = "" if merge_same_file: if not event_mixing: pybasf2.B2INFO("add_independent_merge_path: merge_same_file requires event_mixing, setting it to True") event_mixing = True for module in skim_path.modules(): if module.type() == "RootInput": module.param("isSecondaryInput", True) self._add_independent_merge_path(skim_path, ds_ID, merge_back_event, consistency_check, event_mixing, merge_same_file) pybasf2.Path.add_module = _add_module pybasf2.Path.add_independent_path = _add_independent_path pybasf2.Path.add_independent_merge_path = _add_independent_merge_path