Belle II Software development
metadata.py
1#!/usr/bin/env python3
2
3
10
11"""Small module containing helper functions to set the metadata on objects
12created for the validation correctly """
13
14# std
15from typing import Optional, Union, List, Tuple
16import pathlib
17
18import basf2
19import ROOT
20from ROOT import Belle2
21
22# circumvent BII-1264
23ROOT.gInterpreter.Declare("#include <framework/utilities/MakeROOTCompatible.h>")
24
25
26def file_description_set(
27 rootfile: Union[ROOT.TFile, str, pathlib.PurePath], description: str
28) -> None:
29 """
30 Add file description validation metadata to a ROOT file.
31
32 Args:
33 rootfile (ROOT.TFile, str or pathlib.PurePath): Name of the root file
34 to open or an already open TFile instance
35 description (str): Common description/information of/about all plots
36 in this ROOT file (will be displayed above the plots)
37
38 Returns:
39 None
40 """
41 opened = False
42 if not isinstance(rootfile, ROOT.TFile):
43 if isinstance(rootfile, pathlib.PurePath):
44 rootfile = str(rootfile)
45 rootfile = ROOT.TFile(rootfile, "UPDATE")
46 opened = True
47 if not rootfile.IsOpen() or not rootfile.IsWritable():
48 raise RuntimeError(
49 f"ROOT file {rootfile.GetName()} is not open for writing"
50 )
51 # scope guard to avoid side effects by changing the global gDirectory
52 # in modules ...
53 # noinspection PyUnusedLocal
54 directory_guard = ROOT.TDirectory.TContext(rootfile) # noqa
55 desc = ROOT.TNamed("Description", description)
56 desc.Write()
57 if opened:
58 rootfile.Close()
59
60
61def validation_metadata_set(
62 obj: ROOT.TObject,
63 title: str,
64 contact: str,
65 description: str,
66 check: str,
67 xlabel: Optional[str] = None,
68 ylabel: Optional[str] = None,
69 metaoptions="",
70) -> None:
71 """
72 Set the validation metadata for a given object by setting the necessary
73 values. This function can be used on any object supported by the
74 Validation (histograms, profiles, ntuples)
75
76 Arguments:
77 obj: Instance of the object which should get the metadata
78 title (str): Title to use for the object
79 contact (str): Contact person, usually in the form "Name <email>"
80 description (str): Text description what can be seen in the plot
81 check (str): Text description what to look for in the validation for
82 shifters to easily see if the distribution looks ok
83 xlabel (str): If given try to set this as the label for the x axis
84 ylabel (str): If given try to set this as the label for the y axis
85 metaoptions (str): Metaoptions (additional options to influence the
86 comparison between revisions, styling of the plot, etc.)
87
88 .. warning::
89
90 Different ways to specify LaTeX for different arguments:
91 see `create_validation_histograms`
92 """
93 obj.SetTitle(title)
94 # For ntuples we add the metadata as aliases ...
95 try:
96 obj.SetAlias("Contact", contact)
97 obj.SetAlias("Description", description)
98 obj.SetAlias("Check", check)
99 obj.SetAlias("MetaOptions", metaoptions)
100 except AttributeError:
101 pass
102 # for TH*, TProfile we add it to the list of functions
103 try:
104 function_list = obj.GetListOfFunctions()
105 function_list.Add(ROOT.TNamed("Contact", contact))
106 function_list.Add(ROOT.TNamed("Description", description))
107 function_list.Add(ROOT.TNamed("Check", check))
108 function_list.Add(ROOT.TNamed("MetaOptions", metaoptions))
109 except AttributeError:
110 pass
111
112 # And maybe try to set a label on the axis
113 if xlabel is not None:
114 try:
115 obj.GetXaxis().SetTitle(xlabel)
116 except AttributeError:
117 pass
118
119 if ylabel is not None:
120 try:
121 obj.GetYaxis().SetTitle(ylabel)
122 except AttributeError:
123 pass
124
125
126# noinspection PyIncorrectDocstring
127def validation_metadata_update(
128 rootfile: Union[str, ROOT.TFile, pathlib.PurePath], name: str, *args, **argk
129) -> None:
130 """
131 This is a convenience helper for `validation_metadata_set` in case the
132 objects have already been saved in a ROOT file before: It will open the
133 file (or use an existing TFile), extract the object, add the metadata and
134 save the new version to the file
135
136 Arguments:
137 rootfile (str, ROOT.TFile or pathlib.PurePath): Name of the root file
138 to open or an already open TFile instance
139 name (str): Name of the object in the file
140 title (str): Title to use for the object
141 contact (str): Contact person, usually in the form "Name <email>"
142 description (str): Text description what can be seen in the plot
143 check (str): Text description what to look for in the validation for
144 shifters to easily see if the distribution looks ok
145 xlabel (str): If given try to set this as the label for the x axis
146 ylabel (str): If given try to set this as the label for the y axis
147 metaoptions (str): Metaoptions (additional options to influence the
148 comparison between revisions, styling of the plot, etc.)
149
150 .. warning::
151
152 Different ways to specify LaTeX for different arguments:
153 see `create_validation_histograms`
154 """
155
156 opened = False
157 if not isinstance(rootfile, ROOT.TFile):
158 if isinstance(rootfile, pathlib.PurePath):
159 rootfile = str(rootfile)
160 rootfile = ROOT.TFile(rootfile, "UPDATE")
161 opened = True
162 if not rootfile.IsOpen() or not rootfile.IsWritable():
163 raise RuntimeError(
164 f"ROOT file {rootfile.GetName()} is not open for writing"
165 )
166 obj = rootfile.Get(name)
167 found_obj = False
168 if not obj:
169 subdir = rootfile.GetDirectory(args[0])
170 if subdir:
171 obj = subdir.Get(name)
172 if obj:
173 found_obj = True
174 validation_metadata_set(obj, *(args[1:]), **argk)
175 subdir.WriteObject(obj, name, "Overwrite")
176 if not found_obj:
177 raise RuntimeError(
178 f"Cannot find object named {name} in {rootfile.GetName()}"
179 )
180 else:
181 validation_metadata_set(obj, *args, **argk)
182 # scope guard to avoid side effects by changing the global gDirectory
183 # in modules ...
184 # noinspection PyUnusedLocal
185 directory_guard = ROOT.TDirectory.TContext(rootfile) # noqa
186 obj.Write("", ROOT.TObject.kOverwrite)
187 if opened:
188 rootfile.Close()
189
190
191class ValidationMetadataSetter(basf2.Module):
192 """
193 Simple module to set the valdiation metadata for a given list of objects
194 automatically at the end of event processing
195
196 Just add this module **before** any
197 VariablesToNtuple/VariablesToHistogram modules and it will set the
198 correct validation metadata at the end of processing
199
200 Warning:
201 The module needs to be before the modules creating the objects
202 as terminate() functions are executed in reverse order from last to
203 first module. If this module is after the creation modules the metadata
204 might not be set correctly
205 """
206
208 self,
209 variables: List[Tuple[str]],
210 rootfile: Union[str, pathlib.PurePath],
211 description="",
212 ):
213 """
214 Initialize ValidationMetadataSetter
215
216 Arguments:
217 variables (list(tuple(str))): List of objects to set the metadata
218 for. Each entry should be the name of an object followed by the
219 metadata values which will be forwarded to
220 `validation_metadata_set`:
221 ``(name, title, contact, description, check, xlabel, ylabel,
222 metaoptions)``
223 where ``xlabel``, ``ylabel`` and ``metaoptions`` are optional
224 rootfile (str or pathlib.PurePath): The name of the ROOT file where
225 the objects can be found
226 description (str): Common description/information of/about all plots
227 in this ROOT file (will be displayed above the plots)
228 """
229 super().__init__()
230
231 self._variables = variables
232 if isinstance(rootfile, pathlib.PurePath):
233 rootfile = str(rootfile)
234
235 self._rootfile: str = rootfile
236
238 self._description = description
239
241 self._tfile: ROOT.TFile = None
242
243 def initialize(self):
244 """Make sure we keep the file open"""
246 self._rootfile
247 )
248
249 def terminate(self):
250 """And update the metadata at the end"""
251 for name, *metadata in self._variables:
253 validation_metadata_update(self._tfile, name, *metadata)
254 if self._description:
255 file_description_set(self._tfile, self._description)
256 del self._tfile
257
258
259def create_validation_histograms(
260 path: basf2.Path,
261 rootfile: Union[str, pathlib.PurePath],
262 particlelist: str,
263 variables_1d: Optional[List[Tuple]] = None,
264 variables_2d: Optional[List[Tuple]] = None,
265 description="",
266) -> None:
267 """
268 Create histograms for all the variables and also label them to be useful
269 in validation plots in one go. This is similar to the
270 `modularAnalysis.variablesToHistogram` function but also sets the
271 metadata correctly to be used by the validation
272
273 Arguments:
274 path (basf2.Path): Path where to put the modules
275 rootfile (str or pathlib.PurePath): Name of the output root file
276 particlelist (str): Name of the particle list, can be empty for event
277 dependent variables
278 variables_1d: List of 1D histogram definitions of the form
279 ``var, bins, min, max, title, contact, description, check_for
280 [, xlabel [, ylabel [, metaoptions]]]``
281 variables_2d: List of 2D histogram definitions of the form
282 ``var1, bins1, min1, max1, var2, bins2, min2, max2, title, contact,
283 description, check_for [, xlabel [, ylabel [, metaoptions]]]``
284 description: Common description/information of/about all plots in this
285 ROOT file (will be displayed above the plots)
286
287 .. warning::
288
289 Sadly, there are two different ways to specify latex formulas.
290
291 1. The ROOT-style using ``#``:
292 ``"Einstein-Pythagoras a^{2} + b^{2} = #frac{E}{m}"``
293
294 This style should be used for histogram title and labels, that is the
295 ``title``, ``xlabel`` and ``ylabel`` arguments
296
297 2. The normal Latex style (with escaped backslashes or raw
298 string literals):
299 ``"Einstein-Pythagoras $a^2 + b^2 = \\\\frac{E}{m}$"``.
300
301 This style should be used for all other fields like ``description``,
302 ``check_for``
303
304 You can use the normal Latex style also for histogram title and descriptions
305 but the PDF version of the plots will still be buggy and not show all
306 formulas correctly.
307 """
308 if isinstance(rootfile, pathlib.PurePath):
309 rootfile = str(rootfile)
310 histograms_1d = []
311 histograms_2d = []
312 metadata = []
313 if variables_1d is not None:
314 for var, nbins, vmin, vmax, *data in variables_1d:
315 histograms_1d.append((var, nbins, vmin, vmax))
316 metadata.append([var] + data)
317
318 if variables_2d is not None:
319 for row in variables_2d:
320 var1 = row[0]
321 var2 = row[4]
322 histograms_2d.append(row[:8])
323 metadata.append([var1 + var2] + list(row[8:]))
324
325 path.add_module(
326 ValidationMetadataSetter(metadata, rootfile, description=description)
327 )
328 path.add_module(
329 "VariablesToHistogram",
330 particleList=particlelist,
331 variables=histograms_1d,
332 variables_2d=histograms_2d,
333 fileName=rootfile,
334 )
static std::string makeROOTCompatible(std::string str)
Remove special characters that ROOT dislikes in branch names, e.g.
ROOT.TFile _tfile
Shared pointer to the root file that will be closed when the last user disconnects.
Definition metadata.py:241
__init__(self, List[Tuple[str]] variables, Union[str, pathlib.PurePath] rootfile, description="")
Definition metadata.py:212
_description
Common description/information of/about all plots in this ROOT file (will be displayed above the plot...
Definition metadata.py:238
str _rootfile
And the name of the root file.
Definition metadata.py:235
static RootFileCreationManager & getInstance()
Interface for the FileManager.