From b0174f79d03b95ef917470ade0b243c20e94b8eb Mon Sep 17 00:00:00 2001
From: Stewart Martin-Haugh <smh@cern.ch>
Date: Tue, 11 Feb 2020 18:54:56 +0100
Subject: [PATCH] Python 3 migration and fix for HLTMenuConfig

---
 .../python/HLTMenuConfig/Menu/HLTCFConfig.py  |  23 +-
 .../HLTMenuConfig/Menu/HLTCFConfig.py.bak     | 580 ++++++++++++++++++
 2 files changed, 594 insertions(+), 9 deletions(-)
 create mode 100644 Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig.py.bak

diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig.py
index a7b7ce91b07..6b15307c1e8 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig.py
@@ -25,12 +25,16 @@
 
 """
 
+from builtins import zip
+from builtins import str
+from builtins import map
+from builtins import range
 # Classes to configure the CF graph, via Nodes
 from AthenaCommon.CFElements import parOR, seqAND, seqOR
 from AthenaCommon.AlgSequence import dumpSequence
 from TriggerMenuMT.HLTMenuConfig.Menu.HLTCFDot import  stepCF_DataFlow_to_dot, stepCF_ControlFlow_to_dot, all_DataFlow_to_dot, create_dot
 from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponentsNaming import CFNaming
-#from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import ChainStep
+from DecisionHandling.DecisionHandlingConf import RoRSeqFilter
 
 
 from AthenaCommon.Logging import logging
@@ -67,10 +71,11 @@ def createStepFilterNode(name, seq_list, dump=False):
     for seq in seq_list:
         filterAlg = seq.filter.Alg
         log.info("createStepFilterNode: Add  %s to filter node %s", filterAlg.name(), name)
-        filter_list.append(filterAlg)
+        if not filterAlg in filter_list:
+            filter_list.append(filterAlg)
 
 
-    stepCF = parOR(name + CFNaming.FILTER_POSTFIX, subs=sorted(set(filter_list)))
+    stepCF = parOR(name + CFNaming.FILTER_POSTFIX, subs=filter_list)
 
     if dump:
         dumpSequence (stepCF, indent=0)
@@ -213,7 +218,7 @@ def matrixDisplayOld( allCFSeq ):
                 else:
                     return s.step.sequences[0].hypo.tools
             else:
-                return s.step.combo.getChains().keys()
+                return list(s.step.combo.getChains().keys())
         return []
    
 
@@ -221,7 +226,7 @@ def matrixDisplayOld( allCFSeq ):
 
     def __nextSteps( index, stepName ):
         nextStepName = "Step%s_"%index + "_".join(stepName.split("_")[1:])
-        for sname, seq in mx[index].iteritems():
+        for sname, seq in mx[index].items():
             if sname == nextStepName:
                 return sname.ljust( longestName ) + __nextSteps( index + 1, nextStepName )
         return ""
@@ -229,7 +234,7 @@ def matrixDisplayOld( allCFSeq ):
     log.debug("" )
     log.debug("chains^ vs steps ->")
     log.debug( "="*90 )
-    for sname, seq in mx[1].iteritems():
+    for sname, seq in mx[1].items():
         guessChainName = '_'.join( sname.split( "_" )[1:] )
         log.debug( " Reco chain: %s: %s", guessChainName.rjust(longestName),  __nextSteps( 1, sname ) )
         log.debug( " %s", " ".join( __getHyposOfStep( seq ) ) )
@@ -250,7 +255,7 @@ def matrixDisplay( allCFSeq ):
                 else:
                     return step.sequences[0].hypo.tools
             else:
-                return step.combo.getChains().keys()
+                return list(step.combo.getChains().keys())
         return []
  
    
@@ -267,13 +272,13 @@ def matrixDisplay( allCFSeq ):
 
     # sort dictionary by fist key=step
     from collections import  OrderedDict
-    sorted_mx = OrderedDict(sorted( mx.items(), key= lambda k: k[0]))
+    sorted_mx = OrderedDict(sorted( list(mx.items()), key= lambda k: k[0]))
 
     log.info( "" )
     log.info( "="*90 )
     log.info( "Cumulative Summary of steps")
     log.info( "="*90 )
-    for (step, seq), chains in sorted_mx.items():
+    for (step, seq), chains in list(sorted_mx.items()):
         log.info( "(step, sequence)  ==> (%d, %s) is in chains: ",  step, seq)
         for chain in chains:
             log.info( "              %s",chain)
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig.py.bak b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig.py.bak
new file mode 100644
index 00000000000..a7b7ce91b07
--- /dev/null
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig.py.bak
@@ -0,0 +1,580 @@
+# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+
+"""
+    ------ Documentation on HLT Tree creation -----
+
+++ Filter creation strategy
+
+++ Connections between InputMaker/HypoAlg/Filter
+
+++ Seeds
+
+++ Combined chain strategy
+
+- The combined chains use duplicates of the single-object-HypoAlg, called HypoAlgName_for_stepName.
+  These duplicates are connected to a dedicated ComboHypoAlg (added by the framework), able to count object multiplicity
+     -- This is needed for two reasons:
+           -- the HypoAlg is designed to have only one input TC (that is already for the single object)
+           -- otherwise the HypoAlg would be equipped with differnt HypoTools with the same name (see for example e3_e8)
+     -- If the combined chain is symmetric (with multiplicity >1), the Hypo is duplicated only once,
+        equipped with a HypoTool configured as single object and followed by one ComboHypoAlg
+
+
+
+
+
+"""
+
+# Classes to configure the CF graph, via Nodes
+from AthenaCommon.CFElements import parOR, seqAND, seqOR
+from AthenaCommon.AlgSequence import dumpSequence
+from TriggerMenuMT.HLTMenuConfig.Menu.HLTCFDot import  stepCF_DataFlow_to_dot, stepCF_ControlFlow_to_dot, all_DataFlow_to_dot, create_dot
+from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponentsNaming import CFNaming
+#from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import ChainStep
+
+
+from AthenaCommon.Logging import logging
+log = logging.getLogger( __name__ )
+
+#### Here functions to create the CF tree from CF configuration objects
+def makeSummary(name, flatDecisions):
+    """ Returns a TriggerSummaryAlg connected to given decisions"""
+    from DecisionHandling.DecisionHandlingConfig import TriggerSummaryAlg    
+    summary = TriggerSummaryAlg( CFNaming.stepSummaryName(name) )
+    summary.InputDecision = "L1DecoderSummary"
+    summary.FinalDecisions = list(set(flatDecisions))
+    return summary
+
+
+def createStepRecoNode(name, seq_list, dump=False):
+    """ elementary HLT reco step, contianing all sequences of the step """
+
+    log.debug("Create reco step %s with %d sequences", name, len(seq_list))
+    stepCF = parOR(name + CFNaming.RECO_POSTFIX)
+    for seq in seq_list:
+        stepCF += createCFTree(seq)
+
+    if dump:
+        dumpSequence (stepCF, indent=0)
+    return stepCF
+
+
+def createStepFilterNode(name, seq_list, dump=False):
+    """ elementary HLT filter step: OR node containing all Filters of the sequences. The node gates execution of next reco step """
+
+    log.debug("Create filter step %s with %d filters", name, len(seq_list))
+    filter_list=[]
+    for seq in seq_list:
+        filterAlg = seq.filter.Alg
+        log.info("createStepFilterNode: Add  %s to filter node %s", filterAlg.name(), name)
+        filter_list.append(filterAlg)
+
+
+    stepCF = parOR(name + CFNaming.FILTER_POSTFIX, subs=sorted(set(filter_list)))
+
+    if dump:
+        dumpSequence (stepCF, indent=0)
+    return stepCF
+
+
+def createCFTree(CFseq):
+    """ Creates AthSequencer nodes with sequences attached """
+
+    log.debug(" *** Create CF Tree for CFSequence %s", CFseq.step.name)
+    filterAlg = CFseq.filter.Alg
+
+    #empty step:
+    if len(CFseq.step.sequences)==0:
+        seqAndWithFilter = seqAND(CFseq.step.name, [filterAlg])
+        return seqAndWithFilter
+
+    stepReco = parOR(CFseq.step.name + CFNaming.RECO_POSTFIX)  # all reco algoritms from al lthe sequences in a parallel sequence
+    seqAndView = seqAND(CFseq.step.name + CFNaming.VIEW_POSTFIX, [stepReco])  # include in seq:And to run in views: add here the Hypo
+    seqAndWithFilter = seqAND(CFseq.step.name, [filterAlg, seqAndView])  # add to the main step+filter
+
+    already_connected = []
+    for menuseq in CFseq.step.sequences:
+        ath_sequence = menuseq.sequence.Alg
+        name = ath_sequence.name()
+        if name in already_connected:
+            log.debug("AthSequencer %s already in the Tree, not added again",name)
+            continue
+        else:
+            already_connected.append(name)
+            stepReco += ath_sequence
+        if type(menuseq.hypo) is list:
+           for hp in menuseq.hypo:
+              seqAndView += hp.Alg
+        else:
+           seqAndView += menuseq.hypo.Alg
+
+    if CFseq.step.isCombo:
+        seqAndView += CFseq.step.combo.Alg
+
+    return seqAndWithFilter
+
+
+
+#######################################
+## CORE of Decision Handling
+#######################################
+
+def makeHLTTree(newJO=False, triggerConfigHLT = None):
+    """ creates the full HLT tree"""
+
+    # Check if triggerConfigHLT exits, if yes, derive information from this
+    # this will be in use once TrigUpgrade test has migrated to TriggerMenuMT completely
+
+    # get topSequnece
+    from AthenaCommon.AlgSequence import AlgSequence
+    topSequence = AlgSequence()
+
+    #add the L1Upcacking
+    #    TopHLTRootSeq += L1UnpackingSeq
+
+    # connect to L1Decoder
+    l1decoder = [ d for d in topSequence.getChildren() if d.getType() == "L1Decoder" ]
+    if len(l1decoder)  != 1 :
+        raise RuntimeError(" Can't find 1 instance of L1Decoder in topSequence, instead found this in topSequence "+str(topSequence.getChildren()) )
+
+    # take L1Decoder out of topSeq
+    topSequence.remove( l1decoder )
+
+    # main HLT top sequence
+    hltTop = seqOR("HLTTop")
+
+    # put L1Decoder here
+    hltTop += l1decoder
+
+    # add the HLT steps Node
+    steps = seqAND("HLTAllSteps")
+    hltTop +=  steps
+
+    # make DF and CF tree from chains
+    finalDecisions = decisionTreeFromChains(steps, triggerConfigHLT.configsList(), triggerConfigHLT.dictsList(), newJO)
+
+    flatDecisions=[]
+    for step in finalDecisions:
+        flatDecisions.extend (step)
+
+    summary= makeSummary("TriggerSummaryFinal", flatDecisions)
+    hltTop += summary
+
+    # TODO - check we are not running things twice. Once here and once in TriggerConfig.py
+
+    from TriggerJobOpts.TriggerConfig import collectHypos, collectFilters, collectViewMakers, collectDecisionObjects,\
+        triggerMonitoringCfg, triggerSummaryCfg, triggerMergeViewsAndAddMissingEDMCfg, collectHypoDecisionObjects
+    from AthenaConfiguration.AllConfigFlags import ConfigFlags
+    from AthenaCommon.Configurable import Configurable
+    Configurable.configurableRun3Behavior+=1
+
+    hypos = collectHypos(steps)
+    filters = collectFilters(steps)
+    viewMakers = collectViewMakers(steps)
+    summaryAcc, summaryAlg = triggerSummaryCfg( ConfigFlags, hypos )
+    hltTop += summaryAlg
+    summaryAcc.appendToGlobals()
+    decObj = collectDecisionObjects( hypos, filters, l1decoder[0], summaryAlg )
+    decObjHypoOut = collectHypoDecisionObjects(hypos, inputs=False, outputs=True)
+
+    monAcc, monAlg = triggerMonitoringCfg( ConfigFlags, hypos, filters, l1decoder[0] )
+    monAcc.appendToGlobals()
+    hltTop += monAlg
+
+    # this is a shotcut for now, we always assume we may be writing ESD & AOD outputs, so all gaps will be filled
+    edmAlg = triggerMergeViewsAndAddMissingEDMCfg(['AOD', 'ESD'], hypos, viewMakers, decObj, decObjHypoOut)
+    hltTop += edmAlg
+
+    Configurable.configurableRun3Behavior-=1
+
+    topSequence += hltTop
+
+    # Test the configuration
+    from TriggerMenuMT.HLTMenuConfig.Menu.CFValidation import testHLTTree
+    testHLTTree( topSequence )
+
+def matrixDisplayOld( allCFSeq ):
+    from collections import defaultdict
+    longestName = 5
+    mx = defaultdict(lambda: dict())
+    for stepNumber,step in enumerate(allCFSeq, 1):
+        for seq in step:
+            mx[stepNumber][seq.step.name] = seq # what if ther eare more sequences in one step?
+
+            longestName = max(longestName, len(seq.step.name) )
+
+    longestName = longestName + 1
+
+    def __getHyposOfStep( s ):
+        if len(s.step.sequences):
+            if len(s.step.sequences)==1:
+                if type(s.step.sequences[0].hypo) is list:
+                    return s.step.sequences[0].hypo[0].tools
+                else:
+                    return s.step.sequences[0].hypo.tools
+            else:
+                return s.step.combo.getChains().keys()
+        return []
+   
+
+
+
+    def __nextSteps( index, stepName ):
+        nextStepName = "Step%s_"%index + "_".join(stepName.split("_")[1:])
+        for sname, seq in mx[index].iteritems():
+            if sname == nextStepName:
+                return sname.ljust( longestName ) + __nextSteps( index + 1, nextStepName )
+        return ""
+
+    log.debug("" )
+    log.debug("chains^ vs steps ->")
+    log.debug( "="*90 )
+    for sname, seq in mx[1].iteritems():
+        guessChainName = '_'.join( sname.split( "_" )[1:] )
+        log.debug( " Reco chain: %s: %s", guessChainName.rjust(longestName),  __nextSteps( 1, sname ) )
+        log.debug( " %s", " ".join( __getHyposOfStep( seq ) ) )
+        log.debug( "" )
+
+    log.debug( "%s", "="*90 )
+    log.debug( "" )
+
+
+    
+def matrixDisplay( allCFSeq ):
+ 
+    def __getHyposOfStep( step ):
+        if len(step.sequences):
+            if len(step.sequences)==1:
+                if type(step.sequences[0].hypo) is list:
+                    return step.sequences[0].hypo[0].tools
+                else:
+                    return step.sequences[0].hypo.tools
+            else:
+                return step.combo.getChains().keys()
+        return []
+ 
+   
+    # fill dictionary to cumulate chains on same sequences, in steps (dict with composite keys)
+    from collections import defaultdict
+    mx = defaultdict(list)
+
+    for stepNumber,cfseq_list in enumerate(allCFSeq, 1):
+        for cfseq in cfseq_list:
+            chains = __getHyposOfStep(cfseq.step)
+            for seq in cfseq.step.sequences:
+                mx[stepNumber, seq.sequence.Alg.name()].extend(chains)
+
+
+    # sort dictionary by fist key=step
+    from collections import  OrderedDict
+    sorted_mx = OrderedDict(sorted( mx.items(), key= lambda k: k[0]))
+
+    log.info( "" )
+    log.info( "="*90 )
+    log.info( "Cumulative Summary of steps")
+    log.info( "="*90 )
+    for (step, seq), chains in sorted_mx.items():
+        log.info( "(step, sequence)  ==> (%d, %s) is in chains: ",  step, seq)
+        for chain in chains:
+            log.info( "              %s",chain)
+
+    log.info( "="*90 )
+
+
+
+   
+
+def decisionTreeFromChains(HLTNode, chains, allDicts, newJO):
+    """ creates the decision tree, given the starting node and the chains containing the sequences  """
+
+    log.debug("Run decisionTreeFromChains on %s", HLTNode.name())
+    HLTNodeName= HLTNode.name()
+    if len(chains) == 0:
+        log.info("Configuring empty decisionTree")
+        return []
+
+    # add chains to multiplicity map (new step here, as this was originally in the __init__ of Chain class
+
+    (finalDecisions, CFseq_list) = createDataFlow(chains, allDicts)
+    if not newJO:
+        createControlFlow(HLTNode, CFseq_list)
+    else:
+        from TriggerMenuMT.HLTMenuConfig.Menu.HLTCFConfig_newJO import createControlFlowNewJO
+        createControlFlowNewJO(HLTNode, CFseq_list)
+
+    # decode and attach HypoTools:
+    for chain in chains:
+        chain.decodeHypoToolConfs()
+
+    log.debug("finalDecisions: %s", finalDecisions)
+    if create_dot():
+        all_DataFlow_to_dot(HLTNodeName, CFseq_list)
+
+    # matrix display
+    matrixDisplay( CFseq_list )
+
+    return finalDecisions
+
+
+def createDataFlow(chains, allDicts):
+    """ Creates the filters and connect them to the menu sequences"""
+
+    # find tot nsteps
+    chainWithMaxSteps = max(chains, key=lambda chain: len(chain.steps))
+    NSTEPS = len(chainWithMaxSteps.steps)
+
+    log.debug("createDataFlow for %d chains and total %d steps", len(chains), NSTEPS)
+
+    from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import CFSequence
+    # initialize arrays for monitor
+    finalDecisions = [ [] for n in range(NSTEPS) ]
+    CFseqList = [ [] for n in range(NSTEPS) ]
+
+    # loop over chains
+    for chain in chains:
+        log.info("\n Configuring chain %s with %d steps: \n   - %s ", chain.name,len(chain.steps),'\n   - '.join(map(str, [{step.name:step.multiplicity} for step in chain.steps])))
+
+        lastCFseq = None
+        for nstep, chainStep in enumerate( chain.steps ):
+            log.debug("\n************* Start connecting step %d %s for chain %s", nstep+1, chainStep.name, chain.name)
+
+            filterInput = chain.L1decisions if nstep == 0 else lastCFseq.decisions
+            log.debug("Seeds added; having in the filter now: %s", filterInput)
+
+
+            if len(filterInput) == 0 :
+                log.error("ERROR: Filter for step %s has %d inputs! At least one is expected", chainStep.name, len(filterInput))
+
+
+            # make one filter per step:
+            sequenceFilter= None
+            filterName = CFNaming.filterName(chainStep.name)
+            filterOutput =[ CFNaming.filterOutName(filterName, inputName) for inputName in filterInput ]
+
+            (foundFilter, foundCFSeq) = findCFSequences(filterName, CFseqList[nstep])
+            log.debug("Found %d CF sequences with filter name %s", foundFilter, filterName)
+             # add error if more than one
+            if not foundFilter:
+                sequenceFilter = buildFilter(filterName, filterInput)
+                CFseq = CFSequence( ChainStep=chainStep, FilterAlg=sequenceFilter)
+                CFseq.connect(filterOutput)
+                CFseqList[nstep].append(CFseq)
+                lastCFseq=CFseq
+            else:
+                if len(foundCFSeq) > 1:
+                    log.error("Found more than one seuqences containig this filter %s", filterName)
+                lastCFseq=foundCFSeq[0]
+                sequenceFilter=lastCFseq.filter
+                lastCFseq.connect(filterOutput)
+                [ sequenceFilter.addInput(inputName) for inputName in filterInput ]
+                [ sequenceFilter.addOutput(outputName) for outputName in  filterOutput ]
+
+
+            # add chains to the filter:
+            chainLegs = chain.getChainLegs()
+            for leg in chainLegs:
+                sequenceFilter.setChains(leg)
+                log.debug("Adding chain %s to %s", leg, sequenceFilter.Alg.name())
+            log.debug("Now Filter has chains: %s", sequenceFilter.getChains())
+
+            if chainStep.isCombo:
+                if chainStep.combo is not None:
+                    chainStep.combo.addChain( [d for d in allDicts if d['chainName'] == chain.name ][0])
+                    log.debug("Added chains to ComboHypo: %s",chainStep.combo.getChains())
+
+            if len(chain.steps) == nstep+1:
+                log.debug("Adding finalDecisions for chain %s at step %d:", chain.name, nstep+1)
+                for dec in lastCFseq.decisions:
+                    finalDecisions[nstep].append(dec)
+                    log.debug(dec)
+                    
+        #end of loop over steps
+        log.info("\n Built CF for chain %s with %d steps: \n   - %s ", chain.name,len(chain.steps),'\n   - '.join(map(str, [{step.name:step.multiplicity} for step in chain.steps])))
+    #end of loop over chains
+
+
+    log.debug("End of createDataFlow for %d chains and total %d steps", len(chains), NSTEPS)
+    return (finalDecisions, CFseqList)
+
+
+def createControlFlow(HLTNode, CFseqList):
+    """ Creates Control Flow Tree starting from the CFSequences"""
+
+    HLTNodeName= HLTNode.name()
+    log.debug("createControlFlow on node %s",HLTNodeName)
+
+    for nstep in range(len(CFseqList)):
+        stepSequenceName =  CFNaming.stepName(nstep)
+        log.debug("\n******** Create CF Tree %s with AthSequencers", stepSequenceName)
+
+        #first make the filter step
+        stepFilterNode = createStepFilterNode(stepSequenceName, CFseqList[nstep], dump=False)
+        HLTNode += stepFilterNode
+
+        # then the reco step
+        stepRecoNode = createStepRecoNode(stepSequenceName, CFseqList[nstep], dump=False)
+        HLTNode += stepRecoNode
+
+
+        # then the monitor summary
+        stepDecisions = []
+        for CFseq in CFseqList[nstep]:
+            stepDecisions.extend(CFseq.decisions)
+
+        summary=makeSummary( stepSequenceName, stepDecisions )
+
+        HLTNode += summary
+
+        if create_dot():
+            log.debug("Now Draw...")
+            stepCF_DataFlow_to_dot(stepRecoNode.name(), CFseqList[nstep])
+            stepCF_ControlFlow_to_dot(stepRecoNode)
+
+        log.info("************* End of step %d, %s", nstep+1, stepSequenceName)
+
+    return
+
+
+
+
+"""
+Not used, kept for reference and testing purposes
+To be removed in future
+"""
+def generateDecisionTreeOld(HLTNode, chains, allChainDicts):
+    log.debug("Run generateDecisionTreeOld on %s", HLTNode.name())
+    from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+    acc = ComponentAccumulator()
+    from collections import defaultdict
+    from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import CFSequence
+
+    chainStepsMatrix = defaultdict(lambda: defaultdict(lambda: list()))
+
+    ## Fill chain steps matrix
+    for chain in chains:
+        chain.decodeHypoToolConfs()#allChainDicts)
+        for stepNumber, chainStep in enumerate(chain.steps):
+            chainName = chainStep.name.split('_')[0]
+            chainStepsMatrix[stepNumber][chainName].append(chain)
+
+    allSequences = []
+
+    ## Matrix with steps lists generated. Creating filters for each cell
+    for nstep in chainStepsMatrix:
+        CFsequences = []
+        stepDecisions = []
+        stepAccs = []
+        stepHypos = []
+
+        for chainName in chainStepsMatrix[nstep]:
+            chainsInCell = chainStepsMatrix[nstep][chainName]
+
+            if not chainsInCell:
+                continue
+
+
+            stepCategoryAcc = ComponentAccumulator()
+
+            stepHypo = None
+
+            for chain in chainsInCell:
+                for seq in chain.steps[nstep].sequences:
+                    if seq.ca:
+                        stepCategoryAcc.merge( seq.ca )
+
+                    alg = seq.hypo.Alg
+                    if stepHypo is None:
+                        stepHypo = alg
+                        stepHypos.append( alg )
+                    stepCategoryAcc.addEventAlgo( alg )
+
+            stepAccs.append( stepCategoryAcc )
+
+            stepCategoryAcc.printConfig( True, True )
+            firstChain = chainsInCell[0]
+
+            if nstep == 0:
+                filter_input = firstChain.L1decisions
+            else:
+                filter_input = []
+                for sequence in firstChain.steps[nstep - 1].sequences:
+                    filter_input += sequence.outputs
+
+            # One aggregated filter per chain (one per column in matrix)
+            filterName = 'Filter_{}'.format( firstChain.steps[nstep].name )
+            filter_output =[]
+            for i in filter_input:
+                filter_output.append( CFNaming.filterOutName(filterName, i))
+            sfilter = buildFilter(filterName,  filter_input)
+
+            chainStep = firstChain.steps[nstep]
+
+            CFseq = CFSequence( ChainStep=chainStep, FilterAlg=sfilter, connections=filter_output )
+            CFsequences.append( CFseq )
+
+
+            for sequence in chainStep.sequences:
+                stepDecisions += sequence.outputs
+
+            for chain in chainsInCell:
+                sfilter.setChains(chain.name)
+
+        allSequences.append(CFsequences)
+
+        stepName = 'Step{}'.format(nstep)
+        stepFilter = createStepFilterNode(stepName, CFsequences, dump=False)
+        stepCF = createStepRecoNode('{}_{}'.format(HLTNode.name(), stepName), CFsequences, dump=False)
+
+        from AthenaCommon.CFElements import findOwningSequence
+        for oneAcc, cfseq, hypo in zip( stepAccs, CFsequences, stepHypos):
+            owning = findOwningSequence( stepCF, hypo.getName() )
+            acc.addSequence( owning )
+            acc.merge( oneAcc, sequenceName = owning.getName() )
+        summary = makeSummary('TriggerSummary{}'.format(stepName), stepDecisions)
+
+        HLTNode += stepFilter
+        HLTNode += stepCF
+        HLTNode += summary
+        if create_dot():
+            stepCF_DataFlow_to_dot('{}_{}'.format(HLTNode.name(), stepName), CFsequences)
+            stepCF_ControlFlow_to_dot(stepCF)
+            all_DataFlow_to_dot(HLTNode.name(), allSequences)
+
+        matrixDisplay( allSequences )
+    return acc
+
+
+
+def findCFSequences(filter_name, cfseqList):
+      """
+      searches for a filter, with given name, in the CF sequence list of this step
+      """
+      log.debug( "findCFSequences: filter base name %s", filter_name )
+      foundFilters = [cfseq for cfseq in cfseqList if filter_name == cfseq.filter.Alg.name()]
+      log.debug("found %d filters with base name %s", len( foundFilters ), filter_name)
+
+      found=len(foundFilters)
+      if found:
+          return (found, foundFilters)
+      return (found, None)
+
+
+def buildFilter(filter_name,  filter_input):
+    """
+     Build the FILTER
+     one filter per previous sequence at the start of the sequence: always create a new one
+     if the previous hypo has more than one output, try to get all of them
+     one filter per previous sequence: 1 input/previous seq, 1 output/next seq
+    """
+    from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import  RoRSequenceFilterNode
+    sfilter = RoRSequenceFilterNode(name=filter_name)
+    for i in filter_input:
+        sfilter.addInput(i)
+    for i in filter_input:
+        sfilter.addOutput(CFNaming.filterOutName(filter_name, i))
+
+    log.debug("Added inputs to filter: %s", sfilter.getInputList())
+    log.debug("Added outputs to filter: %s", sfilter.getOutputList())
+    log.debug("Filter Done: %s", sfilter.Alg.name())
+
+    return (sfilter)
-- 
GitLab