ATLAS Offline Software
Loading...
Searching...
No Matches
GlobalSimAlgCfg_local.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3
4#
5# Configure a GlobalSim Algorithm from an XML file
6#
7# Here, configuration means load the Algorithm with configured
8# AlgTools.
9#
10# The configuration XML file specifies each AlgTool's immediate
11# data provider. This python module uses this information to build
12# A directed acyclic graph (DAG), to assign data handle keys in such a way
13# that the DAG is implemented.
14#
15# The GlobalSim Algorithm has two types of AlgTools: TOBwriters and
16# TIPwriters. TOBWriters read in and write out various TOB types
17#
18# TIPwriters read in TOBS, and are used to update the TIP word, which is a
19# bitset which is sent to the CTP.
20#
21# For now, we assume we can run all the TIPwriter tools after we
22# have run all the TOBwriter tools.
23#
24
25# Data structures
26# ----------------
27#
28# alg_ida: dictionary str:int keys are AlgNames: class/instance name.
29#
30# G: A digraph providing parent child relations. Nodes are int alg ids
31#
32# read_handles: dictionary str: (str: str).
33# Outer dictionary key: AlgTool class name
34# Inner dictionary key: standardised read handle name used by config file
35# value: python name of the read handle.
36#
37# write_handles: dictionary str:str key: AlgTool class name.
38# Value: python name of the write handle. Wa allow only one write handle
39# For GlobalSim connections.
40#
41# alg_tools: dictionary {str : int} key = alg full name, int = alg_id
42#
43# input_slots: dict{int: dict{int, str}} outter dict key:parent int id
44# inner dict key: child int id innner dict value: generic slot str eg 'in9'
45
46from AthenaConfiguration.ComponentFactory import CompFactory
47from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
48
49from AthenaCommon.Logging import logging
50logger = logging.getLogger(__name__)
51from AthenaCommon.Constants import DEBUG
52
53from GlobalSimulation.Digraph import Digraph
54from GlobalSimulation.graphAlgs import Topological
55
56import xml.etree.ElementTree as ET
57import os
58from collections import defaultdict
59
60# The following DataHandle look up tables will be removed in
61# future developments.
62
63#The entry to the outer dictiones is the name
64# of a GlobalSim AlgTool
65#
66# for read handles, the value is itself a dictionary with the key being
67# the giving the name referred to by the configuratioh file, and the
68# value of the inner dictionary begin the python name of the read handle.
69# This mecahnism removes the previous existing limits of the number
70# of child AlgTools a parent AlgTool may have.
71
72read_handles = {
73 'eFexCvtrAlgTool': {'in0': 'eFexEMRoIKey'},
74 'gFexRhoCvtrAlgTool': {'in0': 'gFexJetRoIKey'},
75 'Egamma1BDTAlgTool': {'in0': 'LArNeighborhoodTOBContainerReadKey'},
76 'GlobalCellTowerAlgTool': {'in0': 'GlobalLArCellsKey'},
77 'GlobalJet1AlgTool': {'in0': 'GlobalCellTowersKey'},
78 'eEmMultAlgTool': {'in0': 'eEmTOBs'},
79 'eEmEg1BDTMultAlgTool': {'in0': 'eEmEg1BDTTOBContainerKey'},
80 'CommonMultAlgTool': {'in0': 'CommonTOBsKey'},
81 }
82
83write_handles = {
84 'eFexCvtrAlgTool': 'eEmTOBs',
85 'gFexRhoCvtrAlgTool': 'gFexRhoTOBs',
86 'Egamma1BDTAlgTool': 'eEmEg1BDTTOBContainerKey',
87 'GlobalCellTowerAlgTool': 'GlobalCellTowersKey',
88 'GlobalJet1AlgTool': 'GlobalJet1JetsKey',
89}
90
92 dump=False,
93 fn=None,
94 algName='GlobalSimTestAlg',
95 OutputLevel=DEBUG):
96
97 logger.setLevel(OutputLevel)
98 cfg = ComponentAccumulator()
99
100 if fn is None:
101 fn = os.environ.get('GS_CFG_FILE', None)
102 if fn is None:
103 logger.error('Please set export environment variable GS_CFG_FILE'
104 ' with the name of a GlobalSim config xml file')
105 return cfg
106
107 logger.info('GlobalSim local config, cfg file: ' + fn)
108
109 def str_id(toolEl):
110 """ obtain a string id for each AlgTool"""
111
112 a_class = toolEl.attrib['class']
113 a_name = toolEl.attrib['name']
114 return '/'.join((a_class, a_name))
115
116 def classname_from_fullname(fullname):
117 return fullname.split('/')[0]
118
119
120 def configure_algtool(toolEl):
121 """
122 Set the AlgTool properties from configure file information.
123 Datahandles are not processed here.
124 """
125
126 a_class = toolEl.attrib['class']
127 a_name = toolEl.attrib['name']
128 prop_names = []
129 factory = getattr(CompFactory.GlobalSim, a_class)
130 tool = factory(a_name)
131
132 type_factories = {'int': int,
133 'float': float,
134 'str': str}
135
136 for prop in toolEl.iter('property'):
137 name = prop.attrib['name']
138 value = prop.attrib['value']
139 ptype = prop.attrib.get("type", None)
140 if ptype is not None:
141 value = type_factories[ptype](value)
142 setattr(tool, name, value)
143 prop_names.append(name)
144
145 logger.debug('configure_algtool: ' + str(tool))
146 return tool
147
148
149 def fill_alg_ids(root):
150 """
151 Assign an index to each AlgTool instance specified by
152 the configuration file.
153
154 Return this information in a dictionary.
155 """
156
157 alg_ids = {}
158 alg_ind = 1
159
160 alg_tools = {}
161
162 for toolType in ('TOBWriters', 'TIPWriters'):
163 for writerEl in root.iter(toolType):
164 for toolEl in writerEl.iter('AlgTool'):
165 f_name = str_id(toolEl)
166 if f_name in alg_ids:
167 raise AssertionError('Algorithm duplicated in ' + fn)
168 alg_ids[f_name] = alg_ind
169 alg_tools[alg_ind] = (configure_algtool(toolEl), toolType)
170 alg_ind += 1
171 return alg_ids, alg_tools, alg_ind
172
173 def fill_input_slots(root, alg_ids):
174 """
175 Create a dictionary
176 {par_alg_id:int || {input_slot:str || child_alg_id:int}}
177
178 Where is a generic name for the input location, eg "in0", and
179 is used by the config file. The actual location is
180 currently obtained using the read_handles dictionary at the top
181 of this file.
182 """
183
184 input_slots = defaultdict(dict)
185
186 for toolEl in root.iter('AlgTool'):
187 par_full_name = str_id(toolEl)
188 par_id = alg_ids[par_full_name]
189
190 for childEl in toolEl.iter('child'):
191 child_full_name = str_id(childEl)
192 child_id = alg_ids[child_full_name]
193 slot = childEl.attrib.get('slot', None)
194 if slot is None:
195 msg = ['No slot information for ',
196 par_full_name,
197 ' child ',
198 child_full_name]
199 raise AssertionError(' '.join(msg))
200
201 input_slots[par_id][child_id] = slot
202
203 return input_slots
204
205
206 def make_digraph(alg_ids, V):
207 """
208 Construct an Algtool Digraph.
209
210 Obtain parent child relations from the config XML file.
211
212 The graph knows only about
213 the AlgTool insances's indices, and so works from a
214 dictionary that associates the AlgoTool name (string) to its
215
216 ineger index.
217 """
218
219
220 logger.debug('make_digraph alg_ids: ', alg_ids)
221 logger.debug('make_digraph: V ' + str(V))
222
223 # Create an empty DAG
224 G = Digraph(V)
225
226
227 for toolType in ('TOBWriters', 'TIPWriters'):
228
229 for writerEl in root.iter(toolType):
230 for toolEl in writerEl.iter('AlgTool'):
231 f_name = str_id(toolEl)
232 logger.debug('make_digraph: toolType ' + toolType +
233 ' ' + f_name)
234
235
236 par_id = alg_ids[f_name]
237
238 for childEl in toolEl.iter('child'):
239 f_c_name = str_id(childEl)
240 if f_c_name not in alg_ids:
241 raise AssertionError('child ' + f_c_name +
242 ' not in ' + fn)
243
244 G.addEdge(par_id, alg_ids[f_c_name])
245
246 R = G.reverse()
247 roots = [n for n in range(R.V) if not R.adj(n) and n != 0]
248 return G, roots
249
250 def set_SGout_locations(tools):
251 """
252 Set the StoreGate locations to be written to. As the
253 same Algorithm may have > 1 instance, ensure that the
254 write locations differ.
255 """
256 # set the Storegate location each tool writes to.
257
258 out_index = 0
259 for indx, (tool, tooltype) in tools.items():
260 class_name = tool.__class__.__name__
261 handle = write_handles.get(class_name, None)
262
263 if handle is not None:
264 setattr(tool, handle, 'GlobalSim_'+str(out_index))
265 out_index += 1
266
267
268 def set_SGin_locations(tools, alg_ids, input_slots, G):
269 """"
270
271 Set locations read from by each Algorithm according to the
272 call graph G.
273
274 NOTE: currently we assume a tool has one output location
275 and one input location, which allows only "narrow chains".
276 This will be extended to allow multiple children in the near future.
277
278 alg_ids is a str:int map
279 tools is a int : (tool, toolType) map
280 """
281
282 for nid in range(1, G.V):
283 parent = tools[nid][0]
284 child_ids = G.adj(nid)
285 if len(child_ids) == 0:
286 continue
287
288 for child_id in child_ids:
289 slot = input_slots[nid][child_id] # eg 'in0'
290 child_tool = tools[child_id][0] # tools.values: (tool, tooltype)
291 w_handle_name = write_handles[child_tool.__class__.__name__]
292 read_handle = read_handles[parent.__class__.__name__][slot]
293 read_from = getattr(child_tool, w_handle_name)
294 setattr(parent, read_handle, read_from)
295
296
297 # parse the config XML file
298
299 tree = ET.parse(fn)
300 root = tree.getroot()
301
302 # extract tool information from the XML file
303 # alg_ids: str : int
304 # alg_tools: int : (tool, toolType), toolType is a string
305 # V number of vertices (including unused root vertex = 0
306 alg_ids, alg_tools, V= fill_alg_ids(root)
307 input_slots = fill_input_slots(root, alg_ids)
308 G, roots = make_digraph(alg_ids, V)
309 logger.debug('call graph ' + str(G))
310
311 topological = Topological(G, roots=roots)
312 if not topological.isDAG(): raise AssertionError(
313 'Call graph is not a DAG')
314
315 index_order = topological.order()
316 set_SGout_locations(tools=alg_tools)
317 set_SGin_locations(tools=alg_tools, alg_ids = alg_ids,
318 input_slots=input_slots, G=G)
319
320 logger.debug('DAG: ' + str(G))
321 logger.debug('order: ' + str(index_order))
322
323
324 toolType = 'TOBWriters'
325 orderedTOBWriters = [alg_tools[i][0] for i in index_order
326 if alg_tools[i][1] == toolType]
327
328 msg = [str(tool) for tool in orderedTOBWriters]
329 logger.debug(toolType + ': ' + '\n'.join(msg))
330
331
332 toolType = 'TIPWriters'
333 orderedTIPWriters = [alg_tools[i][0] for i in index_order
334 if alg_tools[i][1] == toolType]
335
336 tools = [alg_tools[i][0] for i in index_order]
337 msg = ['GlobalSim tool IO dump:']
338 for tool in tools:
339
340 tname = tool.__class__.__name__ + '/' + tool.name
341
342 logger.debug('GS tool name ' + tname)
343 logger.debug('GS r_handle str(tool)' , str(tool))
344
345 handle_name = read_handles.get(tool.__class__.__name__, None)
346 if handle_name is None:
347 logger.debug('GS r_handle not in table')
348 else:
349
350 logger.debug('GS r_handle from table: ', handle_name)
351 logger.debug('GS r_handle loc from tool: ' + tname + ' ' +
352 str(getattr(tool, handle_name['in0'])))
353
354 handle_name = write_handles.get(tool.__class__.__name__, None)
355 if handle_name is None:
356 logger.debug('GS w_handle not in table')
357 else:
358 logger.debug('GS w_handle from table: ' + handle_name)
359 logger.debug('GS w_handle loc from tool: ' + tname + ' ' +
360 str(getattr(tool, handle_name)))
361
362
363 alg = CompFactory.GlobalSim.GlobalSimulationAlg(algName)
364 alg.globalsim_algs = orderedTOBWriters
365 alg.TIPwriters = orderedTIPWriters
366 alg.OutputLevel = OutputLevel
367 alg.enableDumps = dump
368
369
370 from TrigCaloRec.TrigCaloRecConfig import hltCaloCellSeedlessMakerCfg
371 cfg.merge(hltCaloCellSeedlessMakerCfg(flags, roisKey=''))
372
373 cfg.addEventAlgo(alg)
374 return cfg
GlobalSimulationAlgCfg(flags, dump=False, fn=None, algName='GlobalSimTestAlg', OutputLevel=DEBUG)