ATLAS Offline Software
CFElements.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 from AthenaConfiguration.ComponentFactory import CompFactory
3 import collections
4 
5 AthSequencer = CompFactory.AthSequencer # cache lookup
6 
7 def parAND(name, subs=[]):
8  """parallel AND sequencer"""
9  return AthSequencer( name,
10  ModeOR = False,
11  Sequential = False,
12  StopOverride = True,
13  Members = subs.copy() )
14 
15 def parOR(name, subs=[]):
16  """parallel OR sequencer
17  This is the default sequencer and lets the DataFlow govern the execution entirely.
18  """
19  return AthSequencer( name,
20  ModeOR = True,
21  Sequential = False,
22  StopOverride = True,
23  Members = subs.copy() )
24 
25 def seqAND(name, subs=[]):
26  """sequential AND sequencer"""
27  return AthSequencer( name,
28  ModeOR = False,
29  Sequential = True,
30  StopOverride = False,
31  Members = subs.copy() )
32 
33 def seqOR(name, subs=[]):
34  """sequential OR sequencer
35  Used when a barrier needs to be set by all subs reached irrespective of the decision
36  """
37  return AthSequencer( name,
38  ModeOR = True,
39  Sequential = True,
40  StopOverride = True,
41  Members = subs.copy() )
42 
43 
45  """Return sequence children (empty if comp is not a sequence)"""
46  try:
47  return comp.Members
48  except AttributeError:
49  return []
50 
51 
52 def getAllSequenceNames(seq, depth=0):
53  """ Generate a list of sequence names and depths in the graph, e.g.
54  [('AthAlgSeq', 0), ('seq1', 1), ('seq2', 1), ('seq1', 2)]
55  represents
56  \\__ AthAlgSeq (seq: PAR AND)
57  \\__ seq1 (seq: SEQ AND)
58  \\__ seq2 (seq: SEQ AND)
59  """
60 
61  seqNameList = [(seq.getName(), depth)]
62  for c in getSequenceChildren(seq):
63  if isSequence(c):
64  seqNameList += getAllSequenceNames(c, depth+1)
65 
66  return seqNameList
67 
68 
70  """ Enforce rules for sequence graph - identical items can not be added to itself (even indirectly) """
71 
72  def __noSubSequenceOfName( s, n, seen = set() ):
73  seen = seen.copy()
74  seen.add (s)
75  for c in getSequenceChildren( s ):
76  if c in seen:
77  raise RuntimeError(f"Sequence {c.getName()} contains itself")
78  if isSequence( c ):
79  if c.getName() == n:
80  raise RuntimeError(f"Sequence {n} contains sub-sequence of the same name")
81  __noSubSequenceOfName( c, c.getName(), seen ) # check each sequence for repetition as well
82  __noSubSequenceOfName( c, n, seen )
83 
84  __noSubSequenceOfName( seq, seq.getName() )
85  for c in getSequenceChildren( seq ):
87 
88 
89 def stepSeq(name, filterAlg, rest):
90  """ elementary HLT step sequencer, filterAlg is gating, rest is anything that needs to happen within the step """
91  stepReco = parOR(name+"_reco", rest)
92  stepAnd = seqAND(name, [ filterAlg, stepReco ])
93  return stepAnd
94 
95 
96 def isSequence( obj ):
97  return isinstance(obj, AthSequencer)
98 
99 
100 def findSubSequence( start, nameToLookFor ):
101  """ Traverse sequences tree to find a sequence of a given name. The first one is returned. """
102  if start.getName() == nameToLookFor:
103  return start
104  for c in getSequenceChildren(start):
105  if isSequence( c ):
106  if c.getName() == nameToLookFor:
107  return c
108  found = findSubSequence( c, nameToLookFor )
109  if found:
110  return found
111  return None
112 
113 
114 def findOwningSequence( start, nameToLookFor ):
115  """ find sequence that owns the sequence nameTooLookFor"""
116  for c in getSequenceChildren(start):
117  if c.getName() == nameToLookFor:
118  return start
119  if isSequence( c ):
120  found = findOwningSequence( c, nameToLookFor )
121  if found:
122  return found
123  return None
124 
125 
126 def findAlgorithmByPredicate( startSequence, predicate, depth = 1000000 ):
127  """ Traverse sequences tree to find the first algorithm satisfying given predicate. The first encountered is returned.
128 
129  Depth of the search can be controlled by the depth parameter.
130  Typical use is to limit search to the startSequence with depth parameter set to 1
131  """
132  for c in getSequenceChildren(startSequence):
133  if not isSequence(c):
134  if predicate(c):
135  return c
136  else:
137  if depth > 1:
138  found = findAlgorithmByPredicate( c, predicate, depth-1 )
139  if found:
140  return found
141 
142  return None
143 
144 
145 def findAlgorithm( startSequence, nameToLookFor, depth = 1000000 ):
146  """ Traverse sequences tree to find the algorithm of given name. The first encountered is returned.
147 
148  The name() method is used to obtain the algorithm name, that one has to match to the request.
149  """
150  return findAlgorithmByPredicate( startSequence, lambda alg: alg.getName() == nameToLookFor, depth )
151 
152 
153 def findAllAlgorithms(sequence, nameToLookFor=None):
154  """
155  Returns flat listof of all algorithm instances in this, and in sub-sequences
156  """
157  algorithms = []
158  for child in getSequenceChildren(sequence):
159  if isSequence(child):
160  algorithms += findAllAlgorithms(child, nameToLookFor)
161  else:
162  if nameToLookFor is None or child.getName() == nameToLookFor:
163  algorithms.append(child)
164  return algorithms
165 
166 
167 def findAllAlgorithmsByName(sequence, namesToLookFor=None):
168  """
169  Finds all algorithms in sequence and groups them by name
170 
171  Resulting dict has a following structure
172  {"Alg1Name":[(Alg1Instance, parentSequenceA, indexInSequenceA),(Alg1Instance, parentSequenceB, indexInSequenceB)],
173  "Alg2Name":(Alg2Instance, parentSequence, indexInThisSequence),
174  ....}
175  """
176  algorithms = collections.defaultdict(list)
177  for idx, child in enumerate(getSequenceChildren(sequence)):
178  if child.getName() == sequence.getName():
179  raise RuntimeError(f"Recursively-nested sequence: {child.getName()} contains itself")
180  if isSequence(child):
181  childAlgs = findAllAlgorithmsByName(child, namesToLookFor)
182  for algName in childAlgs:
183  algorithms[algName] += childAlgs[algName]
184  else:
185  if namesToLookFor is None or child.getName() in namesToLookFor:
186  algorithms[child.getName()].append( (child, sequence, idx) )
187  return algorithms
188 
189 
191  """ Converts tree like structure of sequences into dictionary
192  keyed by top/start sequence name containing lists of of algorithms & sequences."""
193 
194  def __inner( seq, collector ):
195  for c in getSequenceChildren(seq):
196  collector[seq.getName()].append( c )
197  if isSequence( c ):
198  __inner( c, collector )
199 
200  from collections import defaultdict,OrderedDict
201  c = defaultdict(list)
202  __inner(start, c)
203  return OrderedDict(c)
204 
205 
206 def iterSequences( start ):
207  """Iterator of sequences and their algorithms from (and including) the `start`
208  sequence object. Do start from a sequence name use findSubSequence."""
209  def __inner( seq ):
210  for c in getSequenceChildren(seq):
211  yield c
212  if isSequence(c):
213  yield from __inner(c)
214  yield start
215  yield from __inner(start)
216 
217 
218 
219 # self test
220 import unittest
221 class TestCF( unittest.TestCase ):
222  def setUp( self ):
223  import AthenaPython.PyAthena as PyAthena
224 
225  top = parOR("top")
226  top.Members += [parOR("nest1")]
227  nest2 = seqAND("nest2")
228  top.Members += [nest2]
229  top.Members += [PyAthena.Alg("SomeAlg0")]
230  nest2.Members += [parOR("deep_nest1")]
231  nest2.Members += [parOR("deep_nest2")]
232 
233  nest2.Members += [PyAthena.Alg("SomeAlg1")]
234  nest2.Members += [PyAthena.Alg("SomeAlg2")]
235  nest2.Members += [PyAthena.Alg("SomeAlg3")]
236  self.top = top
237 
238  def test_findTop( self ):
239  f = findSubSequence( self.top, "top")
240  self.assertIsNotNone( f, "Can not find sequence at start" )
241  self.assertEqual( f.getName(), "top", "Wrong sequence" )
242  # a one level deep search
243  nest2 = findSubSequence( self.top, "nest2" )
244  self.assertIsNotNone( nest2, "Can not find sub sequence" )
245  self.assertEqual( nest2.getName(), "nest2", "Sub sequence incorrect" )
246 
247  def test_findDeep( self ):
248  # deeper search
249  d = findSubSequence( self.top, "deep_nest2")
250  self.assertIsNotNone( d, "Deep searching for sub seqeunce fails" )
251  self.assertEqual( d.getName(), "deep_nest2", "Wrong sub sequence in deep search" )
252 
253  def test_findMissing( self ):
254  # algorithm is not a sequence
255  d = findSubSequence( self.top, "SomeAlg1")
256  self.assertIsNone( d, "Algorithm confused as a sequence" )
257 
258  # no on demand creation
259  inexistent = findSubSequence( self.top, "not_there" )
260  self.assertIsNone( inexistent, "ERROR, found sub sequence that does not relay exist" )
261 
262  # owner finding
263  inexistent = findOwningSequence(self.top, "not_there")
264  self.assertIsNone( inexistent, "ERROR, found owner of inexistent sequence " )
265 
267  owner = findOwningSequence( self.top, "deep_nest1")
268  self.assertEqual( owner.getName(), "nest2", "Wrong owner %s" % owner.getName() )
269 
270  owner = findOwningSequence( self.top, "deep_nest2")
271  self.assertEqual( owner.getName(), "nest2", "Wrong owner %s" % owner.getName() )
272 
273  owner = findOwningSequence( self.top, "SomeAlg1")
274  self.assertEqual( owner.getName(), "nest2", "Wrong owner %s" % owner.getName() )
275 
276  owner = findOwningSequence( self.top, "SomeAlg0")
277  self.assertEqual( owner.getName() , "top", "Wrong owner %s" % owner.getName() )
278 
279  def test_iterSequences( self ):
280  # Traverse from top
281  result = [seq.getName() for seq in iterSequences( self.top )]
282  self.assertEqual( result, ['top', 'nest1', 'nest2', 'deep_nest1', 'deep_nest2',
283  'SomeAlg1', 'SomeAlg2', 'SomeAlg3', 'SomeAlg0'] )
284 
285  # Traverse from nested sequence
286  nest2 = findSubSequence( self.top, "nest2" )
287  result = [seq.getName() for seq in iterSequences( nest2 )]
288  self.assertEqual( result, ['nest2', 'deep_nest1', 'deep_nest2',
289  'SomeAlg1', 'SomeAlg2', 'SomeAlg3'] )
290 
291  # Traverse empty sequence
292  deep_nest2 = findSubSequence( self.top, "deep_nest2" )
293  result = [seq.getName() for seq in iterSequences( deep_nest2 )]
294  self.assertEqual( result, ['deep_nest2'] )
295 
296  # Traverse from algorithm
297  alg1 = findAlgorithm( self.top, "SomeAlg1" )
298  result = [seq.getName() for seq in iterSequences( alg1 )]
299  self.assertEqual( result, ['SomeAlg1'] )
300 
301  def test_findAlgorithms( self ):
302  a1 = findAlgorithm( self.top, "SomeAlg0" )
303  self.assertIsNotNone( a1, "Can't find algorithm present in sequence" )
304 
305  a1 = findAlgorithm( self.top, "SomeAlg1" )
306  self.assertIsNotNone( a1, "Can't find nested algorithm " )
307 
308  nest2 = findSubSequence( self.top, "nest2" )
309 
310  a1 = findAlgorithm( nest2, "SomeAlg0" )
311  self.assertIsNone( a1, "Finding algorithm that is in the upper sequence" )
312 
313  a1 = findAlgorithm( nest2, "NonexistentAlg" )
314  self.assertIsNone( a1, "Finding algorithm that is does not exist" )
315 
316  a1 = findAlgorithm( self.top, "SomeAlg0", 1)
317  self.assertIsNotNone( a1, "Could not find algorithm within the required nesting depth == 1" )
318 
319  a1 = findAlgorithm( self.top, "SomeAlg1", 1)
320  self.assertIsNone( a1, "Could find algorithm even if it is deep in sequences structure" )
321 
322  a1 = findAlgorithm( self.top, "SomeAlg1", 2)
323  self.assertIsNotNone( a1, "Could not find algorithm within the required nesting depth == 2" )
324 
325  a1 = findAlgorithm( self.top, "SomeAlg3", 2)
326  self.assertIsNotNone( a1 is None, "Could find algorithm even if it is deep in sequences structure" )
327 
328 
329 class TestNest( unittest.TestCase ):
330  def test( self ):
331  global isComponentAccumulatorCfg
332  isComponentAccumulatorCfg = lambda : True # noqa: E731 (lambda for mockup)
333 
334  top = parOR("top")
335  nest1 = parOR("nest1")
336  nest2 = seqAND("nest2")
337  top.Members += [nest1, nest2]
338 
339  deep_nest1 = seqAND("deep_nest1")
340  nest1.Members += [deep_nest1]
341 
342  nest2.Members += [nest1] # that one is ok
344  deep_nest1.Members += [nest1] # introducing an issue
345  self.assertRaises( RuntimeError, checkSequenceConsistency, top )
346 
python.CFElements.TestCF
Definition: CFElements.py:221
python.CFElements.flatAlgorithmSequences
def flatAlgorithmSequences(start)
Definition: CFElements.py:190
python.CFElements.findAllAlgorithms
def findAllAlgorithms(sequence, nameToLookFor=None)
Definition: CFElements.py:153
python.CFElements.TestCF.test_findTop
def test_findTop(self)
Definition: CFElements.py:238
AthSequencer
ClassName: AthSequencer.
Definition: AthSequencer.h:40
python.CFElements.findOwningSequence
def findOwningSequence(start, nameToLookFor)
Definition: CFElements.py:114
python.CFElements.TestCF.test_iterSequences
def test_iterSequences(self)
Definition: CFElements.py:279
python.CFElements.findAlgorithmByPredicate
def findAlgorithmByPredicate(startSequence, predicate, depth=1000000)
Definition: CFElements.py:126
python.CFElements.findAllAlgorithmsByName
def findAllAlgorithmsByName(sequence, namesToLookFor=None)
Definition: CFElements.py:167
python.CFElements.stepSeq
def stepSeq(name, filterAlg, rest)
Definition: CFElements.py:89
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.CFElements.TestNest.test
def test(self)
Definition: CFElements.py:330
python.CFElements.TestCF.test_findMissing
def test_findMissing(self)
Definition: CFElements.py:253
python.CFElements.seqAND
def seqAND(name, subs=[])
Definition: CFElements.py:25
python.CFElements.checkSequenceConsistency
def checkSequenceConsistency(seq)
Definition: CFElements.py:69
python.CFElements.seqOR
def seqOR(name, subs=[])
Definition: CFElements.py:33
python.CFElements.TestCF.test_findAlgorithms
def test_findAlgorithms(self)
Definition: CFElements.py:301
python.CFElements.TestCF.setUp
def setUp(self)
Definition: CFElements.py:222
python.CFElements.TestCF.top
top
Definition: CFElements.py:236
python.CFElements.TestNest
Definition: CFElements.py:329
python.CFElements.parOR
def parOR(name, subs=[])
Definition: CFElements.py:15
python.CFElements.getAllSequenceNames
def getAllSequenceNames(seq, depth=0)
Definition: CFElements.py:52
python.CFElements.isSequence
def isSequence(obj)
Definition: CFElements.py:96
PyAthena::Alg
Definition: PyAthenaAlg.h:33
python.CFElements.findSubSequence
def findSubSequence(start, nameToLookFor)
Definition: CFElements.py:100
python.CFElements.findAlgorithm
def findAlgorithm(startSequence, nameToLookFor, depth=1000000)
Definition: CFElements.py:145
python.CFElements.TestCF.test_findRespectingScope
def test_findRespectingScope(self)
Definition: CFElements.py:266
python.CFElements.getSequenceChildren
def getSequenceChildren(comp)
Definition: CFElements.py:44
python.CFElements.TestCF.test_findDeep
def test_findDeep(self)
Definition: CFElements.py:247
python.CFElements.iterSequences
def iterSequences(start)
Definition: CFElements.py:206
python.CFElements.parAND
def parAND(name, subs=[])
Definition: CFElements.py:7