ATLAS Offline Software
Loading...
Searching...
No Matches
trigbs_dumpHLTContentInBS_run3.py
Go to the documentation of this file.
1#!/usr/bin/env python
2#
3# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
4#
5
6'''
7Dump content of the HLT result and HLT related details from the event header
8'''
9
10import argparse
11import eformat
12from datetime import datetime
13from TrigByteStreamTools import hltResultMT
14
15from AthenaCommon.Logging import logging
16log = logging.getLogger('dumpHLTContentInBS')
17
18
20 parser = argparse.ArgumentParser(usage='%(prog)s [options] files',
21 description=__doc__)
22 parser.add_argument('files',
23 metavar='FILE', nargs='+',
24 help='RAW file to inspect')
25 parser.add_argument('-n', '--events',
26 metavar='N', action='store', type=int,
27 help='Process N events')
28 parser.add_argument('-s', '--skip',
29 metavar='N', action='store', type=int,
30 help='Skip N events')
31 parser.add_argument('--ctp', nargs="?", metavar="MODULE_ID", default=False, const=1,
32 help="CTP ROB details of ROB with MODULE_ID [default=%(const)s]")
33 parser.add_argument('--l1',
34 action='store_true', default=False,
35 help='L1 trigger bits (from event header)')
36 parser.add_argument('--ef', '--hlt',
37 action='store_true', default=False,
38 help='EF/HLT trigger bits (from event header)')
39 parser.add_argument('--stag',
40 action='store_true', default=False,
41 help='stream tag')
42 parser.add_argument('--efres', '--hltres',
43 action='store_true', default=False,
44 help='details of EF/HLT ROB payload')
45 parser.add_argument('--sizes',
46 action='store_true', default=False,
47 help='dump info about EDM sizes per result; implies --hltres')
48 parser.add_argument('--deserialize',
49 action='store_true', default=False,
50 help='deserialize EDM collections (slow!); implies --hltres')
51 parser.add_argument('--sizeSummary',
52 action='store_true', default=False,
53 help='dump summary info about EDM sizes at the end')
54 parser.add_argument('--confKeys',
55 action='store_true', default=False,
56 help='dump TrigConfKeys stored in the events; implies --hltres')
57 parser.add_argument('--runtimeMetadata',
58 action='store_true', default=False,
59 help='dump HLT_RuntimeMetadata stored in the events; implies --hltres')
60 return parser
61
62
63def decodeTriggerBits(words, num_sets, base=32):
64 assert len(words) % num_sets == 0
65 n_words_per_set = len(words) // num_sets
66 result = []
67 for iset in range(num_sets):
68 words_in_set = words[iset*n_words_per_set:(iset+1)*n_words_per_set]
69 bit_indices = []
70 for iw in range(len(words_in_set)):
71 bit_indices.extend([base*iw+i for i in range(base) if words_in_set[iw] & (1 << i)])
72 result.append(bit_indices)
73 return result
74
75
76def header_info(event):
77 '''Return a string with basic information about the event from the header'''
78
79 info_str = 'RunNumber: {:6d}, '.format(event.run_no())
80 info_str += 'LB: {:4d}, '.format(event.lumi_block())
81 info_str += 'Global_ID: {:10d}, '.format(event.global_id())
82 info_str += 'LVL1_ID: {:10d}, '.format(event.lvl1_id())
83 info_str += 'BC_ID: {:4d}, '.format(event.bc_id())
84 info_str += 'Time: {}, '.format(datetime.fromtimestamp(event.bc_time_seconds()).isoformat())
85 info_str += 'TT: 0x{:2x}, '.format(event.lvl1_trigger_type())
86 info_str += 'Status: ' + decode_status(event)
87 return info_str
88
89
90def decode_status(fragment):
91 '''Return event/ROB fragment status and string representation'''
92
93 if not fragment.status():
94 return 'None'
95
96 info_str = '[' + ', '.join('0x%x' % s for s in fragment.status()) + ']'
97
98 # First word:
99 status = eformat.helper.Status(fragment.status()[0])
100 info_str += ''.join(f' {item.name}' for code, item in eformat.helper.GenericStatus.values.items()
101 if status.generic() & code)
102
103 # The specific bits of the first word are only relevant for the full event:
104 if isinstance(fragment, eformat.FullEventFragment):
105 info_str += ''.join(f' {item.name}' for code, item in eformat.helper.FullEventStatus.values.items()
106 if status.specific() & code)
107
108 # HLT-specific event status:
109 if len(fragment.status())>1:
110 import cppyy
111 cppyy.gbl.HLT.OnlineErrorCode # trigger auto-loading via enum
112 info_str += ' '+ cppyy.gbl.HLT.OnlineErrorCodeToString(fragment.status()[1]).data()
113
114 return info_str
115
116
117def ctp_rob(event, module_id=1):
118 '''Return a string with information from the CTP ROB'''
119
120 ctp_robs = [rob for rob in event \
121 if rob.source_id().subdetector_id() == eformat.helper.SubDetector.TDAQ_CTP \
122 and rob.source_id().module_id()==module_id]
123
124 if len(ctp_robs)==0:
125 log.warning('No CTP ROB found')
126 return ''
127
128 assert len(ctp_robs)==1
129 rob = ctp_robs[0]
130
131 from TrigByteStreamTools import CTPfragment
132 x = CTPfragment.getExtraPayloadObject(rob)
133 info_str = (f'ROB 0x{rob.source_id().code():x}, '
134 f'L1ID {event.lvl1_id():10d}, '
135 f'LB {event.lumi_block():4d}, '
136 f'Version {CTPfragment.ctpFormatVersion(rob)}, '
137 f'Bunch {CTPfragment.lvl1AcceptBunch(rob)}, '
138 f'HLT counter: {CTPfragment.hltCounter(rob):3d}, '
139 f'Payload #{CTPfragment.numberHltExtraPayloadWords(rob)} '
140 f'{CTPfragment.hltExtraPayloadWords(rob)} '
141 f'L1PSK {x.getL1PSK()} BGK {x.getBGK()}')
142 folderUpdates = CTPfragment.getFolderUpdates(x)
143 if len(folderUpdates)>0:
144 info_str += ' COOLUPD '+''.join([f'[{f.second.folderIndex}, {f.second.lumiBlock}]' for f in folderUpdates])
145 return info_str
146
147
148def lvl1_bits(event):
149 '''Return a string with information about LVL1 bits (IDs of items passed at TBP, TAP, TAV)'''
150
151 info = event.lvl1_trigger_info()
152 lvl1_bits = decodeTriggerBits(info, 3) # TBP, TAP, TAV
153 info_str = 'L1 CTP IDs - TBP: {:s}\n'.format(str(lvl1_bits[0]))
154 info_str += 'L1 CTP IDs - TAP: {:s}\n'.format(str(lvl1_bits[1]))
155 info_str += 'L1 CTP IDs - TAV: {:s}'.format(str(lvl1_bits[2]))
156 return info_str
157
158
159def hlt_bits(event, version=(1,1)):
160 '''Return a string with information about passed chain IDs at HLT'''
161
162 # Version 1.0 has {passed, prescaled, rerun}, 1.1 and later only {passed, prescaled}
163 num_sets = 3 if version[0] < 1 or version==(1,0) else 2
164
165 info = event.event_filter_info()
166 hlt_bits = decodeTriggerBits(info, num_sets)
167 info_str = 'HLT passed chain IDs: {:s}'.format(str(hlt_bits[0]))
168 info_str += '\nHLT prescaled chain IDs: {:s}'.format(str(hlt_bits[1]))
169 if num_sets==3:
170 info_str += '\nHLT rerun chain IDs: {:s}'.format(str(hlt_bits[2]))
171 return info_str
172
173
174def stream_tags(event):
175 def hex_list(nums):
176 return '[' + ', '.join([hex(num) for num in nums]) + ']'
177 info_str = 'Stream Tags:'
178 for st in event.stream_tag():
179 info_str += '\n-- {}_{}'.format(st.type, st.name)
180 robs = list(st.robs)
181 dets = list(st.dets)
182 if len(robs) == 0 and len(dets) == 0:
183 info_str += ' - Full Event Building'
184 else:
185 info_str += ' - Partial Event Building, robs={}, dets={}'.format(hex_list(robs), hex_list(dets))
186 return info_str
187
188
189def hlt_rod_minor_version(rob):
190 version = rob.rod_version()
191 minor_version = version.minor_version()
192 minor_version_M = (minor_version & 0xff00) >> 8
193 minor_version_L = minor_version & 0xff
194 return (minor_version_M, minor_version_L)
195
196
197def hlt_rod_minor_version_from_event(event):
198 for rob in event.children():
199 if rob.source_id().subdetector_id() == eformat.helper.SubDetector.TDAQ_HLT:
200 return hlt_rod_minor_version(rob)
201
202
203def hlt_result(event, print_sizes=False, deserialize=False, conf_keys=False, runtime_metadata=False):
204 num_hlt_robs = 0
205 info_str = ""
206 for rob in event.children():
207 if rob.source_id().subdetector_id() != eformat.helper.SubDetector.TDAQ_HLT:
208 continue
209 num_hlt_robs += 1
210 version = hlt_rod_minor_version(rob)
211 info_str += '\n-- {:s} SourceID: {:s}, Version: {:s}, Size: {:d} bytes, Status: {:s}'.format(
212 rob.__class__.__name__,
213 rob.source_id().human(),
214 f'{version[0]:d}.{version[1]:d}',
215 rob.fragment_size_word()*4,
216 decode_status(rob)
217 )
218 if print_sizes or deserialize or conf_keys or runtime_metadata:
219 if version[0] < 1:
220 raise RuntimeError('Cannot decode data from before Run 3, HLT ROD minor version needs to be >= 1.0')
221 skip_payload = not conf_keys and not runtime_metadata and not deserialize
222 collections = hltResultMT.get_collections(rob, skip_payload=skip_payload)
223 if conf_keys:
224 conf_list = [c for c in collections if 'xAOD::TrigConfKeys_v' in c.name_persistent]
225 conf_available = False
226 for conf in conf_list:
227 conf_obj = conf.deserialise()
228 if not conf_obj:
229 continue
230 conf_available = True
231 info_str += '\n---- {:s}#{:s} SMK: {:d}, L1PSK: {:d}, HLTPSK: {:d}'.format(
232 conf.name_persistent, conf.name_key,
233 conf_obj.smk(), conf_obj.l1psk(), conf_obj.hltpsk())
234 if not conf_available:
235 info_str += '\n---- TrigConfKeys unavailable in this ROB'
236 if runtime_metadata:
237 decorations = ['hostname']
238 meta_list = [c for c in collections if any([f'RuntimeMetadataAux.{decor}' in c.name() for decor in decorations])]
239 meta_available = False
240 for meta in meta_list:
241 meta_obj = meta.deserialise()
242 if not meta_obj:
243 continue
244 meta_available = True
245 info_str += '\n---- RuntimeMetadata {:s}: {:s}'.format(
246 meta.name_key, meta_obj.at(0))
247 if not meta_available:
248 info_str += '\n---- RuntimeMetadata unavailable in this ROB'
249 if print_sizes or deserialize:
250 for coll in collections:
251 indent = 4 if not coll.is_xAOD_decoration() else 6
252 info_str += '\n{:s} {:s}'.format('-'*indent, str(coll))
253 if deserialize and (coll_obj := coll.deserialise()) is not None:
254 try:
255 length = coll_obj.size() # all collections should have this method
256 except Exception:
257 length = None
258 if length is not None:
259 info_str += f' ({length} element' + ('s)' if length!=1 else ')')
260 info_str += '\n{:s} {:s}'.format(' '*indent, str(coll_obj))
261
262
263 info_str = 'Found {:d} HLT ROBs'.format(num_hlt_robs) + info_str
264 return info_str
265
266
267def size_summary(events):
268 data = {}
269 # Format of the data dictionary:
270 # {
271 # 0: {
272 # 'total': 123,
273 # 'collections': {
274 # 'collA': 22,
275 # 'collB': 35
276 # }
277 # }
278 # }
279 for event in events:
280 for rob in event.children():
281 if rob.source_id().subdetector_id() != eformat.helper.SubDetector.TDAQ_HLT:
282 continue
283 version = hlt_rod_minor_version(rob)
284 if version[0] < 1:
285 raise RuntimeError('Cannot decode data from before Run 3, HLT ROD minor version needs to be >= 1.0')
286 module = rob.source_id().module_id()
287 if module not in data.keys():
288 data[module] = {'total': 0}
289 data[module]['total'] += rob.fragment_size_word()*4
290 if 'collections' not in data[module].keys():
291 data[module]['collections'] = {}
292 for coll in hltResultMT.get_collections(rob, skip_payload=True):
293 coll_name = coll.name()
294 if coll_name in data[module]['collections'].keys():
295 data[module]['collections'][coll_name] += coll.size_bytes
296 else:
297 data[module]['collections'][coll_name] = coll.size_bytes
298 info_str = '='*20 + '\nSize summary:\n' + '='*20
299 for module in data.keys():
300 module_size = data[module]['total']
301 module_size_per_evt = module_size / float(len(events))
302 info_str += '\n-- TDAQ_HLT module {:d} total size {:d} bytes, {:.3f} bytes/event'.format(
303 module, module_size, module_size_per_evt)
304 sorted_colls = sorted(data[module]['collections'].items(),
305 key=lambda kv: kv[1],
306 reverse=True)
307 max_name_len = len(max(data[module]['collections'].keys(), key=len))
308 max_name_len = min(120, max_name_len) # Make a reasonable limit to avoid line breaks
309 for coll_name, coll_size in sorted_colls:
310 coll_size_per_evt = coll_size / float(len(events))
311 info_str += '\n---- {:{width}} {:12d} B {:12.3f} B/ev'.format(
312 coll_name, coll_size, coll_size_per_evt, width=max_name_len)
313 return info_str
314
315def dump_info(bsfile, args):
316 log.info('Opening %s', bsfile)
317 input = eformat.istream(bsfile)
318 offset = args.skip if args.skip else 0
319 max_events = min(args.events, len(input)) if args.events else len(input)
320 event_count = 0
321 events = []
322 version = None
323
324 # Loop over events
325 for event in input:
326 event_count += 1
327 if event_count <= offset:
328 continue
329 if event_count > offset+max_events:
330 break
331 events.append(event)
332
333 if version is None:
334 version = hlt_rod_minor_version_from_event(event)
335
336 # Print header info
337 print('{sep:s} Event: {:{width}d}, {:s} {sep:s}'.format(
338 event_count, header_info(event),
339 sep='===', width=len(str(max_events))))
340
341 # Print CTP ROB
342 if args.ctp is not False:
343 print(ctp_rob(event, 1))
344
345 # Print L1/L2/HLT bits
346 if args.l1:
347 print(lvl1_bits(event))
348 if args.ef and version is not None:
349 print(hlt_bits(event, version))
350
351 # Print Stream Tags
352 if args.stag:
353 print(stream_tags(event))
354
355 # HLT Result
356 if args.efres or args.sizes or args.deserialize or args.confKeys or args.runtimeMetadata:
357 print(hlt_result(event, args.sizes, args.deserialize, args.confKeys, args.runtimeMetadata))
358
359 # Size summary (after the loop over events)
360 if args.sizeSummary:
361 print(size_summary(events))
362
363
364if '__main__' in __name__:
365 args = get_parser().parse_args()
366 for f in args.files:
367 dump_info(f, args)