ATLAS Offline Software
Loading...
Searching...
No Matches
TriggerEDM.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 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.TriggerEDMRun2 import TriggerResultsList,TriggerLvl1List,TriggerHLTList,EDMDetails,EDMLibraries
11from TrigEDMConfig.TriggerEDMRun3 import TriggerHLTListRun3,varToRemoveFromAODSLIM,EDMDetailsRun3,getRun3LowMuEDM
12from TrigEDMConfig.TriggerEDMRun4 import TriggerHLTListRun4,EDMDetailsRun4
13from TrigEDMConfig.TriggerEDMProtoRun4 import TriggerHLTListProtoRun4
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_', 'EF_', 'L1_', 'LVL1']
37def recordable(arg):
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 #TODO Remove TriggerHLTListProtoRun4 once fully migrated to Run 4.
66 for entry in TriggerHLTListProtoRun4 + TriggerHLTListRun4:
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 TriggerEDMRun4.py or TriggerHLTListProtoRun4.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):
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 if flags and flags.Trigger.addRun3LowMuEDM:
139 LowMuEDM = getRun3LowMuEDM(flags)
140 log.info( "Adding low mu collections to EDM %i: %s", runVersion, str(LowMuEDM))
141 _addExtraCollectionsToEDMList(edmListCopy, LowMuEDM)
142 elif runVersion == 4:
143 edm4ListCopy = TriggerHLTListRun4.copy()
144 edm4protoListCopy = TriggerHLTListProtoRun4.copy()
145 log.debug("Removing duplicated item between TriggerHLTListProtoRun4 and TriggerHLTListRun4.")
146 for i in range(len(edm4protoListCopy)-1, -1, -1): # Back iterate by index, we might be removing as we go
147 for r4item in edm4ListCopy:
148 if edm4protoListCopy[i][0] == r4item[0]:
149 del edm4protoListCopy[i]
150 log.error(f"- Dupe: {r4item} appears in both TriggerHLTListProtoRun4.py and TriggerHLTListRun4.py - please resolve the duplication.")
151 break
152 lenPreMerge = len(edm4ListCopy)
153 edm4ListCopy.extend(edm4protoListCopy)
154 lenPostMerge = len(edm4ListCopy)
155 edmListCopy = edm4ListCopy
156 log.info(f"Added TriggerHLTListProtoRun4 to TriggerHLTListRun4. EDM entries {lenPreMerge} -> {lenPostMerge}")
157 else:
158 errMsg="ERROR the getRawTriggerEDMList function supports runs 3 and 4."
159 log.error(errMsg)
160 raise RuntimeError(errMsg)
161
162 if flags and flags.Trigger.ExtraEDMList:
163 log.info( "Adding extra collections to EDM %i: %s", runVersion, str(flags.Trigger.ExtraEDMList))
164 _addExtraCollectionsToEDMList(edmListCopy, flags.Trigger.ExtraEDMList)
165
166 return edmListCopy
167
168def getTriggerEDMList(flags, key, runVersion=-1):
169 """
170 List (Literally Python dict) of trigger objects to be placed with flags:
171 flags is the CA flag container
172 key can be" 'ESD', 'AODSLIM', 'AODFULL'
173 runVersion can be: '-1 (Auto-configure)', '1 (Run1)', '2 (Run2)', '3' (Run 3), '4' (Run 4)
174 """
175 # 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.
176 if runVersion == -1:
177 runVersion = flags.Trigger.EDMVersion
178
179 if runVersion == 1:
180 raise RuntimeError("Run-1 EDM is no longer supported")
181
182 elif runVersion == 2:
183 edmList = _getTriggerRun1Run2ObjList(key, [TriggerHLTList, TriggerResultsList])
184 return _getTriggerRun2EDMSlimList(key, edmList) if 'SLIM' in key else edmList
185
186 elif runVersion >= 3:
187 RawEDMList = getRawTriggerEDMList(flags=flags, runVersion=runVersion)
188
189 if key not in AllowedOutputFormats: # AllowedOutputFormats is the entire list of output formats including ESD
190 log.warning('Output format: %s is not in list of allowed formats, please check!', key)
191 return _getRun3TrigObjList(key, [RawEDMList])
192
193 # this keeps only the dynamic variables that have been specified in TriggerEDMRun3
194 Run3TrigEDM = {}
195 Run3TrigEDMSLIM = {}
196
197 if "AODFULL" in key:
198 Run3TrigEDM.update(_getRun3TrigEDMSlimList(key, RawEDMList))
199
200 elif "AODSLIM" in key:
201 # remove the variables that are defined in TriggerEDMRun3.varToRemoveFromAODSLIM from the containers
202
203 # get all containers from list that are marked with AODSLIM
204 if len(varToRemoveFromAODSLIM) == 0:
205 Run3TrigEDM.update(_getRun3TrigEDMSlimList(key, RawEDMList))
206 log.info("No decorations are listed to be removed from AODSLIM")
207 else:
208 Run3TrigEDMSLIM.update(_getRun3TrigEDMSlimList(key, RawEDMList))
209 log.info("The following decorations are going to be removed from the listed collections in AODSLIM {}".format(varToRemoveFromAODSLIM))
210 # Go through all container values and remove the variables to remove
211 # Format of Run3TrigEDMSLIM is {'xAOD::Cont': ['coll1.varA.varB', 'coll2.varD',...],...}
212 for cont, values in Run3TrigEDMSLIM.items():
213 if (isinstance(values, list)):
214 newValues = []
215 for value in values:
216 newValue = value+'.'
217 coll = value.split('.')[0]
218
219 varRemovedFlag = False
220 for myTuple in varToRemoveFromAODSLIM:
221 var = myTuple[0]
222
223 if var in value and coll in myTuple:
224 varRemovedFlag = True
225 removeVar = '.'+var+'.'
226 newValue = newValue.replace(removeVar, '.')
227
228 if newValue[-1:] == '.':
229 newValue = newValue[:-1]
230
231 if varRemovedFlag is False:
232 newValues.append(value)
233 elif varRemovedFlag is True:
234 newValues.append(newValue)
235 else:
236 raise RuntimeError("Decoration removed but no new Value was available, not sure what to do...")
237
238 # Filling the Run3TrigEDM dictionary with the new set of values for each cont
239 Run3TrigEDM[cont] = newValues
240 else:
241 raise RuntimeError("Value in Run3TrigEDM dictionary is not a list")
242
243 else: # ESD
244 Run3TrigEDM.update(_getRun3TrigEDMSlimList(key, RawEDMList))
245
246 log.debug('TriggerEDM for EDM set {} contains the following collections: {}'.format(key, Run3TrigEDM) )
247 return Run3TrigEDM
248
249
250 else:
251 raise RuntimeError("Invalid runVersion=%s supplied to getTriggerEDMList" % runVersion)
252
253
254
255def _getRun3TrigObjProducedInView(theKey, trigEDMList):
256 """
257 Run 3 only
258 Finds a given key from within the trigEDMList.
259 Returns true if this collection is produced inside EventViews
260 (Hence, has the special viewIndex Aux decoration applied by steering)
261 """
262 from TrigEDMConfig.TriggerEDMRun3 import InViews
263
264 return any(coll for coll in itertools.chain(*trigEDMList) if
265 len(coll)>3 and theKey==coll[0].split('#')[1] and
266 any(isinstance(v, InViews) for v in coll[3]))
267
268
269def _handleRun3ViewContainers( el, HLTList ):
270 if 'Aux.' in el:
271 # Get equivalent non-aux string (fragile!!!)
272 keyNoAux = el.split('.')[0].replace('Aux','')
273 # Check if this interface container is produced inside a View
274 inView = _getRun3TrigObjProducedInView(keyNoAux, [HLTList])
275 if el.split('.')[1] == '':
276 # Aux lists zero dynamic vars to save ...
277 if inView:
278 # ... but it was produced in a View, so we need to add the viewIndex dynamic aux
279 return el.split('.')[0]+'.viewIndex'
280 else:
281 # ... and was not in a View, strip all dynamic
282 return el.split('.')[0]+'.-'
283 else:
284 # Aux lists one or more dynamic vars to save ...
285 if inView:
286 # ... and was produced in a View, so add the viewIndex dynamic as well
287 return el+'.viewIndex'
288 else:
289 # ... and was not produced in a View, keep user-supplied list
290 return el
291 else: # no Aux
292 return el
293
294
295def getRun3BSList(flags, keys):
296 """
297 The keys should contain BS and all the identifiers used for scouting.
298 Returns list of tuples (typename#key, [keys], [properties]).
299 """
300
301 keys = set(keys[:])
302 collections = []
303 _HLTList = getRawTriggerEDMList(flags, 3)
304 for definition in _HLTList:
305
306 typename,collkey = definition[0].split("#")
307 # normalise collection name and the key (decorations)
308 typename = persistent(typename)
309 collkey = _handleRun3ViewContainers( collkey, _HLTList )
310 destination = keys & set(definition[1].split())
311 if len(destination) > 0:
312 collections.append( (typename+"#"+collkey, list(destination),
313 definition[3] if len(definition)>3 else []) )
314
315 return collections
316
317# Run3/Run4 fn added as simply appending new HLT Nav entries to start of list
318# during runtime appears to cause Deserialisation issues (ATR-29945).
319# Appending new entries towards end is safer.
321 """
322 Need to avoid inserting new runtime EDM entries at end of list
323 as 'allowTruncation' items need to be at end of list for correct
324 serialisation.
325 Function returns one position before
326 first 'allowTruncation' EDM item. If there is no 'allowTruncation'
327 EDM item, the position returned is the length of the list, i.e. the next
328 index in the list.
329 """
330
331 insert_idx = len(edm_list)
332
333 # start at end of list
334 for i,entry in enumerate(reversed(edm_list)):
335 if len(entry) < 4 or allowTruncation not in entry[3]:
336 insert_idx -= i
337 return insert_idx
338 return insert_idx
339
340
341def addHLTNavigationToEDMList(flags, edmList, allDecisions, hypoDecisions):
342 """
343 Extend TriggerHLTListi Run3/Run4 with HLT Navigation objects
344 """
345
346 # TODO: "EF" for Run 4.
347
348 # HLTNav_* object list is built dynamically during job configuration, here we only define its output targets
349 HLTNavEDMTargets = ''
350
351 insert_idx = getSafeEDMInsertPosition(edmList)
352
353 if not flags.Trigger.doOnlineNavigationCompactification:
354 # If we are not compacting the online EDM, then we must write out all of the individual collections
355 # ESD is added for MC support
356 HLTNavEDMTargets = 'BS ESD'
357
358 for decisionCollection in allDecisions:
359 dynamic = '.-' # Exclude dynamic
360 if decisionCollection in hypoDecisions:
361 # Include dynamic
362 dynamic = '.remap_linkColIndices.remap_linkColKeys'
363 if 'PEBInfoWriter' in decisionCollection:
364 dynamic += '.PEBROBList.PEBSubDetList'
365 typeName = 'xAOD::TrigCompositeContainer#{:s}'.format(decisionCollection)
366 typeNameAux = 'xAOD::TrigCompositeAuxContainer#{:s}Aux{:s}'.format(decisionCollection, dynamic)
367
368 # Cost monitoring only requires a sub-set of the navigation collections.
369 # (And CANNOT use any slimmed/merged collection, as the container names are important)
370 thisCollectionHLTNavEDMTargets = HLTNavEDMTargets
371 if decisionCollection.startswith("HLTNav_FStep") or decisionCollection == "HLTNav_Summary" or decisionCollection.startswith("HLTNav_L1"):
372 thisCollectionHLTNavEDMTargets += ' CostMonDS'
373
374 edmList[insert_idx:insert_idx] = [
375 (typeName, thisCollectionHLTNavEDMTargets, 'Steer'),
376 (typeNameAux, thisCollectionHLTNavEDMTargets, 'Steer')]
377
378def _getRun3TrigObjList(destination, trigEDMList):
379 """
380 Run 3 version
381 Gives back the Python dictionary with the content of ESD/AOD (dst) which can be inserted in OKS.
382 """
383 dset = set(destination.split())
384 toadd = defaultdict(list)
385
386 for item in itertools.chain(*trigEDMList):
387 if item[1] == '': # no output has been defined
388 continue
389
390 confset = set(item[1].split())
391
392 if dset & confset: # intersection of the sets
393 colltype, k = _getTypeAndKey(item[0])
394
395 if k not in toadd[colltype]:
396 toadd[colltype] += [k]
397
398 return toadd
399
400
401def _getRun3TrigEDMSlimList(key, HLTList):
402 """
403 Run 3 version
404 Modified EDM list to remove all dynamic variables
405 Requires changing the list to have 'Aux.-'
406 """
407 _edmList = _getRun3TrigObjList(key,[HLTList])
408
409 output = {}
410 for k,v in _edmList.items():
411 output[k] = [_handleRun3ViewContainers( el, HLTList ) for el in v]
412 return output
413
414#************************************************************
415#
416# For Run 1 and Run 2 (not modified (so far))
417#
418#************************************************************
420 """
421 Run 2 version
422 Modified EDM list to remove all dynamic variables
423 Requires changing the list to have 'Aux.-'
424 """
425 output = {}
426 for k,v in edmList.items():
427 newnames = []
428 for el in v:
429 if 'Aux' in el:
430 newnames+=[el.split('.')[0]+'.-']
431 else:
432 newnames+=[el]
433 output[k] = newnames
434 return output
435
437 """ From name of object in AOD/ESD found by checkFileTrigSize.py, return category """
438
439 """ Clean up object name """
440 s = s.strip()
441
442 # To-do
443 # seperate the first part of the string at the first '_'
444 # search in EDMDetails for the key corresponding to the persistent value
445 # if a key is found, use this as the first part of the original string
446 # put the string back together
447
448 if s.count('.') : s = s[:s.index('.')]
449 if s.count('::'): s = s[s.index(':')+2:]
450 if s.count('<'): s = s[s.index('<')+1:]
451 if s.count('>'): s = s[:s.index('>')]
452 if s.count('.') : s = s[:s.index('.')]
453 if s.count('Dyn') : s = s[:s.index('Dyn')]
454
455 # containers from Run 1-2 and 3 require different preprocessing
456 # s12 is for Run 1-2, s is for Run 3
457 s12 = s
458
459 if s12.startswith('HLT_xAOD__') or s12.startswith('HLT_Rec__') or s12.startswith('HLT_Analysis__') :
460 s12 = s12[s12.index('__')+2:]
461 s12 = s12[s12.index('_')+1:]
462 #if s12.count('.') : s12 = s12[:s12.index('.')]
463 s12 = "HLT_"+s12
464 elif s12.startswith('HLT_'):
465 #if s.count('Dyn') : s = s[:s.index('Dyn')]
466 if s12.count('_'): s12 = s12[s12.index('_')+1:]
467 if s12.count('_'): s12 = s12[s12.index('_')+1:]
468 s12 = "HLT_"+s12
469
470 TriggerListRun2 = TriggerResultsList + TriggerLvl1List + TriggerHLTList
471 TriggerListRun3 = getRawTriggerEDMList(flags=None, runVersion=3)
472
473 category = ''
474 bestMatch = ''
475
476 """ Loop over all objects already defined in lists (and hopefully categorized!!) """
477 for item in TriggerListRun2:
478 t,k = _getTypeAndKey(item[0])
479
480 """ Clean up type name """
481 if t.count('::'): t = t[t.index(':')+2:]
482 if t.count('<'): t = t[t.index('<')+1:]
483 if t.count('>'): t = t[:t.index('>')]
484 if (s12.startswith(t) and s12.endswith(k)) and (len(t) > len(bestMatch)):
485 bestMatch = t
486 category = item[2]
487
488 if k.count('.'): k = k[:k.index('.')]
489 if (s12 == k):
490 bestMatch = k
491 category = item[2]
492
493 for item in TriggerListRun3:
494 t,k = _getTypeAndKey(item[0])
495
496 """ Clean up type name """
497 if t.count('::'): t = t[t.index(':')+2:]
498 if t.count('<'): t = t[t.index('<')+1:]
499 if t.count('>'): t = t[:t.index('>')]
500
501 if (s.startswith(t) and s.endswith(k)) and (len(t) > len(bestMatch)):
502 bestMatch = t
503 category = item[2]
504
505 if k.count('.'): k = k[:k.index('.')]
506 if (s == k):
507 bestMatch = k
508 category = item[2]
509
510 if category == '' and 'HLTNav' in s:
511 category = 'HLTNav'
512
513 if category == '': return 'NOTFOUND'
514 return category
515
516
517
519 """ From the strings containing type and key of trigger EDM extract type and key
520 """
521 return s[:s.index('#')], s[s.index('#')+1:]
522
523def _keyToLabel(key):
524 """ The key is usually HLT_*, this function returns second part of it or empty string
525 """
526 if '_' not in key:
527 return ''
528 else:
529 return key[key.index('_'):].lstrip('_')
530
531def _getTriggerRun1Run2ObjList(destination, lst):
532 """
533 Gives back the Python dictionary with the content of ESD/AOD (dst) which can be inserted in OKS.
534 """
535 dset = set(destination.split())
536
537 toadd = {}
538
539 for item in itertools.chain(*lst):
540 if item[1] == '':
541 continue
542 confset = set(item[1].split())
543 if dset & confset: # intersection of the sets
544 t,k = _getTypeAndKey(item[0])
545 colltype = t
546 if 'collection' in EDMDetails[t]:
547 colltype = EDMDetails[t]['collection']
548 if colltype in toadd:
549 if k not in toadd[colltype]:
550 toadd[colltype] += [k]
551 else:
552 toadd[colltype] = [k]
553 return _InsertContainerNameForHLT(toadd)
554
555
557 """
558 Gives back the Python dictionary with the lvl1 trigger result content of ESD which can be inserted in OKS.
559 """
560 return _getTriggerRun1Run2ObjList('ESD',[TriggerLvl1List])
561
563 """
564 Gives back the Python dictionary with the lvl1 trigger result content of AOD which can be inserted in OKS.
565 """
566 return _getTriggerRun1Run2ObjList('AODFULL',[TriggerLvl1List])
567
568
570 """
571 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
572 """
573 l = []
574 for item in TriggerHLTList:
575 if len (item[1]) == 0: continue
576 t,k = _getTypeAndKey(item[0])
577 if('Aux' in t):
578 continue #we don't wat to preregister Aux containers
579 l += [t+"#"+_keyToLabel(k)]
580 return l
581
582
583def getPreregistrationList(version=2, doxAODConversion=True):
584 """
585 List (Literally Python list) of trigger objects to be preregistered i.e. this objects we want for all levels
586 version can be: '1 (Run1)', '2 (Run2)'
587 """
588
589 l=[]
590 if version==2:
592 elif version==1:
593 raise RuntimeError("Run-1 EDM is no longer supported")
594 else:
595 raise RuntimeError("Invalid version=%s supplied to getPreregistrationList" % version)
596 return l
597
598
600 """Get list of classes to ignore during deserialization"""
601 l = []
602
603 if flags.Trigger.EDMVersion == 2:
604 # Ignore Run-1 EDM classes that were initially/mistakenly written as
605 # empty containers into Run-2 files (ATR-32944).
606 l.append("145610168") # TrigMissingET
607 l.append("1271751257") # ElectronMuonTopoInfoContainer
608 l.append("1142962358") # TrigEFBjetContainer
609 l.append("243892660") # TrigMonEvent (CostMon)
610 l.append("253562967") # TrigMonConfig (CostMon)
611 l.append("1500") # MuonFeature
612 l.append("1501") # CombinedMuonFeature
613 l.append("1503") # TileMuFeature
614 l.append("77762301") # TileTrackMuFeature
615 l.append("110010498") # IsoMuonFeature
616 l.append("233283613") # TrigMuonClusterFeature
617 l.append("262323491") # MuonFeatureDetails
618 l.append("1173853172") # TrigMuonEFInfoContainer
619 l.append("1175295175") # TrigMuonEFIsolationContainer
620 l.append("1314529658") # MuonSegmentCombinationCollection
621 return l
622
623
625 """ List of HLT types to be read from BS, used by the TP
626 """
627 l = []
628 for item in TriggerHLTList:
629 t,k = _getTypeAndKey(item[0])
630 ctype = t
631 if 'collection' in EDMDetails[t]:
632 ctype = EDMDetails[t]['collection']
633 l += [ctype]
634 return l
635
636def getTPList(version=2):
637 """
638 Mapping of Transient objects to Peristent during serialization (BS creation)
639 version can be: '1 (Run1)', '2 (Run2)'
640 """
641 l = {}
642 if version==2:
643 bslist = _getHLTBSTypeList()
644 elif version==1:
645 raise RuntimeError("Run-1 EDM is no longer supported")
646 else:
647 raise RuntimeError("Invalid version=%s supplied to getTPList" % version)
648
649 for t,d in EDMDetails.items():
650 colltype = t
651 if 'collection' in d:
652 colltype = EDMDetails[t]['collection']
653 if colltype in bslist:
654 l[colltype] = d['persistent']
655 return l
656
657
659 return EDMLibraries
660
662 import re
663 output = {}
664 for k,v in typedict.items():
665 newnames = []
666 for el in v:
667 if el.startswith('HLT_') or el == 'HLT':
668 prefixAndLabel = el.split('_',1) #only split on first underscore
669 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
670 #maybe this is not needed anymore since we are now versionless with the CLIDs but it's not hurting either
671 containername = re.sub('::','__',re.sub('_v[0-9]+$','',containername))
672 newnames+=['_'.join([prefixAndLabel[0],containername]+([prefixAndLabel[1]] if len(prefixAndLabel) > 1 else []))]
673 else:
674 newnames+=[el]
675 output[k] = newnames
676 return output
677
678def isCLIDDefined(cgen,typename):
679 """
680 Checks container type name is hashable
681 """
682 c = cgen.genClidFromName(typename)
683 return (cgen.getNameFromClid(c) is not None)
684
685def persistent( transient ):
686 """
687 Retrieve persistent EDM class, for xAOD it is the actual class version
688
689 If absent assumes v1
690 """
691
692 #TODO drop EDMDetailsRun3 once migrated HLT reco to Run 4
693 _EDMDetails = EDMDetailsRun3 | EDMDetailsRun4
694
695 if transient in _EDMDetails and 'persistent' in _EDMDetails[transient]:
696 return _EDMDetails[transient]['persistent']
697 return transient
698
699
700def tpMap():
701 """
702 List
703 """
704 l = {}
705
706 #TODO drop EDMDetailsRun3 once migrated HLT reco to Run 4
707 _EDMDetails = EDMDetailsRun3 | EDMDetailsRun4
708 for tr in _EDMDetails:
709 if "xAOD" in tr:
710 continue
711 l[tr] = persistent(tr)
712 return l
713
714
715def testEDMList(edm_list, check_edmdetails_for_version=-1):
716 """
717 Checks EDM list entries for serialization and configuration compliance.
718 """
719
720 #xAOD types that don't expect accompanying Aux containers
721 _noAuxList = ["xAOD::" + contType for contType in ["TrigConfKeys", "BunchConfKey"]]
722
723 cgen = clidGenerator("", False)
724 return_code = 0
725 found_allow_truncation = False
726 serializable_names = []
727 serializable_names_no_label = []
728 serializable_names_no_properties = []
729
730 for i, edm in enumerate(edm_list):
731
732 #check has sufficient entries
733 if len(edm) < 3:
734 log.error("EDM entry too short for " + edm[0])
735 return_code = 1
736 continue
737
738 serializable_name = edm[0]
739 serializable_name_no_label = re.sub(r"\#.*", "", serializable_name)
740 serializable_name_no_properties = serializable_name.split('.')[0]
741
742 serializable_names.append(serializable_name)
743 serializable_names_no_label.append(serializable_name_no_label)
744 serializable_names_no_properties.append(serializable_name_no_properties)
745
746 #check container type name is hashable
747 if not isCLIDDefined(cgen,serializable_name_no_label):
748 log.error("no CLID for " + serializable_name)
749 return_code = 1
750
751 #check that no '.' in entries _not_ containing 'Aux'
752 if "Aux" not in serializable_name and "." in serializable_name:
753 log.error("A '.' found in non-Aux container name " + serializable_name)
754 return_code = 1
755
756 #check for Aux "."
757 if "Aux" in serializable_name and "Aux." not in serializable_name:
758 log.error("no final Aux. in label for " + serializable_name)
759 return_code = 1
760
761 #check contains exactly one #
762 if serializable_name.count("#") != 1:
763 log.error("Invalid naming structure for " + serializable_name)
764 return_code = 1
765 else: # only proceed if type and name can be separated
766 #check that every interface xAOD container directly followed by matching Aux container.
767 if serializable_name.startswith("xAOD") and "Aux" not in serializable_name:
768 cont_type,cont_name = serializable_name.split("#")
769 if cont_type not in _noAuxList:
770 auxmismatch = False
771 if len(edm_list) == i+1:
772 auxmismatch = True
773 cont_to_test = "nothing"
774 else:
775 cont_to_test = edm_list[i+1][0].split(".")[0]+"."
776 cont_type_short = cont_type.replace("Container","")
777 pattern = re.compile(cont_type_short+r".*Aux.*\#"+cont_name+"Aux.")
778 if not pattern.match(cont_to_test):
779 #test if shallow container type.
780 shallow_pattern = re.compile("xAOD::ShallowAuxContainer#"+cont_name+"Aux.")
781 if not shallow_pattern.match(cont_to_test):
782 auxmismatch = True
783 if auxmismatch:
784 log.error("Expected relevant Aux container following interface container %s, but found %s instead.",serializable_name,cont_to_test)
785 return_code = 1
786 else:
787 #check that targets of interface and aux container match
788 targets = set(edm[1].split())
789 if len(edm_list[i+1]) > 1 and cont_to_test.count("#") == 1:
790 contname_to_test = cont_to_test.split("#")[1]
791 targets_to_test = set(edm_list[i+1][1].split())
792 if len(targets ^ targets_to_test) > 0:
793 log.error("Targets of %s (%s) and %s (%s) do not match",cont_name,targets,contname_to_test,targets_to_test)
794 return_code = 1
795
796 #check that Aux always follows non-Aux (our deserialiser relies on that)
797 if i>0 and "Aux" in serializable_name and "Aux" in edm_list[i-1][0]:
798 log.error(f"Aux container {serializable_name} needs to folow the "
799 "associated interface container in the EDM list")
800 return_code = 1
801
802 #check target types are valid. Check that target lists containing higher level targets
803 #have required low level targets (ESD for AOD targets, AODFULL for SLIM targets).
804 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)
805 for file_type in file_types:
806 if file_type not in AllowedOutputFormats:
807 log.error("unknown file type " + file_type + " for " + serializable_name)
808 return_code = 1
809 for higher_level,required_lower_level in [('AOD','ESD'), ('SLIM','AODFULL')]:
810 if higher_level in file_type and required_lower_level not in file_types:
811 log.error("Target list for %s containing '%s' must also contain lower level target '%s'.",serializable_name,file_type,required_lower_level)
812 return_code = 1
813
814 # Check allowTuncation is only at the end
815 tags = edm[3] if len(edm) > 3 else None
816 allow_truncation_flag = False
817 if tags:
818 allow_truncation_flag = allowTruncation in tags
819 if allow_truncation_flag:
820 found_allow_truncation = True
821
822 if found_allow_truncation and not allow_truncation_flag:
823 log.error("All instances of 'allowTruncation' need to be at the END of the EDM serialisation list")
824 return_code = 1
825
826 #end of for loop over entries. Now do complete list checks.
827
828 #check for duplicates:
829 #check that no two EDM entries match after stripping out characters afer '.'
830 if len(set(serializable_names_no_properties)) != len(serializable_names_no_properties):
831 log.error("Duplicates in EDM list! Duplicates found:")
832 import collections.abc
833 for item, count in collections.Counter(serializable_names_no_properties).items():
834 if count > 1:
835 log.error(str(count) + "x: " + str(item))
836 return_code = 1
837
838 #check EDMDetails
839 if check_edmdetails_for_version > 0:
840 EDMDetailList = []
841 if check_edmdetails_for_version == 3:
842 EDMDetailList = EDMDetailsRun3
843 elif check_edmdetails_for_version >= 4:
844 EDMDetailList = EDMDetailsRun4
845 else: log.warning("No EDMDetails for EDM version %d. Skipping EDMDetails check.", check_edmdetails_for_version)
846
847 for EDMDetail in EDMDetailList:
848 if EDMDetail not in serializable_names_no_label:
849 msg = "EDMDetail for " + EDMDetail + " does not correspond to any name in TriggerList"
850 log.error(msg)
851 return_code = 1
852 else: log.debug("Skipping EDMDetails check.")
853
854 return return_code
855
856def dumpListToJson(fileName,runVersion):
857 import json
858 edmDict = dict([(fmt, getTriggerEDMList(flags=None, key=fmt, runVersion=runVersion)) for fmt in AllowedOutputFormats])
859 with open(fileName,'w') as f:
860 json.dump(edmDict, f)
if(pathvar)
STL class.
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:312
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:179
_getRun3TrigObjList(destination, trigEDMList)
getClassesToIgnore(flags)
getRun3BSList(flags, keys)
_getTriggerRun2EDMSlimList(key, edmList)
addHLTNavigationToEDMList(flags, edmList, allDecisions, hypoDecisions)
_getRun3TrigObjProducedInView(theKey, trigEDMList)
getPreregistrationList(version=2, doxAODConversion=True)
dumpListToJson(fileName, runVersion)
getTPList(version=2)
testEDMList(edm_list, check_edmdetails_for_version=-1)
persistent(transient)
_addExtraCollectionsToEDMList(edmList, extraList)
Definition TriggerEDM.py:73
getSafeEDMInsertPosition(edm_list)
isCLIDDefined(cgen, typename)
_getTriggerRun1Run2ObjList(destination, lst)
getRawTriggerEDMList(flags, runVersion=-1)
_handleRun3ViewContainers(el, HLTList)
_getRun3TrigEDMSlimList(key, HLTList)
_InsertContainerNameForHLT(typedict)
getTriggerEDMList(flags, key, runVersion=-1)