ATLAS Offline Software
Loading...
Searching...
No Matches
TriggerEDM.py
Go to the documentation of this file.
1# Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2
3# ********************* All Tools/Functions for the TriggerEDM **********************
4# Keeping all functions from the original TriggerEDM.py (Run 2 EDM) in this file
5# with this name to not break backwards compatibility
6# Where possible, functions will be adapted to also work with Run 3, they will then be moved
7# to the Run 3 section
8# ***********************************************************************************
9
10from TrigEDMConfig.TriggerEDMRun1 import TriggerL2List,TriggerEFList,TriggerResultsRun1List
11from TrigEDMConfig.TriggerEDMRun2 import TriggerResultsList,TriggerLvl1List,TriggerIDTruth,TriggerHLTList,EDMDetails,EDMLibraries,TriggerL2EvolutionList,TriggerEFEvolutionList
12from TrigEDMConfig.TriggerEDMRun3 import TriggerHLTListRun3,varToRemoveFromAODSLIM,EDMDetailsRun3,getSafeEDMInsertPosition
13from TrigEDMConfig.TriggerEDMRun4 import TriggerHLTListRun4
14from TrigEDMConfig.TriggerEDMDefs import allowTruncation
15from CLIDComps.clidGenerator import clidGenerator
16from collections import defaultdict
17import itertools
18import re
19
20from AthenaCommon.Logging import logging
21log = logging.getLogger('TriggerEDM')
22
23#************************************************************
24#
25# For Run 3 and Run 4
26#
27#************************************************************
28
29# ------------------------------------------------------------
30# AllowedOutputFormats
31# ------------------------------------------------------------
32AllowedOutputFormats = ['BS', 'ESD', 'AODFULL', 'AODSLIM', 'AODBLSSLIM' ]
33from TrigEDMConfig import DataScoutingInfo
34AllowedOutputFormats.extend(DataScoutingInfo.getAllDataScoutingIdentifiers())
35
36_allowedEDMPrefixes = ['HLT_', 'L1_', 'LVL1']
37def recordable( arg, runVersion=3 ):
38 """
39 Verify that the name is in the list of recorded objects and conform to the name convention
40
41 In Run 2 it was a delicate process to configure correctly what got recorded
42 as it had to be set in the algorithm that produced it as well in the TriggerEDM.py in a consistent manner.
43
44 For Run 3 every alg input/output key can be crosschecked against the list of objects to record which is defined here.
45 I.e. in the configuration alg developer would do this:
46 from TriggerEDM.TriggerEDMRun3 import recordable
47
48 alg.outputKey = recordable("SomeKey")
49 If the names are correct the outputKey is assigned with SomeKey, if there is a missmatch an exception is thrown.
50
51 """
52
53 # Allow passing DataHandle as argument - convert to string and remove store name
54 name = str(arg).replace('StoreGateSvc+','')
55
56 if "HLTNav_" in name:
57 log.error( "Don't call recordable({0}), or add any \"HLTNav_\" collection manually to the EDM. See:collectDecisionObjects.".format( name ) )
58 pass
59 else: #negative filtering
60 if not any([name.startswith(p) for p in _allowedEDMPrefixes]):
61 raise RuntimeError( f"The collection name {name} does not start with any of the allowed prefixes: {_allowedEDMPrefixes}" )
62 if "Aux" in name and not name[-1] != ".":
63 raise RuntimeError( f"The collection name {name} is Aux but the name does not end with the '.'" )
64
65 if runVersion >= 3:
66 for entry in TriggerHLTListRun3:
67 if entry[0].split( "#" )[1] == name:
68 return arg
69 msg = "The collection name {0} is not declared to be stored by HLT. Add it to TriggerEDMRun3.py".format( name )
70 log.error("ERROR in recordable() - see following stack trace.")
71 raise RuntimeError( msg )
72
73def _addExtraCollectionsToEDMList(edmList, extraList):
74 """
75 Extend edmList with extraList, keeping track whether a completely new
76 collection is being added, or a dynamic variable is added to an existing collection, or new targets are added to an existing collection.
77 The format of extraList is the same as those of TriggerHLTListRun3.
78 """
79 existing_collections = [(c[0].split("#")[1]).split(".")[0] for c in edmList]
80 insert_idx = getSafeEDMInsertPosition(edmList)
81 for item in extraList:
82 colname = (item[0].split("#")[1]).split(".")[0]
83 if colname not in existing_collections:
84 # a new collection and its Aux container are inserted in front of 'allowTruncation' items.
85 if 'Aux' in colname:
86 edmList.insert(insert_idx+1,item)
87 else:
88 edmList.insert(insert_idx,item)
89 log.info("added new item to Trigger EDM: {}".format(item))
90 else:
91 # Maybe extra dynamic variables or EDM targets are added
92 isAux = "Aux." in item[0]
93 # find the index of the existing item
94 existing_item_nr = [i for i,s in enumerate(edmList) if colname == (s[0].split("#")[1]).split(".")[0]]
95 if len(existing_item_nr) != 1:
96 log.error("Found {} existing edm items corresponding to new item {}, but it must be exactly one!".format(len(existing_item_nr), item))
97 existingItem = edmList[existing_item_nr[0]]
98 if isAux:
99 dynVars = (item[0].split("#")[1]).split(".")[1:]
100 existing_dynVars = (existingItem[0].split("#")[1]).split(".")[1:]
101 existing_dynVars.extend(dynVars)
102 dynVars = list(dict.fromkeys(existing_dynVars))
103 if '' in dynVars:
104 dynVars.remove('')
105 newVars = '.'.join(dynVars)
106 edmTargets = item[1].split(" ") if len(item) > 1 else []
107 existing_edmTargets = existingItem[1].split(" ")
108 edmTargets.extend(existing_edmTargets)
109 edmTargets = list(dict.fromkeys(edmTargets))
110 newTargets = " ".join(edmTargets)
111 typename = item[0].split("#")[0]
112 log.info("old item in Trigger EDM : {}".format(existingItem))
113 signature = existingItem[2] # NOT updated at the moment
114 tags = existingItem[3] if len(existingItem) > 3 else None # NOT updated at the moment
115 edmList.pop(existing_item_nr[0])
116 combName = typename + "#" + colname
117 if isAux:
118 combName += "." + newVars
119 if tags:
120 edmList.insert(existing_item_nr[0], (combName, newTargets, signature, tags))
121 else:
122 edmList.insert(existing_item_nr[0] , (combName, newTargets, signature))
123 log.info("updated item in Trigger EDM: {}".format(edmList[existing_item_nr[0]]))
124
125 if testEDMList(edmList, error_on_edmdetails=False):
126 log.error("edmList contains inconsistencies!")
127
128def getRawTriggerEDMList(flags, runVersion=-1):
129 """
130 The static EDM list does still need some light manipulation before it can be used commonly.
131 Never import TriggerHLTListRun3 or TriggerHLTListRun4 directly, always fetch them via this function
132 """
133 if runVersion == -1:
134 runVersion = flags.Trigger.EDMVersion
135
136 if runVersion == 3:
137 edmListCopy = TriggerHLTListRun3.copy()
138 elif runVersion == 4:
139 edm4ListCopy = TriggerHLTListRun4.copy()
140 edm3ListCopy = TriggerHLTListRun3.copy()
141 log.debug("Removing duplicated item between TriggerHLTListRun3 and TriggerHLTListRun4.")
142 for i in range(len(edm3ListCopy)-1, -1, -1): # Back iterate by index, we might be removing as we go
143 for r4item in edm4ListCopy:
144 if edm3ListCopy[i][0] == r4item[0]:
145 del edm3ListCopy[i]
146 log.debug(f"- Dupe: {r4item} is removed from TriggerHLTListRun3 to be updated by TriggerHLTListRun4")
147 break
148 lenPreMerge = len(edm3ListCopy)
149 edm4ListCopy.extend(edm3ListCopy)
150 lenPostMerge = len(edm4ListCopy)
151 edmListCopy = edm4ListCopy
152 log.info(f"Added TriggerHLTListRun4 to TriggerHLTListRun3. EDM entries {lenPreMerge} -> {lenPostMerge}")
153 if testEDMList(edmListCopy, error_on_edmdetails=False):
154 log.error("edmList contains inconsistencies!")
155 else:
156 errMsg="ERROR the getRawTriggerEDMList function supports runs 3 and 4."
157 log.error(errMsg)
158 raise RuntimeError(errMsg)
159
160 if flags and flags.Trigger.ExtraEDMList:
161 log.info( "Adding extra collections to EDM %i: %s", runVersion, str(flags.Trigger.ExtraEDMList))
162 _addExtraCollectionsToEDMList(edmListCopy, flags.Trigger.ExtraEDMList)
163
164 return edmListCopy
165
166def getTriggerEDMList(flags, key, runVersion=-1):
167 """
168 List (Literally Python dict) of trigger objects to be placed with flags:
169 flags is the CA flag container
170 key can be" 'ESD', 'AODSLIM', 'AODFULL'
171 runVersion can be: '-1 (Auto-configure)', '1 (Run1)', '2 (Run2)', '3' (Run 3), '4' (Run 4)
172 """
173 # We allow for this to be overriden as Run1 bytestream actually need to request the Run2 EDM due to it additionally undergoing a transient xAOD migration.
174 if runVersion == -1:
175 runVersion = flags.Trigger.EDMVersion
176
177 if runVersion == 1:
178 return _getTriggerRun1Run2ObjList(key, [TriggerL2List,TriggerEFList, TriggerResultsRun1List])
179
180 elif runVersion == 2:
181 edmList = _getTriggerRun1Run2ObjList(key, [TriggerHLTList, TriggerResultsList])
182 return _getTriggerRun2EDMSlimList(key, edmList) if 'SLIM' in key else edmList
183
184 elif runVersion >= 3:
185 RawEDMList = getRawTriggerEDMList(flags=flags, runVersion=runVersion)
186
187 if key not in AllowedOutputFormats: # AllowedOutputFormats is the entire list of output formats including ESD
188 log.warning('Output format: %s is not in list of allowed formats, please check!', key)
189 return _getRun3TrigObjList(key, [RawEDMList])
190
191 # this keeps only the dynamic variables that have been specified in TriggerEDMRun3
192 Run3TrigEDM = {}
193 Run3TrigEDMSLIM = {}
194
195 if "AODFULL" in key:
196 Run3TrigEDM.update(_getRun3TrigEDMSlimList(key, RawEDMList))
197
198 elif "AODSLIM" in key:
199 # remove the variables that are defined in TriggerEDMRun3.varToRemoveFromAODSLIM from the containers
200
201 # get all containers from list that are marked with AODSLIM
202 if len(varToRemoveFromAODSLIM) == 0:
203 Run3TrigEDM.update(_getRun3TrigEDMSlimList(key, RawEDMList))
204 log.info("No decorations are listed to be removed from AODSLIM")
205 else:
206 Run3TrigEDMSLIM.update(_getRun3TrigEDMSlimList(key, RawEDMList))
207 log.info("The following decorations are going to be removed from the listed collections in AODSLIM {}".format(varToRemoveFromAODSLIM))
208 # Go through all container values and remove the variables to remove
209 # Format of Run3TrigEDMSLIM is {'xAOD::Cont': ['coll1.varA.varB', 'coll2.varD',...],...}
210 for cont, values in Run3TrigEDMSLIM.items():
211 if (isinstance(values, list)):
212 newValues = []
213 for value in values:
214 newValue = value+'.'
215 coll = value.split('.')[0]
216
217 varRemovedFlag = False
218 for myTuple in varToRemoveFromAODSLIM:
219 var = myTuple[0]
220
221 if var in value and coll in myTuple:
222 varRemovedFlag = True
223 removeVar = '.'+var+'.'
224 newValue = newValue.replace(removeVar, '.')
225
226 if newValue[-1:] == '.':
227 newValue = newValue[:-1]
228
229 if varRemovedFlag is False:
230 newValues.append(value)
231 elif varRemovedFlag is True:
232 newValues.append(newValue)
233 else:
234 raise RuntimeError("Decoration removed but no new Value was available, not sure what to do...")
235
236 # Filling the Run3TrigEDM dictionary with the new set of values for each cont
237 Run3TrigEDM[cont] = newValues
238 else:
239 raise RuntimeError("Value in Run3TrigEDM dictionary is not a list")
240
241 else: # ESD
242 Run3TrigEDM.update(_getRun3TrigEDMSlimList(key, RawEDMList))
243
244 log.debug('TriggerEDM for EDM set {} contains the following collections: {}'.format(key, Run3TrigEDM) )
245 return Run3TrigEDM
246
247
248 else:
249 raise RuntimeError("Invalid runVersion=%s supplied to getTriggerEDMList" % runVersion)
250
251
252
253def _getRun3TrigObjProducedInView(theKey, trigEDMList):
254 """
255 Run 3 only
256 Finds a given key from within the trigEDMList.
257 Returns true if this collection is produced inside EventViews
258 (Hence, has the special viewIndex Aux decoration applied by steering)
259 """
260 from TrigEDMConfig.TriggerEDMRun3 import InViews
261
262 return any(coll for coll in itertools.chain(*trigEDMList) if
263 len(coll)>3 and theKey==coll[0].split('#')[1] and
264 any(isinstance(v, InViews) for v in coll[3]))
265
266
267def _handleRun3ViewContainers( el, HLTList ):
268 if 'Aux.' in el:
269 # Get equivalent non-aux string (fragile!!!)
270 keyNoAux = el.split('.')[0].replace('Aux','')
271 # Check if this interface container is produced inside a View
272 inView = _getRun3TrigObjProducedInView(keyNoAux, [HLTList])
273 if el.split('.')[1] == '':
274 # Aux lists zero dynamic vars to save ...
275 if inView:
276 # ... but it was produced in a View, so we need to add the viewIndex dynamic aux
277 return el.split('.')[0]+'.viewIndex'
278 else:
279 # ... and was not in a View, strip all dynamic
280 return el.split('.')[0]+'.-'
281 else:
282 # Aux lists one or more dynamic vars to save ...
283 if inView:
284 # ... and was produced in a View, so add the viewIndex dynamic as well
285 return el+'.viewIndex'
286 else:
287 # ... and was not produced in a View, keep user-supplied list
288 return el
289 else: # no Aux
290 return el
291
292
293def getRun3BSList(flags, keys):
294 """
295 The keys should contain BS and all the identifiers used for scouting.
296 Returns list of tuples (typename#key, [keys], [properties]).
297 """
298
299 from TrigEDMConfig.TriggerEDMRun3 import persistent
300 keys = set(keys[:])
301 collections = []
302 _HLTList = getRawTriggerEDMList(flags, 3)
303 for definition in _HLTList:
304
305 typename,collkey = definition[0].split("#")
306 # normalise collection name and the key (decorations)
307 typename = persistent(typename)
308 collkey = _handleRun3ViewContainers( collkey, _HLTList )
309 destination = keys & set(definition[1].split())
310 if len(destination) > 0:
311 collections.append( (typename+"#"+collkey, list(destination),
312 definition[3] if len(definition)>3 else []) )
313
314 return collections
315
316
317def _getRun3TrigObjList(destination, trigEDMList):
318 """
319 Run 3 version
320 Gives back the Python dictionary with the content of ESD/AOD (dst) which can be inserted in OKS.
321 """
322 dset = set(destination.split())
323 toadd = defaultdict(list)
324
325 for item in itertools.chain(*trigEDMList):
326 if item[1] == '': # no output has been defined
327 continue
328
329 confset = set(item[1].split())
330
331 if dset & confset: # intersection of the sets
332 colltype, k = _getTypeAndKey(item[0])
333
334 if k not in toadd[colltype]:
335 toadd[colltype] += [k]
336
337 return toadd
338
339
340def _getRun3TrigEDMSlimList(key, HLTList):
341 """
342 Run 3 version
343 Modified EDM list to remove all dynamic variables
344 Requires changing the list to have 'Aux.-'
345 """
346 _edmList = _getRun3TrigObjList(key,[HLTList])
347
348 output = {}
349 for k,v in _edmList.items():
350 output[k] = [_handleRun3ViewContainers( el, HLTList ) for el in v]
351 return output
352
353#************************************************************
354#
355# For Run 1 and Run 2 (not modified (so far))
356#
357#************************************************************
359 """
360 Run 2 version
361 Modified EDM list to remove all dynamic variables
362 Requires changing the list to have 'Aux.-'
363 """
364 output = {}
365 for k,v in edmList.items():
366 newnames = []
367 for el in v:
368 if 'Aux' in el:
369 newnames+=[el.split('.')[0]+'.-']
370 else:
371 newnames+=[el]
372 output[k] = newnames
373 return output
374
376 """ From name of object in AOD/ESD found by checkFileTrigSize.py, return category """
377
378 """ Clean up object name """
379 s = s.strip()
380
381 # To-do
382 # seperate the first part of the string at the first '_'
383 # search in EDMDetails for the key corresponding to the persistent value
384 # if a key is found, use this as the first part of the original string
385 # put the string back together
386
387 if s.count('.') : s = s[:s.index('.')]
388 if s.count('::'): s = s[s.index(':')+2:]
389 if s.count('<'): s = s[s.index('<')+1:]
390 if s.count('>'): s = s[:s.index('>')]
391 if s.count('.') : s = s[:s.index('.')]
392 if s.count('Dyn') : s = s[:s.index('Dyn')]
393
394 # containers from Run 1-2 and 3 require different preprocessing
395 # s12 is for Run 1-2, s is for Run 3
396 s12 = s
397
398 if s12.startswith('HLT_xAOD__') or s12.startswith('HLT_Rec__') or s12.startswith('HLT_Analysis__') :
399 s12 = s12[s12.index('__')+2:]
400 s12 = s12[s12.index('_')+1:]
401 #if s12.count('.') : s12 = s12[:s12.index('.')]
402 s12 = "HLT_"+s12
403 elif s12.startswith('HLT_'):
404 #if s.count('Dyn') : s = s[:s.index('Dyn')]
405 if s12.count('_'): s12 = s12[s12.index('_')+1:]
406 if s12.count('_'): s12 = s12[s12.index('_')+1:]
407 s12 = "HLT_"+s12
408
409 TriggerListRun1 = TriggerL2List + TriggerEFList + TriggerResultsRun1List
410 TriggerListRun2 = TriggerResultsList + TriggerLvl1List + TriggerIDTruth + TriggerHLTList
411 TriggerListRun3 = getRawTriggerEDMList(flags=None, runVersion=3)
412
413 category = ''
414 bestMatch = ''
415
416 """ Loop over all objects already defined in lists (and hopefully categorized!!) """
417 for item in TriggerListRun1+TriggerListRun2:
418 t,k = _getTypeAndKey(item[0])
419
420 """ Clean up type name """
421 if t.count('::'): t = t[t.index(':')+2:]
422 if t.count('<'): t = t[t.index('<')+1:]
423 if t.count('>'): t = t[:t.index('>')]
424 if (s12.startswith(t) and s12.endswith(k)) and (len(t) > len(bestMatch)):
425 bestMatch = t
426 category = item[2]
427
428 if k.count('.'): k = k[:k.index('.')]
429 if (s12 == k):
430 bestMatch = k
431 category = item[2]
432
433 for item in TriggerListRun3:
434 t,k = _getTypeAndKey(item[0])
435
436 """ Clean up type name """
437 if t.count('::'): t = t[t.index(':')+2:]
438 if t.count('<'): t = t[t.index('<')+1:]
439 if t.count('>'): t = t[:t.index('>')]
440
441 if (s.startswith(t) and s.endswith(k)) and (len(t) > len(bestMatch)):
442 bestMatch = t
443 category = item[2]
444
445 if k.count('.'): k = k[:k.index('.')]
446 if (s == k):
447 bestMatch = k
448 category = item[2]
449
450 if category == '' and 'HLTNav' in s:
451 category = 'HLTNav'
452
453 if category == '': return 'NOTFOUND'
454 return category
455
456
457
459 """ From the strings containing type and key of trigger EDM extract type and key
460 """
461 return s[:s.index('#')], s[s.index('#')+1:]
462
463def _keyToLabel(key):
464 """ The key is usually HLT_*, this function returns second part of it or empty string
465 """
466 if '_' not in key:
467 return ''
468 else:
469 return key[key.index('_'):].lstrip('_')
470
471def _getTriggerRun1Run2ObjList(destination, lst):
472 """
473 Gives back the Python dictionary with the content of ESD/AOD (dst) which can be inserted in OKS.
474 """
475 dset = set(destination.split())
476
477 toadd = {}
478
479 for item in itertools.chain(*lst):
480 if item[1] == '':
481 continue
482 confset = set(item[1].split())
483 if dset & confset: # intersection of the sets
484 t,k = _getTypeAndKey(item[0])
485 colltype = t
486 if 'collection' in EDMDetails[t]:
487 colltype = EDMDetails[t]['collection']
488 if colltype in toadd:
489 if k not in toadd[colltype]:
490 toadd[colltype] += [k]
491 else:
492 toadd[colltype] = [k]
493 return _InsertContainerNameForHLT(toadd)
494
495
497 """
498 Gives back the Python dictionary with the truth trigger content of ESD/AOD (dst) which can be inserted in OKS.
499 """
500 return _getTriggerRun1Run2ObjList(dst,[TriggerIDTruth])
501
503 """
504 Gives back the Python dictionary with the lvl1 trigger result content of ESD which can be inserted in OKS.
505 """
506 return _getTriggerRun1Run2ObjList('ESD',[TriggerLvl1List])
507
509 """
510 Gives back the Python dictionary with the lvl1 trigger result content of AOD which can be inserted in OKS.
511 """
512 return _getTriggerRun1Run2ObjList('AODFULL',[TriggerLvl1List])
513
514
515
517 """
518 List (Literally Python list) of trigger objects to be preregistered i.e. this objects we want in every event for L2
519 """
520 l = []
521 for item in TriggerL2List:
522 if len (item[1]) == 0: continue
523 t,k = _getTypeAndKey(item[0])
524 if('Aux' in t):
525 continue #we don't wat to preregister Aux containers
526 l += [t+"#"+_keyToLabel(k)]
527 return l
528
530 """
531 List (Literally Python list) of trigger objects to be preregistered i.e. this objects we want in every event for EF
532 """
533 l = []
534 for item in TriggerEFList:
535 if len (item[1]) == 0: continue
536 t,k = _getTypeAndKey(item[0])
537 if('Aux' in t):
538 continue #we don't wat to preregister Aux containers
539 l += [t+"#"+_keyToLabel(k)]
540 return l
541
543 """
544 List (Literally Python list) of trigger objects to be preregistered i.e. this objects we want in every event for merged L2/EF in addition to default L2 and EF
545 """
546 l = []
547 for item in TriggerHLTList:
548 if len (item[1]) == 0: continue
549 t,k = _getTypeAndKey(item[0])
550 if('Aux' in t):
551 continue #we don't wat to preregister Aux containers
552 l += [t+"#"+_keyToLabel(k)]
553 return l
554
555
556def getPreregistrationList(version=2, doxAODConversion=True):
557 """
558 List (Literally Python list) of trigger objects to be preregistered i.e. this objects we want for all levels
559 version can be: '1 (Run1)', '2 (Run2)'
560 """
561
562 l=[]
563 if version==2:
565 elif version==1:
566 # remove duplicates while preserving order
568 if doxAODConversion:
570 l=list(dict.fromkeys(objs))
571 else:
572 raise RuntimeError("Invalid version=%s supplied to getPreregistrationList" % version)
573 return l
574
575
577 """ List of L2 types to be read from BS, used by the TP
578 """
579 l = []
580 for item in TriggerL2List:
581 t,k = _getTypeAndKey(item[0])
582 ctype = t
583 if 'collection' in EDMDetails[t]:
584 ctype = EDMDetails[t]['collection']
585 l += [ctype]
586 return l
587
589 """ List of EF types to be read from BS, used by the TP
590 """
591 l = []
592 for item in TriggerEFList:
593 t,k = _getTypeAndKey(item[0])
594 ctype = t
595 if 'collection' in EDMDetails[t]:
596 ctype = EDMDetails[t]['collection']
597 l += [ctype]
598 return l
599
601 """ List of HLT types to be read from BS, used by the TP
602 """
603 l = []
604 for item in TriggerHLTList:
605 t,k = _getTypeAndKey(item[0])
606 ctype = t
607 if 'collection' in EDMDetails[t]:
608 ctype = EDMDetails[t]['collection']
609 l += [ctype]
610 return l
611
612def getTPList(version=2):
613 """
614 Mapping of Transient objects to Peristent during serialization (BS creation)
615 version can be: '1 (Run1)', '2 (Run2)'
616 """
617 l = {}
618 if version==2:
619 bslist = _getHLTBSTypeList()
620 elif version==1:
621 bslist = list(set(_getL2BSTypeList() + _getEFBSTypeList()))
622 else:
623 raise RuntimeError("Invalid version=%s supplied to getTPList" % version)
624
625 for t,d in EDMDetails.items():
626 colltype = t
627 if 'collection' in d:
628 colltype = EDMDetails[t]['collection']
629 if colltype in bslist:
630 l[colltype] = d['persistent']
631 return l
632
633
635 return EDMLibraries
636
638 import re
639 output = {}
640 for k,v in typedict.items():
641 newnames = []
642 for el in v:
643 if el.startswith('HLT_') or el == 'HLT':
644 prefixAndLabel = el.split('_',1) #only split on first underscore
645 containername = k if 'Aux' not in k else EDMDetails[k]['parent'] #we want the type in the Aux SG key to be the parent type #104811
646 #maybe this is not needed anymore since we are now versionless with the CLIDs but it's not hurting either
647 containername = re.sub('::','__',re.sub('_v[0-9]+$','',containername))
648 newnames+=['_'.join([prefixAndLabel[0],containername]+([prefixAndLabel[1]] if len(prefixAndLabel) > 1 else []))]
649 else:
650 newnames+=[el]
651 output[k] = newnames
652 return output
653
655 """
656 List of EF trigger objects that were written to ByteStream in Run 1
657 """
658 l = []
659 for item in TriggerEFEvolutionList:
660 if len (item[1]) == 0: continue
661 t,k = _getTypeAndKey(item[0])
662 l += [t+"#"+_keyToLabel(k)]
663 return l
664
666 """
667 List of Run-2 containers equivalent to Run-1 EF containers
668 """
669 l = []
670 for item in TriggerEFEvolutionList:
671 if len (item[1]) == 0: continue
672 t,k = _getTypeAndKey(item[1])
673 l += [t+"#"+_keyToLabel(k)]
674 return l
675
677 """
678 List of L2 trigger objects that were written to ByteStream in Run 1
679 """
680 l = []
681 for item in TriggerL2EvolutionList:
682 if len (item[1]) == 0: continue
683 t,k = _getTypeAndKey(item[0])
684 l += [t+"#"+_keyToLabel(k)]
685 return l
686
688 """
689 List of Run-2 containers equivalent to Run-1 L2 containers
690 """
691 l = []
692 for item in TriggerL2EvolutionList:
693 if len (item[1]) == 0: continue
694 t,k = _getTypeAndKey(item[1])
695 l += [t+"#"+_keyToLabel(k)]
696 return l
697
698def isCLIDDefined(cgen,typename):
699 """
700 Checks container type name is hashable
701 """
702 c = cgen.genClidFromName(typename)
703 return (cgen.getNameFromClid(c) is not None)
704
705def testEDMList(edm_list, error_on_edmdetails = True):
706 """
707 Checks EDM list entries for serialization and configuration compliance.
708 """
709
710 #xAOD types that don't expect accompanying Aux containers
711 _noAuxList = ["xAOD::" + contType for contType in ["TrigConfKeys", "BunchConfKey"]]
712
713 cgen = clidGenerator("", False)
714 return_code = 0
715 found_allow_truncation = False
716 serializable_names = []
717 serializable_names_no_label = []
718 serializable_names_no_properties = []
719
720 for i, edm in enumerate(edm_list):
721
722 #check has sufficient entries
723 if len(edm) < 3:
724 log.error("EDM entry too short for " + edm[0])
725 return_code = 1
726 continue
727
728 serializable_name = edm[0]
729 serializable_name_no_label = re.sub(r"\#.*", "", serializable_name)
730 serializable_name_no_properties = serializable_name.split('.')[0]
731
732 serializable_names.append(serializable_name)
733 serializable_names_no_label.append(serializable_name_no_label)
734 serializable_names_no_properties.append(serializable_name_no_properties)
735
736 #check container type name is hashable
737 if not isCLIDDefined(cgen,serializable_name_no_label):
738 log.error("no CLID for " + serializable_name)
739 return_code = 1
740
741 #check that no '.' in entries _not_ containing 'Aux'
742 if "Aux" not in serializable_name and "." in serializable_name:
743 log.error("A '.' found in non-Aux container name " + serializable_name)
744 return_code = 1
745
746 #check for Aux "."
747 if "Aux" in serializable_name and "Aux." not in serializable_name:
748 log.error("no final Aux. in label for " + serializable_name)
749 return_code = 1
750
751 #check contains exactly one #
752 if serializable_name.count("#") != 1:
753 log.error("Invalid naming structure for " + serializable_name)
754 return_code = 1
755 else: # only proceed if type and name can be separated
756 #check that every interface xAOD container directly followed by matching Aux container.
757 if serializable_name.startswith("xAOD") and "Aux" not in serializable_name:
758 cont_type,cont_name = serializable_name.split("#")
759 if cont_type not in _noAuxList:
760 auxmismatch = False
761 if len(edm_list) == i+1:
762 auxmismatch = True
763 cont_to_test = "nothing"
764 else:
765 cont_to_test = edm_list[i+1][0].split(".")[0]+"."
766 cont_type_short = cont_type.replace("Container","")
767 pattern = re.compile(cont_type_short+r".*Aux.*\#"+cont_name+"Aux.")
768 if not pattern.match(cont_to_test):
769 #test if shallow container type.
770 shallow_pattern = re.compile("xAOD::ShallowAuxContainer#"+cont_name+"Aux.")
771 if not shallow_pattern.match(cont_to_test):
772 auxmismatch = True
773 if auxmismatch:
774 log.error("Expected relevant Aux container following interface container %s, but found %s instead.",serializable_name,cont_to_test)
775 return_code = 1
776 else:
777 #check that targets of interface and aux container match
778 targets = set(edm[1].split())
779 if len(edm_list[i+1]) > 1 and cont_to_test.count("#") == 1:
780 contname_to_test = cont_to_test.split("#")[1]
781 targets_to_test = set(edm_list[i+1][1].split())
782 if len(targets ^ targets_to_test) > 0:
783 log.error("Targets of %s (%s) and %s (%s) do not match",cont_name,targets,contname_to_test,targets_to_test)
784 return_code = 1
785
786 #check that Aux always follows non-Aux (our deserialiser relies on that)
787 if i>0 and "Aux" in serializable_name and "Aux" in edm_list[i-1][0]:
788 log.error(f"Aux container {serializable_name} needs to folow the "
789 "associated interface container in the EDM list")
790 return_code = 1
791
792 #check target types are valid. Check that target lists containing higher level targets
793 #have required low level targets (ESD for AOD targets, AODFULL for SLIM targets).
794 file_types = edm[1].split() # might return empty list, this is fine - 0 output file types allowed for an EDM entry (in case of obsolete containers)
795 for file_type in file_types:
796 if file_type not in AllowedOutputFormats:
797 log.error("unknown file type " + file_type + " for " + serializable_name)
798 return_code = 1
799 for higher_level,required_lower_level in [('AOD','ESD'), ('SLIM','AODFULL')]:
800 if higher_level in file_type and required_lower_level not in file_types:
801 log.error("Target list for %s containing '%s' must also contain lower level target '%s'.",serializable_name,file_type,required_lower_level)
802 return_code = 1
803
804 # Check allowTuncation is only at the end
805 tags = edm[3] if len(edm) > 3 else None
806 allow_truncation_flag = False
807 if tags:
808 allow_truncation_flag = allowTruncation in tags
809 if allow_truncation_flag:
810 found_allow_truncation = True
811
812 if found_allow_truncation and not allow_truncation_flag:
813 log.error("All instances of 'allowTruncation' need to be at the END of the EDM serialisation list")
814 return_code = 1
815
816 #end of for loop over entries. Now do complete list checks.
817
818 #check for duplicates:
819 #check that no two EDM entries match after stripping out characters afer '.'
820 if not len(set(serializable_names_no_properties)) == len(serializable_names_no_properties):
821 log.error("Duplicates in EDM list! Duplicates found:")
822 import collections.abc
823 for item, count in collections.Counter(serializable_names_no_properties).items():
824 if count > 1:
825 log.error(str(count) + "x: " + str(item))
826 return_code = 1
827
828 #check EDMDetails
829 for EDMDetail in EDMDetailsRun3.keys():
830 if EDMDetail not in serializable_names_no_label:
831 msg = "EDMDetail for " + EDMDetail + " does not correspond to any name in TriggerList"
832 if error_on_edmdetails:
833 log.error(msg)
834 return_code = 1
835 else:
836 log.warning(msg)
837
838 return return_code
if(febId1==febId2)
STL class.
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:310
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:177
getPreregistrationList(version=2, doxAODConversion=True)
isCLIDDefined(cgen, typename)
_getRun3TrigEDMSlimList(key, HLTList)
getRun3BSList(flags, keys)
recordable(arg, runVersion=3)
Definition TriggerEDM.py:37
_getTriggerRun1Run2ObjList(destination, lst)
_getRun3TrigObjList(destination, trigEDMList)
getTPList(version=2)
getTriggerEDMList(flags, key, runVersion=-1)
_InsertContainerNameForHLT(typedict)
testEDMList(edm_list, error_on_edmdetails=True)
_addExtraCollectionsToEDMList(edmList, extraList)
Definition TriggerEDM.py:73
_getTriggerRun2EDMSlimList(key, edmList)
_getRun3TrigObjProducedInView(theKey, trigEDMList)
getRawTriggerEDMList(flags, runVersion=-1)
_handleRun3ViewContainers(el, HLTList)