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 'Egamma1BDTAlgTool': {'in0': 'LArNeighborhoodTOBContainerReadKey'},
75 'eEmMultAlgTool': {'in0': 'eEmTOBs'},
76 }
77
78write_handles = {
79 'eFexCvtrAlgTool': 'eEmTOBs',
80 'Egamma1BDTAlgTool': 'eEmEg1BDTTOBContainerKey',
81}
82
84 dump,
85 algName = 'GlobalSimTestAlg',
86 OutputLevel=DEBUG):
87
88 logger.setLevel(OutputLevel)
89 cfg = ComponentAccumulator()
90
91 fn = os.environ.get('GS_CFG_FILE', None)
92 if fn is None:
93 logger.error('Please set export environment variable GS_CFG_FILE'\
94 'with the name of a GloblSim config xml file')
95
96
97 logger.info('GlobalSim local config, cfg file:' + fn)
98
99
100 def str_id(toolEl):
101 """ obtain a string id for each AlgTool"""
102
103 a_class = toolEl.attrib['class']
104 a_name = toolEl.attrib['name']
105 return '/'.join((a_class, a_name))
106
107 def classname_from_fullname(fullname):
108 return fullname.split('/')[0]
109
110
111 def configure_algtool(toolEl):
112 """
113 Set the AlgTool properties from configure file information.
114 Datahandles are not processed here.
115 """
116
117 a_class = toolEl.attrib['class']
118 a_name = toolEl.attrib['name']
119 prop_names = []
120 factory = getattr(CompFactory.GlobalSim, a_class)
121 tool = factory(a_name)
122
123 type_factories = {'int': int,
124 'float': float,
125 'str': str}
126
127 for prop in toolEl.iter('property'):
128 name = prop.attrib['name']
129 value = prop.attrib['value']
130 ptype = prop.attrib.get("type", None)
131 if ptype is not None:
132 value = type_factories[ptype](value)
133 setattr(tool, name, value)
134 prop_names.append(name)
135
136 logger.debug('configure_algtool: ' + str(tool))
137 return tool
138
139
140 def fill_alg_ids(root):
141 """
142 Assign an index to each AlgTool instance specified by
143 the configuration file.
144
145 Return this information in a dictionary.
146 """
147
148 alg_ids = {}
149 alg_ind = 1
150
151 alg_tools = {}
152
153 for toolType in ('TOBWriters', 'TIPWriters'):
154 for writerEl in root.iter(toolType):
155 for toolEl in writerEl.iter('AlgTool'):
156 f_name = str_id(toolEl)
157 if f_name in alg_ids:
158 raise AssertionError('Algorithm duplicated in ' + fn)
159 alg_ids[f_name] = alg_ind
160 alg_tools[alg_ind] = (configure_algtool(toolEl), toolType)
161 alg_ind += 1
162 return alg_ids, alg_tools, alg_ind
163
164 def fill_input_slots(root, alg_ids):
165 """
166 Create a dictionary
167 {par_alg_id:int || {input_slot:str || child_alg_id:int}}
168
169 Where is a generic name for the input location, eg "in0", and
170 is used by the config file. The actual location is
171 currently obtained using the read_handles dictionary at the top
172 of this file.
173 """
174
175 input_slots = defaultdict(dict)
176
177 for toolEl in root.iter('AlgTool'):
178 par_full_name = str_id(toolEl)
179 par_id = alg_ids[par_full_name]
180
181 for childEl in toolEl.iter('child'):
182 child_full_name = str_id(childEl)
183 child_id = alg_ids[child_full_name]
184 slot = childEl.attrib.get('slot', None)
185 if slot is None:
186 msg = ['No slot information for ',
187 par_full_name,
188 ' child ',
189 child_full_name]
190 raise AssertionError(' '.join(msg))
191
192 input_slots[par_id][child_id] = slot
193
194 return input_slots
195
196
197 def make_digraph(alg_ids, V):
198 """
199 Construct an Algtool Digraph.
200
201 Obtain parent child relations from the config XML file.
202
203 The graph knows only about
204 the AlgTool insances's indices, and so works from a
205 dictionary that associates the AlgoTool name (string) to its
206
207 ineger index.
208 """
209
210
211 logger.debug('make_digraph alg_ids: ', alg_ids)
212 logger.debug('make_digraph: V ' + str(V))
213
214 # Create an empty DAG
215 G = Digraph(V)
216
217
218 for toolType in ('TOBWriters', 'TIPWriters'):
219
220 for writerEl in root.iter(toolType):
221 for toolEl in writerEl.iter('AlgTool'):
222 f_name = str_id(toolEl)
223 logger.debug('make_digraph: toolType ' + toolType +
224 ' ' + f_name)
225
226
227 par_id = alg_ids[f_name]
228
229 for childEl in toolEl.iter('child'):
230 f_c_name = str_id(childEl)
231 if f_c_name not in alg_ids:
232 raise AssertionError('child ' + f_c_name +
233 ' not in ' + fn)
234
235 G.addEdge(par_id, alg_ids[f_c_name])
236
237 R = G.reverse()
238 roots = [n for n in range(R.V) if not R.adj(n) and n != 0]
239 return G, roots
240
241 def set_SGout_locations(tools):
242 """
243 Set the StoreGate locations to be written to. As the
244 same Algorithm may have > 1 instance, ensure that the
245 write locations differ.
246 """
247 # set the Storegate location each tool writes to.
248
249 out_index = 0
250 for indx, (tool, tooltype) in tools.items():
251 class_name = tool.__class__.__name__
252 handle = write_handles.get(class_name, None)
253
254 if handle is not None:
255 setattr(tool, handle, 'GlobalSim_'+str(out_index))
256 out_index += 1
257
258
259 def set_SGin_locations(tools, alg_ids, input_slots, G):
260 """"
261
262 Set locations read from by each Algorithm according to the
263 call graph G.
264
265 NOTE: currently we assume a tool has one output location
266 and one input location, which allows only "narrow chains".
267 This will be extended to allow multiple children in the near future.
268
269 alg_ids is a str:int map
270 tools is a int : (tool, toolType) map
271 """
272
273 for nid in range(1, G.V):
274 parent = tools[nid][0]
275 child_ids = G.adj(nid)
276 if len(child_ids) == 0:
277 continue
278
279 for child_id in child_ids:
280 slot = input_slots[nid][child_id] # eg 'in0'
281 child_tool = tools[child_id][0] # tools.values: (tool, tooltype)
282 w_handle_name = write_handles[child_tool.__class__.__name__]
283 read_handle = read_handles[parent.__class__.__name__][slot]
284 read_from = getattr(child_tool, w_handle_name)
285 setattr(parent, read_handle, read_from)
286
287
288 # parse the config XML file
289
290 tree = ET.parse(fn)
291 root = tree.getroot()
292
293 # extract tool information from the XML file
294 # alg_ids: str : int
295 # alg_tools: int : (tool, toolType), toolType is a string
296 # V number of vertices (including unused root vertex = 0
297 alg_ids, alg_tools, V= fill_alg_ids(root)
298 input_slots = fill_input_slots(root, alg_ids)
299 G, roots = make_digraph(alg_ids, V)
300 logger.debug('call graph ' + str(G))
301
302 topological = Topological(G, roots=roots)
303 if not topological.isDAG(): raise AssertionError(
304 'Call graph is not a DAG')
305
306 index_order = topological.order()
307 set_SGout_locations(tools=alg_tools)
308 set_SGin_locations(tools=alg_tools, alg_ids = alg_ids,
309 input_slots=input_slots, G=G)
310
311 logger.debug('DAG: ' + str(G))
312 logger.debug('order: ' + str(index_order))
313
314
315 toolType = 'TOBWriters'
316 orderedTOBWriters = [alg_tools[i][0] for i in index_order
317 if alg_tools[i][1] == toolType]
318
319 msg = [str(tool) for tool in orderedTOBWriters]
320 logger.debug(toolType + ': ' + '\n'.join(msg))
321
322
323 toolType = 'TIPWriters'
324 orderedTIPWriters = [alg_tools[i][0] for i in index_order
325 if alg_tools[i][1] == toolType]
326
327 tools = [alg_tools[i][0] for i in index_order]
328 msg = ['GlobalSim tool IO dump:']
329 for tool in tools:
330
331 tname = tool.__class__.__name__ + '/' + tool.name
332
333 logger.debug('GS tool name ' + tname)
334 logger.debug('GS r_handle str(tool)' , str(tool))
335
336 handle_name = read_handles.get(tool.__class__.__name__, None)
337 if handle_name is None:
338 logger.debug('GS r_handle not in table')
339 else:
340
341 logger.debug('GS r_handle from table: ', handle_name)
342 logger.debug('GS r_handle loc from tool: ' + tname + ' ' +
343 str(getattr(tool, handle_name['in0'])))
344
345 handle_name = write_handles.get(tool.__class__.__name__, None)
346 if handle_name is None:
347 logger.debug('GS w_handle not in table')
348 else:
349 logger.debug('GS w_handle from table: ' + handle_name)
350 logger.debug('GS w_handle loc from tool: ' + tname + ' ' +
351 str(getattr(tool, handle_name)))
352
353
354 alg = CompFactory.GlobalSim.GlobalSimulationAlg(algName)
355 alg.globalsim_algs = orderedTOBWriters
356 alg.TIPwriters = orderedTIPWriters
357 alg.OutputLevel = OutputLevel
358 alg.enableDumps = dump
359
360
361 from TrigCaloRec.TrigCaloRecConfig import hltCaloCellSeedlessMakerCfg
362 cfg.merge(hltCaloCellSeedlessMakerCfg(flags, roisKey=''))
363
364 cfg.addEventAlgo(alg)
365 return cfg
GlobalSimulationAlgCfg(flags, dump, algName='GlobalSimTestAlg', OutputLevel=DEBUG)