128 def getChainDicts(self, flags):
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)
136 log.error("Chain signatures %s not a subset of required signatures %s",set(chainSig),reqd)
139 from TriggerMenuMT.HLT.Config.Utility.DictFromChainName import dictFromChainName
143 for sig, chains in self.chainsInMenu.items():
145 log.debug("Now processing chain: %s from signature %s", chain, sig)
147 chainDict = dictFromChainName(flags, chain)
148 chainDict['chainCounter'] = chainCounter
149 chainDict['prescale'] = 1 # set default chain prescale
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
157 if ss not in self.sigDicts[sigfo]:
158 self.sigDicts[sigfo].append(ss)
160 self.chainDicts.append(chainDict)
162 if not validSignature(sig, set(chainDict['signatures'])):
164 log.error('Chain %s assigned to signature %s but creates %s',
165 chainDict['chainName'], sig, set(chainDict['signatures']))
167 raise RuntimeError('Incorrect assignment of chains to slices -- see preceding messages.')
190 def generateChains(self, flags):
192 combinations_in_menu = []
193 alignmentGroups_to_align = set()
194 length_of_configs = {}
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)]
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
212 length_of_configs[config_grp] = config_length
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])
222 self.allChainsForAlignment = all_chains
223 self.combinationsInMenu = combinations_in_menu
224 self.alignmentGroupsToAlign = alignmentGroups_to_align
225 self.configLengthDict = length_of_configs
230 def generateAllChainConfigs(self, flags):
232 == Obtains chain configs for all chains in menu
235 from TriggerMenuMT.HLT.Config.Utility.MenuAlignmentTools import MenuAlignment
236 from TriggerMenuMT.HLT.CommonSequences import EventBuildingSequences, TLABuildingSequences
238 # get all chain names from menu
239 log.info("Will now get chains from the menu")
240 self.getChainsFromMenu(flags)
242 # decoding of the chain name
243 log.info("Will now get chain dictionaries for each chain")
244 self.getChainDicts(flags)
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_')]
251 #import the necessary signatures
252 log.debug("Importing the necessary signatures")
253 self.importSignaturesToGenerate()
255 log.info("Will now generate the chain configuration for each chain")
256 self.generateChains(flags)
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,
262 self.alignmentGroupsToAlign,
263 self.configLengthDict)
264 menuAlignment.analyse_combinations()
266 # alignmentGroups_to_align = menuAlignment.groupsToAlign
267 # lengthOfChainConfigs = self.configLengthDict
268 # combinationsInMenu = menuAlignment.combinationsInMenu
269 # alignmentGroup_sets_to_align = menuAlignment.setsToAlign
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)
276 for chainDict,chainConfig,lengthOfChainConfigs in self.allChainsForAlignment:
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
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.
287 log.debug("[generateAllChainConfigs] chain %s has config lengths %s and alignment groups %s", chainDict['chainName'], lengthOfChainConfigs, chainDict['alignmentGroups'])
289 alignmentGroups = chainDict['alignmentGroups']
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 )
297 elif len(alignmentGroups) >= 2:
298 alignedChainConfig = menuAlignment.multi_align(chainDict, chainConfig, lengthOfChainConfigs)
299 HLTMenuConfig.registerChain( chainDict )
300 chainConfigs.append( alignedChainConfig )
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")
306 if not HLTMenuConfig.isChainRegistered(chainDict['chainName']):
307 log.error("Chain %s has not been registered in the menu!", chainDict['chainName'])
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.")
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())
319 log.info("[generateAllChainConfigs] all chain configurations have been generated.")
323 def getChainsFromMenu(self, flags):
325 == Returns the list of chain names that are in the menu
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)
332 # Generate the list of chains from the basic menu (terminated in a version number)
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}"')
340 self.chainsInMenu = menu_module.setupMenu()
342 # Filter chains if requested
343 if self.chainFilter is not None:
344 self.signaturesOverwritten = True
346 # Verify that if the chain filter has lists of chains
347 # they are all in the menu
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:
355 for signame in self.chainsInMenu:
356 if chain in [c.name for c in self.chainsInMenu[signame]]:
360 raise RuntimeError(f'Request to enable/disable chain {chain} that is not in menu')
362 for signame in self.chainsInMenu:
363 self.chainsInMenu[signame] = [c for c in self.chainsInMenu[signame]
364 if self.chainFilter(signame, c.name)]
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):
370 log.debug("The following chains were found in the menu:")
371 pprint.pprint(self.chainsInMenu)
374 def __generateChainConfig(self, flags, mainChainDict):
376 # Assembles the chain configuration and returns a chain object with (name, L1see and list of ChainSteps)
379 from TriggerMenuMT.HLT.Config.Utility.ChainDictTools import splitInterSignatureChainDict
380 from TriggerMenuMT.HLT.Config.Utility.ComboHypoHandling import addTopoInfo, comboConfigurator, topoLegIndices, anomdetWPIndices, topo3VarLegIndices
381 from TriggerMenuMT.HLT.Config.Utility.ChainMerging import mergeChainDefs
382 from TriggerMenuMT.HLT.CommonSequences import EventBuildingSequences, TLABuildingSequences
384 # split the the chainDictionaries for each chain and print them in a pretty way
385 chainDicts = splitInterSignatureChainDict(mainChainDict)
387 if log.isEnabledFor(logging.DEBUG):
389 pp = pprint.PrettyPrinter(indent=4, depth=8)
390 log.debug('dictionary is: %s', pp.pformat(chainDicts))
392 # Loop over all chainDicts and send them off to their respective assembly code
393 listOfChainConfigs = []
394 perSig_lengthOfChainConfigs = []
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']
403 chainName = chainPartDict['chainName']
404 log.debug('Checking chainDict for chain %s in signature %s, alignment group %s' , chainName, currentSig, currentAlignGroup)
406 if currentSig in self.availableSignatures:
408 log.debug("[__generateChainConfigs] Trying to get chain config for %s", currentSig)
409 if currentSig in self.defaultFlagsForSignature:
410 sigFlags = self.defaultFlagsForSignature[currentSig]
413 sigFlags = self.chainDefModule[currentSig].prepareDefaultSignatureFlags(flags)
414 except AttributeError:
415 log.debug("prepareDefaultSignatureFlags not present")
417 except Exception as e:
418 log.error(f"Unexpected error invoking prepareDefaultSignatureFlags {e}")
421 self.defaultFlagsForSignature[currentSig] = sigFlags
423 if currentSig in ['Electron', 'Photon', 'Muon', 'Tau', 'Bphysics'] :
424 chainPartConfig, perSig_lengthOfChainConfigs = self.chainDefModule[currentSig].generateChainConfigs(sigFlags, chainPartDict, perSig_lengthOfChainConfigs)
426 chainPartConfig = self.chainDefModule[currentSig].generateChainConfigs(sigFlags, chainPartDict)
427 if currentSig == 'Test' and isinstance(chainPartConfig, tuple):
428 chainPartConfig = chainPartConfig[0]
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.')
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.')
439 log.debug("Chain %s \n chain config: %s",chainPartDict['chainName'],chainPartConfig)
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))
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)
453 for a,b in zip(nSteps,aGrps):
454 lengthOfChainConfigs.append((a,b))
457 # This part is to deal with combined chains between different signatures
459 if len(listOfChainConfigs) == 0:
460 raise Exception('[__generateChainConfigs] No Chain Configuration found for {0}'.format(mainChainDict['chainName']))
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)
470 for a,b in zip(nSteps,aGrps):
471 lengthOfChainConfigs.append((a,b))
473 theChainConfig = listOfChainConfigs[0]
475 for topoID in range(len(mainChainDict['extraComboHypos'])):
476 thetopo = mainChainDict['extraComboHypos'][topoID].strip(string.digits).rstrip(topoLegIndices)
479 if "anomdet" in thetopo:
480 thetopo = thetopo.rstrip(anomdetWPIndices)
482 if "masswiso" in thetopo:
483 thetopo = thetopo.rstrip(topo3VarLegIndices)
485 theChainConfig.addTopo((comboConfigurator[thetopo],thetopo))
487 # Now we know where the topos should go, we can insert them in the right steps
488 if len(theChainConfig.topoMap) > 0:
489 log.debug("Trying to add extra ComboHypoTool for %s",mainChainDict['extraComboHypos'])
490 addTopoInfo(theChainConfig,mainChainDict,listOfChainConfigs,lengthOfChainConfigs)
492 log.error('[__generateChainConfigs] Problems creating ChainDef for chain %s ', chainName)
493 log.error('[__generateChainConfigs] I am in the extraComboHypos section, for %s ', mainChainDict['extraComboHypos'])
494 log.exception('[__generateChainConfigs] Full chain dictionary is\n %s ', mainChainDict)
495 raise Exception('[__generateChainConfigs] Stopping menu generation. Please investigate the exception shown above.')
496 except AttributeError:
497 raise Exception('[__generateChainConfigs] Stopping menu generation. Please investigate the exception shown above.')
499 # Configure event building strategy
500 eventBuildType = mainChainDict['eventBuildType']
501 TLAEventBuildTypes = ('PhysicsTLA', 'FTagPEBTLA', 'EgammaPEBTLA', 'DarkJetPEBTLA')
504 if any(ebtype in eventBuildType for ebtype in TLAEventBuildTypes):
505 log.debug("Adding TLA Step for chain %s", mainChainDict['chainName'])
506 TLABuildingSequences.addTLAStep(flags, theChainConfig, mainChainDict)
507 log.debug('Configuring event building sequence %s for chain %s', eventBuildType, mainChainDict['chainName'])
508 EventBuildingSequences.addEventBuildingSequence(flags, theChainConfig, eventBuildType, mainChainDict)
509 except TypeError as ex:
511 raise Exception('[__generateChainConfigs] Stopping menu generation for EventBuilding/TLA sequences. Please investigate the exception shown above.')
513 log.debug('[__generateChainConfigs] lengthOfChainConfigs %s, ChainConfigs %s ', lengthOfChainConfigs, theChainConfig)
514 return theChainConfig,lengthOfChainConfigs
517 def resolveEmptySteps(self,chainConfigs):
518 max_steps = max([len(cc.steps) for cc in chainConfigs], default=0)
519 steps_are_empty = [True for i in range(0,max_steps)]
521 for cc in chainConfigs:
522 for istep, the_step in enumerate(cc.steps):
523 if not the_step.isEmpty:
524 steps_are_empty[istep] = False
526 emptySteps.append(the_step)
528 log.debug("Are there any fully empty steps? %s", steps_are_empty)
529 log.debug("The empty step(s) and associated chain(s) are: %s", emptySteps)
530 empty_step_indices = [i for i,is_empty in enumerate(steps_are_empty) if is_empty]
532 if len(empty_step_indices) == 0:
535 special_test_menu = self.chainFilter and ( getattr(self.chainFilter, "selectChains", False) or \
536 getattr(self.chainFilter, "disableChains", False) or \
537 getattr(self.chainFilter, "disabledSignatures", False) or \
538 getattr(self.chainFilter, "enabledSignatures", False) )
541 if len(self.availableSignatures) != 1 and not special_test_menu:
542 raise Exception("[resolveEmptySteps] Please find the reason for this empty step and resolve it / remove it from the menu: %s", emptySteps)
544 log.info("Will now delete steps %s (indexed from zero)",empty_step_indices)
546 for cc in chainConfigs:
548 #only add non-empty steps to the new steps list!
549 for istep,step in enumerate(cc.steps):
550 if istep not in empty_step_indices: