23 from ROOT
import Belle2
32 """Getter for the logger instance of this file."""
33 return logging.getLogger(__name__)
36 def coroutine(generator_func):
37 """Famous coroutine decorator.
39 Starts a receiving generator function to the first yield,
40 such that it can receive a send call immediatly.
43 @functools.wraps(generator_func)
44 def start(*args, **kwargs):
45 cr = generator_func(*args, **kwargs)
51 def harvest(foreach="", pick=None, name=None, output_file_name=None, show_results=False):
52 """Decorator to turn a function into a HarvestingModule instance.
54 The decorated function becomes the peel method of the module.
55 The function is invoked once for each element in the foreach storearray and should return
56 a mapping of names and values extracting the relevant variables from the object in question.
61 Name of the StoreArray or StoreObjPtr
62 pick : function(obj) -> bool
63 Function that gets invoked with each object in the foreach StoreArray.
64 It can return a False value, if the object should not be investigated further.
66 Name used in tree abd histrogram names produced in this harvest.
67 output_file_name : string
68 Name of the ROOT output file to be produced.
73 A decorator that turns a function into HarvestingModule instance,
74 which peel function is replaced by the decorated function.
78 An example of the usage pattern can be found at the end of this file
81 def harvest_decorator(peel_func):
82 name_or_default = name
or peel_func.__name__
83 output_file_name_or_default = output_file_name
or "{}.root".format(name_or_default)
84 harvesting_module = HarvestingModule(foreach=foreach,
85 output_file_name=output_file_name_or_default,
87 show_results=show_results)
88 harvesting_module.peel = peel_func
90 harvesting_module.pick = pick
91 return harvesting_module
92 return harvest_decorator
97 """Python module to generate summary figures of merits, plots and/or trees
100 It runs as a proper module in the main path and examines each object in a StoreArray
106 Methods to be overwritten
108 Method called at the start of each event, that may prepare things
109 (e.g. setup lookup tables or precomputed list) used in the following methods.
111 Method called with each object in the StoreArray.
112 Returns a False value if the object should be skipped.
115 Method called with each object in the StoreArray.
116 Extractes the parts relevant for analysis and
117 returns them as MutableMapping (e.g. a dict) of part_name and values.
118 Currently only float values or values convertable to floats are supported.
119 If requested that can change in the future.
121 On termination all the collected values are recasted to numpy arrays and
122 the whole ``crops`` of the harvest are casted to MutableMapping of numpy.array
123 with the same part_name and the same MutableMapping class as returned from peel.
125 Also in the termination phase refiners a invoked with the aggregated crops.
126 Refiners can be given in two ways.
128 First way is as a class methods marked as refiners like
131 def plot(self, crops, tdirectory, **kwds):
134 where self is the module instance, crops is the MutableMapping of numpy arrays and tdirectory
135 is the current tdirectory to which the current output shall be written.
136 The additional kwds leave room for future additional arguments.
138 Second way is to define the refiner method (like plot) out of line and add it to
139 the harvesting module instance refiners list like harvesting_module.refiners.append(plot).
141 Other specialised decorators to mark a function as a Refiner such as
149 Predefined refiner functions exist in the refiners python module as well.
152 save_tree = refiners.save_tree()
154 is a predefined method to output the MutableMapping of numpy arrays as a TTree.
158 default_expert_level = 1
168 """Constructor of the harvesting module.
173 Name of a StoreArray, which objects should be investigated
174 output_file_name : string
175 Name of the ROOT file to which the results should be written.
176 Giving an opened ROOT file is also allowed.
177 If None is given write to the current ROOT file.
178 name : string, optional
179 Name of the harvest that is used in the names of ROOT plots and trees.
180 Defaults to the class name.
181 title : string, optional
182 Name of the harvest that is used in the title of ROOT plots and trees.
183 Defaults to the name.
184 contact : string, optional
185 Contact email adress to be used in the validation plots contact. Defaults to None.
186 expert_level : int, optional
187 Expert level that can be used to switch on more plots.
188 Generally the higher the more detailed to analysis.
189 Meaning depends entirely on the subclass implementing a certain policy.
190 Defaults to default_expert_level.
191 show_results : bool, optional
192 Indicator to show the refined results at termination of the path
203 if not isinstance(self.
output_file_nameoutput_file_name, (ROOT.TFile, str)):
204 raise TypeError(
"output_file_name is allowed to be a string or a ROOT.TFile object")
207 self.set_name(name
or self.__class__.__name__)
210 self.
titletitle = title
or self.name()
227 """Working around that name() is a method.
229 Exposing the name as a property using a different name
234 """Initialisation method of the module.
236 Prepares the receiver stash of objects to be harvestered.
242 """Event method of the module
244 * Does invoke the prepare method before the iteration starts.
245 * In each event fetch the StoreArray / iterable StoreObjPtr,
246 * Iterate through all instances
247 * Feed each instance to the pick method to deside it the instance is relevant
248 * Forward it to the peel method that should generated a dictionary of values
249 * Store each dictionary of values
252 stash = self.
stashstash.send
255 for crop
in self.
gathergather():
258 if isinstance(crop, types.GeneratorType):
260 for crop
in many_crops:
266 """Termination method of the module.
268 Finalize the collected crops.
269 Start the refinement.
272 self.
stashstash.close()
277 except AttributeError:
282 """Create the storing objects for the crop values
284 Currently a numpy.array of doubles is used to store all values in memory.
286 return array.array(
"d")
290 """Coroutine that receives the dictionaries of names and values from peel and store them."""
292 raw_crops = copy.copy(crop)
293 crops = copy.copy(crop)
295 if isinstance(crop, numbers.Number):
299 raw_crops.append(crop)
302 except GeneratorExit:
303 crops = np.array(raw_crops)
305 elif isinstance(crop, collections.MutableMapping):
306 for part_name
in crop:
311 for part_name, parts
in list(raw_crops.items()):
312 if part_name
in crop:
313 parts.append(crop[part_name])
318 except GeneratorExit:
319 for part_name, parts
in list(raw_crops.items()):
320 crops[part_name] = np.array(parts)
323 msg =
"Unrecognised crop {} of type {}".format(
327 raise ValueError(msg)
335 """Iterator that yield the instances form the StoreArray / iterable StoreObj.
339 Object instances from the StoreArray, iterable StoreObj or the StoreObj itself
340 in case it is not iterable.
347 foreach_is_store_obj = foreach
in registered_store_objs
348 foreach_is_store_array = foreach
in registered_store_arrays
350 if foreach
is not None:
351 if foreach_is_store_array:
353 for crop
in store_array:
356 elif foreach_is_store_obj:
363 yield store_obj.obj()
366 msg =
"Name {} does not refer to a valid object on the data store".format(
374 """Default implementation of prepare.
376 Can be overridden by subclasses.
381 """Unpack the the instances and return and dictionary of names to values or
382 a generator of those dictionaries to be saved.
387 Unpacked names and values
393 Unpacked names and values
396 return {
"name": np.nan}
399 """Indicate whether the instance should be forwarded to the peeling
403 bool : Indicator if the instance is valueable in the current harverst.
408 """Receive the gathered crops and forward them to the refiners."""
416 output_tfile = ROOT.TFile(self.
output_file_nameoutput_file_name,
'recreate')
417 output_tdirectory = output_tfile
420 output_tdirectory =
None
423 with root_cd(output_tdirectory):
424 for refiner
in self.
refinersrefiners:
425 refiner(self, crops, tdirectory=output_tdirectory, **kwds)
429 for name
in dir(cls):
430 if isinstance(getattr(cls, name), Refiner):
431 refiner = getattr(self, name)
433 refiner(crops, tdirectory=output_tdirectory, **kwds)
444 root_browse(output_tfile)
445 input(
"Press enter to close")
449 input(
"Press enter to close")
453 """Obtain a iterator from a StoreObj
455 Repeatly calls iter(store_obj) or store_obj.__iter__()
456 until the final iterator returns itself
460 iterator of the StoreObj
462 iterable = store_obj.obj()
464 while iterable
is not last_iterable:
465 if hasattr(iterable,
"__iter__"):
466 iterable, last_iterable = iterable.__iter__(), iterable
468 iterable, last_iterable = iter(iterable), iterable
473 """Test a quick analysis of the MCParticles in generic events."""
474 from .refiners
import save_histograms, save_tree, save_fom
476 def primaries_seen_in_detector(mc_particle):
477 return (mc_particle.hasStatus(Belle2.MCParticle.c_PrimaryParticle)
and
478 mc_particle.hasStatus(Belle2.MCParticle.c_StableInGenerator)
and
479 not mc_particle.hasStatus(Belle2.MCParticle.c_IsVirtual)
and
480 (mc_particle.hasStatus(Belle2.MCParticle.c_LeftDetector)
or
481 mc_particle.hasStatus(Belle2.MCParticle.c_StoppedInDetector)))
484 @save_fom(aggregation=np.mean, select=["energy", "pt"], name="physics", key="mean_{part_name}")
485 @save_histograms(outlier_z_score=5.0,
allow_discrete=True,
filter=lambda xs: xs != 0.0,
filter_on="is_secondary",
select=["pt", "is_secondary"],
folder_name="secondary_pt")
486 @save_histograms(outlier_z_score=5.0,
allow_discrete=True,
groupby="status",
select=["is_secondary", "pt"])
487 @save_histograms(outlier_z_score=5.0,
allow_discrete=True,
select=["is_secondary", "pt"],
stackby="is_secondary",
folder_name="pt_stackby_is_secondary/nested_test")
488 @save_histograms(outlier_z_score=5.0,
allow_discrete=True,
select={'pt': '$p_t$'},
title="Distribution of p_{t}")
490 @harvest(foreach="MCParticles",
pick=primaries_seen_in_detector,
output_file_name="MCParticleOverview.root")
491 def MCParticleOverview(mc_particle):
492 momentum = mc_particle.getMomentum()
493 pdg_code = mc_particle.getPDG()
494 secondary_process = mc_particle.getSecondaryPhysicsProcess()
498 tan_lambda=np.divide(1.0, np.tan(momentum.Theta())),
500 secondary_process=secondary_process,
501 is_secondary=secondary_process != 0,
502 mass=mc_particle.getMass(),
503 status=mc_particle.getStatus(),
504 pdg_mass=ROOT.TDatabasePDG.Instance().GetParticle(pdg_code).Mass(),
505 energy=mc_particle.getEnergy(),
509 from .run
import HarvestingRun
511 class ExampleHarvestingRun(HarvestingRun):
514 def harvesting_module(self):
515 return MCParticleOverview
517 ExampleHarvestingRun().configure_and_execute_from_commandline()
520 if __name__ ==
"__main__":
522 A (simplified) python wrapper for StoreArray.
static std::vector< std::string > list(DataStore::EDurability durability=DataStore::EDurability::c_Event)
Return list of available arrays for given durability.
a (simplified) python wrapper for StoreObjPtr.
static std::vector< std::string > list(DataStore::EDurability durability=DataStore::EDurability::c_Event)
Return list of available objects for given durability.
title
Name of this harvest.
refiners
A list of additional refiner instances to be executed on top of the refiner methods that are members ...
foreach
Name of the StoreArray or iterable StoreObjPtr that contains the objects to be harvested.
stash
stash of the harvested crops (start with those in the barn)
contact
Contact email address to be displayed on the validation page.
def iter_store_obj(store_obj)
show_results
Switch to show the result ROOT file in a TBrowser on terminate.
raw_crops
the dictionaries from peel as a numpy.array of doubles
output_file_name
Name of the ROOT output file to be generated.
def create_crop_part_collection()
crops
the dictionaries from peel
expert_level
Integer expert level that controlls to detail of plots to be generated.
def __init__(self, foreach, output_file_name, name=None, title=None, contact=None, expert_level=None, show_results=False)
int default_expert_level
The default value of expert_level if not specified explicitly by the caller.