diff --git a/PhysicsAnalysis/AnalysisCommon/ParticleJetTools/python/ParticleJetToolsConfig.py b/PhysicsAnalysis/AnalysisCommon/ParticleJetTools/python/ParticleJetToolsConfig.py index 92496227ae2d930d2e177e8326dbd592de4d20b9..c35dfe4c54ec01bb43cf2309acb25da35d95deb1 100644 --- a/PhysicsAnalysis/AnalysisCommon/ParticleJetTools/python/ParticleJetToolsConfig.py +++ b/PhysicsAnalysis/AnalysisCommon/ParticleJetTools/python/ParticleJetToolsConfig.py @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration ######################################################################## # # @@ -36,18 +36,21 @@ truthpartoptions = { "FlavourLabel":{"ToolType":CompFactory.CopyFlavorLabelTruthParticles,"ptmin":5000}, } def getCopyTruthLabelParticles(truthtype): - truthcategory = "" + toolProperties = {} if truthtype == "Partons": truthcategory = "Partons" - if truthtype in ["WBosons", "ZBosons", "HBosons", "TQuarksFinal"]: + elif truthtype in ["WBosons", "ZBosons", "HBosons", "TQuarksFinal"]: truthcategory = "BosonTop" else: truthcategory = "FlavourLabel" + toolProperties['ParticleType'] = truthtype + tooltype = truthpartoptions[truthcategory]["ToolType"] - ptmin = truthpartoptions[truthcategory]["ptmin"] - ctp = tooltype("truthpartcopy_{0}".format(truthtype), - PtMin = ptmin, - OutputName = "TruthLabel{0}".format(truthtype)) + toolProperties.update( PtMin = truthpartoptions[truthcategory]["ptmin"], + OutputName = "TruthLabel"+truthtype) + ctp = tooltype("truthpartcopy_"+truthtype, + **toolProperties + ) return ctp # Generates input truth particle containers for truth jets diff --git a/Reconstruction/Jet/JetCalibTools/python/JetCalibToolsConfig.py b/Reconstruction/Jet/JetCalibTools/python/JetCalibToolsConfig.py index 8d419d0281c61eab7d2594460e0abbbdc53ded7c..eecaa96de3b5304889fab734672e53056199464d 100644 --- a/Reconstruction/Jet/JetCalibTools/python/JetCalibToolsConfig.py +++ b/Reconstruction/Jet/JetCalibTools/python/JetCalibToolsConfig.py @@ -160,7 +160,8 @@ def getJetCalibToolPrereqs(modspec,jetdef): prereqs = [] prereqs.append("mod:ConstitFourMom") if "JetArea" in calibseq: # Will not insert a prefix here - prereqs.append("input:EventDensity") + if calibcontext.startswith("Trig"): prereqs.append("input:HLT_EventDensity") + else: prereqs.append("input:EventDensity") if "GSC" in calibseq: prereqs += ["mod:CaloEnergies"] if calibcontext != "TrigRun2": # No track/MS GSC for trigger w/o FTK diff --git a/Reconstruction/Jet/JetMomentTools/python/JetMomentToolsConfig.py b/Reconstruction/Jet/JetMomentTools/python/JetMomentToolsConfig.py index 28628be137b85d0a44e8032d57b70437839719b3..dd0b7083e55c2e4b29e3b265d31dd5b8ad1a2756 100644 --- a/Reconstruction/Jet/JetMomentTools/python/JetMomentToolsConfig.py +++ b/Reconstruction/Jet/JetMomentTools/python/JetMomentToolsConfig.py @@ -74,7 +74,7 @@ def getConstitFourMomTool(jetdef): cfourmom = CompFactory.JetConstitFourMomTool("constitfourmom_{0}".format(jetdef.basename)) if "LCTopo" in jetdef.basename or "EMTopo" in jetdef.basename: cfourmom.JetScaleNames = ["DetectorEtaPhi"] - if "HLT_" in jetdef.inputdef.inputname: + if "HLT_" in jetdef.fullname(): cfourmom.AltConstitColls = [""] cfourmom.AltConstitScales = [0] cfourmom.AltJetScales = ["JetConstitScaleMomentum"] @@ -104,6 +104,7 @@ def getJVFTool(modspec=""): TrackParticleContainer = trackcollectionmap[modspec]["Tracks"], TrackSelector = jettrackselloose, ) + print('aaaa', modspec, jvf.VertexContainer) return jvf diff --git a/Reconstruction/Jet/JetRecConfig/python/ConstModHelpers.py b/Reconstruction/Jet/JetRecConfig/python/ConstModHelpers.py index 5a1750857ffcf48949bc0eea67a82c3d7728f32a..d1b4f4ac84ed3d97fde527e4bc6ef93c5f775bda 100644 --- a/Reconstruction/Jet/JetRecConfig/python/ConstModHelpers.py +++ b/Reconstruction/Jet/JetRecConfig/python/ConstModHelpers.py @@ -1,11 +1,12 @@ -# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration - -######################################################################## -# # -# ConstModUtils: A module for configuring constituent modifiers # -# Author: TJ Khoo # -# # -######################################################################## +# 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') @@ -19,86 +20,121 @@ constmodlog = Logging.logging.getLogger('ConstModHelpers') from AthenaConfiguration.ComponentFactory import CompFactory -# Tool types and configs can be expanded if the user -# wishes to add their own custom definitions - -# Maybe we need a config class in JetDefinition? -ConstModTools = { - # Topoclusters - "Origin": CompFactory.CaloClusterConstituentsOrigin, - "EM": CompFactory.ClusterAtEMScaleTool, - # Particle flow - "CorrectPFO": - CompFactory.CorrectPFOTool, - "CHS": CompFactory.ChargedHadronSubtractionTool, - # Pileup suppression - "Vor": CompFactory.VoronoiWeightTool, - "CS": CompFactory.ConstituentSubtractorTool, - "SK": CompFactory.SoftKillerWeightTool -} - -ConstModConfigs = { - # Topoclusters - "Origin": {}, - "EM": {}, - # Particle flow - "CorrectPFO": - {"WeightPFOTool": CompFactory.getComp("CP::WeightPFOTool")("weightPFO")}, - "CHS": {}, - # Pileup suppression - "Vor": {"doSpread":False, "nSigma":0}, - "CS": {"MaxEta":5.}, - "SK": {} -} - -def getConstitModAlg(constit,suffix="",tvaKey="JetTrackVtxAssoc",vtxKey="PrimaryVertices",monTool=None): - inputtype = constit.basetype - # Need to extend to TCC - if inputtype not in [xAODType.CaloCluster, xAODType.ParticleFlow]: - constmodlog.error("Only ParticleFlow and CaloCluster currently supported!") - raise TypeError("Unsupported input type {0}".format(inputtype)) +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) + + - sequence = list(constit.modifiers) # Copy, as we may make some additions - typename = {xAODType.CaloCluster: "TopoCluster", - xAODType.ParticleFlow: "EMPFlow" - }[inputtype] - if inputtype == xAODType.ParticleFlow: - # Always do 4mom corrections first and CHS last - sequence = ["CorrectPFO"] + sequence + ["CHS"] - # If no mods are needed, don't give back a tool - if sequence == []: return None +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: - if step == "LC": - continue # Nothing to do for LC clusters - tool = None - - toolname = "ConstitMod{0}_{1}{2}".format(typename,step,suffix) - tool = ConstModTools[step](toolname,**ConstModConfigs[step]) - - # May want to set also for cluster origin correction - # but so far unused - if step=="CorrectPFO": - tool.VertexContainerKey = vtxKey - if step=="CHS": - tool.TrackVertexAssociation = tvaKey - tool.VertexContainerKey = vtxKey + 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 ) - if inputtype == xAODType.ParticleFlow and step not in ["CorrectPFO","CHS"]: + 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}{2}".format(sequenceshort,typename,suffix) - inputcontainer = str(constit.rawname) - outputcontainer = str(constit.inputname) + 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 @@ -111,14 +147,45 @@ def getConstitModAlg(constit,suffix="",tvaKey="JetTrackVtxAssoc",vtxKey="Primary outputcontainer = chopPFO(outputcontainer) modseq = CompFactory.JetConstituentModSequence(seqname, - InputType=inputtype, - OutputContainer = outputcontainer, - InputContainer= inputcontainer, - Modifiers = modlist, - MonTool = monTool + InputType=inputtype, + OutputContainer = outputcontainer, + InputContainer= inputcontainer, + Modifiers = modlist, + 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/JetDefinition.py b/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py index cdfafdd6e15595914a9db2dc14f777a17753ed09..b762ff297842b864349f9ce082c153b932f3a04d 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetDefinition.py @@ -1,19 +1,21 @@ # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration -######################################################################## -# # -# JetDefinition: A module for classes encoding definitions of jets and # -# related objects for configuring jet reconstruction # -# Author: TJ Khoo # -# # -######################################################################## +""" + +JetDefinition: A module for classes encoding definitions of jets and +related objects for configuring jet reconstruction + +Author: TJ Khoo, P-A Delsart + +""" -__all__ = ["JetConstit", "JetGhost", "JetDefinition","xAODType"] +__all__ = [ "JetDefinition","xAODType", "JetModifier", "JetConstitModifier" , "JetConstitSeq", "JetInputDef"] from AthenaCommon import Logging jetlog = Logging.logging.getLogger('JetDefinition') from xAODBase.xAODType import xAODType +from .Utilities import make_lproperty, onlyAttributesAreProperties, clonable, make_alias # Code from JetRecUtils # define the convention that we write R truncating the decimal point @@ -36,171 +38,15 @@ def buildJetAlgName(finder, mainParam, variableRMassScale=None, variableRMinRadi return finder + "VR" + str(int(variableRMassScale/1000)) + "Rmax" + rmaxstr + "Rmin" + rminstr return finder + formatRvalue(mainParam) -# A class that defines the type of object used to build a jet -# Normally defaults to standard offline input containers, but -# can be overridden. -class JetConstit(object): - def __init__(self, - objtype, # The type of xAOD object from which to build the jets - modifiers=[], # Modifications to be applied to constituents prior to jet finding - rawname=None, # Override the default input collection - inputname=None, # Override the default output collection (not to be used with modifiers) - prefix=None): # Optional prefix for the inputname (mainly for trigger) - self.__basetype = objtype - self.__modifiers = modifiers - # Override for unmodified container name - self.__rawname = rawname - # Override for final container name - self.__inputname = inputname - # Prefix for inputname - self.__prefix = prefix - - self.defineLabelAndContainerNames() - pass - - def __hash__(self): - return hash((self.__basetype,str(self.__modifiers))) - - def __eq__(self,rhs): - return self.__hash__() == rhs.__hash__() - def __ne__(self,rhs): - return (not self.__eq__(rhs)) - # Define type and modifiers as properties, with - # custom setter/getter such that if changed, these - # force resetting of the label and container name - @property - def basetype(self): - return self.__basetype - @basetype.setter - def basetype(self,basetype): - self.__basetype = basetype - self.defineLabelAndContainerNames() - - @property - def prefix(self): - return self.__prefix - - @property - def modifiers(self): - return self.__modifiers - @modifiers.setter - def modifiers(self,modifiers): - self.__modifiers = modifiers - self.defineLabelAndContainerNames() - - def defineLabelAndContainerNames(self): - labelnames = { - xAODType.CaloCluster: "Topo", - xAODType.ParticleFlow: "EMPFlow", - xAODType.TrackParticle: "Track", - xAODType.TruthParticle: "Truth", - xAODType.TrackCaloCluster: "TrackCaloCluster", - xAODType.Jet: "Jet", - } - - # Need capability to override label? - self.label = "" - self.rawname = "" - self.inputname = "" - - # Truth "modifiers" specify the selection tool config - # e.g. WZ, WZDressed, DarkHadrons, ... - # Track "modifiers" specifiy the vertex association - # Other "modifiers" determine the constit mods e.g. - # origin correction, PU suppression etc - # Topoclusters should also specify EM or LC - # Jets could specify a filter e.g. pt cut or JVT?? - modstring = "" - if self.__modifiers: - for mod in self.__modifiers: - # Handle special case of topocluster state - if mod in ["EM","LC"]: - self.label += mod - else: - modstring += mod - - self.label += labelnames[self.basetype] - if self.basetype!=xAODType.Jet: - self.label += modstring - if self.basetype==xAODType.TruthParticle: - self.label = self.label.replace("NoWZ","WZ") - - containernames = { - xAODType.CaloCluster: "TopoClusters", - xAODType.ParticleFlow: "ParticleFlowObjects", - xAODType.TrackParticle: "JetSelectedTracks", - xAODType.TruthParticle: "JetInputTruthParticles", - xAODType.TrackCaloCluster: "TrackCaloClusters", - xAODType.Jet: "Jets", - } - # Sometimes the unmodified container name is longer - defaultaffixesraw = { - xAODType.CaloCluster: "CaloCal", - xAODType.ParticleFlow: "JetETMiss", - xAODType.TrackCaloCluster: "CombinedAndNeutral", - } - # Sometimes the standard contstit container has default mods not in modlist - defaultaffixesinput = { - xAODType.ParticleFlow: "CHS", - } - - # User-specified override - if self.__rawname: - self.rawname = self.__rawname - else: # Default to standard container - if self.basetype in defaultaffixesraw.keys(): - self.rawname = defaultaffixesraw[self.basetype] - self.rawname += containernames[self.basetype] - - # User-specified override - if self.__inputname: - self.inputname = self.__inputname - else: # Default to naming with modifiers if requested, else default container - if not modstring and self.basetype in defaultaffixesinput.keys(): - modstring = defaultaffixesinput[self.basetype] - modslast = [xAODType.TruthParticle, xAODType.TrackCaloCluster] - if self.basetype in modslast: - self.inputname = containernames[self.basetype]+modstring - else: - self.inputname = modstring+containernames[self.basetype] - - if self.__prefix: - self.inputname = self.__prefix+self.inputname - pass - - # Define a string conversion for printing - def __str__(self): - return "JetConstit({0}: {1})".format(self.label,self.inputname) - # Need to override __repr__ for printing in lists etc - __repr__ = __str__ - -# Not too clear at this point what other info is needed -# In principle one might want to state the truth types etc -class JetGhost(object): - def __init__(self, inputtype): - self.inputtype = inputtype - self.label = "Ghost"+inputtype - pass - - def __hash__(self): - return hash(self.inputtype) - - def __eq__(self,rhs): - return self.__hash__() == rhs.__hash__() - - def __ne__(self,rhs): - return (not self.__eq__(rhs)) - - # Define a string conversion for printing - def __str__(self): - return "JetGhost(Ghost{0})".format(self.inputtype) - # Need to override __repr__ for printing in lists etc - __repr__ = __str__ +def _condAlwaysPass(condflags): + return True,"" from AthenaCommon.SystemOfUnits import MeV +@clonable +@onlyAttributesAreProperties class JetDefinition(object): def __init__(self, algorithm, # The fastjet clustering algorithm @@ -210,19 +56,27 @@ class JetDefinition(object): ptminfilter=5e3*MeV, # The minimum pt to retain xAOD jets after calibration in MeV ghostdefs=[], # The list of JetGhosts to ghost-associate modifiers=[], # The list of JetModifiers to execute after jet finding - extrainputs=[]): # The list of additional input types needed for jet finding - + extrainputs=[], # The list of additional input types needed for jet finding + standardRecoMode = False, # + prefix = "", # allows to tune the full JetContainer name + suffix = "", # allows to tune the full JetContainer name + lock = False, # lock the properties of this instance to avoid accidental overwrite after __init__ + ): + + self._locked = False # unlock during init # Should add some type checking here # Could use JetContainerInfo conversion - self.__algorithm = algorithm - if self.__algorithm not in ["Kt","AntiKt","CamKt"]: + if algorithm not in ["Kt","AntiKt","CamKt"]: jetlog.error("FastJet algorithm specification was not one of Kt, AntiKt, CamKt!") raise KeyError("Invalid fastjet algorithm choice: {0}".format(self.algorithm)) - - self.__radius = radius - self.__inputdef = inputdef - self.__defineName() - + self._algorithm = algorithm + + self._radius = radius + self._inputdef = inputdef + self._prefix = prefix + self._suffix = suffix + self._defineName() + self.ptmin = ptmin # The pt down to which FastJet is run self.ptminfilter = ptminfilter # The pt above which xAOD::Jets are kept, may include calibration if ptmin<1000.*MeV or ptminfilter<1000.*MeV: @@ -232,12 +86,19 @@ class JetDefinition(object): self.modifiers = modifiers # Tools to modify the jet self.extrainputs = extrainputs # Any extra input dependencies + self.standardRecoMode = standardRecoMode + # These should probably go in a derived class self.VRMinRadius = None self.VRMassScale = None - pass + + # used internally to resolve dependencies + self._prereqDic = {} + self._prereqOrder = [] + self._locked = lock + def __hash__(self): return hash((self.__radius,self.__inputdef,self.ptmin,self.ptminfilter,str(self.ghostdefs),str(self.modifiers),str(self.extrainputs))) @@ -250,37 +111,79 @@ class JetDefinition(object): # Define core attributes as properties, with # custom setter/getter such that if changed, these # force resetting of the jet name - @property - def algorithm(self): - return self.__algorithm - @algorithm.setter + @make_lproperty + def algorithm(self): pass + + @algorithm.lsetter def algorithm(self,algorithm): - self.__algorithm = algorithm - self.__defineName() + self._algorithm = algorithm + self._defineName() - @property - def radius(self): - return self.__radius - @radius.setter + @make_lproperty + def radius(self): pass + + @radius.lsetter def radius(self,radius): - self.__radius = radius - self.__defineName() + self._radius = radius + self._defineName() - @property - def inputdef(self): - return self.__inputdef - @inputdef.setter + @make_lproperty + def inputdef(self): pass + + @inputdef.lsetter def inputdef(self,inputdef): - self.__inputdef = inputdef - self.__defineName() - - def __defineName(self): - self.basename = buildJetAlgName(self.__algorithm,self.__radius)+self.__inputdef.label + self._inputdef = inputdef + self._defineName() + + @make_lproperty + def prefix(self): pass + + @prefix.lsetter + def prefix(self,p): + self._prefix = p + self._defineName() + + @make_lproperty + def suffix(self): pass + + @suffix.lsetter + def suffix(self,p): + self._suffix = p + self._defineName() + + @make_lproperty + def basename(self): pass + + @basename.lsetter + def basename(self,v): + raise Exception("Can NOT set property basename of JetDefinition ",self," Change prefix or suffix instead.") + + + @make_lproperty + def ghostdefs(self): pass + @make_lproperty + def modifiers(self): pass + @make_lproperty + def extrainputs(self): pass + @make_lproperty + def standardRecoMode(self): pass + + @make_lproperty + def VRMinRadius(self): pass + @make_lproperty + def VRMassScale(self): pass + + + def fullname(self): + return self.prefix+self.basename+"Jets"+self.suffix + + def _defineName(self): + self._basename = buildJetAlgName(self.algorithm,self.radius)+self.inputdef.label # .label if self.inputdef.basetype == xAODType.CaloCluster: # Omit cluster origin correction from jet name # Keep the origin correction explicit because sometimes we may not # wish to apply it, whereas PFlow corrections are applied implicitly - self.basename = self.basename.replace("Origin","") + self._basename = self.basename.replace("Origin","") pass # Define a string conversion for printing @@ -291,14 +194,19 @@ class JetDefinition(object): ######################################################################## -# Helper to instantiate a generic jet modifier -# Tools that typically have more complex properties set should have -# their own dedicated helper functions defined +@clonable +@onlyAttributesAreProperties class JetModifier(object): + """Helper to instantiate a generic jet modifier + Tools that typically have more complex properties set should have + their own dedicated helper 'createfn' functions defined""" + def __init__(self,tooltype,toolname, - helperfn=None, - prereqs=[],modspec=None,passJetDef=False): + createfn=None, + filterfn=_condAlwaysPass, + prereqs=[],modspec=None,passJetDef=False, + ): # For the easy cases where no helper function is needed. # They will be ignored in the case of a helper, # but are still required such that it's obvious what @@ -315,10 +223,10 @@ class JetModifier(object): # modifier, and a ComponentAccumulator instance, # in case additional supporting tools/services # need to be set up. - if helperfn is None: - self.helperfn = self.getGenericModifier + if createfn is None: + self.createfn = self.getGenericModifier else: - self.helperfn = helperfn + self.createfn = createfn self.modspec = modspec self.passJetDef = passJetDef @@ -328,8 +236,35 @@ class JetModifier(object): # in which case a helper function can be defined. self.prereqs = prereqs + # a function taking a CondFlags as argument and deciding if this JetModifier is compatible + # with the conditions. + # The function must return a tuple : (bool, "reason of failure") + self.filterfn = filterfn + + self._instanceMap = {} + #self._locked = lock + + + + @make_lproperty + def tooltype(self):pass + @make_lproperty + def toolname(self):pass + @make_lproperty + def createfn(self):pass + @make_lproperty + def modspec(self):pass + @make_lproperty + def passJetDef(self):pass + @make_lproperty + def prereqs(self):pass + @make_lproperty + def filterfn(self):pass + + + def __hash__(self): - return hash((self.toolname,self.tooltype,self.helperfn.__name__,self.modspec,self.passJetDef,str(self.prereqs))) + return hash((self.toolname,self.tooltype,self.createfn.__name__,self.modspec,self.passJetDef,str(self.prereqs))) def __eq__(self,rhs): return self.__hash__() == rhs.__hash__() @@ -348,12 +283,194 @@ class JetModifier(object): tool = CompFactory.getComp(self.tooltype)(self.toolname) return tool - def getPrereqs(self,modspec="",jetdef=None): - prereqs = [] - if self.prereqs.__class__ == list: - prereqs += self.prereqs - else: - prereqs += self.prereqs(modspec,jetdef) - jetlog.verbose("Prereqs for {0}: [{1}]".format(self.toolname,", ".join(prereqs))) - return prereqs + + +######################################################################## + +@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). + + Currently this class is mainly here to hold a helper (algoBuilder) function in charge of creating an 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 : + - name : container name in event store + - objtype : the xAODType (ex: xAODType.TruthParticle, xAODType.CaloCluster, ...) + - algoBuilder [optional] : a function returning a configured algorithm which build the container + the function is called as algoBuilder(parentjetdef, specs) where + parentjetdef is the JetDefinition for which this input building is called. + specs is self.specs + If omitted, it is assumed the container pre-exists in the event store. + - specs [optional] : a string (or anything) which specifies some options, and passed to the algoBuilder function + - 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 for this input definition. + """ + def __init__(self, name, objtype, algoBuilder=None, specs=None, filterfn= _condAlwaysPass, prereqs=[]): + self.name = name + self.basetype = objtype + self.algoBuilder = algoBuilder + self.specs = specs + 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 + def name(self): pass + @make_lproperty + def algoBuilder(self): pass + @make_lproperty + def basetype(self): pass + @make_lproperty + def specs(self): pass + @make_lproperty + def filterfn(self):pass + @make_lproperty + def prereqs(self):pass + + # make outputname an alias of name so JetInputDef shares an interface with JetConstitSeq. + outputname = make_alias("name") + # @make_lproperty + # def outputname(self): pass + # @outputname.setter + # def outputname(self,v): + # raise Exception("Can not set the 'outputname' attribute of a JetInputDef, set its 'name' instead") + + +######################################################################## + +@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 + + +@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 + 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 + 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._locked = lock + + @make_lproperty + def basetype(self): pass + + @make_lproperty + def containername(self): pass + + @make_lproperty + def prereqs(self): pass + + @make_lproperty + def filterfn(self):pass + + # make an alias on containername so JetConstitSource and JetConstitSeq share an interface + inputname = make_alias("containername") + +@clonable +@onlyAttributesAreProperties +class JetConstitSeq(JetConstitSource): + """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. + """ + def __init__(self, + name, + objtype, # The type of xAOD object from which to build the jets + 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 + label = None, + filterfn=_condAlwaysPass, + lock = False, # lock all properties of this instance + ): + + JetConstitSource.__init__(self,name, objtype, outputname, prereqs=prereqs, filterfn=filterfn,label=label,lock=False, finalinit=False, ) + self.inputname = inputname or name + self.modifiers = modifiers + + self._instanceMap = dict() # internal maps of modifier to actual configuration object + + self._locked = lock + + @make_lproperty + def modifiers(self): pass + + @make_lproperty + def inputname(self): pass + @make_lproperty + def label(self): pass + + + + def __hash__(self): + return hash((self._basetype,str(self._modifiers))) + + def __eq__(self,rhs): + return self.__hash__() == rhs.__hash__() + + def __ne__(self,rhs): + return (not self.__eq__(rhs)) + + + + # Define a string conversion for printing + def __str__(self): + return "JetConstitSeq({0}: {1})".format(self.name,self.inputname) + # Need to override __repr__ for printing in lists etc + __repr__ = __str__ + diff --git a/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py index 4c90abb5bccdefdf8ad5c719705dcb5b34b63726..b12202360b7bc2ec715635f66874f1b7303c79d9 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetGroomConfig.py @@ -13,8 +13,29 @@ jetlog = Logging.logging.getLogger('JetGroomConfig') from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator from AthenaConfiguration.ComponentFactory import CompFactory +import JetRecConfig.JetModConfig as modH +from JetRecConfig.JetRecConfig import instantiateAliases, buildJetModifierList + import six + +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 + """ + + instantiateAliases(groomdef.ungroomeddef) + + for mod in groomdef.modifiers: + modInstance = modH.aliasToModDef(mod, groomdef) + groomdef._prereqDic['mod:'+mod] = modInstance + groomdef._prereqOrder.append('mod:'+mod) + + ######################################################################## # Get a jet groomer class given a tool name and the grooming definition object # The pjsin argument is for forwards compatibility when we switch to @@ -30,7 +51,7 @@ def getJetGroomer(groomdef,pjsin): # Function for configuring the jet algorithm and groomers, given the # set of dependencies # -def getJetGroomAlg(jetname,groomdef,pjsin,modlist,monTool=None): +def getJetGroomAlg(jetname,groomdef,pjsin,monTool=None): jetlog.debug("Configuring JetAlgorithm \"jetalg_{0}\"".format(jetname)) from . import JetRecConfig @@ -39,16 +60,11 @@ def getJetGroomAlg(jetname,groomdef,pjsin,modlist,monTool=None): groomer = getJetGroomer(groomdef,pjsin) groomer.JetBuilder = builder - from . import JetModConfig - mods = [] - # Dependency resolution should be done externally - for moddef,modspec in modlist: - mod = JetModConfig.getModifier(groomdef,moddef,modspec) - mods.append(mod) + mods = buildJetModifierList(groomdef) rectool = CompFactory.JetRecTool(jetname, JetGroomer=groomer, - InputContainer=groomdef.ungroomedname, + InputContainer=groomdef.ungroomeddef.fullname(), OutputContainer=jetname, JetPseudojetRetriever=CompFactory.JetPseudojetRetriever("jpjretriever"), JetModifiers=mods) diff --git a/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py b/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py index 578e105bdd1e3ab7ab7a5f6003b05c560ff53d75..2d5aa6bdd27811a8fd06a2edd9ca5470f32421a1 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetGrooming.py @@ -11,29 +11,38 @@ __all__ = ["GroomingDefinition","JetTrimming","JetSoftDrop"] from AthenaCommon import Logging +from .Utilities import make_lproperty, onlyAttributesAreProperties, clonable jetlog = Logging.logging.getLogger('JetGrooming') +@clonable +@onlyAttributesAreProperties class GroomingDefinition(object): def __init__(self, ungroomeddef, # Ungroomed JetDefinition - ungroomedname, # Input collection name (cannot be determined uniquely from inputdef) + #ungroomedname, # Input collection name (cannot be determined uniquely from inputdef) groomspec, # Dict describing the grooming settings - modifiers=[]): # JetModifiers to run after grooming + modifiers=[], # JetModifiers to run after grooming + lock=False, + ): - self.ungroomedname = ungroomedname + #self.ungroomedname = ungroomedname # Dedicated setter/getter - self.__ungroomeddef = ungroomeddef - self.__groomspec = groomspec + self._ungroomeddef = ungroomeddef.clone() # to avoid messing with external jetdef + self._groomspec = groomspec - self.__checkGroomSpec(groomspec) - self.__groomspec = groomspec + self._checkGroomSpec(groomspec) + self._groomspec = groomspec - self.__defineName() + self._defineName() self.modifiers = modifiers # Tools to modify the jet - pass + # used internally to resolve dependencies + self._prereqDic = {} + self._prereqOrder = [] + self._locked = lock + def __hash__(self): return hash("") @@ -47,15 +56,34 @@ class GroomingDefinition(object): # Define core attributes as properties, with # custom setter/getter such that if changed, these # force resetting of the jet name - @property - def ungroomeddef(self): - return self.__ungroomeddef - @ungroomeddef.setter + @make_lproperty + def ungroomeddef(self): pass + @ungroomeddef.lsetter def ungroomeddef(self,ungroomeddef): - self.__ungroomeddef = ungroomeddef - self.defineName() + self._ungroomeddef = ungroomeddef.clone() + self._defineName() + + + @make_lproperty + def modifiers(self): pass + + @make_lproperty + def groomspec(self): + return self.__groomspec + @groomspec.lsetter + def groomspec(self,groomspec): + self._checkGroomSpec(groomspec) + self._groomspec = groomspec + self._defineName() + + # To override in derived classes + def groomSpecAsStr(self): + return "Groomed" - def __checkGroomSpec(self,groomspec): + def fullname(self): + return self.ungroomeddef.prefix+self.basename+"Jets" + + def _checkGroomSpec(self,groomspec): # Check if options are supported (implemented) groomalg = groomspec["groomalg"] supportedGrooming = ["Trim","SoftDrop"] @@ -64,33 +92,20 @@ class GroomingDefinition(object): for groomalg in supportedGrooming: jetlog.error(groomalg) raise KeyError("Invalid grooming algorithm choice: {0}".format(groomalg)) + + # @property + # def inputdef(self): + # return self.__inputdef + # @inputdef.setter + # def inputdef(self,inputdef): + # self.__inputdef = inputdef + # self._defineName() - @property - def groomspec(self): - return self.__groomspec - @groomspec.setter - def groomspec(self,groomspec): - self.__groomspec = groomspec - self.defineName() - - @property - def inputdef(self): - return self.__inputdef - @inputdef.setter - def inputdef(self,inputdef): - self.__inputdef = inputdef - self.defineName() - - # To override in derived classes - def groomSpecAsStr(self): - return "Groomed" - def __defineName(self): - ungroomedNameBase = self.ungroomeddef.basename + def _defineName(self): # chop the label off so we can insert the trimming spec - groomedName = ungroomedNameBase + self.groomSpecAsStr() + groomedName = self.ungroomeddef.basename + self.groomSpecAsStr() self.basename = groomedName - pass # Define a string conversion for printing def __str__(self): @@ -99,13 +114,17 @@ class GroomingDefinition(object): __repr__ = __str__ +@clonable +@onlyAttributesAreProperties class JetTrimming(GroomingDefinition): def __init__(self, ungroomeddef, # Ungroomed JetDefinition - ungroomedname, # Input collection name (cannot be determined uniquely from inputdef) + #ungroomedname, # Input collection name (cannot be determined uniquely from inputdef) smallR, # Subjet radius ptfrac, # Minimum subjet pt fraction - modifiers=[]): # JetModifiers to run after grooming + modifiers=[], # JetModifiers to run after grooming + lock=False, + ): # Apart from groomalg and ToolType, these correspond to the # grooming tool property values @@ -122,7 +141,7 @@ class JetTrimming(GroomingDefinition): "PtFrac": ptfrac, } - super(JetTrimming,self).__init__(ungroomeddef,ungroomedname,groomspec,modifiers) + super(JetTrimming,self).__init__(ungroomeddef,groomspec,modifiers,lock=lock,finalinit=False) def groomSpecAsStr(self): ptfrac = self.groomspec["PtFrac"] @@ -134,13 +153,16 @@ class JetTrimming(GroomingDefinition): groomstr = "TrimmedPtFrac{}SmallR{}".format(ptfracstr,smallRstr) return groomstr +@clonable +@onlyAttributesAreProperties class JetSoftDrop(GroomingDefinition): def __init__(self, ungroomeddef, # Ungroomed JetDefinition - ungroomedname, # Input collection name (cannot be determined uniquely from inputdef) + #ungroomedname, # Input collection name (cannot be determined uniquely from inputdef) zcut, # ZCut beta, # Beta - modifiers=[]): # JetModifiers to run after grooming + modifiers=[], # JetModifiers to run after grooming + lock=False): # Apart from groomalg and ToolType, these correspond to the # grooming tool property values @@ -156,7 +178,7 @@ class JetSoftDrop(GroomingDefinition): "Beta": beta, } - super(JetSoftDrop,self).__init__(ungroomeddef,ungroomedname,groomspec,modifiers) + super(JetSoftDrop,self).__init__(ungroomeddef,groomspec,modifiers, lock=lock, finalinit=False) def groomSpecAsStr(self): beta = self.groomspec["Beta"] diff --git a/Reconstruction/Jet/JetRecConfig/python/JetInputConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetInputConfig.py new file mode 100644 index 0000000000000000000000000000000000000000..6c9e48d3f667d7b99fbc0c362c43d4721eb04ac7 --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/python/JetInputConfig.py @@ -0,0 +1,72 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +""" +# JetInputConfig: A helper module providing function to setup algorithms +# in charge of preparing input sources to jets (ex: EventDensity algo, track +# or truth selection,...) +# +# Author: P-A Delsart # +""" + + +from AthenaConfiguration.ComponentFactory import CompFactory + +def _buildJetAlgForInput(suffix, tools ): + jetalg = CompFactory.JetAlgorithm("jetalg_"+suffix, + Tools = tools, + ) + return jetalg + +def buildJetSelectedTracks( parentjetdef, inputspec ): + from JetRecTools import JetRecToolsConfig + # Jet track selection + t = JetRecToolsConfig.getTrackSelTool(doWriteTracks=True) + return _buildJetAlgForInput("JetSelectedTrack", + tools = [ t ] + ) + + +def buildJetTrackVertexAssoc( parentjetdef, inputspec ): + from JetRecTools import JetRecToolsConfig + # Jet track selection + return _buildJetAlgForInput("JetTVA", + tools = [ JetRecToolsConfig.getTrackVertexAssocTool() ] + ) + + + + +def buildJetInputTruth(parentjetdef, truthmod): + truthmod = truthmod or "" + from ParticleJetTools.ParticleJetToolsConfig import getCopyTruthJetParticles + return _buildJetAlgForInput("truthpartcopy_"+truthmod, + tools = [ getCopyTruthJetParticles(truthmod) ] + ) + +def buildLabelledTruth(parentjetdef, truthmod): + from ParticleJetTools.ParticleJetToolsConfig import getCopyTruthLabelParticles + tool = getCopyTruthLabelParticles(truthmod) + return _buildJetAlgForInput("truthlabelcopy_"+truthmod, + tools = [ tool ] + ) + + + +######################################################################## +def buildEventShapeAlg( parentjetdef, inputspec ): + """Function producing an EventShapeAlg to calculate + median energy density for pileup correction""" + + nameprefix = inputspec or "" + label = parentjetdef.inputdef.label + rhokey = nameprefix+"Kt4"+label+"EventShape" + rhotoolname = "EventDensity_"+nameprefix+"Kt4"+label + + rhotool = CompFactory.EventDensityTool(rhotoolname) + rhotool.InputContainer = "PseudoJet"+label # same as in PseudoJet algs + rhotool.OutputContainer = rhokey + + eventshapealg = CompFactory.EventDensityAthAlg("{0}{1}Alg".format(nameprefix,rhotoolname)) + eventshapealg.EventDensityTool = rhotool + + return eventshapealg + diff --git a/Reconstruction/Jet/JetRecConfig/python/JetModConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetModConfig.py index 8c6478b8f47a65c19a2d1889e8975e56843da335..9d157f17c2bb1831518bbfeceba3b35e50b502b6 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetModConfig.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetModConfig.py @@ -1,79 +1,18 @@ -# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration - -######################################################################## -# # -# JetModConfig: A helper module for configuring jet modifier tools # -# Author: TJ Khoo # -# # -######################################################################## +# 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') -######################################################################## -# Function for generating a list of JetModifier tools -# Keep a default empty modstrings as we might want to actually -# specify some defaults (e.g. calib/filter/sort) for specific configs -# -# First incarnation is "dumb" but we want this to be "smart" and set up -# the right tools in order, propagating dependencies. -# -def getFinalModifierListAndPrereqs(mods_initial, jetdef): - if len(mods_initial)==0: return [], set() - mods_final = [] - # By the end of the recursion, all mods should have been applied already. - prereqs = set() - - modlog.verbose("Initial mod list: " + str(mods_initial)) - - # Loop over the initial list - # Extract the concrete definition and specifier string - # Then grab any prereqs, and expand if the list contains mods - for mod in mods_initial: - moddef, modspec = getModDefAndSpec( mod ) - modreqs = moddef.getPrereqs(modspec,jetdef) - prereqmods = [] - for req in modreqs: - reqtype, reqspec = req.split(':',1) - if reqtype == "mod": - prereqmods.append(reqspec) - else: - prereqs.add( req ) - # Recursion happens here - prereqmods_final, moreprereqs = getFinalModifierListAndPrereqs( prereqmods, jetdef ) - prereqs.update( moreprereqs ) - mods_final += prereqmods_final + [(moddef,modspec)] - - modlog.verbose("Current input prereqs: {0}".format(prereqs)) - modlog.verbose("Final modlist: {0}".format(mods_final)) - return mods_final, prereqs - -# [Optional] Args are: -# 1. Tool Type (may be ignored if the helper is a custom one) -# 2. Tool Name (may be ignored if the helper is a custom one) -# [3.] Config helper -# [4.] Prereqs (default to []). If "None", will try to get from the function. -# [5.] Flag passJetDef specifying if the helper needs the jet definition -# We use a helper class to encapsulate the modifier configuration - -# Translate modifier string into JetModifier if necessary -# Extracts the modspec from the string or the config object -def getModDefAndSpec(mod): - from .StandardJetMods import jetmoddict - moddef = mod - modspec = "" - if mod.__class__ == type("str"): - modkey = mod - if ":" in mod: - modkey, modspec = mod.split(':',1) - moddef = jetmoddict[modkey] - else: - modspec = moddef.modspec - modlog.verbose("Interpreted modifier string {0} as {1} with specification \"{2}\"".format(mod,moddef,modspec)) - return moddef, modspec - -# Translate JetModifier into a concrete tool def getModifier(jetdef, moddef, modspec): + """Translate JetModifier into a concrete tool""" modtool = None modlog.verbose("Retrieving modifier {0}".format(str(moddef))) @@ -86,11 +25,80 @@ def getModifier(jetdef, moddef, modspec): # Get the modifier tool try: - modtool = moddef.helperfn(**kwargs) + modtool = moddef.createfn(**kwargs) 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.helperfn) ) + modlog.error( "Helper function is \"{0}\"".format(moddef.createfn) ) raise ValueError( "JetModConfig unable to handle mod {0} with spec \"{1}\"".format(moddef,modspec) ) 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() . + """ + + from JetRecTools.JetRecToolsConfig import trackcollectionmap + + 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 aabff57aeaab8760497ef7d29a2a7402a89e41f8..4ba2d6d7e5be0ff1e36fe393b24f6252a6b4b911 100644 --- a/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py +++ b/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py @@ -1,55 +1,48 @@ # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration -######################################################################## -# # -# JetRecConfig: A helper module for configuring jet reconstruction # -# Author: TJ Khoo # -# # +""" +JetRecConfig: A helper module for configuring jet reconstruction + +Author: TJ Khoo, P-A Delsart +""" + ######################################################################## from AthenaCommon import Logging jetlog = Logging.logging.getLogger('JetRecConfig') -from xAODBase.xAODType import xAODType from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator from AthenaConfiguration.ComponentFactory import CompFactory +import JetRecConfig.ConstModHelpers as constH +import JetRecConfig.JetModConfig as modH + -def propertiesOf(comp): - """ Obtain properties irrespectively of the config system""" - try: - propNames = comp._descriptors - return propNames - except Exception: - pass - return comp.properties() -__all__ = ["JetRecCfg", "resolveDependencies", "JetInputCfg"] +__all__ = ["JetRecCfg", "JetInputCfg"] + + ######################################################################## -# Top-level function for running jet finding -# (i.e. clustering from inputs) -# This returns a ComponentAccumulator that can be merged with others -# from elsewhere in the job, but will provide everything needed to -# reconstruct one jet collection. -# This could still be modularised further into the subcomponents of the -# jet reconstruction job. For now, based on public tools, as private -# tool migration has not been completed. -# -# Receives the jet definition and input flags, mainly for input file -# peeking such that we don't attempt to reproduce stuff that's already -# in the input file -def JetRecCfg(jetdef, configFlags, jetnameprefix="",jetnamesuffix="", evsprefix="", jetnameoverride=None): - # Ordinarily we want to have jet collection names be descriptive and derived from - # the configured reconstruction. - # Nevertheless, we allow an explicit specification when necessary - # e.g. to ensure that the correct name is used in grooming operations - if jetnameoverride: - jetsfullname = jetnameoverride - else: - jetsfullname = jetnameprefix+jetdef.basename+"Jets"+jetnamesuffix + +def JetRecCfg(jetdef0, configFlags): + """Top-level function for running jet finding + (i.e. clustering from inputs) + This returns a ComponentAccumulator that can be merged with others + from elsewhere in the job, but will provide everything needed to + reconstruct one jet collection. + This could still be modularised further into the subcomponents of the + jet reconstruction job. + Receives the jet definition and input flags, mainly for input file + peeking such that we don't attempt to reproduce stuff that's already + in the input file + """ + # we clone the jetdef, so we're sure we're not using a 'locked' one + jetdef = jetdef0.clone() + + jetsfullname = jetdef.fullname() jetlog.info("Setting up to find {0}".format(jetsfullname)) sequencename = jetsfullname @@ -58,328 +51,101 @@ def JetRecCfg(jetdef, configFlags, jetnameprefix="",jetnamesuffix="", evsprefix= from AthenaCommon.CFElements import parOR components.addSequence( parOR(sequencename) ) - deps = resolveDependencies( jetdef ) + # create proper config instances for each input and ghost aliases in this jetdef + # this implicitely calculates and adds the dependencies. + instantiateAliases(jetdef) + + # check if the conditions are compatible with the inputs & modifiers of this jetdef. + # if in standardRecoMode we will remove whatever is incompatible and still try to run + # if not, we raise an exception + removeComponentFailingConditions(jetdef, configFlags, raiseOnFailure= not jetdef.standardRecoMode) + # Schedule the various input collections. # We don't have to worry about ordering, as the scheduler # will handle the details. Just merge the components. - # - # To facilitate running in serial mode, we also prepare - # the constituent PseudoJetAlgorithm here (needed for rho) - inputcomps = JetInputCfg(deps["inputs"], configFlags, sequenceName=jetsfullname, evsprefix=evsprefix) - constitpjalg = inputcomps.getPrimary() + inputcomps = JetInputCfg(jetdef, configFlags, sequenceName=jetsfullname) + components.merge(inputcomps) + + # Schedule the constituent PseudoJetAlg + constitpjalg = getConstitPJGAlg( jetdef.inputdef ) constitpjkey = constitpjalg.OutputContainer + components.addEventAlgo( constitpjalg, jetsfullname ) - components.merge(inputcomps) - pjs = [constitpjkey] + pjContNames = [constitpjkey] # Schedule the ghost PseudoJetAlgs - for ghostdef in deps["ghosts"]: + ghostlist = [ key for key in jetdef._prereqOrder if key.startswith('ghost:')] + for ghostkey in ghostlist: + ghostdef = jetdef._prereqDic[ghostkey] ghostpjalg = getGhostPJGAlg( ghostdef ) components.addEventAlgo( ghostpjalg, sequencename ) ghostpjkey = ghostpjalg.OutputContainer - pjs.append( ghostpjkey ) + pjContNames.append( ghostpjkey ) # Generate a JetAlgorithm to run the jet finding and modifiers # (via a JetRecTool instance). - mergepjalg = CompFactory.PseudoJetMerger( - "pjmergealg_"+jetsfullname, - InputPJContainers = pjs, - OutputContainer = "PseudoJetMerged_"+jetsfullname) - - components.addEventAlgo(mergepjalg, sequencename) - - jetrecalg = getJetRecAlg(jetsfullname, jetdef, "PseudoJetMerged_"+jetsfullname, deps["mods"]) - + jetrecalg = getJetAlgorithm(jetsfullname, jetdef, pjContNames) components.addEventAlgo(jetrecalg, sequencename) jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(jetsfullname)) return components -######################################################################## -# The real workhorse -- establishes the full sequence of jet reco, -# recursively expanding the prerequisites -# -# Avoids constructing any configurables at this stage, the goal being -# to produce a human-readable job description. -def resolveDependencies(jetdef): - - jetlog.info("Resolving dependencies for {0} definition".format(jetdef.basename)) - - # Accumulate prerequisites of the base constituent type - # We just collect everything and sort out the types later - prereqs = set() # Resolve duplication as we go - prereqs.update( getConstitPrereqs( jetdef.inputdef ) ) - prereqs.update( set( ["input:"+dep for dep in jetdef.extrainputs] ) ) - - # Add the Filter modifier if desired (usually it is) - # It might be simpler to just eliminate ptminfilter - # and always make this an explicit modifier - mods_initial = list(jetdef.modifiers) - if jetdef.ptminfilter>1e-9: - filtstr = "Filter:{0:.0f}".format(jetdef.ptminfilter) - # Insert pt filter after calibration if present - idx=-1 - for imod, mod in enumerate(mods_initial): - if mod.startswith("Calib"): - idx = imod+1 - break - mods_initial.insert(idx,filtstr) - - # Accumulate prerequisites of the modifiers, as these are - # the most extensive. Internally resolves modifier chains, - # returning an updated modifiers list - # Need to use a list, as the order matters. - # The elements of the "final" list are tuples extracting - # the modifier specification. - from . import JetModConfig - mods_final, modprereqs = JetModConfig.getFinalModifierListAndPrereqs( mods_initial, jetdef ) - - # Remove the duplicates in the mod list -- just do this - # once at the end and preserve ordering. - def dedupe(mylist): - outlist = [] - usedset = set() - for item in mylist: - if not (item in usedset): - outlist.append(item) - usedset.add(item) - return outlist - mods_final = dedupe( mods_final ) - - prereqs.update( modprereqs ) - - # Ghost prerequisites are only of type input, so we can - # afford to sort now. - prereqdict = {"ghost":set(), "input":set()} - prereqdict.update( classifyPrereqs(prereqs) ) - - # Copy the explicitly requested ghost defs and add to - # these those required by modifiers. - ghostdefs = set(jetdef.ghostdefs).union(prereqdict["ghost"]) - # Expand from strings to JetGhost objects where needed. - ghostdefs = expandPrereqs( "ghost",ghostdefs ) - - # Accumulate prerequisites of the ghost-associated types - jetlog.info(" Full list of ghosts: ") - for ghostdef in sorted(list(ghostdefs), key=lambda g: g.inputtype): - jetlog.info(" " + str(ghostdef)) - gprereqs = getGhostPrereqs(ghostdef) - prereqdict["input"].update( [req.split(':',1)[1] for req in gprereqs] ) - - jetlog.info(" Full list of mods: ") - for mod, modspec in mods_final: - jetlog.info(" " + str(mod) + ("" if not modspec else ": \"{0}\"".format(modspec))) - - # Return a dict of the dependencies, converting sets to lists. - # May want to further separate input deps. - dependencies = { - "inputs": [jetdef.inputdef] + sorted(list( prereqdict["input"] )), - "ghosts": list( ghostdefs ), - "mods": mods_final - } - - # We don't expand the inputs at this stage, as they are diverse - # and don't have a dedicated config class. - # Doing so may trigger another level of expansion if the inputs - # include a jet collection. - return dependencies - -######################################################################## -# Function for classifying prerequisites -# -def classifyPrereqs(prereqs): - prereqdict = {} - for req in prereqs: - key,val = req.split(":",1) - jetlog.verbose( "Interpreted prereqs: {0} --> {1}".format(key,val) ) - if key not in prereqdict.keys(): - prereqdict[key] = set() - prereqdict[key].add(val) - - return prereqdict - -######################################################################## -# Function for expanding prerequisites into definitions -# Only supporting ghosts for now, but could be extended -# -def expandPrereqs(reqtype,prereqs): - reqdefs = set() - from .JetDefinition import JetGhost - for req in prereqs: - if reqtype=="ghost": - if req.__class__ == JetGhost: - reqdefs.add( req ) - else: - ghostdef = JetGhost(req) - reqdefs.add( ghostdef ) - jetlog.debug("Expanded prereq {0} to {1}".format(req,ghostdef)) - else: - jetlog.error("Prereqs \"{0}\" unsupported!".format(reqtype)) - return None - return reqdefs - -######################################################################## -# 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) ######################################################################## -# Function producing an EventShapeAlg to calculate -# medaian energy density for pileup correction -# -def getEventShapeAlg( constit, constitpjkey, evsprefix="" ): - - rhokey = evsprefix+"Kt4"+constit.label+"EventShape" - rhotoolname = "EventDensity_{}Kt4{}".format(evsprefix,constit.label) - - rhotool = CompFactory.EventDensityTool(rhotoolname) - rhotool.InputContainer = constitpjkey - rhotool.OutputContainer = rhokey +def JetInputCfg(jetdef, configFlags, sequenceName): + """Function for setting up inputs to jet finding - eventshapealg = CompFactory.EventDensityAthAlg("{0}{1}Alg".format(evsprefix,rhotoolname)) - eventshapealg.EventDensityTool = rhotool + This includes constituent modifications, track selection, copying of + input truth particles and event density calculations + """ - return eventshapealg - -######################################################################## -# Function for setting up inputs to jet finding -# -# This includes constituent modifications, track selection, copying of -# input truth particles and event density calculations -def JetInputCfg(inputdeps, configFlags, sequenceName, evsprefix=""): jetlog.info("Setting up jet inputs.") components = ComponentAccumulator(sequenceName) jetlog.info("Inspecting input file contents") - filecontents = [i for i in configFlags.Input.Collections] - - constit = inputdeps[0] - # Truth and track particle inputs are handled later - if constit.basetype not in [xAODType.TruthParticle, xAODType.TrackParticle] and constit.inputname!=constit.rawname: - # Protection against reproduction of existing containers - if constit.inputname in filecontents: - jetlog.debug("Input container {0} for label {1} already in input file.".format(constit.inputname, constit.label)) - else: - jetlog.debug("Preparing Constit Mods for label {0} from {1}".format(constit.label,constit.inputname)) - # May need to generate constituent modifier sequences to - # produce the input collection - from . import ConstModHelpers - constitalg = ConstModHelpers.getConstitModAlg(constit) - if constitalg: - components.addEventAlgo(constitalg) + filecontents = configFlags.Input.Collections - # Schedule the constituent PseudoJetAlg - constitpjalg = getConstitPJGAlg( constit ) - constitpjkey = constitpjalg.OutputContainer - # Mark the constit PJGAlg as the primary so that the caller - # can access the output container name - components.addEventAlgo( constitpjalg, primary=True ) - - # Track selection and vertex association kind of go hand in hand, though it's not - # completely impossible that one might want one and not the other - if "JetSelectedTracks" in inputdeps or "JetTrackVtxAssoc" in inputdeps: - jetlog.debug("Setting up input track containers and track-vertex association") - from JetRecTools import JetRecToolsConfig - # Jet track selection - jettrackselloose = JetRecToolsConfig.getTrackSelTool(doWriteTracks=True) - jettvassoc = JetRecToolsConfig.getTrackVertexAssocTool() - - jettrkprepalg = CompFactory.JetAlgorithm("jetalg_TrackPrep") - jettrkprepalg.Tools = [ jettrackselloose, jettvassoc ] - components.addEventAlgo( jettrkprepalg ) - - # Resolve the rest of the input dependencies - for dep in inputdeps[1:]: - # Generate prequisite truth particle collections - # There may be more than one. - if dep.startswith("JetInputTruthParticles"): - # Special conditions e.g. "WZ" are set as a suffix preceded by ":" - truthmod = '' - if ":" in dep: - truthmod = dep.split(':')[1] - tpcname = "truthpartcopy"+truthmod - jetlog.debug("Setting up input truth particle container JetInputTruthParticles{0}".format(truthmod)) - - from ParticleJetTools.ParticleJetToolsConfig import getCopyTruthJetParticles - tpc = getCopyTruthJetParticles(truthmod) - - tpcalg = CompFactory.JetAlgorithm("jetalg_{0}".format(tpcname)) - tpcalg.Tools = [tpc] - components.addEventAlgo(tpcalg) - - # Truth particles specifically for truth labels - elif dep.startswith("TruthLabel"): - truthlabel = dep[10:] - tpcname = "truthpartcopy_"+truthlabel - - jetlog.debug("Setting up input truth particle container TruthLabel{0}".format(truthlabel)) - from ParticleJetTools.ParticleJetToolsConfig import getCopyTruthLabelParticles - tpc = getCopyTruthLabelParticles(truthlabel) - - tpcalg = CompFactory.JetAlgorithm("jetalg_{0}".format(tpcname)) - tpcalg.Tools = [tpc] - components.addEventAlgo(tpcalg) - - # Calculate the event density for jet area subtraction taking the - # jet constituents as input - # Possibly not needed if constituent suppression has been applied. - # Will want to update the standalone ED python for other uses, - # e.g. isolation or rho from constituents that are not used to - # build a particular jet collection (e.g. neutral PFOs) - # - # Needs protection against reproduction of existing containers - elif dep == "EventDensity": - rhokey = "Kt4"+constit.label+"EventShape" - if rhokey in filecontents: - jetlog.info("Event density {0} for label {1} already in input file.".format(rhokey, constit.label)) + inputdeps = [ inputkey for inputkey in jetdef._prereqOrder if inputkey.startswith('input:')] + + for inputfull in inputdeps: + #inputkey = inputfull[6:] # remove 'input:' + inputInstance = jetdef._prereqDic[inputfull] + from .JetDefinition import JetConstitSource + + if isinstance(inputInstance, JetConstitSource): + if inputInstance.containername in filecontents: + jetlog.debug("Input container {0} for label {1} already in input file.".format(inputInstance.containername, inputInstance.name)) else: - components.addEventAlgo( getEventShapeAlg(constit,constitpjkey,evsprefix) ) - + 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) + if constitalg: + components.addEventAlgo(constitalg) + else: + jetlog.debug("Requesting input {} with function {} and specs {}".format(inputInstance.name, inputInstance.algoBuilder, inputInstance.specs) ) + # inputInstance must be a JetInputDef + if inputInstance.algoBuilder: + components.addEventAlgo( inputInstance.algoBuilder( jetdef, inputInstance.specs ) ) + else: + # for now just hope the input will be present... + pass return components -######################################################################## -# Functions for generating PseudoJetAlgorithms, including determining -# the prerequisites for their operation -# -def getConstitPrereqs(basedef): - prereqs = [] - if basedef.basetype==xAODType.TrackParticle: - prereqs = ["input:JetSelectedTracks","input:JetTrackVtxAssoc"] - elif basedef.basetype==xAODType.TruthParticle: - prereqs = ["input:JetInputTruthParticles:"+basedef.inputname[22:]] - return prereqs - -def getGhostPrereqs(ghostdef): - jetlog.verbose("Getting ghost PseudoJets of type {0}".format(ghostdef.inputtype)) - - prereqs = [] - if ghostdef.inputtype=="Track": - prereqs = ["input:JetSelectedTracks","input:JetTrackVtxAssoc"] - elif ghostdef.inputtype.startswith("TruthLabel"): - truthsuffix = ghostdef.inputtype[5:] - prereqs = ["input:TruthLabel"+truthsuffix] - elif ghostdef.inputtype == "Truth": - prereqs = ["input:JetInputTruthParticles"] - return prereqs - -def getConstitPJGAlg(basedef): - jetlog.debug("Getting PseudoJetAlg for label {0} from {1}".format(basedef.label,basedef.inputname)) - # - full_label = basedef.label - if basedef.basetype == xAODType.Jet: - full_label += "_"+basedef.inputname +def getConstitPJGAlg(constitdef): + """returns a configured PseudoJetAlgorithm which converts the inputs defined by constitdef into fastjet::PseudoJet""" + + jetlog.debug("Getting PseudoJetAlg for label {0} from {1}".format(constitdef.name,constitdef.inputname)) + + full_label = constitdef.label + pjgalg = CompFactory.PseudoJetAlgorithm( - "pjgalg_"+basedef.label, - InputContainer = basedef.inputname, + "pjgalg_"+constitdef.label, + InputContainer = constitdef.containername, OutputContainer = "PseudoJet"+full_label, Label = full_label, SkipNegativeEnergy=True @@ -387,44 +153,50 @@ def getConstitPJGAlg(basedef): return pjgalg def getGhostPJGAlg(ghostdef): - label = "Ghost"+ghostdef.inputtype - kwargs = { - "OutputContainer": "PseudoJet"+label, - "Label": label, - "SkipNegativeEnergy": True - } + """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. + """ + label = "Ghost"+ghostdef.label # IMPORTANT !! "Ghost" in the label will be interpreted by teh C++ side ! + + kwargs = dict( + InputContainer = ghostdef.containername, + OutputContainer= "PseudoJet"+label, + Label= label, + SkipNegativeEnergy= True, + #OutputLevel = 3, + ) pjaclass = CompFactory.PseudoJetAlgorithm - if ghostdef.inputtype=="MuonSegment": + if ghostdef.basetype=="MuonSegment": # Muon segments have a specialised type pjaclass = CompFactory.MuonSegmentPseudoJetAlgorithm - kwargs = { - "InputContainer":"MuonSegments", - "OutputContainer":"PseudoJet"+label, - "Label":label, - "Pt":1e-20 - } - elif ghostdef.inputtype=="Track": - kwargs["InputContainer"] = "JetSelectedTracks" - elif ghostdef.inputtype.startswith("TruthLabel"): - truthsuffix = ghostdef.inputtype[5:] - kwargs["InputContainer"] = "TruthLabel"+truthsuffix - elif ghostdef.inputtype == "Truth": - kwargs["InputContainer"] = "JetInputTruthParticles" - else: - raise ValueError("Unhandled ghost type {0} received!".format(ghostdef.inputtype)) - - pjgalg = pjaclass( - "pjgalg_"+label, - **kwargs - ) + kwargs.update( Pt =1e-20 ) # ??,) + kwargs.pop('SkipNegativeEnergy') + + pjgalg = pjaclass( "pjgalg_"+label, **kwargs ) return pjgalg -######################################################################## -# Function for configuring the jet algorithm and builders, given the -# set of dependencies -# -def getJetAlgorithm(jetname, jetdef, pjs, modlist, monTool = None): + +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() @@ -432,25 +204,72 @@ def getJetAlgorithm(jetname, jetdef, pjs, modlist, monTool = None): 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 + +######################################################################## +# Function that substitues JetRecTool + JetAlgorithm +# +def getJetRecAlg(jetname, jetdef, pjContNames, modlist): + + jclust = CompFactory.JetClusterer("builder") + jclust.JetAlgorithm = jetdef.algorithm + jclust.JetRadius = jetdef.radius + jclust.PtMin = jetdef.ptmin + jclust.InputPseudoJets = pjContNames + jclust.GhostArea = 0.01 # In which cases do we not want areas? + jclust.JetInputType = jetdef.inputdef.basetype + from . import JetModConfig mods = [] for moddef,modspec in modlist: mod = JetModConfig.getModifier(jetdef,moddef,modspec) mods.append(mod) - rectool = getJetRecTool(jetname,finder,pjs,mods) - if monTool: rectool.MonTool = monTool + jra = CompFactory.JetRecAlg( + "jetrecalg_"+jetname, + Provider = jclust, + Modifiers = mods, + OutputContainer = jetname) - jetalg = CompFactory.JetAlgorithm("jetalg_"+jetname) - jetalg.Tools = [rectool] + autoconfigureModifiers(jra.Modifiers, jetname) + + return jra - return jetalg ######################################################################## -# Function for generating a jet builder, i.e. converter from -# fastjet EDM to xAOD EDM -# +# 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 + """ # 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") @@ -459,9 +278,9 @@ def getJetBuilder(doArea=True): return builder ######################################################################## -# Function for generating a jet finder, i.e. interface to fastjet # def getJetFinder(jetname, jetdef): + """Creates a jet finder, i.e. interface to fastjet""" finder = CompFactory.JetFinder("jetfind_"+jetname, JetAlgorithm = jetdef.algorithm, JetRadius = jetdef.radius, @@ -485,6 +304,107 @@ def getJetRecTool(jetname, finder, pjs, mods): return jetrec + +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 + """ + + # start with the inputdef (replacing the jetdef attribute to ensure it really is an instance, not only a str) + jetdef.inputdef = constH.aliasToInputDef(jetdef.inputdef, jetdef) + + jetdef._prereqDic['input:'+jetdef.inputdef.name] = jetdef.inputdef + jetdef._prereqOrder.append('input:'+jetdef.inputdef.name) + + for g in jetdef.extrainputs: + gInstance = constH.aliasToInputDef( g , jetdef) + jetdef._prereqDic['input:'+g] = gInstance + jetdef._prereqOrder.append('input:'+g) + + 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) + + 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 removeComponentFailingConditions(jetdef, configflags, raiseOnFailure=True): + """Filters the lists jetdef.modifiers and jetdef.ghosts (and jetdef._prereqOrder), so only the components + comptatible with configflags are selected. + The compatibility is ultimately tested using the component 'filterfn' attributes. + Internally calls the function isComponentPassingConditions() (see below) + """ + jetlog.info("******************") + jetlog.info("Standard Reco mode : filtering components in "+str(jetdef)) + + + ## TODO : + ## do not raise an exceptin immediately. Instead collect all failure + ## then report all of them, then raise + + # define a helper function returning a filtered list of components. + def filterList(inList, compType): + nOut=0 + outList=[] + # loop over components in the list to be filtered + for comp in inList: + fullkey = compType+':'+comp + cInstance = jetdef._prereqDic[fullkey] + ok, reason = isComponentPassingConditions(cInstance, configflags, jetdef._prereqDic) + if not ok : + if raiseOnFailure: + raise Exception("JetDefinition {} can NOT be scheduled. Failure of {} {} reason={}".format( + jetdef, compType, comp, reason) ) + + nOut+=1 + jetlog.info("IMPORTANT : removing {} {} reason={} ".format(compType, comp, reason)) + jetdef._prereqOrder.remove(fullkey) + else: + outList.append(comp) + jetlog.info(" *** Number of {} filtered components = {} final list={}".format(compType, nOut, outList) ) + return outList + # --------- + + # call the helper function to perform filtering : + jetdef.ghostdefs = filterList( jetdef.ghostdefs, "ghost") + jetdef.modifiers = filterList( jetdef.modifiers, "mod") + +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." + + ok, reason = component.filterfn(configflags) + return ok, reason + + + + + if __name__=="__main__": # Setting needed for the ComponentAccumulator to do its thing @@ -518,32 +438,3 @@ if __name__=="__main__": import sys sys.exit(0) - -######################################################################## -# Function that substitues JetRecTool + JetAlgorithm -# -def getJetRecAlg(jetname, jetdef, pjs, modlist): - - jclust = CompFactory.JetClusterer("builder") - jclust.JetAlgorithm = jetdef.algorithm - jclust.JetRadius = jetdef.radius - jclust.PtMin = jetdef.ptmin - jclust.InputPseudoJets = pjs - jclust.GhostArea = 0.01 # In which cases do we not want areas? - jclust.JetInputType = jetdef.inputdef.basetype - - from . import JetModConfig - mods = [] - for moddef,modspec in modlist: - mod = JetModConfig.getModifier(jetdef,moddef,modspec) - mods.append(mod) - - jra = CompFactory.JetRecAlg( - "jetrecalg_"+jetname, - Provider = jclust, - Modifiers = mods, - OutputContainer = jetname) - - autoconfigureModifiers(jra.Modifiers, jetname) - - return jra diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardJetConstits.py b/Reconstruction/Jet/JetRecConfig/python/StandardJetConstits.py new file mode 100644 index 0000000000000000000000000000000000000000..13005ce8aa2e3a15bd1f89ccf5a055a2f91a7e4b --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/python/StandardJetConstits.py @@ -0,0 +1,204 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +""" + StandardJetConstits: A module defining standard definitions for jet constituents. + These can be copied and modified by users who want something a bit + different from what is provided. + + Author: TJ Khoo, P-A Delsart + * Written for the time being to work in R21, but with R22 in mind + + +""" + +######################################################################## +from .JetDefinition import xAODType, JetConstitSeq, JetInputDef, JetConstitModifier, JetConstitSource + + +# 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() + + +# This module contains the helper functions needed to instantiate the input container external +# to Jet domain +import JetRecConfig.JetInputConfig as inputcfg + +def _isMCTrue(flags): + """A simple filter function for testing if we're running in MC + (probably worth re-allocating somehere else)""" + return flags.Input.isMC, "Not MC input" + + +######################################################################## +## List of standard input sources for jets. + +_stdInputList = [ + # Format is : + # JetInputDef( containername , containerType, ...optionnal parameters... ) + + # ***************************** + JetInputDef("CaloCalTopoClusters", xAODType.CaloCluster), + + # ***************************** + JetInputDef("JetETMissParticleFlowObjects", xAODType.ParticleFlow), + + # ***************************** + JetInputDef("JetSelectedTracks", xAODType.TrackParticle, algoBuilder = inputcfg.buildJetSelectedTracks), + JetInputDef("JetTrackVtxAssoc", xAODType.TrackParticle, algoBuilder = inputcfg.buildJetTrackVertexAssoc), + + # ***************************** + JetInputDef("EventDensity", "EventShape", algoBuilder = inputcfg.buildEventShapeAlg, + 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, + prereqs = lambda jetdef : ["input:"+jetdef.inputdef.name], # this will force the input to be build *before* the EventDensity alg. + specs = 'HLT_' + ), + + # ***************************** + JetInputDef("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 ), + + # 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"), +] + + +_truthFlavours = ["BHadronsInitial", "BHadronsFinal", "BQuarksFinal", + "CHadronsInitial", "CHadronsFinal", "CQuarksFinal", + "TausFinal", + "WBosons", "ZBosons", "HBosons", "TQuarksFinal", + "Partons",] +for label in _truthFlavours: + # re-use the main truth input definition : + _stdInputList.append( JetInputDef("TruthLabel"+label, xAODType.TruthParticle, + algoBuilder = inputcfg.buildLabelledTruth, + filterfn=_isMCTrue, specs = label ) ) + + + +# Fill the jetinputdefdic from the above list +for ji in _stdInputList: + ji._locked = True # lock the definitions so we have unmutable references ! + jetinputdefdic[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 + + + + + + +## *************************************** +## List of standard constituent sequences +## This sequences uses the above constit modifiers +_stdSeqList = [ + # Format is : + # JetConstitSeq( name , input_cont_type, list_of_modifiers, inputcontainer, outputcontainer ) + # or + # JetConstitSource( name, input_cont_type, containername) + # see JetDefinition.py for details. + + # ***************************** + # Cluster constituents + JetConstitSeq("EMTopoOrigin", xAODType.CaloCluster, ["EM","Origin"], "CaloCalTopoClusters", "EMOriginTopoClusters"), + JetConstitSeq("LCTopoOrigin",xAODType.CaloCluster, ["LC","Origin"],"CaloCalTopoClusters", "LCOriginTopoClusters"), + JetConstitSeq("LCTopoCSSK", xAODType.CaloCluster, ["LC","Origin","CS","SK"],"CaloCalTopoClusters", "LCOriginTopoCSSK"), + + + + + # ***************************** + # 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'), + + # Particle Flow Objects with Constituent Subtraction + SoftKiller + JetConstitSeq("EMPFlowCSSK", xAODType.ParticleFlow,["CorrectPFO", "CS","SK", "CHS"] , 'JetETMissParticleFlowObjects', 'CSSKParticleFlowObjects'), + + + # ***************************** + # Track constituents + JetConstitSource("Track", xAODType.TrackParticle,'JetSelectedTracks'), + + # Track particles from the primary vertex + JetConstitSeq("PV0Track", xAODType.TrackParticle,["PV0"],'JetSelectedTracks', 'PV0JetSelectedTracks', + prereqs= ["input:JetTrackVtxAssoc"], ), + + # ***************************** + # Muon segments. Only used as ghosts + JetConstitSource("MuonSegment", "MuonSegment", "MuonSegments" ), + + + # ***************************** + # Truth particles (see JetInputDef declarations above for more details) + JetConstitSource("Truth", xAODType.TruthParticle, "JetInputTruthParticles" ), + + JetConstitSource("TruthWZ", xAODType.TruthParticle, "JetInputTruthParticlesNoWZ" ), +] + +for label in _truthFlavours: + _stdSeqList.append( JetConstitSource(label, xAODType.TruthParticle, "TruthLabel"+label ) ) + +# Fill the jetconstitdic from the above list +for jc in _stdSeqList: + jc._locked = True + jetconstitdic[jc.name] = jc diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py b/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py index 3606cd15440f36f7ccc7a6c68edf9b652acabd90..a0f3d4017a991a8e6d2bee6041a9a4eb871c5d1b 100644 --- a/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py +++ b/Reconstruction/Jet/JetRecConfig/python/StandardJetMods.py @@ -1,25 +1,28 @@ -# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +""" +This module defines the standard JetModifier tools used in jet reco +Definitions are grouped in a dictionary of tool configurations using the helpers defined +in package configs. +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: + 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. + [5.] Flag passJetDef specifying if the helper needs the jet definition + --> should this be by default? prefer to avoid ignored args +""" from .JetDefinition import JetModifier -######################################################################## -# Define the dictionary of tool configurations using the helpers defined -# in package configs. -# This dict maps a modifier alias to the JetModifier config object -# that in turn will be responsible for generating a configured tool. -# -# [Optional] 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. -# [5.] Flag passJetDef specifying if the helper needs the jet definition -# --> should this be by default? prefer to avoid ignored args jetmoddict = {} ######################################################################## # Define the simple modifier setups here -- those defined in JetRec. -#from JetRec import JetRecConf + from AthenaConfiguration.ComponentFactory import CompFactory def getJetFilterTool(modspec): threshold = int(modspec) @@ -28,7 +31,7 @@ def getJetFilterTool(modspec): return jetptfilter jetrecmods = { "Sort": JetModifier("JetSorter","jetsort"), - "Filter": JetModifier("JetFilterTool","jetptfilter",helperfn=getJetFilterTool), + "Filter": JetModifier("JetFilterTool","jetptfilter",createfn=getJetFilterTool), } jetmoddict.update (jetrecmods) @@ -42,7 +45,7 @@ jetmoddict.update (jetrecmods) from JetCalibTools import JetCalibToolsConfig jetcalibmods = { "Calib": JetModifier("JetCalibrationTool","jetcalib_jetcoll_calibseq", - helperfn=JetCalibToolsConfig.getJetCalibToolFromString, + createfn=JetCalibToolsConfig.getJetCalibToolFromString, prereqs=JetCalibToolsConfig.getJetCalibToolPrereqs,passJetDef=True) } jetmoddict.update(jetcalibmods) @@ -55,6 +58,7 @@ jetmoddict.update(jetcalibmods) # Standard jet moments from JetMomentTools import JetMomentToolsConfig jetmomentmods = { + # Easy cases, no special config or prereqs, just default tool config "ClusterMoments": JetModifier("JetClusterMomentsTool", "clsmoms"), "ECPSFrac": JetModifier("JetECPSFractionTool", "ecpsfrac"), @@ -62,32 +66,34 @@ jetmomentmods = { # More complex cases here "CaloEnergies": JetModifier("JetCaloEnergies", "jetens", - prereqs=["mod:EMScaleMom"]), + prereqs=["mod:EMScaleMom"] + ), "CaloQuality": JetModifier("JetCaloQualityTool", "caloqual", - helperfn=JetMomentToolsConfig.getCaloQualityTool), + createfn=JetMomentToolsConfig.getCaloQualityTool), "ConstitFourMom": JetModifier("JetConstitFourMomTool", "constitfourmom_basename", - helperfn=JetMomentToolsConfig.getConstitFourMomTool, + createfn=JetMomentToolsConfig.getConstitFourMomTool, passJetDef=True), "EMScaleMom": JetModifier("JetEMScaleMomTool", "emscalemom_basename", - helperfn=JetMomentToolsConfig.getEMScaleMomTool, + createfn=JetMomentToolsConfig.getEMScaleMomTool, passJetDef=True), "JVF": JetModifier("JetVertexFractionTool", "jvf", - helperfn=JetMomentToolsConfig.getJVFTool, + createfn=JetMomentToolsConfig.getJVFTool, prereqs = ["mod:TrackMoments"] ), "JVT": JetModifier("JetVertexTaggerTool", "jvt", - helperfn=JetMomentToolsConfig.getJVTTool, + createfn=JetMomentToolsConfig.getJVTTool, prereqs = [ "mod:JVF" ]), "LArHVCorr": JetModifier("JetLArHVTool", "larhvcorr", prereqs = ["mod:EMScaleMom"]), "OriginSetPV": JetModifier("JetOriginCorrectionTool", "origin_setpv", prereqs = [ "mod:JVF" ]), "TrackMoments": JetModifier("JetTrackMomentsTool", "trkmoms", - helperfn=JetMomentToolsConfig.getTrackMomentsTool, - prereqs = [ "input:JetSelectedTracks","input:JetTrackVtxAssoc","ghost:Track" ]), + createfn=JetMomentToolsConfig.getTrackMomentsTool, + prereqs = [ "input:JetTrackVtxAssoc","ghost:Track" ]), + "TrackSumMoments": JetModifier("JetTrackSumMomentsTool", "trksummoms", - helperfn=JetMomentToolsConfig.getTrackSumMomentsTool, - prereqs = [ "input:JetSelectedTracks","input:JetTrackVtxAssoc","ghost:Track" ]), - } + createfn=JetMomentToolsConfig.getTrackSumMomentsTool, + prereqs = [ "input:JetTrackVtxAssoc","ghost:Track" ]), +} jetmoddict.update(jetmomentmods) # Truth labelling moments @@ -95,16 +101,16 @@ from ParticleJetTools import ParticleJetToolsConfig particlejetmods = { # Easy cases, no special config or prereqs, just default tool config "PartonTruthLabel": JetModifier("Analysis::JetPartonTruthLabel","partontruthlabel", - prereqs=["ghost:TruthLabelPartons"]), + prereqs=["ghost:Partons"]), # More complex cases here "TruthPartonDR": JetModifier("Analysis::JetConeLabeling","truthpartondr", - helperfn=ParticleJetToolsConfig.getJetConeLabeling), + createfn=ParticleJetToolsConfig.getJetConeLabeling), "JetDeltaRLabel": JetModifier("ParticleJetDeltaRLabelTool","jetdrlabeler_jetptmin", - helperfn=ParticleJetToolsConfig.getJetDeltaRLabelTool, - prereqs=["input:TruthLabelBHadronsFinal", - "input:TruthLabelCHadronsFinal", - "input:TruthLabelTausFinal"]) + createfn=ParticleJetToolsConfig.getJetDeltaRLabelTool, + prereqs=["ghost:BHadronsFinal", + "ghost:CHadronsFinal", + "ghost:TausFinal"]) } jetmoddict.update(particlejetmods) @@ -112,3 +118,15 @@ jetmoddict.update(particlejetmods) # This can also be expanded by users if they would rather do this than # pass in JetModifier instances in the JetDefinition + + +## TEMPORARY HACK (change the names of ghost tracks ) +from JetRecTools.JetRecToolsConfig import trackcollectionmap +trackcollectionmap[""] = { + "Tracks": "InDetTrackParticles", + "JetTracks": "JetSelectedTracks", + "Vertices": "PrimaryVertices", + "TVA": "JetTrackVtxAssoc", + "GhostTracks": "PseudoJetGhostTrack", + "GhostTracksLabel": "GhostTrack", + } diff --git a/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py b/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py new file mode 100644 index 0000000000000000000000000000000000000000..54f83e59704ad74e8a4d5b0245a580f87cf5a451 --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/python/StandardSmallRJets.py @@ -0,0 +1,79 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +from JetRecConfig.StandardJetConstits import jetconstitdic as cst +from .JetDefinition import JetDefinition +from xAODBase.xAODType import xAODType + + + +# ********************************************************* +# Ghost-associated particles for the standard small R jets +# ********************************************************* +standardghosts = ["Track","MuonSegment","Truth"] + + +flavourghosts = ["TruthLabel"+ghosttype for ghosttype in [ + "BHadronsInitial", "BHadronsFinal", "BQuarksFinal", + "CHadronsInitial", "CHadronsFinal", "CQuarksFinal", + "TausFinal", + "WBosons", "ZBosons", "HBosons", "TQuarksFinal", + "Partons",] +] + + + + +# ********************************************************* +# Modifiers for the standard small R jets +# ********************************************************* +# (use tuples rather than lists to prevent accidental modification) +standardrecomods = ( "Filter:10000","Width","TrackMoments","TrackSumMoments","JVF","JVT","OriginSetPV", + "CaloEnergies", ) +clustermods = ("ECPSFrac","ClusterMoments",)# "LArHVCorr" ) +truthmods = ("PartonTruthLabel","TruthPartonDR","JetDeltaRLabel:5000" ) # not working well yet ? +pflowmods = () + + + +# ********************************************************* +# Standard small R jets definitions +# ********************************************************* + + +AntiKt4EMPFlow = JetDefinition("AntiKt",0.4,cst.EMPFlow, + ghostdefs = standardghosts , # not working well yet : flavourghosts, + modifiers = ("Calib:T0:mc",)+standardrecomods+truthmods, + standardRecoMode = True, + lock = True +) + + + + + +AntiKt4LCTopo = JetDefinition("AntiKt",0.4,cst.LCTopoOrigin, + ghostdefs = standardghosts, # not working well yet : flavourghosts,, + modifiers = standardrecomods+truthmods+clustermods, + standardRecoMode = True, + lock = True, +) + + +AntiKt4EMTopo = JetDefinition("AntiKt",0.4,cst.EMTopoOrigin, + ghostdefs = standardghosts, # not working well yet : flavourghosts,, + modifiers = standardrecomods+truthmods+clustermods, + standardRecoMode = True, + lock = True, +) + +AntiKt4Truth = JetDefinition("AntiKt",0.4, cst.Truth, + modifiers = [], + standardRecoMode = True, + lock = True, +) + +AntiKt4TruthWZ = JetDefinition("AntiKt",0.4, cst.TruthWZ, + modifiers = [], + standardRecoMode = True, + lock = True, +) diff --git a/Reconstruction/Jet/JetRecConfig/python/Utilities.py b/Reconstruction/Jet/JetRecConfig/python/Utilities.py new file mode 100644 index 0000000000000000000000000000000000000000..9769877e09a9b3830fbd146364fb48c6ac392182 --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/python/Utilities.py @@ -0,0 +1,137 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + + +""" +This module defines utilities for the jet config. +These are mainly to allow to "lock" the standard, reference definitions so +they are not accidentally changed by some user scripts. +""" + +class lproperty(property): + """Build on the standard property to allow a property to be locked if the holding class has its _locked attribute set to True """ + def lsetter(self, func): + def lockedfunc(self, v): + if self._locked: + raise Exception("Error "+func.__name__+" is locked. Either clone or unlock",self) + func(self,v) + + return self.setter(lockedfunc) + +def make_lproperty( func ): + """creates a property from a class method (or a str) which can be locked if the holding class has its _locked attribute set to True + + usage : + class A: + # simple locked property + x = make_lproperty('x') + + # same but using decorator + @make_lproperty + def y(self): pass + + # same but now with customized setter : + @make_lproperty + def z(self): pass + @z.lsetter + def z(self, v): + print("setting z to ",v) + self._z = v + + """ + if isinstance(func, str): + pname = func + else: + pname = func.__name__ + pname_i = '_'+pname + + def _getter(self): + return getattr(self,pname_i, None) + def _setter(self, v): + if self._locked: + raise AttributeError("Error property '"+pname+"' is locked. Either clone or unlock",self) + setattr(self, pname_i, v) + return lproperty(_getter, _setter) + +def make_alias( prop ): + """Returns a property which act as a read-only alias to existing prop """ + def _getter(self): + return getattr(self,prop,None) + def _setter(self,v): + raise AttributeError("Cannot set alias "+pname+" from "+str(self) ) + return property(_getter, _setter) + +def listClassLProperties(cls): + lprops = [k for (k,v) in cls.__dict__.items() if isinstance(v,lproperty) ] + lprops +=[ '_'+k for k in lprops] + for base in cls.__bases__: + lprops += listClassLProperties(base) + return lprops + +def onlyAttributesAreProperties(cls): + """Transforms the input class cls so the only attributes which can be set are the lproperty of the class. + Best used as a decorator. Ex : + @onlyAttributesAreProperties + class A: + myprop0 = make_lproperty('myprop0') + + a = A() + a.myprop0 = 0 # ok + a.mypropO = 3 # impossible + """ + # build the list of attributes allowed to be set : these are the properties and _locked + #cls._allowedattributes = [k for (k,v) in cls.__dict__.items() if isinstance(v,lproperty) ] + #cls._allowedattributes +=[ '_'+k for k in cls._allowedattributes] + cls._allowedattributes = listClassLProperties( cls ) + cls._allowedattributes += ['_locked'] + + # flag to activate the prevention of adding new attributes. we set it at the end of __init__ + cls._nomoreattributes=False + cls._locked = False + + cls.__init__origin = cls.__init__ + + # define a new __init__ for this class. + # the 'finalinit' argument allows to avoid locking the allowed attribute : this is to be used when a derived class wants to call the init of it's base class. + def initwraper(self, *l,finalinit=True, **args): + cls.__init__origin(self, *l,**args) + self._nomoreattributes = finalinit + cls.__init__ = initwraper + + # define a __setattr__ for this class + def setattr(self, k, v): + if self._nomoreattributes and k not in self._allowedattributes: + raise AttributeError("Setting attribute "+k+" on "+str(self)+" not allowed") + super(cls,self).__setattr__(k,v) + cls.__setattr__ = setattr + + return 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 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 + return o + cls.clone = clone + return 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) + """ + def __setitem__(self, k, v): + if k in self: + raise KeyError("Can't override key "+k) + super().__setitem__(k,v) + super().__setattr__(k,v) + + def update(self, **args): # we need to redefine it + for k,v in args.items(): + self[k]=v diff --git a/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py b/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py index 11f94e75234cb42d359ec614e7ff2b6599b574b8..7e9d703797e9a4023b0291f6bee11d78ad198842 100755 --- a/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py +++ b/Reconstruction/Jet/JetRecConfig/share/JetRecTestCfg.py @@ -20,7 +20,7 @@ def JetRecTestCfg(jetdefs,configFlags,args): jetlog.info("Printing component accumulators for each jet collection") jetcas = [] for jetdef in jetdefs: - jetcomps = JetRecConfig.JetRecCfg(jetdef,configFlags,jetnameprefix="New") + jetcomps = JetRecConfig.JetRecCfg(jetdef,configFlags) if args.printAccumulators: jetcomps.printConfig(withDetails=args.verboseAccumulators,summariseProps=True) jetcas.append(jetcomps) @@ -35,75 +35,62 @@ def JetRecTestCfg(jetdefs,configFlags,args): def DefineJetCollections(configFlags): - # Here we define the jet configurations we want to build - # These mod and ghost lists should go in a common module - standardrecomods = ["Width","TrackMoments","TrackSumMoments","JVF","JVT","OriginSetPV", - "CaloEnergies","LArHVCorr"] - clustermods = ["ECPSFrac","ClusterMoments"] - truthmods = ["PartonTruthLabel","TruthPartonDR","JetDeltaRLabel:5000"] if configFlags.Input.isMC else [] - - from JetRecConfig.JetDefinition import JetGhost - ghostlist = ["Track","MuonSegment","Truth"] - standardghosts = [JetGhost(ghosttype) for ghosttype in ghostlist] - flavourghostlist = ["BHadronsInitial", "BHadronsFinal", "BQuarksFinal", - "CHadronsInitial", "CHadronsFinal", "CQuarksFinal", - "TausFinal", - "WBosons", "ZBosons", "HBosons", "TQuarksFinal", - "Partons", - ] - flavourghosts = [JetGhost("TruthLabel"+ghosttype) for ghosttype in flavourghostlist] - standardghosts += flavourghosts - ######################################################################## # First a demonstration of just building jets using standard definitions - from JetRecConfig.StandardJetDefs import AntiKt4EMTopo, AntiKt4EMPFlow, AntiKt4Truth, AntiKt4TruthWZ + from JetRecConfig.StandardSmallRJets import AntiKt4EMTopo, AntiKt4EMPFlow, AntiKt4Truth, AntiKt4TruthWZ - # This updates the original jet definitions, so might be a little risky - # in derivation code. Safer would be to always deepcopy into a local variable. - AntiKt4EMTopo.ptminfilter = 15e3 - AntiKt4EMTopo.modifiers = ["Calib:T0:mc","Sort"] + standardrecomods + clustermods + truthmods - AntiKt4EMTopo.ghostdefs = standardghosts - #AntiKt4EMTopo.modifiers = ["Calib:AnalysisLatest:mc"] - AntiKt4EMPFlow.ptminfilter = 10e3 - AntiKt4EMPFlow.modifiers = ["Calib:T0:mc","Sort"] + standardrecomods + truthmods - AntiKt4EMPFlow.ghostdefs = standardghosts - #AntiKt4EMPFlow.modifiers = ["Calib:AnalysisLatest:mc"] + # ************************ + # TEMPORARY : comment out jet def modifications + + # # This updates the original jet definitions, so might be a little risky + # # in derivation code. Safer would be to always deepcopy into a local variable. + # #AntiKt4EMTopo.ptminfilter = 15e3 + # AntiKt4EMTopo.modifiers = ["Calib:T0:mc","Sort"] + standardrecomods + clustermods + truthmods - AntiKt4Truth.ptminfilter = 2e3 - AntiKt4Truth.extrainputs = ["EventDensity"] + # AntiKt4EMPFlow.ptminfilter = 10e3 + # AntiKt4EMPFlow.modifiers = ["Calib:T0:mc","Sort"] + standardrecomods + truthmods + # AntiKt4EMPFlow.ghostdefs = standardghosts + # #AntiKt4EMPFlow.modifiers = ["Calib:AnalysisLatest:mc"] - AntiKt4TruthWZ.ptminfilter = 2e3 - AntiKt4TruthWZ.extrainputs = ["EventDensity"] + # AntiKt4Truth.ptminfilter = 2e3 + # AntiKt4Truth.extrainputs = ["EventDensity"] - ######################################################################## - # Now we define our own definitions - from JetRecConfig.JetDefinition import JetConstit, JetDefinition, xAODType - EMTopoCSSK = JetConstit(xAODType.CaloCluster, ["EM","Origin","CS","SK"]) - AntiKt4EMTopoCSSK = JetDefinition("AntiKt",0.4,EMTopoCSSK,ptmin=2e3,ptminfilter=2e3) - AntiKt4EMTopoCSSK.modifiers = ["ConstitFourMom"] + standardrecomods + clustermods + truthmods - AntiKt4EMTopoCSSK.ghostdefs = standardghosts - AntiKt4EMTopoCSSK.extrainputs = ["EventDensity"] + # AntiKt4TruthWZ.ptminfilter = 2e3 + # AntiKt4TruthWZ.extrainputs = ["EventDensity"] ######################################################################## - # We can also copy and modify the standard ones - from copy import deepcopy - from JetRecConfig.StandardJetDefs import CHSPFlow - - CSSKPFlow = deepcopy(CHSPFlow) - CSSKPFlow.modifiers = ["CS","SK"] - AntiKt4EMPFlowCSSK = deepcopy(AntiKt4EMPFlow) - AntiKt4EMPFlowCSSK.inputdef = CSSKPFlow - AntiKt4EMPFlowCSSK.modifiers = ["ConstitFourMom"] + standardrecomods + truthmods - AntiKt4EMPFlowCSSK.ptmin = 2e3 - AntiKt4EMPFlowCSSK.ptminfilter = 2e3 - AntiKt4EMPFlowCSSK.ghostdefs = standardghosts - AntiKt4EMPFlowCSSK.extrainputs = ["EventDensity"] - - jetdefs = [AntiKt4EMTopo, - AntiKt4EMPFlow, - AntiKt4EMTopoCSSK, - AntiKt4EMPFlowCSSK] + # ************************ + # TEMPORARY : comment out custom CSSK definitions + # # Now we define our own definitions + # from JetRecConfig.JetDefinition import JetConstit, JetDefinition, xAODType + # EMTopoCSSK = JetConstit(xAODType.CaloCluster, ["EM","Origin","CS","SK"]) + # AntiKt4EMTopoCSSK = JetDefinition("AntiKt",0.4,EMTopoCSSK,ptmin=2e3,ptminfilter=2e3) + # AntiKt4EMTopoCSSK.modifiers = ["ConstitFourMom"] + standardrecomods + clustermods + truthmods + # AntiKt4EMTopoCSSK.ghostdefs = standardghosts + # AntiKt4EMTopoCSSK.extrainputs = ["EventDensity"] + + # ######################################################################## + # # We can also copy and modify the standard ones + # from copy import deepcopy + # from JetRecConfig.StandardJetDefs import CHSPFlow + + # CSSKPFlow = deepcopy(CHSPFlow) + # CSSKPFlow.modifiers = ["CS","SK"] + # AntiKt4EMPFlowCSSK = deepcopy(AntiKt4EMPFlow) + # AntiKt4EMPFlowCSSK.inputdef = CSSKPFlow + # AntiKt4EMPFlowCSSK.modifiers = ["ConstitFourMom"] + standardrecomods + truthmods + # AntiKt4EMPFlowCSSK.ptmin = 2e3 + # AntiKt4EMPFlowCSSK.ptminfilter = 2e3 + # AntiKt4EMPFlowCSSK.ghostdefs = standardghosts + # AntiKt4EMPFlowCSSK.extrainputs = ["EventDensity"] + + jetdefs = [ + AntiKt4EMTopo.clone(prefix="New"), + AntiKt4EMPFlow.clone(prefix="New"), + # AntiKt4EMTopoCSSK, + # AntiKt4EMPFlowCSSK, + ] if configFlags.Input.isMC: jetdefs += [AntiKt4Truth, AntiKt4TruthWZ] diff --git a/Reconstruction/Jet/JetRecConfig/share/test_StandardSmallRJets.py b/Reconstruction/Jet/JetRecConfig/share/test_StandardSmallRJets.py new file mode 100644 index 0000000000000000000000000000000000000000..e815f191100d7992b215a866f8f97f4da2e082a8 --- /dev/null +++ b/Reconstruction/Jet/JetRecConfig/share/test_StandardSmallRJets.py @@ -0,0 +1,108 @@ +# Copyright (C) 2002-2020 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."] + for jetdef in jetdefs: + key = jetdef.fullname() + outputlist += ["xAOD::JetContainer#"+key, + "xAOD::JetAuxContainer#"+key+"Aux."] + + # 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/RecExample/RecExRecoTest/test/test_recexreco_art_jets_fromesd_newJobConfig.sh b/Reconstruction/RecExample/RecExRecoTest/test/test_recexreco_art_jets_fromesd_newJobConfig.sh new file mode 100755 index 0000000000000000000000000000000000000000..5bf6a5d4ed3e5ccf0d04e34dbd0ba364a726081e --- /dev/null +++ b/Reconstruction/RecExample/RecExRecoTest/test/test_recexreco_art_jets_fromesd_newJobConfig.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# art-description: Athena runs jet reconstruction, using the new job configuration for Run 3, from an ESD file +# art-type: local +# art-athena-mt +# art-include: master/Athena + +python $WorkDir_DIR/jobOptions/JetRecConfig/test_StandardSmallRJets.py --filesIn /cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/RecExRecoTest/mc16_13TeV.361022.Pythia8EvtGen_A14NNPDF23LO_jetjet_JZ2W.recon.ESD.e3668_s3170_r10572_homeMade.pool.root -n 50 | tee temp.log +echo "art-result: ${PIPESTATUS[0]}" + +test_postProcessing_Errors.sh temp.log diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py index 7e871e53d9525af1cfeecb9210dad69f88fbf8e0..c12e27287296daa56c9fbbb33f0273a65166f1bc 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py @@ -7,7 +7,9 @@ # and translate it into the python configuration objects used by # jet reco code. -from JetRecConfig.JetDefinition import JetConstit, xAODType, JetDefinition +from JetRecConfig.JetDefinition import JetConstitSeq,JetConstitSource, xAODType, JetDefinition +from . import TriggerJetMods # this is to define the ConstitFourMom_copy JetModifierC +TriggerJetMods.ConstitFourMom_copy from AthenaCommon.Logging import logging log = logging.getLogger("TriggerMenuMT.HLTMenuConfig.Jet.JetRecoConfiguration") @@ -39,36 +41,57 @@ def defineJetConstit(jetRecoDict,clustersKey=None,pfoPrefix=None): constitMods = [] # Get the details of the constituent definition: # type, mods and the input container name + + if "pf" in jetRecoDict["dataType"]: 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] # apply constituent pileup suppression if "cs" in jetRecoDict["dataType"]: constitMods.append("CS") if "sk" in jetRecoDict["dataType"]: constitMods.append("SK") + # Generate a new JetConstitModifier with track proterties setup according to trkopt + constitModWithAlternateTrk("CHS", trkopt) # + constitMods += ["CHS"+trkopt] + + inputPFO = pfoPrefix+"ParticleFlowObjects" + modstring = ''.join(constitMods[1:-1]) + if modstring == '': + modstring='CHS' + if not constitMods: - jetConstit = JetConstit( xAODType.ParticleFlow, constitMods, rawname=pfoPrefix+"ParticleFlowObjects", inputname=pfoPrefix+"CHSParticleFlowObjects") + jetConstit = JetConstitSeq( "HLT_EMPFlow", xAODType.ParticleFlow, constitMods, inputname=inputPFO, outputname=pfoPrefix+"CHSParticleFlowObjects", label="EMPFlow") else: - jetConstit = JetConstit( xAODType.ParticleFlow, constitMods, rawname=pfoPrefix+"ParticleFlowObjects", prefix=pfoPrefix) - + jetConstit = JetConstitSeq( "HLT_EMPFlow"+modstring, xAODType.ParticleFlow, constitMods, inputname=inputPFO, outputname=pfoPrefix+modstring+"ParticleFlowObjects",label='EMPFlow'+(modstring if modstring!='CHS' else '') ) + + if "tc" in jetRecoDict["dataType"]: - # apply this scale - if jetRecoDict["calib"] == "em": - constitMods = ["EM"] + constitMods - elif jetRecoDict["calib"] == "lcw": - constitMods = ["LC"] + constitMods - # read from this cluster collection, - # overriding the standard offline collection - jetConstit = JetConstit( xAODType.CaloCluster, constitMods, rawname=clustersKey, prefix="HLT_") # apply constituent pileup suppression if "cs" in jetRecoDict["dataType"]: constitMods.append("CS") if "sk" in jetRecoDict["dataType"]: constitMods.append("SK") - jetConstit.modifiers = constitMods - if clustersKey is not None and jetRecoDict["dataType"]=="tc": - jetConstit.inputname = clustersKey + # build a modifier identifier : + modstring = ''.join(constitMods) + # prepend the cluster calib state : + if jetRecoDict["calib"] == "em": + constitMods = ["EM"] + constitMods + elif jetRecoDict["calib"] == "lcw": + constitMods = ["LC"] + constitMods + + jetConstit = JetConstitSeq( "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) + return jetConstit @@ -79,7 +102,7 @@ def interpretRecoAlg(recoAlg): # Arbitrary min pt for fastjet, set to be low enough for MHT(?) # Could/should adjust higher for large-R -def defineJets(jetRecoDict,clustersKey=None,pfoPrefix=None): +def defineJets(jetRecoDict,clustersKey=None,prefix='',pfoPrefix=None): minpt = { 4: 7000, 10: 50000, @@ -87,20 +110,26 @@ def defineJets(jetRecoDict,clustersKey=None,pfoPrefix=None): jetalg, jetradius, jetextra = interpretRecoAlg(jetRecoDict["recoAlg"]) actualradius = float(jetradius)/10 jetConstit = defineJetConstit(jetRecoDict,clustersKey,pfoPrefix) - jetDef = JetDefinition( "AntiKt", actualradius, jetConstit, ptmin=minpt[jetradius]) + + suffix="_"+jetRecoDict["jetCalib"] + if jetRecoDict["trkopt"] != "notrk": + suffix += "_{}".format(jetRecoDict["trkopt"]) + + + jetDef = JetDefinition( "AntiKt", actualradius, jetConstit, ptmin=minpt[jetradius], prefix=prefix, suffix=suffix) return jetDef -def defineReclusteredJets(jetRecoDict): - rcJetConstit = JetConstit( xAODType.Jet, []) +def defineReclusteredJets(jetRecoDict,smallRjets): + rcJetConstit = JetConstitSource("RCJet", xAODType.Jet, smallRjets, label='JetRC') rcJetDef = JetDefinition( "AntiKt", 1.0, rcJetConstit) return rcJetDef -def defineGroomedJets(jetRecoDict,ungroomedDef,ungroomedJetsName): +def defineGroomedJets(jetRecoDict,ungroomedDef):#,ungroomedJetsName): from JetRecConfig.JetGrooming import JetTrimming, JetSoftDrop groomAlg = jetRecoDict["recoAlg"][3:] if 'sd' in jetRecoDict["recoAlg"] else jetRecoDict["recoAlg"][-1] groomDef = { - "sd":JetSoftDrop(ungroomedDef,ungroomedJetsName,zcut=0.1,beta=1.0), - "t" :JetTrimming(ungroomedDef,ungroomedJetsName,smallR=0.2,ptfrac=0.05), + "sd":JetSoftDrop(ungroomedDef,zcut=0.1,beta=1.0), + "t" :JetTrimming(ungroomedDef,smallR=0.2,ptfrac=0.05), }[groomAlg] return groomDef @@ -115,9 +144,9 @@ def getModSpec(modname,modspec=''): def defineTrackMods(trkopt): trkmods = [ - (jetmoddict["TrackMoments"],trkopt), - (jetmoddict["JVF"],trkopt), - (jetmoddict["JVT"],trkopt) + "TrackMoments:"+trkopt, + "JVF:"+trkopt, + "JVT:"+trkopt, ] return trkmods @@ -167,16 +196,16 @@ def defineCalibFilterMods(jetRecoDict,dataSource,rhoKey="auto"): calibSeq += "_Insitu" calibSpec = ":".join( [calibContext, dataSource, calibSeq, rhoKey, pvname, gscDepth] ) - from .TriggerJetMods import ConstitFourMom_copy + if jetalg=="a4": - calibMods = [(ConstitFourMom_copy,""), - getModSpec("CaloEnergies"), # Needed for GSC - getModSpec("Calib",calibSpec), - getModSpec("Sort")] + calibMods = ["ConstitFourMom_copy", + "CaloEnergies", # Needed for GSC + "Calib:"+calibSpec, + "Sort"] else: - calibMods = [(ConstitFourMom_copy,""), - getModSpec("Calib",calibSpec), - getModSpec("Sort")] + calibMods = ["ConstitFourMom_copy", + "Calib:"+calibSpec, + "Sort"] filtercut = {"a4":7000, "a10":50000, "a10r": 50000, "a10t":50000, "a10sd":50000}[jetalg] - return calibMods + [getModSpec("Filter",filtercut)] + return calibMods + ["Filter:"+str(filtercut)] diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py index 54d1b7d8202a5e9084bc37e0621723a40eafc9f5..9189cd93342c2a3d9e5a3ae02cd04302bba7a2d6 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py @@ -7,6 +7,9 @@ from TriggerMenuMT.HLTMenuConfig.Menu.ChainConfigurationBase import RecoFragment from AthenaConfiguration.ComponentAccumulator import conf2toConfigurable from TriggerMenuMT.HLTMenuConfig.Jet import JetRecoConfiguration from JetRecConfig.JetRecConfig import getConstitPJGAlg, getJetAlgorithm +from JetRecConfig import JetInputConfig +from JetRecConfig import JetRecConfig + from TrigEDMConfig.TriggerEDMRun3 import recordable # Translate the reco dict to a string for suffixing etc @@ -81,28 +84,25 @@ def standardJetRecoSequence( configFlags, dataSource, RoIs, **jetRecoDict ): from eflowRec.PFHLTSequence import PFHLTSequence (pfseq, pfoPrefix) = RecoFragmentsPool.retrieve(PFHLTSequence, configFlags, clustersin=clustersKey, tracktype=jetRecoDict["trkopt"]) recoSeq += pfseq - jetDef = JetRecoConfiguration.defineJets(jetRecoDict,pfoPrefix=pfoPrefix) + jetDef = JetRecoConfiguration.defineJets(jetRecoDict,pfoPrefix=pfoPrefix, prefix=jetNamePrefix) else: - jetDef = JetRecoConfiguration.defineJets(jetRecoDict,clustersKey=clustersKey) - useConstitMods = ["sktc","cssktc", "pf", "csskpf"] - doConstitMods = jetRecoDict["dataType"] in useConstitMods + jetDef = JetRecoConfiguration.defineJets(jetRecoDict,clustersKey=clustersKey, prefix=jetNamePrefix) - # chosen jet collection - jetsFullName = jetNamePrefix+jetDef.basename+"Jets_"+jetRecoDict["jetCalib"] - if jetRecoDict["trkopt"] != "notrk": - jetsFullName += "_{}".format(jetRecoDict["trkopt"]) + doConstitMods = jetRecoDict["dataType"] in ["sktc","cssktc", "pf", "csskpf"] + + jetsFullName = jetDef.fullname() sequenceOut = recordable(jetsFullName) + JetRecConfig.instantiateAliases(jetDef) if doConstitMods: # Get online monitoring jet rec tool from JetRecTools import OnlineMon monJetRecTool = OnlineMon.getMonTool_Algorithm("HLTJets/"+jetsFullName+"/") from JetRecConfig.ConstModHelpers import getConstitModAlg - if jetRecoDict["trkopt"] == "notrk": - recoSeq += getConstitModAlg(jetDef.inputdef,suffix="HLT",tvaKey="JetTrackVtxAssoc",vtxKey="PrimaryVertices",monTool=monJetRecTool) - else: - recoSeq += getConstitModAlg(jetDef.inputdef,suffix="HLT",tvaKey=trkcolls["TVA"],vtxKey=trkcolls["Vertices"],monTool=monJetRecTool) + constitModAlg = getConstitModAlg(jetDef.inputdef, monTool=monJetRecTool) + if constitModAlg: + recoSeq += constitModAlg # Add the PseudoJetGetter alg to the sequence constitPJAlg = getConstitPJGAlg( jetDef.inputdef ) @@ -114,7 +114,6 @@ def standardJetRecoSequence( configFlags, dataSource, RoIs, **jetRecoDict ): if trkcolls: pjs.append(trkcolls["GhostTracks"]) - from JetRecConfig import JetRecConfig jetModList = [] if jetRecoDict["trkopt"] != "notrk": trkMods = JetRecoConfiguration.defineTrackMods(jetRecoDict["trkopt"]) @@ -123,7 +122,7 @@ def standardJetRecoSequence( configFlags, dataSource, RoIs, **jetRecoDict ): rhoKey = "auto" if "sub" in jetRecoDict["jetCalib"]: # Add the event shape alg if needed for area subtraction - eventShapeAlg = JetRecConfig.getEventShapeAlg( jetDef.inputdef, constitPJKey, "HLT_" ) + eventShapeAlg = JetInputConfig.buildEventShapeAlg( jetDef, "HLT_" ) recoSeq += conf2toConfigurable(eventShapeAlg) # Not currently written because impossible to merge # across event views, which is maybe a concern in @@ -131,19 +130,26 @@ def standardJetRecoSequence( configFlags, dataSource, RoIs, **jetRecoDict ): rhoKey = eventShapeAlg.EventDensityTool.OutputContainer # Import the standard jet modifiers as defined for offline - # We can add/configure these differently if desired. In particular, - # we could define a TriggerJetMods module if settings need to - # diverge substantially e.g. track/vertex collections + # We can add/configure these differently if desired. calibMods = JetRecoConfiguration.defineCalibFilterMods(jetRecoDict,dataSource, rhoKey) jetModList += calibMods + # 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.getJetAlgorithm(jetsFullName, jetDef, pjs, jetModList, monTool) + jetRecAlg = JetRecConfig.getJetAlgorithm(jetsFullName, jetDef, pjs, monTool) recoSeq += conf2toConfigurable( jetRecAlg ) # End of basic jet reco return recoSeq, sequenceOut, jetDef @@ -162,25 +168,26 @@ def groomedJetRecoSequence( configFlags, dataSource, RoIs, **jetRecoDict ): # Need to forward the pseudojets of the parents to the groomer parentpjs = getattr(ungroomedJetRecoSequence,"jetalg_{}".format(ungroomedJetsName)).Tools[0].InputPseudoJets - groomDef = JetRecoConfiguration.defineGroomedJets(jetRecoDict,ungroomedDef,ungroomedJetsName) - groomedJetsFullName = jetNamePrefix+groomDef.basename+"Jets_"+jetRecoDict["jetCalib"] + + groomDef = JetRecoConfiguration.defineGroomedJets(jetRecoDict,ungroomedDef)#,ungroomedJetsName) + groomedJetsFullName = groomDef.fullname()+"_"+jetRecoDict["jetCalib"] if jetRecoDict["trkopt"]!="notrk": groomedJetsFullName += "_"+jetRecoDict["trkopt"] - groomedModList = JetRecoConfiguration.defineCalibFilterMods(jetRecoDict,dataSource) + groomDef.modifiers = JetRecoConfiguration.defineCalibFilterMods(jetRecoDict,dataSource) # Can add substructure mods here # Get online monitoring tool from JetRec import JetOnlineMon monTool = JetOnlineMon.getMonTool_TrigJetAlgorithm("HLTJets/"+groomedJetsFullName+"/") - from JetRecConfig.JetGroomConfig import getJetGroomAlg - groomalg = getJetGroomAlg(groomedJetsFullName,groomDef,parentpjs,groomedModList,monTool) + from JetRecConfig.JetGroomConfig import getJetGroomAlg, instantiateGroomingAliases + instantiateGroomingAliases(groomDef) + groomalg = getJetGroomAlg(groomedJetsFullName,groomDef,parentpjs,monTool) recoSeq += conf2toConfigurable( groomalg ) - sequenceOut = recordable(groomedJetsFullName) - jetDef = groomDef - return recoSeq, sequenceOut, jetDef + sequenceOut = recordable(groomedJetsFullName) + return recoSeq, sequenceOut, groomDef # Reclustering -- call the basic jet reco and add this to the sequence, @@ -202,9 +209,8 @@ def reclusteredJetRecoSequence( configFlags, dataSource, RoIs, **jetRecoDict ): OutputContainer=filteredJetsName, PtMin=rcJetPtMin) - rcJetDef = JetRecoConfiguration.defineReclusteredJets(jetRecoDict) - rcJetDef.inputdef.inputname = filteredJetsName - rcJetsFullName = jetNamePrefix+rcJetDef.basename+"RCJets_"+jetRecoDict["jetCalib"] + rcJetDef = JetRecoConfiguration.defineReclusteredJets(jetRecoDict, filteredJetsName) + rcJetsFullName = jetNamePrefix+rcJetDef.basename+"Jets_"+jetRecoDict["jetCalib"] rcModList = [] # Could set substructure mods rcJetDef.modifiers = rcModList @@ -217,7 +223,7 @@ def reclusteredJetRecoSequence( configFlags, dataSource, RoIs, **jetRecoDict ): monTool = JetOnlineMon.getMonTool_TrigJetAlgorithm("HLTJets/"+rcJetsFullName+"/") rcPJs = [rcConstitPJKey] - rcJetRecAlg = getJetAlgorithm(rcJetsFullName, rcJetDef, rcPJs, rcModList, monTool) + rcJetRecAlg = getJetAlgorithm(rcJetsFullName, rcJetDef, rcPJs, monTool) recoSeq += conf2toConfigurable( rcJetRecAlg ) diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/TriggerJetMods.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/TriggerJetMods.py index 408f56fc5fb4618d86e963b2bf7a6da04febd095..89531ab3a8530735ef8d1f6d908551942e31a848 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/TriggerJetMods.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/TriggerJetMods.py @@ -17,4 +17,6 @@ def getConstitFourMomTool_copy(): return cfourmom ConstitFourMom_copy = JetModifier("JetConstitFourMomTool", "constitfourmom_copy", - helperfn=getConstitFourMomTool_copy) + createfn=getConstitFourMomTool_copy) +from JetRecConfig.StandardJetMods import jetmoddict +jetmoddict['ConstitFourMom_copy'] = ConstitFourMom_copy diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/generateJet.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/generateJet.py index 183e5aa9f0c845e35f2af47ab8a95e6857561da4..5683afb49b17b36d8451e2f8a26dc083556eecfd 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/generateJet.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/generateJet.py @@ -56,22 +56,24 @@ def generateChains( flags, chainDict ): #sequencing of actual jet reconstruction from JetRecConfig import JetRecConfig - from JetRecConfig.JetDefinition import JetConstit, JetDefinition, xAODType - + from JetRecConfig.JetDefinition import JetConstitSeq, JetDefinition, xAODType, JetInputDef + from JetRecConfig.StandardJetConstits import jetinputdefdic + + # declare a jet source for the HLT clusters if needed : + jetinputdefdic.setdefault(clustersname , JetInputDef(clustersname, xAODType.CaloCluster) ) #hardcoded jet collection for now clustermods = ["ECPSFrac","ClusterMoments"] trigMinPt = 7e3 - HLT_EMTopo = JetConstit( xAODType.CaloCluster, ["EM"]) - HLT_EMTopo.rawname = clustersname - HLT_EMTopo.inputname = clustersname - HLT_AntiKt4EMTopo_subjesIS = JetDefinition( "AntiKt", 0.4, HLT_EMTopo, ptmin=trigMinPt,ptminfilter=trigMinPt) + HLT_EMTopo = JetConstitSeq( "HLT_EMTopo",xAODType.CaloCluster, ["EM"], clustersname, clustersname,label="EMTopo") + + HLT_AntiKt4EMTopo_subjesIS = JetDefinition( "AntiKt", 0.4, HLT_EMTopo, ptmin=trigMinPt,ptminfilter=trigMinPt, + prefix="HLT_", + suffix = "_subjesIS", + ) HLT_AntiKt4EMTopo_subjesIS.modifiers = ["Calib:TrigRun2:data:JetArea_EtaJES_GSC_Insitu:HLT_Kt4EMTopoEventShape","Sort"] + clustermods - jetprefix="HLT_" - jetsuffix="_subjesIS" - evsprefix="HLT_" # May need a switch to disable automatic modifier prerequisite generation - jetRecoComps = JetRecConfig.JetRecCfg(HLT_AntiKt4EMTopo_subjesIS, flags, jetprefix, jetsuffix, evsprefix) + jetRecoComps = JetRecConfig.JetRecCfg(HLT_AntiKt4EMTopo_subjesIS, flags) inEventReco.mergeReco(jetRecoComps) acc.merge(inEventReco,stepReco.getName()) @@ -79,7 +81,7 @@ def generateChains( flags, chainDict ): #hypo from TrigHLTJetHypo.TrigJetHypoToolConfig import trigJetHypoToolFromDict hypo = CompFactory.TrigJetHypoAlgMT("TrigJetHypoAlgMT_a4tcem_subjesIS") - jetsfullname = jetprefix+HLT_AntiKt4EMTopo_subjesIS.basename+"Jets"+jetsuffix + jetsfullname = HLT_AntiKt4EMTopo_subjesIS.fullname() hypo.Jets = jetsfullname acc.addEventAlgo(hypo) diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/AlgConfigs.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/AlgConfigs.py index 0d8ee40c27af82dab0051e271acaf1be136c5bc1..91b456ed7964e603e47594ae71b630e7ae542d5e 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/AlgConfigs.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/AlgConfigs.py @@ -144,6 +144,7 @@ class TrkMHTConfig(AlgConfig): jetRecoDict = jetRecoDictForMET(trkopt="ftf", **recoDict) # TODO - right now jet calibration is hardcoded to EM jetRecoDict["calib"] = "em" + jetSeq, jetName, jetDef = RecoFragmentsPool.retrieve( jetRecoSequence, ConfigFlags, **jetRecoDict ) @@ -168,7 +169,7 @@ class TrkMHTConfig(AlgConfig): self.fexAlg.TrackSelTool.maxZ0SinTheta = 1.5 self.fexAlg.TrackSelTool.maxD0overSigmaD0 = 3 self.fexAlg.TrackSelTool.minPt = 1 * Units.GeV - + class PFSumConfig(AlgConfig): @classmethod @@ -273,7 +274,6 @@ class MHTPufitConfig(AlgConfig): def __init__(self, **recoDict): super(MHTPufitConfig, self).__init__(**recoDict) from ..Jet.JetRecoSequences import jetRecoSequence - from ..Jet.JetRecoConfiguration import defineJets from TriggerMenuMT.HLTMenuConfig.CommonSequences.CaloSequenceSetup import ( caloClusterRecoSequence, ) @@ -300,23 +300,22 @@ class MHTPufitConfig(AlgConfig): clustersin=clusterName, tracktype=jetRecoDict["trkopt"], ) - jetDef = defineJets(jetRecoDict, pfoPrefix=pfoPrefix) + #jetDef = defineJets(jetRecoDict, pfoPrefix=pfoPrefix) elif jetRecoDict["dataType"] == "tc": - jetDef = defineJets(jetRecoDict, clustersKey=clusterName) + pass + #jetDef = defineJets(jetRecoDict, clustersKey=clusterName) else: raise ValueError( "Unexpected jetDataType {}".format(jetRecoDict["dataType"]) ) - inputName = jetDef.inputdef.inputname + inputName = jetDef.inputdef.containername calibHasAreaSub = "sub" in jetRecoDict["jetCalib"] if calibHasAreaSub: - from JetRecConfig.JetRecConfig import getEventShapeAlg, getConstitPJGAlg - - evtShapeAlg = getEventShapeAlg( - jetDef.inputdef, - getConstitPJGAlg(jetDef.inputdef).OutputContainer, - "HLT_", - ) + from JetRecConfig.JetRecConfig import instantiateAliases + from JetRecConfig.JetInputConfig import buildEventShapeAlg + instantiateAliases(jetDef) + evtShapeAlg = buildEventShapeAlg( jetDef, "HLT_" ) + rhoKey = evtShapeAlg.EventDensityTool.OutputContainer else: rhoKey = "" diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py index d3a86c27f3c681c2a4ab55359f408790a70e7be0..ce5aedd1244a59e4b94fb4deeeca6801e491e474 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/MET/METRecoSequences.py @@ -57,13 +57,18 @@ def pfoRecoSequence(dummyFlags, RoIs, **recoDict): jetRecoDict["calib"] = "em" jetRecoDict["dataType"] = "pf" constit = defineJetConstit(jetRecoDict, pfoPrefix=pfoPrefix) + from JetRecConfig.ConstModHelpers import aliasToInputDef + constit = aliasToInputDef(constit) constit_mod_seq = getConstitModAlg( constit, - "HLT", - tvaKey="JetTrackVtxAssoc_{trkopt}".format(**jetRecoDict), - vtxKey="HLT_IDVertex_FS", + # "HLT", + # tvaKey="JetTrackVtxAssoc_{trkopt}".format(**jetRecoDict), + # vtxKey="HLT_IDVertex_FS", ) - return [tcSeq, pfSeq, constit_mod_seq], pfoPrefix + allSeqs = [tcSeq, pfSeq] + if constit_mod_seq : + allSeqs.append(constit_mod_seq) + return allSeqs, pfoPrefix def cvfClusterSequence(dummyFlags, RoIs, **recoDict):