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