Loading [MathJax]/extensions/tex2jax.js
ATLAS Offline Software
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
482  # Suppress the warning until we fully decomission the old DeepSet tau triggers in a couple of months
483  if overlaps == ['tracktwoMVA']: overlaps = []
484 
485  if overlaps:
486  #log.error("[analyseChainName] The following string(s) is/are already defined as defaults, please remove: %s", overlaps)
487  #raise RuntimeError("[analyseChainname] Default config appearing in chain name, please remove: %s", overlaps)
488  log.warning("[analyseChainName] The following chainpart %s contains the string(s) %s which is/are already defined as defaults, please remove!", chainparts, overlaps)
489 
490  # ---- check remaining parts for complete matches in allowedPropertiesAndValues Dict ----
491  # ---- unmatched = list of tokens that are not found in the allowed values as a whole ----
492  matchedparts = []
493  log.debug("[analyseChainname] parts to match are %s", parts)
494  for pindex, part in enumerate(parts):
495  for prop, allowedValues in allowedSignaturePropertiesAndValues.items():
496  if part in allowedValues:
497  if type(chainProperties[prop]) is list:
498  chainProperties[prop] += [part]
499  else:
500  chainProperties[prop] = part
501  matchedparts.append(part)
502 
503  # Jet-specific operations
504  if chainProperties['signature']=='Jet':
505 
506  # ----- at this point we can figure out if the chain is a bJet chain and update defaults accordingly
507  if chainProperties['bTag'] != '':
508  log.debug('Setting b-jet chain defaults')
509  # b-jet chain, so we now use the bJet defaults if they have not already been overriden
510  bJetDefaultValues, allowedbJetPropertiesAndValues = getSignatureInformation('Bjet')
511  for prop, value in bJetDefaultValues.items():
512  propSet=False
513  for value in allowedbJetPropertiesAndValues[prop]:
514  if value in matchedparts:
515  propSet=True
516  break
517 
518  # if the property was not set already, then set if according to the b-jet defaults
519  if propSet is False:
520  log.debug('Changing %s from %s to %s', prop, str(chainProperties[prop]), str(bJetDefaultValues[prop]))
521  chainProperties[prop] = bJetDefaultValues[prop]
522 
523 
524  if chainProperties['signature'] == 'Jet' and chainProperties['beamspotChain'] != '':
525  log.debug('Setting beamspot chain defaults')
526  BeamspotDefaultValues, allowedBeamspotPropertiesAndValues = getSignatureInformation('Beamspot_Jet')
527 
528  for prop, value in BeamspotDefaultValues.items():
529  propSet=False
530  for value in allowedBeamspotPropertiesAndValues[prop]:
531  if value in matchedparts:
532  propSet=True
533  break
534 
535  # if the property was not set already, then set if according to the b-jet defaults
536  if propSet is False:
537  log.debug('Changing %s from %s to %s', prop, str(chainProperties[prop]), str(BeamspotDefaultValues[prop]))
538  chainProperties[prop] = BeamspotDefaultValues[prop]
539 
540 
541  # Substitute after b-jet defaults have been set otherwise they will be overridden
542  if chainProperties['extra']:
543  # Atypical handling here: jets translate the 1 char suffix to an eta range
544  try:
545  etarange = etaRangeAbbrev[chainProperties['extra']]
546  chainProperties['etaRange'] = etarange
547  except KeyError:
548  raise RuntimeError(f"Invalid jet 'extra' value {chainProperties['extra']} in {chainProperties['chainPartName']}. Only [a,c,f] allowed")
549 
550  # ---- checking the parts that haven't been matched yet ----
551  log.debug("matched parts %s", matchedparts)
552  leftoverparts = set(parts)-set(matchedparts)
553  log.debug('leftoverparts %s', leftoverparts)
554  for pindex, part in enumerate(leftoverparts):
555  for prop, allowedValues in allowedSignaturePropertiesAndValues.items():
556  if prop in chainProperties.keys():
557  continue
558  for aV in allowedValues:
559  if (aV in part):
560  if (chainProperties['signature'] in ['Egamma', 'Muon'] )& (prop in ['trkInfo','hypoInfo']):
561  chainProperties[prop] = part
562  part = part.replace(part,'')
563  else:
564  chainProperties[prop] = aV
565  part = part.replace(aV,'')
566  break # done with allowed values for that property
567 
568  assert len(part.split()) == 0, "These parts of the chain name {} are not understood: {}".format(chainpartsNoL1,part)
569 
570 
571  # ---- remove properties that aren't allowed in the chain properties for a given siganture ----
572  forbiddenProperties = set(chainProperties.keys()) - set(allowedSignaturePropertiesAndValues.keys())
573  log.debug('chainProperties: %s', sorted(set(chainProperties.keys())))
574  log.debug('allowedSignaturePropertiesAndValues: %s', sorted(set(allowedSignaturePropertiesAndValues.keys())))
575  for fb in forbiddenProperties:
576  forbiddenValue = chainProperties.pop(fb)
577  assert forbiddenValue == '', "Property {} not allowed for signature '{}', but specified '{}'".format (fb, chainProperties['signature'], forbiddenValue)
578 
579  # ---- set the alignment group here, once we have fully worked out the properties ----
580  # ---- this is for the benefit of the probe leg in T&P chains ----
581 
582  if any(x in chainName for x in ('larnoiseburst', 'idcalib', 'metcalo', 'mettrk')):
583  chainProperties['alignmentGroup'] = 'JetMET'
584  else:
585  if 'tnpInfo' in chainProperties.keys() and chainProperties['tnpInfo'] != "":
586  chainProperties['alignmentGroup'] = getAlignmentGroupFromPattern(mdicts[chainindex]['signature'],chainProperties['tnpInfo'])
587  else:
588  chainProperties['alignmentGroup'] = getAlignmentGroupFromPattern(mdicts[chainindex]['signature'],chainProperties['extra'])
589 
590  # ---- the info of the general and the specific chain parts dict ----
591  allChainProperties.append(chainProperties)
592 
593  # ---- depending on if signatures are different in this chain, break up the chainProperties dictionary ----
594  # ---- finally also taking care of the signature key ----
595  genchainDict['chainParts'] = allChainProperties
596  for cPart in allChainProperties:
597  if cPart['signature'] == 'Jet' and cPart['bTag'] != '':
598  cPart['signature'] = 'Bjet'
599  if 'tnpInfo' in cPart.keys() and cPart['tnpInfo'] != "":
600  cPart['alignmentGroup'] = getAlignmentGroupFromPattern('Bjet', cPart['tnpInfo'])
601  else:
602  cPart['alignmentGroup'] = getAlignmentGroupFromPattern('Bjet', cPart['extra'])
603  genchainDict['signatures'] += [cPart['signature']]
604  genchainDict['alignmentGroups'] += [cPart['alignmentGroup']]
605 
606  # Postprocess chains with multiple jet chainParts
607  # to set them consistently from only one specification
608  if len(genchainDict['chainParts'])>1:
609  if 'Jet' in genchainDict['signatures'] or 'Bjet' in genchainDict['signatures']:
610  genchainDict['chainParts'] = unifyJetRecoParts(genchainDict['chainParts'])
611 
612  #genchainDict['signature'] = allChainProperties[0]['signature']
613 
614  return genchainDict
615 
616 
618  rt = []
619  for i in A:
620  if isinstance(i,list): rt.extend(flattenChainGroups(i))
621  else: rt.append(i)
622  return rt
623 
624 def checkChainStream(myStream):
625  if len(myStream) == 1:
626  if myStream[0] == 'express':
627  return False
628  return True
629 
630 def dictFromChainName(flags, chainInfo):
631  """
632  Transforms ChainProp into the ChainDict
633 
634  The logic:
635  ---- Loop over all chains (keys) in dictionary ----
636  ---- Then complete the dict with other info ----
637  Default input format will be a ChainProp object:
638  ChainProp(name: str, groups: list[str], l1SeedThresholds: list[str] = [], stream: list[str] = ['Main'],
639  monGroups: list[str] = [], mergingStrategy: str = 'auto', mergingOrder: list[str] = [], mergingOffset: int = -1),
640  but for nwo plain chain name is also supported
641  """
642 
643  # these if/elif/else statements are due to temporary development
644  if type(chainInfo) is str:
645  chainName = chainInfo
646  l1Thresholds = []
647  stream = ''
648  groups = []
649  mergingStrategy = 'parallel'
650  mergingOffset = -1
651  mergingOrder = []
652  monGroups = []
653 
654  elif 'ChainProp' in str(type(chainInfo)):
655  #this is how we define chains in the menu - the normal behaviour of this function
656  chainName = chainInfo.name
657  l1Thresholds = chainInfo.l1SeedThresholds
658  stream = chainInfo.stream
659  groups = flattenChainGroups(chainInfo.groups)
660  mergingStrategy = chainInfo.mergingStrategy
661  mergingOffset = chainInfo.mergingOffset
662  mergingOrder = chainInfo.mergingOrder
663  monGroups = chainInfo.monGroups
664 
665  else:
666  raise RuntimeError("Format of chainInfo passed to genChainDict not known")
667 
668  #check here the content of the stream
669  if not checkChainStream(stream):
670  raise RuntimeError("Chain {}, format of chainInfo:stream {} is not valid".format(chainName, stream))
671 
672  L1item = getL1item(flags, chainName)
673 
674  log.debug("Analysing chain with name: %s", chainName)
675  chainDict = analyseChainName(chainName, l1Thresholds, L1item)
676  log.debug('ChainProperties: %s', chainDict)
677 
678  for chainPart in chainDict['chainParts']:
679  # fill the sigFolder and subSigs folder
680  for sf in chainPart['sigFolder']:
681  if sf in chainDict['sigDicts']:
682  chainDict['sigDicts'][sf].extend(chainPart['subSigs'])
683  else:
684  chainDict['sigDicts'].update({sf:chainPart['subSigs']})
685  if sf == 'Bjet':
686  chainDict['sigDicts'].update({'Jet':['Jet']})
687 
688 
689  thisSignature = chainPart['signature']
690  thisAlignGroup = chainPart['alignmentGroup']
691  thisExtra = chainPart['extra']
692  thisL1 = chainPart['L1threshold']
693  thisChainPartName = chainPart['chainPartName']
694 
695  if thisSignature in ['Muon','Bphysics']:
696  if 'MuonnoL1' in thisAlignGroup or 'lateMu' in thisExtra:
697  if 'FSNOSEED' not in thisL1:
698  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)
699  else:
700  if 'MU' not in thisL1:
701  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)
702  #incorrectL1=True
703 
704  if thisSignature in ['Electron','Photon'] and not ('UPC' in stream): # don't apply for HI UPC chains
705  if 'EM' not in thisL1 and 'BKee' not in thisL1 and 'All' not in thisL1:
706  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)
707  #incorrectL1=True
708 
709  if thisSignature in ['Tau']:
710  if 'TAU' not in thisL1:
711  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)
712  #incorrectL1=True
713 
714  if thisSignature in ['Jet','Bjet','MET','UnconventionalTracking']:
715  if 'FSNOSEED' not in thisL1:
716  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)
717  #incorrectL1=True
718 
719  if thisChainPartName in ['noalg']:
720  # All streamers should be unseeded except RoI-based PEB streamers which need a real RoI for PEB
721  if 'FSNOSEED' not in thisL1 and not isRoIBasedPEB(chainDict['eventBuildType']):
722  log.error("noalg chains should be seeded from FSNOSEED. Check %s seeded from %s (defined L1: %s), signature %s",chainDict['chainName'],thisL1,l1Thresholds,thisSignature)
723  #incorrectL1=True
724 
725 # if incorrectL1 is True:
726 # raise Exception("You are using incorrect L1 seed, please check for ERROR messages...")
727 
728 
729  log.debug('Parts: signature %s, align %s, extra %s, L1 %s', thisSignature, thisAlignGroup, thisExtra, thisL1)
730 
731 
732 
733  # setting the L1 item
734  chainDict['L1item'] = L1item
735  chainDict['stream'] = stream
736  chainDict['groups'] = groups
737  chainDict['mergingStrategy'] = mergingStrategy
738  chainDict['mergingOffset'] = mergingOffset
739  chainDict['mergingOrder'] = mergingOrder
740  chainDict['monGroups'] = monGroups
741  chainDict['chainNameHash'] = string2hash(chainDict['chainName'])
742 
743 
744  allChainMultiplicities = getChainMultFromDict(chainDict)
745  chainDict['chainMultiplicities'] = allChainMultiplicities
746  log.debug('Setting chain multiplicities: %s ', allChainMultiplicities)
747 
748 
752 
753  return chainDict
754 
755 def main():
756 
757  from AthenaConfiguration.AllConfigFlags import initConfigFlags
758  flags = initConfigFlags()
759 
760  parser = flags.getArgumentParser()
761  parser.add_argument(
762  'chain',
763  type=str,
764  help='Chain name to be parsed into chain dictionary')
765  parser.add_argument(
766  '-t', '--thresholds',
767  nargs='+',
768  help='L1 thresholds, needed for multileg or fullscan chains')
769  args = flags.fillFromArgs(parser=parser)
770  flags.lock()
771 
772  if args.thresholds:
773  from TriggerMenuMT.HLT.Config.Utility.ChainDefInMenu import ChainProp
774  cd = dictFromChainName(flags, ChainProp(args.chain,l1SeedThresholds=args.thresholds,groups=[]))
775  else:
776  cd = dictFromChainName(flags, args.chain)
777 
778  from pprint import pprint
779  pprint(cd)
780 
781  import json
782  with open(f'{args.chain}.dict.json','w') as f:
783  json.dump(cd, f, indent=2)
784 
785  return 0
786 
787 if __name__=='__main__':
788  main()
789 
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:1462
DictFromChainName.main
def main()
Definition: DictFromChainName.py:755
DictFromChainName.dictFromChainName
def dictFromChainName(flags, chainInfo)
Definition: DictFromChainName.py:630
DictFromChainName.checkChainStream
def checkChainStream(myStream)
Definition: DictFromChainName.py:624
intersection
std::vector< std::string > intersection(std::vector< std::string > &v1, std::vector< std::string > &v2)
Definition: compareFlatTrees.cxx:25
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
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
python.LArMinBiasAlgConfig.int
int
Definition: LArMinBiasAlgConfig.py:59
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:617
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:1389
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:71
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:1411
python.HLT.Menu.L1Seeds.getSpecificL1Seeds
def getSpecificL1Seeds(l1seedname, l1itemobject, menu_name)
Definition: L1Seeds.py:348
python.AllConfigFlags.initConfigFlags
def initConfigFlags()
Definition: AllConfigFlags.py:19
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
DictFromChainName.getEBPartFromParts
def getEBPartFromParts(chainName, chainParts)
Definition: DictFromChainName.py:91