A simple python module
Contents
3.4.13. A simple python module#
This lesson will give you an idea about the structure and use of basf2 modules.
Most of the modules in our software are implemented in C++ and are made
available for analysis by modularAnalysis
. This package consists of python
wrapper functions around the C++ modules in order to use them in the python
steering file. You have already learned about this in
The basics.
C++ is very strong and fast, but usually much more complicated to read and write than Python. For this reason the basf2 framework provides the possibility to write modules also in Python. This can be very helpful if you want to investigate or test something.
To put your hands on this, simply copy the code into a python file and run it
with basf2 my_python_module.py
. It is nothing more than a steering file with
your own class.
Minimal example#
Let’s begin with the following minimal example for a new python module. It is
the “Hello World” of basf2 modules. The magic happens in the class
MinModule(basf2.Module)
. In this basic example, the class only consists of
one member function event
that is called once for each event. We use the
logging function basf2.B2INFO()
to print our message. To execute the model,
we need to create a path and generate dummy events. Then, our module is added to
the path and we run the path.
#!/usr/bin/env python3
import basf2 as b2
class MinModule(b2.Module):
"""Very minimal class to print Hello World in each event"""
def event(self):
"""Event function, called once for each event"""
b2.B2INFO("Hello World!")
# create a path
main = b2.Path()
# set to generate 10 dummy events
main.add_module("EventInfoSetter", evtNumList=[10])
# and add our module
main.add_module(MinModule())
# run the path
b2.process(main)
You can see that implementing a minimal python module just takes 5 lines of code (3 without documentation) so it’s very nice for fast and quick prototyping.
Note
Python modules have to be implemented or imported in the steering file
Python modules are usually much slower then C++ modules but for many small tasks this does not make a significant difference.
These hacky modules will not appear in module list (
basf2 -m
)Python modules can only be used in analysis code or private scripts. Only C++ modules can be added to the official reconstruction code that is run for HLT or for calibration.
Detailed usage#
Let’s extend the minimal example class above to show all methods which can be implemented by a Python module. As you have seen above all the member functions are optional.
#!/usr/bin/env python3
import basf2 as b2
class MinModule(b2.Module):
"""A minimal example of a basf2 module in python."""
def __init__(self):
"""Constructor"""
# call constructor of base class, required if you implement __init__
# yourself!
super().__init__()
# and do whatever else is necessary like declaring member variables
def initialize(self):
"""Called once in the beginning just before starting processing"""
b2.B2INFO("initialize()")
def beginRun(self):
"""Called every time a run changes before the actual events in that run
are processed
"""
b2.B2INFO("beginRun()")
def event(self):
"""Called once for each event"""
b2.B2INFO("event()")
def endRun(self):
"""Called every time a run changes after the actual events in that run
were processed
"""
b2.B2INFO("endRun()")
def terminate(self):
"""Called once after all the processing is complete"""
b2.B2INFO("terminate()")
# create a path
main = b2.Path()
# set to generate 10 dummy events
main.add_module("EventInfoSetter", evtNumList=[10])
# and add our module
main.add_module(MinModule())
# run the path
b2.process(main)
Accessing Datastore Objects#
Datastore objects can be accessed via the
PyStoreArray
class and the
PyStoreObj classes.
Let’s create a small module which will print the event number and information on
MCParticles, namely the PDG code. To have tracks available, we will use the
ParticleGun
module, which generates very simple events.
Exercise
Write a Python module that prints the number of charged particles per event
and the total charge. Note: per default, the ParticleGun
generates only one
track per event, but you can adjust this.
Hint
You can find information on the Particle class in
doxygen.
The ParticleGun
has the option nTracks
.
Solution
#!/usr/bin/env python3
import basf2 as b2
from ROOT import Belle2
class AccessingDataStoreModule(b2.Module):
"""An example of a basf2 module in python which accesses things in the datastore."""
def initialize(self):
"""Create a member to access event info and the MCParticles
StoreArray
"""
#: an example object from the datastore (the metadata collection for the event)
self.eventinfo = Belle2.PyStoreObj("EventMetaData")
#: an example array from the datastore (the list of MC particles)
self.particles = Belle2.PyStoreArray("MCParticles")
def event(self):
"""Print the number of charged particles and the total charge"""
n_charged = 0
total_charge = 0
for particle in self.particles:
charge = particle.getCharge()
if charge:
n_charged += 1
total_charge += charge
b2.B2INFO(
f"Number of charged particles = {n_charged}, "
f"total charge of event = {total_charge}"
)
# create a path
main = b2.Path()
# generate events
main.add_module("EventInfoSetter", evtNumList=[10])
# generate events with 3 tracks (not all of them are charged tracks)
main.add_module("ParticleGun", nTracks=3)
# and add our module
main.add_module(AccessingDataStoreModule())
# run the path
b2.process(main)
Note
For PyStoreObj you can access most members of the underlying class
directly, like eventinfo.getEvent()
above. However if you want to get the
object directly or want to access a member which also exists by the same name in
PyStoreObj you can use the obj() member to get a reference to the underlying
object itself: eventinfo.obj().getEvent()
More advanced examples#
framework/examples/cdcplotmodule.py
- A full example that uses matplotlib to plot CDCSimHitsframework/examples/interactive_python.py
drops into an interactive (i)python shell inside the event() function, allowing exploration of available objects and data structuresreconstruction/examples/plot_LL_diff.py
- Gets PID log-likelihoods, uses relations to get corresponding MC truth and fills ROOT histograms accordingly
Stuck? We can help!
If you get stuck or have any questions to the online book material, the #starterkit-workshop channel in our chat is full of nice people who will provide fast help.
Refer to Collaborative Tools. for other places to get help if you have specific or detailed questions about your own analysis.
Improving things!
If you know how to do it, we recommend you to report bugs and other requests
with GitLab.
Make sure to use the documentation-training
label of the basf2
project.
If you just want to give very quick feedback, use the last box “Quick feedback”.
Please make sure to be as precise as possible to make it easier for us to fix things! So for example:
typos (where?)
missing bits of information (what?)
bugs (what did you do? what goes wrong?)
too hard exercises (which one?)
etc.
If you are familiar with git and want to create your first merge request for the software, take a look at How to contribute. We’d be happy to have you on the team!
Quick feedback!
Author of this lesson
Pascal Schmolz