diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLT/Config/MenuComponents.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLT/Config/MenuComponents.py index e41c5e173416a9208de30fcd4c5089a4aec1aa11..1a1057734aca7841b81c2cb607dfb28aaa42fec4 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLT/Config/MenuComponents.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLT/Config/MenuComponents.py @@ -364,12 +364,22 @@ class EmptyMenuSequence: return "MenuSequence::%s \n Hypo::%s \n Maker::%s \n Sequence::%s \n HypoTool::%s\n"\ %(self.name, "Empty", self.maker.Alg.getName(), self.sequence.Alg.getName(), "None") +def createEmptyMenuSequenceCfg(flags, name): + """ creates the generator function named as the empty sequence""" + def create_sequence(name): + return EmptyMenuSequence(name) + # this allows to create the function with the same name as the sequence + #TODO need to extend it to also use it instead of EmptyMenuSequenceCfg inside custom steps + create_sequence.__name__ = name + globals()[name] = create_sequence + return globals()[name] + def EmptyMenuSequenceCfg(flags, name): """Function to create a EmptyMenuSequence (used in the functools.partial)""" return EmptyMenuSequence(name) def isEmptySequenceCfg(o): - return o.func.__name__ == "EmptyMenuSequenceCfg" + return 'Empty' in o.func.__name__ class MenuSequence: """Class to group reco sequences with the Hypo. @@ -527,7 +537,6 @@ class Chain(object): elif re.search('^Step[0-9]{2}_', step_name): step_name = step_name[7:] step.name = 'Step%d_'%(stepID+1)+step_name - # also modify the empty sequence names to follow the step name change for iseq, seq in enumerate(step.sequenceGens): if isEmptySequenceCfg(seq): @@ -535,8 +544,8 @@ class Chain(object): if re.search('Seq[0-9]_',name): newname = re.sub('Seq[0-9]_', 'Seq%d_'%(stepID+1), name) #replace the empty sequence - step.sequenceGens[iseq]=functools.partial(EmptyMenuSequenceCfg, None, name=newname) - + thisEmpty = createEmptyMenuSequenceCfg(None, newname) + step.sequenceGens[iseq]=functools.partial(thisEmpty, name=newname) return diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLT/Config/Utility/ChainMerging.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLT/Config/Utility/ChainMerging.py index ab95a4d73784cd4e8468fdb74fd7b11556b59f7e..ce134ef7c44c3a18c28056696b506e04cf09c7c5 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLT/Config/Utility/ChainMerging.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLT/Config/Utility/ChainMerging.py @@ -1,7 +1,7 @@ # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration from TriggerMenuMT.HLT.Config.Utility.MenuAlignmentTools import get_alignment_group_ordering as getAlignmentGroupOrdering -from TriggerMenuMT.HLT.Config.MenuComponents import Chain, ChainStep, EmptyMenuSequenceCfg, isEmptySequenceCfg +from TriggerMenuMT.HLT.Config.MenuComponents import Chain, ChainStep, EmptyMenuSequenceCfg, isEmptySequenceCfg, createEmptyMenuSequenceCfg from AthenaCommon.Logging import logging from DecisionHandling.DecisionHandlingConfig import ComboHypoCfg @@ -176,8 +176,9 @@ def mergeParallel(chainDefList, offset, leg_numbering = None, perSig_lengthOfCha sigNames += [stepDict['chainParts'][0]['signature'] + is_fs_string] seqMultName = '_'.join([sigName for sigName in sigNames]) - seqStepName = getMergedEmptyStepName(align_grp_to_lengthen, current_leg_ag_length+i, 1, seqMultName) - seqNames = [getEmptySeqName(previous_step_dicts[iSeq]['signature'], current_leg_ag_length+i, align_grp_to_lengthen) for iSeq in range(len(sigNames))] + nLegs = 1 # TODO, make it follow the real multiplicity of the step + seqStepName = getMergedEmptyStepName(align_grp_to_lengthen, current_leg_ag_length+i, nLegs, seqMultName) + seqNames = [getEmptySeqName(previous_step_dicts[iSeq]['signature'], current_leg_ag_length+i, align_grp_to_lengthen,i) for iSeq in range(len(sigNames))] emptySequences = build_empty_sequences(previous_step_dicts, step_mult, 'mergeParallel', cConfig.L1decisions, seqNames, chainName) # insert a step with an empty sequence @@ -193,7 +194,8 @@ def mergeParallel(chainDefList, offset, leg_numbering = None, perSig_lengthOfCha log.debug("[mergeParallel] Alignment groups are empty for this combined chain") allSteps.append(cConfig.steps) - allStepsMult.append(len(cConfig.steps[0].multiplicity)) + #TODO: instead of the real step multiplicy (len(cConfig.steps[0].multiplicity))), we set allStepsMult=[1] because the zip doesn't need it: when a step is missing in one leg, one None step is added, not multiple steps. I think we can remove the allStepsMult in the zip_longest below + allStepsMult.append(1) nSteps.append(len(cConfig.steps)) l1Decisions.extend(cConfig.L1decisions) @@ -245,17 +247,11 @@ def getMergedEmptyStepName(alignmentGroup, stepNumber, multiplicity, signature): currentStepName = 'Empty' + alignmentGroup +'Align'+str(stepNumber)+'_'+ str(multiplicity) + signature return currentStepName -def getEmptySeqName(stepName, step_number, alignGroup): - #remove redundant instances of StepN - if re.search('^Step[0-9]_',stepName): - stepName = stepName[6:] - elif re.search('^Step[0-9]{2}_', stepName): - stepName = stepName[7:] - seqName = 'Empty'+ alignGroup +'Seq'+str(step_number)+ '_'+ stepName +def getEmptySeqName(signature, step_number, alignGroup,order): + seqName = 'Empty'+ alignGroup +'Seq'+str(step_number)+ '_'+ str(order) + signature return seqName - def isFullScanRoI(inputL1Nav): fsRoIList = ['HLTNav_L1FSNOSEED','HLTNav_L1MET','HLTNav_L1J'] @@ -339,10 +335,9 @@ def serial_zip(allSteps, chainName, chainDefList, legOrdering): log.debug("[serial_zip] nLegs: %s, len(emptyChainDicts): %s, len(L1decisions): %s", nLegs, len(emptyChainDicts), len(chainDefList[stepPlacement2].L1decisions)) sigNames = [] for ileg,(emptyChainDict,_) in enumerate(zip(emptyChainDicts,chainDefList[stepPlacement2].L1decisions)): - if isFullScanRoI(chainDefList[stepPlacement2].L1decisions[ileg]): - sigNames +=[emptyChainDict['chainParts'][0]['signature']+'FS'] - else: - sigNames +=[emptyChainDict['chainParts'][0]['signature']] + is_fs_string = 'FS' if isFullScanRoI(chainDefList[stepPlacement2].L1decisions[ileg]) else '' + sigNames +=[emptyChainDict['chainParts'][0]['signature']+is_fs_string] + seqMultName = '_'.join([sigName for sigName in sigNames]) currentAG = '' @@ -364,7 +359,7 @@ def serial_zip(allSteps, chainName, chainDefList, legOrdering): seqStepName = getMergedEmptyStepName(currentAG, ag_step_index, nLegs, seqMultName) - seqNames = [getEmptySeqName(emptyChainDicts[iSeq]['signature'], ag_step_index, currentAG) for iSeq in range(nLegs)] + seqNames = [getEmptySeqName(emptyChainDicts[iSeq]['signature'], ag_step_index, currentAG,iSeq) for iSeq in range(nLegs)] log.verbose("[serial_zip] step name for this leg: %s", seqStepName) log.verbose("[serial_zip] created empty sequence(s): %s", seqNames) @@ -465,7 +460,7 @@ def makeCombinedStep(parallel_steps, stepNumber, chainDefList, allSteps = None, log.debug("hasNonEmptyStep %d", hasNonEmptyStep) if not hasNonEmptyStep: - + # only empty steps here if len(parallel_steps)>=len(chainDefList) and all(step is None for step in parallel_steps[len(chainDefList):]): # We need to remove manually here the None steps exceeding the len of chainDefList. The right solution # would be to make sure that these cases don't happen upstream, but I am not confident enough with this @@ -477,7 +472,8 @@ def makeCombinedStep(parallel_steps, stepNumber, chainDefList, allSteps = None, # every step is empty but some might have empty sequences and some might not if step is None or step.isEmpty: new_stepDicts = deepcopy(chainDefList[chain_index].steps[-1].stepDicts) - currentStepName = getMergedEmptyStepName(chainDefList[chain_index].alignmentGroups[0], stepNumber, chainDefList[chain_index].steps[-1].multiplicity, new_stepDicts[0]['signature']) + nLegs = len(chainDefList[chain_index].steps[-1].multiplicity) + currentStepName = getMergedEmptyStepName(chainDefList[chain_index].alignmentGroups[0], stepNumber, nLegs, new_stepDicts[0]['signature']) log.debug('[makeCombinedStep] step has no sequences, making empty step %s', currentStepName) # we need a chain dict here, use the one corresponding to this leg of the chain @@ -518,42 +514,50 @@ def makeCombinedStep(parallel_steps, stepNumber, chainDefList, allSteps = None, log.debug("[makeCombinedStep] Merged empty step: \n %s", theChainStep) return theChainStep - stepSeq = [] - chain_indices = [] - # create the list of leg indices that takes into account the multiplicity of the sub-legs - # (for example if a leg is already the result of a merging of other sub-legs, the leg index is repeated as many times as the number of sublegs. alignmentGroups are stored preserving this order, so it can be used here) + stepSeq = [] + legsInStep = [] + # count the number of legs inside this chain part/step (inner legs) + # this happens if the step is already the result of a merging, due to the alignemnt, and can have more than one leg + # use the alignmentGroups here, which is stored by grouping the legs per alignemnt group + # TODO: can be extracted from stepDict['chainParts'][0]['multiplicity']? for num, chain in enumerate(chainDefList): - chain_indices.extend(list(repeat(num, len(chain.alignmentGroups)))) - - for chain_index, step in zip(chain_indices, parallel_steps): #this is a horizontal merge! - + legsInStep.append(len(chain.alignmentGroups)) + assert(len(legsInStep) == len(parallel_steps)) + + for chain_index, step in enumerate(parallel_steps): #this is a horizontal merge! + if step is None or (hasNonEmptyStep and step.isEmpty): # this happens for merging chains with different numbers of steps, we need to "pad" out with empty sequences to propogate the decisions # all other chain parts' steps should contain an empty sequence - + + log.debug("[makeCombinedStep] step %s is Empty and has %d legs", step.name if step is not None else "None", legsInStep[chain_index]) if alignment_group == "": alignment_group = chainDefList[0].alignmentGroups[0] - new_stepDict = deepcopy(chainDefList[chain_index].steps[-1].stepDicts[-1]) - seqName = getEmptySeqName(new_stepDict['signature'], stepNumber, alignment_group) + # loop over the inner legs of this sub-chain and create one empty sequence per each inner leg + for innerLeg in range(legsInStep[chain_index]): + new_stepDict = deepcopy(chainDefList[chain_index].steps[-1].stepDicts[-1]) + seqName = getEmptySeqName( new_stepDict['signature'], stepNumber, alignment_group, innerLeg) + log.debug("[makeCombinedStep] creating Empty sequence %s", seqName) + signature=new_stepDict['signature'] + is_fs_string = 'FS' if isFullScanRoI(chainDefList[chain_index].L1decisions[0]) else '' + seqName=seqName+is_fs_string + signature=new_stepDict['signature']+is_fs_string + thisEmpty = createEmptyMenuSequenceCfg(None, seqName) + stepSeq.append(functools.partial(thisEmpty, name=seqName)) + oldLegName = new_stepDict['chainName'] + if re.search('^leg[0-9]{3}_',oldLegName): + oldLegName = oldLegName[7:] + new_stepDict['chainName'] = legName(oldLegName,leg_counter) + stepDicts.append(new_stepDict) + leg_counter += 1 - if isFullScanRoI(chainDefList[chain_index].L1decisions[0]): - stepSeq.append(functools.partial(EmptyMenuSequenceCfg, None, name=seqName+"FS")) - currentStepName = getMergedEmptyStepName(alignment_group, stepNumber, new_stepDict['chainParts'][0]['multiplicity'], new_stepDict['signature']+'FS') - - else: - stepSeq.append(functools.partial(EmptyMenuSequenceCfg, None, name=seqName)) - currentStepName = getMergedEmptyStepName(alignment_group, stepNumber, new_stepDict['chainParts'][0]['multiplicity'], new_stepDict['signature']) + nLegs = legsInStep[chain_index] + currentStepName = getMergedEmptyStepName(alignment_group, stepNumber, nLegs, signature) log.debug("[makeCombinedStep] found empty step to be merged, step number: %d chain_index: %s, step name: %s, made new empty sequence name: %s", stepNumber, chain_index, currentStepName, seqName) - # we need a chain dict here, use the one corresponding to this leg of the chain - oldLegName = new_stepDict['chainName'] - if re.search('^leg[0-9]{3}_',oldLegName): - oldLegName = oldLegName[7:] - new_stepDict['chainName'] = legName(oldLegName,leg_counter) - stepDicts.append(new_stepDict) - leg_counter += 1 + else: # Standard step, append it to the combined step log.debug("[makeCombinedStep] step %s, multiplicity = %s", step.name, str(step.multiplicity)) @@ -590,7 +594,7 @@ def makeCombinedStep(parallel_steps, stepNumber, chainDefList, allSteps = None, # the step naming for combined chains needs to be revisted!! stepName += '_' + currentStepName - log.debug('[makeCombinedStep] current step name %s',stepName) + log.debug('[makeCombinedStep] current step name %s, with %d sequences',stepName, len(stepSeq)) # for merged steps, we need to update the name to add the leg name comboHypoTools = list(set(comboHypoTools)) @@ -632,13 +636,13 @@ def zip_longest_parallel(AllSteps, multiplicity, fillvalue=None): def build_empty_sequences(emptyChainDicts, step_mult, caller, L1decisions, seqNames, chainName): emptySequences = [] - for ileg in range(len(L1decisions)): - if isFullScanRoI(L1decisions[ileg]): - log.debug("[%s] adding FS empty sequenc with name %s", caller, seqNames[ileg]+"FS") - emptySequences += [functools.partial(EmptyMenuSequenceCfg, None, name=seqNames[ileg]+"FS")] - else: - log.debug("[%s] adding non-FS empty sequence with name %s", caller, seqNames[ileg]) - emptySequences += [functools.partial(EmptyMenuSequenceCfg, None, name=seqNames[ileg])] + for ileg in range(len(L1decisions)): + is_fs_string = 'FS' if isFullScanRoI(L1decisions[ileg]) else '' + sname = seqNames[ileg]+is_fs_string + log.debug("[%s] adding %s empty sequenc with name %s", caller, is_fs_string, sname) + thisEmpty = createEmptyMenuSequenceCfg(None, sname) + emptySequences += [functools.partial(thisEmpty, name=sname)] + log.verbose("[%s] emptyChainDicts %s", caller, emptyChainDicts) log.debug("[%s] %s has number of empty sequences %d and empty legs in stepDicts %d",