ATLAS Offline Software
Loading...
Searching...
No Matches
HLTCFComponents.py
Go to the documentation of this file.
1# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3from TriggerMenuMT.HLT.Config.MenuComponents import AlgNode, HypoAlgNode
4from TriggerMenuMT.HLT.Config.ControlFlow.MenuComponentsNaming import CFNaming
5from TriggerMenuMT.HLT.Config.Utility.HLTMenuConfig import HLTMenuConfig
6from TriggerMenuMT.HLT.Config.ControlFlow.HLTCFTools import isComboHypoAlg
7from TriggerMenuMT.HLT.Config.MenuComponents import EmptyMenuSequence
8from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
9from AthenaConfiguration.ComponentFactory import CompFactory
10from AthenaCommon.CFElements import findAlgorithmByPredicate, parOR, seqAND
11from functools import lru_cache
12
13from AthenaCommon.Logging import logging
14log = logging.getLogger( __name__ )
15
16RoRSeqFilter = CompFactory.RoRSeqFilter
17PassFilter = CompFactory.PassFilter
18
19
20class SequenceFilterNode(AlgNode):
21 """Node for any kind of sequence filter"""
22 def __init__(self, Alg, inputProp, outputProp):
23 AlgNode.__init__(self, Alg, inputProp, outputProp)
24
25 def addChain(self, name, input_name):
26 return
27
28 def getChains(self):
29 return []
30
31 def getChainsPerInput(self):
32 return [[]]
33
34 def __repr__(self):
35 return "SequenceFilter::%s [%s] -> [%s], chains=%s"%(self.Alg.name,' '.join(map(str, self.getInputList())),' '.join(map(str, self.getOutputList())), self.getChains())
36
37
38class RoRSequenceFilterNode(SequenceFilterNode):
39 def __init__(self, name):
40 Alg= RoRSeqFilter(name)
41 SequenceFilterNode.__init__(self, Alg, 'Input', 'Output')
42
43 def addChain(self, name, input_name):
44 input_index = self.readInputList().index(input_name)
45 if len(self.Alg.ChainsPerInput) == input_index:
46 self.Alg.ChainsPerInput.append([name])
47 elif len(self.Alg.ChainsPerInput) > input_index:
48 self.Alg.ChainsPerInput[input_index].append(name)
49 else:
50 log.error("Error: why requiring input %i when size is %i ?" , input_index , len(self.Alg.ChainsPerInput))
51 raise RuntimeError("Error: why requiring input %i when size is %i " , input_index , len(self.Alg.ChainsPerInput))
52
53 return self.Alg.Chains.append(name) # still neded?
54
55 def getChains(self):
56 return self.Alg.Chains
57
58 def getChainsPerInput(self):
59 return self.Alg.ChainsPerInput
60
61
62
63class PassFilterNode(SequenceFilterNode):
64 """ PassFilter is a Filter node without inputs/outputs, so OutputProp=InputProp=empty"""
65 def __init__(self, name):
66 Alg=CompFactory.AthSequencer( "PassSequence" )
67 Alg.IgnoreFilterPassed=True # always pass
68 SequenceFilterNode.__init__(self, Alg, '', '')
69
70 def addOutput(self, name):
71 self.outputs.append(str(name))
72
73 def addInput(self, name):
74 self.inputs.append(str(name))
75
76 def getOutputList(self):
77 return self.outputs
78
79 def getInputList(self):
80 return self.inputs
81
82def isPassSequence(alg):
83 return isinstance(alg, PassFilterNode)
84
85#########################################################
86# CFSequence class
87#########################################################
88class CFSequence(object):
89 """Class to describe the flow of decisions through ChainStep + filter with their connections (input, output)
90 A Filter can have more than one input/output if used in different chains, so this class stores and manages all of them (when doing the connect)
91 """
92 def __init__(self, ChainStep, FilterAlg):
93 log.debug(" *** Create CFSequence %s with Filter %s", ChainStep.name, FilterAlg.Alg.getName())
94 self.filterNode = FilterAlg
95 self.step = ChainStep
96
97 self.ca = ComponentAccumulator()
98 #empty step: add the PassSequence, one instance only is appended to the tree
99 seqAndWithFilter = FilterAlg.Alg if ChainStep.isEmpty else seqAND(ChainStep.name)
100 self.ca.addSequence(seqAndWithFilter)
101 self.seq = seqAndWithFilter
102 if not ChainStep.isEmpty:
103 self.ca.addEventAlgo(FilterAlg.Alg, sequenceName=seqAndWithFilter.getName())
104 self.stepReco = parOR(ChainStep.name + CFNaming.RECO_POSTFIX)
105 # all reco algorithms from all the sequences in a parallel sequence
106 self.ca.addSequence(self.stepReco, parentName=seqAndWithFilter.getName())
107 log.debug("created parOR %s inside seqAND %s ", self.stepReco.getName(), seqAndWithFilter.getName())
108 self.mergeStepSequences(ChainStep)
109 # merge the Hypoalg (before the Combo)
110 for menuseq in ChainStep.sequences:
111 if not isinstance(menuseq, EmptyMenuSequence):
112 self.ca.merge(menuseq.hypoAcc, sequenceName=seqAndWithFilter.getName())
113
114 self.connectCombo()
115 self.setDecisions()
116 if self.step.combo is not None:
117 self.ca.merge(self.step.combo.acc, sequenceName=seqAndWithFilter.getName())
118 log.debug("CFSequence.__init: created %s ",self)
119
120
121 def setDecisions(self):
122 """ Set the output decision of this CFSequence as the hypo outputdecision; In case of combo, takes the Combo outputs"""
123 self.decisions=[]
124 # empty steps:
125 if self.step.combo is None:
126 self.decisions.extend(self.filterNode.getOutputList())
127 else:
128 self.decisions.extend(self.step.combo.getOutputList())
129 log.debug("CFSequence: set out decisions: %s", self.decisions)
130
131
132 def connectCombo(self):
133 """ connect Combo to Hypos"""
134 if self.step.combo is None:
135 log.debug("CFSequence.connectCombo: no Combo found")
136 return
137
138 for seq in self.step.sequences:
139 combo_input=seq.getOutputList()[0]
140 self.step.combo.addInput(combo_input)
141 inputs = self.step.combo.readInputList()
142 legindex = inputs.index(combo_input)
143 log.debug("CFSequence.connectCombo: adding input to %s: %s", self.step.combo.Alg.getName(), combo_input)
144 # inputs are the output decisions of the hypos of the sequences
145 combo_output=CFNaming.comboHypoOutputName (self.step.combo.Alg.getName(), legindex)
146 self.step.combo.addOutput(combo_output)
147 log.debug("CFSequence.connectCombo: adding output to %s: %s", self.step.combo.Alg.getName(), combo_output)
148
149 def mergeStepSequences(self, chainStep):
150 for menuseq in chainStep.sequences:
151 try:
152 self.ca.merge(menuseq.ca, sequenceName=self.stepReco.getName())
153 except Exception as e:
154 log.error(f'Failed to merge into {self.stepReco.getName()}')
155 raise e
156
157 @lru_cache(None)
158 def findComboHypoAlg(self):
159 return findAlgorithmByPredicate(self.seq, lambda alg: alg.name == self.step.Alg.name and isComboHypoAlg(alg))
160
161 def __repr__(self):
162 return "--- CFSequence ---\n + Filter: %s \n + decisions: %s\n + %s \n"%(\
163 self.filterNode.Alg.name, self.decisions, self.step)
164
165
166
167
168class CFGroup(object):
169 """Class to store the Step + its Filter (CFSequence) plus the chains and dictionaries of the legs using that step """
170 def __init__(self, ChainStep, FilterAlg):
171 self.stepDicts = [] # will become a list of lists
172 self.chains = []
173 self.comboToolConfs = []
174 self.createCFSequence(ChainStep, FilterAlg)
175 log.debug("CFGroup.__init: created for %s ",ChainStep.name)
176
177 def createCFSequence(self, ChainStep, FilterAlg):
178 '''This creates the CAs for the menu sequences, if fastMenu style, and the CFSequence'''
179 log.debug("CFGroup.creating CFSEquence")
180 self.sequenceCA = CFSequence(ChainStep, FilterAlg)
181 return self.sequenceCA
182
183 def addStepLeg(self, newstep, chainName):
184 self.stepDicts.append(newstep.stepDicts) # one dict per leg
185 self.chains.append(chainName)
186 self.comboToolConfs.append(newstep.comboToolConfs)
187
188
189 def connect(self, connections):
190 """Connect filter to ChainStep (and all its sequences) through these connections (which are sets of filter outputs)
191 if a ChainStep contains the same sequence multiple times (for multi-leg chains),
192 the filter is connected only once (to avoid multiple DH links)
193 """
194 if log.isEnabledFor(logging.DEBUG):
195 log.debug("CFGroup: connect Filter %s with %d menuSequences of step %s, using %d connections", self.sequenceCA.filterNode.Alg.name, len(self.sequenceCA.step.sequences), self.sequenceCA.step.name, len(connections))
196 log.debug(" --- sequences: ")
197 for seq in self.sequenceCA.step.sequences:
198 log.debug(seq)
199
200 if len(connections) == 0:
201 log.error("No filter outputs are set!")
202
203 if len(self.sequenceCA.step.sequences):
204 # check whether the number of filter outputs are the same as the number of sequences in the step
205 if len(connections) != len(self.sequenceCA.step.sequences):
206 log.error("CFGroup: Found %d connections and %d MenuSequences in Step %s", len(connections), len(self.sequenceCA.step.sequences), self.sequenceCA.step.name)
207 raise Exception("[CFGroup] Connections and sequences do not match, this must be fixed!")
208
209 for nseq, seq in enumerate(self.sequenceCA.step.sequences):
210 filter_out = connections[nseq]
211 log.debug("CFGroup: Found input %s to sequence::%s from Filter::%s", filter_out, seq.name, self.sequenceCA.filterNode.Alg.name)
212 seq.connectToFilter( filter_out )
213 else:
214 log.debug("This CFGroup has no sequences: outputs are the Filter outputs, which are %d", len(self.sequenceCA.decisions))
215
216 def createHypoTools(self, flags):
217 """ set and create HypoTools accumulated on the self.step from an input step configuration
218 """
219 if self.sequenceCA.step.combo is None:
220 return
221
222 log.debug("CFGroup.createHypoTools for Step %s", self.sequenceCA.step.name)
223 for sdict in self.stepDicts:
224 for seq, onePartChainDict in zip(self.sequenceCA.step.sequences, sdict):
225 log.debug(' seq: %s, onePartChainDict:', seq.name)
226 log.debug(' %s', onePartChainDict)
227 if not isinstance(seq, EmptyMenuSequence):
228 hypoToolConf=seq.getHypoToolConf()
229 if hypoToolConf is None: # avoid empty sequences
230 log.error("HypoToolConf not found ", seq.name)
231 hypoToolConf.setConf( onePartChainDict )
232 hypo = HypoAlgNode(Alg = self.sequenceCA.ca.getEventAlgo(seq.hypo.Alg.getName()))
233 hypoToolAcc = hypo.addHypoTool(flags, hypoToolConf) #this creates the HypoTools
234 if isinstance(hypoToolAcc, ComponentAccumulator):
235 self.sequenceCA.ca.merge(hypoToolAcc)
236
237 for chain,conf in zip(self.chains, self.comboToolConfs):
238 chainDict = HLTMenuConfig.getChainDictFromChainName(chain)
239 self.sequenceCA.step.combo.createComboHypoTools(flags, chainDict, conf)