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