21from ROOT
import Belle2
30 """Getter for the logger instance of this file."""
31 return logging.getLogger(__name__)
34def coroutine(generator_func):
35 """Famous coroutine decorator.
37 Starts a receiving generator function to the first yield,
38 such that it can receive a sent call immediately.
41 @functools.wraps(generator_func)
42 def start(*args, **kwargs):
43 cr = generator_func(*args, **kwargs)
49def harvest(foreach="", pick=None, name=None, output_file_name=None, show_results=False):
50 """Decorator to turn a function into a HarvestingModule instance.
52 The decorated function becomes the peel method of the module.
53 The function is invoked once for each element in the foreach storearray and should return
54 a mapping of names and values extracting the relevant variables from the object in question.
59 Name of the StoreArray or StoreObjPtr
60 pick : function(obj) -> bool
61 Function that gets invoked with each object in the foreach StoreArray.
62 It can return a False value, if the object should not be investigated further.
64 Name used in tree abd histrogram names produced in this harvest.
65 output_file_name : string
66 Name of the ROOT output file to be produced.
71 A decorator that turns a function into HarvestingModule instance,
72 which peel function is replaced by the decorated function.
76 An example of the usage pattern can be found at the end of this file
79 def harvest_decorator(peel_func):
80 name_or_default = name
or peel_func.__name__
81 output_file_name_or_default = output_file_name
or f
"{name_or_default}.root"
82 harvesting_module = HarvestingModule(foreach=foreach,
83 output_file_name=output_file_name_or_default,
85 show_results=show_results)
86 harvesting_module.peel = peel_func
88 harvesting_module.pick = pick
89 return harvesting_module
90 return harvest_decorator
95 """Python module to generate summary figures of merits, plots and/or trees
98 It runs as a proper module in the main path and examines each object in a StoreArray
104 Methods to be overwritten
106 Method called at the start of each event, that may prepare things
107 (e.g. setup lookup tables or precomputed list) used in the following methods.
109 Method called with each object in the StoreArray.
110 Returns a False value if the object should be skipped.
113 Method called with each object in the StoreArray.
114 Extractes the parts relevant for analysis and
115 returns them as MutableMapping (e.g. a dict) of part_name and values.
116 Currently only float values or values convertible to floats are supported.
117 If requested that can change in the future.
119 On termination all the collected values are recasted to numpy arrays and
120 the whole ``crops`` of the harvest are casted to MutableMapping of numpy.array
121 with the same part_name and the same MutableMapping class as returned from peel.
123 Also in the termination phase refiners a invoked with the aggregated crops.
124 Refiners can be given in two ways.
126 First way is as a class methods marked as refiners like
129 def plot(self, crops, tdirectory, **kwds):
132 where self is the module instance, crops is the MutableMapping of numpy arrays and tdirectory
133 is the current tdirectory to which the current output shall be written.
134 The additional kwds leave room for future additional arguments.
136 Second way is to define the refiner method (like plot) out of line and add it to
137 the harvesting module instance refiners list like harvesting_module.refiners.append(plot).
139 Other specialised decorators to mark a function as a Refiner such as
147 Predefined refiner functions exist in the refiners python module as well.
150 save_tree = refiners.save_tree()
152 is a predefined method to output the MutableMapping of numpy arrays as a TTree.
156 default_expert_level = 1
166 """Constructor of the harvesting module.
171 Name of a StoreArray, which objects should be investigated
172 output_file_name : string
173 Name of the ROOT file to which the results should be written.
174 Giving an opened ROOT file is also allowed.
175 If None is given write to the current ROOT file.
176 name : string, optional
177 Name of the harvest that is used in the names of ROOT plots and trees.
178 Defaults to the class name.
179 title : string, optional
180 Name of the harvest that is used in the title of ROOT plots and trees.
181 Defaults to the name.
182 contact : string, optional
183 Contact email address to be used in the validation plots contact. Defaults to None.
184 expert_level : int, optional
185 Expert level that can be used to switch on more plots.
186 Generally the higher the more detailed to analysis.
187 Meaning depends entirely on the subclass implementing a certain policy.
188 Defaults to default_expert_level.
189 show_results : bool, optional
190 Indicator to show the refined results at termination of the path
202 raise TypeError(
"output_file_name is allowed to be a string or a ROOT.TFile object")
205 self.set_name(name
or self.__class__.__name__)
225 """Working around that name() is a method.
227 Exposing the name as a property using a different name
232 """Initialisation method of the module.
234 Prepares the receiver stash of objects to be harvestered.
240 """Event method of the module
242 * Does invoke the prepare method before the iteration starts.
243 * In each event fetch the StoreArray / iterable StoreObjPtr,
244 * Iterate through all instances
245 * Feed each instance to the pick method to decide it the instance is relevant
246 * Forward it to the peel method that should generated a dictionary of values
247 * Store each dictionary of values
250 stash = self.
stash.send
253 for crop
in self.
gather():
256 if isinstance(crop, types.GeneratorType):
258 for crop
in many_crops:
264 """Termination method of the module.
266 Finalize the collected crops.
267 Start the refinement.
275 except AttributeError:
280 """Create the storing objects for the crop values
282 Currently a numpy.array of doubles is used to store all values in memory.
284 return array.array(
"d")
288 """Coroutine that receives the dictionaries of names and values from peel and store them."""
290 raw_crops = copy.copy(crop)
291 crops = copy.copy(crop)
293 if isinstance(crop, numbers.Number):
297 raw_crops.append(crop)
300 except GeneratorExit:
301 crops = np.array(raw_crops)
303 elif isinstance(crop, collections.abc.MutableMapping):
304 for part_name
in crop:
309 for part_name, parts
in list(raw_crops.items()):
310 if part_name
in crop:
311 parts.append(crop[part_name])
316 except GeneratorExit:
317 for part_name, parts
in list(raw_crops.items()):
318 crops[part_name] = np.array(parts)
321 msg = f
"Unrecognised crop {crop} of type {type(crop)}"
322 raise ValueError(msg)
330 """Iterator that yield the instances form the StoreArray / iterable StoreObj.
334 Object instances from the StoreArray, iterable StoreObj or the StoreObj itself
335 in case it is not iterable.
342 foreach_is_store_obj = foreach
in registered_store_objs
343 foreach_is_store_array = foreach
in registered_store_arrays
345 if foreach
is not None:
346 if foreach_is_store_array:
348 yield from store_array
350 elif foreach_is_store_obj:
356 yield store_obj.obj()
359 msg = f
"Name {self.foreach} does not refer to a valid object on the data store"
365 """Default implementation of prepare.
367 Can be overridden by subclasses.
372 """Unpack the the instances and return and dictionary of names to values or
373 a generator of those dictionaries to be saved.
378 Unpacked names and values
384 Unpacked names and values
387 return {
"name": np.nan}
390 """Indicate whether the instance should be forwarded to the peeling
394 bool : Indicator if the instance is valuable in the current harvest.
399 """Receive the gathered crops and forward them to the refiners."""
408 output_tdirectory = output_tfile
411 output_tdirectory =
None
414 with root_cd(output_tdirectory):
416 refiner(self, crops, tdirectory=output_tdirectory, **kwds)
420 for name
in dir(cls):
421 if isinstance(getattr(cls, name), Refiner):
422 refiner = getattr(self, name)
424 refiner(crops, tdirectory=output_tdirectory, **kwds)
435 root_browse(output_tfile)
436 input(
"Press enter to close")
440 input(
"Press enter to close")
444 """Obtain a iterator from a StoreObj
446 Repeatedly calls iter(store_obj) or store_obj.__iter__()
447 until the final iterator returns itself
451 iterator of the StoreObj
453 iterable = store_obj.obj()
455 while iterable
is not last_iterable:
456 if hasattr(iterable,
"__iter__"):
457 iterable, last_iterable = iterable.__iter__(), iterable
459 iterable, last_iterable = iter(iterable), iterable
464 """Test a quick analysis of the MCParticles in generic events."""
465 from .refiners
import save_histograms, save_tree, save_fom
467 def primaries_seen_in_detector(mc_particle):
468 return (mc_particle.hasStatus(Belle2.MCParticle.c_PrimaryParticle)
and
469 mc_particle.hasStatus(Belle2.MCParticle.c_StableInGenerator)
and
470 not mc_particle.hasStatus(Belle2.MCParticle.c_IsVirtual)
and
471 (mc_particle.hasStatus(Belle2.MCParticle.c_LeftDetector)
or
472 mc_particle.hasStatus(Belle2.MCParticle.c_StoppedInDetector)))
475 @save_fom(aggregation=np.mean, select=["energy", "pt"], name="physics", key="mean_{part_name}")
476 @save_histograms(outlier_z_score=5.0,
478 filter=
lambda xs: xs != 0.0,
479 filter_on=
"is_secondary",
480 select=[
"pt",
"is_secondary"],
481 folder_name=
"secondary_pt")
482 @save_histograms(outlier_z_score=5.0,
485 select=[
"is_secondary",
"pt"])
486 @save_histograms(outlier_z_score=5.0,
488 select=[
"is_secondary",
"pt"],
489 stackby=
"is_secondary",
490 folder_name=
"pt_stackby_is_secondary/nested_test")
491 @save_histograms(outlier_z_score=5.0,
493 select={
'pt':
'$p_t$'},
494 title=
"Distribution of p_{t}")
496 @harvest(foreach=
"MCParticles",
497 pick=primaries_seen_in_detector,
498 output_file_name=
"MCParticleOverview.root")
499 def MCParticleOverview(mc_particle):
500 momentum = mc_particle.getMomentum()
501 pdg_code = mc_particle.getPDG()
502 secondary_process = mc_particle.getSecondaryPhysicsProcess()
506 tan_lambda=np.divide(1.0, np.tan(momentum.Theta())),
508 secondary_process=secondary_process,
509 is_secondary=secondary_process != 0,
510 mass=mc_particle.getMass(),
511 status=mc_particle.getStatus(),
512 pdg_mass=ROOT.TDatabasePDG.Instance().GetParticle(pdg_code).Mass(),
513 energy=mc_particle.getEnergy(),
517 from .run
import HarvestingRun
519 class ExampleHarvestingRun(HarvestingRun):
522 def harvesting_module(self):
523 return MCParticleOverview
525 ExampleHarvestingRun().configure_and_execute_from_commandline()
528if __name__ ==
"__main__":
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.
foreach
Name of the StoreArray or iterable StoreObjPtr that contains the objects to be harvested.
list refiners
A list of additional refiner instances to be executed on top of the refiner methods that are members ...
stash
stash of the harvested crops (start with those in the barn)
contact
Contact email address to be displayed on the validation page.
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.
crops
the dictionaries from peel
__init__(self, foreach, output_file_name, name=None, title=None, contact=None, expert_level=None, show_results=False)
int expert_level
Integer expert level that controls to detail of plots to be generated.
create_crop_part_collection()
iter_store_obj(store_obj)
int default_expert_level
The default value of expert_level if not specified explicitly by the caller.