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.
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 controls 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.