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):