diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/ChainLabelParser.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/ChainLabelParser.py
index 1f107aa34c88b77807a563b9727e4205c467808d..0e54cfb3f66872744d7e05f8ae1219ead2cbcac7 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/ChainLabelParser.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/ChainLabelParser.py
@@ -1,9 +1,9 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 from __future__ import print_function
 from __future__ import absolute_import
 
 from TrigHLTJetHypo.node import Node
-from TrigHLTJetHypo.constants import lchars, digits
+from TrigHLTJetHypo.constants import lchars, param_alphabet
 
 def get_char(s):
     """character generator"""
@@ -158,7 +158,7 @@ class ChainLabelParser(object):
         
         c = next(self.gc)
     
-        if c in lchars or c in digits or c ==',':
+        if c in param_alphabet:
             self.paramAppend(c)
             return
 
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/ConditionsToolSetterFastReduction.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/ConditionsToolSetterFastReduction.py
index a313842708186618cb92a7bed17ed7fb4de6b81b..4c6e272af4786a3c242cc8fb13b78027a8706577 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/ConditionsToolSetterFastReduction.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/ConditionsToolSetterFastReduction.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 
 """Instantiates TrigJetHypoToolConfig_fastreduction AlgTool 
 from a hypo tree."""
@@ -84,6 +84,32 @@ class ConditionsToolSetterFastReduction(object):
 
         return condition_tools
 
+
+    def _make_filter_condition_tool(self, node):
+
+        """Condtion filters use a list of CompoundCondition containing
+        single jet elemental conditions  select a subset of the reco
+        jets to send to the a Condition"""
+        
+        el_condition_tools = []
+
+        for fc, mult in node.filter_dicts:
+            
+            assert len(fc) == 1  # 1 elemental condition
+            assert mult == 1
+            el_condition_tools.extend(self._make_el_condition_tools(fc))
+
+        if not el_condition_tools:
+            el_condition_tools.append(self.algToolFactory('all'))
+
+        capacitychecked_condition_tool = self.algToolFactory(
+            'capacitychecked')
+
+        capacitychecked_condition_tool.conditionMakers = el_condition_tools
+        capacitychecked_condition_tool.multiplicity = 1
+        
+        return capacitychecked_condition_tool
+
     
     def _make_compound_condition_tools(self, node):
         """For each element of  node.conf_attrs, construct a 
@@ -112,6 +138,7 @@ class ConditionsToolSetterFastReduction(object):
         for i in range(len(node.conf_attrs)):
             c, mult = node.conf_attrs[i]
             cpi = ''
+ 
             if node.chainpartinds:
                 cpi = node.chainpartinds[i][0]
                 assert mult == node.chainpartinds[i][1]
@@ -136,29 +163,6 @@ class ConditionsToolSetterFastReduction(object):
         return outer_condition_tools
 
     
-    def _make_filter_tool(self, node):
-        """Condtion filters use a list of CompoundCondition containing
-        single jet elemental conditions  select a subset of the reco
-        jets to send to the a Condition"""
-        
-        el_condition_tools = []
-        for fc, mult in node.filter_conditions:
-            assert len(fc) == 1  # 1 elemental condition
-            assert mult == 1
-            el_condition_tools.extend(self._make_el_condition_tools(fc))
-
-        if not el_condition_tools:
-            el_condition_tools.append(self.algToolFactory('all'))
-
-        capacitychecked_condition_tool = self.algToolFactory(
-            'capacitychecked')
-
-        capacitychecked_condition_tool.conditionMakers = el_condition_tools
-        capacitychecked_condition_tool.multiplicity = 1
-        
-        return capacitychecked_condition_tool
-    
-
     def _mod_leaf(self, node):
         """ Add Condition tools to For a leaf node."""
 
@@ -178,16 +182,8 @@ class ConditionsToolSetterFastReduction(object):
         node.compound_condition_tools = self._make_compound_condition_tools(
             node)
 
-        # make condition builder AlgTools for the condition filters.
-        # condition filters select subsets of the input jets to present
-        # to a condition,
-        
-        node.filter_tool = self._make_filter_tool(node)
-
-    
-        # if node.scenario == 'agg':
-        #     print (node)
-        #    assert False
+        node.filter_condition_tool = self._make_filter_condition_tool(
+            node)
             
     def report(self):
         return str(self.algToolFactory)
@@ -203,10 +199,11 @@ class ConditionsToolSetterFastReduction(object):
 
             assert (len(node.compound_condition_tools) == 1)
             cmap[node.node_id] = node.compound_condition_tools[0]
-            fmap[node.node_id] = node.filter_tool
-            
+
+            fmap[node.node_id] = node.filter_condition_tool
+
         else:
-            # must have a tool for Gaudi to instantiate in
+
             cmap[node.node_id] = self.algToolFactory('capacitychecked')
             cmap[node.node_id].conditionMakers = [self.algToolFactory('all')]
             cmap[node.node_id].multiplicity = 1
@@ -215,7 +212,6 @@ class ConditionsToolSetterFastReduction(object):
             fmap[node.node_id].conditionMakers = [self.algToolFactory('all')]
             fmap[node.node_id].multiplicity = 1
 
-            
         
         for cn in node.children:
             self._fill_conditions_map(cn, cmap, fmap)
@@ -255,23 +251,26 @@ class ConditionsToolSetterFastReduction(object):
         self._set_conditions(tree)
   
 
-        tree_map = {}
+        tree_map = {}  # tree of node indices
         self._fill_tree_map(tree, tree_map)
 
-        for k, v in tree_map.items():
-            log.debug("Tree map debug %s %s", str(k), str(v))
-            
         treeVec = self._map_2_vec(tree_map)
 
         conditionsMap = {}
         filterConditionsMap = {}
+
         self._fill_conditions_map(tree, conditionsMap, filterConditionsMap)
-        conditionsVec = self._map_2_vec(conditionsMap)
+
+        # conditionVec is an attribute as it will be used directly
+        # to make prefilter tools, so hold onto it here
+        self.conditionMakersVec = self._map_2_vec(conditionsMap)
         filterConditionsVec = self._map_2_vec(filterConditionsMap)
                
         # make a config tool and provide it with condition makers
         config_tool = self.algToolFactory('fastreduction')
-        config_tool.conditionMakers = conditionsVec
+
+
+        config_tool.conditionMakers = self.conditionMakersVec
         config_tool.filtConditionsMakers = filterConditionsVec
         config_tool.treeVector = treeVec
         self.config_tool = config_tool
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/FastReductionAlgToolFactory.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/FastReductionAlgToolFactory.py
index 5cf284a9274fcd98d45f458e128bd8abaeb261de..beec83e6d744f1feac1a132e455e438a9ba5c4dd 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/FastReductionAlgToolFactory.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/FastReductionAlgToolFactory.py
@@ -11,7 +11,11 @@ class FastReductionAlgToolFactory:
         self.tool_factories = {
             'eta': [CompFactory.TrigJetConditionConfig_abs_eta, 0], 
             'peta': [CompFactory.TrigJetConditionConfig_signed_eta, 0],
+            'ceta': [CompFactory.TrigJetConditionConfig_signed_eta, 0],
             'neta': [CompFactory.TrigJetConditionConfig_signed_eta, 0],
+            'pphi': [CompFactory.TrigJetConditionConfig_phi, 0],
+            'cphi': [CompFactory.TrigJetConditionConfig_phi, 0],
+            'nphi': [CompFactory.TrigJetConditionConfig_phi, 0],
             'et': [CompFactory.TrigJetConditionConfig_et, 0],
             'djmass': [CompFactory.TrigJetConditionConfig_dijet_mass, 0],
             'djdphi': [CompFactory.TrigJetConditionConfig_dijet_dphi, 0],
@@ -39,7 +43,8 @@ class FastReductionAlgToolFactory:
 
 
     def __call__(self, key, extra=''):
-   
+
+        key = key.split(':')[-1]  # key = 'eta' for 'XXX:eta'
         klass = self.tool_factories[key][0]
         sn = self.tool_factories[key][1]
         
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/TrigJetHypoToolConfig.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/TrigJetHypoToolConfig.py
index d46791a7c4df7d787d370d5cbaab763d730f600a..c9eb6890c3368d18e15079effa322f2e1a35fef6 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/TrigJetHypoToolConfig.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/TrigJetHypoToolConfig.py
@@ -1,9 +1,11 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 from __future__ import print_function
 
 from AthenaConfiguration.ComponentFactory import CompFactory
 
-from TrigHLTJetHypo.treeVisitors import TreeParameterExpander
+from TrigHLTJetHypo.treeVisitors import (TreeParameterExpander,
+                                         FilterConditionsMover)
+
 from TrigHLTJetHypo.ConditionsToolSetterFastReduction import (
     ConditionsToolSetterFastReduction,
 )
@@ -13,10 +15,10 @@ from TrigHLTJetHypo.FastReductionAlgToolFactory import (
     )
 
 from  TrigHLTJetHypo.chainDict2jetLabel import chainDict2jetLabel
+from  TrigHLTJetHypo.prefilterLabelFromChainDict import (
+    prefilterLabelFromChainDict,)
 
 from  TrigHLTJetHypo.ChainLabelParser import ChainLabelParser
-from TrigHLTJetHypo.node import Node
-from TrigHLTJetHypo.NodeSplitterVisitor import NodeSplitterVisitor
 
 
 from AthenaCommon.Logging import logging
@@ -24,74 +26,57 @@ log = logging.getLogger( 'TrigJetHypoToolConfig' )
 
 algToolFactory = FastReductionAlgToolFactory()
 
-def  tree2tools(rootless_tree, toolSetter):
 
-    
-    # add a root node so that split simple nodes cann connect.
-    tree = Node('root')
-    tree.children = [rootless_tree]
-    tree.node_id = 0
-    tree.parent_id = 0
-    rootless_tree.tree_top = False
-    tree.tree_top = True
-
-    #expand strings of cuts to a cut dictionary
+def  tree2tools(tree, toolSetter):
+
+    # expand strings of cuts to a cut dictionary
     visitor = TreeParameterExpander()
     tree.accept(visitor)
     log.debug(visitor.report())
 
-    visitor = NodeSplitterVisitor()
+    # move the filter conditions into node.filter_conditions
+    visitor = FilterConditionsMover()
     tree.accept(visitor)
 
     # tell the child nodes who their parent is.
     tree.set_ids(node_id=0, parent_id=0)
 
-    # create - possibly nested - tools
-
-    # chain name in run 2 dicts were missing the 'HLT_' prefix
-    # but it seems it is necessary to run the hypos in AthenaMT ?...?
-
+    # create - possibly nested - tools, The tools are attached to the visitor.
     toolSetter.mod(tree)
 
     return tree  # used by debug tools
 
 
-def  nodeForestFromChainLabel(
-        chain_label, # simple([(260et,320eta490, leg000)])
-        chain_name,): # HLT_j260_320eta490_L1J75_31ETA49
-    
-    parser = ChainLabelParser(chain_label, debug=False)
+def  nodesFromLabel(label):
+    """from a label eg simple([(260et,320eta490, leg000)])
+    create a node. The node may have children, thus forming a tree."""
 
+    parser = ChainLabelParser(label, debug=False)
     return parser.parse()
    
-def trigJetHypoToolHelperFromDict_(
+def trigJetHypoToolHelperConfigurersFromLabel(
         chain_label,  # simple([(260et,320eta490, leg000)])
         chain_name,):  # HLT_j260_320eta490_L1J75_31ETA49
 
-    
-    node_forest =  nodeForestFromChainLabel(chain_label,
-                                            chain_name)
-
-    helper_tool = algToolFactory('helper')
+    # construct the FastReduction Trees
+    node_forest =  nodesFromLabel(chain_label)
 
+    # use a visitor to convert simple types to a HypoConfigurer Tool,
+    # one for each
+    # tree, The HypoConfigurer Tool will have ConditionConfigure AlgTools
+    # each creating a single Condition.
+    configurer_tools = []
     for tree in node_forest:
         toolSetter = ConditionsToolSetterFastReduction(algToolFactory)
         tree2tools(tree, toolSetter)
-        helper_tool.HypoConfigurers.append(toolSetter.config_tool)   
+        configurer_tools.append(toolSetter.config_tool)   
 
         log.debug(toolSetter.report())
-    
-    return helper_tool
 
-def  trigJetHypoToolHelperFromDict(chain_dict):
-    """Produce  a jet trigger hypo tool helper from a chainDict
-    Helper tools do the hypio work. They are used, for example
-    by TrigJetHypoToolMT to  devide whether an event passes.
-    A Helper Tool returned by this function may be the root of a Helper
-    Tool tree structure."""
+    return configurer_tools
 
-    
-    log.debug('trigJetHypoToolFromDict chainDict %s', str(chain_dict))
+
+def trigJetConfigurerToolsFromDict(chain_dict):
 
     try:
         chain_label = chainDict2jetLabel(chain_dict)
@@ -101,17 +86,60 @@ def  trigJetHypoToolHelperFromDict(chain_dict):
             chain_dict['chainName'],)
         m += '  jet hypo scenario: %s' % (
             chain_dict['chainParts'][0]['hypoScenario'],)
-
-        log.error(m)
-
+        log.error(m)        
         raise e
 
     chain_name = chain_dict['chainName']
+    return trigJetHypoToolHelperConfigurersFromLabel(chain_label, chain_name)
 
+       
+def  trigJetHypoToolHelperFromDict(chain_dict):
+    """Produce  a jet trigger hypo tool helper from a chainDict
+    Helper tools do the hypio work. They are used, for example
+    by TrigJetHypoToolMT to  devide whether an event passes.
+    A Helper Tool returned by this function may be the root of a Helper
+    Tool tree structure."""
 
-    return trigJetHypoToolHelperFromDict_(chain_label,
-                                          chain_name)
+    
+    log.debug('trigJetHypoToolFromDict chainDict %s', str(chain_dict))
+    algToolFactory = FastReductionAlgToolFactory()
+
+    # Build the helper tool
+    helper_tool = algToolFactory('helper')
 
+    # build the Configuration Tools for the JetHypoHelperTool.
+    # there is one Configurer per tree in the forest.
+    configurer_tools = trigJetConfigurerToolsFromDict(chain_dict)
+
+    helper_tool.HypoConfigurers = configurer_tools
+
+    # find the prefilters. These are Conditions that will be used
+    # remove jets from the input jet collection
+    # before the helper is run (eg blank out eta-phi regions,...)
+    #
+    # 1/2021... one could imagine using FastReducer to act as
+    # a prefilter predicate. For now, more prosaically
+    # we build a Conditions tree consisting  of a root node with
+    # a single child. The root node willbe an AcceptAll condition.
+    # Vetoing on this will remove all jets. The usefull conditions
+    # are in the child node. These will be extracted here.
+
+    label = prefilterLabelFromChainDict(chain_dict)
+    if label:
+        forest = ChainLabelParser(label, debug=False).parse()
+        assert len(forest) == 1
+        tree = forest[0]
+
+        assert tree.size() == 2  # root and single child.
+        
+        tool_setter = ConditionsToolSetterFastReduction(algToolFactory)
+        
+        tree2tools(tree.children[0], tool_setter)  # single child node
+
+        helper_tool.prefiltConditionMakers = tool_setter.conditionMakersVec
+
+    return helper_tool
+    
 
 def  trigJetHypoToolFromDict_(chain_dict, hypo_tool, debug=False):
     """Produce  a jet trigger hypo tool from a chainDict"""
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/chainDict2jetLabel.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/chainDict2jetLabel.py
index d06990e10133342deff7bbfb5c37477b4326b2b6..d06b0eb25cd9c0c0ac216677920bb5df2d0a9abf 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/chainDict2jetLabel.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/chainDict2jetLabel.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 from __future__ import print_function
 from __future__ import absolute_import
 import re
@@ -17,6 +17,19 @@ reject_substr = (
 
 reject_substr_res = re.compile(r'%s' % '|'.join(reject_substr))
 
+def make_label(scenario, pattern, template, extra={}):
+
+    r = re.compile(pattern)
+    m = r.match(scenario)
+    assert m, 'chainDict2jetlabel - pattern %s does not match scenario %s' % (
+        pattern, scenario)
+
+    argdict = m.groupdict()
+    argdict.update(extra)
+
+    label = template % argdict
+    return label
+
 
 def _select_simple_chainparts(chain_parts):
     """ Reject unsuported chain parts """
@@ -43,7 +56,7 @@ def _make_simple_label(chain_parts, leg_label):
         raise NotImplementedError(msg)
 
     chainpartind = 0
-    label = 'simple(['
+    label = 'root([]'
     for cp in chain_parts:
         smcstr =  str(cp['smc'])
         jvtstr =  str(cp['jvt'])
@@ -51,6 +64,7 @@ def _make_simple_label(chain_parts, leg_label):
         if smcstr == 'nosmc':
             smcstr = ''
         for i in range(int(cp['multiplicity'])):
+            label += 'simple(['
             # condition_str = '(%set,%s,%s)' % (str(cp['threshold']),
             #                                  str(cp['etaRange']),
             #                                  smcstr,)
@@ -72,9 +86,9 @@ def _make_simple_label(chain_parts, leg_label):
             if not condition_str.endswith(')'):
                 condition_str += ')'
             label += condition_str
+            label += '])'
         chainpartind += 1
-
-    label += '])'
+    label += ')'
     return label
 
 
@@ -103,68 +117,40 @@ def _make_fbdjnoshared_label(chain_parts, leg_label):
     assert len(chain_parts) == 1
     
     scenario = chain_parts[0]['hypoScenario']
-    assert scenario.startswith('f')
-    args = _args_from_scenario(scenario)
-
-    # arg res tuples constain a regex, and a counter
-    # to count the number of matches.
-    arg_res = [
-        [re.compile(r'(?P<lo>\d*)(?P<key>fbet)(?P<hi>\d*)'), 0],
-        [re.compile(r'(?P<lo>\d*)(?P<key>mass)(?P<hi>\d*)'), 0],
-        [re.compile(r'(?P<lo>\d*)(?P<key>et)(?P<hi>\d*)'), 0],
-    ]
-
-    defaults = {
-        'et0': ('101', 'inf'),
-        'et1': ('103', 'inf'),
-        'mass0': ('800', 'inf'),
-        'fbet0': ('501', 'inf'),
-    }
-
-    argvals = {}
-    assert len(args) == len(arg_res) + 1  # +1 because et occurs twice.
-    while args:
-        arg = args.pop()
-        for ar  in arg_res:
-            regx = ar[0]
-            occurence=ar[1]  # no iof time this argument is used eg et used 2x
-            m = regx.match(arg)
-            if m is not None:
-                gd = m.groupdict()
-                key = gd['key'] + str(occurence)
-                ar[1] += 1  # bump the occurrence cout
-                try:
-                    lo = float(gd['lo'])
-                except ValueError:
-                    lo = defaults[key][0]
-                argvals[key+'lo'] = lo 
-                try:
-                    hi = float(gd['hi'])
-                except ValueError:
-                    hi = defaults[key][1]
-                argvals[key+'hi'] =  hi
-
-    assert len(args) == 0
-
-    argvals['leg_label'] = leg_label
-
-    return """
-    all
-    (
-      []
-      simple
-      (
-        [(%(fbet0lo).0fet, 500neta, leg000)(%(fbet0lo).0fet, peta500, %(leg_label)s)]
-      )
-      dijet
-      (
-        [(%(mass0lo).0fdjmass, 26djdphi)]
-        simple
-        (
-          [(%(et0lo).0fet, 0eta320, leg000)(%(et1lo).0fet, 0eta320, %(leg_label)s)]
-        )
-      )
-    )""" % argvals
+    assert scenario.startswith('fbdjnoshared')
+
+    # example scenario: fbdjnosharedSEP10etSEP20etSEP34massSEP50fbet
+    # example label:
+    # root([]
+    #  simple([(50et, 500neta, leg000)])
+    #  simple([(50et, peta500, leg000)])
+    #
+    #  dijet
+    #  (
+    #    [(34djmass, 26djdphi)]
+    #    simple([(20et, 0eta320, leg000)])
+    #    simple([(10et, 0eta320, leg000)])
+    #   )
+    # )
+
+    pattern = r'^fbdjnosharedSEP'\
+        r'(?P<j1etlo>\d*)et(?P<j1ethi>\d*)SEP'\
+        r'(?P<j2etlo>\d*)et(?P<j2ethi>\d*)SEP'\
+        r'(?P<masslo>\d*)mass(?P<masshi>\d*)SEP'\
+        r'(?P<fbetlo>\d*)fbet(?P<fbethi>\d*)$'
+        
+        
+    template = r'root([]'\
+        r'simple([(%(fbetlo)set%(fbethi)s, 500neta, %(leg_label)s)])'\
+        r'simple([(%(fbetlo)set%(fbethi)s, peta500, %(leg_label)s)])'\
+        r'dijet([(%(masslo)sdjmass%(masshi)s, 26djdphi)]'\
+        r'simple([(%(j1etlo)set%(j1ethi)s, 0eta320, %(leg_label)s)])'\
+        r'simple([(%(j2etlo)set%(j2ethi)s, 0eta320, %(leg_label)s)])))'
+
+    extra = {'leg_label': leg_label}
+    
+    label = make_label(scenario, pattern, template, extra)
+    return label
 
 
 def  _make_fbdjshared_label(chain_parts, leg_label):
@@ -174,16 +160,16 @@ def  _make_fbdjshared_label(chain_parts, leg_label):
 
     
     return """
-    simple
-    (
-    [(50et, 500neta, leg000)(50et, peta500, leg000)]
+    root([]
+    simple([(50et, 500neta, %s)])
+    simple([(50et, peta500, %s)])
     )
     dijet
     (
     [(34djmass, 26djdphi)]
-        simple
-        ([(10et, 0eta320, leg000)(20et, 0eta320, leg000)])
-    )"""
+        simple([(10et, 0eta320, %s)])
+        simple([(20et, 0eta320, %s)])
+    )""" % ((leg_label,) * 4)
 
     
 def _make_dijet_label(chain_parts, leg_label):
@@ -202,56 +188,27 @@ def _make_dijet_label(chain_parts, leg_label):
     
     assert scenario.startswith('dijet')
 
-    arg_res = [
-        re.compile(r'^(?P<lo>\d*)(?P<key>djmass)(?P<hi>\d*)$'),
-        re.compile(r'^(?P<lo>\d*)(?P<key>j1et)(?P<hi>\d*)$'),
-        re.compile(r'^(?P<lo>\d*)(?P<key>j1eta)(?P<hi>\d*)$'),
-        re.compile(r'^(?P<lo>\d*)(?P<key>j2et)(?P<hi>\d*)$'),
-        re.compile(r'^(?P<lo>\d*)(?P<key>j2eta)(?P<hi>\d*)$'),
-    ]
-
-    defaults = {
-        'j1et': ('100', 'inf'),
-        'j2et': ('100', 'inf'),
-        'j1eta': ('0', '320'),
-        'j2eta': ('0', '320'),
-        'djmass': ('1000', 'inf'),
-    }
+    # example scenario: 'dijetSEP80j1etSEP0j1eta240SEP80j2etSEP0j2eta240SEP700djmass',
 
+    pattern = r'^dijetSEP('\
+    r'(?P<j1etlo>\d*)j1et(?P<j1ethi>\d*)SEP'\
+    r'(?P<j1etalo>\d*)j1eta(?P<j1etahi>\d*)SEP'\
+    r'(?P<j2etlo>\d*)j2et(?P<j2ethi>\d*)SEP'\
+    r'(?P<j2etalo>\d*)j2eta(?P<j2etahi>\d*)SEP'\
+    r'(?P<djmasslo>\d*)djmass(?P<djmasshi>\d*))$'
 
-    args = _args_from_scenario(scenario)
-    argvals = {}
-    while args:
-        assert len(args) == len(arg_res)
-        arg = args.pop()
-        for r in arg_res:
-            m = r.match(arg)
-            if m is not None:
-                arg_res.remove(r)
-                gd = m.groupdict()
-                key = gd['key']
-
-                try:
-                    lo = float(gd['lo'])
-                except ValueError:
-                    lo = defaults[key][0]
-                argvals[key+'lo'] = lo 
-                try:
-                    hi = float(gd['hi'])
-                except ValueError:
-                    hi = defaults[key][1]
-                argvals[key+'hi'] =  hi
-
-    assert len(args) == len(arg_res)
-    assert len(args) == 0
-
-    argvals['leg_label'] = leg_label
-    
-    return """
-    dijet(
-    [(%(djmasslo).0fdjmass)]
-    simple([(%(j1etlo).0fet, %(j1etalo).0feta%(j1etahi).0f, %(leg_label)s)
-    (%(j2etlo).0fet, %(j2etalo).0feta%(j2etahi).0f, %(leg_label)s)]))""" % argvals
+    template = 'root([] dijet(' \
+        '[(%(djmasslo)sdjmass%(djmasshi)s, 26djdphi)]'\
+        'simple([(%(j1etlo)set, %(j1etalo)seta%(j1etahi)s, %(leg_label)s)])'\
+        'simple([(%(j2etlo)set, %(j2etalo)seta%(j2etahi)s, %(leg_label)s)])))'
+
+    # example label:
+    #    dijet([(700djmass)] simple([(80et, 0eta240, leg002)]) simple([(80et, 0eta240, leg002)])))
+
+    extra = {'leg_label': leg_label}
+    label = make_label(scenario, pattern, template, extra)
+
+    return label
 
 
 def _make_agg_label(chain_parts, leg_label):
@@ -269,58 +226,23 @@ def _make_agg_label(chain_parts, leg_label):
     assert len(chain_parts) == 1, '_make_agg_label, no. of chain parts != 1'
     scenario = chain_parts[0]['hypoScenario']
     
-    assert scenario.startswith('agg'), '_make_agg_label(): scenario does not start with agg'
-
-    arg_res = [
-        re.compile(r'^(?P<lo>\d*)(?P<key>ht)(?P<hi>\d*)$'),
-        re.compile(r'^(?P<lo>\d*)(?P<key>et)(?P<hi>\d*)$'),
-        re.compile(r'^(?P<lo>\d*)(?P<key>eta)(?P<hi>\d*)$'),
-    ]
-
-    defaults = {
-        'ht': ('0', 'inf'),
-        'et': ('0', 'inf'),
-        'eta': ('0', 'inf'),
-     }
-
-
-    args = _args_from_scenario(scenario)
-    argvals = {}
-    nargs = len(args)
-    assert len(args) <= len(arg_res), 'bad num of args %d, expected < %d' % (len(args),
-                                                                             len(arg_res))
-
-    # obtain argument values frrom scenario
-    while args:
-        arg = args.pop()
-        for r in arg_res:
-            m = r.match(arg)
-            if m is not None:
-                arg_res.remove(r)
-                gd = m.groupdict()
-                key = gd['key']
-
-                try:
-                    lo = float(gd['lo'])
-                except ValueError:
-                    lo = float(defaults[key][0])
-                argvals[key+'lo'] = lo 
-                try:
-                    hi = float(gd['hi'])
-                except ValueError:
-                    hi = float(defaults[key][1])
-                argvals[key+'hi'] =  hi
-
-    assert len(argvals) == 2*nargs, 'no of args: %d, expected %d' % (len(argvals), 2*nargs)
-
-    argvals['leg_label'] = leg_label
-    result =  """
-    agg([(%(htlo).0fht, %(leg_label)s)
-        (%(etlo).0fet)
-    (%(etalo).0feta%(etahi).0f)
-    ])"""  % argvals
-    print (result)
-    return result
+    # assert scenario.startswith('agg'), '_make_agg_label(): scenario does not start with agg'
+
+    # the scenario contains the  ht cut, and filter cuts.
+    # all cuts thast do no start with 'ht are filter cuts
+
+    pattern = r'^aggSEP(?P<htlo>\d*)ht(?P<hthi>\d*)SEP'\
+        r'(?P<etlo>\d*)et(?P<ethi>\d*)SEP'\
+        r'(?P<etalo>\d*)eta(?P<etahi>\d*)$'
+    
+    template = 'root([]agg([(%(htlo)sht, %(leg_label)s)'\
+        '(%(etlo)sfltr:et)'\
+        '(%(etalo)sfltr:eta%(etahi)s)]))'
+    
+    extra = {'leg_label': leg_label}
+    label = make_label(scenario, pattern, template, extra)
+
+    return label
     
 
 def chainDict2jetLabel(chain_dict):
@@ -335,7 +257,8 @@ def chainDict2jetLabel(chain_dict):
                    if len(chainParts) > 1 create and of simple and other.
     """
 
-    # suported scenarios 
+    # suported scenarios. Caution! two keys in the router dict
+    # must not share a common initial substring.
     router = {
         'simple': _make_simple_label,
         'agg':   _make_agg_label,
@@ -344,18 +267,23 @@ def chainDict2jetLabel(chain_dict):
         'fbdjnoshared': _make_fbdjnoshared_label,
     }
 
+    # check that no key is the initial susbstring of another key
+    # such a case would break the code below.
+    keys = sorted(router.keys(), key=len)
+    for i in range(1, len(keys)):
+        assert not (keys[i].startswith(keys[i-1]))
+
     # chain_part - scenario association
     cp_sorter = {}
     for k in router: cp_sorter[k] = []
 
     chain_parts = chain_dict['chainParts']
     for cp in chain_parts:
-        if cp['signature'] != 'Jet' and cp['signature'] != 'Bjet': 
-            continue
-        for k in cp_sorter:
-            if cp['hypoScenario'].startswith(k):
-                cp_sorter[k].append(cp)
-                break
+        if cp['signature'] in ('Jet', 'Bjet'): 
+            for k in cp_sorter:
+                if cp['hypoScenario'].startswith(k):
+                    cp_sorter[k].append(cp)
+                    break
 
     # obtain labels by scenario.
     labels = []
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/constants.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/constants.py
index 10b599fea3161ccc3a1c21390ea541c3874a40cf..92d11664c9f48111656fa0666c44f47c9caef83a 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/constants.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/constants.py
@@ -1,8 +1,12 @@
-# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+
 
 lchars = 'abcdefghijklmnopqrstuvwxyz'
 digits = '0123456789'
 delims = '()[],'
+signs = '-+'
+seps = ':,'
 logicals = ('and', 'or', 'not')
-alphabet = lchars + digits + delims
+param_alphabet = lchars + digits + seps
+alphabet = lchars + digits + delims + signs + seps
 
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/node.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/node.py
index 9bc459c46b581f42844a33fecf5078a172767305..3101cbf50a6b9a6e7da4010932827d7b9d2a4a92 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/node.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/node.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 """
 Node - represents a tree structure. scenario and parameters which are strings 
 filled in while parsing a jet hyp[o label. A visitor is used to convert 
@@ -25,17 +25,20 @@ class Node(object):
         self.scenario = scenario
         self.parameters = ''
         self.children = []
-        self.conf_attrs = []  # list of dictionaries
+        self.conf_attrs = []  # list of dictionaries to build conditions
 
+
+       
         # filled in by a CondtionsTollSetter:
         self.compound_condition_tools = [] 
         self.chainpartinds = []
-
+        
         # Condition objects may have filters
         # eg HT may have an et filter. Filters are made up of conditions
         # and are used to form jet subsets.
-        self.filter_conditions = []
-        self.filter_tool = None
+        self.filter_condition_tool = None
+        self.filter_dicts = []
+        
         
         self.tree_top = False
         self.tool = None
@@ -94,16 +97,17 @@ class Node(object):
         for ca in self.conf_attrs:
             s.append(indent + str(ca))
         
-        s.append(indent + 'filter_conditions [%d]:' % (
-            len(self.filter_conditions),))
+        s.append(indent + 'filter_dicts [%d]:' % (
+            len(self.filter_dicts),))
                  
-        for fc in self.filter_conditions:
+        for fc in self.filter_dicts:
             s.append(indent + str(fc))
 
         s.append(indent + 'compoundConditionTools [%d]:' % len(
             self.compound_condition_tools))
 
-        s.append(indent + 'filter_tool :' + str(self.filter_tool))
+        s.append(indent + 'condition_filter_tool: %s' % str(
+            self.filter_condition_tool))
 
         s.append(indent + 'No of children: %d\n' % len(self.children))
 
@@ -119,6 +123,11 @@ class Node(object):
             
         return s
 
+    def size(self):
+        sz = 1
+        for c in self.children: sz += c.size()
+        return sz
+
     def dump(self):
 
         return '\n'.join(self.dump_(0))
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/prefilterLabelFromChainDict.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/prefilterLabelFromChainDict.py
new file mode 100644
index 0000000000000000000000000000000000000000..1aa700db6906ecbed6965f7689b47cbfe8d2fa1e
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/prefilterLabelFromChainDict.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+
+from __future__ import print_function
+from TrigHLTJetHypo.constants import lchars
+import re
+
+cut_re = re.compile(r'(?P<lo>\d*)(?P<key>[%s]+)(?P<hi>\d*)' %lchars)
+
+def prefilterArgsFromChainDict(chain_dict):
+    """Marshal and return the strings use to build a prefilter for the jet
+    hypo. These may be spread across > 1 chainPart within the chainDict."""
+    
+    chain_parts = [cp for cp in chain_dict['chainParts'] if
+                   cp['signature'] in ('Jet', 'Bjet') and 'prefilters' in cp]
+    
+    prefilter_args = []
+
+    [prefilter_args.extend(cp['prefilters']) for cp in chain_parts]
+    
+    for p in prefilter_args:
+        assert p.startswith('pfltr')
+                   
+    return prefilter_args
+                   
+
+def prefilterLabelFromChainDict(chain_dict):
+    """create a jet hypo label. For now, only arguments like
+    'prefltrSEP100ceta90SEP100nphi50' are accepted. """
+
+                   
+    prefilter_args = prefilterArgsFromChainDict(chain_dict)
+    if not prefilter_args: return ''
+    
+    label = 'root([]simple(['
+
+    arg_spacer = ', '
+    for pfa in prefilter_args:
+        frags = pfa.split('SEP')[1:]
+        assert frags
+        label += '('
+        for frag in frags:
+            cut_match = cut_re.match(frag)
+            assert cut_match is not None
+            label += frag + arg_spacer
+        label = label[:-len(arg_spacer)] + ')'
+    label += ']))'
+
+    return label
+                  
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/testChainDictMaker.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/testChainDictMaker.py
index 9e37cfaa47b5ce3ffc9226f899391b5c4043a94a..a7b72d6e57c826a48993cde7578ee6440e380102 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/testChainDictMaker.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/testChainDictMaker.py
@@ -1,18 +1,20 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 """Make chain dicts for testing jet hypo config modules"""
 
 from __future__ import print_function
 
 from TriggerMenuMT.HLTMenuConfig.Menu.Physics_pp_run3_v1 import (
-        SingleJetGroup,
-        MultiJetGroup)
+    SingleJetGroup,
+    MultiJetGroup,
+    MuonJetGroup,
+    PhysicsStream)
 
 from TriggerMenuMT.HLTMenuConfig.Menu.ChainDefInMenu import ChainProp
 from TriggerMenuMT.HLTMenuConfig.Menu.DictFromChainName import dictFromChainName
 
 from chainDict2jetLabel import chainDict2jetLabel 
 from TrigJetHypoToolConfig import (trigJetHypoToolFromDict,
-                                   nodeForestFromChainLabel,
+                                   nodesFromLabel,
                                    tree2tools,)
 
 from TrigHLTJetHypo.ConditionsToolSetterFastReduction import (
@@ -22,8 +24,9 @@ from TrigHLTJetHypo.ConditionsToolSetterFastReduction import (
 from TrigHLTJetHypo.FastReductionAlgToolFactory import (
     FastReductionAlgToolFactory,)
 
+import sys
 
-def testChainDictMaker():
+def testChainDictMaker(idict):
 
     chain_props = [
         ChainProp(name='HLT_j260_320eta490_L1J75_31ETA49',
@@ -36,7 +39,7 @@ def testChainDictMaker():
                   l1SeedThresholds=['FSNOSEED']*2, groups=MultiJetGroup),
 
 
-        ChainProp(name='HLT_j0_HTSEP1000htSEP100etSEP0eta320_L1J15',
+        ChainProp(name='HLT_j0_aggSEP1000htSEP30etSEP0eta320_L1J15',
                   l1SeedThresholds=['FSNOSEED'], groups=MultiJetGroup),
 
 
@@ -52,10 +55,30 @@ def testChainDictMaker():
 
          ChainProp(name='HLT_j0_fbdjshared_L1J20', groups=SingleJetGroup),
         
-        ChainProp(name='HLT_j40_j0_aggSEP50htSEP10etSEP0eta320_L1J20',l1SeedThresholds=['FSNOSEED']*2, groups=MultiJetGroup),
-        ChainProp(name='HLT_j0_fbdjnosharedSEP10etSEP20etSEP34massSEP50fbet_L1J20', groups=SingleJetGroup),
+        ChainProp(name='HLT_j40_j0_aggSEP50htSEP10etSEP0eta320_L1J20',
+                  l1SeedThresholds=['FSNOSEED']*2,
+                  groups=MultiJetGroup),
+
+        ChainProp(name='HLT_j0_fbdjnosharedSEP10etSEP20etSEP34massSEP50fbet_L1J20',
+                  groups=SingleJetGroup),
+
+        ChainProp(name='HLT_j60_pfltrSEP100ceta90SEP100nphi50_L1J20',
+                  groups=SingleJetGroup),
+
+        ChainProp(name='HLT_j45_pf_ftf_preselj20_L1J15', groups=SingleJetGroup),
+        
+        ChainProp(name='HLT_j85_ftf_pfltrSEP300ceta210SEP300nphi10_L1J20',
+                  groups=SingleJetGroup),
+        
+        ChainProp(name='HLT_j0_dijetSEP80j1etSEP0j1eta240SEP80j2etSEP0j2eta240SEP700djmass_L1J20', groups=SingleJetGroup),
+
+        ChainProp(name='HLT_2mu6_2j50_0eta490_j0_dijetSEP50j1etSEP50j2etSEP900djmass_L1MJJ-500-NFF',l1SeedThresholds=['MU6','FSNOSEED', 'FSNOSEED'],stream=[PhysicsStream], groups=MuonJetGroup),
     ]
 
+    if idict is not None:
+        chain_props = [chain_props[idict]]
+
+    print (chain_props)
     result = []
     for cp in chain_props:
         chain_dict = dictFromChainName(cp)
@@ -64,7 +87,12 @@ def testChainDictMaker():
     return result
 
 if __name__ == '__main__':
-    dicts = testChainDictMaker()
+
+    idict = None
+    if len(sys.argv) > 1:
+        idict = int(sys.argv[1])
+                    
+    dicts = testChainDictMaker(idict)
     for d in dicts:
         print('')
         print (d)
@@ -85,13 +113,13 @@ if __name__ == '__main__':
         chain_name = d[1]['chainName']
 
 
-        forest = nodeForestFromChainLabel(label, chain_name)
+        forest = nodesFromLabel(label)
 
         algToolFactory = FastReductionAlgToolFactory()
         for tree in forest:
             toolSetter=ConditionsToolSetterFastReduction(algToolFactory)
             
-            print (tree2tools(rootless_tree=tree,
+            print (tree2tools(tree,
                               toolSetter=toolSetter).dump())
         print ()
         
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/test_cases.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/test_cases.py
index 20946219824c9a31f36d3352998e4cda66e95100..37f605105408686adcf6014085e6cfe20acddc36 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/test_cases.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/test_cases.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 
 from __future__ import print_function
 
@@ -49,6 +49,8 @@ test_strings = [
         )
     )""",
 
+    
+    'simple([(38et, 0eta320, 100fltr:neta50, 90fltr:cphi100)])',
 ]
 
 
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/treeVisitors.py b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/treeVisitors.py
index a91db097e259da63274c673568464df9de136b9c..611bf7ec145a13cf828b4f803b90ac7ec1275e72 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/python/treeVisitors.py
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/python/treeVisitors.py
@@ -1,59 +1,58 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 from __future__ import print_function
 from __future__ import absolute_import
 
-from .constants import lchars
+from .constants import (lchars, seps, digits)
 
 import re
 from collections import defaultdict
 
+win_alphabet = lchars + seps
+window_re = re.compile(
+    r'^(?P<lo>\d*)(?P<attr>[%s]+)(?P<hi>\d*)' % win_alphabet)
+
 def defaultParameters(parameter, default=''):  # default if parameter unknown
-    defaults = {'etalo': '0',
-                'etahi': '320',
-                'petalo': '0',  # +ve eta
-                'petahi': '320',
-                'netalo': '320',  # -ve eta
-                'netahi': '0',
-                'etlo':   '0',
-                'ethi':   'inf',
-                'EtThreshold': '0.',
-                'eta_mins': '0.',
-                'eta_maxs': '3.2',
-                'asymmetricEtas': 0,  # exception: not a string
-                'djmasslo': '0.0',
-                'djmasshi': 'inf',
-                'djdetalo': '0.',
-                'djdetahi': 'inf',
-                'djdphilo': '0.',
-                'djdphihi': 'inf',
-                'qjmasslo': '0.0',
-                'qjmasshi': 'inf',
-                'momCutslo': '-inf',
-                'momCutshi': 'inf',
-                'smclo': '0',
-                'smchi': 'inf',                
-                'jvtlo': '0',
-                'jvthi': 'inf',
-                'htlo' : '1000.',
-                'hthi' : 'inf',
+    explicit_defaults = {
+        'etahi': '320',
+        'j1etahi': '320',
+        'j2etahi': '320',
+        'eta_maxs': '3.2',
+        'EtThreshold': '0.',
+        'eta_mins': '0.',
+        'asymmetricEtas': 0,  # exception: not a string
+        'momCutslo': '-inf',
     }
 
+    parameter = parameter.split(':')[-1]  # fltr:eta....
     if parameter.startswith('mom'):
         parameter = 'momCuts'
 
-    if parameter not in  defaults:
-        print ('defaultParameters: unknown parameter, returning default ',
-               parameter)
 
-    return defaults.get(parameter, default)
+    if parameter in explicit_defaults: return explicit_defaults[parameter]
+    
+    if not default:
+        if parameter.endswith('lo'):
+            default = '0'
+        elif parameter.endswith('hi'):
+            default = 'inf'
+        else:
+            raise RuntimeError(
+                'cannot find default for parameter ' + str(parameter))
 
+    print ('returning default value for ' + str(parameter) + ': ', default)
+    return default
 
 
 def scaleFactors(parameter):
-    defaults = {
+
+    defaults = {  # only positive values here. sign done elsewhere
         'eta': 0.01,
-        'neta': -0.01,
+        'neta': 0.01,
+        'ceta': 0.01,
         'peta': 0.01,
+        'nphi': 0.01,
+        'cphi': 0.01,
+        'pphi': 0.01,
         'et': 1000.,
         'ht': 1000.,
         'smc': 1000.,
@@ -64,8 +63,12 @@ def scaleFactors(parameter):
         'momCuts': 0.01,
         'jvt': 0.01,
     }
+
+    parameter = parameter.split(':')[-1]  # fltr:eta....
+
     if parameter.startswith('mom'):
         parameter = 'momCuts'
+
     return defaults[parameter]
         
 class Checker(object):
@@ -122,8 +125,6 @@ class ConditionsDictMaker(object):
 
     """
 
-    window_re = re.compile(
-        r'^(?P<lo>\d*)(?P<attr>[%s]+)(?P<hi>\d*)' % lchars)
 
 
     # key: substring from chain label. value: attribute of python
@@ -141,7 +142,7 @@ class ConditionsDictMaker(object):
            ['900mass,26dphi']
         """
         
-        alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789,'
+        alphabet = lchars + seps + digits
         pat = re.compile(r'(^\([%s]+\))'% alphabet )
         s = params
         m = True
@@ -151,6 +152,7 @@ class ConditionsDictMaker(object):
             if m is not None:
                 conditions.append(m.group(0))
                 s = s[len(conditions[-1]):]
+
         assert params == ''.join(conditions)
         conditions = [c[1:-1] for c in conditions]  # strip parens
         return conditions
@@ -184,8 +186,7 @@ class ConditionsDictMaker(object):
                 toks.remove(chainpartinds[-1][0])
 
             for t in toks:
-                m = self.window_re.match(t)
-                limits_dict = {}
+                m = window_re.match(t)
                 if m is None:
                     msgs.append('match failed for parameter %s' % t)
                     error = True
@@ -208,17 +209,23 @@ class ConditionsDictMaker(object):
                 #    must be set. The  attribute name is used directly,
                 #    the defaults do not require multiplying by a scale factor.
                 #   
-                
+
                 attr = group_dict['attr']  # attribute name in label
                 lo = group_dict['lo']  # string: low value or ''
                 hi = group_dict['hi']  # string high value or ''
 
                 def scale_limit(limit, sf):
 
-                    try:
-                        fl = float(limit)
-                    except TypeError: # limit = 'inf' or similar
-                        return limit
+                    # note on the value inf in python
+                    # the value of float('inf') is inf.
+                    # the value of float('-inf') is -inf.
+                    # the value of 10 * inf is inf.
+                    #
+                    # this is case independent:
+                    #
+                    # the value of float('iNf') is inf.
+
+                    fl = float(limit)
 
                     if fl != 0:  # avoid '-0'
                         fl = fl * sf
@@ -233,15 +240,37 @@ class ConditionsDictMaker(object):
                     
                 sf = scaleFactors(attr)
                 
+                # scale fctors and negative limts
+                # Scaling taking the string version of the limit found in
+                # the jet labelm and
+                #
+                # converts it to a float, and scales it. eg for eta,
+                # '320' -> '3.2'
+                # 
+                # Some limits are negative. eg the values assocated with
+                # neta are bot negative. Eg the  '320neta100' represent
+                # cut values of -3.2 and -1.0. 
+                #
+                # Currently (1/2021), dor attributes neta and nphi the 
+                # both the low and high limits are negative, while for
+                # ceta, cphi (c = crossing zero) the lower limits are
+                # negative.
+
+                neg_low = ('neta', 'nphi', 'ceta', 'cphi')
+                neg_high = ('neta', 'nphi')
+                
+                limits_dict = {}
                 if lo:
                     # find the python proxy class  name
-                    limits_dict['min'] = scale_limit(lo, sf)
-                        
+                    ssf = -sf if  attr in neg_low else sf
+                    limits_dict['min'] = scale_limit(lo, ssf)
+                    
                 if hi:
-                    limits_dict['max'] = scale_limit(hi, sf)
+                    ssf = -sf if  attr in neg_high else sf
+                    limits_dict['max'] = scale_limit(hi, ssf)
                         
                 cdict[attr] = limits_dict
-            
+
             result.append((cdict, mult)) # append dictionary and mult.
 
 
@@ -268,9 +297,6 @@ class TreeParameterExpander_simple(object):
     parameter strings look like '40et, 0eta320, nosmc'
     """
     
-    window_re = re.compile(
-        r'^(?P<lo>\d*)(?P<attr>[%s]+)(?P<hi>\d*)' % lchars)
-
     def __init__(self):
         self.msgs = []
 
@@ -286,47 +312,6 @@ class TreeParameterExpander_simple(object):
         return '%s: ' % self.__class__.__name__ + '\n'.join(self.msgs) 
 
 
-class TreeParameterExpander_agg(object):
-    """Convert parameter string into duction holding low, high window
-        cut vals, as for the  'simple' scenario. Then place conditions
-        not in the agg list in the filters dict. These conditions wil be used
-        to select the subset of the jet collection to be presented to the agg
-        conditions."""
-
-    agg_conditions = ('ht',)
-    
-    def __init__(self):
-        self.msgs = []
-
-    def mod(self, node):
-
-        simple_expander = TreeParameterExpander_simple()
-        simple_expander.mod(node)
-
-        # example conf_attrs:
-        # conf_attrs [3]:
-        # (defaultdict(<class 'dict'>,
-        #              {'ht': {'min': '1000000.0', 'max': 'inf'}}), 1)
-        # (defaultdict(<class 'dict'>,
-        #              {'et': {'min': '30000.0', 'max': 'inf'}}), 1)
-        # (defaultdict(<class 'dict'>,
-        #             {'eta': {'min': '0.0', 'max': '3.2'}}), 1)
-
-
-        for ca in node.conf_attrs:
-            assert len(ca) == 2  # (dict, mult)
-            assert len(ca[0]) == 1  # one entry in dict
-            ca_keys = ca[0].keys()
-            cond_name = list(ca_keys)[0]
-            if cond_name not in self.agg_conditions:
-                node.filter_conditions.append(ca)
-        for fc in node.filter_conditions:
-            node.conf_attrs.remove(fc)
-
-    def report(self):
-        return '%s: ' % self.__class__.__name__ + '\n'.join(self.msgs) 
-
-
 class TreeParameterExpander_dijet(object):
     """Convert parameter string into tuples holding low, high window
     cut vals. Specialistaion for the dijet scenario
@@ -337,10 +322,6 @@ class TreeParameterExpander_dijet(object):
     which will convert numeric values, and symbolic values such as 'inf'
     """
     
-    window_re = re.compile(
-        r'^(?P<lo>\d*)(?P<attr>[%s]+)(?P<hi>\d*)' % lchars)
-
-
     def __init__(self):
         self.msgs = []
 
@@ -382,6 +363,28 @@ class  TreeParameterExpander_all(object):
         return '%s: ' % self.__class__.__name__ + '\n'.join(self.msgs)
 
 
+class FilterConditionsMover:
+    def mod(self, node):
+        """Move dictionary used to construct the filter for a Condition from
+        node.conf_attrs to node.filter_dicts"""
+
+        filter_dicts = []
+        for ca in node.conf_attrs:
+            assert len(ca) == 2  # (dict, mult)
+            ca_keys = ca[0].keys()
+            n_filtkey = 0
+
+            for ca_key in ca_keys:
+                if 'fltr' in ca_key: n_filtkey += 1
+            assert n_filtkey == 0 or n_filtkey == len(ca_keys)
+
+            if n_filtkey > 0:
+                filter_dicts.append(ca)
+
+        [node.conf_attrs.remove(ca) for ca in filter_dicts]
+        node.filter_dicts = filter_dicts
+
+
 class TreeParameterExpander_null(object):
     """Does nothing except check the parameter string is empty"""
 
@@ -403,7 +406,7 @@ class TreeParameterExpander(object):
         'z': TreeParameterExpander_null,
         'root': TreeParameterExpander_null,
         'simple': TreeParameterExpander_simple,
-        'agg': TreeParameterExpander_agg,
+        'agg': TreeParameterExpander_simple,
         'dijet': TreeParameterExpander_dijet,
         'qjet': TreeParameterExpander_simple,
         'all': TreeParameterExpander_all,
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ConditionFilter.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ConditionFilter.cxx
index 04d82ce07394bcbf32635acd2e172bdd4b60b813..feac88b849455d8c4afd0a4f1ddb334de0ea9863 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ConditionFilter.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ConditionFilter.cxx
@@ -6,12 +6,14 @@
 
 #include "./ConditionFilter.h"
 
-ConditionFilter::ConditionFilter(ConditionPtrs& conditions):
+//ConditionFilter::ConditionFilter(ConditionPtrs& conditions):
+ConditionFilter::ConditionFilter(ConditionsMT& conditions):
   m_conditions(std::move(conditions)) {
 }
 
 struct FilterPred{
-  FilterPred(const ConditionPtr& cptr,
+
+  FilterPred(const ConditionMT& cptr,
 	     const std::unique_ptr<ITrigJetHypoInfoCollector>& collector):
     m_cptr(cptr), m_collector(collector) {
   }
@@ -21,7 +23,7 @@ struct FilterPred{
     return m_cptr->isSatisfied(hjv, m_collector);
   }
 
-  const ConditionPtr& m_cptr;
+  const ConditionMT& m_cptr;
   const std::unique_ptr<ITrigJetHypoInfoCollector>& m_collector;
 };
 
@@ -54,4 +56,13 @@ std::string ConditionFilter::toString() const {
   return ss.str();
 }
 
-	   
+HypoJetVector
+ConditionFilter::filter (const HypoJetVector& jv,
+			 const std::unique_ptr<ITrigJetHypoInfoCollector>& col) const {
+  return filter(jv.cbegin(), jv.cend(), col);
+}
+
+std::ostream& operator<<(std::ostream& os, const ConditionFilter& cf){
+  os << cf.toString();
+  return os;
+}
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ConditionFilter.h b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ConditionFilter.h
index 3911c3f5ff322a8c2d88407e9f8888d0e74fc20d..942c63e839caa24cc9fa15956fa7d144ac34320a 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ConditionFilter.h
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/ConditionFilter.h
@@ -5,23 +5,33 @@
 #ifndef TRIGHLTJETHYPO_CONDITIONFILTER_H
 #define TRIGHLTJETHYPO_CONDITIONFILTER_H
 
-#include "./CapacityCheckedConditionsDefs.h"
+#include "./ConditionsDefsMT.h"
+#include <ostream>
 
 class ConditionFilter {
  public:
-  ConditionFilter(ConditionPtrs&);
+
+  ConditionFilter(){};
+
+  ConditionFilter(ConditionsMT&);
 
   // find the subset of jets which satisfy a sequence of conditions
   HypoJetVector filter (const HypoJetCIter& b,
 			const HypoJetCIter& e,
 			const std::unique_ptr<ITrigJetHypoInfoCollector>&
 			) const;
+
+  HypoJetVector filter (const HypoJetVector&,
+			const std::unique_ptr<ITrigJetHypoInfoCollector>&
+			) const;
   
   std::string toString() const;  
  private:
-  ConditionPtrs m_conditions;
 
-  
+  ConditionsMT m_conditions;
 };
 
+std::ostream& operator<<(std::ostream&, const ConditionFilter&);
+
+
 #endif
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/PhiConditionMT.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/PhiConditionMT.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4c111d01fb7c36966c32fb0035379c4d0b2a2f3a
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/PhiConditionMT.cxx
@@ -0,0 +1,61 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+#
+#include "./PhiConditionMT.h"
+#include "./ITrigJetHypoInfoCollector.h"
+#include "TrigHLTJetHypo/TrigHLTJetHypoUtils/IJet.h"
+
+#include <sstream>
+#include <cmath>
+#include <TLorentzVector.h>
+
+PhiConditionMT::PhiConditionMT(double phiMin,
+			       double phiMax): m_min(phiMin), m_max(phiMax){
+}
+
+
+bool
+PhiConditionMT::isSatisfied(const pHypoJet& ip,
+			  const std::unique_ptr<ITrigJetHypoInfoCollector>& collector) const {
+  
+  auto phi = ip->phi();
+  bool pass = m_min <= phi and m_max > phi;
+  
+  if(collector){
+    const void* address = static_cast<const void*>(this);
+
+    std::stringstream ss0;
+    ss0 << "PhiConditionMT: (" << address << ") " 
+        << " phi[" << m_min << ", " << m_max << "]" 
+        << " pass: "  << std::boolalpha << pass << '\n';
+    
+    auto j_addr = static_cast<const void*>(ip.get());
+    std::stringstream ss1;
+    ss1 <<  "     jet : ("<< j_addr << ") phi " << phi << '\n';
+    
+    collector->collect(ss0.str(), ss1.str());
+    
+  }
+  return pass;
+}
+
+
+bool 
+PhiConditionMT::isSatisfied(const HypoJetVector& ips,
+			  const std::unique_ptr<ITrigJetHypoInfoCollector>& c) const {
+  auto result =  isSatisfied(ips[0], c);
+  return result;
+}
+
+
+std::string PhiConditionMT::toString() const {
+  std::stringstream ss;
+  ss << "PhiConditionMT (" << this << ") phiMin "
+     <<  m_min 
+     << " phiMax " 
+     << m_max 
+     <<'\n';
+
+  return ss.str();
+}
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/PhiConditionMT.h b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/PhiConditionMT.h
new file mode 100644
index 0000000000000000000000000000000000000000..d81e6d0793c834b3f1c0677602d4d22041f2f9aa
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/PhiConditionMT.h
@@ -0,0 +1,48 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGHLTJETHYPO_PHICONDITIONSIGNEDMT_H
+#define TRIGHLTJETHYPO_PHICONDITIONSIGNEDMT_H
+
+
+/********************************************************************
+ *
+ * NAME:     PhiConditionSignedMT.h
+ * PACKAGE:  Trigger/TrigHypothesis/TrigHLTJetHypo
+ *
+ * AUTHOR:   P. Sherwood
+ *********************************************************************/
+
+#include <string>
+#include "./IConditionMT.h"
+
+namespace HypoJet{
+  class IJet;
+}
+
+class ITrigJetHypoInfoCollector;
+
+class PhiConditionMT: public IConditionMT{
+ public:
+  PhiConditionMT(double phiMin,
+		       double phiMax);
+
+  bool isSatisfied(const HypoJetVector&,
+                   const std::unique_ptr<ITrigJetHypoInfoCollector>&) const override;
+
+  virtual unsigned int capacity() const override{return s_capacity;}
+  std::string toString() const override;
+  
+ private:
+
+  double m_min;
+  double m_max;
+  bool isSatisfied(const pHypoJet&,
+                   const std::unique_ptr<ITrigJetHypoInfoCollector>&) const;
+  
+  const static  unsigned int s_capacity{1};
+  
+};
+
+#endif
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigHLTJetHypoUtils/HypoJetDefs.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigHLTJetHypoUtils/HypoJetDefs.cxx
index e29de4a36ddad603c6d38637a144b1c63196a913..cd5330df2cf14a33fe7c3b9b1cc21c280ab3dd5b 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigHLTJetHypoUtils/HypoJetDefs.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigHLTJetHypoUtils/HypoJetDefs.cxx
@@ -12,7 +12,8 @@ std::ostream& operator << (std::ostream& out, const HypoJetVector& hjv) {
     out << static_cast<const void*>(j.get())
 	<< " e " << j->e()
 	<< " et " << j->et()
-	<< " eta " << j->eta() << '\n';
+	<< " eta " << j->eta()
+	<< " phi " << j->phi() << '\n';
   }
   return out;
 }
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetConditionConfig_phi.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetConditionConfig_phi.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..be9e7474b34be62f6b37219828f73dccff450ca4
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetConditionConfig_phi.cxx
@@ -0,0 +1,81 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TrigJetConditionConfig_phi.h"
+
+#include "GaudiKernel/StatusCode.h"
+#include "./PhiConditionMT.h"
+#include <cmath>
+
+TrigJetConditionConfig_phi::TrigJetConditionConfig_phi(const std::string& type,
+						       const std::string& name,
+						       const IInterface* parent) :
+  base_class(type, name, parent){
+
+}
+
+StatusCode TrigJetConditionConfig_phi::initialize() {
+
+  auto convert = [](const std::string& s) {
+    
+    if (s == "PI"){
+      return M_PI;
+    } else if (s == "-PI") {
+      return -M_PI;      
+    }
+    return std::stod(s);
+  };
+
+  try {
+    m_min = convert(m_strmin);
+  } catch (...) {
+    ATH_MSG_ERROR ("Cannot convert " + m_strmin + " to double");
+    return StatusCode::FAILURE;
+  }
+  
+  try {
+    m_max = convert(m_strmax);
+  } catch (...) {
+    ATH_MSG_ERROR ("Cannot convert " +  m_strmax + " to double");
+    return StatusCode::FAILURE;
+  }
+    
+  CHECK(checkVals());
+  return StatusCode::SUCCESS;
+}
+
+
+ConditionMT TrigJetConditionConfig_phi::getCondition() const {
+  return std::make_unique<PhiConditionMT>(m_min, m_max);
+}
+
+
+StatusCode TrigJetConditionConfig_phi::checkVals() const {
+
+  if (m_min > m_max){
+    ATH_MSG_ERROR(" min phi >  max phi");
+    return StatusCode::FAILURE;
+  }
+
+  if (m_min < -M_PI) {
+    ATH_MSG_ERROR(" min phi " << m_min << " out of range");
+    return StatusCode::FAILURE;
+  }
+
+  if (m_max > M_PI) {
+    ATH_MSG_ERROR(" max phi " << m_max << " out of range");
+    return StatusCode::FAILURE;
+  }
+  
+  return StatusCode::SUCCESS;
+}
+
+
+bool TrigJetConditionConfig_phi::addToCapacity(std::size_t) {
+  return false;
+}
+
+std::size_t TrigJetConditionConfig_phi::capacity() const {
+  return getCondition()->capacity();
+}
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetConditionConfig_phi.h b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetConditionConfig_phi.h
new file mode 100644
index 0000000000000000000000000000000000000000..7aeebf68afa8f0e2e7ff3397080cb22500b0179a
--- /dev/null
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetConditionConfig_phi.h
@@ -0,0 +1,47 @@
+/*
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TRIGJETCONDITIONCONFIG_PHI_H
+#define TRIGJETCONDITIONCONFIG_PHI_H
+
+#include "ITrigJetConditionConfig.h"
+#include "./ConditionsDefsMT.h"
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "./ConditionsDefsMT.h"
+#include "./ArgStrToDouble.h"
+
+class TrigJetConditionConfig_phi:
+public extends<AthAlgTool, ITrigJetConditionConfig> {
+
+ public:
+  
+  TrigJetConditionConfig_phi(const std::string& type,
+			     const std::string& name,
+			     const IInterface* parent);
+  
+  virtual StatusCode initialize() override;
+  virtual ConditionMT getCondition() const override;
+
+  virtual bool addToCapacity(std::size_t) override;
+  virtual std::size_t capacity() const override;
+  
+ private:
+
+  // phi min and max are strings. "PI" and "-PI" , as well
+  // as any intermediate value that converts to a double value
+  // in the range (-pi, pi), eg "1.0", are accepted.
+  
+  Gaudi::Property<std::string>
+    m_strmin{this, "min", {}, "min for phi region"};
+  
+  Gaudi::Property<std::string>
+    m_strmax{this, "max", {}, "max for phi region"};
+
+  double m_min{0.};
+  double m_max{0.};
+  
+  StatusCode checkVals()  const;
+ 
+};
+#endif
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolConfig_fastreduction.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolConfig_fastreduction.cxx
index 755ac22a197680c0d9a7da4c66442179f07e2d5b..9fa71359e6daf24fd3426da8670b67c4b1b2f1ee 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolConfig_fastreduction.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolConfig_fastreduction.cxx
@@ -15,6 +15,7 @@
 #include "./CapacityCheckedCondition.h"
 #include "./FastReductionMatcher.h"
 #include "./Tree.h"
+#include "./ConditionsDefsMT.h"
 
 #include "TrigCompositeUtils/TrigCompositeUtils.h"
 
@@ -89,7 +90,8 @@ TrigJetHypoToolConfig_fastreduction::getConditionFilters() const {
   auto filters = std::vector<std::unique_ptr<ConditionFilter>>();
   
   for(const auto& cm : m_filtConditionMakers){
-    ConditionPtrs filterConditions;  // will contain a single Condition
+
+    ConditionsMT filterConditions;  // will contain a single Condition
     filterConditions.push_back(cm->getCapacityCheckedCondition());
     auto cf = std::make_unique<ConditionFilter>(filterConditions);
     filters.push_back(std::move(cf));
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolConfig_fastreduction.h b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolConfig_fastreduction.h
index de228b5c9df656672ef850083c18f59d5209ba85..51511a4fc23545480b42c7c436ee6bdecd4b7658 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolConfig_fastreduction.h
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolConfig_fastreduction.h
@@ -62,6 +62,7 @@ public extends<AthAlgTool, ITrigJetHypoToolNoGrouperConfig> {
   ToolHandleArray<ITrigJetCapacityCheckedConditionConfig> m_filtConditionMakers{
     this, "filtConditionsMakers", {},
     "hypo tree Condition builder AlgTools for Condition filters"};
+
   
   Gaudi::Property<std::vector<std::size_t>> m_treeVec{
     this, "treeVector", {}, "integer sequence representation of jet hypo tree"};
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.cxx
index cd5f8306032d7f574e910cd31a7137c8749bd5ff..a21699d8d8ccd408e461e8c2bb6f1e72ceece81b 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.cxx
@@ -6,10 +6,11 @@
 #include "./ITrigJetHypoInfoCollector.h"
 #include "./xAODJetCollector.h"
 #include "./JetTrigTimer.h"
-#include "TrigHLTJetHypo/TrigHLTJetHypoUtils/CleanerFactory.h"
 #include "TrigHLTJetHypo/TrigHLTJetHypoUtils/xAODJetAsIJet.h"  // TLorentzVec
 #include "./nodeIDPrinter.h"
 #include "./DebugInfoCollector.h"
+#include "./ConditionInverter.h"
+#include "./CompoundConditionMT.h"
 #include <algorithm>
 #include <sstream>
 
@@ -21,6 +22,7 @@ TrigJetHypoToolHelperNoGrouper::TrigJetHypoToolHelperNoGrouper(const std::string
 
 StatusCode TrigJetHypoToolHelperNoGrouper::initialize() {
 
+  CHECK(makePrefilter());
   for (const auto& config : m_configs) {
     m_matchers.push_back(config->getMatcher());
   }
@@ -43,38 +45,34 @@ TrigJetHypoToolHelperNoGrouper::collectData(const std::string& exetime,
 
 
 bool
-TrigJetHypoToolHelperNoGrouper::pass(HypoJetVector& jets,
+TrigJetHypoToolHelperNoGrouper::pass(HypoJetVector& jetsIn,
 				     xAODJetCollector& jetCollector,
 				     const std::unique_ptr<ITrigJetHypoInfoCollector>& collector) const {
   
   if(collector){
     std::stringstream ss;
-    ss <<  "No of jets " + std::to_string(jets.size()) + '\n';
-    ss << jets; 
+    ss <<  "No of jets " + std::to_string(jetsIn.size()) + '\n';
+    ss << jetsIn; 
     collector->collect(name(), ss.str());
   }
 
   JetTrigTimer timer;
   timer.start();
   
-  if(jets.empty()){   
+  if(jetsIn.empty()){   
     timer.stop();
     bool pass = false;
     collectData(timer.readAndReset(), collector, pass);
     return pass;
   }
 
-  // jet cleaning
+  // prefiltering.
+
+  auto jets = m_prefilter.filter(jetsIn, collector);
+
   HypoJetIter jets_begin = jets.begin(); 
   HypoJetIter jets_end = jets.end(); 
-  for(auto cleaner: m_cleaners){
-    jets_end = std::partition(jets_begin,
-			      jets_end,
-			      [cleaner](const pHypoJet& j){
-				return cleaner->select(j);}
-			      );
-  }
-
+ 
   // see if matchers pass. Each matcher conatains a FastReducer tree.
   // if  > matcher, this means the conditions of different trees may
   // share jets.
@@ -107,12 +105,7 @@ std::string TrigJetHypoToolHelperNoGrouper::toString() const {
   std::stringstream ss;
   ss << name();
   
-  ss << "Cleaners:\n No of cleaners: "  << m_cleaners.size() << '\n';
-  
-  for(auto cleaner : m_cleaners) {
-    ss << cleaner->toString() 
-       << '\n';
-  }
+  ss << "prefilter:\n " << m_prefilter << '\n';
   
   ss << "\nMatchers [" << m_matchers.size() << "]:\n\n";
   unsigned int imatcher{0};
@@ -136,6 +129,40 @@ std::size_t TrigJetHypoToolHelperNoGrouper::requiresNJets() const {
   return m_configs[0]->requiresNJets();
 }
 
+StatusCode TrigJetHypoToolHelperNoGrouper::makePrefilter(){
+  /* set up the prefilter by looping over the precondition 
+     Condition maker AlgTools to obtain the elemental Conditions,
+     place these in a single compound Condition, and warp this in a
+     CondtionInverter. This is passed to the ConditionFilter object.
+  */
 
-
+  // if no conditions the filter will apply n inverter to an empty
+  // Compound Condition, which will kill all events.
+  if (m_prefilterConditionMakers.empty()) {return StatusCode::SUCCESS;}
+  
+  auto makeElementalFilterCondition = [](auto& conditionMaker)->ConditionMT {
+    return conditionMaker->getCapacityCheckedCondition();
+  };
+
+  // fill a container with pointers to an elemental condition
+  // note: ICapacityCheckedCondition derives from IConditionMT
+  ConditionsMT prefilterConditions{};
+  std::transform(m_prefilterConditionMakers.begin(),
+		 m_prefilterConditionMakers.end(),
+		 std::back_inserter(prefilterConditions),
+		 makeElementalFilterCondition);
+
+  // create a compound condition pointer.
+  auto cc = std::make_unique<CompoundConditionMT>(prefilterConditions);
+
+  // create a conditonsMT vec, add the inversuion of the compound condition
+  // to it. With the inversion, the invert compound condition acts as veto
+  ConditionsMT condVec;
+  condVec.push_back(std::make_unique<ConditionInverterMT>(std::move(cc)));
+  
+  // create an filter from the vector containing the inverted condition.
+  m_prefilter = ConditionFilter{condVec};
+  
+  return StatusCode::SUCCESS;
+}
 
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.h b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.h
index c398770e741ab74022aa9f21b63b895bcbc52fe0..e93490bc270f6182119a1fa1c7f02c998fc6a4bb 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.h
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolHelperNoGrouper.h
@@ -23,9 +23,11 @@
 #include "TrigHLTJetHypo/TrigHLTJetHypoUtils/ICleanerTool.h"
 #include "./IJetsMatcherMT.h"
 #include "./ConditionsDefsMT.h"
+#include "./ConditionFilter.h"
 
 #include "TrigHLTJetHypo/ITrigJetHypoToolHelperMT.h"
 #include "ITrigJetHypoToolNoGrouperConfig.h"
+#include "./ITrigJetCapacityCheckedConditionConfig.h"
 
 class ITrigJetHypoInfoCollector;
 class xAODJetCollector;
@@ -56,18 +58,22 @@ public extends<AthAlgTool, ITrigJetHypoToolHelperMT> {
   // Object that matches jet groups with Conditions
   std::vector<std::unique_ptr<IJetsMatcherMT>> m_matchers;
 
-  // Bridge objects to ICleaner predicate function objects to allow polymorphic
-  // pointers to be used with the STL (pass by value).
-  ToolHandleArray<ICleanerTool> m_cleaners;
-
   ///////////////////////////////
 
  // Used to generate helper objects foe TrigHLTJetHelper
  // from user supplied values
  ToolHandleArray<ITrigJetHypoToolNoGrouperConfig> m_configs {
    this, "HypoConfigurers", {},
-   "Configurers to set up TrigJetHypoHelperNoGrouper"}; 
+   "Configurers to set up TrigJetHypoHelperNoGrouper"};
+
+  ToolHandleArray<ITrigJetCapacityCheckedConditionConfig>
+  m_prefilterConditionMakers{this, "prefiltConditionMakers", {},
+    "hypo tree Condition builder AlgTools for hypo pre-filtering"};
+
+  // object that copies selected incomming jets into a new vector.
+  ConditionFilter m_prefilter{}; 
 
+  
   Gaudi::Property<bool>
   m_debug {this, "debug", false, "instantantiate helpers with this debug flag"};
   
@@ -76,7 +82,9 @@ public extends<AthAlgTool, ITrigJetHypoToolHelperMT> {
                   const std::unique_ptr<ITrigJetHypoInfoCollector>&,
                   const std::optional<bool>& pass) const;
 
- virtual std::string toString() const override;
+  StatusCode makePrefilter();
+  
+  virtual std::string toString() const override;
 };
 
 #endif
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolMT.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolMT.cxx
index 3f1141a4035749d5517fefe64a0c84ef5742ac0a..9b6ea318e3b69b331318c5a4b081952f61c89458 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolMT.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/TrigJetHypoToolMT.cxx
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 */
 
 // ********************************************************************
@@ -75,9 +75,13 @@ TrigJetHypoToolMT::decide(const xAOD::JetContainer* jets,
   }
 
   std::unique_ptr<ITrigJetHypoInfoCollector> infocollector(nullptr);
+  std::unique_ptr<ITrigJetHypoInfoCollector> jetdumper(nullptr);
   if(m_visitDebug){
     auto collectorName = name() + "_" + std::to_string(m_eventSN->getSN());
     infocollector.reset(new  DebugInfoCollector(collectorName));
+    auto jetdumperName =
+      name()+"_passingjets_" + std::to_string(m_eventSN->getSN());
+    jetdumper.reset(new  DebugInfoCollector(jetdumperName));
   }
   
    
@@ -140,7 +144,12 @@ TrigJetHypoToolMT::decide(const xAOD::JetContainer* jets,
   if (infocollector){
     infocollector->collect("TrigJetHypoToolMT", msg);
     infocollector->write();
+
+    std::stringstream ss;
+    ss << jetCollector.hypoJets();
+    jetdumper->collect("passed", ss.str());
   }
+  
   return StatusCode::SUCCESS;
 }
 
diff --git a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/components/TrigHLTJetHypo_entries.cxx b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/components/TrigHLTJetHypo_entries.cxx
index 4974748f7260026bcd956dea9e4ad01c8ba1b568..b16c516d42fa500a9a38dac6e1423f6f5cc3fd27 100644
--- a/Trigger/TrigHypothesis/TrigHLTJetHypo/src/components/TrigHLTJetHypo_entries.cxx
+++ b/Trigger/TrigHypothesis/TrigHLTJetHypo/src/components/TrigHLTJetHypo_entries.cxx
@@ -18,6 +18,7 @@
 //
 #include "../TrigJetConditionConfig_abs_eta.h"
 #include "../TrigJetConditionConfig_signed_eta.h"
+#include "../TrigJetConditionConfig_phi.h"
 #include "../TrigJetConditionConfig_et.h"
 #include "../TrigJetConditionConfig_ht.h"
 #include "../TrigJetConditionConfig_htfr.h"
@@ -62,6 +63,7 @@ DECLARE_COMPONENT(TrigHLTJetHypo_TLA)
 DECLARE_COMPONENT(TrigHLTJetHypo_EtaEt)
 
 
+DECLARE_COMPONENT(TrigJetConditionConfig_phi)
 DECLARE_COMPONENT(TrigJetConditionConfig_signed_eta)
 DECLARE_COMPONENT(TrigJetConditionConfig_abs_eta)
 DECLARE_COMPONENT(TrigJetConditionConfig_et)
diff --git a/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref b/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref
index 661057efcdc06389c064a87177d42b857d1dc358..1cc1febd15fa9c63b00ac37a2f6c6951fb8ee4a8 100644
--- a/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref
+++ b/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref
@@ -2144,6 +2144,12 @@ HLT_j460_a10t_lcw_nojcalib_35smcINF_L1J100:
   eventCount: 0
 HLT_j460_a10t_lcw_nojcalib_L1J100:
   eventCount: 0
+HLT_j60_j0_fbdjshared_L1J20:
+  eventCount: 7
+  stepCounts:
+    0: 7
+  stepFeatures:
+    0: 177
 HLT_j80_0eta240_2j60_320eta490_j0_dijetSEP80j1etSEP0j1eta240SEP80j2etSEP0j2eta240SEP700djmass_L1J20:
   eventCount: 0
 HLT_j80_L1J15:
@@ -2164,6 +2170,14 @@ HLT_j85_ftf_L1J20:
   stepFeatures:
     0: 19
     1: 22
+HLT_j85_ftf_pfltrSEP300ceta210SEP300nphi10_L1J20:
+  eventCount: 10
+  stepCounts:
+    0: 19
+    1: 10
+  stepFeatures:
+    0: 19
+    1: 14
 HLT_j85_pf_ftf_L1J20:
   eventCount: 13
   stepCounts:
diff --git a/Trigger/TrigValidation/TriggerTest/share/ref_data_v1Dev_build.ref b/Trigger/TrigValidation/TriggerTest/share/ref_data_v1Dev_build.ref
index 00ed56d809329fffe706bc505f3f5ee36823fe6c..628473f53c78de4f10d4dcfd1bb17547dc2847ff 100644
--- a/Trigger/TrigValidation/TriggerTest/share/ref_data_v1Dev_build.ref
+++ b/Trigger/TrigValidation/TriggerTest/share/ref_data_v1Dev_build.ref
@@ -1070,6 +1070,12 @@ HLT_j460_a10t_lcw_nojcalib_35smcINF_L1J100:
   eventCount: 0
 HLT_j460_a10t_lcw_nojcalib_L1J100:
   eventCount: 0
+HLT_j60_j0_fbdjshared_L1J20:
+  eventCount: 1
+  stepCounts:
+    0: 1
+  stepFeatures:
+    0: 9
 HLT_j80_0eta240_2j60_320eta490_j0_dijetSEP80j1etSEP0j1eta240SEP80j2etSEP0j2eta240SEP700djmass_L1J20:
   eventCount: 0
 HLT_j80_L1J15:
@@ -1094,6 +1100,14 @@ HLT_j85_ftf_L1J20:
   stepFeatures:
     0: 5
     1: 3
+HLT_j85_ftf_pfltrSEP300ceta210SEP300nphi10_L1J20:
+  eventCount: 3
+  stepCounts:
+    0: 5
+    1: 3
+  stepFeatures:
+    0: 5
+    1: 3
 HLT_j85_pf_ftf_L1J20:
   eventCount: 3
   stepCounts:
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py
index a85843e8c207bdaef7ca1473a6a82186df900c6f..8d380d8f67a6617e799aeb8575702bde52b47252 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py
@@ -283,10 +283,15 @@ def setupMenu():
 
         ChainProp(name='HLT_j0_fbdjnosharedSEP10etSEP20etSEP34massSEP50fbet_L1J20', groups=SingleJetGroup),
         ChainProp(name='HLT_j0_fbdjshared_L1J20', groups=SingleJetGroup),
+        ChainProp(name='HLT_j60_j0_fbdjshared_L1J20', l1SeedThresholds=['FSNOSEED']*2, groups=MultiJetGroup),
 
         ChainProp(name='HLT_j0_aggSEP1000htSEP30etSEP0eta320_L1J20', groups=SingleJetGroup),
         ChainProp(name='HLT_j0_aggSEP500htSEP30etSEP0eta320_L1J20', groups=SingleJetGroup),
 
+
+        ChainProp(name='HLT_j85_ftf_pfltrSEP300ceta210SEP300nphi10_L1J20',
+                  groups=SingleJetGroup),
+
         ChainProp(name='HLT_j40_j0_aggSEP50htSEP10etSEP0eta320_L1J20',
                   l1SeedThresholds=['FSNOSEED']*2, groups=MultiJetGroup),
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py
index 6167871ac88943742bddbcde9ffb4d35733cbebc..457679351526ff1f580464f3f5cd300ae5187ff5 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 from AthenaCommon.Logging import logging
 log = logging.getLogger( __name__ )
 log.info("Importing %s",__name__)
@@ -143,6 +143,7 @@ JetChainParts = {
                       'aggSEP100htSEP10etSEP0eta320',
                       'aggSEP50htSEP10etSEP0eta320',
                       ],
+
     # Simple hypo configuration. Single property cuts defined as MINvarMAX
     'etaRange'      :
       ['0eta320', '320eta490', '0eta240', '0eta290'],
@@ -150,8 +151,8 @@ JetChainParts = {
       ['010jvt', '011jvt', '015jvt', '020jvt', '050jvt', '059jvt'],
     'momCuts'       : # Generic moment cut on single jets
       ['050momemfrac100','momhecfrac010','050momemfrac100SEPmomhecfrac010'],
-    'cleaning'      : # Jet cleaning per jet (currently unused)
-      ['noCleaning',],
+    'prefilters'      : # Pre-hypo jet selectors (including cleaning)
+    ['loose', 'pfltrSEP300ceta210SEP300nphi10'], 
     'smc'           : # "Single mass condition" -- rename?
       ['30smcINF', '35smcINF', '40smcINF', '50smcINF', '60smcINF', 'nosmc'],
     # Setup for alternative data stream readout
@@ -194,7 +195,7 @@ JetChainParts_Default = {
     'etaRange'      : '0eta320',
     'jvt'           : '',
     'momCuts'       : '',
-    'cleaning'      : 'noCleaning',
+    'prefilters'    : [],
     'hypoScenario'  : 'simple',
     'smc'           : 'nosmc',
     #