ATLAS Offline Software
Loading...
Searching...
No Matches
l1MenuGraph.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2
3# The code in this module is used to examine the contents of a Menu
4# and create a list of AlgData objects. These objects contain the information
5# required to initialise a GlobalSim AlgTool used to run a GlobalL1TopoSim
6# Algorithm.
7
8
9
10from .getMenu import getMenu
11from .Digraph import Digraph
12from .dot import dot
13
14from AthenaCommon.Logging import logging
15logger = logging.getLogger(__name__)
16from AthenaCommon.Constants import VERBOSE
17logger.setLevel(VERBOSE)
18
19import pprint
20
21class AlgData():
22
23 def __init__(self):
24 self.name = None
25 self.klass = None
26 self.l1AlgType = None # TOPO, MUTOPO, R2TOPO, MULTTOPO
27
28 # globalL1AlgType must appear in router table in toolFromAlgData
29 self.globalL1AlgType = None # INPUT, COUNT, SORT, DECISION
30
31 self.inputs = []
32 self.outputs = []
33
34 self.input_sns = []
35 self.sn = None
36
37 self.numResultBits = None
38
39 # only for some COUNT algorithms
40 self.threshold = None
42
43 def __str__(self):
44
45 s = ['Algdata:']
46 for k, v in self.__dict__.items():
47 s.append(k + ': ' + str(v))
48
49 return '\n'.join(s)
50
51def l1MenuGraph(flags, do_dot=False, verbose=False):
52 """Find the L1Topo call graph. For each node, provide a AlgData
53 object. These objects carry information needed to configure
54 Athena components."""
55
56 L1Menu = getMenu(flags)
57
58 logger.debug('menu object')
59 logger.debug(L1Menu.printSummary())
60
61 alg_data_list = []
62
63 sn = 1 # node number. 0 is reserved for root
64 for algType in ("TOPO", "MULTTOPO"):
65 algs = L1Menu.topoAlgorithms(algType) # map: algName: alg details
66
67 for alg_key in algs.keys():
68 keyed_algs = algs[alg_key]
69 for name, value in keyed_algs.items():
70
71 ad = AlgData()
72 ad.name = name
73 ad.klass = value['klass']
74 ad.l1AlgType = algType
75 if algType == 'MULTTOPO':
76 ad.globalL1AlgType = 'COUNT'
77 try:
78 threshold_name = value['threshold']
79 ad.threshold = L1Menu.thresholds()[threshold_name]['value']
80 ad.threshold_flavour = value['flavour']
81 except KeyError:
82 logger.info('No threshold data for COUNT alg '+name)
83
84 inputs = value['input']
85 if isinstance(inputs, list):
86 ad.inputs = inputs
87 else:
88 ad.inputs = [inputs]
89
90 outputs = value['output']
91 if isinstance(outputs, list):
92 ad.outputs = outputs
93 else:
94 ad.outputs = [outputs]
95
96 ad.sn = sn
97 ad.menu = value
98
99 sn += 1
100 alg_data_list.append(ad)
101
102
103 # make lists with no duplicates in a manner that avoids
104 # non-fatal but unpleasant changes in odering if set() is naively used.
105 all_inputs = []
106 [all_inputs.extend(ad.inputs) for ad in alg_data_list]
107 all_inputs = {}.fromkeys(all_inputs)
108 all_inputs = all_inputs.keys()
109
110 all_outputs = []
111 [all_outputs.extend(ad.outputs) for ad in alg_data_list]
112 all_outputs = {}.fromkeys(all_outputs)
113 all_outputs = all_outputs.keys()
114
115
116 missing_inputs = []
117 for k in all_inputs:
118 if k not in all_outputs:
119 missing_inputs.append(k)
120
121 logger.debug('input algs to be added ['+ str(len(missing_inputs))+']')
122 for io in missing_inputs:
123 logger.debug('input alg: ' + io)
124 ad = AlgData()
125 # the class string must match the name with which the
126 # class appears in the AlRegister
127 #
128 klass = 'TopoEventInputAlg'
129 ad.name = klass + '_' + io
130 ad.klass = klass
131 ad.outputs = [io]
132 ad.globalL1AlgType = 'INPUT'
133 ad.sn = sn
134 sn += 1
135 alg_data_list.append(ad)
136
137 # identify the SORT and DECISION algorithms
138 for ad in alg_data_list:
139 if ad.globalL1AlgType is not None: continue
140 isSortAlg = ad.inputs[0] in missing_inputs
141 if isSortAlg:
142 if len(ad.inputs) != 1:
143 raise RuntimeError('AlgData with > 1 inputs '
144 'at least 1 from input alg')
145
146 ad.globalL1AlgType = 'SORT'
147 else:
148 for inp in ad.inputs:
149 if inp in missing_inputs:
150 raise RuntimeError('AlgData with >1 inputs'
151 'at least 1 from input alg')
152
153 ad.globalL1AlgType = 'DECISION'
154
155
156 alg2inputs = {}
157 output2alg = {}
158 for ad in alg_data_list:
159 algname = ad.name
160 assert algname not in alg2inputs
161
162 alg2inputs[algname] = ad.inputs
163
164 for outp in ad.outputs:
165 assert outp not in output2alg
166 output2alg[outp] = algname
167
168 logger.verbose('\nalg2inputs')
169 logger.verbose(pprint.pformat(alg2inputs, indent=4))
170
171 logger.verbose('\noutput2alg')
172 logger.verbose(pprint.pformat(output2alg, indent=4))
173
174
175 # ensure alg names are unique
176 alg_names = [ad.name for ad in alg_data_list]
177 assert len(alg_names) == len(set(alg_names))
178
179 # ensure output names are not shared by more than one algorithm
180 outputs = []
181 [outputs.extend(ad.outputs) for ad in alg_data_list]
182 assert len(outputs) == len(set(outputs))
183
184 # algorithm C is the child of algorithm P if C has an output
185 # which is an input to P
186
187 output2sn = {}
188 for a in alg_data_list:
189 for o in a.outputs:
190 output2sn[o] = a.sn
191
192 # from input names, fill in input sns:
193 for ad in alg_data_list:
194 ad.input_sns = [output2sn[inp] for inp in ad.inputs]
195
196
197 logger.verbose('\nalg data:')
198 for ad in alg_data_list:
199 logger.verbose(pprint.pformat(str(ad), indent=4))
200
201 adj_table = {}
202 V = sn # no. of vertices. 0 reserved for root. sn has already been bumped
203 for v in range(V): # need V locations
204 adj_table[v] = []
205
206 for a in alg_data_list:
207 for i in a.inputs:
208 if i in output2sn:
209 adj_table[a.sn].append(output2sn[i])
210
211 G = Digraph(V)
212 for v in adj_table:
213 for w in adj_table[v]:
214 G.addEdge(v, w)
215
216 if do_dot:
217 dot(G, 'G.dot')
218
219 return G, alg_data_list
220
221if __name__ == '__main__':
222 l1MenuGraph(do_dot=True, verbose=True)
STL class.
Definition dot.py:1