Belle II Software  release-05-02-19
per_event_statistics.py
1 import basf2
2 import ROOT
3 from ROOT import Belle2
4 import numpy as np
5 
6 # circumvent BII-1264
7 ROOT.gROOT.ProcessLine("#include <framework/utilities/MakeROOTCompatible.h>")
8 
9 
10 class PerEventStatisticsGetterModule(basf2.Module):
11  """
12  A basf2 python module to export *all* module time statistics (of every event, not just averaged)
13  into a ROOT TTree written to a file.
14  """
15 
16  def __init__(self, output_file_name):
17  """
18  Create a new PerEventStatisticsGetterModule. You have to give the name of the
19  file, where the TTree with the full event statistics will be saved.
20  """
21  super().__init__()
22 
23 
24  self.output_file_name = output_file_name
25 
26  self.tfile = None
27 
28  self.statistics = None
29 
30 
31  self.event_number = np.zeros(3, dtype=float)
32 
33  self.ttree_inputs = None
34 
35  self.last_time_sum = None
36 
37 
38  self.branches_added = False
39 
40 
41  self.event_meta_data = Belle2.PyStoreObj("EventMetaData")
42 
43  def initialize(self):
44  """
45  Create the needed store object pointer in the DataStore and the TFile with the TTree.
46  """
47  self.event_meta_data.isRequired()
48 
49  # try to avoid side effects ...
50  old = ROOT.gDirectory
51  # Before creating the TTree, open the output file.
52  self.tfile = ROOT.TFile.Open(self.output_file_name, "RECREATE")
53  # Create the TTree.
54  self.statistics = ROOT.TTree("statistics", "Per event execution Statistics for all modules")
55  # and reset stupid global directory pointer
56  ROOT.gDirectory = old
57 
58  # Add branches identifying the event. The reason these are [i:] instead
59  # of just [i] is that the latter just returns a single float object which
60  # doesn't work as a buffer to TTree::Branch. We need a buffer with an
61  # address where it can write the value. So we take a reference to the
62  # array starting at element i with [i:]
63  self.statistics.Branch("expNo", self.event_number[0:], "expNo/D")
64  self.statistics.Branch("runNo", self.event_number[1:], "runNo/D")
65  self.statistics.Branch("evtNo", self.event_number[2:], "evtNo/D")
66 
67  def event(self):
68  """
69  The event loop: Store the statistics as a new row in the TTree.
70  """
71  # TTree reference (for shorter code)
72  ttree = self.statistics
73 
74  # Get the statistics
75  module_stats = basf2.statistics.modules
76 
77  # Fill in the event information
78  self.event_number[0] = self.event_meta_data.getExperiment()
79  self.event_number[1] = self.event_meta_data.getRun()
80  self.event_number[2] = self.event_meta_data.getEvent()
81 
82  # Again, change the directory
83  old = ROOT.gDirectory
84  self.tfile.cd()
85 
86  if not self.branches_added:
87 
88  # make sure all this is only done in the output process
90  basf2.B2FATAL("PerEventStatisticsGetterModule can only be used in single processing mode or in the output process")
91 
92  self.ttree_inputs = np.zeros(len(module_stats), dtype=float)
93  self.last_time_sum = np.zeros(len(module_stats), dtype=float)
94 
95  for i, stat in enumerate(module_stats):
96  # escape the module names in ROOT-safe manner. Otherwise weird stuff happens like
97  # sub-branches get created or the branch cannot be opened in the TBrowser
98  module_name = "{name}_{i}".format(name=Belle2.makeROOTCompatible(stat.name), i=i)
99  # Create the branch. See initialize() for why it's [i:] and not [i]
100  ttree.Branch(module_name, self.ttree_inputs[i:], f"{module_name}/D")
101 
102  self.branches_added = True
103 
104  # Fill in the module statistics into the branches
105  time_sum = np.array([m.time_sum(basf2.statistics.EVENT) for m in module_stats], dtype=float)
106  # We need to change the contents of the ttree_inputs array **without**
107  # changing its address so we cannot just assign. But we can assign to
108  # to the full array itself using the empty slice syntax.
109  self.ttree_inputs[:] = time_sum - self.last_time_sum
110  self.last_time_sum[:] = time_sum
111 
112  # Send the branches to the TTree.
113  ttree.Fill()
114 
115  # and back again
116  ROOT.gDirectory = old
117 
118  def terminate(self):
119  """
120  Write out the merged statistics to the ROOT file.
121  This should only be called once, as we would end up with different versions otherwise.
122  """
123  # Change the path a last time
124  old = ROOT.gDirectory
125  self.tfile.cd()
126 
127  self.statistics.Write()
128  self.tfile.Close()
129 
130  # and back again
131  ROOT.gDirectory = old
per_event_statistics.PerEventStatisticsGetterModule.tfile
tfile
Will host the pointer to the opened TFile later.
Definition: per_event_statistics.py:26
per_event_statistics.PerEventStatisticsGetterModule
Definition: per_event_statistics.py:10
per_event_statistics.PerEventStatisticsGetterModule.initialize
def initialize(self)
Definition: per_event_statistics.py:43
Belle2::PyStoreObj
a (simplified) python wrapper for StoreObjPtr.
Definition: PyStoreObj.h:69
Belle2::ProcHandler::isOutputProcess
static bool isOutputProcess()
Return true if the process is an output process.
Definition: ProcHandler.cc:227
per_event_statistics.PerEventStatisticsGetterModule.__init__
def __init__(self, output_file_name)
Definition: per_event_statistics.py:16
Belle2::makeROOTCompatible
std::string makeROOTCompatible(std::string str)
Remove special characters that ROOT dislikes in branch names, e.g.
Definition: MakeROOTCompatible.cc:74
per_event_statistics.PerEventStatisticsGetterModule.ttree_inputs
ttree_inputs
The columns for the statistics TTree (they will be filled in the event function).
Definition: per_event_statistics.py:33
per_event_statistics.PerEventStatisticsGetterModule.event
def event(self)
Definition: per_event_statistics.py:67
Belle2::getRun
static ExpRun getRun(map< ExpRun, pair< double, double >> runs, double t)
Get exp number + run number from time.
Definition: Splitter.cc:262
per_event_statistics.PerEventStatisticsGetterModule.output_file_name
output_file_name
Name of the output file.
Definition: per_event_statistics.py:24
per_event_statistics.PerEventStatisticsGetterModule.event_number
event_number
The columns to store the event number.
Definition: per_event_statistics.py:31
per_event_statistics.PerEventStatisticsGetterModule.statistics
statistics
Will host the TTree later.
Definition: per_event_statistics.py:28
per_event_statistics.PerEventStatisticsGetterModule.event_meta_data
event_meta_data
The event meta data.
Definition: per_event_statistics.py:41
per_event_statistics.PerEventStatisticsGetterModule.branches_added
branches_added
A flag to indicate that we have already added the Branches to the TTree (which we will do in the firs...
Definition: per_event_statistics.py:38
Belle2::ProcHandler::parallelProcessingUsed
static bool parallelProcessingUsed()
Returns true if multiple processes have been spawned, false in single-core mode.
Definition: ProcHandler.cc:221
per_event_statistics.PerEventStatisticsGetterModule.terminate
def terminate(self)
Definition: per_event_statistics.py:118
per_event_statistics.PerEventStatisticsGetterModule.last_time_sum
last_time_sum
Last recored sum of event calls for all modules.
Definition: per_event_statistics.py:35