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