Belle II Software development
_notebooksupport.py
1
8from lxml import etree
9from lxml.builder import E
10from IPython import get_ipython
11from . import core as b2core
12import multiprocessing
13
14import ROOT
15from ROOT import Belle2
16
17
18_process_warning = False
19
20
21def _create_pathhtml(path, name="Path", details=None):
22 """Create an html fragment for a given Path object"""
23 size = len(path.modules())
24 fragment = E.div(f"{name} with {size} modules")
25 if details is not None:
26 fragment.append(details)
27
28 module_list = E.ol(style="list-style-type:decimal; font-style:normal;")
29 for m in path.modules():
30 name = m.name()
31 tname = m.type()
32 tname = f" (type \"{tname}\")" if tname != name else ""
33 module = E.li("Module ", E.b(name), tname)
34 detail_list = E.ul(style="margin:0em 0em 0.5em 0em")
35 for p in m.available_params():
36 if p.setInSteering:
37 detail_list.append(E.li(E.code(f"{p.name} = {p.values}")))
38 for c in m.get_all_conditions():
39 after = str(c.get_after_path())
40 details = E.span(" (condition: ", E.code(f"{c.get_operator()} {c.get_value()}"), ", afterwards:", E.code(after), ')')
41 detail_list.append(E.li(_create_pathhtml(c.get_path(), "Conditional Path", details=details),
42 style="list-style-type: disclosure-closed"))
43 if len(detail_list) > 0:
44 module.append(detail_list)
45
46 module_list.append(module)
47 if len(module_list) > 0:
48 fragment.append(module_list)
49 return fragment
50
51
52def print_path_html(path):
53 """Print a path (and all sub paths) as html"""
54 return etree.tostring(_create_pathhtml(path, "basf2.Path")).decode()
55
56
57def print_module_html(module):
58 """Print a module (and its parameters) as well as the description"""
59 name = module.name()
60 tname = module.type()
61 tname = f" (type {tname})" if tname != name else ""
62 module_html = E.div("basf2.Module ", E.b(name), tname, E.p(module.description),
63 E.style("table.b2module * { text-align: left !important; }"))
64 cols = ["parameter", "type", "default", "current", "changed", "is required"]
65 if len(module.available_params()) > 0:
66 params = E.tbody()
67 for p in module.available_params():
68 params.append(E.tr(E.td(p.name), E.td(E.code(str(p.type))), E.td(E.code(str(p.default))), E.td(E.code(str(p.values))),
69 E.td(E.b("yes") if p.setInSteering else "no"),
70 E.td(E.b("yes") if p.forceInSteering else "no")))
71 params.append(E.tr(E.td(), E.td(p.description, colspan="5")))
72
73 module_html.append(E.table(E.thead(E.tr(*[E.th(e) for e in cols])), params, **{"class": "b2module"}))
74
75 return etree.tostring(module_html).decode()
76
77
78def enable_notebooksupport():
79 """
80 Small function to enable some notebook compatibility.
81 Mostly make sure that log messages are shown correctly (they are C++ so they need some special love to show up).
82 """
83 # we're in a notebook, reset log system to print to python sys.stdout
84 # and we know we want colored output even though stdout might not be a terminal
85 b2core.logging.reset()
86 b2core.logging.add_console(True)
87 b2core.logging.enable_python_logging = True
88
89 # we need to import ROOT because otherwise it can lockup later
90 import ROOT # noqa
91
92 # register html converters for common objects
93 html_formatter = get_ipython().display_formatter.formatters['text/html']
94 html_formatter.for_type(b2core.Path, print_path_html)
95 html_formatter.for_type(b2core.Module, print_module_html)
96
97
98def _child_process(pipe, path, max_events):
99 """Execute process() in a child process but send the persistent datastore objects back to the parent"""
100 # do the processing
101 b2core.process(path, max_events)
102 # no send back all the objects we want. Which currently is only the stats
103 return_objects = {}
104 for name in ["ProcessStatistics"]:
105 name = str(name)
106 storeobj = Belle2.PyStoreObj(name, Belle2.DataStore.c_Persistent)
107 if storeobj.isValid():
108 return_objects[name] = str(ROOT.TBufferJSON.ToJSON(storeobj.obj()))
109 pipe.send(return_objects)
110 pipe.close()
111
112
113def process(path, max_event=0, calculateStatistics=False):
114 """
115 Start processing events using the modules in the given `basf2.Path` object.
116
117 Can be called multiple times in one steering file.
118
119 This is a convenience wrapper which will automatically call the
120 `process()` function in a separate process to prevent FATAL errors from killing
121 the notebook.
122
123 Caveats:
124 As processing is done in a separate process the global state of the notebook
125 cannot be modified. For example a python modules collecting information will appear to be
126 empty after execution and global variables will also not be modified.
127
128 If you need to modify the state of the notebook during processing you need to call
129 basf2.core.process
130
131 Parameters:
132 path: The path with which the processing starts
133 max_event: The maximal number of events which will be processed, 0 for no limit
134 calculateStatistics: Switch to turn on calculation of processing statistics
135
136 Returns:
137 None
138 """
139 global _process_warning
140 if not _process_warning:
141 _process_warning = True
142 b2core.B2INFO("process() called in a Jupyter Notebook. See help(basf2.process) for caveats")
143
144 datastore = Belle2.DataStore.Instance()
145 if calculateStatistics:
146 Belle2.Environment.Instance().setStats(True)
147 ctx = multiprocessing.get_context("fork")
148 nanny, child = ctx.Pipe()
149 process = ctx.Process(target=_child_process, args=(child, path, max_event))
150 process.start()
151 process.join()
152 if process.exitcode != 0:
153 raise RuntimeError("Event processing was not successful")
154 return_objects = nanny.recv()
155 nanny.close()
156 datastore.setInitializeActive(True)
157 try:
158 for name, value in return_objects.items():
159 storeobj = Belle2.PyStoreObj(name, Belle2.DataStore.c_Persistent)
160 if value is not None:
161 storeobj.registerInDataStore()
162 storeobj.create()
163 obj = ROOT.TBufferJSON.ConvertFromJSON(value)
164 ROOT.SetOwnership(obj, False)
165 storeobj.assign(obj, True)
166 finally:
167 datastore.setInitializeActive(False)
static DataStore & Instance()
Instance of singleton Store.
Definition DataStore.cc:53
static Environment & Instance()
Static method to get a reference to the Environment instance.
a (simplified) python wrapper for StoreObjPtr.
Definition PyStoreObj.h:67