diff --git a/Reconstruction/Jet/JetMomentTools/python/JetMomentToolsConfig.py b/Reconstruction/Jet/JetMomentTools/python/JetMomentToolsConfig.py index b01da21c53700253b88dea8ee497325662da5627..96d12a47f67d6444599cb119b8598fa6ca5e684d 100644 --- a/Reconstruction/Jet/JetMomentTools/python/JetMomentToolsConfig.py +++ b/Reconstruction/Jet/JetMomentTools/python/JetMomentToolsConfig.py @@ -17,8 +17,8 @@ from AthenaCommon import Logging jetmomentlog = Logging.logging.getLogger('JetMomentToolsConfig') from JetRecTools import JetRecToolsConfig -from JetRecTools.JetRecToolsConfig import trackcollectionmap from AthenaConfiguration.ComponentFactory import CompFactory +from JetRecConfig.StandardJetContext import jetContextDic from xAODBase.xAODType import xAODType @@ -92,10 +92,10 @@ def getConstitFourMomTool(jetdef, modspec=""): return cfourmom # Jet vertex fraction with selection. -def getJVFTool(jetdec, modspec): - jettrackselloose = JetRecToolsConfig.getTrackSelTool(modspec) - # retrieve the tracking keys to be used with modspec : - trackingKeys = trackcollectionmap[modspec] +def getJVFTool(jetdef, modspec): + jettrackselloose = JetRecToolsConfig.getTrackSelTool(modspec or jetdef.context) + # retrieve the tracking keys to be used with modspec : + trackingKeys = jetContextDic[modspec or jetdef.context] jvf = CompFactory.JetVertexFractionTool( "jvf", VertexContainer = trackingKeys["Vertices"], @@ -111,15 +111,15 @@ def getJVFTool(jetdec, modspec): def getJVTTool(jetdef, modspec): jvt = CompFactory.JetVertexTaggerTool( "jvt", - VertexContainer = trackcollectionmap[modspec]["Vertices"], + VertexContainer = jetContextDic[modspec or jetdef.context]["Vertices"], ) return jvt def getTrackMomentsTool(jetdef, modspec): - jettrackselloose = JetRecToolsConfig.getTrackSelTool(modspec) + jettrackselloose = JetRecToolsConfig.getTrackSelTool(modspec or jetdef.context) # retrieve the tracking keys to be used with modspec : - trackingKeys = trackcollectionmap[modspec] + trackingKeys = jetContextDic[modspec or jetdef.context] trackmoments = CompFactory.JetTrackMomentsTool( "trkmoms", @@ -127,14 +127,15 @@ def getTrackMomentsTool(jetdef, modspec): AssociatedTracks = trackingKeys["GhostTracksLabel"], TrackVertexAssociation = trackingKeys["TVA"], TrackMinPtCuts = [500, 1000], - TrackSelector = jettrackselloose + TrackSelector = jettrackselloose, + DoPFlowMoments = 'PFlow' in jetdef.fullname() , ) return trackmoments def getTrackSumMomentsTool(jetdef, modspec): jettrackselloose = JetRecToolsConfig.getTrackSelTool(modspec) # retrieve the tracking keys to be used with modspec : - trackingKeys = trackcollectionmap[modspec] + trackingKeys = jetContextDic[modspec or jetdef.context] tracksummoments = CompFactory.JetTrackSumMomentsTool( "trksummoms", VertexContainer = trackingKeys["Vertices"], @@ -150,7 +151,7 @@ def getTrackSumMomentsTool(jetdef, modspec): def getOriginCorrVxTool(jetdef, modspec): origin_setpv = CompFactory.JetOriginCorrectionTool( "jetorigin_setpv", - VertexContainer = trackcollectionmap[modspec]["Vertices"], + VertexContainer = jetContextDic[modspec or jetdef.context]["Vertices"], OriginCorrectedName = "", OnlyAssignPV = True, ) diff --git a/Reconstruction/Jet/JetRecConfig/CMakeLists.txt b/Reconstruction/Jet/JetRecConfig/CMakeLists.txt index fb87cd5b0f731cb61d26bd1124c7e08c3fe97653..1498e3441a70e29eaf1e6e7c850dfb542779dcef 100644 --- a/Reconstruction/Jet/JetRecConfig/CMakeLists.txt +++ b/Reconstruction/Jet/JetRecConfig/CMakeLists.txt @@ -1,13 +1,28 @@ -# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration # Declare the package name: atlas_subdir( JetRecConfig ) # Install files from the package: atlas_install_python_modules( python/*.py POST_BUILD_CMD ${ATLAS_FLAKE8} ) -atlas_install_scripts( share/JetRecTestCfg.py test/*.sh ) +atlas_install_scripts( share/JetRecTestCfg.py test/*.sh test/*py) atlas_install_joboptions( share/*.py ) -atlas_add_test( JetRecCfgTest - SCRIPT test_JetRecCfg_build.sh + +atlas_add_test( JetDefUTest + SCRIPT test_JetDefinitions_properties.py + POST_EXEC_SCRIPT nopost.sh ) + +# argument '-n 0' prevent a full run and performs only the jet config +atlas_add_test( JetStandarSmallRConfig + SCRIPT test_StandardJets.py "-j smallR -n 0" + POST_EXEC_SCRIPT nopost.sh ) + +atlas_add_test( JetStandarLargeRConfig + SCRIPT test_StandardJets.py "-j largeR -n 0" + POST_EXEC_SCRIPT nopost.sh ) + +atlas_add_test( JetStandarCSSKConfig + SCRIPT test_StandardJets.py "-j cssk -n 0" POST_EXEC_SCRIPT nopost.sh ) + diff --git a/Reconstruction/Jet/JetRecConfig/README.md b/Reconstruction/Jet/JetRecConfig/README.md index d2a01ffe30ce4625c159e4efbbee2d2507afd255..9d5fe910f1ecfa1a084d923a7ad004bd40d0b396 100644 --- a/Reconstruction/Jet/JetRecConfig/README.md +++ b/Reconstruction/Jet/JetRecConfig/README.md @@ -12,18 +12,26 @@ documentation: https://twiki.cern.ch/twiki/bin/viewauth/AtlasComputing/AthenaJobConfigRun3 The backbone of this configuration framework is a set of classes that encode -what is meaningful in a jet definition or jet constituent, such that helper -code can interpret the definitions to generate all necessary tools/algs. -These are defined in `JetDefinition.py`, and currently comprise: -* `JetConstit`: A set of constituents used for jet finding, which may have - constituent modifiers applied. +what is meaningful in a jet definition or jet constituent. Helper +code will then interpret these definitions to generate all necessary tools/algs. + +The definition classes are defined in `JetDefinition.py`, and currently comprise: +* Classes defining the input to jet finding. `JetInputExternal` describes external containers : clusters, tracks, event density... `JetInputConstit` and `JetInputConstitSeq` describe what can be used as constituents of jets. * `JetDefinition`: A jet configuration, fundamentally the clustering algorithm, radius, and input constituent type. Can be extended with lists of jet - modifiers. Jet grooming is still to be added (might be a derived class?) -* `JetGhost`: A set of objects to be ghost-associated to the jets. Very - simplistic class at present. + modifiers. * `JetModifier`: A configuration of a tool that adds jet moments or otherwise manipulates the jet collection. +* There is also jet context dictionnary defined in + `StandardJetContext.py`. A jet context is simply a set of parameters + that we want to keep consistent across all components within a + JetDefinition. These parameters are mainly related to tracks (track + & vertex container names, trk-vtx association map,...). Each set of + parameters is kept together within a dict and each dict is stored + in the `jetContextDic`. For example `jetContextDic["default"]` is + the parameters for offline reco and `jetContextDic["ftf"]` can be + used in the trigger config. Each `JetDefinition` has a context + (which is "default" by default...). Once the constituent and definition have been defined, these are passed to the `JetRecConfig.JetRecCfg` function, which builds up all needed @@ -34,24 +42,21 @@ Athena job and potentially with other jet reconstruction sequences. As an intermediate step, the dictionary of dependencies for the jet reco job can be extracted without attempting to configure any Athena tools/algs. This -is done by passing the jet definition to `JetRecConfig.resolveDependencies`, +is done by passing the jet definition to `DependencyHelper.solveDependencies`, allowing quick checking of the elements that will be integrated to form the jet collection, including input collections, ghosts and modifiers. If working with standard objects, the user can to do something like ``` -from StandardJetDefs import AntiKt4LCTopo -lcjetcfg = JetRecConfig.JetRecCfg(jetseq,AntiKt4LCTopo) +from JetRecConfig.StandardSmallRJets import AntiKt4LCTopo +acc = JetRecConfig.JetRecCfg(AntiKt4LCTopo) ``` For minor variations, one can copy and modify the standard definitions: ``` -from StandardJetDefs import LCTopoOrigin, AntiKt4LCTopo -from copy import deepcopy -LCTopoCSSK = deepcopy(LCTopoOrigin) -LCTopoCSSK.modifiers += ["CS","SK"] -AntiKt4LCTopoCSSK = deepcopy(AntiKt4LCTopo) -AntiKt4LCTopoCSSK.inputdef = LCTopoCSSK -lccsskjetcfg = JetRecConfig.JetRecCfg(jetseq,AntiKt4LCTopoCSSK) +from JetRecConfig.StandardSmallRJets import AntiKt4LCTopo +from JetRecConfig.StandardJetConstits import stdConstitDic as cst +AntiKt4LCTopoCSSK = AntiKt4LCTopo.clone(inputdef = cst.LCTopoCSSK, modifiers=["Filter:13000"] ) + ``` The definitions can of course be built up from scratch by users who want full control. @@ -66,7 +71,7 @@ https://indico.cern.ch/event/697121/contributions/2859415/attachments/1585431/25 To provide a large variety of modifier tool configurations in an organised way, each modifier tool is configured via the `JetModifier` helper class. This in -turn delegates setup of the configuration to a helper function that specifies +turn can delegates setup of the configuration to a helper function (`createfn` argument) that specifies non-default tool properties, and may take into account the details of the jets. Every modifier keeps track of its prerequisites, which may be other modifiers, jet input objects (e.g. tracks) or ghosts (e.g. GhostTracks). @@ -82,19 +87,23 @@ some other mods be run first: jvt = JetModifier("JetVertexTaggerTool", "jvt", prereqs = [ "mod:JVF" ]) ``` -Finally, a helper function might be defined to set up the tool correctly, -possibly with input from a string encoding of the modifier setup: + +One can pass the properties need for the tool in the ctor of the modifier + +``` + ktdr = JetModifier("KtDeltaRTool", "ktdr", JetRadius = 0.4), +``` + +Instead of a simple value, the property can be a function. This is +useful when the property needs to depend on the jet definition to +which the modifier is attached. The function will be called with 2 +args : the jet definition and a specifier (defaulting to "", see below). + ``` -def getJetFilterTool(modspec): - from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator - ca = ComponentAccumulator() - threshold = int(modspec) - jetptfilter = JetRecConf.JetFilterTool("jetptfilter_{0}mev".format(threshold)) - jetptfilter.PtMin = threshold - ca.addPublicTool( jetptfilter ) - return jetptfilter, ca +def _jetname(jetdef,modspec): + return jetdef.fullname() -"Filter": JetModifier("JetFilterTool","jetptfilter",helperfn=getJetFilterTool), +Width = JetModifier("JetWidthTool", "width", JetContainer = _jetname), ``` A helper function can potentially be provided for the prereqs as well. @@ -107,11 +116,11 @@ AntiKt4LCTopo.modifiers = ["Calib:AnalysisLatest:mc","Sort","JVT"] which will first ensure that all tools are called that are needed for calibration, then sort the collection (a filter is also applied, controlled by other options), and finally compute JVT, again adding all modifiers required -by JVT. In the process, jet track selection and ghost association will be -added to the job. +by JVT. In the process, jet track selection and ghost association will +be automatically added to the job because they are necessary for JVT. The set of standard modifiers (for offline reconstruction) is concentrated -in JetRecConfig/python/StandardJetMods.py, which in turn accesses tool +in `JetRecConfig.StandardJetMods`, which in turn accesses tool configuration helpers that are defined in the packages containing the tool C++ implementations. This allows a local lookup of the specific default configurations, while making it easy to determine where the source code @@ -124,20 +133,10 @@ To run: cd $MYWORKDIR mkdir run cd run -python JetRecTestCfg.py -H # Get instructions +python test_StandardJets.py --help # Get instructions ``` You can dump printouts of the dependency dict and/or component accumulators for each jet collection or run a full job in serial or multithreaded modes. # TODO -* Set up configuration helpers for: - * Jet substructure moment tools -* Develop a generic configuration helper for grooming jets - * This should support both finding+grooming and standalone grooming based - on the input collection being pre-made. - * Some autoconfiguration could be useful here? However, missing data - dependencies should be covered by the AthenaMT scheduler. -* Some steering flags will eventually be needed to cope with different - situations. However, hopefully they can mostly just modify the top-level - configurations, without needing anything propagated into the helpers. diff --git a/Reconstruction/Jet/JetRecConfig/python/ConstModHelpers.py b/Reconstruction/Jet/JetRecConfig/python/ConstModHelpers.py deleted file mode 100644 index 0eb60c2f3dff43e922c3cace6fc50994fe115956..0000000000000000000000000000000000000000 --- a/Reconstruction/Jet/JetRecConfig/python/ConstModHelpers.py +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration - -""" - # -ConstModUtils: A module for configuring constituent modifiers # -Author: TJ Khoo # - # -""" - -import cppyy -try: - cppyy.load_library('libxAODBaseObjectTypeDict') -except Exception: - pass -from ROOT import xAODType -xAODType.ObjectType - -from AthenaCommon import Logging -constmodlog = Logging.logging.getLogger('ConstModHelpers') - -from AthenaConfiguration.ComponentFactory import CompFactory - - -from .StandardJetConstits import jetconstitdic, jetinputdefdic, jetconstitmoddic -from .JetDefinition import JetConstitSeq, JetInputDef, JetConstitSource - -class _dummyJetDef: - def __init__(self): - self._prereqDic = {} - self._prereqOrder = [] - - -def aliasToInputDef(alias, parentjetdef=None): - """convert a string alias to a full config object, either a JetConstitSeq or a JetInputDef according to the alias. - - This also recursively translate all aliases which are dependencies of this aliases. All these dependencies are - collected into the 'parentjetdef' (JetDefinition ). - """ - parentjetdef = parentjetdef or _dummyJetDef() - # ---------------- - # support the cases where alias is not a string but directly a config object - if isinstance(alias, JetConstitSource): - instantiateJetConstitAliases(alias, parentjetdef) - return alias - if isinstance(alias, JetInputDef): - instantiateJetInputDefAliases(alias, parentjetdef) - return alias - - # else assume it's a string - - if alias in jetinputdefdic: - inputdef = jetinputdefdic[alias].clone() - instantiateJetInputDefAliases(inputdef, parentjetdef) - return inputdef - else: - constitseq = jetconstitdic[alias].clone( ) - instantiateJetConstitAliases(constitseq, parentjetdef) - return constitseq - -def instantiateJetConstitAliases(constitseq, parentjetdef): - """Reccursively translate all aliases appearing in the prereqs of constitseq into proper config objects. - All are collected into the parentjetdef for which this JetConstitSeq is being configured. - Then instantiates all aliases for JetConstitModifier - """ - - # we technically need a JetInputDef for constitseq.inputname : conveniently declare it if not existing : - jetinputdefdic.setdefault( constitseq.inputname, JetInputDef( constitseq.inputname, constitseq.basetype) ) - # we re-use the instantiateJetInputDefAliases to instantiate the prereqs - instantiateJetInputDefAliases( constitseq, parentjetdef,pre_prereqs=['input:'+constitseq.inputname]) - - # JetConstitSource don't have modifiers, we can return immediately - if not hasattr( constitseq, "modifiers") : return - - # instantiate the JetConstitModifier (those don't have dependencies) - for mod in constitseq.modifiers: - modInstance = jetconstitmoddic[ mod ].clone() - constitseq._instanceMap[mod] = modInstance - -def instantiateJetInputDefAliases(jetinputdef, parentjetdef, pre_prereqs=[]): - """Reccursively translate all aliases appearing in the prereqs of jetinputdef into proper config objects. - All are collected into the parentjetdef for which this JetConstitSeq is being configured. - """ - prereqs = jetinputdef.prereqs(parentjetdef) if callable(jetinputdef.prereqs) else jetinputdef.prereqs - prereqs = pre_prereqs + prereqs - for prereq in prereqs : - reqInstance = parentjetdef._prereqDic.get( prereq, None) - if reqInstance is None: - _ , inputkey = prereq.split(':') # always in the form "input:XXX" - reqInstance = aliasToInputDef(inputkey, parentjetdef) - parentjetdef._prereqDic[prereq] = reqInstance - parentjetdef._prereqOrder.append(prereq) - - - - - - -def getConstitModAlg(constit, monTool=None): - """returns a configured JetConstituentModSequence or None if constit.modifiers == [] """ - - # JetConstitSource do not need any JetConstituentModSequence - # (they are only needed to trigger the building of the source container and a PJ algo) - if not isinstance(constit, JetConstitSeq): return - - - inputtype = constit.basetype - - sequence = constit.modifiers - - modlist = [] - - #if modlist == []: return - if constit.inputname == constit.containername: return - - for step in sequence: - modInstance = constit._instanceMap[ step ] - if not modInstance.tooltype: continue - - toolclass = getattr( CompFactory, modInstance.tooltype) - - # update the properties : if some of them are function, just replace by calling this func : - for k,v in modInstance.properties.items(): - if callable(v) : - modInstance.properties[k ] = v( constit ) - - tool = toolclass(modInstance.name,**modInstance.properties) - - if inputtype == xAODType.ParticleFlow and modInstance.tooltype not in ["CorrectPFOTool","ChargedHadronSubtractionTool"]: - tool.IgnoreChargedPFO=True - tool.ApplyToChargedPFO=False - tool.InputType = inputtype - modlist.append(tool) - - sequenceshort = "".join(sequence) - seqname = "ConstitMod{0}_{1}".format(sequenceshort,constit.name) - inputcontainer = str(constit.inputname) - outputcontainer = str(constit.containername) - if inputtype==xAODType.ParticleFlow: - # Tweak PF names because ConstModSequence needs to work with - # up to 4 containers - def chopPFO(thestring): - pfostr = "ParticleFlowObjects" - if thestring.endswith(pfostr): - return thestring[:-len(pfostr)] - return thestring - inputcontainer = chopPFO(inputcontainer) - outputcontainer = chopPFO(outputcontainer) - - modseq = CompFactory.JetConstituentModSequence(seqname, - InputType=inputtype, - OutputContainer = outputcontainer, - InputContainer= inputcontainer, - Modifiers = modlist, - ) - if monTool: - modseq.MonTool = monTool - - constitmodalg = CompFactory.JetAlgorithm("jetalg_{0}".format(modseq.getName())) - constitmodalg.Tools = [modseq] - - return constitmodalg - - -def constitModWithAlternateTrk(mod, trkopt): - """Generates a new JetConstitModifier cloned from mod and stores it in the standard jetconstitmoddic. - The cloned JetConstitModifier has all its track related properties changed according to trackcollectionmap[trkopt] - This is mainly useful to configure trigger algs. - """ - from JetRecTools.JetRecToolsConfig import trackcollectionmap - from JetRecConfig.StandardJetConstits import jetconstitmoddic - newmod = mod+trkopt - - if newmod in jetconstitmoddic: - return jetconstitmoddic[newmod] - - newmodInstance = jetconstitmoddic[mod].clone() - - trackproperties = trackcollectionmap[trkopt] - - if mod == "CorrectPFO": - newmodInstance.properties.update(VertexContainerKey=trackproperties["Vertices"]) - elif mod=="CHS": - newmodInstance.properties.update(VertexContainerKey=trackproperties["Vertices"], - TrackVertexAssociation=trackproperties["TVA"]) - jetconstitmoddic[newmod] = newmodInstance - - return newmodInstance - - diff --git a/Reconstruction/Jet/JetRecConfig/python/DependencyHelper.py b/Reconstruction/Jet/JetRecConfig/python/DependencyHelper.py new file mode 100644 index 0000000000000000000000000000000000000000..388fe6c66b9360b3bab190bdb6207c43d0c93a8a --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/python/DependencyHelper.py @@ -0,0 +1,194 @@ +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration +""" + +Functions to solve dependencies of Jet reco components. + +Jet reco components are objects described by classes in JetDefinition.py (including JetDefinition, JetModifier,...) +and the dependencies of each instance are set as (list of) string aliases refering to other components. + +The functions here are scaning reccursively all the aliases, building the corresponging component objects and +collecting them in a JetDefinition. + +""" +from .JetDefinition import JetInputExternal, JetInputConstit, JetModifier + +class _dummyJetDef: + def __init__(self): + self._prereqDic = {} + self._prereqOrder = [] + + +def solveDependencies( jetdef0 ): + """ Retrieve reccursively all dependencies described by str aliases (from modifiers, ghosts, etc..) within jetdef0. + The aliases are converted in to proper config objects (like JetModifier, JetInputConstit,...) and are collected into + a cloned version of jetdef0. + The cloned version is returned and contains all the necessary information to build the actual C++ tools and algs. + (in particular, the _prereqDic and _prereqOrder internal members of the clone are filled). + """ + + jetdef = jetdef0.clone() + # # start with the inputdef, cloning it so we're not altering a private copy + # jetdef.inputdef = jetdef.inputdef.clone() + # + solveConstitDependencies(jetdef.inputdef, jetdef, inplace=True) + + jetdef._prereqDic['input:'+jetdef.inputdef.name] = jetdef.inputdef + jetdef._prereqOrder.append('input:'+jetdef.inputdef.name) + + for g in jetdef.extrainputs: + gInstance = aliasToInputDef( g , jetdef) + jetdef._prereqDic['input:'+g] = gInstance + jetdef._prereqOrder.append('input:'+g) + + for g in jetdef.ghostdefs: + gInstance = aliasToInputDef( g , jetdef) + jetdef._prereqDic['input:'+g] = gInstance + jetdef._prereqOrder.append('input:'+g) + jetdef._prereqDic['ghost:'+g] = gInstance + jetdef._prereqOrder.append('ghost:'+g) + + for mod in jetdef.modifiers: + modInstance = aliasToModDef(mod, jetdef) + jetdef._prereqDic['mod:'+mod] = modInstance + jetdef._prereqOrder.append('mod:'+mod) + + # Deduplicate the prereq (with python > 3.6 dict is ordered so the trick is guaranteed to work) : + jetdef._prereqOrder[:] = list(dict.fromkeys(jetdef._prereqOrder) ) + + return jetdef + + +def solveGroomingDependencies( groomdef0 ): + """Retrieve all dependencies described by str aliases in groomdef0.modifiers. + + The aliases are converted in to proper config objects (like JetModifier, JetInputConstit,...) and are collected into + a cloned version of groomdef0. + The cloned version is returned and contains all the necessary information to build the actual C++ tools and algs. + (in particular, the _prereqDic and _prereqOrder internal members of the clone are filled). + """ + + groomdef = groomdef0.clone() + for mod in groomdef.modifiers: + modInstance = aliasToModDef(mod, groomdef) + groomdef._prereqDic['mod:'+mod] = modInstance + groomdef._prereqOrder.append('mod:'+mod) + return groomdef + + +def aliasToInputDef(alias, parentjetdef=None): + """convert a string alias to a full config object, either a JetInputConstitSeq or a JetInputExternal according to the alias. + + This also recursively translate all aliases which are dependencies of this alias. All these dependencies are + collected into the 'parentjetdef' (JetDefinition ). + """ + parentjetdef = parentjetdef or _dummyJetDef() + # ---------------- + # support the cases where alias is not a string but directly a config object + if isinstance(alias, JetInputConstit): + solveConstitDependencies(alias, parentjetdef, inplace=True) + return alias + if isinstance(alias, JetInputExternal): + solveInputExternalDependencies(alias, parentjetdef, inplace=True) + return alias + + # else assume it's a string + + from .StandardJetConstits import stdConstitDic, stdInputExtDic + if alias in stdInputExtDic: + # then it must be a JetInputExternal instance : + return solveInputExternalDependencies(stdInputExtDic[alias], parentjetdef) + else: + # then it must be a JetInputConstit or JetInputConstitSeq instance : + return solveConstitDependencies(stdConstitDic[alias], parentjetdef) + +def solveConstitDependencies(constitseq, parentjetdef, inplace=False): + """Reccursively translate all aliases appearing in the prereqs of constitseq into proper config objects. + All are collected into the parentjetdef for which this JetInputConstitSeq is being configured. + Then instantiates all aliases for JetConstitModifier + """ + + if not inplace: + constitseq = constitseq.clone() + + from .StandardJetConstits import stdInputExtDic, stdContitModifDic + # we technically need a JetInputExternal for constitseq.inputname : conveniently declare it if not existing : + stdInputExtDic.setdefault( constitseq.inputname, JetInputExternal( constitseq.inputname, constitseq.basetype) ) + # we re-use the solveInputExternalDependencies to instantiate the prereqs + constitseq.prereqs += ['input:'+constitseq.inputname] # make sure the external input to these constituents are taken into account. + solveInputExternalDependencies( constitseq, parentjetdef) + + # JetInputConstit don't have modifiers, we can return immediately + if not hasattr( constitseq, "modifiers") : return constitseq + + # instantiate the JetConstitModifier (those don't have dependencies) + for mod in constitseq.modifiers: + modInstance = stdContitModifDic[ mod ].clone() + constitseq._instanceMap[mod] = modInstance + + return constitseq + +def solveInputExternalDependencies(jetinputext, parentjetdef, inplace=False): + """Reccursively translate all aliases appearing in the prereqs of jetinputext into proper config objects. + All are collected into the parentjetdef for which this JetInputConstitSeq is being configured. + """ + if not inplace: + jetinputext = jetinputext.clone() + if callable(jetinputext.prereqs): + jetinputext.prereqs = jetinputext.prereqs(parentjetdef) + for prereq in jetinputext.prereqs : + reqInstance = parentjetdef._prereqDic.get( prereq, None) + if reqInstance is None: + _ , inputkey = prereq.split(':') # always in the form "input:XXX" + reqInstance = aliasToInputDef(inputkey, parentjetdef) + parentjetdef._prereqDic[prereq] = reqInstance + parentjetdef._prereqOrder.append(prereq) + + return jetinputext + + + + +def prereqToDef(prereq, parentjetdef): + """translate a prereq string in the form 'type:alias' into a known config object. + """ + reqtype, reqkey = prereq.split(':',1) + if reqtype=='mod': + reqInstance = aliasToModDef(reqkey, parentjetdef) + else: + reqInstance = aliasToInputDef(reqkey, parentjetdef) + return reqInstance + +def aliasToModDef(alias, parentjetdef ): + """return a JetModifier config object corresponding to alias, also recursively translating all aliases in the dependencies of this JetModifier.""" + if isinstance(alias, JetModifier): + return alias + # else assume it's a string + + # split it, to extract the optional specifiers + modL = alias.split(":") + modkey = modL[0] + modspec = ':'.join(modL[1:]) + + # retrieve an instance from the known modifiers in StandardJetMods : + from .StandardJetMods import stdJetModifiers + moddef = stdJetModifiers[modkey].clone( modspec = modspec) + + + if callable(moddef.prereqs): + moddef.prereqs = moddef.prereqs( modspec, parentjetdef ) + + for prereq in moddef.prereqs: + reqInstance = parentjetdef._prereqDic.get( prereq, None) + if reqInstance is None: + reqInstance = prereqToDef(prereq, parentjetdef) + + if prereq.startswith('ghost:'): + # then it is also an input : register this requirement also as an input + prereqN = prereq.split(':')[1] + parentjetdef._prereqOrder.append('input:'+prereqN) + parentjetdef._prereqDic['input:'+prereqN] = reqInstance # the input config instance is identical for input and ghost (only the PseudoJet will differ) + + parentjetdef._prereqOrder.append(prereq) + parentjetdef._prereqDic[prereq] = reqInstance + + return moddef diff --git a/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py b/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py index 416206121b712555135284b503559cf189be3bc6..91aadbc184009ea32eb81c2d5becf48d76289807 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py @@ -8,20 +8,20 @@ related objects for configuring jet reconstruction Various classes encode definitions of different types of components used in Jet Reco. They are : - - JetInputDef : describes how to build a source container, typically external to the jet domain. This includes input to jet finding (ex: CaloCluster, Track) but also other sources like EventDensity... + - JetInputExternal : describes how to build a source container, typically external to the jet domain. This includes input to jet finding (ex: CaloCluster, Track) but also other sources like EventDensity... - - JetConstitSource : describes specifically a input constituents source (an input to a PseudoJetAlgorithm), thus referring to a JetInputDef. - - JetConstitSeq : a subclass of JetConstitSource, describing how the constituents are modified by a JetConstituentModSequence (ex: PU or Origin correction). + - JetInputConstit : describes specifically an input constituents container (an input to a PseudoJetAlgorithm), thus referring to a JetInputExternal as the primary source. + - JetInputConstitSeq : a subclass of JetInputConstit, describing how a constituents container is build from a JetConstituentModSequence (ex: PU or Origin correction). - JetConstitModifier : describes a constituent modifier tool to be used in a JetConstituentModSequence - - JetDefinition : describes a full jet reco sequence. Uses a JetConstitSource and a list of JetModifier + - JetDefinition : describes a full jet reco sequence. Uses a JetInputConstit and a list of JetModifier - JetModifier : describes a JetModifier c++ tool. Author: TJ Khoo, P-A Delsart """ -__all__ = [ "JetDefinition","xAODType", "JetModifier", "JetConstitModifier" , "JetConstitSeq", "JetInputDef"] +__all__ = [ "JetDefinition","xAODType", "JetModifier", "JetConstitModifier" , "JetInputConstitSeq", "JetInputExternal"] from AthenaCommon import Logging jetlog = Logging.logging.getLogger('JetDefinition') @@ -43,12 +43,15 @@ def formatRvalue(parameter): else: return "{0:.1g}".format(10*parameter).replace('.','') + # Could also split off a VR name builder -def buildJetAlgName(finder, mainParam, variableRMassScale=None, variableRMinRadius=None): # variableRMassScale (Rho) in MeV +def buildJetAlgName(finder, mainParam, + variableRMassScale=None, variableRMinRadius=None): + """variableRMassScale (Rho) in MeV """ if ( variableRMassScale and variableRMinRadius ): rmaxstr = formatRvalue(mainParam) rminstr = formatRvalue(variableRMinRadius) - return finder + "VR" + str(int(variableRMassScale/1000)) + "Rmax" + rmaxstr + "Rmin" + rminstr + return f"{finder}VR{str(int(variableRMassScale/1000))}Rmax{rmaxstr}Rmin{rminstr}" return finder + formatRvalue(mainParam) @@ -72,6 +75,7 @@ class JetDefinition(object): standardRecoMode = False, # prefix = "", # allows to tune the full JetContainer name suffix = "", # allows to tune the full JetContainer name + context = "default", # describe a context for which this definition will be used. See StandardJetContext lock = False, # lock the properties of this instance to avoid accidental overwrite after __init__ ): @@ -87,6 +91,7 @@ class JetDefinition(object): self._inputdef = inputdef self._prefix = prefix self._suffix = suffix + self._context = context self._defineName() self.ptmin = ptmin # The pt down to which FastJet is run @@ -172,6 +177,9 @@ class JetDefinition(object): def VRMinRadius(self): pass @make_lproperty def VRMassScale(self): pass + + @make_lproperty + def context(self): pass def fullname(self): @@ -188,7 +196,7 @@ class JetDefinition(object): # Define a string conversion for printing def __str__(self): - return "JetDefinition({0}, ptmin: {1} MeV)".format(self.basename,self.ptmin) + return f"JetDefinition({self.fullname()})" # Need to override __repr__ for printing in lists etc __repr__ = __str__ @@ -298,12 +306,12 @@ class JetModifier(object): @clonable @onlyAttributesAreProperties -class JetInputDef(object): - """This describes an input source to jet finding, typically a container build outside the jet domain. - Sources can be container of constituents or ghost constituents (ex: clusters, tracks,...) but also - other object needed by JetModifier (ex: EventDensity or track-vertex association map). +class JetInputExternal(object): + """This class allows to declare primary data sources to jet finding which are typically outside of jet domain. + Such sources can be container of particles (ex: clusters, selection of tracks,...) but also + other object needed by some JetModifier (ex: EventDensity or track-vertex association map). - Currently this class is mainly here to hold a helper (algoBuilder) function in charge of creating an algorithm to build the source. + The class is mainly here to hold a helper function (algoBuilder) in charge of configuring the proper algorithm to build the source. If this function is None, then we expect the container pre-exists in the evt store. Arguments to the constructor : @@ -318,7 +326,7 @@ class JetInputDef(object): - filterfn : a function taking a CondFlags as argument and deciding if this JetModifier is compatible with the conditions (same as JetModifier.filterfn ) The function must return a tuple : (bool, "reason of failure") - - prereqs : a list of prerequisites (str) for this input definition. + - prereqs : a list of prerequisites (str) for this input definition. If any, these str must match the name of other existing JetInputExternal instances. """ def __init__(self, name, objtype, algoBuilder=None, specs=None, containername=None, filterfn= _condAlwaysPass, prereqs=[]): self.name = name @@ -338,9 +346,6 @@ class JetInputDef(object): self.filterfn = filterfn self.prereqs = prereqs - # # make outputname an alias of name, so JetInputDef shares an interface with JetConstitSeq. - # # we set the hidden attribute because the real one is unsettable (see below) - # self._outputname = name # Set outputname as an alias to name. @make_lproperty @@ -356,39 +361,15 @@ class JetInputDef(object): @make_lproperty def prereqs(self):pass - # make outputname an alias of name so JetInputDef shares an interface with JetConstitSeq. + # make outputname an alias of name so JetInputExternal shares an interface with JetInputConstitSeq. outputname = make_alias("name") - -######################################################################## - -@clonable -@onlyAttributesAreProperties -class JetConstitModifier(object): - """Configuration for a constituent modifier tool to be used in a JetConstituentModSequence. - See StandardJetConstits.py for usage of this class. + # Define a string conversion for printing + def __str__(self): + return f"JetInputExternal({self.name},type={str(self.basetype)})" - the properties argument in __init__ defines directly the properties of the final tool : - if the tool has the property "PtMin" then passing 'dict(PtMin=10*GeV)' will result in 'tool.PtMin = 10*GeV' - IMPORTANT : If a property is itself an other tool, we can pass a function returning the tool like in 'dict(TheSubTool = mySubToolFunc)' - The function will be called only when appropriate in the form 'tool.TheSubTool = mySubToolFunc(constitseq)' - """ - def __init__(self, - name, - tooltype, - properties={}): - self.name = name - self.tooltype = tooltype - self.properties = properties - - @make_lproperty - def name(self): pass - @make_lproperty - def tooltype(self): pass - @make_lproperty - def properties(self): pass - +######################################################################## from enum import IntEnum, auto @@ -456,42 +437,47 @@ class JetInputType(IntEnum): @clonable @onlyAttributesAreProperties -class JetConstitSource(object): - """Configuration for simplest constituents (or ghost constituents) to jets. - This describes what can be the input to a PseudoJetAlgorithm. - The containername attribute must correspond to an existing JetInputDef so the system knows how to build this +class JetInputConstit(object): + """Configuration for simplest constituents (or ghost constituents) to jets. + This describes what can be the input to a PseudoJetAlgorithm. + The containername attribute must correspond to an existing JetInputExternal so the system knows how to build this source container (if necessary). """ - def __init__(self, - name, # identifies this constit source, must be unique. - objtype, # The type of xAOD object from which to build the jets - containername, # The key of the source container in the event store. - prereqs = [], # will contain references to JetInputDef - label = None, # used to describe a category for these constits. if None, will default to name - jetinputtype=None, # The JetInputType category. Can be passed as a string. - # if None, set according to objtype. - filterfn=_condAlwaysPass, - lock = False, # lock all properties of this instance - ): - self.name = name + def __init__( + self, + name, # identifies this constit source, must be unique. + objtype, # The type of xAOD object from which to build the jets + containername, # The key of the source container in the event store. + prereqs=[], # will contain references to JetInputExternal + label=None, # used to describe a category for these constits. if None, will default to name + jetinputtype=None, # The JetInputType category. Can be passed as a string. + # if None, set according to objtype. + filterfn=_condAlwaysPass, + lock=False, # lock all properties of this instance + ): + + self.name = name self.containername = containername - #self.inputname = containername # will act as an alias to containername (and immutable since it's not a property) self.prereqs = prereqs self.label = label or name - + self.basetype = objtype - self.filterfn = filterfn + self.filterfn = filterfn - jetinputtype = jetinputtype or JetInputType.fromxAODType( objtype ) - if isinstance(jetinputtype, str): jetinputtype = JetInputType[jetinputtype] + jetinputtype = jetinputtype or JetInputType.fromxAODType(objtype) + if isinstance(jetinputtype, str): + jetinputtype = JetInputType[jetinputtype] self.jetinputtype = jetinputtype - + self._locked = lock @make_lproperty def basetype(self): pass + @make_lproperty + def name(self): pass + @make_lproperty def containername(self): pass @@ -504,12 +490,18 @@ class JetConstitSource(object): @make_lproperty def jetinputtype(self): pass - # make an alias on containername so JetConstitSource and JetConstitSeq share an interface + # make an alias on containername so JetInputConstit and JetInputConstitSeq share an interface inputname = make_alias("containername") + # Define a string conversion for printing + def __str__(self): + return f"JetInputConstit({self.name},type={str(self.basetype)})" + + + @clonable @onlyAttributesAreProperties -class JetConstitSeq(JetConstitSource): +class JetInputConstitSeq(JetInputConstit): """Configuration for JetConstituentModSequence. Describes the constituents which need to be build with a JetConstituentModSequence. Uses a list of aliases to JetConstitModifier to describe the modif steps. @@ -520,14 +512,14 @@ class JetConstitSeq(JetConstitSource): modifiers=[], # Modifications to be applied to constituents prior to jet finding inputname=None, # input collection which will be transformed into the source constituents outputname=None, # output collection, will be set to self.containername - prereqs = [], # will contain references to JetInputDef + prereqs = [], # will contain references to JetInputExternal label = None, jetinputtype=None, filterfn=_condAlwaysPass, lock = False, # lock all properties of this instance ): - JetConstitSource.__init__(self,name, objtype, outputname, prereqs=prereqs, jetinputtype=jetinputtype, filterfn=filterfn,label=label,lock=False, finalinit=False, ) + JetInputConstit.__init__(self,name, objtype, outputname, prereqs=prereqs, jetinputtype=jetinputtype, filterfn=filterfn,label=label,lock=False, finalinit=False, ) self.inputname = inputname or name self.modifiers = modifiers @@ -555,10 +547,40 @@ class JetConstitSeq(JetConstitSource): return (not self.__eq__(rhs)) - # Define a string conversion for printing def __str__(self): - return "JetConstitSeq({0}: {1})".format(self.name,self.inputname) + return f"JetInputConstitSeq({self.name}, {self.inputname} , {self.outputname})" # Need to override __repr__ for printing in lists etc __repr__ = __str__ + + +@clonable +@onlyAttributesAreProperties +class JetConstitModifier(object): + """Configuration for a constituent modifier tool to be used in a JetConstituentModSequence. + See StandardJetConstits.py for usage of this class. + + the properties argument in __init__ defines directly the properties of the final tool : + if the tool has the property "PtMin" then passing 'dict(PtMin=10*GeV)' will result in 'tool.PtMin = 10*GeV' + IMPORTANT : If a property is itself an other tool, we can pass a function returning the tool like in 'dict(TheSubTool = mySubToolFunc)' + The function will be called only when appropriate in the form 'tool.TheSubTool = mySubToolFunc(constitseq)' + """ + def __init__(self, + name, + tooltype, + properties={}): + self.name = name + self.tooltype = tooltype + self.properties = properties + + @make_lproperty + def name(self): pass + @make_lproperty + def tooltype(self): pass + @make_lproperty + def properties(self): pass + + + + diff --git a/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py deleted file mode 100644 index 389602decaff09cfca6a4106043594b7243d9d77..0000000000000000000000000000000000000000 --- a/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration -""" -JetGroomConfig: A helper module for configuring jet grooming -Author: TJ Khoo, P-A Delsart -""" - -######################################################################## - -from AthenaCommon import Logging -jetlog = Logging.logging.getLogger('JetGroomConfig') - -from AthenaConfiguration.ComponentFactory import CompFactory - -import JetRecConfig.JetModConfig as modH -from JetRecConfig.JetRecConfig import buildJetModifierList, addJetClusteringToComponent, JetRecCfg - - - -def instantiateGroomingAliases( groomdef ): - """ Instantiate all the modifier aliases contained in this groomdef. - At the same time fills the internal _prereqDic and _prereqOrder containers. - - This functions - * assumes jetdef is not 'locked' - * implies calls to recursives function constH.aliasToInputDef and modH.aliasToModDef - """ - - for mod in groomdef.modifiers: - modInstance = modH.aliasToModDef(mod, groomdef) - groomdef._prereqDic['mod:'+mod] = modInstance - groomdef._prereqOrder.append('mod:'+mod) - - -######################################################################## -# -def getJetGroomAlg(groomdef,monTool=None): - """Returns a configured JetRecAlg set-up to perform the grooming defined by 'groomdef' - ('monTool' is a temporary placeholder, it is expected to be used in the trigger in the future) - """ - jetlog.debug("Configuring grooming alg \"jetalg_{0}\"".format(groomdef.fullname())) - - - # the grooming tool (a IJetProvider instance) - groomClass = CompFactory.getComp(groomdef.tooltype) - groomer = groomClass(groomdef.groomalg, - UngroomedJets = groomdef.ungroomeddef.fullname(), - ParentPseudoJets = groomdef.ungroomeddef._internalAtt['finalPJContainer'], - **groomdef.properties) - # get JetModifier list - mods = buildJetModifierList(groomdef) - - # put everything together in a JetRecAlg - jetname = groomdef.fullname() - jra = CompFactory.JetRecAlg( - "jetrecalg_"+jetname, - Provider = groomer, - Modifiers = mods, - OutputContainer = jetname, - MonTool = monTool) - - - from JetRecConfig.JetRecConfig import autoconfigureModifiers - autoconfigureModifiers(jra.Modifiers, jetname) - - return jra - -def getJetGroomAlg_jetAlg(groomdef,pjsin,monTool=None): - """Returns a configured grooming algs based on the old JetAlgorithm - - We keep this function for compatibility with trigger config. When the trigger switches to JetRecAlg, this can be removed. - """ - jetname = groomdef.fullname() - jetlog.debug("Configuring JetAlgorithm \"jetalg_{0}\"".format(jetname)) - - from . import JetRecConfig - builder = JetRecConfig.getJetBuilder() - - groomClass = CompFactory.getComp(groomdef.tooltype) - groomer = groomClass(groomdef.groomalg, - JetBuilder = builder, - **groomdef.properties) - - - - mods = buildJetModifierList(groomdef) - - rectool = CompFactory.JetRecTool(jetname, - JetGroomer=groomer, - InputContainer=groomdef.ungroomeddef.fullname(), - OutputContainer=jetname, - JetPseudojetRetriever=CompFactory.JetPseudojetRetriever("jpjretriever"), - JetModifiers=mods) - - if monTool: rectool.MonTool = monTool - - jetalg = CompFactory.JetAlgorithm("jetalg_"+jetname) - jetalg.Tools = [rectool] - return jetalg - -def addGroomToComponent(components,groomdef, configFlags, ): - """Instantiate and schedule all the algorithms needed to run the grooming alg 'groomdef' and - add them in the ComponentAccumulator 'components' - - This function is meant to be called from the top-level JetRecConfig.JetRecCfg - (groomdef is expected to be non locked and will be modified). - """ - sequenceName = groomdef.fullname() - - # Translate modifier aliases into JetModifier config instances. - # ( This also detects input dependencies, see below) - instantiateGroomingAliases(groomdef) - - # Transfer the input & ghost dependencies onto the parent jet alg, - # so they are handled when instatiating the parent jet algs - groomdef.ungroomeddef.ghostdefs += [ g.split(':')[1] for g in groomdef._prereqOrder if g.startswith('ghost:')] - groomdef.ungroomeddef.extrainputs += [ g.split(':')[1] for g in groomdef._prereqOrder if g.startswith('input:')] - - jetlog.info("Scheduling parent alg {} for {} ".format(groomdef.ungroomeddef.fullname(), groomdef.fullname())) - # now instantiate the parent jet alg. - # (we always want it even if the parent jets are already in the input file because - # we need to rebuild the pseudoJet) - addJetClusteringToComponent(components, groomdef.ungroomeddef, configFlags, sequenceName) - - groomalg = getJetGroomAlg(groomdef) - - components.addEventAlgo(groomalg, sequenceName) - - jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(groomdef.fullname())) - - return components - - -if __name__=="__main__": - - # Setting needed for the ComponentAccumulator to do its thing - from AthenaCommon.Configurable import Configurable - Configurable.configurableRun3Behavior=True - - # Config flags steer the job at various levels - from AthenaConfiguration.AllConfigFlags import ConfigFlags - ConfigFlags.Input.Files = ["/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/ASG/mc16_13TeV.410501.PowhegPythia8EvtGen_A14_ttbar_hdamp258p75_nonallhad.merge.AOD.e5458_s3126_r9364_r9315/AOD.11182705._000001.pool.root.1"] - ConfigFlags.Concurrency.NumThreads = 1 - ConfigFlags.Concurrency.NumConcurrentEvents = 1 - ConfigFlags.lock() - - # Get a ComponentAccumulator setting up the fundamental Athena job - from AthenaConfiguration.MainServicesConfig import MainServicesCfg - cfg=MainServicesCfg(ConfigFlags) - - # Add the components for reading in pool files - from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg - cfg.merge(PoolReadCfg(ConfigFlags)) - - # Add the components from our jet reconstruction job - from StandardJetDefs import AntiKt10LCTopo - from JetGrooming import JetTrimming - AntiKt10LCTopo.ptminfilter = 100e3 - AntiKt10LCTopoTrimmedPtFrac5SmallR20 = JetTrimming(AntiKt10LCTopo,"AntiKt10LCTopoJets",smallR=0.2,ptfrac=0.05) - cfg.merge(JetRecCfg(AntiKt10LCTopoTrimmedPtFrac5SmallR20,ConfigFlags)) - - cfg.printConfig(withDetails=False,summariseProps=True) - - cfg.run(maxEvents=10) diff --git a/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py b/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py index 1eb68ddab5b9b2c28eadd78c93ef75088473fa5b..3c10f99eccd04a78dbf6a1f1fb980cdcb814c996 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py @@ -26,6 +26,7 @@ class GroomingDefinition(object): modifiers=[], # JetModifiers to run after grooming suffix = '', # allows to tune the full JetContainer name lock=False, # lock the properties of this instance to avoid accidental overwrite after __init__ + context = None, # describe a context for which this definition will be used. See StandardJetContext **properties # any other argument is expected a grooming tool property ): @@ -34,6 +35,8 @@ class GroomingDefinition(object): if lock: properties = ldict(properties) # ldict to freeze the properties self.properties = properties + context = context if context is not None else ungroomeddef.context + self._context = context self.suffix = suffix self._defineName() @@ -72,6 +75,9 @@ class GroomingDefinition(object): def modifiers(self): pass @make_lproperty def suffix(self): pass + + @make_lproperty + def context(self): pass # To be overriden in derived classes def groomSpecAsStr(self): diff --git a/Reconstruction/Jet/JetRecConfig/python/JetInputConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetInputConfig.py index 204778ba38947af3ac0c3d9706f2a5969b3624e7..3af47d95e44366d0ca6f375d6a1ce468dc732fe1 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetInputConfig.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetInputConfig.py @@ -16,31 +16,21 @@ def _buildJetAlgForInput(suffix, tools ): ) return jetalg -def buildJetSelectedTracks( parentjetdef, inputspec ): - from JetRecTools import JetRecToolsConfig - # Jet track selection - t = JetRecToolsConfig.getTrackSelTool(doWriteTracks=True, - cutLevel="NoCut", minPt=500) - return _buildJetAlgForInput("JetSelectedTrack", - tools = [ t ] - ) - def buildJetTrackUsedInFitDeco( parentjetdef, inputspec ): from JetRecTools import JetRecToolsConfig # Jet track used-in-fit decoration return _buildJetAlgForInput("JetUsedInFitDeco", - tools = [ JetRecToolsConfig.getTrackUsedInFitTool() ] + tools = [ JetRecToolsConfig.getTrackUsedInFitTool(parentjetdef.context) ] ) def buildJetTrackVertexAssoc( parentjetdef, inputspec ): from JetRecTools import JetRecToolsConfig # Jet track TTVA return _buildJetAlgForInput("JetTVA", - tools = [ JetRecToolsConfig.getTrackVertexAssocTool() ] + tools = [ JetRecToolsConfig.getTrackVertexAssocTool(parentjetdef.context) ] ) - def buildJetInputTruth(parentjetdef, truthmod): truthmod = truthmod or "" diff --git a/Reconstruction/Jet/JetRecConfig/python/JetModConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetModConfig.py deleted file mode 100644 index f1c39db7cf1d1b96e0332146d6eb742d07e1d519..0000000000000000000000000000000000000000 --- a/Reconstruction/Jet/JetRecConfig/python/JetModConfig.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration - -""" - # -JetModConfig: A helper module for configuring jet modifier tools # -Author: TJ Khoo # - # -""" -from AthenaCommon import Logging -from .JetDefinition import JetModifier -import JetRecConfig.ConstModHelpers as constHelper -modlog = Logging.logging.getLogger('JetModConfig') - -def getModifier(jetdef, moddef, modspec): - """Translate JetModifier into a concrete tool""" - modlog.verbose("Retrieving modifier {0}".format(str(moddef))) - - # Get the modifier tool - try: - modtool = moddef.createfn(jetdef, modspec) - except Exception as e: - modlog.error( "Unhandled modifier specification {0} for mod {1} acting on jet def {2}!".format(modspec,moddef,jetdef.basename) ) - modlog.error( "Received exception \"{0}\"".format(e) ) - modlog.error( "Helper function is \"{0}\"".format(moddef.createfn) ) - raise ValueError( "JetModConfig unable to handle mod {0} with spec \"{1}\"".format(moddef,modspec) ) - - - # now we overwrite the default properties of the tool, by those - # set in the moddef : - for k,v in moddef.properties.items(): - if callable(v) : - # The value we got is a function : we call it to get the actual value we want to set on the tool - v = v(jetdef, modspec) - setattr(modtool, k, v) - - return modtool - - - -def prereqToDef(prereq, parentjetdef): - """translate a prereq string in the form 'type:alias' into a known config object. - """ - reqtype, reqkey = prereq.split(':',1) - if reqtype=='mod': - reqInstance = aliasToModDef(reqkey, parentjetdef) - else: - reqInstance = constHelper.aliasToInputDef(reqkey, parentjetdef) - return reqInstance - -def aliasToModDef(alias, parentjetdef ): - """return an alias into a JetModifier config object, recursively instatiating all aliases in the dependencies of this JetModifier.""" - if isinstance(alias, JetModifier): - return alias - # else assume it's a string - - # split it, to extract the optional specifiers - modL = alias.split(":") - modkey = modL[0] - modspec = ':'.join(modL[1:]) - - # retrieve an instance from the known modifiers in StandardJetMods : - from .StandardJetMods import jetmoddict - moddef = jetmoddict[modkey].clone( modspec = modspec) - - - if callable(moddef.prereqs): - moddef.prereqs = moddef.prereqs( modspec, parentjetdef ) - - for prereq in moddef.prereqs: - reqInstance = parentjetdef._prereqDic.get( prereq, None) - if reqInstance is None: - reqInstance = prereqToDef(prereq, parentjetdef) - - if prereq.startswith('ghost:'): - # then it is also an input : register this requirement also as an input - prereqN = prereq.split(':')[1] - parentjetdef._prereqOrder.append('input:'+prereqN) - parentjetdef._prereqDic['input:'+prereqN] = reqInstance # the input config instance is identical for input and ghost (only the PseudoJet will differ) - - parentjetdef._prereqOrder.append(prereq) - parentjetdef._prereqDic[prereq] = reqInstance - - return moddef - - - -def jetModWithAlternateTrk(jetdef, trkopt): - """ Update all the track-related JetModifier in jetdef._prereqOrder so that they have their optional specification - ('modspec') set to trkopt. - This is needed when some prereq JetModifier are generated without the specification. The main use case is trigger where the Track and vertex container name are different than the standard. - IMPORTANT : this must be called after instantiateAliases() . - """ - - def changeJetModSpec( mod ): - if mod in ['mod:JVT','mod:JVF', 'mod:TrackMoments', 'mod:TrackSumMoments']: - return mod+':'+trkopt - return mod - - for i in range(len(jetdef._prereqOrder)): - jetdef._prereqOrder[i] = changeJetModSpec( jetdef._prereqOrder[i] ) - - # Deduplicate the prereq (with python > 3.6 dict is ordered so the trick is guaranteed to work) : - jetdef._prereqOrder[:] = list(dict.fromkeys(jetdef._prereqOrder) ) diff --git a/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py index 06950d8ed482b496dbf26c6a7885408d89104f0f..d1144460d2b5f89f30070c599826d0515d3d4141 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py @@ -1,8 +1,11 @@ -# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration """ JetRecConfig: A helper module for configuring jet reconstruction +The functions defined here turn JetDefinition object into ComponentAccumulator or list of algs fully configured +and ready to be inserted in the framework sequence. + Author: TJ Khoo, P-A Delsart """ @@ -12,16 +15,16 @@ import os from AthenaCommon import Logging jetlog = Logging.logging.getLogger('JetRecConfig') +from ROOT import xAODType +xAODType.ObjectType from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator from AthenaConfiguration.ComponentFactory import CompFactory -import JetRecConfig.ConstModHelpers as constH -import JetRecConfig.JetModConfig as modH -from JetRecConfig.JetDefinition import JetDefinition +from JetRecConfig.JetDefinition import JetDefinition, JetInputConstitSeq from JetRecConfig.JetGrooming import GroomingDefinition - +from JetRecConfig.DependencyHelper import solveDependencies, solveGroomingDependencies, aliasToModDef @@ -30,24 +33,26 @@ __all__ = ["JetRecCfg", "JetInputCfg"] ######################################################################## +# +# Top level functions returning ComponentAccumulator out of JetDefinition - -def JetRecCfg(jetdef, configFlags, returnFinalJetDef=False): +def JetRecCfg(jetdef, configFlags, returnConfiguredDef=False): """Top-level function for running jet finding or grooming. This returns a ComponentAccumulator that can be merged with others - from elsewhere in the job, but will provide everything needed to + from elsewhere in the job and which provides everything needed to reconstruct one jet collection. - Receives the jet or grooming definition (jetdef) and input flags (configFlags), mainly for input file + + arguments : + - jetdef : jet or grooming definition + - configFlags : the AthenaConfiguration.AllConfigFlags.ConfigFlags, mainly for input file peeking such that we don't attempt to reproduce stuff that's already in the input file. + - returnConfiguredDef : is for debugging. It will also returns the cloned JetDefinition which contains the calculated dependencies. - returnFinalJetDef : is for debugging. It will also returns the cloned JetDefinition which contains the calculated dependencies. """ - # we clone the jetdef, so we're sure we're not using a 'locked' one - jetdef_i = jetdef.clone() - sequenceName = jetdef_i.fullname() + sequenceName = jetdef.fullname() jetlog.info("******************") jetlog.info("Setting up to find {0}".format(sequenceName)) @@ -56,57 +61,149 @@ def JetRecCfg(jetdef, configFlags, returnFinalJetDef=False): components.addSequence( parOR(sequenceName) ) # call the relevant function according to jetdef_i type - if isinstance(jetdef_i, JetDefinition): - addJetClusteringToComponent(components, jetdef_i, configFlags) + if isinstance(jetdef, JetDefinition): + algs, jetdef_i = getJetDefAlgs(jetdef, configFlags, True) + elif isinstance(jetdef, GroomingDefinition): + algs, jetdef_i = getJetGroomAlgs(jetdef, configFlags, True) + + for a in algs: + components.addEventAlgo( a , sequenceName = sequenceName ) - elif isinstance(jetdef_i, GroomingDefinition): - from JetRecConfig.JetGroomConfig import addGroomToComponent - addGroomToComponent(components, jetdef_i, configFlags) + if returnConfiguredDef: return components, jetdef_i + return components + + +def JetInputCfg(jetOrConstitdef, configFlags, context="default"): + """Returns a ComponentAccumulator containing algs needed to build inputs to jet finding as defined by jetOrConstitdef - if returnFinalJetDef: return components, jetdef_i + jetOrConstitdef can either be + * a JetDefinition : this happens when called from JetRecCfg, then the jetdef._prereqDic/Order are used. + * a JetInputConstit : to allow scheduling the corresponding constituents algs independently of any jet alg. + + context is only used if jetOrConstitdef is not a JetDefinition and must refer to a context in StandardJetContext + """ + components = ComponentAccumulator() + + algs = getInputAlgs(jetOrConstitdef, configFlags, context) + + for a in algs: + components.addEventAlgo( a ) + + return components + +def PseudoJetCfg(jetdef, configFlags): + """Builds a ComponentAccumulator for creating PseudoJetContainer needed by jetdef. + THIS updates jetdef._internalAtt['finalPJContainer'] + """ + components = ComponentAccumulator() + pjalglist = getPseudoJetAlgs(jetdef) + for pjalg in pjalglist: + components.addEventAlgo(pjalg) return components -def addJetClusteringToComponent(components, jetdef_i, configFlags, sequenceName=None): - """internal function which instantiates the JetRecAlg defined by the JetDefinition 'jetdef_i' - into the ComponentAccumulator 'components' + +######################################################################## +######################################################################## +# +# Top level functions returning list of algs out of JetDefinition + + +def getJetDefAlgs(jetdef, configFlags, returnConfiguredDef=False, monTool=None): + """ Create the algorithms necessary to build the jet collection defined by jetdef. + + This internally finds all the dependencies declared into jetdef (through input, ghosts & modifiers) + and returns a list of all necessary algs. + + if returnConfiguredDef==True, also returns the fully configured clone of jetdef containing solved dependencies (debugging) + + monTool is to allow the trigger config to pass a monitoring tool. """ - sequenceName = sequenceName or jetdef_i.fullname() - # create proper config instances for each input and ghost aliases in this jetdef_i - # this implicitely calculates and adds the dependencies. - instantiateAliases(jetdef_i) + + # Scan the dependencies of this jetdef, also converting all aliases it contains + # into config objects and returning a fully configured copy. + jetdef_i = solveDependencies(jetdef) + # check if the conditions are compatible with the inputs & modifiers of this jetdef_i. # if in standardRecoMode we will remove whatever is incompatible and still try to run # if not, we raise an exception - removeComponentFailingConditions(jetdef_i, configFlags, raiseOnFailure= not jetdef_i.standardRecoMode) - + canrun = removeComponentFailingConditions(jetdef_i, configFlags, raiseOnFailure= not jetdef_i.standardRecoMode) + if not canrun : + if returnConfiguredDef: + return [], jetdef_i + return [] + + algs = [] + + # With jetdef_i, we can now instantiate the proper c++ tools and algs. - # Schedule the various input collections. - # We don't have to worry about ordering, as the scheduler - # will handle the details. Just merge the components. - inputcomps = JetInputCfg(jetdef_i, configFlags) - components.merge(inputcomps, sequenceName=sequenceName) + # algs needed to build the various inputs (constituents, track selection, event density, ...) + algs += getInputAlgs(jetdef_i, configFlags , monTool=monTool) - # schedule the algs to create fastjet::PseudoJet objects out of the inputs - pjCompo= PseudoJetCfg(jetdef_i, configFlags) - components.merge(pjCompo, sequenceName=sequenceName) + # algs to create fastjet::PseudoJet objects out of the inputs + algs+= getPseudoJetAlgs(jetdef_i) # Generate a JetRecAlg to run the jet finding and modifiers - jetrecalg = getJetRecAlg( jetdef_i) - components.addEventAlgo(jetrecalg, sequenceName=sequenceName) + jetrecalg = getJetRecAlg( jetdef_i, monTool=monTool) + algs += [jetrecalg] jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(jetdef_i.fullname())) + + if returnConfiguredDef: + return algs, jetdef_i + return algs - return components +def getJetGroomAlgs(groomdef, configFlags, returnConfiguredDef=False, monTool=None): + """Instantiate and schedule all the algorithms needed to run the grooming alg 'groomdef' and + add them in the ComponentAccumulator 'components' + + This function is meant to be called from the top-level JetRecConfig.JetRecCfg + (groomdef is expected to be non locked and will be modified). + + monTool is to allow the trigger config to pass a monitoring tool. + """ + + # Find dependencies from modifier aliases and get a fully configured groomdef + # ( This also detects input dependencies, see below) + groomdef_i = solveGroomingDependencies(groomdef) + # Transfer the input & ghost dependencies onto the parent jet alg, + # so they are handled when instatiating the parent jet algs + groomdef_i.ungroomeddef.ghostdefs += [ g.split(':')[1] for g in groomdef_i._prereqOrder if g.startswith('ghost:')] + groomdef_i.ungroomeddef.extrainputs += [ g.split(':')[1] for g in groomdef_i._prereqOrder if g.startswith('input:')] -def buildPseudoJetAlgs(jetdef): + jetlog.info("Scheduling parent alg {} for {} ".format(groomdef.ungroomeddef.fullname(), groomdef.fullname())) + + # Retrieve algs needed to build the parent (ungroomed) jets + # (we always want it even if the parent jets are already in the input file because + # we need to rebuild the pseudoJet) + algs, ungroomeddef_i = getJetDefAlgs(groomdef_i.ungroomeddef, configFlags, True) + groomdef_i._ungroomeddef = ungroomeddef_i # set directly the internal members to avoid complication. This is fine, since we've been cloning definitions. + + algs += [ getJetRecGroomAlg(groomdef_i, monTool=monTool) ] + + + jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(groomdef_i.fullname())) + + if returnConfiguredDef: return algs, groomdef_i + return algs + + +######################################################################## +# +# Mid level functions returning specific type of algs out of JetDefinition +# functions below assumines the JetDefinition has its dependencies solved by a call to solveDependencies() +# + + +def getPseudoJetAlgs(jetdef): """ Builds the list of configured PseudoJetAlgorithm needed for this jetdef. THIS updates jetdef._internalAtt['finalPJContainer'] (this function is factorized out of PseudoJetCfg so it can be used standalone in the trigger config) """ constitpjalg = getConstitPJGAlg( jetdef.inputdef ) + #print("aaaa" , constitpjalg, constitpjalg.OutputContainer) finalPJContainer = constitpjalg.OutputContainer pjalglist = [constitpjalg] @@ -121,9 +218,9 @@ def buildPseudoJetAlgs(jetdef): pjalglist.append(ghostpjalg) pjContNames.append( ghostpjalg.OutputContainer ) - # .. and merge them together with the input constituents + # .. and merge them together with the input constituents mergeId = mergedPJId( pjContNames ) - finalPJContainer = constitpjalg.OutputContainer+"_merged"+mergeId + finalPJContainer = str(finalPJContainer)+"_merged"+mergeId mergerName = "PJMerger_id"+mergeId mergeAlg =CompFactory.PseudoJetMerger( mergerName, @@ -136,68 +233,64 @@ def buildPseudoJetAlgs(jetdef): jetdef._internalAtt['finalPJContainer'] = finalPJContainer return pjalglist -def PseudoJetCfg(jetdef, configFlags): - """Builds a ComponentAccumulator for creating PseudoJetContainer needed by jetdef. - THIS updates jetdef._internalAtt['finalPJContainer'] - """ - components = ComponentAccumulator() - pjalglist = buildPseudoJetAlgs(jetdef) - for pjalg in pjalglist: - components.addEventAlgo(pjalg) - return components _mergedPJContainers = dict() def mergedPJId(pjList): """returns a simple unique ID for the list of PseudoJet container in pjList""" - t = tuple(pjList) + t = tuple(str(n) for n in pjList) # make sure it is string (it can be DataHandle in old style config) currentSize = len(_mergedPJContainers) return str(_mergedPJContainers.setdefault(t, currentSize)) -######################################################################## -def JetInputCfg(jetOrConstitdef, configFlags): - """Function for setting up inputs to jet finding - - This includes constituent modifications, track selection, copying of - input truth particles and event density calculations +def getInputAlgs(jetOrConstitdef, configFlags, context="default", monTool=None): + """Returns the list of configured algs needed to build inputs to jet finding as defined by jetOrConstitdef jetOrConstitdef can either be - * a JetDefinition : this happens when called from JetRecCfg, then the jetdef._prereqDic/Order are used. - * a JetConstitSource : to allow scheduling the corresponding constituents algs independently of any jet alg. + * a JetDefinition : this happens when called from JetRecCfg or getJetDefAlgs then the jetdef._prereqDic/Order are used. + * a JetInputConstit : to allow scheduling the corresponding constituents algs independently of any jet alg. + + context is only used if jetOrConstitdef is not a JetDefinition and must refer to a context in StandardJetContext. + + The returned list may contain several algs, including constituent modifications algs, track selection, copying of + input truth particles and event density calculations + """ - components = ComponentAccumulator() - - from .JetDefinition import JetConstitSource, JetDefinition - if isinstance(jetOrConstitdef, JetConstitSource): - jetlog.info("Setting up jet inputs from JetConstitSource : "+jetOrConstitdef.name) - jetdef = JetDefinition('Kt', 0., jetOrConstitdef.clone()) - instantiateAliases(jetdef) - removeComponentFailingConditions(jetdef, configFlags, raiseOnFailure= not jetdef.standardRecoMode) + from .JetDefinition import JetInputConstit, JetDefinition + if isinstance(jetOrConstitdef, JetInputConstit): + # technically we need a JetDefinition, so just build an empty one only containing our JetInputConstit + jetlog.info("Setting up jet inputs from JetInputConstit : "+jetOrConstitdef.name) + jetdef = solveDependencies( JetDefinition('Kt', 0., jetOrConstitdef, context=context) ) + canrun = removeComponentFailingConditions(jetdef, configFlags, raiseOnFailure= not jetdef.standardRecoMode) + if not canrun: + return [] else: jetdef = jetOrConstitdef jetlog.info("Inspecting input file contents") - filecontents = configFlags.Input.Collections - inputdeps = [ inputkey for inputkey in jetdef._prereqOrder if inputkey.startswith('input:')] + try: + filecontents = configFlags.Input.Collections + except Exception: + filecontents = [] + inputdeps = [ inputkey for inputkey in jetdef._prereqOrder if inputkey.startswith('input:')] + algs = [] for inputfull in inputdeps: inputInstance = jetdef._prereqDic[inputfull] - isprimary = False # actually not using it yet. - if isinstance(inputInstance, JetConstitSource): + if isinstance(inputInstance, JetInputConstit): if inputInstance.containername in filecontents: jetlog.debug("Input container {0} for label {1} already in input file.".format(inputInstance.containername, inputInstance.name)) else: jetlog.debug("Preparing Constit Mods for label {0} from {1}".format(inputInstance.name,inputInstance.inputname)) # May need to generate constituent modifier sequences to # produce the input collection - from . import ConstModHelpers - constitalg = ConstModHelpers.getConstitModAlg(inputInstance) + #from . import ConstModHelpers + constitalg = getConstitModAlg(jetdef, inputInstance, monTool=monTool) if constitalg: - components.addEventAlgo(constitalg, primary=isprimary) - else: # it must be a JetInputDef + algs +=[ constitalg ] + else: # it must be a JetInputExternal cname = inputInstance.containername(jetdef,inputInstance.specs) # (by defaults this is just inputInstance.name) if cname in filecontents: jetlog.debug("Input container {0} for prereq {1} already in input file.".format(cname, inputInstance.name)) @@ -205,15 +298,22 @@ def JetInputCfg(jetOrConstitdef, configFlags): jetlog.debug("Requesting input {} with function {} and specs {}".format(inputInstance.name, inputInstance.algoBuilder, inputInstance.specs) ) # check if it has something to build an Algorithm if inputInstance.algoBuilder: - components.addEventAlgo( inputInstance.algoBuilder( jetdef, inputInstance.specs ), primary=isprimary ) + algs+=[ inputInstance.algoBuilder( jetdef, inputInstance.specs ) ] else: # for now just hope the input will be present... pass - return components + return algs + + +######################################################################## + def getConstitPJGAlg(constitdef): - """returns a configured PseudoJetAlgorithm which converts the inputs defined by constitdef into fastjet::PseudoJet""" + """returns a configured PseudoJetAlgorithm which converts the inputs defined by constitdef into fastjet::PseudoJet + + IMPORTANT : constitdef must have its dependencies solved (i.e. it must result from a solveDependencies() call) +""" jetlog.debug("Getting PseudoJetAlg for label {0} from {1}".format(constitdef.name,constitdef.inputname)) @@ -232,8 +332,10 @@ def getGhostPJGAlg(ghostdef): """returns a configured PseudoJetAlgorithm which converts the inputs defined by constitdef into fastjet::PseudoJet The difference for the above is this is dedicated to ghosts which need variations for the Label and the muon segment cases. + + IMPORTANT : ghostdef must have its dependencies solved (i.e. it must result from a solveDependencies() call) """ - label = "Ghost"+ghostdef.label # IMPORTANT !! "Ghost" in the label will be interpreted by teh C++ side ! + label = "Ghost"+ghostdef.label # IMPORTANT !! "Ghost" in the label will be interpreted by the C++ side ! kwargs = dict( InputContainer = ghostdef.containername, @@ -254,48 +356,11 @@ def getGhostPJGAlg(ghostdef): return pjgalg -def buildJetModifierList( jetdef ): - """returns the list of configured JetModifier tools needed by this jetdef. - This is done by instantiating the actual C++ tool as ordered in jetdef._prereqOrder - """ - modlist = [ key for key in jetdef._prereqOrder if key.startswith('mod:')] - - from . import JetModConfig - mods = [] - for modkey in modlist: - moddef = jetdef._prereqDic[modkey] - modkey = modkey[4:] # remove 'mod:' - modspec = '' if ':' not in modkey else modkey.split(':',1)[1] - mod = JetModConfig.getModifier(jetdef,moddef,modspec) - mods.append(mod) - - return mods - -def getJetAlgorithm(jetname, jetdef, pjContNames, monTool = None): - """returns a configured JetAlgorithm """ - jetlog.debug("Configuring JetAlgorithm \"jetalg_{0}\"".format(jetname)) - - builder = getJetBuilder() - - finder = getJetFinder(jetname, jetdef) - finder.JetBuilder = builder - - mods = buildJetModifierList(jetdef) - - rectool = getJetRecTool(jetname,finder,pjContNames,mods) - if monTool: rectool.MonTool = monTool - - jetalg = CompFactory.JetAlgorithm("jetalg_"+jetname) - jetalg.Tools = [rectool] - - return jetalg - -######################################################################## -# New JetRecAlgorithm to replace JetRecTool -# This call is for a JRA that runs jet-finding -# def getJetRecAlg( jetdef, monTool = None): - """ """ + """Returns the configured JetRecAlg instance corresponding to jetdef + + IMPORTANT : jetdef must have its dependencies solved (i.e. it must result from solveDependencies() ) + """ pjContNames = jetdef._internalAtt['finalPJContainer'] jclust = CompFactory.JetClusterer( "builder", @@ -308,7 +373,7 @@ def getJetRecAlg( jetdef, monTool = None): RandomOption = 1, ) - mods = buildJetModifierList(jetdef) + mods = getJetModifierTools(jetdef) jetname = jetdef.fullname() jra = CompFactory.JetRecAlg( @@ -321,22 +386,51 @@ def getJetRecAlg( jetdef, monTool = None): # this option can't be set in AnalysisBase -> set only if explicitly asked : jra.MonTool = monTool - autoconfigureModifiers(jra.Modifiers, jetname) + return jra + + +def getJetRecGroomAlg(groomdef,monTool=None): + """Returns a configured JetRecAlg set-up to perform the grooming defined by 'groomdef' + ('monTool' is a temporary placeholder, it is expected to be used in the trigger in the future) + """ + jetlog.debug("Configuring grooming alg \"jetalg_{0}\"".format(groomdef.fullname())) + + + # the grooming tool (a IJetProvider instance) + groomClass = CompFactory.getComp(groomdef.tooltype) + groomer = groomClass(groomdef.groomalg, + UngroomedJets = groomdef.ungroomeddef.fullname(), + ParentPseudoJets = groomdef.ungroomeddef._internalAtt['finalPJContainer'], + **groomdef.properties) + # get JetModifier list + mods = getJetModifierTools(groomdef) + # put everything together in a JetRecAlg + jetname = groomdef.fullname() + jra = CompFactory.JetRecAlg( + "jetrecalg_"+jetname, + Provider = groomer, + Modifiers = mods, + OutputContainer = jetname, + MonTool = monTool) + + + return jra ######################################################################## -# Get a JetRecAlg set up to copy a jet collection and apply mods -# In this setup we do not resolve dependencies because typically -# these may be set up already in the original jet collection -# In future we may wish to add a toggle. -# -# The decoration list can be set in order for the decorations -# (jet moments) on the original jets to be propagated to the -# copy collection. Beware of circular dependencies! def getJetCopyAlg(jetsin, jetsoutdef, decorations=[], shallowcopy=True, shallowIO=True, monTool=None): - + """ + Get a JetRecAlg set up to copy a jet collection and apply mods + In this setup we do not resolve dependencies because typically + these may be set up already in the original jet collection + In future we may wish to add a toggle. + + The decoration list can be set in order for the decorations + (jet moments) on the original jets to be propagated to the + copy collection. Beware of circular dependencies! + """ jcopy = CompFactory.JetCopier( "copier", InputJets = jetsin, @@ -345,11 +439,10 @@ def getJetCopyAlg(jetsin, jetsoutdef, decorations=[], shallowcopy=True, shallowI ShallowIO=shallowIO) # Convert mod aliases into concrete tools - from . import JetModConfig mods = [] for mod in jetsoutdef.modifiers: - moddef = JetModConfig.aliasToModDef(mod,jetsoutdef) - mods.append(JetModConfig.getModifier(jetsoutdef,moddef,moddef.modspec)) + moddef = aliasToModDef(mod,jetsoutdef) + mods.append(getModifier(jetsoutdef,moddef,moddef.modspec)) jetsoutname = jetsoutdef.fullname() jra = CompFactory.JetRecAlg( @@ -359,107 +452,136 @@ def getJetCopyAlg(jetsin, jetsoutdef, decorations=[], shallowcopy=True, shallowI OutputContainer = jetsoutname, MonTool = monTool) - autoconfigureModifiers(jra.Modifiers, jetsoutname) return jra -######################################################################## -# For each modifier in the given list with a configurable input container -# name ("JetContainer"), configure it to containerName. -# Also handle any container-specific configuration needed. -def autoconfigureModifiers(modifiers, containerName): - for mod in modifiers: - if "JetContainer" in propertiesOf( mod ): - mod.JetContainer = containerName - if "DoPFlowMoments" in propertiesOf( mod ): - mod.DoPFlowMoments = ("PFlow" in containerName) - - -def propertiesOf(comp): - """ Obtain properties irrespectively of the config system""" - try: - propNames = comp._descriptors - return propNames - except Exception: - pass - return comp.properties() - -######################################################################## -def getJetBuilder(doArea=True): - """Returns a jet builder (JetFromPseudojet) , i.e. converter from - fastjet EDM to xAOD EDM + +def getConstitModAlg(parentjetdef, constitSeq, monTool=None): + """returns a configured JetConstituentModSequence or None if constit.modifiers == [] + + The JetConstituentModSequence is determined by the JetInputConstitSeq constitSeq . + However, details of the configuration of the JetConstituentModSequence may depends on which JetDefinition + this JetConstituentModSequence is intended for. Thus the function also requires a parentjetdef JetDefinition input + + IMPORTANT : parentjetdef & constitSeq must have their dependencies solved (i.e. they must result from solveDependencies() ) + + See also getConstitModAlg_nojetdef """ - # Do we have any reasons for not using the area one? - # Maybe CPU reduction if we don't need areas for calibration - builder = CompFactory.JetFromPseudojet("jetbuild") - if doArea: - builder.Attributes = ["ActiveArea","ActiveAreaFourVector"] - return builder + + # JetInputConstit do not need any JetConstituentModSequence + # (they are only needed to trigger the building of the source container and a PJ algo) + if not isinstance(constitSeq, JetInputConstitSeq): return -######################################################################## -# -def getJetFinder(jetname, jetdef): - """Creates a jet finder, i.e. interface to fastjet""" - finder = CompFactory.JetFinder("jetfind_"+jetname, - JetAlgorithm = jetdef.algorithm, - JetRadius = jetdef.radius, - PtMin = jetdef.ptmin, - GhostArea = 0.01, - RandomOption = 1, - ) - return finder + + inputtype = constitSeq.basetype -######################################################################## -# Function for generating a JetRecTool -# -def getJetRecTool(jetname, finder, pjs, mods): - # Create the JetRecTool and pass the inputs - jetrec = CompFactory.JetRecTool("jetrec_"+jetname, - OutputContainer = jetname, - InputPseudoJets = pjs, - JetFinder = finder, - JetModifiers = mods ) - autoconfigureModifiers(jetrec.JetModifiers, jetname) - return jetrec + sequence = constitSeq.modifiers + + modlist = [] + #if modlist == []: return + if constitSeq.inputname == constitSeq.containername: return + + for step in sequence: + modInstance = constitSeq._instanceMap[ step ] + if not modInstance.tooltype: continue + toolclass = getattr( CompFactory, modInstance.tooltype) -def instantiateAliases( jetdef ): - """ Instantiate all the aliases contained in this jetdef : modifiers, ghosts and prereqs. - At the same time fills the internal _prereqDic and _prereqOrder containers. - - This functions - * assumes jetdef is not 'locked' - * implies calls to recursives function constH.aliasToInputDef and modH.aliasToModDef - """ + # update the properties : if some of them are function, just replace by calling this func : + for k,v in modInstance.properties.items(): + if callable(v) : + modInstance.properties[k ] = v(parentjetdef, constitSeq ) + + tool = toolclass(modInstance.name,**modInstance.properties) + + if inputtype == xAODType.ParticleFlow and modInstance.tooltype not in ["CorrectPFOTool","ChargedHadronSubtractionTool"]: + tool.IgnoreChargedPFO=True + tool.ApplyToChargedPFO=False + tool.InputType = inputtype + modlist.append(tool) + + sequenceshort = "".join(sequence) + seqname = "ConstitMod{0}_{1}".format(sequenceshort,constitSeq.name) + inputcontainer = str(constitSeq.inputname) + outputcontainer = str(constitSeq.containername) + if inputtype==xAODType.ParticleFlow: + # Tweak PF names because ConstModSequence needs to work with + # up to 4 containers + def chopPFO(thestring): + pfostr = "ParticleFlowObjects" + if thestring.endswith(pfostr): + return thestring[:-len(pfostr)] + return thestring + inputcontainer = chopPFO(inputcontainer) + outputcontainer = chopPFO(outputcontainer) + + modseq = CompFactory.JetConstituentModSequence(seqname, + InputType=inputtype, + OutputContainer = outputcontainer, + InputContainer= inputcontainer, + Modifiers = modlist, + ) + if monTool: + modseq.MonTool = monTool + + constitmodalg = CompFactory.JetAlgorithm("jetalg_{0}".format(modseq.getName())) + constitmodalg.Tools = [modseq] - # start with the inputdef, cloning it so we're not altering a private copy - jetdef.inputdef = jetdef.inputdef.clone() - constH.instantiateJetConstitAliases(jetdef.inputdef, jetdef) + return constitmodalg - jetdef._prereqDic['input:'+jetdef.inputdef.name] = jetdef.inputdef - jetdef._prereqOrder.append('input:'+jetdef.inputdef.name) +def getConstitModAlg_nojetdef( constitSeq, context="default", monTool=None): + """Same as getConstitModAlg. + This is a convenient function to obtain a JetConstituentModSequence when it is certain, no JetDef is needed. + This function just builds a dummy JetDefinition then calls getConstitModAlg + Needed in the trigger config. + """ + jetdef = solveDependencies( JetDefinition('Kt', 0., constitSeq, context=context) ) + constitSeq = jetdef._prereqDic['input:'+constitSeq.name] # retrieve the fully configured version of constitSeq + return getConstitModAlg(jetdef, constitSeq, monTool=monTool) - for g in jetdef.extrainputs: - gInstance = constH.aliasToInputDef( g , jetdef) - jetdef._prereqDic['input:'+g] = gInstance - jetdef._prereqOrder.append('input:'+g) + +def getJetModifierTools( jetdef ): + """returns the list of configured JetModifier tools needed by this jetdef. + This is done by instantiating the actual C++ tool as ordered in jetdef._prereqOrder + """ + modlist = [ key for key in jetdef._prereqOrder if key.startswith('mod:')] - for g in jetdef.ghostdefs: - gInstance = constH.aliasToInputDef( g , jetdef) - jetdef._prereqDic['input:'+g] = gInstance - jetdef._prereqOrder.append('input:'+g) - jetdef._prereqDic['ghost:'+g] = gInstance - jetdef._prereqOrder.append('ghost:'+g) + mods = [] + for modkey in modlist: + moddef = jetdef._prereqDic[modkey] + modkey = modkey[4:] # remove 'mod:' + modspec = '' if ':' not in modkey else modkey.split(':',1)[1] + mod = getModifier(jetdef,moddef,modspec) + mods.append(mod) + + return mods - for mod in jetdef.modifiers: - modInstance = modH.aliasToModDef(mod, jetdef) - jetdef._prereqDic['mod:'+mod] = modInstance - jetdef._prereqOrder.append('mod:'+mod) - # Deduplicate the prereq (with python > 3.6 dict is ordered so the trick is guaranteed to work) : - jetdef._prereqOrder[:] = list(dict.fromkeys(jetdef._prereqOrder) ) +def getModifier(jetdef, moddef, modspec): + """Translate JetModifier into a concrete tool""" + jetlog.verbose("Retrieving modifier {0}".format(str(moddef))) + + # Get the modifier tool + try: + modtool = moddef.createfn(jetdef, modspec) + except Exception as e: + jetlog.error( "Unhandled modifier specification {0} for mod {1} acting on jet def {2}!".format(modspec,moddef,jetdef.basename) ) + jetlog.error( "Received exception \"{0}\"".format(e) ) + jetlog.error( "Helper function is \"{0}\"".format(moddef.createfn) ) + raise ValueError( "JetModConfig unable to handle mod {0} with spec \"{1}\"".format(moddef,modspec) ) + + + # now we overwrite the default properties of the tool, by those + # set in the moddef : + for k,v in moddef.properties.items(): + if callable(v) : + # The value we got is a function : we call it to get the actual value we want to set on the tool + v = v(jetdef, modspec) + setattr(modtool, k, v) + + return modtool @@ -476,14 +598,17 @@ def removeComponentFailingConditions(jetdef, configflags, raiseOnFailure=True): ## TODO : ## do not raise an exceptin immediately. Instead collect all failure ## then report all of them, then raise + + fullname = jetdef.fullname() # define a helper function returning a filtered list of components. def filterList(inList, compType): nOut=0 outList=[] + basekey= compType+':' if compType!="" else "" # loop over components in the list to be filtered for comp in inList: - fullkey = compType+':'+comp + fullkey = basekey+comp cInstance = jetdef._prereqDic[fullkey] ok, reason = isComponentPassingConditions(cInstance, configflags, jetdef._prereqDic) if not ok : @@ -492,31 +617,49 @@ def removeComponentFailingConditions(jetdef, configflags, raiseOnFailure=True): jetdef, compType, comp, reason) ) nOut+=1 - jetlog.info("IMPORTANT : removing {} {} reason={} ".format(compType, comp, reason)) + jetlog.info(f"{fullname} : removing {compType} {comp} reason={reason}") jetdef._prereqOrder.remove(fullkey) + if compType=='ghost': + jetdef._prereqOrder.remove('input:'+comp) else: outList.append(comp) jetlog.info(" *** Number of {} filtered components = {} final list={}".format(compType, nOut, outList) ) + return outList # --------- - + + # --------- + # first check if the input can be obtained. If not return. + #jetdef.inputdef.prereqs += ['input:'+jetdef.inputdef.inputname] # this is technically necesary. + ok,reason = isComponentPassingConditions( jetdef.inputdef, configflags, jetdef._prereqDic) + if not ok: + if raiseOnFailure: + raise Exception(f"JetDefinition {jetdef} can NOT be scheduled. Failure of input {jetdef.inputdef.name} reason={reason}" ) + jetlog.info(f"IMPORTANT : removing {jetdef} because input incompatible with job conditions. Reason={reason} ") + return False + # call the helper function to perform filtering : jetdef.ghostdefs = filterList( jetdef.ghostdefs, "ghost") jetdef.modifiers = filterList( jetdef.modifiers, "mod") + # finally filter all possible intermediate dependency : + filterList( list(jetdef._prereqOrder), "") + return True + +# def removeComponentFailingConditions(jetdef, configflags, raiseOnFailure=True): +# for + def isComponentPassingConditions(component, configflags, prereqDic): """Test if component is compatible with configflags. This is done by calling component.filterfn AND testing all its prereqs. """ - from .JetDefinition import JetModifier, JetConstitModifier - if isinstance(component, (JetModifier, JetConstitModifier)): - for req in component.prereqs: - if req not in prereqDic: - return False, "prereq "+req+" not available" - reqInstance = prereqDic[req] - ok, reason = isComponentPassingConditions(reqInstance, configflags, prereqDic) - if not ok : - return False, "prereq "+str(component)+" failed." + for req in component.prereqs: + if req not in prereqDic: + return False, "prereq "+req+" not available" + reqInstance = prereqDic[req] + ok, reason = isComponentPassingConditions(reqInstance, configflags, prereqDic) + if not ok : + return False, "prereq "+str(reqInstance)+" failed because : "+reason ok, reason = component.filterfn(configflags) return ok, reason diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardJetConstits.py b/Reconstruction/Jet/JetRecConfig/python/StandardJetConstits.py index ecf5a48963b18ce96a7d7e7d38357bc203facfcb..7adc6a2fe35a55f9b2f90580eed77653b23bd18e 100644 --- a/Reconstruction/Jet/JetRecConfig/python/StandardJetConstits.py +++ b/Reconstruction/Jet/JetRecConfig/python/StandardJetConstits.py @@ -1,7 +1,9 @@ + # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration """ - StandardJetConstits: A module defining standard definitions for jet constituents. + StandardJetConstits: A module defining standard definitions for jet inputs : external container and + constituents. These can be copied and modified by users who want something a bit different from what is provided. @@ -12,25 +14,28 @@ """ ######################################################################## -from .JetDefinition import xAODType, JetConstitSeq, JetInputDef, JetConstitModifier, JetConstitSource +from .JetDefinition import xAODType, JetInputConstitSeq, JetInputExternal, JetConstitModifier, JetInputConstit # Prepare dictionnaries to hold all of our standard definitions. # They will be filled from the list below from .Utilities import ldict -jetconstitdic = ldict() -jetconstitmoddic = ldict() -jetinputdefdic = ldict() +stdInputExtDic = ldict() +stdConstitDic = ldict() +stdContitModifDic = ldict() # This module contains the helper functions needed to instantiate the input container external # to Jet domain import JetRecConfig.JetInputConfig as inputcfg +import JetRecTools.JetRecToolsConfig as jrtcfg -def _isMCTrue(flags): +def isMC(flags): """A simple filter function for testing if we're running in MC + returns (bool, str) where the str contains an explanation of why the bool is False. (probably worth re-allocating somehere else)""" - return flags.Input.isMC, "Not MC input" + return flags.Input.isMC, "Input file is not MC" + ######################################################################## @@ -38,45 +43,48 @@ def _isMCTrue(flags): _stdInputList = [ # Format is : - # JetInputDef( containername , containerType, ...optionnal parameters... ) - + # JetInputExternal( containername , containerType, ...optionnal parameters... ) + # When defined, algoBuilder is a function returning the actual alg building the input. + # it will be called as : algoBuilder(jetdef, spec) where jetdef is the parent JetDefinition + # ***************************** - JetInputDef("CaloCalTopoClusters", xAODType.CaloCluster), + JetInputExternal("CaloCalTopoClusters", xAODType.CaloCluster), # ***************************** - JetInputDef("JetETMissParticleFlowObjects", xAODType.ParticleFlow), + JetInputExternal("JetETMissParticleFlowObjects", xAODType.ParticleFlow), # ***************************** - JetInputDef("JetSelectedTracks", xAODType.TrackParticle, algoBuilder = inputcfg.buildJetSelectedTracks), - JetInputDef("JetTrackUsedInFitDeco", xAODType.TrackParticle, algoBuilder = inputcfg.buildJetTrackUsedInFitDeco), - JetInputDef("JetTrackVtxAssoc", xAODType.TrackParticle, algoBuilder = inputcfg.buildJetTrackVertexAssoc), + JetInputExternal("JetSelectedTracks", xAODType.TrackParticle, algoBuilder = lambda jdef,_ : jrtcfg.getTrackSelAlg(jdef.context ) ), + JetInputExternal("JetTrackUsedInFitDeco", xAODType.TrackParticle, algoBuilder = inputcfg.buildJetTrackUsedInFitDeco), + JetInputExternal("JetTrackVtxAssoc", xAODType.TrackParticle, algoBuilder = inputcfg.buildJetTrackVertexAssoc, + prereqs = ["input:JetTrackUsedInFitDeco"] ), # ***************************** - JetInputDef("EventDensity", "EventShape", algoBuilder = inputcfg.buildEventShapeAlg, + JetInputExternal("EventDensity", "EventShape", algoBuilder = inputcfg.buildEventShapeAlg, containername = lambda jetdef, specs : (specs or "")+"Kt4"+jetdef.inputdef.label+"EventShape", prereqs = lambda jetdef : ["input:"+jetdef.inputdef.name] # this will force the input to be build *before* the EventDensity alg. ), - JetInputDef("HLT_EventDensity", "EventShape", algoBuilder = inputcfg.buildEventShapeAlg, + JetInputExternal("HLT_EventDensity", "EventShape", algoBuilder = inputcfg.buildEventShapeAlg, containername = lambda jetdef, specs : (specs or "")+"Kt4"+jetdef.inputdef.label+"EventShape", prereqs = lambda jetdef : ["input:"+jetdef.inputdef.name], # this will force the input to be build *before* the EventDensity alg. specs = 'HLT_' ), # ***************************** - JetInputDef("MuonSegments", "MuonSegment",), + JetInputExternal("MuonSegments", "MuonSegment",), # ***************************** # Truth particles from the hard scatter vertex prior to Geant4 simulation. # Neutrinos and muons are omitted; all other stable particles are included. - JetInputDef("JetInputTruthParticles", xAODType.TruthParticle, - algoBuilder = inputcfg.buildJetInputTruth, filterfn=_isMCTrue ), + JetInputExternal("JetInputTruthParticles", xAODType.TruthParticle, + algoBuilder = inputcfg.buildJetInputTruth, filterfn=isMC ), # Truth particles from the hard scatter vertex prior to Geant4 simulation. # Prompt electrons, muons and neutrinos are excluded, all other stable particles # are included, in particular leptons and neutrinos from hadron decays. - JetInputDef("JetInputTruthParticlesNoWZ", xAODType.TruthParticle, - algoBuilder = inputcfg.buildJetInputTruth, filterfn=_isMCTrue,specs="NoWZ"), + JetInputExternal("JetInputTruthParticlesNoWZ", xAODType.TruthParticle, + algoBuilder = inputcfg.buildJetInputTruth, filterfn=isMC,specs="NoWZ"), ] @@ -87,63 +95,19 @@ _truthFlavours = ["BHadronsInitial", "BHadronsFinal", "BQuarksFinal", "Partons",] for label in _truthFlavours: # re-use the main truth input definition : - _stdInputList.append( JetInputDef("TruthLabel"+label, xAODType.TruthParticle, + _stdInputList.append( JetInputExternal("TruthLabel"+label, xAODType.TruthParticle, algoBuilder = inputcfg.buildLabelledTruth, - filterfn=_isMCTrue, specs = label ) ) + filterfn=isMC, specs = label ) ) -# Fill the jetinputdefdic from the above list +# Fill the stdInputExtDic from the above list for ji in _stdInputList: ji._locked = True # lock the definitions so we have unmutable references ! - jetinputdefdic[ji.name] = ji - - + stdInputExtDic[ji.name] = ji -######################################################################## -## List of standard constituent modifiers - -def _getPFOTool(constitSeq): - """One Property of the CorrectPFO constit modifier is a tool. - we use this function as a placeholder, allowing to delay the intantiation of this property tool - to the time the modifier itself is instantiated. - """ - from AthenaConfiguration.ComponentFactory import CompFactory - return CompFactory.getComp("CP::WeightPFOTool")("weightPFO") - - -vtxKey = "PrimaryVertices" -tvaKey = "JetTrackVtxAssoc" -_stdModList = [ - # Format is : - # JetConstitModifier( name , toolType, dictionnary_of_tool_properties ) - # (see JetDefinition.py for more details) - - JetConstitModifier("Origin", "CaloClusterConstituentsOrigin", ), - JetConstitModifier("EM", "ClusterAtEMScaleTool", ), - JetConstitModifier("LC", "", ), - # Particle flow - JetConstitModifier("CorrectPFO", "CorrectPFOTool", - dict(VertexContainerKey=vtxKey, - WeightPFOTool= _getPFOTool ) ), - - JetConstitModifier("CHS", "ChargedHadronSubtractionTool", - dict(VertexContainerKey=vtxKey, TrackVertexAssociation=tvaKey) ), - - # Pileup suppression - JetConstitModifier("Vor", "VoronoiWeightTool", dict(doSpread=False, nSigma=0) ), - JetConstitModifier("CS", "ConstituentSubtractorTool", dict(MaxEta=5. ) ), - JetConstitModifier("SK", "SoftKillerWeightTool",), - -] - -# Fill the jetconstitmoddic from the above list -for ji in _stdModList: - ji._locked = True - jetconstitmoddic[ji.name] = ji - @@ -154,18 +118,18 @@ for ji in _stdModList: ## This sequences uses the above constit modifiers _stdSeqList = [ # Format is typically : - # JetConstitSeq( name , input_cont_type, list_of_modifiers, inputcontainer, outputcontainer ) + # JetInputConstitSeq( name , input_cont_type, list_of_modifiers, inputcontainer, outputcontainer ) # or - # JetConstitSource( name, input_cont_type, containername) + # JetInputConstit( name, input_cont_type, containername) # see JetDefinition.py for details. # ***************************** # Cluster constituents - JetConstitSeq("EMTopoOrigin", xAODType.CaloCluster, ["EM","Origin"], + JetInputConstitSeq("EMTopoOrigin", xAODType.CaloCluster, ["EM","Origin"], "CaloCalTopoClusters", "EMOriginTopoClusters", jetinputtype="EMTopo"), - JetConstitSeq("LCTopoOrigin",xAODType.CaloCluster, ["LC","Origin"], + JetInputConstitSeq("LCTopoOrigin",xAODType.CaloCluster, ["LC","Origin"], "CaloCalTopoClusters", "LCOriginTopoClusters", jetinputtype="LCTopo"), - JetConstitSeq("LCTopoCSSK", xAODType.CaloCluster, ["LC","Origin","CS","SK"], + JetInputConstitSeq("LCTopoCSSK", xAODType.CaloCluster, ["LC","Origin","CS","SK"], "CaloCalTopoClusters", "LCOriginTopoCSSK", jetinputtype="LCTopo"), @@ -175,37 +139,86 @@ _stdSeqList = [ # EM-scale particle flow objects with charged hadron subtraction # For now we don't specify a scale, as only one works well, but # this could be incorporated into the naming scheme and config - JetConstitSeq("EMPFlow", xAODType.ParticleFlow,["CorrectPFO", "CHS"] , 'JetETMissParticleFlowObjects', 'CHSParticleFlowObjects'), + JetInputConstitSeq("EMPFlow", xAODType.ParticleFlow,["CorrectPFO", "CHS"] , 'JetETMissParticleFlowObjects', 'CHSParticleFlowObjects'), # Particle Flow Objects with Constituent Subtraction + SoftKiller - JetConstitSeq("EMPFlowCSSK", xAODType.ParticleFlow,["CorrectPFO", "CS","SK", "CHS"] , + JetInputConstitSeq("EMPFlowCSSK", xAODType.ParticleFlow,["CorrectPFO", "CS","SK", "CHS"] , 'JetETMissParticleFlowObjects', 'CSSKParticleFlowObjects', jetinputtype="EMPFlow"), # ***************************** # Track constituents - JetConstitSource("Track", xAODType.TrackParticle,'JetSelectedTracks'), + JetInputConstit("Track", xAODType.TrackParticle,'JetSelectedTracks'), # Track particles from the primary vertex - JetConstitSeq("PV0Track", xAODType.TrackParticle,["PV0"],'JetSelectedTracks', 'PV0JetSelectedTracks', - prereqs= ["input:JetTrackUsedInFitDeco","input:JetTrackVtxAssoc"], ), + JetInputConstitSeq("PV0Track", xAODType.TrackParticle,["PV0"],'JetSelectedTracks', 'PV0JetSelectedTracks', + prereqs= ["input:JetTrackUsedInFitDeco","input:JetTrackVtxAssoc"], ), # ***************************** # Muon segments. Only used as ghosts - JetConstitSource("MuonSegment", "MuonSegment", "MuonSegments" ), + JetInputConstit("MuonSegment", "MuonSegment", "MuonSegments" ), # ***************************** - # Truth particles (see JetInputDef declarations above for more details) - JetConstitSource("Truth", xAODType.TruthParticle, "JetInputTruthParticles" ), + # Truth particles (see JetInputExternal declarations above for more details) + JetInputConstit("Truth", xAODType.TruthParticle, "JetInputTruthParticles" ), - JetConstitSource("TruthWZ", xAODType.TruthParticle, "JetInputTruthParticlesNoWZ", jetinputtype="TruthWZ"), + JetInputConstit("TruthWZ", xAODType.TruthParticle, "JetInputTruthParticlesNoWZ", jetinputtype="TruthWZ"), ] for label in _truthFlavours: - _stdSeqList.append( JetConstitSource(label, xAODType.TruthParticle, "TruthLabel"+label ) ) + _stdSeqList.append( JetInputConstit(label, xAODType.TruthParticle, "TruthLabel"+label ) ) -# Fill the jetconstitdic from the above list +# Fill the stdConstitDic from the above list for jc in _stdSeqList: jc._locked = True - jetconstitdic[jc.name] = jc + stdConstitDic[jc.name] = jc + + + +######################################################################## +## List of standard constituent modifiers + +def _getPFOTool(*l): + """One Property of the CorrectPFO constit modifier is a tool. + we use this function as a placeholder, allowing to delay the intantiation of this property tool + to the time the modifier itself is instantiated. + """ + from AthenaConfiguration.ComponentFactory import CompFactory + return CompFactory.getComp("CP::WeightPFOTool")("weightPFO") + + +from JetRecConfig.StandardJetContext import propFromContext + +vtxKey = "PrimaryVertices" +tvaKey = "JetTrackVtxAssoc" +_stdModList = [ + # Format is : + # JetConstitModifier( name , toolType, dictionnary_of_tool_properties ) + # (see JetDefinition.py for more details) + + JetConstitModifier("Origin", "CaloClusterConstituentsOrigin", ), + JetConstitModifier("EM", "ClusterAtEMScaleTool", ), + JetConstitModifier("LC", "", ), + # Particle flow + JetConstitModifier("CorrectPFO", "CorrectPFOTool", + dict(VertexContainerKey=propFromContext("Vertices"), + WeightPFOTool= _getPFOTool ) ), + + JetConstitModifier("CHS", "ChargedHadronSubtractionTool", + # get the track properties from the context with wich jet will be configured with propFromContext + # See StandardJetContext.py for the default values. + dict(VertexContainerKey=propFromContext("Vertices"), + TrackVertexAssociation=propFromContext("TVA"))), + + # Pileup suppression + JetConstitModifier("Vor", "VoronoiWeightTool", dict(doSpread=False, nSigma=0) ), + JetConstitModifier("CS", "ConstituentSubtractorTool", dict(MaxEta=5. ) ), + JetConstitModifier("SK", "SoftKillerWeightTool",), + +] + +# Fill the stdContitModifDic from the above list +for ji in _stdModList: + ji._locked = True + stdContitModifDic[ji.name] = ji diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardJetContext.py b/Reconstruction/Jet/JetRecConfig/python/StandardJetContext.py new file mode 100644 index 0000000000000000000000000000000000000000..ffccf550f9ffad9e9bf7e1732c58461f6bcf287a --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/python/StandardJetContext.py @@ -0,0 +1,53 @@ +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration +""" +This module defines the standard 'jet contexts'. +A jet context is a set of options (mainly related to Tracks) shared by several +components (modifier tools, input tools/algs, ...). + +Other contexts are expected to be defined, for example in the trigger to deal with oher track collection, or for +analysis having non-default PV0 choices. + +Setting a context to a JetDefinition ensures identical properties are consistently set across the components configured by the JetDefinition. +""" +from JetRecConfig.Utilities import ldict + + +jetContextDic = ldict( + #********************************** + # The default set of common jet options : + default = ldict( + Tracks = "InDetTrackParticles", + JetTracks = "JetSelectedTracks", + Vertices = "PrimaryVertices", + TVA = "JetTrackVtxAssoc", + GhostTracks = "PseudoJetGhostTrack", + GhostTracksLabel = "GhostTrack", + GhostTrackCutLevel = 'NoCut', + + # options passed to InDet::InDetTrackSelectionTool. + # Note : these are the standard options used for track calculations. Tracks selected for ghost-associaton have CutLevel=NoCut by default : see ghostTrackCutLevel above + trackSelOptions = ldict( CutLevel = "Loose", minPt=500 ), + + ), + + #********************************** + # This is not a jet context, but the list of keys related to track. + # We store it here since it is a related central place. Used in trigger config. + trackKeys = ["Tracks", "Vertices", "TVA", "GhostTracks", "GhostTracksLabel", "JetTracks"], +) + +jetContextDic[""] = jetContextDic["default"] + + +def propFromContext(propName): + """Some properties might depend on the context for which jets are configured. + This function returns a helper function which gives the value of the property propName according to the current context. + + """ + def getProp(jetdef, spec): + context = jetdef.context + if isinstance(spec, str): + # user may have passed explicitly a str : allow to overide jetdef.context if non void + context = spec or context + return jetContextDic[context][propName] + return getProp diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py b/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py index 43f3445e7d98a7a46d0e8e62420159dc3ed74f35..a0a2f90bedac26acc794c808f2ad9920fcc23c92 100644 --- a/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py +++ b/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py @@ -1,3 +1,4 @@ + # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration """ This module defines the standard JetModifier tools used in jet reco @@ -8,44 +9,48 @@ This dict maps a modifier alias to the JetModifier config object that in turn will be responsible for generating a configured tool. The JetModifier config class is defined in JetDefinition.py -[Optional] Args to the JetModifier constructor are: + + Args to the JetModifier constructor are: 1. Tool Type (ignored if the helper is a custom one) 2. Tool Name (ignored if the helper is a custom one) - [3.] Config helper - [4.] Prereqs (default to []). Can also be a function. + 3. createfn : helper function which build the actual tool. If none, we just instantiate the tool type. + 4. prereqs : Prerequisites (default to []). Can also be a function which returns a list + X. all other keyword arguments are directly interpreted as Property of the tool. + for ex, passing 'PtMin=10.' will configure the tool as in 'tool.PtMin = 10' + we can pass function as the value : + 'JetContainerName=nameFunc' will configure as in 'tool.JetContainerName=nameFunc(jetdef, modspec)' --> should this be by default? prefer to avoid ignored args """ from .JetDefinition import JetModifier +from .Utilities import ldict from AthenaConfiguration.ComponentFactory import CompFactory -jetmoddict = {} + +stdJetModifiers = ldict() ######################################################################## # Define the simple modifier setups here -- those defined in JetRec. - -jetrecmods = { - "Sort": JetModifier("JetSorter","jetsort"), - "Filter": JetModifier("JetFilterTool","jetptfilter_{modspec}", - # we give a function as PtMin : it will be evaluated when instantiating the tool (modspec will come alias usage like "Filter:10000" --> PtMin=100000) - PtMin = lambda _,modspec: int(modspec) ) # -} -jetmoddict.update (jetrecmods) +stdJetModifiers.update( + Sort = JetModifier("JetSorter","jetsort"), + Filter = JetModifier("JetFilterTool","jetptfilter_{modspec}", + # we give a function as PtMin : it will be evaluated when instantiating the tool (modspec will come alias usage like "Filter:10000" --> PtMin=100000) + PtMin = lambda _,modspec: int(modspec) ) # +) ######################################################################## -# Below, we populate the jetmoddict with modifier definitions for tools +# Below, we populate the stdJetModifiers with modifier definitions for tools # that are defined in other packages. # When necessary, the helper functions 'createfn' for tools from package 'PackageName' live in modules called # PackageName.PackageConfig (modules to be moved) # Calibration from JetCalibTools import JetCalibToolsConfig -jetcalibmods = { - "Calib": JetModifier("JetCalibrationTool","jetcalib_jetcoll_calibseq", - createfn=JetCalibToolsConfig.getJetCalibToolFromString, - prereqs=JetCalibToolsConfig.getJetCalibToolPrereqs) - } -jetmoddict.update(jetcalibmods) +stdJetModifiers.update( + Calib = JetModifier("JetCalibrationTool","jetcalib_jetcoll_calibseq", + createfn=JetCalibToolsConfig.getJetCalibToolFromString, + prereqs=JetCalibToolsConfig.getJetCalibToolPrereqs) +) # TBD: # All items below in principle will support decoration mode, rather @@ -54,78 +59,88 @@ jetmoddict.update(jetcalibmods) # Many JetMoment tools need to know the name of the container they operate on. -# We set this function as the 'JetContainer' property so the config system can assign the right name to the c++ tool. +# We set the function below as the 'JetContainer' property so the config system +# can assign the right name to the c++ tool. def _jetname(jetdef,modspec): return jetdef.fullname() +def isMC(flags): + """A simple filter function for testing if we're running in MC + returns (bool, str) where the str contains an explanation of why the bool is False. + (probably worth re-allocating somehere else)""" + return flags.Input.isMC, "Input file is not MC" + + # Standard jet moments from JetMomentTools import JetMomentToolsConfig -jetmomentmods = { +stdJetModifiers.update( # Easy cases, no special config or prereqs, just default tool config - "ClusterMoments": JetModifier("JetClusterMomentsTool", "clsmoms", JetContainer = _jetname), - "ECPSFrac": JetModifier("JetECPSFractionTool", "ecpsfrac", JetContainer = _jetname), - "Width": JetModifier("JetWidthTool", "width", JetContainer = _jetname), + ClusterMoments = JetModifier("JetClusterMomentsTool", "clsmoms", JetContainer = _jetname), + ECPSFrac = JetModifier("JetECPSFractionTool", "ecpsfrac", JetContainer = _jetname), + Width = JetModifier("JetWidthTool", "width", JetContainer = _jetname), # More complex cases here - "CaloEnergies": JetModifier("JetCaloEnergies", "jetens", + CaloEnergies = JetModifier("JetCaloEnergies", "jetens", prereqs=["mod:EMScaleMom"], JetContainer = _jetname, ), - "CaloQuality": JetModifier("JetCaloQualityTool", "caloqual", + CaloQuality = JetModifier("JetCaloQualityTool", "caloqual", TimingCuts = [5,10], Calculations = ["LArQuality", "N90Constituents", "FracSamplingMax", "NegativeE", "Timing", "HECQuality", "Centroid", "AverageLArQF", "BchCorrCell"],JetContainer = _jetname), - "ConstitFourMom": JetModifier("JetConstitFourMomTool", "constitfourmom_basename", - createfn=JetMomentToolsConfig.getConstitFourMomTool,), - "EMScaleMom": JetModifier("JetEMScaleMomTool", "emscalemom_basename", - createfn=JetMomentToolsConfig.getEMScaleMomTool,), + ConstitFourMom = JetModifier("JetConstitFourMomTool", "constitfourmom_basename", + createfn=JetMomentToolsConfig.getConstitFourMomTool,), + EMScaleMom = JetModifier("JetEMScaleMomTool", "emscalemom_basename", + createfn=JetMomentToolsConfig.getEMScaleMomTool, + JetContainer = _jetname), - "JVF": JetModifier("JetVertexFractionTool", "jvf", + JVF = JetModifier("JetVertexFractionTool", "jvf", createfn=JetMomentToolsConfig.getJVFTool, - prereqs = ["mod:TrackMoments"] ), - "JVT": JetModifier("JetVertexTaggerTool", "jvt", + prereqs = ["mod:TrackMoments"] ,JetContainer = _jetname), + JVT = JetModifier("JetVertexTaggerTool", "jvt", createfn=JetMomentToolsConfig.getJVTTool, - prereqs = [ "mod:JVF" ]), - "LArHVCorr": JetModifier("JetLArHVTool", "larhvcorr", + prereqs = [ "mod:JVF" ],JetContainer = _jetname), + LArHVCorr = JetModifier("JetLArHVTool", "larhvcorr", prereqs = ["mod:EMScaleMom"],JetContainer = _jetname), - "OriginSetPV": JetModifier("JetOriginCorrectionTool", "origin_setpv", - prereqs = [ "mod:JVF" ]), - "TrackMoments": JetModifier("JetTrackMomentsTool", "trkmoms", - createfn=JetMomentToolsConfig.getTrackMomentsTool, - prereqs = [ "input:JetTrackUsedInFitDeco","input:JetTrackVtxAssoc","ghost:Track" ]), - - "TrackSumMoments": JetModifier("JetTrackSumMomentsTool", "trksummoms", - createfn=JetMomentToolsConfig.getTrackSumMomentsTool, - prereqs = [ "input:JetTrackUsedInFitDeco","input:JetTrackVtxAssoc","ghost:Track" ]), - "Charge" : JetModifier("JetChargeTool", "jetcharge", - prereqs = [ "ghost:Track" ]), -} -jetmoddict.update(jetmomentmods) + OriginSetPV = JetModifier("JetOriginCorrectionTool", "origin_setpv", + prereqs = [ "mod:JVF" ],JetContainer = _jetname), + TrackMoments = JetModifier("JetTrackMomentsTool", "trkmoms", + createfn=JetMomentToolsConfig.getTrackMomentsTool, + prereqs = [ "input:JetTrackVtxAssoc","ghost:Track" ],JetContainer = _jetname), + + TrackSumMoments = JetModifier("JetTrackSumMomentsTool", "trksummoms", + createfn=JetMomentToolsConfig.getTrackSumMomentsTool, + prereqs = [ "input:JetTrackVtxAssoc","ghost:Track" ],JetContainer = _jetname), + Charge = JetModifier("JetChargeTool", "jetcharge", + prereqs = [ "ghost:Track" ]), +) # Truth labelling moments from ParticleJetTools import ParticleJetToolsConfig -particlejetmods = { +stdJetModifiers.update( # Easy cases, no special config or prereqs, just default tool config - "PartonTruthLabel": JetModifier("Analysis::JetPartonTruthLabel","partontruthlabel", + PartonTruthLabel = JetModifier("Analysis::JetPartonTruthLabel","partontruthlabel", prereqs=["ghost:Partons"]), # More complex cases here - "TruthPartonDR": JetModifier("Analysis::JetConeLabeling","truthpartondr", - JetTruthMatchTool = lambda *l : CompFactory.Analysis.JetQuarkLabel("jetquarklabel", McEventCollection='TruthEvents') ), + TruthPartonDR = JetModifier("Analysis::JetConeLabeling","truthpartondr", + filterfn=isMC, + JetTruthMatchTool = lambda *l : CompFactory.Analysis.JetQuarkLabel("jetquarklabel", McEventCollection='TruthEvents'), + ), - "JetDeltaRLabel": JetModifier("ParticleJetDeltaRLabelTool","jetdrlabeler_jetptmin", - createfn=ParticleJetToolsConfig.getJetDeltaRLabelTool, - prereqs=["ghost:BHadronsFinal", - "ghost:CHadronsFinal", - "ghost:TausFinal"]) - } -jetmoddict.update(particlejetmods) + JetDeltaRLabel = JetModifier("ParticleJetDeltaRLabelTool","jetdrlabeler_jetptmin", + createfn=ParticleJetToolsConfig.getJetDeltaRLabelTool, + prereqs=["ghost:BHadronsFinal", + "ghost:CHadronsFinal", + "ghost:TausFinal"]) +) + -# Substructure tools -substrmods = dict( +# Substructure tools +stdJetModifiers.update( nsubjettiness = JetModifier( "NSubjettinessTool", "nsubjettiness",Alpha = 1.0), nsubjettinessR = JetModifier( "NSubjettinessRatiosTool", "nsubjettinessR",), @@ -143,10 +158,9 @@ substrmods = dict( ktmassdrop = JetModifier( "KtMassDropTool", "ktmassdrop"), ecorr = JetModifier( "EnergyCorrelatorTool", "ecorr", Beta = 1.0), - ecorrR = JetModifier( "EnergyCorrelatorRatiosTool", "ecorrR", ), + ecorrR = JetModifier( "EnergyCorrelatorRatiosTool", "ecorrR", ), ecorrgeneral = JetModifier( "EnergyCorrelatorGeneralizedTool", "ecorrgeneral"), - ecorrgeneralratios = JetModifier( "EnergyCorrelatorGeneralizedRatiosTool", "ecorrgeneralratios"), comshapes = JetModifier( "CenterOfMassShapesTool","comshapes"), @@ -158,4 +172,3 @@ substrmods = dict( qw = JetModifier( "QwTool", "qw"), #showerdec = JetModifier( " ShowerDeconstructionTool"), ) -jetmoddict.update(substrmods) diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardLargeRJets.py b/Reconstruction/Jet/JetRecConfig/python/StandardLargeRJets.py index 8b8549d6d66d2c27a4ec51e07a3ae23a5500c141..47fffe614612a4dffb77cf35b2f1aff2adabd649 100644 --- a/Reconstruction/Jet/JetRecConfig/python/StandardLargeRJets.py +++ b/Reconstruction/Jet/JetRecConfig/python/StandardLargeRJets.py @@ -1,6 +1,6 @@ -# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration -from JetRecConfig.StandardJetConstits import jetconstitdic as cst +from JetRecConfig.StandardJetConstits import stdConstitDic as cst from .JetDefinition import JetDefinition from .JetGrooming import JetTrimming, JetSoftDrop diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py b/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py index 68b70fc3796ab911f947ffd66ee933bc8f8b4464..4fc500b0d7217c6a299862ea2ff0df79c1428ffa 100644 --- a/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py +++ b/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py @@ -1,6 +1,6 @@ # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration -from JetRecConfig.StandardJetConstits import jetconstitdic as cst +from JetRecConfig.StandardJetConstits import stdConstitDic as cst from .JetDefinition import JetDefinition diff --git a/Reconstruction/Jet/JetRecConfig/python/Utilities.py b/Reconstruction/Jet/JetRecConfig/python/Utilities.py index 6f1a29cc0d6206912eb0175367254287571d5afc..2ecf54632bd9404387dbf07bbbad2a2a55df26f8 100644 --- a/Reconstruction/Jet/JetRecConfig/python/Utilities.py +++ b/Reconstruction/Jet/JetRecConfig/python/Utilities.py @@ -109,13 +109,20 @@ def onlyAttributesAreProperties(cls): def clonable(cls): """Transforms the input class cls by adding a clone() method. This clone() method returns a clone instance with a _locked attribute set to False by default (so the clone is modifiable) """ + + def recc_lock(o, lock): + if hasattr(o,"_locked"): + o._locked = lock + for k,v in o.__dict__.items(): + recc_lock(v,lock) + def clone(self, lock=False, **args): from copy import deepcopy o = deepcopy(self) o._locked = False # unlock so we can modfiy the clone with user given arguments for k,v in args.items(): setattr(o,k,v) - o._locked = lock + recc_lock(o,lock) # make sure lock is propagated to all lockable sub-object return o cls.clone = clone return cls @@ -123,9 +130,18 @@ def clonable(cls): class ldict(dict): """A dictionnary which items can not be modified once set. - - Also its items are attributes (Main/only motivation : easier interactive inspection) + + Also implements 2 additional features : + - a clone method to conveniently create a new version with only some items changed + - all items are also attributes (i.e. d['a'] can be accessed by d.a : this is mostly for easier interactive inspection) """ + def __init__(self, **args): + super().__init__(**args) + # make key aivailable as attribute + for k,v in args.items(): + super().__setattr__(k,v) + + def __setitem__(self, k, v): if k in self: raise KeyError("Can't override key "+k) @@ -135,3 +151,29 @@ class ldict(dict): def update(self, **args): # we need to redefine it for k,v in args.items(): self[k]=v + + def clone(self,**args): + from copy import deepcopy + o = deepcopy(self) + for k,v in args.items(): + # bypass the protection to update the user given items + dict.__setitem__(o,k,v) + dict.__setattr__(o,k,v) + return o + +def flattenDic(inD): + """returns a copy of the inD dictionnary with no nested directories. Instead the content of nested dict inside inD are indexed by keys constructed from nested keys : + {'A' : 3 , 'B' : { 'C':5, 'D': 6} } --> {'A' : 3 , 'B.C':5, 'B.D': 6} + This works only if nested dic have str keys. + + This function is used mainly in trigger config + """ + outD = {} + for k,v in inD.items(): + if isinstance(v,dict): + subD = flattenDic(v) + for subk, subv in subD.items(): + outD[f'{k}.{subk}']=subv + else: + outD[k]=v + return outD diff --git a/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py b/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py deleted file mode 100755 index 1322fcf541350c158fd50ca8b0ad3a720714751d..0000000000000000000000000000000000000000 --- a/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env python -from JetRecConfig import JetRecConfig -from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator - -from pprint import pprint, pformat - -from AthenaCommon import Logging -jetlog = Logging.logging.getLogger("testJetRecDeps") - -def DefineJetCollections(configFlags): - - ######################################################################## - # import the standard definitions - from JetRecConfig.StandardSmallRJets import AntiKt4EMPFlow, AntiKt4EMTopo, AntiKt4Truth, AntiKt4TruthWZ - from JetRecConfig.StandardLargeRJets import AntiKt10LCTopoSoftDrop - - # Example for defining a custom definition - from JetRecConfig.JetDefinition import JetConstitSeq, JetDefinition, xAODType - from JetRecConfig.StandardSmallRJets import standardrecomods, clustermods, truthmods, standardghosts - EMTopoCSSK = JetConstitSeq("EMTopoOriginCSSK", xAODType.CaloCluster, ["EM","Origin","CS","SK"], "CaloCalTopoClusters", "EMOriginTopoCSSK", label="EMTopoOriginCSSK") - AntiKt4EMTopoCSSK = JetDefinition("AntiKt",0.4,EMTopoCSSK,ptmin=2e3,prefix="New") - recomods = tuple( mod for mod in standardrecomods if not mod.startswith("Calib") ) # remove calib modifier : no calib exists for EMTopoCSSK - AntiKt4EMTopoCSSK.modifiers = ["Filter:2000","ConstitFourMom"] - AntiKt4EMTopoCSSK.modifiers += recomods + clustermods + truthmods - AntiKt4EMTopoCSSK.ghostdefs = standardghosts - AntiKt4EMTopoCSSK.extrainputs = ["EventDensity"] - - ######################################################################## - # We can also clone and modify an standard definition - from JetRecConfig.StandardJetConstits import jetconstitdic - AntiKt4EMPFlowCSSK = AntiKt4EMPFlow.clone(prefix="New") - AntiKt4EMPFlowCSSK.inputdef = jetconstitdic["EMPFlowCSSK"] - AntiKt4EMPFlowCSSK.modifiers = ["Filter:2000","ConstitFourMom"] - AntiKt4EMPFlowCSSK.extrainputs = ["EventDensity"] - - jetdefs = [ - # we re-clone the standard with a prefix to avoid conflicts with pre-existing in-file collections - AntiKt4EMTopo.clone(prefix="New"), - AntiKt4EMPFlow.clone(prefix="New"), - AntiKt4EMTopoCSSK, - AntiKt4EMPFlowCSSK, - AntiKt10LCTopoSoftDrop, - ] - if configFlags.Input.isMC: - jetdefs += [AntiKt4Truth.clone(prefix="New"), - AntiKt4TruthWZ.clone(prefix="New"),] - - return jetdefs - -if __name__=="__main__": - from argparse import ArgumentParser - parser = ArgumentParser(prog="JetRecTestCfg: An example configuration module for jet reconstruction", - usage="Call with an input file, pass -n=0 to skip execution, -t 0 for serial or 1 for threaded execution.") - # - parser.add_argument("-H", "--Help", default=False, action="store_true", help="Evidently pyROOT interferes with help :(") - # - parser.add_argument("-f", "--filesIn", type=str, help="Comma-separated list of input files") - parser.add_argument("-M", "--msgLvl", default="INFO", help="The message verbosity") - parser.add_argument("-n", "--nEvents", default=0, type=int, help="The number of events to run. 0 skips execution") - # - parser.add_argument("-t", "--nThreads", default=1, type=int, help="The number of concurrent threads to run. 0 uses serial Athena.") - # - parser.add_argument("-D", "--dumpSG", default=False, action="store_true", help="Toggle StoreGate dump on each event") - parser.add_argument("-d", "--printDependencies", default=False, action="store_true", help="Print the dependency dicts for each jet collection") - parser.add_argument("-a", "--printAccumulators", default=False, action="store_true", help="Print the component accumulators for each jet collection separately") - parser.add_argument("-V", "--verboseAccumulators", default=False, action="store_true", help="Print full details of the AlgSequence for each accumulator") - args = parser.parse_args() - - if args.Help: - parser.print_help() - import sys - sys.exit(0) - - # Setting needed for the ComponentAccumulator to do its thing - from AthenaCommon.Configurable import Configurable - Configurable.configurableRun3Behavior=True - - # Set message levels - from AthenaCommon import Constants - msgLvl = getattr(Constants,args.msgLvl) - from AthenaCommon.Logging import log - log.setLevel(msgLvl) - - # Config flags steer the job at various levels - from AthenaConfiguration.AllConfigFlags import ConfigFlags - ConfigFlags.Input.Files = args.filesIn.split(",") - - # Flags relating to multithreaded execution - ConfigFlags.Concurrency.NumThreads = args.nThreads - if args.nThreads>0: - ConfigFlags.Scheduler.ShowDataDeps = True - ConfigFlags.Scheduler.ShowDataFlow = True - ConfigFlags.Scheduler.ShowControlFlow = True - ConfigFlags.Concurrency.NumConcurrentEvents = args.nThreads - - # Prevent the flags from being modified - ConfigFlags.lock() - - # Get a ComponentAccumulator setting up the fundamental Athena job - from AthenaConfiguration.MainServicesConfig import MainServicesCfg - cfg=MainServicesCfg(ConfigFlags) - - # Add the components for reading in pool files - from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg - cfg.merge(PoolReadCfg(ConfigFlags)) - - # Nowadays the jet calibration tool requires the EventInfo - # to be decorated with lumi info, which is not in Run 2 AODs - from LumiBlockComps.LuminosityCondAlgConfig import LuminosityCondAlgCfg - cfg.merge(LuminosityCondAlgCfg(ConfigFlags)) - - from AthenaConfiguration.ComponentFactory import CompFactory - muWriter = CompFactory.LumiBlockMuWriter("LumiBlockMuWriter",LumiDataKey="LuminosityCondData") - cfg.addEventAlgo(muWriter,"AthAlgSeq") - - ######################################################################## - # Define flags steering the jet reco config - jetdefs = DefineJetCollections(ConfigFlags) - - # Add the components from our jet reconstruction job - from JetRecConfig.JetRecConfig import JetRecCfg - for jetdef in jetdefs: - comp = JetRecCfg(jetdef,ConfigFlags) - comp.printConfig(withDetails=args.verboseAccumulators,summariseProps=True) - - cfg.merge(comp) - - # Write what we produced to AOD - # First define the output list - outputlist = ["EventInfo#*"] - originaljets = ["AntiKt4EMPFlowJets","AntiKt4EMTopoJets"] - for jetcoll in originaljets: - outputlist += ["xAOD::JetContainer#"+jetcoll, - "xAOD::JetAuxContainer#"+jetcoll+"Aux.-PseudoJet"] - for jetdef in jetdefs: - key = "{0}{1}Jets".format("New",jetdef.basename) - outputlist += ["xAOD::JetContainer#"+key, - "xAOD::JetAuxContainer#"+key+"Aux.-PseudoJet"] - - # Now get the output stream components - from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg - cfg.merge(OutputStreamCfg(ConfigFlags,"xAOD",ItemList=outputlist)) - pprint( cfg.getEventAlgo("OutputStreamxAOD").ItemList ) - - # Optionally, print the contents of the store every event - cfg.getService("StoreGateSvc").Dump = args.dumpSG - - # Save this configuration to a pickle file - f=open("JetRecTest.pkl","wb") - cfg.store(f) - f.close() - - # Run the job - cfg.run(maxEvents=args.nEvents) diff --git a/Reconstruction/Jet/JetRecConfig/share/README.md b/Reconstruction/Jet/JetRecConfig/share/README.md index 3a768c83199d87b5687f66bfc8e8e4287f13615c..b0a538db09557792c8e73fe66bd7068dcaf02785 100644 --- a/Reconstruction/Jet/JetRecConfig/share/README.md +++ b/Reconstruction/Jet/JetRecConfig/share/README.md @@ -1,20 +1,3 @@ # JetRecConfig/share ## JobOptions contents - -### JetRecTestCfg.py - -This module encodes an example/test job for jet reconstruction, -which builds a small number of standard jet collections and writes -them to an output xAOD file. - -Call `python JetRecTestCfg.py -H` for help. - -### test_JetDefinitions_properties.py - -This script tests whether jet definition properties correctly -update all necessary information when they are altered by -a user, for example when using a deepcopy-update pattern to -construct new JetConstit or JetDefinition instances. - -Should be moved to a test/ dir and turned into a unit test. diff --git a/Reconstruction/Jet/JetRecConfig/share/test_JetDefinitions_properties.py b/Reconstruction/Jet/JetRecConfig/share/test_JetDefinitions_properties.py deleted file mode 100644 index b614b69554bd667cb5d043b6d3b04efd8a171030..0000000000000000000000000000000000000000 --- a/Reconstruction/Jet/JetRecConfig/share/test_JetDefinitions_properties.py +++ /dev/null @@ -1,58 +0,0 @@ -# A test of the property setting/getting on JetDefinition classes -# We want the defining attributes on each class to trigger resets of -# derived attributes, such as container names, labels etc - -def printJetConstit(name, jetconstit): - print "{0} constituents".format(name) - print " basetype: {0}".format(jetconstit.basetype) - print " modifiers list: {0}".format(str(jetconstit.modifiers)) - print " label: {0}".format(jetconstit.label) - print " input container: {0}".format(jetconstit.inputname) - print "" - -def printJetDefinition(name, jetdef): - print "{0} jets".format(name) - print " algorithm: {0}".format(jetdef.algorithm) - print " radius: {0}".format(jetdef.radius) - print " input label: {0}".format(jetdef.inputdef.label) - print " basename: {0}".format(jetdef.basename) - print " modifiers list: {0}".format(str(jetdef.modifiers)) - print "" - -# Start with a basic constituent and print some information -from JetRecConfig.JetDefinition import * -CHSPFlow = JetConstit(xAOD.Type.ParticleFlow, []) -printJetConstit("CHSPFlow",CHSPFlow) - -# Now copy and progressively change some of the aspects -from copy import deepcopy -CSSKPFlow = deepcopy(CHSPFlow) -printJetConstit("CSSKPFlow (fresh copy from CHSPFlow)",CSSKPFlow) -CSSKPFlow.modifiers += ["CS","SK"] -printJetConstit("CSSKPFlow (set mods)",CSSKPFlow) - -CSSKLCTopo = deepcopy(CSSKPFlow) -printJetConstit("CSSKLCTopo (fresh copy from CSSKPFlow)",CSSKLCTopo) -CSSKLCTopo.basetype = xAOD.Type.CaloCluster -printJetConstit("CSSKLCTopo (set input type)",CSSKLCTopo) - -# Now we do this with a basic jet definition -AntiKt4EMPFlow = JetDefinition("AntiKt",0.4,CHSPFlow,ptminfilter=10e3) -AntiKt4EMPFlow.modifiers = ["Calib:T0:mc","Sort"] -printJetDefinition("AntiKt4EMPFlow",AntiKt4EMPFlow) - -AntiKt4EMPFlowCSSK = deepcopy(AntiKt4EMPFlow) -printJetDefinition("AntiKt4EMPFlowCSSK (fresh copy from AntiKt4EMPFlow)",AntiKt4EMPFlowCSSK) -AntiKt4EMPFlowCSSK.inputdef = CSSKPFlow -AntiKt4EMPFlowCSSK.modifiers = ["JetConstitFourMomTool"] -printJetDefinition("AntiKt4EMPFlowCSSK (update input def)",AntiKt4EMPFlowCSSK) - -AntiKt10EMPFlowCSSK = deepcopy(AntiKt4EMPFlowCSSK) -printJetDefinition("AntiKt10EMPFlowCSSK (fresh copy from AntiKt4EMPFlowCSSK)",AntiKt10EMPFlowCSSK) -AntiKt10EMPFlowCSSK.radius = 1.0 -printJetDefinition("AntiKt10EMPFlowCSSK (update radius)",AntiKt10EMPFlowCSSK) - -CamKt10EMPFlowCSSK = deepcopy(AntiKt10EMPFlowCSSK) -printJetDefinition("CamKt10EMPFlowCSSK (fresh copy from AntiKt10EMPFlowCSSK)",CamKt10EMPFlowCSSK) -CamKt10EMPFlowCSSK.algorithm = "CamKt" -printJetDefinition("CamKt10EMPFlowCSSK (update radius)",CamKt10EMPFlowCSSK) diff --git a/Reconstruction/Jet/JetRecConfig/share/test_StandardSmallRJets.py b/Reconstruction/Jet/JetRecConfig/share/test_StandardSmallRJets.py deleted file mode 100644 index 613248a9e5e192be1220f01464f390734b50bd72..0000000000000000000000000000000000000000 --- a/Reconstruction/Jet/JetRecConfig/share/test_StandardSmallRJets.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration - -from JetRecConfig.StandardSmallRJets import AntiKt4EMPFlow, AntiKt4LCTopo -from JetRecConfig.JetRecConfig import JetRecCfg, jetlog - -jetlog.setLevel(2) - -from pprint import pprint, pformat - -if __name__=="__main__": - from argparse import ArgumentParser - parser = ArgumentParser(prog="StandardTests: runs standard jet reconstruction from an ESD", - usage="Call with an input file, pass -n=0 to skip execution, -t 0 for serial or 1 for threaded execution.") - # - parser.add_argument("-H", "--Help", default=False, action="store_true", help="Evidently pyROOT interferes with help :(") - # - parser.add_argument("-f", "--filesIn", type=str, help="Comma-separated list of input files") - parser.add_argument("-M", "--msgLvl", default="INFO", help="The message verbosity") - parser.add_argument("-n", "--nEvents", default=0, type=int, help="The number of events to run. 0 skips execution") - # - parser.add_argument("-t", "--nThreads", default=1, type=int, help="The number of concurrent threads to run. 0 uses serial Athena.") - parser.add_argument("-D", "--dumpSG", default=False, action="store_true", help="Toggle StoreGate dump on each event") - # - args = parser.parse_args() - - if args.Help: - parser.print_help() - import sys - sys.exit(0) - - # Setting needed for the ComponentAccumulator to do its thing - from AthenaCommon.Configurable import Configurable - Configurable.configurableRun3Behavior=True - - # Set message levels - from AthenaCommon import Constants - msgLvl = getattr(Constants,args.msgLvl) - from AthenaCommon.Logging import log - log.setLevel(msgLvl) - - # Config flags steer the job at various levels - from AthenaConfiguration.AllConfigFlags import ConfigFlags - ConfigFlags.Input.Files = args.filesIn.split(",") - - # Flags relating to multithreaded execution - ConfigFlags.Concurrency.NumThreads = args.nThreads - if args.nThreads>0: - ConfigFlags.Scheduler.ShowDataDeps = True - ConfigFlags.Scheduler.ShowDataFlow = True - ConfigFlags.Scheduler.ShowControlFlow = True - ConfigFlags.Concurrency.NumConcurrentEvents = args.nThreads - - # Prevent the flags from being modified - ConfigFlags.lock() - - - - # Get a ComponentAccumulator setting up the fundamental Athena job - from AthenaConfiguration.MainServicesConfig import MainServicesCfg - cfg=MainServicesCfg(ConfigFlags) - - # Add the components for reading in pool files - from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg - cfg.merge(PoolReadCfg(ConfigFlags)) - - # Nowadays the jet calibration tool requires the EventInfo - # to be decorated with lumi info, which is not in Run 2 AODs - from LumiBlockComps.LuminosityCondAlgConfig import LuminosityCondAlgCfg - cfg.merge(LuminosityCondAlgCfg(ConfigFlags)) - - from AthenaConfiguration.ComponentFactory import CompFactory - muWriter = CompFactory.LumiBlockMuWriter("LumiBlockMuWriter",LumiDataKey="LuminosityCondData") - cfg.addEventAlgo(muWriter,"AthAlgSeq") - - - - # Add the components from our jet reconstruction job - jetdefs = [AntiKt4EMPFlow.clone(prefix="New") , AntiKt4LCTopo.clone(prefix="New") ] - for jetdef in jetdefs: - cfg.merge( JetRecCfg(jetdef,ConfigFlags) ) - - # Write what we produced to AOD - # First define the output list - outputlist = ["EventInfo#*"] - originaljets = ["AntiKt4EMPFlowJets","AntiKt4EMTopoJets"] - for jetcoll in originaljets: - outputlist += ["xAOD::JetContainer#"+jetcoll, - "xAOD::JetAuxContainer#"+jetcoll+"Aux.-PseudoJet"] - for jetdef in jetdefs: - key = jetdef.fullname() - outputlist += ["xAOD::JetContainer#"+key, - "xAOD::JetAuxContainer#"+key+"Aux.-PseudoJet"] - - # Now get the output stream components - from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg - cfg.merge(OutputStreamCfg(ConfigFlags,"xAOD",ItemList=outputlist)) - pprint( cfg.getEventAlgo("OutputStreamxAOD").ItemList ) - - # Optionally, print the contents of the store every event - cfg.getService("StoreGateSvc").Dump = args.dumpSG - - # Save this configuration to a pickle file - f=open("JetRecTest.pkl","wb") - cfg.store(f) - f.close() - - # Run the job - cfg.run(maxEvents=args.nEvents) diff --git a/Reconstruction/Jet/JetRecConfig/test/test_JetDefinitions_properties.py b/Reconstruction/Jet/JetRecConfig/test/test_JetDefinitions_properties.py new file mode 100755 index 0000000000000000000000000000000000000000..532ca82da1fa52cf0109478503f2fe214daf3c96 --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/test/test_JetDefinitions_properties.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration + +# A test of the property setting/getting on JetDefinition classes +# We want the defining attributes on each class to trigger resets of +# derived attributes, such as container names, labels etc + +from JetRecConfig.JetDefinition import * +import unittest + +def printJetConstit(jetconstit): + print(f"{jetconstit.name} constituents") + print(f" basetype: {jetconstit.basetype}") + print(f" modifiers list: {str(jetconstit.modifiers)}") + print(f" label: {jetconstit.label}") + print(f" input container: {jetconstit.inputname}") + if isinstance(jetconstit, JetInputConstitSeq): + print(f" output container: {jetconstit.containername}") + print() + + +def printJetDefinition(desc, jetdef): + print(f"{desc} jets") + print(f" fullname: {jetdef.fullname()}") + print(f" algorithm: {jetdef.algorithm}") + print(f" radius: {jetdef.radius}") + print(f" input label: {jetdef.inputdef.label}") + print(f" basename: {jetdef.basename}") + print(f" modifiers list: {str(jetdef.modifiers)}") + print() + +# Start with a basic constituent and print some information + +class TestJetDef(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.CHSPFlow = JetInputConstitSeq("chspflow",xAODType.ParticleFlow, [], + "InputPFlow", "OutputPFlow", jetinputtype="EMPFlow" + ) + cls.AntiKt4EMPFlow = JetDefinition("AntiKt",0.4,cls.CHSPFlow,ptmin=10e3,modifiers=["bla","bli"]) + + + def test_0build_JetInputConstitSeq(self): + printJetConstit(self.CHSPFlow) + + def test_1setattr_JetInputConstitSeq(self): + # make sure we can't set attributes which are not declared properties + with self.assertRaises(AttributeError): + # nameAA is not a property + self.CHSPFlow.nameAA = 'not set' + + def test_2clone_JetInputConstitSeq(self): + CSSKPFlow = self.CHSPFlow.clone(name="Simple chspflow clone") + printJetConstit(CSSKPFlow) + CSSKPFlow.modifiers += ["CS", "SK"] + print(" --> after modifiers change, modifiers=", CSSKPFlow.modifiers) + CSSKPFlow = self.CHSPFlow.clone( + name="chspflow clone with args", modifiers=["CS", "SK"] + ) + printJetConstit(CSSKPFlow) + + + # make sure locked containers are locked + CSSKPFlow_l = CSSKPFlow.clone(lock=True) + with self.assertRaises(Exception): + CSSKPFlow_l.modifiers = [] + + def test_3print_JetDef(self): + printJetDefinition("basic jetdef",self.AntiKt4EMPFlow) + + def test_4setattr_JetDef(self): + # make sure we can't set attributes which are not declared properties + with self.assertRaises(AttributeError): + # nameAA is not a property + self.AntiKt4EMPFlow.nameAA = 'not set' + + def test_4clone_JetDef(self): + Ak4cloned = self.AntiKt4EMPFlow.clone( modifiers = ["Calib:T0:mc","Sort"] ) + printJetDefinition("cloned jet def",Ak4cloned) + Ak4cloned_l = self.AntiKt4EMPFlow.clone( modifiers = ["Calib:T0:mc","Sort"] , lock=True) + printJetDefinition("cloned - locked jet def",Ak4cloned) + + self.assertEqual( Ak4cloned.modifiers , Ak4cloned_l.modifiers) + + with self.assertRaises(Exception): + Ak4cloned_l.modifiers = [] + + def test_5changename_JetDef(self): + Ak4 = self.AntiKt4EMPFlow + n0 = Ak4.fullname() + print(" -> jet fullname ",n0) + Ak4.radius = Ak4.radius*2 + n1 = Ak4.fullname() + print(" -> jet fullname ",n1) + Ak4.algorithm = 'Kt' + n2 = Ak4.fullname() + print(" -> jet fullname ",n2) + self.assertNotEqual(n0,n1) + self.assertNotEqual(n1,n2) + +if __name__ == '__main__': + unittest.main() diff --git a/Reconstruction/Jet/JetRecConfig/test/test_JetRecCfg_build.sh b/Reconstruction/Jet/JetRecConfig/test/test_JetRecCfg_build.sh deleted file mode 100755 index c6bc35229ead9eb70aa6ce4bf4ba6f95a9771843..0000000000000000000000000000000000000000 --- a/Reconstruction/Jet/JetRecConfig/test/test_JetRecCfg_build.sh +++ /dev/null @@ -1 +0,0 @@ -JetRecTestCfg.py -f /cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/ASG/mc16_13TeV.410501.PowhegPythia8EvtGen_A14_ttbar_hdamp258p75_nonallhad.merge.AOD.e5458_s3126_r9364_r9315/AOD.11182705._000001.pool.root.1 -n 10 diff --git a/Reconstruction/Jet/JetRecConfig/test/test_StandardJets.py b/Reconstruction/Jet/JetRecConfig/test/test_StandardJets.py new file mode 100755 index 0000000000000000000000000000000000000000..6767cf618a70a9ea189dbc41eed11e85ae04c60b --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/test/test_StandardJets.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration + + +# should choose a better default ?? +DEFAULT_INPUTFILE = "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/DerivationFrameworkART/AOD.14795494._005958.pool.root.1" + +from argparse import ArgumentParser +parser = ArgumentParser(prog="StandardTests: runs standard jet reconstruction from an ESD", + usage="Call with an input file, pass -n=0 to skip execution, -t 0 for serial or 1 for threaded execution.") +# +parser.add_argument("-H", "--Help", default=False, action="store_true", help="Evidently pyROOT interferes with help :(") +# +parser.add_argument("-f", "--filesIn", type=str, help="Comma-separated list of input files", + default=DEFAULT_INPUTFILE) +parser.add_argument("-M", "--msgLvl", default="INFO", help="The message verbosity") +parser.add_argument("-n", "--nEvents", default=10, type=int, help="The number of events to run. 0 skips execution") +# +parser.add_argument("-t", "--nThreads", default=1, type=int, help="The number of concurrent threads to run. 0 uses serial Athena.") +parser.add_argument("-D", "--dumpSG", default=False, action="store_true", help="Toggle StoreGate dump on each event") +parser.add_argument("-j", "--jetType", default="smallR", type=str, choices={"smallR","largeR", "cssk"}, + help="the type of jet definitions to test") + +# +args = parser.parse_args() + +if args.Help: + parser.print_help() + import sys + sys.exit(0) + + + +from pprint import pprint +from JetRecConfig.JetRecConfig import JetRecCfg, jetlog + +# Set message levels +from AthenaCommon import Logging, Constants +msgLvl = getattr(Constants,args.msgLvl) +Logging.log.setLevel(msgLvl) +tlog = Logging.logging.getLogger('test_StandardJets') + + +# Setting needed for the ComponentAccumulator to do its thing +from AthenaCommon.Configurable import Configurable +Configurable.configurableRun3Behavior=True + + +# Config flags steer the job at various levels +from AthenaConfiguration.AllConfigFlags import ConfigFlags +ConfigFlags.Input.Files = args.filesIn.split(",") + +# Flags relating to multithreaded execution +ConfigFlags.Concurrency.NumThreads = args.nThreads +if args.nThreads>0: + ConfigFlags.Scheduler.ShowDataDeps = True + ConfigFlags.Scheduler.ShowDataFlow = True + ConfigFlags.Scheduler.ShowControlFlow = True + ConfigFlags.Concurrency.NumConcurrentEvents = args.nThreads + +# Prevent the flags from being modified +ConfigFlags.lock() + + +# Get a ComponentAccumulator setting up the fundamental Athena job +from AthenaConfiguration.MainServicesConfig import MainServicesCfg +cfg=MainServicesCfg(ConfigFlags) + + +# *********************************************** +# Prepare the JetDefinition to be tested +if args.jetType=='smallR': + from JetRecConfig.StandardSmallRJets import AntiKt4EMPFlow, AntiKt4LCTopo, AntiKt4Truth + jetdefs0 = [AntiKt4EMPFlow , AntiKt4LCTopo, AntiKt4Truth] + # we add a suffix to avoid conflicts with in-file existing jets + jetdefs =[ d.clone(prefix="New") for d in jetdefs0 ] + alljetdefs = jetdefs0+jetdefs + +elif args.jetType=='largeR': + from JetRecConfig.StandardLargeRJets import AntiKt10LCTopoTrimmed, AntiKt10TruthTrimmed, AntiKt10LCTopoSoftDrop + jetdefs = [ AntiKt10LCTopoTrimmed, AntiKt10TruthTrimmed , AntiKt10LCTopoSoftDrop ] + alljetdefs = jetdefs + +elif args.jetType=='cssk': + from JetRecConfig.StandardSmallRJets import AntiKt4LCTopo,AntiKt4EMPFlow + from JetRecConfig.StandardJetConstits import stdConstitDic as cst + # remove the calib tool from modifiers, because no standard calib defined for the custom def below + nocalibL = [m for m in AntiKt4LCTopo.modifiers if m!="Calib:T0:mc" ] + AntiKt4LCTopoCSSK = AntiKt4LCTopo.clone(inputdef = cst.LCTopoCSSK, modifiers=nocalibL ) + nocalibL = [m for m in AntiKt4EMPFlow.modifiers if m!="Calib:T0:mc" ] + AntiKt4EMPFlowCSSK = AntiKt4EMPFlow.clone(inputdef = cst.EMPFlowCSSK, modifiers=nocalibL ) + jetdefs = [AntiKt4LCTopoCSSK,AntiKt4EMPFlowCSSK] + alljetdefs = jetdefs + +# *********************************************** +if args.nEvents == 0: + # Don't run over events --> just run the jet config. + # Add the components from our jet reconstruction job + for jetdef in jetdefs: + cfg.merge( JetRecCfg(jetdef,ConfigFlags) ) + import sys + tlog.info("Performed jet config. Exiting now") + sys.exit(0) + + + +# *********************************************** +# else setup a full job + +# Add the components for reading in pool files +from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg +cfg.merge(PoolReadCfg(ConfigFlags)) + +# Nowadays the jet calibration tool requires the EventInfo +# to be decorated with lumi info, which is not in Run 2 AODs +from LumiBlockComps.LuminosityCondAlgConfig import LuminosityCondAlgCfg +cfg.merge(LuminosityCondAlgCfg(ConfigFlags)) + +from AthenaConfiguration.ComponentFactory import CompFactory +muWriter = CompFactory.LumiBlockMuWriter("LumiBlockMuWriter",LumiDataKey="LuminosityCondData") +cfg.addEventAlgo(muWriter,"AthAlgSeq") + + + +# Add the components from our jet reconstruction job +for jetdef in jetdefs: + cfg.merge( JetRecCfg(jetdef,ConfigFlags) ) + + +# Now get the output stream components +outputlist = ["EventInfo#*"] +# append all these jets to our output file +for j in alljetdefs: + key = j.fullname() + outputlist += [f"xAOD::JetContainer#{key}", + f"xAOD::JetAuxContainer#{key}Aux.-PseudoJet"] + +from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg +cfg.merge(OutputStreamCfg(ConfigFlags,"xAOD",ItemList=outputlist)) +pprint( cfg.getEventAlgo("OutputStreamxAOD").ItemList ) + +# Optionally, print the contents of the store every event +cfg.getService("StoreGateSvc").Dump = args.dumpSG + +# Run the job +cfg.run(maxEvents=args.nEvents) diff --git a/Reconstruction/Jet/JetRecTools/CMakeLists.txt b/Reconstruction/Jet/JetRecTools/CMakeLists.txt index 2cad10978e282bfed461ce22292f9e083f75c7df..d9b6e59fd835d14210ccbc8abccb2362e45c5936 100644 --- a/Reconstruction/Jet/JetRecTools/CMakeLists.txt +++ b/Reconstruction/Jet/JetRecTools/CMakeLists.txt @@ -18,7 +18,7 @@ endif() # Component(s) in the package. atlas_add_library( JetRecToolsLib - JetRecTools/*.h Root/*.cxx + JetRecTools/*.h Root/*.cxx src/*.cxx PUBLIC_HEADERS JetRecTools INCLUDE_DIRS ${FASTJET_INCLUDE_DIRS} PRIVATE_INCLUDE_DIRS ${FASTJETCONTRIB_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} diff --git a/Reconstruction/Jet/JetRecTools/JetRecTools/JetRecToolsDict.h b/Reconstruction/Jet/JetRecTools/JetRecTools/JetRecToolsDict.h index dafb5aaf4fc851fce34e2ac9ec798f03fe0132d7..183d369f572a079d73ce090dc072a5ea82e6a1a1 100644 --- a/Reconstruction/Jet/JetRecTools/JetRecTools/JetRecToolsDict.h +++ b/Reconstruction/Jet/JetRecTools/JetRecTools/JetRecToolsDict.h @@ -19,5 +19,6 @@ #include "JetRecTools/PuppiWeightTool.h" #include "JetRecTools/SoftKillerWeightTool.h" #include "JetRecTools/VoronoiWeightTool.h" +#include "JetRecTools/JetTrackSelectionAlg.h" #endif //JETRECTOOLS_JETRECTOOLSDICT_H diff --git a/Reconstruction/Jet/JetRecTools/JetRecTools/JetTrackSelectionAlg.h b/Reconstruction/Jet/JetRecTools/JetRecTools/JetTrackSelectionAlg.h new file mode 100644 index 0000000000000000000000000000000000000000..00c0f0c26ed7228da6cbff068d3cb8c2df9d1a07 --- /dev/null +++ b/Reconstruction/Jet/JetRecTools/JetRecTools/JetTrackSelectionAlg.h @@ -0,0 +1,47 @@ +// this is a -*- C++ -*- file +/* + Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration +*/ + +//////////////////////////////////////////////////// +/// \class JetTrackSelectionAlg +/// +/// Algorithm tasked to create a a filtered collection of tracks using a InDetTrackSelectionTool. +/// The algorithm writes out a container in VIEW_ELEMENTS mode. +/// + +#ifndef JetTrackSelectionAlg_H +#define JetTrackSelectionAlg_H + +#include "AnaAlgorithm/AnaReentrantAlgorithm.h" +#include "AsgTools/ToolHandle.h" +#include "AsgDataHandles/ReadHandleKey.h" +#include "AsgDataHandles/WriteHandleKey.h" + +#include "InDetTrackSelectionTool/IInDetTrackSelectionTool.h" + +#include "xAODTracking/TrackParticleContainer.h" +#include "AthContainers/ConstDataVector.h" + +class IJetExecuteTool; + +class JetTrackSelectionAlg : public EL::AnaReentrantAlgorithm { + +public: + + JetTrackSelectionAlg(const std::string & n, ISvcLocator* l) : EL::AnaReentrantAlgorithm(n,l) {} + using EL::AnaReentrantAlgorithm::AnaReentrantAlgorithm; + + /// Athena algorithm's Hooks + StatusCode initialize() override; + StatusCode execute(const EventContext& ctx) const override; + +private: + ToolHandle<InDet::IInDetTrackSelectionTool> m_selector {this, "TrackSelector", "", "track selection tool"}; + + SG::ReadHandleKey<xAOD::TrackParticleContainer> m_input = {this, "InputContainer", "", "The input tracks"}; + SG::WriteHandleKey<ConstDataVector<xAOD::TrackParticleContainer>> m_output= {this, "OutputContainer", "JetSelectedTracks", "The output filtered tracks"}; + +}; + +#endif diff --git a/Reconstruction/Jet/JetRecTools/JetRecTools/selection.xml b/Reconstruction/Jet/JetRecTools/JetRecTools/selection.xml index b745c26c08136f8d71fedf2f2e649d9aafaf92f1..d10ca910e45109c5a67ff0f8550505a6269adcc0 100644 --- a/Reconstruction/Jet/JetRecTools/JetRecTools/selection.xml +++ b/Reconstruction/Jet/JetRecTools/JetRecTools/selection.xml @@ -14,5 +14,6 @@ <class name="PuppiWeightTool" /> <class name="SoftKillerWeightTool" /> <class name="VoronoiWeightTool" /> + <class name="JetTrackSelectionAlg" /> </lcgdict> diff --git a/Reconstruction/Jet/JetRecTools/python/JetRecToolsConfig.py b/Reconstruction/Jet/JetRecTools/python/JetRecToolsConfig.py index 73dfd3a6cefd1bd585fed3e872b6b94381acb497..ab6721e44bb9789bf6fd3b66bc9d9ecd8e965210 100644 --- a/Reconstruction/Jet/JetRecTools/python/JetRecToolsConfig.py +++ b/Reconstruction/Jet/JetRecTools/python/JetRecToolsConfig.py @@ -16,85 +16,87 @@ from AthenaConfiguration.ComponentFactory import CompFactory from JetRecConfig.JetRecConfig import isAthenaRelease -# May need to specify non-standard tracking collections, e.g. for trigger -# Testing code -- move to another module and perhaps allow extensions -# e.g. in a dedicated trigger collections module to keep online/offline -# code more factorised -trackcollectionmap = { - # Offline track collections - "": { - "Tracks": "InDetTrackParticles", - "JetTracks": "JetSelectedTracks", - "Vertices": "PrimaryVertices", - "TVA": "JetTrackVtxAssoc", - "GhostTracks": "PseudoJetGhostTrack", - "GhostTracksLabel": "GhostTrack", - } -} - -def getTrackSelTool(trkopt="",doWriteTracks=False, cutLevel="Loose", minPt=500): - - # Track selector needs its own hierarchical config getter in JetRecTools? - idtrackselloose = CompFactory.getComp("InDet::InDetTrackSelectionTool")( - "idtrackselloose", - CutLevel = cutLevel, - minPt = minPt, - ) + +def getIDTrackSelectionTool(trkOpt, **userProps): + from JetRecConfig.StandardJetContext import jetContextDic + # see the default options in jetContextDic from StandardJetContext.py + selProperties = jetContextDic[trkOpt]["trackSelOptions"].clone( **userProps) + idtracksel = CompFactory.getComp("InDet::InDetTrackSelectionTool")( + "idtracksel_"+trkOpt, + **selProperties ) + if os.environ.get("AtlasProject",None) != "AnalysisBase": # thes options can not be set in AnalysisBase. (but not setting them is equivalent to set them to False) - idtrackselloose.UseTrkTrackTools = False - idtrackselloose.Extrapolator = "" - idtrackselloose.TrackSummaryTool = "" + idtracksel.UseTrkTrackTools = False + idtracksel.Extrapolator = "" + idtracksel.TrackSummaryTool = "" + return idtracksel + +def getTrackSelAlg(trkOpt="default", ): + from JetRecConfig.StandardJetContext import jetContextDic + trkProperties = jetContextDic[trkOpt] + + # Get a InDetTrackSelectionTool, OVERWRITING the CutLevel : + idtracksel = getIDTrackSelectionTool(trkOpt, CutLevel=trkProperties['GhostTrackCutLevel']) + + return CompFactory.JetTrackSelectionAlg( "trackselalg", + TrackSelector = idtracksel, + InputContainer = trkProperties["Tracks"], + OutputContainer = trkProperties["JetTracks"], + ) + +def getTrackSelTool(trkOpt=""): + # this tool is still used by trk moment tools. + # it should be deprecated in favor of simply the InDet tool + idtrackselloose = getIDTrackSelectionTool(trkOpt) jettrackselloose = CompFactory.JetTrackSelectionTool( "jettrackselloose", Selector = idtrackselloose ) - # Should phase this out completely! - # Make a jet track selection alg - # Elsewhere just use the ID track tool directly - if doWriteTracks: - jettracksname = "JetSelectedTracks" - if trkopt: jettracksname += "_{}".format("trkopt") - jettrackselloose.InputContainer = trackcollectionmap[trkopt]["Tracks"] - jettrackselloose.OutputContainer = jettracksname return jettrackselloose -def getTrackVertexAssocTool(trkopt="", theSequence=None, ttva_opts = { "WorkingPoint" : "Custom", "d0_cut" : 2.0, "dzSinTheta_cut" : 2.0 }): - if trkopt: "_{}".format(trkopt) +def getTrackVertexAssocTool(trkOpt="", theSequence=None, ttva_opts = { "WorkingPoint" : "Custom", "d0_cut" : 2.0, "dzSinTheta_cut" : 2.0 }): + if trkOpt: "_{}".format(trkOpt) # Track-vertex association # This is to be deprecated # In fact can probably be switched already to match legacy master # but for a future MR from TrackVertexAssociationTool.getTTVAToolForReco import getTTVAToolForReco + from JetRecConfig.StandardJetContext import jetContextDic + trkProperties = jetContextDic[trkOpt] + idtvassoc = getTTVAToolForReco( "idloosetvassoc", - TrackContName = trackcollectionmap[trkopt]["Tracks"], - VertexContName = trackcollectionmap[trkopt]["Vertices"], + TrackContName = trkProperties["Tracks"], + VertexContName = trkProperties["Vertices"], returnCompFactory = True, add2Seq=theSequence, - addDecoAlg=isAthenaRelease(), + addDecoAlg= isAthenaRelease(), # ?? it seems mandatory ?? **ttva_opts ) jettvassoc = CompFactory.TrackVertexAssociationTool( "jettvassoc", - TrackParticleContainer = trackcollectionmap[trkopt]["Tracks"], - TrackVertexAssociation = trackcollectionmap[trkopt]["TVA"], - VertexContainer = trackcollectionmap[trkopt]["Vertices"], + TrackParticleContainer = trkProperties["Tracks"], + TrackVertexAssociation = trkProperties["TVA"], + VertexContainer = trkProperties["Vertices"], TrackVertexAssoTool = idtvassoc ) return jettvassoc -def getTrackUsedInFitTool(trkopt=""): - if trkopt: "_{}".format(trkopt) +def getTrackUsedInFitTool(trkOpt=""): + if trkOpt: "_{}".format(trkOpt) # InDet decorator tool: + from JetRecConfig.StandardJetContext import jetContextDic + + trkProperties = jetContextDic[trkOpt] IDUsedInFitTrkDecoTool = CompFactory.getComp("InDet::InDetUsedInFitTrackDecoratorTool")( "IDUsedInFitTrkDecoTool", - TrackContainer = trackcollectionmap[trkopt]["Tracks"], - VertexContainer = trackcollectionmap[trkopt]["Vertices"], + TrackContainer = trkProperties["Tracks"], + VertexContainer = trkProperties["Vertices"], AMVFVerticesDecoName = "TTVA_AMVFVertices_forReco", AMVFWeightsDecoName = "TTVA_AMVFWeights_forReco" ) diff --git a/Reconstruction/Jet/JetRecTools/src/JetTrackSelectionAlg.cxx b/Reconstruction/Jet/JetRecTools/src/JetTrackSelectionAlg.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5cd4bd1fff80cdceefda7e7d4d7879cf1fe82aaa --- /dev/null +++ b/Reconstruction/Jet/JetRecTools/src/JetTrackSelectionAlg.cxx @@ -0,0 +1,51 @@ +/* + Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration +*/ + + +#include "JetRecTools/JetTrackSelectionAlg.h" +#include "AsgDataHandles/ReadHandle.h" +#include "AsgDataHandles/WriteHandle.h" + + +StatusCode JetTrackSelectionAlg::initialize() { + ATH_MSG_DEBUG("Initializing " ); + + + StatusCode sc = m_selector.retrieve(); + if (sc.isFailure()) {ATH_MSG_ERROR("Can't retrieve ITrackSelectorTool "<< m_selector.name() ); return sc;} + + ATH_CHECK(m_input.initialize()); + ATH_CHECK(m_output.initialize()); + + return StatusCode::SUCCESS; +} + +StatusCode JetTrackSelectionAlg::execute(const EventContext& ctx) const { + ATH_MSG_DEBUG(" execute() ... "); + + auto inputTracksH = SG::makeHandle(m_input,ctx); + if (! inputTracksH.isValid()){ + ATH_MSG_ERROR("Can't retrieve input track container "<< m_input.key()); + return StatusCode::FAILURE; + } + + + using OutContType = ConstDataVector<xAOD::TrackParticleContainer>; + auto selectedTracks = std::make_unique<OutContType>(SG::VIEW_ELEMENTS); + + for ( const xAOD::TrackParticle* trk : *inputTracksH.cptr() ) { + if ( trk != 0 && m_selector->accept(*trk, 0) ) { + selectedTracks->push_back(trk); + } + } + + + auto handle_out = SG::makeHandle(m_output, ctx); + + if (!handle_out.record(std::move(selectedTracks)) ){ + ATH_MSG_ERROR("Can't record output track container "<< m_output.key()); + return StatusCode::FAILURE; + } + return StatusCode::SUCCESS; +} diff --git a/Reconstruction/Jet/JetRecTools/src/components/JetRecTools_entries.cxx b/Reconstruction/Jet/JetRecTools/src/components/JetRecTools_entries.cxx index eb49139f1c1361aa7fea5589218aa7db5a7caf3e..599ffeb76b339cef6bcc5825eef825e0ba82dfe3 100644 --- a/Reconstruction/Jet/JetRecTools/src/components/JetRecTools_entries.cxx +++ b/Reconstruction/Jet/JetRecTools/src/components/JetRecTools_entries.cxx @@ -14,6 +14,8 @@ #include "JetRecTools/CorrectPFOTool.h" #include "JetRecTools/ChargedHadronSubtractionTool.h" #include "JetRecTools/PuppiWeightTool.h" +#include "JetRecTools/JetTrackSelectionAlg.h" + #if !defined(XAOD_ANALYSIS) #include "JetRecTools/JetUsedInFitTrackDecoratorTool.h" #endif @@ -35,6 +37,7 @@ DECLARE_COMPONENT( JetInputElRemovalTool ) DECLARE_COMPONENT( CorrectPFOTool ) DECLARE_COMPONENT( ChargedHadronSubtractionTool ) DECLARE_COMPONENT( PuppiWeightTool ) +DECLARE_COMPONENT( JetTrackSelectionAlg ) #if !defined(XAOD_ANALYSIS) DECLARE_COMPONENT( JetUsedInFitTrackDecoratorTool ) diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py index a576c422fea62ba054f8f46fc678a935cfb30e3c..b464795a1bcbe46c9986aceb3eeb6588c3ec6575 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py @@ -7,9 +7,9 @@ # and translate it into the python configuration objects used by # jet reco code. -from JetRecConfig.JetDefinition import JetConstitSeq,JetConstitSource, xAODType, JetDefinition +from JetRecConfig.JetDefinition import JetInputConstitSeq,JetInputConstit, xAODType, JetDefinition # this is to define trigger specific JetModifiers (ex: ConstitFourMom_copy) : -from .TriggerJetMods import jetmoddict # noqa: F401 +from .TriggerJetMods import stdJetModifiers # noqa: F401 from AthenaCommon.Logging import logging log = logging.getLogger(__name__) @@ -67,6 +67,7 @@ def extractRecoDict(chainParts): return recoDict + # Translate the reco dict to a string for suffixing etc def jetRecoDictToString(jetRecoDict): strtemp = "{recoAlg}_{constitMod}{constitType}_{clusterCalib}_{jetCalib}_{cleaning}" @@ -110,11 +111,7 @@ def defineJetConstit(jetRecoDict,clustersKey=None,pfoPrefix=None): if pfoPrefix is None: raise RuntimeError("JetRecoConfiguration: Cannot define PF jets without pfo prefix!") - trkopt = jetRecoDict['trkopt'] - from JetRecConfig.ConstModHelpers import constitModWithAlternateTrk - # Generate a new JetConstitModifier with track proterties setup according to trkopt - constitModWithAlternateTrk("CorrectPFO", trkopt) - constitMods = ["CorrectPFO"+trkopt] + constitMods = ["CorrectPFO"] # apply constituent pileup suppression if "vs" in jetRecoDict["constitMod"]: constitMods.append("Vor") @@ -122,9 +119,7 @@ def defineJetConstit(jetRecoDict,clustersKey=None,pfoPrefix=None): constitMods.append("CS") if "sk" in jetRecoDict["constitMod"]: constitMods.append("SK") - # Generate a new JetConstitModifier with track proterties setup according to trkopt - constitModWithAlternateTrk("CHS", trkopt) # - constitMods += ["CHS"+trkopt] + constitMods += ["CHS"] inputPFO = pfoPrefix+"ParticleFlowObjects" modstring = ''.join(constitMods[1:-1]) @@ -132,9 +127,9 @@ def defineJetConstit(jetRecoDict,clustersKey=None,pfoPrefix=None): modstring='CHS' if not constitMods: - jetConstit = JetConstitSeq( "HLT_EMPFlow", xAODType.ParticleFlow, constitMods, inputname=inputPFO, outputname=pfoPrefix+"CHSParticleFlowObjects", label="EMPFlow") + jetConstit = JetInputConstitSeq( "HLT_EMPFlow", xAODType.ParticleFlow, constitMods, inputname=inputPFO, outputname=pfoPrefix+"CHSParticleFlowObjects", label="EMPFlow") else: - jetConstit = JetConstitSeq( "HLT_EMPFlow"+modstring, xAODType.ParticleFlow, constitMods, inputname=inputPFO, outputname=pfoPrefix+modstring+"ParticleFlowObjects",label='EMPFlow'+(modstring if modstring!='CHS' else '') ) + jetConstit = JetInputConstitSeq( "HLT_EMPFlow"+modstring, xAODType.ParticleFlow, constitMods, inputname=inputPFO, outputname=pfoPrefix+modstring+"ParticleFlowObjects",label='EMPFlow'+(modstring if modstring!='CHS' else '') ) if jetRecoDict["constitType"] == "tc": @@ -153,11 +148,11 @@ def defineJetConstit(jetRecoDict,clustersKey=None,pfoPrefix=None): elif jetRecoDict["clusterCalib"] == "lcw": constitMods = ["LC"] + constitMods - jetConstit = JetConstitSeq( "HLT_"+constitMods[0]+"Topo",xAODType.CaloCluster, constitMods, inputname=clustersKey, outputname=clustersKey+modstring,label=constitMods[0]+'Topo'+modstring) + jetConstit = JetInputConstitSeq( "HLT_"+constitMods[0]+"Topo",xAODType.CaloCluster, constitMods, inputname=clustersKey, outputname=clustersKey+modstring,label=constitMods[0]+'Topo'+modstring) - # declare our new JetConstitSeq in the standard dictionary - from JetRecConfig.StandardJetConstits import jetconstitdic - jetconstitdic.setdefault(jetConstit.name, jetConstit) + # declare our new JetInputConstitSeq in the standard dictionary + from JetRecConfig.StandardJetConstits import stdConstitDic + stdConstitDic.setdefault(jetConstit.name, jetConstit) return jetConstit @@ -183,11 +178,11 @@ def defineJets(jetRecoDict,clustersKey=None,prefix='',pfoPrefix=None): suffix += "_{}".format(jetRecoDict["trkopt"]) - jetDef = JetDefinition( "AntiKt", actualradius, jetConstit, ptmin=minpt[jetradius], prefix=prefix, suffix=suffix) + jetDef = JetDefinition( "AntiKt", actualradius, jetConstit, ptmin=minpt[jetradius], prefix=prefix, suffix=suffix, context=jetRecoDict["trkopt"]) return jetDef def defineReclusteredJets(jetRecoDict,smallRjets,inputlabel,prefix,suffix): - rcJetConstit = JetConstitSource("RCJet", xAODType.Jet, smallRjets, label=inputlabel+'RC') + rcJetConstit = JetInputConstit("RCJet", xAODType.Jet, smallRjets, label=inputlabel+'RC') rcJetDef = JetDefinition( "AntiKt", 1.0, rcJetConstit, prefix=prefix, suffix=suffix) return rcJetDef @@ -210,7 +205,7 @@ def defineGroomedJets(jetRecoDict,ungroomedDef):#,ungroomedJetsName): # Make generating the list a bit more comprehensible def getModSpec(modname,modspec=''): - return (jetmoddict[modname],str(modspec)) + return (stdJetModifiers[modname],str(modspec)) def defineTrackMods(trkopt): trkmods = [ diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py index 32c7a5cc07128174ab6a8427bb468284c46e7c61..abaf3a762f939724f6460442793ba353118f1bb9 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py @@ -5,9 +5,9 @@ from AthenaCommon.CFElements import parOR from TriggerMenuMT.HLTMenuConfig.Menu.ChainConfigurationBase import RecoFragmentsPool from AthenaConfiguration.ComponentAccumulator import conf2toConfigurable -from JetRecConfig.JetRecConfig import getConstitPJGAlg -from JetRecConfig import JetInputConfig -from JetRecConfig import JetRecConfig +from JetRecConfig import JetInputConfig, JetRecConfig +from JetRecConfig.StandardJetContext import jetContextDic +from JetRecConfig.DependencyHelper import solveDependencies, solveGroomingDependencies from TrigEDMConfig.TriggerEDMRun3 import recordable @@ -41,9 +41,8 @@ jetNamePrefix = "HLT_" # Need to do this hacky extraction to get around the inability # to hash dicts as input to RecoFragmentsPool.retrieve -from .JetTrackingConfig import trkcollskeys def getTrkColls(jetRecoDict): - trkcolls = {key:jetRecoDict[key] for key in trkcollskeys} + trkcolls = {key:jetRecoDict[key] for key in jetContextDic["trackKeys"]} return trkcolls # The top-level sequence, forwards arguments as appropriate to @@ -73,15 +72,29 @@ def jetRecoSequence( configFlags, clustersKey, **jetRecoDict ): configFlags, dataSource=dataSource, clustersKey=clustersKey, **jetRecoDict) -# Normal jet reconstruction, no reclustering or grooming def standardJetBuildSequence( configFlags, dataSource, clustersKey, **jetRecoDict ): + """This build the standard jet (not groomed or reclustered). + + This is similar to JetRecConfig.getJetDefAlgs(). However due to how the alg flow is organized in the + chain steps, we can't use this function directly. + Instead we + - construct a JetDefinition + - use lower-level function in JetRecConfig with this JetDefinition to get the necessary algs and build our sequence manually. + + """ + jetDefString = jetRecoDictToString(jetRecoDict) buildSeq = parOR( "JetBuildSeq_"+jetDefString, []) - doesTracking = jetRecoDict["trkopt"]!="notrk" - trkcolls = getTrkColls(jetRecoDict) if doesTracking else {} - if doesTracking and not trkcolls: + trkopt = jetRecoDict["trkopt"] + doesTracking = trkopt != "notrk" + + context = jetContextDic.get(trkopt,None) + + if doesTracking and not context: raise RuntimeError("Failed to retrieve track collections for trkopt '{}'".format(jetRecoDict["trkopt"])) + # ***************************** + # First part : build a JetDefinition (and a pflow alg if needed) isPFlow = jetRecoDict["constitType"] == "pf" # Add particle flow reconstruction if needed @@ -96,65 +109,65 @@ def standardJetBuildSequence( configFlags, dataSource, clustersKey, **jetRecoDic jetDef = JetRecoConfiguration.defineJets(jetRecoDict,pfoPrefix=pfoPrefix,prefix=jetNamePrefix) else: jetDef = JetRecoConfiguration.defineJets(jetRecoDict,clustersKey=clustersKey,prefix=jetNamePrefix) - + # chosen jet collection jetsFullName = jetDef.fullname() jetsOut = recordable(jetsFullName) - JetRecConfig.instantiateAliases(jetDef) + + # build the list of jetModifiers. + # Sort and filter + jetModList = ["Sort", "Filter:"+str(JetRecoConfiguration.getFilterCut(jetRecoDict["recoAlg"])), "ConstitFourMom_copy"] + if doesTracking: + jetModList += ["TrackMoments", "JVF", "JVT"] + + if jetRecoDict["recoAlg"] == "a4": + jetModList += ["CaloEnergies"] # Needed for GSC + + jetDef.modifiers = jetModList + # make sure all the modifiers have their dependencies solved + jetDef = solveDependencies(jetDef) + + # ***************************** + # Second part : instantiate the actual algs and insert them in the sequence skipConstitMods = (jetRecoDict["constitMod"]=='') and (jetRecoDict["constitType"]=='tc') and (jetRecoDict["clusterCalib"]=="lcw") if not skipConstitMods: + # Then we need a constituent modifier sequence. # Get online monitoring jet rec tool from JetRecTools import OnlineMon monJetRecTool = OnlineMon.getMonTool_Algorithm("HLTJets/"+jetsFullName+"/") - from JetRecConfig.ConstModHelpers import getConstitModAlg - constitModAlg = getConstitModAlg(jetDef.inputdef, monTool=monJetRecTool) + # get the alg from the standard jet config helper : + constitModAlg = JetRecConfig.getConstitModAlg(jetDef, jetDef.inputdef, monTool=monJetRecTool) if constitModAlg: buildSeq += constitModAlg # Add the PseudoJetGetter alg to the sequence - constitPJAlg = getConstitPJGAlg( jetDef.inputdef ) - constitPJKey = str(constitPJAlg.OutputContainer) + constitPJAlg = JetRecConfig.getConstitPJGAlg( jetDef.inputdef ) buildSeq += conf2toConfigurable( constitPJAlg ) - # Basic list of PseudoJets is just the constituents - # Append ghosts (tracks) if desired - finalpjs = constitPJKey - # Also compile modifier list - jetModList = [] - if doesTracking: - trkMods = JetRecoConfiguration.defineTrackMods(jetRecoDict["trkopt"]) - jetModList += trkMods + finalpjs = str(constitPJAlg.OutputContainer) - finalpjs = str(constitPJAlg.OutputContainer)+"MergedWithGhostTracks" + if doesTracking: + # We need to do ghost association. + # The ghost tracks pseudoJet are build in other part of the chain : here + # we just need to merge our constituents with them + finalpjs = finalpjs+"MergedWithGhostTracks" mergerName = "PJMerger_"+finalpjs from JetRec import JetRecConf mergeAlg = JetRecConf.PseudoJetMerger( mergerName, - InputPJContainers = [constitPJKey,trkcolls["GhostTracks"]], + InputPJContainers = [str(constitPJAlg.OutputContainer),context["GhostTracks"]], OutputContainer = finalpjs) buildSeq += mergeAlg - jetDef._internalAtt['finalPJContainer'] = finalpjs - - # Sort and filter - jetModList += ["Sort", "Filter:"+str(JetRecoConfiguration.getFilterCut(jetRecoDict["recoAlg"])), "ConstitFourMom_copy"] - if jetRecoDict["recoAlg"] == "a4": - jetModList += ["CaloEnergies"] # Needed for GSC + # set the name of the final PseudoJetContainer to be used as input : + jetDef._internalAtt['finalPJContainer'] = finalpjs + # Get online monitoring tool from JetRec import JetOnlineMon monTool = JetOnlineMon.getMonTool_TrigJetAlgorithm("HLTJets/"+jetsFullName+"/") - jetDef.modifiers = jetModList - # recall instantiateAliases because we updated the modifiers - JetRecConfig.instantiateAliases(jetDef) - - # make sure all our JetModifier have their track inputs set up according to trkopt - from JetRecConfig.JetModConfig import jetModWithAlternateTrk - jetModWithAlternateTrk(jetDef, jetRecoDict['trkopt'] ) - - # Generate a JetAlgorithm to run the jet finding and modifiers - # (via a JetRecTool instance). - jetRecAlg = JetRecConfig.getJetRecAlg(jetDef, monTool) + # finally get the JetRecAlg : + jetRecAlg = JetRecConfig.getJetRecAlg(jetDef, monTool=monTool) buildSeq += conf2toConfigurable( jetRecAlg ) return buildSeq, jetsOut, jetDef @@ -194,9 +207,8 @@ def standardJetRecoSequence( configFlags, dataSource, clustersKey, **jetRecoDict # If we need JVT, just rerun the JVT modifier doesTracking = jetRecoDict["trkopt"] != "notrk" isPFlow = jetRecoDict["constitType"] == "pf" - decorList = JetRecoConfiguration.getDecorList(doesTracking,isPFlow) if doesTracking: - jetDef.modifiers.append("JVT:"+jetRecoDict["trkopt"]) + jetDef.modifiers.append("JVT") #+jetRecoDict["trkopt"] #Configuring jet cleaning mods now if jetRecoDict["cleaning"] != 'noCleaning': #Decorate with jet cleaning info only if not a PFlow chain (no cleaning available for PFlow jets now) @@ -259,14 +271,12 @@ def groomedJetRecoSequence( configFlags, dataSource, clustersKey, **jetRecoDict from JetRec import JetOnlineMon monTool = JetOnlineMon.getMonTool_TrigJetAlgorithm("HLTJets/"+groomedJetsFullName+"/") - from JetRecConfig.JetGroomConfig import getJetGroomAlg, instantiateGroomingAliases - instantiateGroomingAliases(groomDef) - groomalg = getJetGroomAlg(groomDef,monTool) + groomDef = solveGroomingDependencies(groomDef) + groomalg = JetRecConfig.getJetRecGroomAlg(groomDef,monTool) recoSeq += conf2toConfigurable( groomalg ) jetsOut = recordable(groomedJetsFullName) - jetDef = groomDef - return recoSeq, jetsOut, jetDef + return recoSeq, jetsOut, groomDef # Reclustering -- call the basic jet reco and add this to the sequence, @@ -295,7 +305,7 @@ def reclusteredJetRecoSequence( configFlags, dataSource, clustersKey, **jetRecoD rcModList = [] # Could set substructure mods rcJetDef.modifiers = rcModList - rcConstitPJAlg = getConstitPJGAlg( rcJetDef.inputdef ) + rcConstitPJAlg = JetRecConfig.getConstitPJGAlg( rcJetDef.inputdef ) rcConstitPJKey = str(rcConstitPJAlg.OutputContainer) recoSeq += conf2toConfigurable( rcConstitPJAlg ) @@ -310,3 +320,6 @@ def reclusteredJetRecoSequence( configFlags, dataSource, clustersKey, **jetRecoD jetsOut = recordable(rcJetDef.fullname()) jetDef = rcJetDef return recoSeq, jetsOut, jetDef + + + diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetTrackingConfig.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetTrackingConfig.py index c416519ea9c1840d66c629c349067b7118b23d72..b8b27f2f4ad74873b47aa9d3cd3de178d92b65be 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetTrackingConfig.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetTrackingConfig.py @@ -4,38 +4,43 @@ from AthenaCommon.CFElements import parOR -from JetRecTools.JetRecToolsConfig import getTrackVertexAssocTool +from JetRecTools import JetRecToolsConfig as jrtcfg from AthenaConfiguration.ComponentFactory import CompFactory from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator, conf2toConfigurable from TrigInDetConfig.TrigInDetPriVtxConfig import makeVertices -# these keys are not used in this file, they are used elsewhere, so -# wouldn;t it be better to actually define them in the file where they -# are needed ? -trkcollskeys = ["Tracks", "Vertices", "TVA", "GhostTracks", "GhostTracksLabel", "JetTracks"] - -def get_trackcolls(trkopt, adaptiveVertex): - if trkopt == "notrk": - return {} - elif trkopt == "ftf": +def retrieveJetContext(trkopt): + # Tell the standard jet config about the specific track related options we are using here. + # This is done by defining a new jet context into jetContextDic. + # Then passing this context name in the JetDefinition and standard helper function will ensure + # these options will consistently be used everywhere. + from JetRecConfig.StandardJetContext import jetContextDic + from AthenaConfiguration.AllConfigFlags import ConfigFlags as flags + if trkopt not in jetContextDic: from TrigInDetConfig.ConfigSettings import getInDetTrigConfig - IDTrigConfig = getInDetTrigConfig('jet') + IDTrigConfig = getInDetTrigConfig( 'jet' ) + if flags.Trigger.Jet.doAMVFPriorityTTVA: + verticesname = IDTrigConfig.vertex + else: + verticesname = IDTrigConfig.vertex_jet + + tvaname = f"JetTrackVtxAssoc_{trkopt}" label = f"GhostTrack_{trkopt}" + ghosttracksname = f"PseudoJet{label}" + + jetContextDic[trkopt] = jetContextDic['default'].clone( + Tracks = IDTrigConfig.tracks_FTF(), + Vertices = verticesname, + TVA = tvaname, + GhostTracks = ghosttracksname, + GhostTracksLabel = label , + JetTracks = f'JetSelectedTracks_{trkopt}', + ) - return { - "Tracks": IDTrigConfig.tracks_FTF(), - "Vertices": IDTrigConfig.adaptiveVertex if adaptiveVertex else IDTrigConfig.vertex, - "TVA": f"JetTrackVtxAssoc_{trkopt}", - "GhostTracks": f"PseudoJet{label}", - "GhostTracksLabel": label, - "JetTracks": f"JetSelectedTracks_{trkopt}", - } - else: - raise KeyError(f"Unknown trkopt '{trkopt}'") - + return jetContextDic[trkopt], jetContextDic["trackKeys"] def JetTrackingSequence(dummyFlags,trkopt,RoIs): from AthenaConfiguration.AllConfigFlags import ConfigFlags as flags @@ -73,43 +78,33 @@ def JetTrackingCfg(flags, trkopt, RoIs): acc = ComponentAccumulator() from TrigInDetConfig.ConfigSettings import getInDetTrigConfig IDTrigConfig = getInDetTrigConfig( 'jet' ) - trackcollmap = None if trkopt == "ftf": from TrigInDetConfig.TrigInDetConfig import trigInDetFastTrackingCfg from TrigInDetConfig.TrigInDetPriVtxConfig import vertexFinderCfg acc.merge(trigInDetFastTrackingCfg(flags, RoIs, signatureName="jet", in_view=False)) - # Figure out the output containers - label = f"GhostTrack_{trkopt}" - trackcollmap = { - "Tracks": IDTrigConfig.tracks_FTF(), - "TVA": f"JetTrackVtxAssoc_{trkopt}", - "GhostTracks": f"PseudoJet{label}", - "GhostTracksLabel": label, - "JetTracks": f"JetSelectedTracks_{trkopt}", - } + # get the jetContext for trkopt (and build it if not existing yet) + jetContext, trkKeys = retrieveJetContext(trkopt) if flags.Trigger.Jet.doAMVFPriorityTTVA: - trackcollmap["Vertices"] = IDTrigConfig.vertex acc.merge( JetVertexCfg( - flags, trkopt, IDTrigConfig.adaptiveVertex, trackcollmap, + flags, trkopt, IDTrigConfig.adaptiveVertex, jetContext, ) ) else: - trackcollmap["Vertices"] = IDTrigConfig.vertex_jet acc.merge( vertexFinderCfg( flags, signature="jet", - inputTracks=trackcollmap["Tracks"], + inputTracks=jetContext["Tracks"], outputVertices=IDTrigConfig.vertex, adaptiveVertexing=IDTrigConfig.adaptiveVertex, ) ) acc.merge( JetVertexCfg( - flags, trkopt, IDTrigConfig.adaptiveVertex_jet, trackcollmap, + flags, trkopt, IDTrigConfig.adaptiveVertex_jet, jetContext, ) ) else: @@ -118,20 +113,24 @@ def JetTrackingCfg(flags, trkopt, RoIs): # Add the pseudo-jet creator acc.addEventAlgo( CompFactory.PseudoJetAlgorithm( - f"pjgalg_{trackcollmap['GhostTracksLabel']}", - InputContainer=trackcollmap["Tracks"], - OutputContainer=trackcollmap["GhostTracks"], - Label=trackcollmap["GhostTracksLabel"], + f"pjgalg_{jetContext['GhostTracksLabel']}", + InputContainer=jetContext["Tracks"], + OutputContainer=jetContext["GhostTracks"], + Label=jetContext["GhostTracksLabel"], SkipNegativeEnergy=True, ) ) - return acc, trackcollmap + + # make sure we output only the key,value related to tracks (otherwise, alg duplication issues) + outmap = { k:jetContext[k] for k in trkKeys } + + return acc, outmap def jetVertex( signature, jetseq, trkopt, config, verticesname=None, adaptiveVertex=None, selector=None ): + # ***************************** # run the vertex algorithm ... - if verticesname is None: verticesname = config.vertex if adaptiveVertex is None: @@ -139,68 +138,54 @@ def jetVertex( signature, jetseq, trkopt, config, verticesname=None, adaptiveVer tracksname = config.tracks_FTF() + # get the algs : vtxAlgs = makeVertices( signature, tracksname, verticesname, config, adaptiveVertex ) prmVtx = vtxAlgs[-1] jetseq += prmVtx - outmap = None - # track to vertex association ... + label = f"GhostTrack_{trkopt}" - tvaname = "JetTrackVtxAssoc_"+trkopt - label = "GhostTrack_{}".format(trkopt) - ghosttracksname = "PseudoJet{}".format(label) - - - from JetRecTools.JetRecToolsConfig import trackcollectionmap - if trkopt not in trackcollectionmap.keys(): - trkcolls = { - "Tracks": tracksname, - "Vertices": verticesname, - "TVA": tvaname, - "GhostTracks" : ghosttracksname, - "GhostTracksLabel": label - } - - trackcollectionmap[trkopt] = trkcolls - # why is some of this adding of parameters to the trackcollectionmap - # done here, and some in getTrackSelTool ? Could these two functions - # not be combined or broken up into more transparent smaller functions ? - - # Jet track selection - jettrackselloose = getTrackSelTool_Trig( trkopt, doWriteTracks=True ) - jettracksname = jettrackselloose.OutputContainer - - trackcollectionmap[trkopt]["JetTracks"] = jettracksname - - prepname = "jetalg_TrackPrep"+trkopt - jettvassoc = getTrackVertexAssocTool( trkopt, jetseq , - ttva_opts = { "WorkingPoint" : "Custom", - "d0_cut" : 2.0, - "dzSinTheta_cut" : 2.0, - "doPVPriority": adaptiveVertex, - } - ) - - jettrkprepalg = CompFactory.JetAlgorithm(prepname) - jettrkprepalg.Tools = [ jettrackselloose, jettvassoc ] - jetseq += conf2toConfigurable( jettrkprepalg ) - + # get the jetContext for trkopt (and build it if not existing yet) + jetContext, trkKeys = retrieveJetContext(trkopt) + + # ***************************** + # Jet track selection algorithm + jettrackselalg = jrtcfg.getTrackSelAlg( trkopt ) + + # Track-vtx association. We create a TrackVertexAssocTool then call it through a + # JetAlgorithm which just calls its execute() method. In the future the plan is to + # convert this TrackVertexAssocTool in a simple alg just as for track selection. + jettvassoc = jrtcfg.getTrackVertexAssocTool( trkopt, jetseq , + ttva_opts = { "WorkingPoint" : "Custom", + "d0_cut" : 2.0, + "dzSinTheta_cut" : 2.0, + "doPVPriority": adaptiveVertex, + } + ) + jettrkprepalg = CompFactory.JetAlgorithm("jetalg_TrackPrep"+trkopt, + Tools = [ jettvassoc ]) + + # Pseudojets for ghost tracks pjgalg = CompFactory.PseudoJetAlgorithm( "pjgalg_"+label, InputContainer=tracksname, - OutputContainer=ghosttracksname, + OutputContainer=jetContext["GhostTracks"], Label=label, SkipNegativeEnergy=True ) - + + # Add the 3 algs to the sequence : + jetseq += conf2toConfigurable( jettrackselalg ) + jetseq += conf2toConfigurable( jettrkprepalg ) jetseq += conf2toConfigurable( pjgalg ) - outmap = trackcollectionmap[trkopt] + # make sure we output only the key,value related to tracks (otherwise, alg duplication issues) + outmap = { k:jetContext[k] for k in trkKeys } return outmap -def JetVertexCfg(flags, trkopt, adaptiveVertex, trkcolls): +def JetVertexCfg(flags, trkopt, adaptiveVertex, jetContext): """ Create the jet vertexing """ from TrigInDetConfig.TrigInDetPriVtxConfig import vertexFinderCfg from TrackVertexAssociationTool.TTVAToolConfig import TTVAToolCfg @@ -208,14 +193,10 @@ def JetVertexCfg(flags, trkopt, adaptiveVertex, trkcolls): acc = vertexFinderCfg( flags, signature = "jet", - inputTracks = trkcolls["Tracks"], - outputVertices = trkcolls["Vertices"], + inputTracks = jetContext["Tracks"], + outputVertices = jetContext["Vertices"], adaptiveVertexing = adaptiveVertex) - # Tell the central jet code about these collections/names - from JetRecTools.JetRecToolsConfig import trackcollectionmap - trackcollectionmap.setdefault(trkopt, trkcolls) - # Create the track selection tool # TODO - this is not used anywhere that I can see so I'm skipping it @@ -227,9 +208,9 @@ def JetVertexCfg(flags, trkopt, adaptiveVertex, trkcolls): Tools = [ CompFactory.TrackVertexAssociationTool( "jettvassoc", - TrackParticleContainer = trkcolls["Tracks"], - TrackVertexAssociation = trkcolls["TVA"], - VertexContainer = trkcolls["Vertices"], + TrackParticleContainer = jetContext["Tracks"], + TrackVertexAssociation = jetContext["TVA"], + VertexContainer = jetContext["Vertices"], TrackVertexAssoTool = acc.popToolsAndMerge( TTVAToolCfg( flags, @@ -238,8 +219,8 @@ def JetVertexCfg(flags, trkopt, adaptiveVertex, trkcolls): d0_cut = 2.0, dzSinTheta_cut = 2.0, doPVPriority = adaptiveVertex, - TrackContName = trkcolls["Tracks"], - VertexContName = trkcolls["Vertices"], + TrackContName = jetContext["Tracks"], + VertexContName = jetContext["Vertices"], ) ), ) @@ -248,34 +229,3 @@ def JetVertexCfg(flags, trkopt, adaptiveVertex, trkcolls): ) return acc -def getTrackSelTool_Trig( trkopt="", doWriteTracks=False, cutLevel="Loose", minPt=500 ): - - from JetRecTools.JetRecToolsConfig import trackcollectionmap - - # Track selector needs its own hierarchical config getter in JetRecTools? - idtrackselloose = CompFactory.getComp("InDet::InDetTrackSelectionTool")( - "idtrackselloose", - CutLevel = cutLevel, - minPt = minPt, - UseTrkTrackTools = False, - Extrapolator = "", - TrackSummaryTool = "" - ) - jettrackselloose = CompFactory.JetTrackSelectionTool( - "jettrackselloose", - Selector = idtrackselloose - ) - # Should phase this out completely! - # Make a jet track selection alg - # Elsewhere just use the ID track tool directly - if doWriteTracks: - jettracksname = "JetSelectedTracks" - # hack to retain the curent track collection name with "trkopt" - # extnsion, and extend onle for additional vertex collections - if trkopt: - jettracksname += "_{}".format(trkopt) - jettrackselloose.InputContainer = trackcollectionmap[trkopt]["Tracks"] - jettrackselloose.OutputContainer = jettracksname - - return jettrackselloose - diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/TriggerJetMods.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/TriggerJetMods.py index 9888cc0dd74a2d5c88a03f62d4336a66060e198f..0b8a4f498a8254885b4e80fdfb80fade779cc5c7 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/TriggerJetMods.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/TriggerJetMods.py @@ -3,10 +3,17 @@ from JetRecConfig.JetDefinition import JetModifier ######################################################################## # Special modifier setups used by jet trigger, but not in offline -# We just extend the jetmoddict from the standard config. +# We just extend the stdJetModifiers from the standard config. -from JetRecConfig.StandardJetMods import jetmoddict -jetmoddict.update( + +# Many JetMoment tools need to know the name of the container they operate on. +# We set the function below as the 'JetContainer' property so the config system +# can assign the right name to the c++ tool. +def _jetname(jetdef,modspec): + return jetdef.fullname() + +from JetRecConfig.StandardJetMods import stdJetModifiers +stdJetModifiers.update( # No need for the special momentum scales, just copy the basic four-vec # to "DetectorEtaPhi", because we're not doing origin correction @@ -18,10 +25,11 @@ jetmoddict.update( AltJetScales = ["JetConstitScaleMomentum"] ), Cleaning = JetModifier("JetCleaningTool","jetcleaning_{modspec}", - # This allows to set the modifier using a string like - # "Cleaning:CLEAN_LEVEL" as defined in JetCleaningTool - # (example "Cleaning:LooseBad") - CutLevel=lambda _, modspec: str(modspec), - prereqs=[f"mod:{mod}" for mod in ['CaloQuality']] + # This allows to set the modifier using a string like + # "Cleaning:CLEAN_LEVEL" as defined in JetCleaningTool + # (example "Cleaning:LooseBad") + CutLevel=lambda _, modspec: str(modspec), + prereqs=[f"mod:{mod}" for mod in ['CaloQuality']], + JetContainer = _jetname ), ) diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/ConfigHelpers.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/ConfigHelpers.py index d423c7f8f60c1d6b1ef55bf0b9c899db7b2b5388..df4eedae7a749b0ebae3e17fd8053b07a19d1dd7 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/ConfigHelpers.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/ConfigHelpers.py @@ -167,6 +167,7 @@ class AlgConfig(ABC): return self._athSequences def menuSequences(self): + """ Get the menu sequences (split by step) """ if hasattr(self, "_menuSequences"): return self._menuSequences diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py index 659e29d3e740ae572cdad5ec3da2293e4d5711b9..92af7a39a056f03ec3ca3cd96350867d1b42b0fd 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py @@ -1,4 +1,3 @@ -# # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration # @@ -86,7 +85,7 @@ class ClusterInputConfig(AlgInputConfig): LCCaloClusterRecoSequence, ) from ..Jet.JetRecoConfiguration import defineJetConstit - from JetRecConfig.ConstModHelpers import getConstitModAlg, aliasToInputDef + from JetRecConfig.JetRecConfig import getConstitModAlg_nojetdef calib = recoDict["calib"] if calib == "em": @@ -105,10 +104,9 @@ class ClusterInputConfig(AlgInputConfig): recoDict = copy.copy(recoDict) recoDict["constitType"] = "tc" jetRecoDict = jetRecoDictForMET(**recoDict) - constit = aliasToInputDef( - defineJetConstit(jetRecoDict, clustersKey=clusterName) - ) - constit_mod_seq = getConstitModAlg(constit) + constit = defineJetConstit(jetRecoDict, clustersKey=clusterName) + # we pass the context argument to make sure the properties inside our JetConstit are tuned according to trkopt + constit_mod_seq = getConstitModAlg_nojetdef(constit,context=jetRecoDict.get("trkopt","default")) sequences.append(constit_mod_seq) # Update the name to the modified container name clusterName = constit.containername @@ -119,7 +117,7 @@ class ClusterInputConfig(AlgInputConfig): from ..CommonSequences.CaloConfig import CaloClusterCfg from ..CommonSequences.FullScanDefs import em_clusters, lc_clusters from ..Jet.JetRecoConfiguration import defineJetConstit - from JetRecConfig.ConstModHelpers import getConstitModAlg, aliasToInputDef + from JetRecConfig.JetRecConfig import getConstitModAlg_nojetdef if recoDict["calib"] == "em": doLC = False @@ -138,10 +136,10 @@ class ClusterInputConfig(AlgInputConfig): # Force the datatype to topoclusters recoDict = copy.copy(recoDict) recoDict["constitType"] = "tc" - constit = aliasToInputDef( - defineJetConstit(jetRecoDictForMET(**recoDict), clustersKey=clustername) - ) - acc.addEventAlgo(getConstitModAlg(constit)) + jetRecoDict = jetRecoDictForMET(**recoDict) + constit = defineJetConstit(jetRecoDict, clustersKey=clustername) + for a in getConstitModAlg_nojetdef(constit, context=jetRecoDict.get("trkopt","default")): + acc.addEventAlgo(a) clustername = constit.containername return acc, {"Clusters": clustername} @@ -186,10 +184,9 @@ default_inputs.add_input(EMClusterInputConfig()) class TrackingInputConfig(AlgInputConfig): def __init__(self): - from ..Jet.JetTrackingConfig import trkcollskeys - + from JetRecConfig.StandardJetContext import jetContextDic super().__init__( - produces=copy.copy(trkcollskeys), + produces=copy.copy(jetContextDic['trackKeys']), step=1, ) @@ -230,7 +227,7 @@ class PFOInputConfig(AlgInputConfig): def create_sequence(self, inputs, RoIs, recoDict): from eflowRec.PFHLTSequence import PFHLTSequence from ..Jet.JetRecoConfiguration import defineJetConstit - from JetRecConfig.ConstModHelpers import getConstitModAlg, aliasToInputDef + from JetRecConfig.JetRecConfig import getConstitModAlg_nojetdef pfSeq, pfoPrefix = RecoFragmentsPool.retrieve( PFHLTSequence, @@ -246,8 +243,9 @@ class PFOInputConfig(AlgInputConfig): # Force the jet data type to the correct thing recoDict["constitType"] = "pf" jetRecoDict = jetRecoDictForMET(trkopt="ftf", **recoDict) - constit = aliasToInputDef(defineJetConstit(jetRecoDict, pfoPrefix=pfoPrefix)) - constit_mod_seq = getConstitModAlg(constit) + constit = defineJetConstit(jetRecoDict, pfoPrefix=pfoPrefix) + # we pass the context argument to make sure the properties inside our JetConstit are tuned according to trkop + constit_mod_seq = getConstitModAlg_nojetdef(constit,context=jetRecoDict.get("trkopt","default")) # Update the PFO prefix pfoPrefix = constit.containername if pfoPrefix.endswith("ParticleFlowObjects"): @@ -267,7 +265,7 @@ class PFOInputConfig(AlgInputConfig): def create_accumulator(self, flags, inputs, RoIs, recoDict): from eflowRec.PFHLTConfig import PFCfg from ..Jet.JetRecoConfiguration import defineJetConstit - from JetRecConfig.ConstModHelpers import getConstitModAlg, aliasToInputDef + from JetRecConfig.JetRecConfig import getConstitModAlg_nojetdef acc = PFCfg( flags, @@ -284,13 +282,10 @@ class PFOInputConfig(AlgInputConfig): recoDict = copy.copy(recoDict) # Force the jet data type to the correct thing recoDict["constitType"] = "pf" - constit = aliasToInputDef( - defineJetConstit( - jetRecoDictForMET(trkopt="ftf", **recoDict), - pfoPrefix="HLT_ftf", - ) - ) - acc.addEventAlgo(getConstitModAlg(constit)) + jetRecoDict=jetRecoDictForMET(trkopt="ftf", **recoDict), + constit = defineJetConstit(jetRecoDict, pfoPrefix="HLT_ftd") + # we pass the context argument to make sure the properties inside our JetConstit are tuned according to trkop + acc.addEventAlgo( getConstitModAlg_nojetdef(constit,context=jetRecoDict.get("trkopt","default")) ) # WARNING getConstitModAlg_nojetdef could return None, however this won't happen for PFlow # Update the PFO prefix pfoPrefix = constit.containername if pfoPrefix.endswith("ParticleFlowObjects"):