ATLAS Offline Software
DictFromChainName.py
Go to the documentation of this file.
1 #! /bin/env python
2 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3 
4 """
5 Class to obtain the chain configuration dictionary from the short or long name
6 
7 Authors of original code in TriggerMenu: Joerg Stelzer, Moritz Backes, Catrin Bernius
8 
9 """
10 __author__ = 'Catrin Bernius'
11 __version__=""
12 __doc__="Decoding of chain name into a dictionary"
13 
14 from TriggerMenuMT.HLT.Menu.EventBuildingInfo import isRoIBasedPEB
15 from TrigConfHLTUtils.HLTUtils import string2hash
16 from AthenaCommon.Logging import logging
17 import re
18 from copy import deepcopy
19 
20 import collections.abc
21 
22 log = logging.getLogger( __name__ )
23 
24 def getOverallL1item(flags, chainName):
25  """
26  Extracts the L1 seed from the chain name, checks for issue in naming, if found, fails
27 
28  In simple case the L1 items is just the last part of the name, after the _L1...
29  There are though more complicated names like ...._L1...._L1..._L1... in such cases the L1 seed item is the last one
30  """
31  assert '_L1' in chainName, 'ERROR IN CHAIN {}, missing L1 seed at the end i.e. _L1...' .format(chainName)
32 
33  from TriggerMenuMT.HLT.Menu.L1Seeds import valid_multiseeds, getSpecificL1Seeds
34  from TrigConfigSvc.TriggerConfigAccess import getL1MenuAccess
35 
36  # this assumes that the last string of a chain name is the overall L1 item
37  cNameParts = chainName.rsplit("_L1",1)
38  l1seed = 'L1_' + cNameParts[-1]
39  # For reference, remapping of L1seeds lived originally in LVL1MenuConfig/LVL1Menu/L1Seeds.py
40  # L1 collections can be read out from there in case they are needed again
41  if l1seed == 'L1_All':
42  return ''
43  if l1seed == 'L1_test': #Multiseeded chains are build like this
44  return 'L1_EM24VHI,L1_MU20'
45  if l1seed in valid_multiseeds:
46  # For these item seed specifications we need to derive the precise list of item names from the L1Menu.
47  lvl1access = getL1MenuAccess(flags)
48  itemsDict = lvl1access.items(includeKeys = ['name','ctpid','triggerType'])
49  l1seedlist = getSpecificL1Seeds(l1seed, itemsDict, flags.Trigger.triggerMenuSetup)
50  return l1seedlist
51 
52  return l1seed
53 
54 def getL1item(flags, chainName):
55  mainL1 = getOverallL1item(flags, chainName)
56  return mainL1
57 
59  """
60  Breaks the item name into parts separated by the _ and from each element extract the threshold name
61 
62  Fails when used on topo items or ill formatted items
63  Examples: L1_MU4 returns [MU4]
64  L1_2MU4 returns [MU4, MU4]
65  L1_MU4_MU6 returns [MU4, MU6]
66  """
67  assert item.startswith("L1_"), "The L1 item {} name does not start with L1".format( item )
68  assert "-" not in item, "The {} is Topo item and can not be parsed".format( item )
69 
70  multThressholds = item.split("_")[1:] # skips the L1_
71  thresholds = []
72  for mt in multThressholds:
73  assert mt[0] != '1', 'Issue while parsing {}, multiplicity of 1, should not be configured, if multiplicity greater than 9 is needed then support for it needs to be added'.format( mt )
74  if mt[0] in '23456789': # first letter is a number == multiplicity, multiplicity 1 is not specified
75  thresholds.extend( int(mt[0])*[mt[1:]] )
76  else:
77  thresholds.append( mt ) # no muliplicity prefix
78  return thresholds
79 
81  """
82  As above but eliminates repeated thresholds
83  """
84  allThreshold = getAllThresholdsFromItem(item)
85  s = set()
86  def _(t):
87  s.add(t)
88  return t
89  return [ _(t) for t in allThreshold if t not in s ]
90 
91 def getEBPartFromParts( chainName, chainParts ):
92  """
93  If EB identifier is in the name, return it.
94 
95  Checks if there is only one identifier
96  """
97  # ---- event building identifier ----
98  from TriggerMenuMT.HLT.Menu.EventBuildingInfo import getAllEventBuildingIdentifiers
99  eventBuildTypes = set( getAllEventBuildingIdentifiers() ).intersection( chainParts )
100  assert len(eventBuildTypes) <= 1, 'Chain {} has more than one Event Building identifier: {}, that is not supported'.format( chainName, eventBuildTypes)
101  if eventBuildTypes:
102  return eventBuildTypes.pop()
103  return ''
104 
105 
106 def getChainThresholdFromName(chainParts, signature):
107  """
108  Decode threshold value from the chain name
109  """
110 
111  from TriggerMenuMT.HLT.Menu.SignatureDicts import getBasePattern
112  pattern = getBasePattern()
113  trigType = []
114  thresholdToPass = 0
115 
116  allThresh = []
117  for cpart in chainParts:
118  m = pattern.match( cpart )
119  if m:
120  log.debug("In getChainThresholdFromName: Pattern found in this string: %s", cpart)
121  groupdict = m.groupdict()
122  allThresh.append(groupdict['threshold'])
123  trigType.append(groupdict['trigType'])
124  if signature == groupdict['trigType']:
125  thresholdToPass = groupdict['threshold']
126  break
127  return thresholdToPass
128 
129 def getChainMultFromDict(chainDict):
130  """
131  Look for all multiplicities stored in chains
132  """
133  allMultis = []
134 
135  for cpart in chainDict['chainParts']:
136  if cpart['multiplicity'] != '':
137  allMultis.append( int(cpart['multiplicity']))
138  return allMultis
139 
140 
141 # For certain chains, we need to include the L1 seed threshold
142 # in the leg name for disambiguation.
143 # This checks that the explicit threshold matches the threshold list
144 # In this case, the seed thresholds cannot be inferred from the L1 name
145 matchL1 = re.compile('L1([a-zA-Z0-9]+)(_.*)?')
146 def verifyExplicitL1Thresholds(chainname,chainparts,L1thresholds):
147  assert len(L1thresholds)>0, f'L1 thresholds cannot be inferred from L1 name for chain {chainname} with explicit L1 seed thresholds'
148 
149  log.debug('Verifying explicit L1 thresholds for %s, received L1thresholds %s',chainname,L1thresholds)
150 
151  counter = 0
152  for part, threshold in zip(chainparts,L1thresholds):
153  if '_L1' in part:
154  matches = matchL1.findall(part)
155  assert len(matches)==1, f"Inappropriate number of L1 thresholds {matches} in chain part '{part}'"
156  thisL1, remainder = matches[0] # list of tuples
157  assert remainder == '', f"Explicit L1 threshold should be last element in chain part {part}"
158  log.verbose(' ChainPart %s has explicit L1 threshold %s',part,thisL1)
159  assert 'PROBE' not in thisL1, f"Omit 'PROBE' from explicit L1 threshold '{thisL1}'"
160  assert thisL1 == threshold.replace('PROBE',''), f"Explicit L1 threshold for chainpart '{thisL1}' does not match threshold '{threshold}' from list"
161  counter += 1
162  assert counter>0, f"Did not find explicit L1 seeds in chain parts for {chainname}!"
163 
164 def unifyJetRecoParts(chainParts):
165  from TriggerMenuMT.HLT.Menu.SignatureDicts import getSignatureInformation, JetRecoKeys
166 
167  """
168  Postprocess the jet chainParts to set all the reco config consistently
169  For formatting reasons, we extract these from the last jet chainPart
170  then propagate to all jet chainParts.
171  Any inconsistencies in non-default options are considered a malformed chain
172  Technically we can't tell if the default was explicitly set, though.
173  """
174 
175  jetDefaults, _ = getSignatureInformation('Jet')
176  # Extract the jet chainParts, and get the reco specs from the last one
177  _jetRecoKeys = JetRecoKeys
178  jetChainParts = [cPart for cPart in chainParts if cPart['signature'] in ['Jet','Bjet']]
179  jetRecoDict = {k:jetChainParts[-1][k] for k in _jetRecoKeys}
180 
181  # Check consistency of the preceding jet parts
182  for p in jetChainParts[:-1]:
183  for k in _jetRecoKeys:
184  # Assume that keys are all defined
185  if p[k] != jetDefaults[k]: # OK if the spec is default
186  log.error('Inconsistent jet reco setting for %s in chainPart %s', k, p['chainPartName'])
187  log.error('Jet chainParts: %s', jetChainParts)
188  raise RuntimeError('Jet reco should be specified only in the last jet chainPart')
189 
190  # Substitute the reco settings consistently and return
191  newChainParts = []
192  for cPart in chainParts:
193  newCPart = deepcopy(cPart)
194  if newCPart['signature'] in ['Jet','Bjet']:
195  for k in _jetRecoKeys:
196  newCPart[k] = jetRecoDict[k]
197  newChainParts.append(newCPart)
198 
199  return newChainParts
200 
201 def analyseChainName(chainName, L1thresholds, L1item):
202  """
203  Function to obtain the chain configuration dictionary from the short name by parsing its
204  components and finding the corresponding properties which are defined in SignatureDicts.
205  The naming convention is defined in https://twiki.cern.ch/twiki/bin/view/Atlas/TriggerNamingRun2
206  and https://twiki.cern.ch/twiki/bin/view/Atlas/TriggerNamingRun3
207  """
208 
209  # ---- dictionary with all chain properties ----
210  from TriggerMenuMT.HLT.Menu.SignatureDicts import ChainDictTemplate
211  from TriggerMenuMT.HLT.Menu.SignatureDicts import getSignatureInformation
212  from TriggerMenuMT.HLT.Jet.JetRecoCommon import etaRangeAbbrev
213  genchainDict = deepcopy(ChainDictTemplate)
214  genchainDict['chainName'] = chainName
215 
216  # ---- remove the L1 item from the name ----
217  hltChainName = chainName.rsplit("_L1",1)[0]
218 
219  # ---- check for HLT_HLT in name ---
220  if 'HLT_HLT' in hltChainName:
221  log.error("%s is incorrect, please fix the HLT_HLT part", hltChainName)
222  raise RuntimeError("[analyseChainName] chain name contains HLT_HLT, please fix in corresponding menu where this chain is included")
223 
224  # ---- specific chain part information ----
225  allChainProperties=[]
226  cparts = hltChainName.split("_")
227  if 'HLT' in hltChainName:
228  cparts.remove('HLT')
229 
230  # ---- handle EB directive ----
231  genchainDict['eventBuildType'] = getEBPartFromParts( chainName, cparts )
232  if genchainDict['eventBuildType']:
233  cparts.remove( genchainDict['eventBuildType'] )
234 
235  hltChainNameShort = '_'.join(cparts)
236 
237  # ---- identify the topo algorithm and add to genchainDict -----
238  from TriggerMenuMT.HLT.Menu.SignatureDicts import AllowedTopos, AllowedTopos_comb, AllowedTopos_Bphysics_topoVariant, AllowedTopos_Bphysics_topoExtra
239  topo = ''
240  topos=[]
241  extraComboHypos = []
242  bphys_topoVariant=[]
243  bphys_topoExtra = []
244  bphysTopos = False
245  toposIndexed={}
246  topoindex = -5
247  for cindex, cpart in enumerate(cparts):
248  #should make this if...elif...?
249  if cpart in AllowedTopos:
250  log.debug('" %s" is in this part of the name %s -> topo alg', AllowedTopos, cpart)
251  topo = cpart
252  topoindex = cindex
253  toposIndexed.update({topo : topoindex})
254  hltChainNameShort=hltChainNameShort.replace('_'+cpart, '')
255  topos.append(topo)
256  elif cpart in AllowedTopos_Bphysics_topoVariant:
257  log.debug('[analyseChainName] chain part %s is a BLS topo variant, adding to bphys_topoVariant', cpart)
258  bphys_topoVariant.append(cpart)
259  toposIndexed.update({cpart : cindex})
260  bphysTopos = True
261  elif cpart in AllowedTopos_Bphysics_topoExtra:
262  log.debug('[analyseChainName] chain part %s is a BLS extra topo hypo, adding to bphys_topoExtra', cpart)
263  bphys_topoExtra.append(cpart)
264  toposIndexed.update({cpart : cindex})
265  bphysTopos = True
266  else:
267  log.debug('[analyseChainName] chain part %s is not a general topo, BLS extra or variant topo hypo, checking comb topos next', cpart)
268  if cpart in AllowedTopos_comb:
269  log.debug('[analyseChainName] chain part %s is a combined topo hypo, adding to extraComboHypo', cpart)
270  toposIndexed.update({cpart : cindex})
271  extraComboHypos.append(cpart)
272 
273  genchainDict['topo'] = topos
274  genchainDict['extraComboHypos'] = extraComboHypos
275 
276  if bphysTopos is True:
277  genchainDict['topoVariant'] = bphys_topoVariant
278  genchainDict['topoExtra'] = bphys_topoExtra
279 
280  # remove the parts that have been already identified
281  for t, i in enumerate(toposIndexed):
282  if (t in cparts):
283  log.debug('topo %s with index %s is going to be deleted from chain parts', t, i)
284  del cparts[i]
285 
286 
287  # ---- Find the signature defining patterns ----
288  # ---- and write them out in dictionary ----
289  # ---- expected format: <Multiplicity(int)><TriggerType(str)>
290  # <Threshold(int)><isolation,...(str|str+int)> ----
291  # EXCEPT FOR CHAINS ...
292  from TriggerMenuMT.HLT.Menu.SignatureDicts import getBasePattern
293  pattern = getBasePattern()
294  mdicts=[]
295  multichainindex=[]
296 
297 
298  # ---- obtain dictionary parts for signature defining patterns ----
299  from TriggerMenuMT.HLT.Menu.SignatureDicts import getSignatureNameFromToken, AllowedCosmicChainIdentifiers, \
300  AllowedCalibChainIdentifiers, AllowedMonitorChainIdentifiers, AllowedBeamspotChainIdentifiers
301 
302  from TriggerMenuMT.HLT.Config.Utility.MenuAlignmentTools import get_alignment_group_from_pattern as getAlignmentGroupFromPattern
303 
304  def buildDict(signature, sigToken ):
305  groupdict = {'signature': signature, 'threshold': '', 'multiplicity': '',
306  'trigType': sigToken, 'extra': ''}
307  mdicts.append( groupdict )
308 
309  for cpart in cparts:
310 
311  log.debug("Looping over chain part: %s", cpart)
312  m = pattern.match(cpart)
313 
314  if m:
315  log.debug("Pattern found in this string: %s", cpart)
316  groupdict = m.groupdict()
317 
318  multiChainIndices = [i for i in range(len(hltChainNameShort)) if ( hltChainNameShort.startswith(cpart, i) ) ]
319  log.debug("MultiChainIndices: %s", multiChainIndices)
320  for theMultiChainIndex in multiChainIndices:
321  # this check is necessary for the bjet chains, example: j45_bloose_3j45
322  # j45 would be found in [0, 13], and 3j45 in [12]
323  # so need to make sure the multiplicities are considered here!
324  if (theMultiChainIndex != 0) & (hltChainNameShort[theMultiChainIndex-1] != '_'):
325  continue
326 
327  if theMultiChainIndex not in multichainindex:
328  multichainindex.append(theMultiChainIndex)
329 
330  log.debug("HLTChainName: %s", hltChainName)
331  log.debug("HLTChainNameShort: %s", hltChainNameShort)
332  log.debug("cpart: %s", cpart)
333  log.debug("groupdict: %s", groupdict)
334  log.debug("multichainindex: %s", multichainindex)
335 
336  sName = getSignatureNameFromToken(cpart)
337 
338  groupdict['signature'] = sName
339  log.debug("groupdict['signature']: %s",groupdict['signature'])
340 
341  mdicts.append(groupdict)
342 
343  elif cpart =='noalg':
344  multichainindex.append(hltChainNameShort.index(cpart))
345  buildDict( 'Streaming', 'streamer')
346  break # stop loop here so that further parts like noalg_idmon are discarded, this allows have copies with different output streams and prescales. Caveat: all noalg chains go into the Streaming slice
347  elif cpart =='acceptedevts':
348  multichainindex.append(hltChainNameShort.index(cpart))
349  buildDict( 'Calib', 'calib')
350  break # stop loop here so that further parts like acceptedevts_larnoiseburst are discarded. They are only used to define different prescales
351  else:
352  for chainCategory in [(['mb'], 'MinBias', 'mb'),
353  (['hi'], 'HeavyIon', 'mb'),
354  (AllowedCosmicChainIdentifiers, 'Cosmic', 'cosmic'),
355  (AllowedCalibChainIdentifiers, 'Calib', 'calib'),
356  (AllowedMonitorChainIdentifiers, 'Monitor', 'calib'),
357  (AllowedBeamspotChainIdentifiers, 'Beamspot', 'beamspot'),
358  (['eb'], 'EnhancedBias', 'eb')]:
359  if cpart in chainCategory[0]:
360  log.debug('Doing chain type %s', chainCategory[1])
361  multichainindex.append(hltChainNameShort.index(cpart))
362  buildDict(chainCategory[1], chainCategory[2])
363 
364 
365  # If multiple parts exist, split the string and analyse each
366  # part depending on the signature it belongs to
367  multichainparts=[]
368  multichainindex = sorted(multichainindex, key=int)
369  cN = deepcopy(hltChainNameShort)
370  for i in reversed(multichainindex):
371  if i!=0:
372  log.debug('Appending to multichainparts (i!=0): %s', hltChainNameShort[i:len(cN)])
373 
374  multichainparts.append(hltChainNameShort[i:len(cN)])
375  cN = cN[0:i-1]
376  else:
377  log.debug('Appending to multichainparts: %s', hltChainNameShort[i:len(cN)])
378  multichainparts.append(cN)
379  log.debug("multichainparts: %s",multichainparts)
380 
381  # build the chainProperties dictionary for each part of the chain
382  # add it to a allChainProperties
383  multichainparts.reverse()
384  log.debug('multichainparts after reverse: %s', multichainparts)
385 
386  # verify if the L1 setup is consistent with the chain
387  # sizes of seeds matter
388  if len(L1thresholds) != 0:
389  assert len(L1thresholds) == len(multichainparts), 'ERROR IN CHAIN {} definition, l1 thresholds specified {} have different length than chain parts {}'\
390  .format(chainName, str(L1thresholds), str(multichainparts) )
391 
392  # tmp removed this check since the L1seeds depend on sym or asym cases
393  # if len(L1thresholds) == 0:
394  # getAllThresholdsFromItem, getUniqueThresholdsFromItem
395  # assert len( getAllThresholdsFromItem( L1item )) == len(multichainparts), 'ERROR IN CHAIN {} definition, l1 thresholds extracted from the L1 item name {} have different length than chain parts {}, set L1 thresholds ChainProp'\
396  # .format( chainName, str( getAllThresholdsFromItem( L1item ) ), str(multichainparts))
397 
398  # check the case when _L1 appears more than once in the name
399  if chainName.count("_L1") > 1:
400  verifyExplicitL1Thresholds(chainName,multichainparts,L1thresholds)
401 
402  for chainindex, chainparts in enumerate(multichainparts):
403  chainProperties = {} #will contain properties for one part of chain if multiple parts
404  chainpartsNoL1 = chainparts.rsplit('_L1',1)[0] if 'L1' in chainparts else chainparts
405 
406  if len(L1thresholds) != 0:
407  chainProperties['L1threshold'] = L1thresholds[chainindex]
408  else:
409  __th = getAllThresholdsFromItem ( L1item )
410  assert chainindex < len(__th), "In defintion of the chain {chainName} there is not enough thresholds to be used, index: {chainindex} >= number of thresholds, thresholds are: {__th}"
411  chainProperties['L1threshold'] = __th[chainindex] #replced getUniqueThresholdsFromItem
412 
413  parts=chainpartsNoL1.split('_')
414  log.debug("[analyseChainName] chain parts w/o L1 are %s", parts)
415  if None in parts:
416  log.error("[analyseChainName] chainpartsNoL1 -> parts: %s -> %s", chainpartsNoL1, parts)
417  raise Exception("[analyseChainName] parts contains None, please identify how this is happening")
418  parts=list(filter(None,parts))
419  log.debug("[analyseChainName] chainpartsNoL1 %s, parts %s", chainpartsNoL1, parts)
420 
421  # --- check for duplicate strings in chain part name ---
422  duplicateParts = ([item for item, count in collections.Counter(parts).items() if count > 1])
423  log.debug("[analyseChainName] chainpartsNoL1 %s contains duplicate strings %s", chainpartsNoL1, duplicateParts)
424  if duplicateParts:
425  log.error("[analyseChainName] chain %s has duplicate strings %s, please fix!!", chainName, duplicateParts)
426  raise RuntimeError("[analyseChainName] Check the chain name, there are duplicate configurations: %s", duplicateParts)
427 
428  chainProperties['trigType']=mdicts[chainindex]['trigType']
429  if 'extra' in chainProperties:
430  raise RuntimeError("[analyseChainName] Overwrote 'extra' value, only one supported!")
431  else:
432  chainProperties['extra']=mdicts[chainindex]['extra']
433  multiplicity = mdicts[chainindex]['multiplicity'] if not mdicts[chainindex]['multiplicity'] == '' else '1'
434  chainProperties['multiplicity'] = multiplicity
435  chainProperties['threshold']=mdicts[chainindex]['threshold']
436  chainProperties['signature']=mdicts[chainindex]['signature']
437 
438  # if we have a L1 topo in a multi-chain then we want to remove it from the chain name
439  # but only if it's the same as the L1item_main; otherwise it belongs to chain part and we q
440  # have to keep it in the name
441  chainProperties['chainPartName'] = chainparts
442  if ('-' in L1item) and (len(multichainparts) > 1):
443  chainProperties['chainPartName'] = chainpartsNoL1
444 
445  if len(multichainparts) > 1 and L1item.count("_") > 1 :
446  chainProperties['chainPartName'] = chainpartsNoL1
447 
448  log.debug('[analyseChainname] Chainparts: %s', chainparts)
449  if (chainProperties['signature'] != 'Cosmic') \
450  & (chainProperties['signature'] != 'Calib')\
451  & (chainProperties['signature'] != 'Streaming') \
452  & (chainProperties['signature'] != 'Beamspot') \
453  & (chainProperties['signature'] != 'Monitor') :
454  parts.pop(0)
455 
456 
457  #---- Check if topo is a bphysics topo -> change signature ----
458  from TriggerMenuMT.HLT.Menu.SignatureDicts import AllAllowedTopos_Bphysics
459  for t in genchainDict['topo']:
460  if (t in AllAllowedTopos_Bphysics):
461  chainProperties['signature'] = 'Bphysics'
462  if "tnpInfo" in chainProperties.keys() and chainProperties['tnpInfo'] != "":
463  chainProperties['alignmentGroup'] = getAlignmentGroupFromPattern('Bphysics',chainProperties['tnpInfo'])
464  else:
465  chainProperties['alignmentGroup'] = getAlignmentGroupFromPattern('Bphysics',chainProperties['extra'])
466 
467  # ---- import the relevant dictionaries for each part of the chain ----
468  SignatureDefaultValues, allowedSignaturePropertiesAndValues = getSignatureInformation(chainProperties['signature'])
469  log.debug('SignatureDefaultValues: %s', SignatureDefaultValues)
470  allDefaults = list(SignatureDefaultValues.values())
471  log.debug('All default values in a list')
472 
473  # ---- update chain properties with default properties ----
474  result = deepcopy(SignatureDefaultValues)
475  result.update(chainProperties)
476  chainProperties = result
477 
478  # ---- check that all parts to be matched are not specified as defaults already ----
479  overlaps = [x for x in parts if x in allDefaults]
480  log.debug("parts that are also in defaults: %s ", overlaps)
481  if overlaps:
482  #log.error("[analyseChainName] The following string(s) is/are already defined as defaults, please remove: %s", overlaps)
483  #raise RuntimeError("[analyseChainname] Default config appearing in chain name, please remove: %s", overlaps)
484  log.warning("[analyseChainName] The following chainpart %s contains the string(s) %s which is/are already defined as defaults, please remove!", chainparts, overlaps)
485 
486  # ---- check remaining parts for complete matches in allowedPropertiesAndValues Dict ----
487  # ---- unmatched = list of tokens that are not found in the allowed values as a whole ----
488  matchedparts = []
489  log.debug("[analyseChainname] parts to match are %s", parts)
490  for pindex, part in enumerate(parts):
491  for prop, allowedValues in allowedSignaturePropertiesAndValues.items():
492  if part in allowedValues:
493  if type(chainProperties[prop]) is list:
494  chainProperties[prop] += [part]
495  else:
496  chainProperties[prop] = part
497  matchedparts.append(part)
498 
499  # Jet-specific operations
500  if chainProperties['signature']=='Jet':
501 
502  # ----- at this point we can figure out if the chain is a bJet chain and update defaults accordingly
503  if chainProperties['bTag'] != '':
504  log.debug('Setting b-jet chain defaults')
505  # b-jet chain, so we now use the bJet defaults if they have not already been overriden
506  bJetDefaultValues, allowedbJetPropertiesAndValues = getSignatureInformation('Bjet')
507  for prop, value in bJetDefaultValues.items():
508  propSet=False
509  for value in allowedbJetPropertiesAndValues[prop]:
510  if value in matchedparts:
511  propSet=True
512  break
513 
514  # if the property was not set already, then set if according to the b-jet defaults
515  if propSet is False:
516  log.debug('Changing %s from %s to %s', prop, str(chainProperties[prop]), str(bJetDefaultValues[prop]))
517  chainProperties[prop] = bJetDefaultValues[prop]
518 
519 
520  if chainProperties['signature'] == 'Jet' and chainProperties['beamspotChain'] != '':
521  log.debug('Setting beamspot chain defaults')
522  BeamspotDefaultValues, allowedBeamspotPropertiesAndValues = getSignatureInformation('Beamspot_Jet')
523 
524  for prop, value in BeamspotDefaultValues.items():
525  propSet=False
526  for value in allowedBeamspotPropertiesAndValues[prop]:
527  if value in matchedparts:
528  propSet=True
529  break
530 
531  # if the property was not set already, then set if according to the b-jet defaults
532  if propSet is False:
533  log.debug('Changing %s from %s to %s', prop, str(chainProperties[prop]), str(BeamspotDefaultValues[prop]))
534  chainProperties[prop] = BeamspotDefaultValues[prop]
535 
536 
537  # Substitute after b-jet defaults have been set otherwise they will be overridden
538  if chainProperties['extra']:
539  # Atypical handling here: jets translate the 1 char suffix to an eta range
540  try:
541  etarange = etaRangeAbbrev[chainProperties['extra']]
542  chainProperties['etaRange'] = etarange
543  except KeyError:
544  raise RuntimeError(f"Invalid jet 'extra' value {chainProperties['extra']} in {chainProperties['chainPartName']}. Only [a,c,f] allowed")
545 
546  # ---- checking the parts that haven't been matched yet ----
547  log.debug("matched parts %s", matchedparts)
548  leftoverparts = set(parts)-set(matchedparts)
549  log.debug('leftoverparts %s', leftoverparts)
550  for pindex, part in enumerate(leftoverparts):
551  for prop, allowedValues in allowedSignaturePropertiesAndValues.items():
552  if prop in chainProperties.keys():
553  continue
554  for aV in allowedValues:
555  if (aV in part):
556  if (chainProperties['signature'] in ['Egamma', 'Muon'] )& (prop in ['trkInfo','hypoInfo']):
557  chainProperties[prop] = part
558  part = part.replace(part,'')
559  else:
560  chainProperties[prop] = aV
561  part = part.replace(aV,'')
562  break # done with allowed values for that property
563 
564  assert len(part.split()) == 0, "These parts of the chain name {} are not understood: {}".format(chainpartsNoL1,part)
565 
566 
567  # ---- remove properties that aren't allowed in the chain properties for a given siganture ----
568  forbiddenProperties = set(chainProperties.keys()) - set(allowedSignaturePropertiesAndValues.keys())
569  log.debug('chainProperties: %s', sorted(set(chainProperties.keys())))
570  log.debug('allowedSignaturePropertiesAndValues: %s', sorted(set(allowedSignaturePropertiesAndValues.keys())))
571  for fb in forbiddenProperties:
572  forbiddenValue = chainProperties.pop(fb)
573  assert forbiddenValue == '', "Property {} not allowed for signature '{}', but specified '{}'".format (fb, chainProperties['signature'], forbiddenValue)
574 
575  # ---- set the alignment group here, once we have fully worked out the properties ----
576  # ---- this is for the benefit of the probe leg in T&P chains ----
577 
578  if any(x in chainName for x in ('larnoiseburst', 'idcalib', 'metcalo', 'mettrk')):
579  chainProperties['alignmentGroup'] = 'JetMET'
580  else:
581  if 'tnpInfo' in chainProperties.keys() and chainProperties['tnpInfo'] != "":
582  chainProperties['alignmentGroup'] = getAlignmentGroupFromPattern(mdicts[chainindex]['signature'],chainProperties['tnpInfo'])
583  else:
584  chainProperties['alignmentGroup'] = getAlignmentGroupFromPattern(mdicts[chainindex]['signature'],chainProperties['extra'])
585 
586  # ---- the info of the general and the specific chain parts dict ----
587  allChainProperties.append(chainProperties)
588 
589  # ---- depending on if signatures are different in this chain, break up the chainProperties dictionary ----
590  # ---- finally also taking care of the signature key ----
591  genchainDict['chainParts'] = allChainProperties
592  for cPart in allChainProperties:
593  if cPart['signature'] == 'Jet' and cPart['bTag'] != '':
594  cPart['signature'] = 'Bjet'
595  if 'tnpInfo' in cPart.keys() and cPart['tnpInfo'] != "":
596  cPart['alignmentGroup'] = getAlignmentGroupFromPattern('Bjet', cPart['tnpInfo'])
597  else:
598  cPart['alignmentGroup'] = getAlignmentGroupFromPattern('Bjet', cPart['extra'])
599  genchainDict['signatures'] += [cPart['signature']]
600  genchainDict['alignmentGroups'] += [cPart['alignmentGroup']]
601 
602  # Postprocess chains with multiple jet chainParts
603  # to set them consistently from only one specification
604  if len(genchainDict['chainParts'])>1:
605  if 'Jet' in genchainDict['signatures'] or 'Bjet' in genchainDict['signatures']:
606  genchainDict['chainParts'] = unifyJetRecoParts(genchainDict['chainParts'])
607 
608  #genchainDict['signature'] = allChainProperties[0]['signature']
609 
610  return genchainDict
611 
612 
614  rt = []
615  for i in A:
616  if isinstance(i,list): rt.extend(flattenChainGroups(i))
617  else: rt.append(i)
618  return rt
619 
620 def checkChainStream(myStream):
621  if len(myStream) == 1:
622  if myStream[0] == 'express':
623  return False
624  return True
625 
626 def dictFromChainName(flags, chainInfo):
627  """
628  Transforms ChainProp into the ChainDict
629 
630  The logic:
631  ---- Loop over all chains (keys) in dictionary ----
632  ---- Then complete the dict with other info ----
633  Default input format will be a ChainProp object:
634  ChainProp(name: str, groups: list[str], l1SeedThresholds: list[str] = [], stream: list[str] = ['Main'],
635  monGroups: list[str] = [], mergingStrategy: str = 'auto', mergingOrder: list[str] = [], mergingOffset: int = -1),
636  but for nwo plain chain name is also supported
637  """
638 
639  # these if/elif/else statements are due to temporary development
640  if type(chainInfo) is str:
641  chainName = chainInfo
642  l1Thresholds = []
643  stream = ''
644  groups = []
645  mergingStrategy = 'parallel'
646  mergingOffset = -1
647  mergingOrder = []
648  monGroups = []
649 
650  elif 'ChainProp' in str(type(chainInfo)):
651  #this is how we define chains in the menu - the normal behaviour of this function
652  chainName = chainInfo.name
653  l1Thresholds = chainInfo.l1SeedThresholds
654  stream = chainInfo.stream
655  groups = flattenChainGroups(chainInfo.groups)
656  mergingStrategy = chainInfo.mergingStrategy
657  mergingOffset = chainInfo.mergingOffset
658  mergingOrder = chainInfo.mergingOrder
659  monGroups = chainInfo.monGroups
660 
661  else:
662  raise RuntimeError("Format of chainInfo passed to genChainDict not known")
663 
664  #check here the content of the stream
665  if not checkChainStream(stream):
666  raise RuntimeError("Chain {}, format of chainInfo:stream {} is not valid".format(chainName, stream))
667 
668  L1item = getL1item(flags, chainName)
669 
670  log.debug("Analysing chain with name: %s", chainName)
671  chainDict = analyseChainName(chainName, l1Thresholds, L1item)
672  log.debug('ChainProperties: %s', chainDict)
673 
674  for chainPart in chainDict['chainParts']:
675  # fill the sigFolder and subSigs folder
676  for sf in chainPart['sigFolder']:
677  if sf in chainDict['sigDicts']:
678  chainDict['sigDicts'][sf].extend(chainPart['subSigs'])
679  else:
680  chainDict['sigDicts'].update({sf:chainPart['subSigs']})
681  if sf == 'Bjet':
682  chainDict['sigDicts'].update({'Jet':['Jet']})
683 
684 
685  thisSignature = chainPart['signature']
686  thisAlignGroup = chainPart['alignmentGroup']
687  thisExtra = chainPart['extra']
688  thisL1 = chainPart['L1threshold']
689  thisChainPartName = chainPart['chainPartName']
690 
691  if thisSignature in ['Muon','Bphysics']:
692  if 'MuonnoL1' in thisAlignGroup or 'lateMu' in thisExtra:
693  if 'FSNOSEED' not in thisL1:
694  log.error("Muon noL1 and lateMu chain should be seeded from FSNOSEED. Check %s seeded from %s (defined L1: %s), signature %s",chainDict['chainName'],thisL1,l1Thresholds,thisSignature)
695  else:
696  if 'MU' not in thisL1:
697  log.error("Standard muon and Bphysics chain should be seeded from L1_MU. Check %s seeded from %s (defined L1: %s), signature %s",chainDict['chainName'],thisL1,l1Thresholds,thisSignature)
698  #incorrectL1=True
699 
700  if thisSignature in ['Electron','Photon'] and not ('UPC' in stream): # don't apply for HI UPC chains
701  if 'EM' not in thisL1 and 'BKee' not in thisL1 and 'All' not in thisL1:
702  log.error("Standard egamma chains should be seeded from L1_EM. Check %s seeded from %s (defined L1: %s), signature %s",chainDict['chainName'],thisL1,l1Thresholds,thisSignature)
703  #incorrectL1=True
704 
705  if thisSignature in ['Tau']:
706  if 'TAU' not in thisL1:
707  log.error("Standard tau chains should be seeded from L1_TAU. Check %s seeded from %s (defined L1: %s), signature %s",chainDict['chainName'],thisL1,l1Thresholds,thisSignature)
708  #incorrectL1=True
709 
710  if thisSignature in ['Jet','Bjet','MET','UnconventionalTracking']:
711  if 'FSNOSEED' not in thisL1:
712  log.error("Jet, b-jet, MET chains should be seeded from FSNOSEED. Check %s seeded from %s (defined L1: %s), signature %s",chainDict['chainName'],thisL1,l1Thresholds,thisSignature)
713  #incorrectL1=True
714 
715  if thisChainPartName in ['noalg']:
716  # All streamers should be unseeded except RoI-based PEB streamers which need a real RoI for PEB
717  if 'FSNOSEED' not in thisL1 and not isRoIBasedPEB(chainDict['eventBuildType']):
718  log.error("noalg chains should be seeded from FSNOSEED. Check %s seeded from %s (defined L1: %s), signature %s",chainDict['chainName'],thisL1,l1Thresholds,thisSignature)
719  #incorrectL1=True
720 
721 # if incorrectL1 is True:
722 # raise Exception("You are using incorrect L1 seed, please check for ERROR messages...")
723 
724 
725  log.debug('Parts: signature %s, align %s, extra %s, L1 %s', thisSignature, thisAlignGroup, thisExtra, thisL1)
726 
727 
728 
729  # setting the L1 item
730  chainDict['L1item'] = L1item
731  chainDict['stream'] = stream
732  chainDict['groups'] = groups
733  chainDict['mergingStrategy'] = mergingStrategy
734  chainDict['mergingOffset'] = mergingOffset
735  chainDict['mergingOrder'] = mergingOrder
736  chainDict['monGroups'] = monGroups
737  chainDict['chainNameHash'] = string2hash(chainDict['chainName'])
738 
739 
740  allChainMultiplicities = getChainMultFromDict(chainDict)
741  chainDict['chainMultiplicities'] = allChainMultiplicities
742  log.debug('Setting chain multiplicities: %s ', allChainMultiplicities)
743 
744 
748 
749  return chainDict
750 
751 def main():
752 
753  from AthenaConfiguration.AllConfigFlags import initConfigFlags
754  flags = initConfigFlags()
755 
756  parser = flags.getArgumentParser()
757  parser.add_argument(
758  'chain',
759  type=str,
760  help='Chain name to be parsed into chain dictionary')
761  parser.add_argument(
762  '-t', '--thresholds',
763  nargs='+',
764  help='L1 thresholds, needed for multileg or fullscan chains')
765  args = flags.fillFromArgs(parser=parser)
766  flags.lock()
767 
768  if args.thresholds:
769  from TriggerMenuMT.HLT.Config.Utility.ChainDefInMenu import ChainProp
770  cd = dictFromChainName(flags, ChainProp(args.chain,l1SeedThresholds=args.thresholds,groups=[]))
771  else:
772  cd = dictFromChainName(flags, args.chain)
773 
774  from pprint import pprint
775  pprint(cd)
776 
777  import json
778  with open(f'{args.chain}.dict.json','w') as f:
779  json.dump(cd, f, indent=2)
780 
781  return 0
782 
783 if __name__=='__main__':
784  main()
785 
DictFromChainName.getAllThresholdsFromItem
def getAllThresholdsFromItem(item)
Definition: DictFromChainName.py:58
DictFromChainName.getOverallL1item
def getOverallL1item(flags, chainName)
Definition: DictFromChainName.py:24
Run3DQTestingDriver._
_
Definition: Run3DQTestingDriver.py:35
vtune_athena.format
format
Definition: vtune_athena.py:14
python.HLT.Menu.SignatureDicts.getBasePattern
def getBasePattern()
Definition: SignatureDicts.py:1406
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
DictFromChainName.main
def main()
Definition: DictFromChainName.py:751
DictFromChainName.dictFromChainName
def dictFromChainName(flags, chainInfo)
Definition: DictFromChainName.py:626
DictFromChainName.checkChainStream
def checkChainStream(myStream)
Definition: DictFromChainName.py:620
intersection
std::vector< std::string > intersection(std::vector< std::string > &v1, std::vector< std::string > &v2)
Definition: compareFlatTrees.cxx:25
covarianceTool.filter
filter
Definition: covarianceTool.py:514
DictFromChainName.unifyJetRecoParts
def unifyJetRecoParts(chainParts)
Definition: DictFromChainName.py:164
python.TriggerConfigAccess.getL1MenuAccess
L1MenuAccess getL1MenuAccess(flags=None)
Definition: TriggerConfigAccess.py:129
DictFromChainName.getChainThresholdFromName
def getChainThresholdFromName(chainParts, signature)
Definition: DictFromChainName.py:106
DictFromChainName.getUniqueThresholdsFromItem
def getUniqueThresholdsFromItem(item)
Definition: DictFromChainName.py:80
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
DictFromChainName.getChainMultFromDict
def getChainMultFromDict(chainDict)
Definition: DictFromChainName.py:129
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
DictFromChainName.flattenChainGroups
def flattenChainGroups(A)
Definition: DictFromChainName.py:613
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename T::value_type > sorted(T begin, T end)
Helper function to create a sorted vector from an unsorted one.
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:232
DictFromChainName.getL1item
def getL1item(flags, chainName)
Definition: DictFromChainName.py:54
python.HLT.Menu.SignatureDicts.getSignatureNameFromToken
def getSignatureNameFromToken(chainpart)
Definition: SignatureDicts.py:1333
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
DictFromChainName.verifyExplicitL1Thresholds
def verifyExplicitL1Thresholds(chainname, chainparts, L1thresholds)
Definition: DictFromChainName.py:146
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:79
Trk::open
@ open
Definition: BinningType.h:40
ActsTrk::detail::MakeDerivedVariant::extend
constexpr std::variant< Args..., T > extend(const std::variant< Args... > &, const T &)
Definition: MakeDerivedVariant.h:17
python.HLT.Menu.SignatureDicts.getSignatureInformation
def getSignatureInformation(signature)
Definition: SignatureDicts.py:1355
python.HLT.Menu.L1Seeds.getSpecificL1Seeds
def getSpecificL1Seeds(l1seedname, l1itemobject, menu_name)
Definition: L1Seeds.py:348
python.AllConfigFlags.initConfigFlags
def initConfigFlags()
Definition: AllConfigFlags.py:19
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
HLTUtils.string2hash
def string2hash(string)
Definition: HLTUtils.py:5
str
Definition: BTagTrackIpAccessor.cxx:11
python.HLT.Menu.EventBuildingInfo.getAllEventBuildingIdentifiers
def getAllEventBuildingIdentifiers()
Definition: EventBuildingInfo.py:45
DictFromChainName.analyseChainName
def analyseChainName(chainName, L1thresholds, L1item)
Definition: DictFromChainName.py:201
python.HLT.CommonSequences.EventBuildingSequences.isRoIBasedPEB
isRoIBasedPEB
Definition: EventBuildingSequences.py:394
WriteBchToCool.update
update
Definition: WriteBchToCool.py:67
DictFromChainName.getEBPartFromParts
def getEBPartFromParts(chainName, chainParts)
Definition: DictFromChainName.py:91