ATLAS Offline Software
GenerateMenuMT.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2 
3 from typing import Optional
4 import importlib, re, string
5 
6 from TriggerMenuMT.HLT.Config.Utility.HLTMenuConfig import HLTMenuConfig
7 
8 from AthenaCommon.Logging import logging
9 log = logging.getLogger(__name__)
10 
11 # _maxAllowedCustomCH: this variable keeps track of the number of custom ComboHypo that are named differently as expected from the ControlFlow rule (which sets one ComboHypo per step, both named the same). These violations are already investigated and allowed because the ComboHypos are able to handle the decisions internally. Any change of this parameter needs to be discussed with experts
12 _maxAllowedCustomCH = 10
13 
15  return ['Streaming','Monitor','Beamspot','Cosmic', 'Calib', 'EnhancedBias']
16 
18  return ['MinBias','Electron','Photon','Muon','Tau','Jet', 'Bjet','MET','UnconventionalTracking','HeavyIon']
19 
21  return ['Bjet', 'Egamma', 'Combined']
22 
24  return ['Streaming']
25 
27  return ['Test']
28 
30  return ['Bphysics']
31 
34 
35 
37  """Standard chain filter"""
38  def __init__(self, flags):
39  for f in ("enabledSignatures", "disabledSignatures", "selectChains", "disableChains"):
40  # Ensure flag values have correct type
41  value = getattr(flags.Trigger, f)
42  assert isinstance(value, list), f"Flag Trigger.{f}={value!r} is not of type list"
43  # Store flag
44  setattr(self, f, value)
45 
46  def __call__(self, signame, chain):
47  return ((signame in self.enabledSignatures and signame not in self.disabledSignatures) and \
48  (not self.selectChains or chain in self.selectChains) and chain not in self.disableChains)
49 
50  def __str__(self) -> str:
51  return f'FilterChainsToGenerate(enabledSignatures={self.enabledSignatures!r}, disabledSignatures={self.disabledSignatures!r}, selectChains={self.selectChains!r}, disableChains={self.disableChains!r})'
52 
53 
54 class Singleton(type):
55  _instances = {}
56  def __call__(cls, *args, **kwargs):
57  if cls not in cls._instances:
58  cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
59  return cls._instances[cls]
60 
61  def clear(cls):
62  cls._instances.clear()
63 
64 
65 class GenerateMenuMT(metaclass=Singleton):
66  """Singleton class for the Trigger Menu"""
67  def __init__(self):
68  self.base_menu_name: str = ''
69 
70  self.chainsInMenu = {} # signature : [chains]
71 
73  self.chainDicts = []
76  self.configLengthDict = {}
77 
78  self.signaturesOverwritten = False
79  self.L1Prescales = None
80  self.HLTPrescales = None
81 
82  self.chainFilter = None
84 
85  self.sigDicts = {}
86 
87  self.chainDefModule = {} # Generate[SIG]ChainDefs module for each SIGnature
89 
90 
91  # Define which signatures (folders) are required for each slice
92  def getRequiredSignatures(theslice):
93  allSigs = allSignatures()
94  signatureDeps = {sig:[sig] for sig in allSigs}
95  # Special cases
96  signatureDeps.update({
97  # Bjet always requires jet
98  'Bjet': ['Bjet','Jet'],
99  # Egamma contains two signatures
100  'Egamma': ['Electron','Photon'],
101  'Combined': combinedSignatures(),
102  })
103  return set(signatureDeps[theslice]+defaultSignatures()) # always allow streamers
104 
105 
106  def setChainFilter(self, f):
107  """Set chain filter for menu generation.
108 
109  This can be any callable object taking two
110  arguments for signature and chain name and returning a boolean.
111  E.g. to only generate Egamma chains:
112  menu.setChainFilter(lambda slice,chain : slice=='Egamma').
113 
114  In the special case that f is a functor with the list attributes
115  selectChains and/or disableChains, the contents will be explicitly
116  checked to be in the menu.
117  """
118  fname = f.__class__.__name__ if isinstance(f,object) else f.__name__
119  import inspect
120  if len(inspect.signature(f).parameters)!=2:
121  log.error('%s is not a valid chain filter. Function/callable needs take two arguments '
122  'for signature and chain name and return a boolean', fname)
123  else:
124  log.info('Setting chain filter to: %s', f)
125  self.chainFilter = f
126 
127 
128  def getChainDicts(self, flags):
129 
130  def validSignature(currentSig, chainSig):
131  """Check if chain is assigned to the correct signature"""
132  reqd = GenerateMenuMT.getRequiredSignatures(currentSig)
133  isValid = chainSig.issubset( reqd )
134  log.debug("Chain signatures: %s, required signatures: %s",chainSig,reqd)
135  if not isValid:
136  log.error("Chain signatures %s not a subset of required signatures %s",set(chainSig),reqd)
137  return isValid
138 
139  from TriggerMenuMT.HLT.Config.Utility.DictFromChainName import dictFromChainName
140 
141  chainCounter = 0
142  invalid = False
143  for sig, chains in self.chainsInMenu.items():
144  for chain in chains:
145  log.debug("Now processing chain: %s from signature %s", chain, sig)
146  chainCounter += 1
147  chainDict = dictFromChainName(flags, chain)
148  chainDict['chainCounter'] = chainCounter
149  chainDict['prescale'] = 1 # set default chain prescale
150 
151  # Pick out the folder and subsignature directories to import
152  for sigfo, subsig in chainDict['sigDicts'].items():
153  if sigfo not in self.sigDicts:
154  self.sigDicts[sigfo] = subsig
155  else:
156  for ss in subsig:
157  if ss not in self.sigDicts[sigfo]:
158  self.sigDicts[sigfo].append(ss)
159 
160  self.chainDicts.append(chainDict)
161 
162  if not validSignature(sig, set(chainDict['signatures'])):
163  invalid=True
164  log.error('Chain %s assigned to signature %s but creates %s',
165  chainDict['chainName'], sig, set(chainDict['signatures']))
166  if invalid:
167  raise RuntimeError('Incorrect assignment of chains to slices -- see preceding messages.')
168 
170  """check if all the signature files can be imported and then import them"""
171 
172  for sig, subSigs in self.sigDicts.items():
173  try:
174  for ss in subSigs:
175  import_module = 'TriggerMenuMT.HLT.' + sig +'.Generate' + ss + 'ChainDefs'
176  self.chainDefModule[ss] = importlib.import_module(import_module)
177 
178  if ss not in self.availableSignatures:
179  self.availableSignatures.append(ss)
180 
181  except ImportError:
182  log.exception('Problems when importing ChainDef generating code for %s', sig)
183  import traceback
184  traceback.print_exc()
185 
186  log.info('Available signature(s) for chain generation: %s', self.availableSignatures)
187 
188  return
189 
190  def generateChains(self, flags):
191  all_chains = []
192  combinations_in_menu = []
193  alignmentGroups_to_align = set()
194  length_of_configs = {}
195 
196  nchainDicts = len(self.chainDicts)
197  notify_increment = max(int(nchainDicts / 10),1)
198  for ichainDict, chainDict in enumerate(self.chainDicts):
199  log.debug("Next: getting chain configuration for chain %s ", chainDict['chainName'])
200  if ichainDict % notify_increment==0:
201  log.info("Generating HLT chain %d / %d", ichainDict+1, nchainDicts)
202  chainConfig,lengthOfChainConfigs = self.__generateChainConfig(flags, chainDict)
203  all_chains += [(chainDict,chainConfig,lengthOfChainConfigs)]
204 
205  #update the alignment group length dictionary if we have a longer number of steps
206  #or the signature isn't registered in the dictionary yet
207  for config_length, config_grp in lengthOfChainConfigs:
208  if config_grp in length_of_configs:
209  if config_length > length_of_configs[config_grp]:
210  length_of_configs[config_grp] = config_length
211  else:
212  length_of_configs[config_grp] = config_length
213 
214  # find the chains that contain more than one alignment group, to keep track
215  # of what combinations do we need to deal with.
216  # we're using sets here so we don't end up with duplicates
217  if len(set(chainDict['alignmentGroups'])) > 1:
218  combinations_in_menu += [list(set(chainDict['alignmentGroups']))]
219  for align_group in list(set(chainDict['alignmentGroups'])):
220  alignmentGroups_to_align.update([align_group])
221 
222  self.allChainsForAlignment = all_chains
223  self.combinationsInMenu = combinations_in_menu
224  self.alignmentGroupsToAlign = alignmentGroups_to_align
225  self.configLengthDict = length_of_configs
226 
227  return
228 
229 
230  def generateAllChainConfigs(self, flags):
231  """
232  == Obtains chain configs for all chains in menu
233  """
234 
235  from TriggerMenuMT.HLT.Config.Utility.MenuAlignmentTools import MenuAlignment
236  from TriggerMenuMT.HLT.CommonSequences import EventBuildingSequences, TLABuildingSequences
237 
238  # get all chain names from menu
239  log.info("Will now get chains from the menu")
240  self.getChainsFromMenu(flags)
241 
242  # decoding of the chain name
243  log.info("Will now get chain dictionaries for each chain")
244  self.getChainDicts(flags)
245 
246  if flags.Trigger.disableCPS:
247  log.warning('Removing all CPS group because the flag Trigger.disableCPS is set')
248  for chainDict in self.chainDicts:
249  chainDict['groups'] = [g for g in chainDict['groups'] if not g.startswith('RATE:CPS_')]
250 
251  #import the necessary signatures
252  log.debug("Importing the necessary signatures")
254 
255  log.info("Will now generate the chain configuration for each chain")
256  self.generateChains(flags)
257 
258  log.info("Will now calculate the alignment parameters")
259  #dict of signature: set it belongs to
260  #e.g. {'Electron': ['Electron','Muon','Photon']}
261  menuAlignment = MenuAlignment(self.combinationsInMenu,
263  self.configLengthDict)
264  menuAlignment.analyse_combinations()
265 
266  # alignmentGroups_to_align = menuAlignment.groupsToAlign
267  # lengthOfChainConfigs = self.configLengthDict
268  # combinationsInMenu = menuAlignment.combinationsInMenu
269  # alignmentGroup_sets_to_align = menuAlignment.setsToAlign
270 
271  log.info('Aligning the following signatures: %s',sorted(menuAlignment.sets_to_align))
272  log.debug('Length of each of the alignment groups: %s',self.configLengthDict)
273 
274  chainConfigs = []
275 
276  for chainDict,chainConfig,lengthOfChainConfigs in self.allChainsForAlignment:
277 
278  # start by ordering electron, photon, muon by having e+mu, g+mu, e+g chains
279  # desired ordering: electron, photon, muon, tau, jet, met, b-jet
280 
281  # lengthOfChainConfigs is something like this: [(4, 'Photon'), (5, 'Muon')]
282  # needs to match up with the maximum number of steps in a signature in the menu (length_of_configs)
283  # start with electron! Only need to add post-steps for combined electron chains if the max length in a combined chain
284  # is greater than the number of electron steps combined chain. Assume that the max length of an electron chain occurs
285  # in a combined chain.
286 
287  log.debug("[generateAllChainConfigs] chain %s has config lengths %s and alignment groups %s", chainDict['chainName'], lengthOfChainConfigs, chainDict['alignmentGroups'])
288 
289  alignmentGroups = chainDict['alignmentGroups']
290 
291  #parallel-merged single-signature chains or single signature chains. Anything that needs no splitting!
292  if len(set(alignmentGroups)) == 1:
293  alignedChainConfig = menuAlignment.single_align(chainDict, chainConfig)
294  HLTMenuConfig.registerChain( chainDict )
295  chainConfigs.append( alignedChainConfig )
296 
297  elif len(alignmentGroups) >= 2:
298  alignedChainConfig = menuAlignment.multi_align(chainDict, chainConfig, lengthOfChainConfigs)
299  HLTMenuConfig.registerChain( chainDict )
300  chainConfigs.append( alignedChainConfig )
301 
302  else:
303  log.error("Menu can't deal with combined chains with more than two alignmentGroups at the moment. oops...")
304  raise NotImplementedError("more than three alignment groups still needs implementing in ChainMerging.py, ATR-22206")
305 
306  if not HLTMenuConfig.isChainRegistered(chainDict['chainName']):
307  log.error("Chain %s has not been registered in the menu!", chainDict['chainName'])
308  import pprint
309  pp = pprint.PrettyPrinter(indent=4, depth=8)
310  log.error('The chain dictionary is: %s', pp.pformat(chainDict))
311  raise Exception("Please fix the menu or the chain.")
312 
313  # align event building sequences
314  log.info("[generateAllChainConfigs] general alignment complete, will now align TLA chains")
315  TLABuildingSequences.alignTLASteps(chainConfigs, HLTMenuConfig.dicts())
316  log.info("[generateAllChainConfigs] general and TLA alignment complete, will now align PEB chains")
317  EventBuildingSequences.alignEventBuildingSteps(chainConfigs, HLTMenuConfig.dicts())
318 
319  log.info("[generateAllChainConfigs] all chain configurations have been generated.")
320  return chainConfigs
321 
322 
323  def getChainsFromMenu(self, flags):
324  """
325  == Returns the list of chain names that are in the menu
326  """
327 
328  self.base_menu_name = re.match(r'\w*_v\d*', flags.Trigger.triggerMenuSetup).group(0)
329  log.info(f'Menu name: {flags.Trigger.triggerMenuSetup}')
330  log.debug('Base menu name: %s', self.base_menu_name)
331 
332  # Generate the list of chains from the basic menu (terminated in a version number)
333  try:
334  menu_module = importlib.import_module(f'TriggerMenuMT.HLT.Menu.{self.base_menu_name}')
335  except Exception as e:
336  log.fatal(f'Failed to import menu module "{self.base_menu_name}" inferred from menu "{flags.Trigger.triggerMenuSetup}"')
337  raise e
338 
339  # Load Menu
340  self.chainsInMenu = menu_module.setupMenu()
341 
342  # Filter chains if requested
343  if self.chainFilter is not None:
344  self.signaturesOverwritten = True
345 
346  # Verify that if the chain filter has lists of chains
347  # they are all in the menu
348  chainsToCheck = []
349  if hasattr(self.chainFilter,'selectChains'):
350  chainsToCheck += self.chainFilter.selectChains
351  if hasattr(self.chainFilter,'disableChains'):
352  chainsToCheck += self.chainFilter.disableChains
353  for chain in chainsToCheck:
354  inMenu = False
355  for signame in self.chainsInMenu:
356  if chain in [c.name for c in self.chainsInMenu[signame]]:
357  inMenu = True
358  break
359  if not inMenu:
360  raise RuntimeError(f'Request to enable/disable chain {chain} that is not in menu')
361 
362  for signame in self.chainsInMenu:
363  self.chainsInMenu[signame] = [c for c in self.chainsInMenu[signame]
364  if self.chainFilter(signame, c.name)]
365 
366  if not self.chainsInMenu:
367  log.warning("There seem to be no chains in the menu - please check")
368  elif log.isEnabledFor(logging.DEBUG):
369  import pprint
370  log.debug("The following chains were found in the menu:")
371  pprint.pprint(self.chainsInMenu)
372 
373 
374  def __generateChainConfig(self, flags, mainChainDict):
375  """
376  # Assembles the chain configuration and returns a chain object with (name, L1see and list of ChainSteps)
377  """
378 
379  from TriggerMenuMT.HLT.Config.Utility.ChainDictTools import splitInterSignatureChainDict
380  from TriggerMenuMT.HLT.Config.Utility.ComboHypoHandling import addTopoInfo, comboConfigurator, topoLegIndices, anomdetWPIndices
381  from TriggerMenuMT.HLT.Config.Utility.ChainMerging import mergeChainDefs
382  from TriggerMenuMT.HLT.CommonSequences import EventBuildingSequences, TLABuildingSequences
383 
384  # split the the chainDictionaries for each chain and print them in a pretty way
385  chainDicts = splitInterSignatureChainDict(mainChainDict)
386 
387  if log.isEnabledFor(logging.DEBUG):
388  import pprint
389  pp = pprint.PrettyPrinter(indent=4, depth=8)
390  log.debug('dictionary is: %s', pp.pformat(chainDicts))
391 
392  # Loop over all chainDicts and send them off to their respective assembly code
393  listOfChainConfigs = []
394  perSig_lengthOfChainConfigs = []
395 
396  for chainPartDict in chainDicts:
397  chainPartConfig = None
398  currentSig = chainPartDict['signature']
399  currentAlignGroup = None
400  if len(chainPartDict['chainParts']) == 1:
401  currentAlignGroup = chainPartDict['chainParts'][0]['alignmentGroup']
402 
403  chainName = chainPartDict['chainName']
404  log.debug('Checking chainDict for chain %s in signature %s, alignment group %s' , chainName, currentSig, currentAlignGroup)
405 
406  if currentSig in self.availableSignatures:
407  try:
408  log.debug("[__generateChainConfigs] Trying to get chain config for %s", currentSig)
409  if currentSig in self.defaultFlagsForSignature:
410  sigFlags = self.defaultFlagsForSignature[currentSig]
411  else:
412  try:
413  sigFlags = self.chainDefModule[currentSig].prepareDefaultSignatureFlags(flags)
414  except AttributeError:
415  log.debug("prepareDefaultSignatureFlags not present")
416  sigFlags = flags
417  except Exception as e:
418  log.error(f"Unexpected error invoking prepareDefaultSignatureFlags {e}")
419  sigFlags = flags
420 
421  self.defaultFlagsForSignature[currentSig] = sigFlags
422 
423  if currentSig in ['Electron', 'Photon', 'Muon', 'Tau', 'Bphysics'] :
424  chainPartConfig, perSig_lengthOfChainConfigs = self.chainDefModule[currentSig].generateChainConfigs(sigFlags, chainPartDict, perSig_lengthOfChainConfigs)
425  else:
426  chainPartConfig = self.chainDefModule[currentSig].generateChainConfigs(sigFlags, chainPartDict)
427  if currentSig == 'Test' and isinstance(chainPartConfig, tuple):
428  chainPartConfig = chainPartConfig[0]
429  except Exception:
430  log.error('[__generateChainConfigs] Problems creating ChainDef for chain %s ', chainName)
431  log.error('[__generateChainConfigs] I am in chain part\n %s ', chainPartDict)
432  log.exception('[__generateChainConfigs] Full chain dictionary is\n %s ', mainChainDict)
433  raise Exception('[__generateChainConfigs] Stopping menu generation. Please investigate the exception shown above.')
434  else:
435  log.error('Chain %s cannot be generated - Signature "%s" not available', chainPartDict['chainName'], currentSig)
436  log.error('Available signature(s): %s', self.availableSignatures)
437  raise Exception('Stopping the execution. Please correct the configuration.')
438 
439  log.debug("Chain %s \n chain config: %s",chainPartDict['chainName'],chainPartConfig)
440 
441  listOfChainConfigs.append(chainPartConfig)
442  log.debug("[__generateChainConfigs] adding to the perSig_lengthOfChainConfigs list (%s, %s)",chainPartConfig.nSteps,chainPartConfig.alignmentGroups)
443  perSig_lengthOfChainConfigs.append((chainPartConfig.nSteps,chainPartConfig.alignmentGroups))
444 
445  # this will be a list of lists for inter-sig combined chains and a list with one
446  # multi-element list for intra-sig combined chains
447  # here, we flatten it accordingly (works for both cases!)
448  lengthOfChainConfigs = []
449  for nSteps, aGrps in perSig_lengthOfChainConfigs:
450  if len(nSteps) != len(aGrps):
451  log.error("Chain part has %s steps and %s alignment groups - these don't match!",nSteps,aGrps)
452  else:
453  for a,b in zip(nSteps,aGrps):
454  lengthOfChainConfigs.append((a,b))
455 
456 
457  # This part is to deal with combined chains between different signatures
458  try:
459  if len(listOfChainConfigs) == 0:
460  raise Exception('[__generateChainConfigs] No Chain Configuration found for {0}'.format(mainChainDict['chainName']))
461  else:
462  if len(listOfChainConfigs)>1:
463  log.debug("Merging strategy from dictionary: %s", mainChainDict["mergingStrategy"])
464  theChainConfig, perSig_lengthOfChainConfigs = mergeChainDefs(listOfChainConfigs, mainChainDict, perSig_lengthOfChainConfigs)
465  lengthOfChainConfigs = []
466  for nSteps, aGrps in perSig_lengthOfChainConfigs:
467  if len(nSteps) != len(aGrps):
468  log.error("Post-merged chain part has %s steps and %s alignment groups - these don't match!",nSteps,aGrps)
469  else:
470  for a,b in zip(nSteps,aGrps):
471  lengthOfChainConfigs.append((a,b))
472  else:
473  theChainConfig = listOfChainConfigs[0]
474 
475  for topoID in range(len(mainChainDict['extraComboHypos'])):
476  thetopo = mainChainDict['extraComboHypos'][topoID].strip(string.digits).rstrip(topoLegIndices)
477 
478 
479  if "anomdet" in thetopo:
480  thetopo = thetopo.rstrip(anomdetWPIndices)
481 
482  theChainConfig.addTopo((comboConfigurator[thetopo],thetopo))
483 
484  # Now we know where the topos should go, we can insert them in the right steps
485  if len(theChainConfig.topoMap) > 0:
486  log.debug("Trying to add extra ComboHypoTool for %s",mainChainDict['extraComboHypos'])
487  addTopoInfo(theChainConfig,mainChainDict,listOfChainConfigs,lengthOfChainConfigs)
488  except RuntimeError:
489  log.error('[__generateChainConfigs] Problems creating ChainDef for chain %s ', chainName)
490  log.error('[__generateChainConfigs] I am in the extraComboHypos section, for %s ', mainChainDict['extraComboHypos'])
491  log.exception('[__generateChainConfigs] Full chain dictionary is\n %s ', mainChainDict)
492  raise Exception('[__generateChainConfigs] Stopping menu generation. Please investigate the exception shown above.')
493  except AttributeError:
494  raise Exception('[__generateChainConfigs] Stopping menu generation. Please investigate the exception shown above.')
495 
496  # Configure event building strategy
497  eventBuildType = mainChainDict['eventBuildType']
498  TLAEventBuildTypes = ('PhysicsTLA', 'FTagPEBTLA', 'EgammaPEBTLA', 'DarkJetPEBTLA')
499  if eventBuildType:
500  try:
501  if any(ebtype in eventBuildType for ebtype in TLAEventBuildTypes):
502  log.debug("Adding TLA Step for chain %s", mainChainDict['chainName'])
503  TLABuildingSequences.addTLAStep(flags, theChainConfig, mainChainDict)
504  log.debug('Configuring event building sequence %s for chain %s', eventBuildType, mainChainDict['chainName'])
505  EventBuildingSequences.addEventBuildingSequence(flags, theChainConfig, eventBuildType, mainChainDict)
506  except TypeError as ex:
507  log.error(ex)
508  raise Exception('[__generateChainConfigs] Stopping menu generation for EventBuilding/TLA sequences. Please investigate the exception shown above.')
509 
510  log.debug('[__generateChainConfigs] lengthOfChainConfigs %s, ChainConfigs %s ', lengthOfChainConfigs, theChainConfig)
511  return theChainConfig,lengthOfChainConfigs
512 
513 
514  def resolveEmptySteps(self,chainConfigs):
515  max_steps = max([len(cc.steps) for cc in chainConfigs], default=0)
516  steps_are_empty = [True for i in range(0,max_steps)]
517  emptySteps = []
518  for cc in chainConfigs:
519  for istep, the_step in enumerate(cc.steps):
520  if not the_step.isEmpty:
521  steps_are_empty[istep] = False
522  else:
523  emptySteps.append(the_step)
524 
525  log.debug("Are there any fully empty steps? %s", steps_are_empty)
526  log.debug("The empty step(s) and associated chain(s) are: %s", emptySteps)
527  empty_step_indices = [i for i,is_empty in enumerate(steps_are_empty) if is_empty]
528 
529  if len(empty_step_indices) == 0:
530  return chainConfigs
531 
532  special_test_menu = self.chainFilter and ( getattr(self.chainFilter, "selectChains", False) or \
533  getattr(self.chainFilter, "disableChains", False) or \
534  getattr(self.chainFilter, "disabledSignatures", False) or \
535  getattr(self.chainFilter, "enabledSignatures", False) )
536 
537 
538  if len(self.availableSignatures) != 1 and not special_test_menu:
539  raise Exception("[resolveEmptySteps] Please find the reason for this empty step and resolve it / remove it from the menu: %s", emptySteps)
540 
541  log.info("Will now delete steps %s (indexed from zero)",empty_step_indices)
542 
543  for cc in chainConfigs:
544  new_steps = []
545  #only add non-empty steps to the new steps list!
546  for istep,step in enumerate(cc.steps):
547  if istep not in empty_step_indices:
548  new_steps += [step]
549  cc.steps = new_steps
550 
551  return chainConfigs
552 
553  def generatePrescales(self, flags, prescale_set: Optional[str] = '__auto__'):
554  '''Add prescales for disabling items (e.g. MC production)'''
555 
556  menu_name = flags.Trigger.triggerMenuSetup
557  if prescale_set == '__auto__':
558  if menu_name.endswith('_prescale'):
559  # Get the prescale set name from the Menu
560  prescale_set = menu_name.removeprefix(f'{self.base_menu_name}_').removesuffix('_prescale')
561  else:
562  prescale_set = None
563 
564  if prescale_set:
565  from TriggerMenuMT.HLT.Menu.MenuPrescaleConfig import menu_prescale_set_gens
566  if prescale_set not in menu_prescale_set_gens:
567  raise RuntimeError(f'Unknown menu prescale set for menu {flags.Trigger.triggerMenuSetup}')
568 
569  gen = menu_prescale_set_gens[prescale_set]
570  else:
571  from TriggerMenuMT.HLT.Config.Utility.MenuPrescaleSet import AutoPrescaleSetGen
572  gen = AutoPrescaleSetGen()
573 
574  # Generate prescale set
575  log.info(f'Generating automatic prescale set: {prescale_set}')
576  ps_set = gen.generate(flags, store=True)
577  self.L1Prescales = ps_set.l1_prescales
578  self.HLTPrescales = ps_set.hlt_prescales
579 
580 
581 
582 def generateMenuMT(flags):
583  """
584  == Main function to generate the L1, L1Topo and HLT menu configs and CA, using the GenerateMenuMT class
585  """
586  # Generate L1 menu
587  # The L1Menu json file is produced here
588  from TrigConfigSvc.TrigConfigSvcCfg import generateL1Menu
589  generateL1Menu(flags)
590 
591 
592  menu = GenerateMenuMT()
593 
594  # Apply generation filter (enable/disable chains and signatures)
595  chains_gen_filter = FilterChainsToGenerate(flags)
596  menu.setChainFilter(chains_gen_filter)
597  log.debug('Filtering chains: %d', menu.chainFilter is not None)
598 
599  # Generate all chains configuration
600  finalListOfChainConfigs = menu.generateAllChainConfigs(flags)
601 
602  checkNumberOfLegs = [chain.checkNumberOfLegs() for chain in finalListOfChainConfigs]
603  if 0 in checkNumberOfLegs:
604  log.error('There is a chain with unexpected number of legs. Revisit your configuration')
605 
606  log.info('Number of configured chains: %d', len(finalListOfChainConfigs))
607 
608  from TriggerMenuMT.HLT.Config import MenuComponents
609  if len(MenuComponents._CustomComboHypoAllowed)> _maxAllowedCustomCH:
610  log.error(f'Found {len(MenuComponents._CustomComboHypoAllowed)} ComboHypo algorithms violating the one-CH-per-step rule, only {_maxAllowedCustomCH} are allowed (which are BLS ComboHypos). This is the list of current violations: {MenuComponents._CustomComboHypoAllowed}. Please consolidate your choice of ComboHypo, by checking that it is able to handle decisions internally; if yes eventually increase the limit set by _maxAllowedCustomCH, after discussing with experts')
611  # Generate and apply the automatic prescale sets (e.g. for disabling items in an MC production)
612  menu.generatePrescales(flags)
613 
614 
615  # Remove any remaining steps that are fully empty in all chains
616  finalListOfChainConfigs = menu.resolveEmptySteps(finalListOfChainConfigs)
617  log.debug("finalListOfChainConfig: %s", finalListOfChainConfigs)
618 
619  # Make the HLT configuration tree
620  # The HLTMenu json file is produced here.
621  log.info("Making the HLT configuration tree")
622  menuAcc, CFseq_list = makeHLTTree(flags, finalListOfChainConfigs)
623 
624  # Configure ChainFilters for ROBPrefetching
625  from TriggerJobOpts.TriggerConfigFlags import ROBPrefetching
626  if ROBPrefetching.InitialRoI in flags.Trigger.ROBPrefetchingOptions:
627  from TrigGenericAlgs.TrigGenericAlgsConfig import prefetchingInitialRoIConfig
628  menuAcc.merge(prefetchingInitialRoIConfig(flags, CFseq_list), 'HLTBeginSeq')
629 
630 
631  # Post-generation checks:
632  log.info("Checking the L1HLTConsistency...")
633  from TriggerMenuMT.HLT.Config.Validation.CheckL1HLTConsistency import checkL1HLTConsistency
634  checkL1HLTConsistency(flags)
635 
636  log.info("Checking the Coherent Prescale assignments...")
637  from TriggerMenuMT.HLT.Config.Validation.CheckCPSGroups import checkCPSGroups
638  checkCPSGroups(HLTMenuConfig.dictsList())
639 
640  log.info("Checking that all chains streamed in express have a signature or detctor monGroup")
641  from TriggerMenuMT.HLT.Config.Validation.CheckMonGroups import checkMonGroups
642  checkMonGroups(HLTMenuConfig.dictsList())
643 
644  # Cleanup menu singletons to allow garbage collection (ATR-28855)
645  GenerateMenuMT.clear()
646  from TriggerMenuMT.HLT.Config import MenuComponents
647  MenuComponents._ComboHypoPool.clear()
648  MenuComponents._CustomComboHypoAllowed.clear()
649 
650  return menuAcc
651 
652 
653 def makeHLTTree(flags, chainConfigs):
654  """
655  Generate appropriate Control Flow Graph wiht all HLT algorithms
656  """
657  from TriggerMenuMT.HLT.Config.ControlFlow.HLTCFConfig import decisionTreeFromChains, sequenceScanner
658  from TriggerJobOpts.TriggerConfig import collectViewMakers
659  from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
660  from AthenaCommon.CFElements import seqAND
661 
662  acc = ComponentAccumulator()
663  steps = seqAND('HLTAllSteps')
664  finalDecisions, CFseq_list, menuAcc = decisionTreeFromChains(flags, steps, chainConfigs, HLTMenuConfig.dictsList())
665  if log.getEffectiveLevel() <= logging.DEBUG:
666  menuAcc.printConfig()
667 
668  acc.merge(menuAcc)
669  successful_scan = sequenceScanner( steps )
670  if not successful_scan:
671  raise Exception("[makeHLTTree] At least one sequence is expected in more than one step. Check error messages and fix!")
672 
673  flatDecisions=[]
674  for step in finalDecisions:
675  flatDecisions.extend (step)
676 
677  viewMakers = collectViewMakers(steps)
678  viewMakerMap = {vm.name:vm for vm in viewMakers}
679  for vmname, vm in viewMakerMap.items():
680  log.debug(f"[makeHLTTree] {vmname} InputMakerOutputDecisions: {vm.InputMakerOutputDecisions}")
681  if vmname.endswith("_probe"):
682  try:
683  log.debug(f"Setting InputCachedViews on {vmname} to read decisions from tag leg {vmname[:-6]}: {vm.InputMakerOutputDecisions}")
684  vm.InputCachedViews = viewMakerMap[vmname[:-6]].InputMakerOutputDecisions
685  except KeyError: # We may be using a probe leg that has different reco from the tag
686  log.debug(f"Tag leg does not match probe: '{vmname[:-6]}', will not use cached views")
687 
688 
689  # Generate JSON representation of the config
690  from TriggerMenuMT.HLT.Config.JSON.HLTMenuJSON import generateJSON
691  generateJSON(flags, HLTMenuConfig.dictsList(), menuAcc.getSequence("HLTAllSteps"))
692 
693  # Store the HLTMonitoring json file
694  from TriggerMenuMT.HLT.Config.JSON.HLTMonitoringJSON import generateDefaultMonitoringJSON
695  generateDefaultMonitoringJSON(flags, HLTMenuConfig.dictsList())
696 
697  return acc, CFseq_list
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename R::value_type > sorted(const R &r, PROJ proj={})
Helper function to create a sorted vector from an unsorted range.
GenerateMenuMT.calibCosmicMonSignatures
def calibCosmicMonSignatures()
Definition: GenerateMenuMT.py:14
GenerateMenuMT.allSignatures
def allSignatures()
Definition: GenerateMenuMT.py:32
python.JetAnalysisCommon.ComponentAccumulator
ComponentAccumulator
Definition: JetAnalysisCommon.py:302
vtune_athena.format
format
Definition: vtune_athena.py:14
GenerateMenuMT.GenerateMenuMT.chainDefModule
chainDefModule
Definition: GenerateMenuMT.py:87
GenerateMenuMT.defaultSignatures
def defaultSignatures()
Definition: GenerateMenuMT.py:23
GenerateMenuMT.Singleton.clear
def clear(cls)
Definition: GenerateMenuMT.py:61
GenerateMenuMT.FilterChainsToGenerate
Definition: GenerateMenuMT.py:36
GenerateMenuMT.Singleton
Definition: GenerateMenuMT.py:54
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
GenerateMenuMT.GenerateMenuMT.__generateChainConfig
def __generateChainConfig(self, flags, mainChainDict)
Definition: GenerateMenuMT.py:374
GenerateMenuMT.FilterChainsToGenerate.__init__
def __init__(self, flags)
Definition: GenerateMenuMT.py:38
GenerateMenuMT.GenerateMenuMT.chainFilter
chainFilter
Definition: GenerateMenuMT.py:82
GenerateMenuMT.GenerateMenuMT.configLengthDict
configLengthDict
Definition: GenerateMenuMT.py:76
python.TrigConfigSvcCfg.generateL1Menu
def generateL1Menu(flags)
Definition: TrigConfigSvcCfg.py:189
GenerateMenuMT.makeHLTTree
def makeHLTTree(flags, chainConfigs)
Definition: GenerateMenuMT.py:653
CheckL1HLTConsistency.checkL1HLTConsistency
def checkL1HLTConsistency(flags)
Definition: CheckL1HLTConsistency.py:48
GenerateMenuMT.GenerateMenuMT.setChainFilter
def setChainFilter(self, f)
Definition: GenerateMenuMT.py:106
DictFromChainName.dictFromChainName
def dictFromChainName(flags, chainInfo)
Definition: DictFromChainName.py:652
python.HLT.Egamma.GenerateElectronChainDefs.prepareDefaultSignatureFlags
AthConfigFlags prepareDefaultSignatureFlags(AthConfigFlags inflags)
Definition: GenerateElectronChainDefs.py:38
HLTCFConfig.decisionTreeFromChains
def decisionTreeFromChains(flags, HLTNode, chains, allDicts)
Definition: HLTCFConfig.py:157
ComboHypoHandling.addTopoInfo
def addTopoInfo(theChainConfig, mainChainDict, listOfChainDefs, lengthOfChainConfigs)
Definition: ComboHypoHandling.py:216
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
GenerateMenuMT.GenerateMenuMT.base_menu_name
base_menu_name
Definition: GenerateMenuMT.py:328
GenerateMenuMT.bphysicsSignatures
def bphysicsSignatures()
Definition: GenerateMenuMT.py:29
GenerateMenuMT.GenerateMenuMT.getChainDicts
def getChainDicts(self, flags)
Definition: GenerateMenuMT.py:128
GenerateMenuMT.GenerateMenuMT.allChainsForAlignment
allChainsForAlignment
Definition: GenerateMenuMT.py:72
GenerateMenuMT.GenerateMenuMT.availableSignatures
availableSignatures
Definition: GenerateMenuMT.py:83
python.CFElements.seqAND
def seqAND(name, subs=[])
Definition: CFElements.py:25
GenerateMenuMT.GenerateMenuMT.defaultFlagsForSignature
defaultFlagsForSignature
Definition: GenerateMenuMT.py:88
HLTCFConfig.sequenceScanner
def sequenceScanner(HLTNode)
Definition: HLTCFConfig.py:101
GenerateMenuMT.Singleton.__call__
def __call__(cls, *args, **kwargs)
Definition: GenerateMenuMT.py:56
GenerateMenuMT.GenerateMenuMT.HLTPrescales
HLTPrescales
Definition: GenerateMenuMT.py:80
GenerateMenuMT.combinedSignatures
def combinedSignatures()
Definition: GenerateMenuMT.py:17
GenerateMenuMT.GenerateMenuMT.chainDicts
chainDicts
Definition: GenerateMenuMT.py:73
GenerateMenuMT.GenerateMenuMT.chainsInMenu
chainsInMenu
Definition: GenerateMenuMT.py:70
GenerateMenuMT.GenerateMenuMT.resolveEmptySteps
def resolveEmptySteps(self, chainConfigs)
Definition: GenerateMenuMT.py:514
GenerateMenuMT.GenerateMenuMT
Definition: GenerateMenuMT.py:65
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:194
GenerateMenuMT.GenerateMenuMT.__init__
def __init__(self)
Definition: GenerateMenuMT.py:67
GenerateMenuMT.generateMenuMT
def generateMenuMT(flags)
Definition: GenerateMenuMT.py:582
GenerateMenuMT.GenerateMenuMT.combinationsInMenu
combinationsInMenu
Definition: GenerateMenuMT.py:74
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
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
GenerateMenuMT.GenerateMenuMT.sigDicts
sigDicts
Definition: GenerateMenuMT.py:85
GenerateMenuMT.GenerateMenuMT.getRequiredSignatures
def getRequiredSignatures(theslice)
Definition: GenerateMenuMT.py:92
GenerateMenuMT.Singleton._instances
_instances
Definition: GenerateMenuMT.py:55
CheckMonGroups.checkMonGroups
def checkMonGroups(chainDicts)
Definition: CheckMonGroups.py:15
GenerateMenuMT.FilterChainsToGenerate.__str__
str __str__(self)
Definition: GenerateMenuMT.py:50
GenerateMenuMT.GenerateMenuMT.generateChains
def generateChains(self, flags)
Definition: GenerateMenuMT.py:190
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:71
GenerateMenuMT.jointSignatures
def jointSignatures()
Definition: GenerateMenuMT.py:20
python.TriggerConfig.collectViewMakers
def collectViewMakers(steps)
Definition: TriggerConfig.py:67
GenerateMenuMT.GenerateMenuMT.signaturesOverwritten
signaturesOverwritten
Definition: GenerateMenuMT.py:78
GenerateMenuMT.GenerateMenuMT.importSignaturesToGenerate
def importSignaturesToGenerate(self)
Definition: GenerateMenuMT.py:169
HLTMonitoringJSON.generateDefaultMonitoringJSON
def generateDefaultMonitoringJSON(flags, chainDicts)
Definition: HLTMonitoringJSON.py:21
CaloLCW_tf.group
group
Definition: CaloLCW_tf.py:28
ChainMerging.mergeChainDefs
def mergeChainDefs(listOfChainDefs, chainDict, perSig_lengthOfChainConfigs=None)
Definition: ChainMerging.py:15
python.CaloAddPedShiftConfig.int
int
Definition: CaloAddPedShiftConfig.py:45
python.TrigGenericAlgsConfig.prefetchingInitialRoIConfig
def prefetchingInitialRoIConfig(flags, CFseq_list)
Definition: TrigGenericAlgsConfig.py:159
GenerateMenuMT.GenerateMenuMT.L1Prescales
L1Prescales
Definition: GenerateMenuMT.py:79
GenerateMenuMT.GenerateMenuMT.getChainsFromMenu
def getChainsFromMenu(self, flags)
Definition: GenerateMenuMT.py:323
HLTMenuJSON.generateJSON
def generateJSON(flags, chainDicts, HLTAllSteps)
Definition: HLTMenuJSON.py:83
GenerateMenuMT.FilterChainsToGenerate.__call__
def __call__(self, signame, chain)
Definition: GenerateMenuMT.py:46
pickleTool.object
object
Definition: pickleTool.py:29
ChainDictTools.splitInterSignatureChainDict
def splitInterSignatureChainDict(chainDict)
Definition: ChainDictTools.py:9
CheckCPSGroups.checkCPSGroups
def checkCPSGroups(chainDicts)
Definition: CheckCPSGroups.py:19
GenerateMenuMT.GenerateMenuMT.alignmentGroupsToAlign
alignmentGroupsToAlign
Definition: GenerateMenuMT.py:75
GenerateMenuMT.GenerateMenuMT.generateAllChainConfigs
def generateAllChainConfigs(self, flags)
Definition: GenerateMenuMT.py:230
python.HLT.Bjet.GenerateBjetChainDefs.generateChainConfigs
def generateChainConfigs(flags, chainDict)
Definition: GenerateBjetChainDefs.py:13
GenerateMenuMT.testSignatures
def testSignatures()
Definition: GenerateMenuMT.py:26
GenerateMenuMT.GenerateMenuMT.generatePrescales
def generatePrescales(self, flags, Optional[str] prescale_set='__auto__')
Definition: GenerateMenuMT.py:553