diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-basic.py b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-basic.py index 4033edef57ded1ae24dc118267667ff83aa92996..9162310a1f7d0b99e8ec7a1e78acc601fd518218 100644 --- a/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-basic.py +++ b/DaVinciExamples/python/DaVinciExamples/tupling/example-tupling-basic.py @@ -18,13 +18,12 @@ Example of a typical DaVinci job: __author__ = "Maurizio Martinelli" __date__ = "2021-03-16" -from DaVinci.standard_particles import make_detached_mumu -from DaVinci import options, run_davinci, DVNode, DVHelper +from DaVinci import options, run_davinci +from DaVinci.standard_particles import make_detached_mumu, make_KsDD from DaVinci.reco_objects import upfront_reconstruction_from_file as upfront_reconstruction import Functors as F -from FunTuple import ParticleTupleProp, convert_to_parsable_objs -from PyConf.Algorithms import PrintDecayTree from PyConf.Algorithms import FunTuple_Particles as FunTuple +from FunTuple import ParticleTupleProp, convert_to_parsable_objs # print control flow and data flow graphs options.control_flow_file = 'control_flow.gv' @@ -41,13 +40,29 @@ options.lumi = True print(options) -# Prepare the node with the selection +# selections dimuons = make_detached_mumu() +kshorts = make_KsDD() + + +# Add FUNTuples +def CreateSimpleTuple(name, tree_name, parsable_objs, loki_preamble, inputs): + ftup = FunTuple( + name=name, + tree_name=tree_name, + branch_names_prefix=parsable_objs[0], + decay_descriptors=parsable_objs[1], + loki_functors=parsable_objs[2][0], + loki_functor_branch_names=parsable_objs[2][1], + thor_functors=parsable_objs[3][0], + thor_functor_branch_names=parsable_objs[3][1], + loki_preamble=loki_preamble, + input_location=inputs) + return ftup + -# Add a FUNTuple #FunTuple: define list of preambles for loki loki_preamble = ['TRACK_MAX_PT = MAXTREE(ISBASIC & HASTRACK, PT, -1)'] - #FunTuple: Jpsi info ParticleJpsi = ParticleTupleProp( branch_name_prefix="Jpsi", @@ -74,22 +89,34 @@ ParticleMuPlus = ParticleTupleProp( particle_code_thor={'THOR_P': F.P}) #FunTuple: Do not need to do this if a custom gaudi property for ParticleTupleProp is implemented -parsable_objs = convert_to_parsable_objs([ParticleJpsi, ParticleMuPlus]) - -#FunTuple: Configure FunTuple algorithm -ftup = FunTuple( - name="DimuonsTuple", - tree_name="DecayTree", - branch_names_prefix=parsable_objs[0], - decay_descriptors=parsable_objs[1], - loki_functors=parsable_objs[2][0], - loki_functor_branch_names=parsable_objs[2][1], - thor_functors=parsable_objs[3][0], - thor_functor_branch_names=parsable_objs[3][1], - loki_preamble=loki_preamble, - input_location=dimuons) - -dvh = DVHelper() -dvh.add_selections_node("Selection1", upfront_reconstruction() + [dimuons]) -dvh.add_tuples_node('Tuple1', [ftup]) -dvh.run(options) +parsable_objs_dimuons = convert_to_parsable_objs( + [ParticleJpsi, ParticleMuPlus]) +ftup_dimuons = CreateSimpleTuple("DimuonsTuple", "DecayTree", + parsable_objs_dimuons, loki_preamble, dimuons) + +ParticleKS = ParticleTupleProp( + branch_name_prefix="Ks", + decay_descriptor="KS0 -> pi+ pi-", + #Dict -> {name:functor} + particle_code_loki={ + 'LOKI_P': 'P', + 'LOKI_PT': 'PT', + 'LOKI_Muonp_PT': 'CHILD(PT, 1)', + 'LOKI_Muonm_PT': 'CHILD(PT, 2)', + 'LOKI_MAXPT': 'TRACK_MAX_PT', + 'LOKI_N_HIHGPT_TRCKS': 'NINTREE(ISBASIC & HASTRACK & (PT > 1500*MeV))' + }, + particle_code_thor={ + 'THOR_P': F.P, + 'THOR_PT': F.PT + }) +parsable_objs_kshorts = convert_to_parsable_objs([ParticleKS]) +ftup_kshorts = CreateSimpleTuple("KsTuple", "DecayTree", parsable_objs_kshorts, + loki_preamble, kshorts) + +algs = { + 'DiMuons': upfront_reconstruction() + [dimuons, ftup_dimuons], + 'KShorts': upfront_reconstruction() + [kshorts, ftup_kshorts] +} + +run_davinci(options, algs) diff --git a/DaVinciExamples/tests/qmtest/tupling.qms/test_example-tupling-basic.qmt b/DaVinciExamples/tests/qmtest/tupling.qms/test_example-tupling-basic.qmt index ee15744a622575fd5bc4f65ab07b3a8c67f52563..e96714b4dbd0c6bf35b44a422cfa2e9c8b23e34c 100644 --- a/DaVinciExamples/tests/qmtest/tupling.qms/test_example-tupling-basic.qmt +++ b/DaVinciExamples/tests/qmtest/tupling.qms/test_example-tupling-basic.qmt @@ -22,7 +22,7 @@ RFileCnv INFO dumping contents of /NTUPLES/FILE1 TFile: name=DV-example-tupling-basic-ntp.root, title=Gaudi Trees, option=CREATE ****************************************************************************** *Tree :DecayTree : DecayTree * -*Entries : 3 : Total = 6907 bytes File Size = 2072 * +*Entries : 3 : Total = 6907 bytes File Size = 2065 * * : : Tree compression factor = 1.00 * ****************************************************************************** *Br 0 :Jpsi_LOKI_N_HIHGPT_TRCKS : Jpsi_LOKI_N_HIHGPT_TRCKS/D * @@ -65,6 +65,43 @@ TFile: name=DV-example-tupling-basic-ntp.root, title=Gaudi Trees, option=CREATE *Entries : 3 : Total Size= 642 bytes File Size = 109 * *Baskets : 1 : Basket Size= 32000 bytes Compression= 1.00 * *............................................................................* +****************************************************************************** +*Tree :DecayTree : DecayTree * +*Entries : 4 : Total = 5597 bytes File Size = 1775 * +* : : Tree compression factor = 1.01 * +****************************************************************************** +*Br 0 :Ks_LOKI_N_HIHGPT_TRCKS : Ks_LOKI_N_HIHGPT_TRCKS/D * +*Entries : 4 : Total Size= 695 bytes File Size = 114 * +*Baskets : 1 : Basket Size= 32000 bytes Compression= 1.11 * +*............................................................................* +*Br 1 :Ks_LOKI_PT : Ks_LOKI_PT/D * +*Entries : 4 : Total Size= 635 bytes File Size = 114 * +*Baskets : 1 : Basket Size= 32000 bytes Compression= 1.00 * +*............................................................................* +*Br 2 :Ks_LOKI_MAXPT : Ks_LOKI_MAXPT/D * +*Entries : 4 : Total Size= 650 bytes File Size = 117 * +*Baskets : 1 : Basket Size= 32000 bytes Compression= 1.00 * +*............................................................................* +*Br 3 :Ks_LOKI_Muonp_PT : Ks_LOKI_Muonp_PT/D * +*Entries : 4 : Total Size= 665 bytes File Size = 120 * +*Baskets : 1 : Basket Size= 32000 bytes Compression= 1.00 * +*............................................................................* +*Br 4 :Ks_LOKI_Muonm_PT : Ks_LOKI_Muonm_PT/D * +*Entries : 4 : Total Size= 665 bytes File Size = 120 * +*Baskets : 1 : Basket Size= 32000 bytes Compression= 1.00 * +*............................................................................* +*Br 5 :Ks_LOKI_P : Ks_LOKI_P/D * +*Entries : 4 : Total Size= 630 bytes File Size = 113 * +*Baskets : 1 : Basket Size= 32000 bytes Compression= 1.00 * +*............................................................................* +*Br 6 :Ks_THOR_P : Ks_THOR_P/D * +*Entries : 4 : Total Size= 630 bytes File Size = 113 * +*Baskets : 1 : Basket Size= 32000 bytes Compression= 1.00 * +*............................................................................* +*Br 7 :Ks_THOR_PT : Ks_THOR_PT/D * +*Entries : 4 : Total Size= 635 bytes File Size = 114 * +*Baskets : 1 : Basket Size= 32000 bytes Compression= 1.00 * +*............................................................................* """, stdout, result, causes, signature_offset = 0) countErrorLines({"FATAL":0, "ERROR":0}) @@ -86,4 +123,4 @@ except: raise Exception('Unexpected error while opening file: ') <argument name="exit_code"><integer>1</integer></argument> </text></argument> -</extension> \ No newline at end of file +</extension> diff --git a/Phys/DaVinci/python/DaVinci/__init__.py b/Phys/DaVinci/python/DaVinci/__init__.py index d44ee4dc5806a51bf450b1729e6dacd4d09c30ac..2427da62d9ed115e9ca052886f21b5d366d3305e 100644 --- a/Phys/DaVinci/python/DaVinci/__init__.py +++ b/Phys/DaVinci/python/DaVinci/__init__.py @@ -10,6 +10,6 @@ ############################################################################### from __future__ import absolute_import -from .config import options, run_davinci, DVNode, DVHelper +from .config import options, run_davinci, DVNode -__all__ = ('options', 'run_davinci', 'DVNode', 'DVHelper') +__all__ = ('options', 'run_davinci', 'DVNode') diff --git a/Phys/DaVinci/python/DaVinci/config.py b/Phys/DaVinci/python/DaVinci/config.py index ae2058b14ae572577492338630b22a079f882941..66ff329ea80f265ab8d91bffeddd7d001ba089a5 100644 --- a/Phys/DaVinci/python/DaVinci/config.py +++ b/Phys/DaVinci/python/DaVinci/config.py @@ -84,92 +84,30 @@ class DVNode(namedtuple('DVNode', ['node', 'extra_outputs'])): # noqa return self.output_producer is not None -class DVHelper(): - """Class for collecting DV algorithms.""" - - def __init__(self): - self.selections = [] - self.user = [] - self.tuples = [] - self.writers = [] - - def add_node(self, node_name, algs, kind): - if hasattr(self, kind): - setattr(self, kind, - getattr(self, kind) + [DVNode(node_name, algs)]) - else: - print('the specified note type ({}) is not valid'.format(kind)) - return - - def add_selections_node(self, node_name, algs): - self.add_node(node_name, algs, 'selections') - return - - def add_user_node(self, node_name, algs): - self.add_node(node_name, algs, 'user') - return - - def add_tuples_node(self, node_name, algs): - self.add_node(node_name, algs, 'tuples') - return - - def add_writers_node(self, node_name, algs): - self.add_node(node_name, algs, 'writers') - return - - def run(self, options, public_tools=[]): - run_davinci( - options, - selections=self.selections, - user=self.user, - tuples=self.tuples, - writers=self.writers, - public_tools=public_tools) - - -def davinci_control_flow(options, - SelectionNodes=[], - UserAlgorithms=[], - TuplesNodes=[], - WritersNodes=[]): +def davinci_control_flow(options, user_analysis_nodes=[]): ''' - DaVinci control flow is split in a few sections as described in DaVinci/issue#2 + DaVinci control flow is split in a few sections as described in DaVinci/issue#2 (then simplified) DaVinci (LAZY_AND) *-- LuminosityNode (NONLAZY_OR) | *-- EventAccounting/EventAccount - *-- DVSelectionsNode (NONLAZY_OR) - | +-- DVCandidate1Node (LAZY_AND) - | | *-- PVFilter - | | *-- ParticlesFilter - | | *-- Candidate1Combiner - | +-- DVCandidate2Node (LAZY_AND) - | | *-- PVFilter - | | *-- ParticlesFilter - | | *-- Candidate2Combiner - *-- DVUserAnalysisNode (NONLAZY_OR) - | *-- PrintHeader - | *-- PrintDecayTree/PrintMyB - *-- DVTuplesNode (NONLAZY_OR) - | +-- DVTuple1Node (LAZY_AND) - | | *-- AlgorithmsForTuple1 - | | *-- Tuple1 - | +-- DVTuple2Node (LAZY_AND) - | | *-- AlgorithmsForTuple2 - | | *-- Tuple2 - | +-- DVTuple3Node (LAZY_AND) - | | *-- AlgorithmsForTuple3 - | | *-- Tuple3 - *-- DVWriterNode (LAZY_AND) - *-- DSTWriter? - *-- TuplesWriter + *-- UserAnalysisNode (NONLAZY_OR) + +-- DVUser1Node (LAZY_AND) + | *-- PVFilter + | *-- ParticlesFilter + | *-- Candidate1Combiner + | *-- AlgorithmsForTuple1 + | *-- Tuple1 + +-- DVUser2Node (LAZY_AND) + *-- PVFilter + *-- ParticlesFilter + *-- Candidate2Combiner + *-- AlgorithmsForTuple2 + *-- Tuple2 The main parts are . LuminosityNode - . SelectionsNode . UserAnalysisNode - . TuplesNode - . WritersNode who are in AND among each other and can accomodate nodes (defined with the class DVNode) in OR among themselves. @@ -181,17 +119,14 @@ def davinci_control_flow(options, options.finalize() dv_top_children = [] # Setup luminosity - LumiNodes = [] + lumi_nodes = [] if options.lumi: - LumiNodes += [ + lumi_nodes += [ DVNode('Lumi', [EventAccounting(name='EventAccount')]) ] # this should be modified to reflect LumiAlgsConf (configured separately?) ordered_nodes = OrderedDict() - ordered_nodes['Luminosity'] = LumiNodes - ordered_nodes['Selections'] = SelectionNodes - ordered_nodes['UserAlgorithms'] = UserAlgorithms - ordered_nodes['Tuples'] = TuplesNodes - ordered_nodes['Writers'] = WritersNodes + ordered_nodes['Luminosity'] = lumi_nodes + ordered_nodes['UserAnalysis'] = user_analysis_nodes for k, v in ordered_nodes.items(): if len(v): cnode = CompositeNode( @@ -208,30 +143,42 @@ def davinci_control_flow(options, force_order=True) -def run_davinci(options, - selections=[], - user=[], - tuples=[], - writers=[], - public_tools=[]): +def prepare_davinci_nodes(user_algs={}): + ''' + This function takes as input a dictionary of user algorithms in the form + { + 'DVNode1' : [<list of algs>], + 'DVNode2' : [<list of algs>], + } + and creates the node to send to `davinci_control_flow`. + ''' + dv_nodes = [] + for k, algs in user_algs.items(): + #if type(algs) == dict: # it could be extended to add complexity, but it is probably useless. The experienced user has all that's needed. + # dv_nodes += [prepare_davinci_nodes(algs)] + #else: + if not type(algs) == list: + raise ValueError('value not a list of algorithms. quitting.') + dv_nodes += [DVNode(k, algs)] + return dv_nodes + + +def run_davinci(options, user_algs={}, public_tools=[]): ''' DaVinci application control flow Args: options (ApplicationOptions): holder of application options - selections : list of selections nodes - user : list of user nodes - tuples : list of tuples nodes - writers : list of writers nodes + user_algs : list of user algorithms public_tools (list): list of public `Tool` instances to configure ''' - if not len(selections + user + tuples + writers): + if not len(user_algs): dummy = CompositeNode("EmptyNode", children=[]) config = configure(options, dummy, public_tools=public_tools) else: config = configure_input(options) - top_dv_node = davinci_control_flow(options, selections, user, tuples, - writers) + dv_nodes = prepare_davinci_nodes(user_algs) + top_dv_node = davinci_control_flow(options, dv_nodes) config.update( configure(options, top_dv_node, public_tools=public_tools)) return config