Forked from
atlas / athena
82902 commits behind the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MenuComponents.py 37.94 KiB
# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
from AthenaCommon.Logging import logging
log = logging.getLogger( __name__ )
from collections import MutableSequence
from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponentsNaming import CFNaming
from AthenaCommon.CFElements import parOR, seqAND, compName, getProp
from DecisionHandling.DecisionHandlingConfig import ComboHypoCfg
from AthenaConfiguration.ComponentFactory import CompFactory
RoRSeqFilter=CompFactory.RoRSeqFilter
class Node(object):
"""base class representing one Alg + inputs + outputs, to be used to Draw dot diagrams and connect objects"""
def __init__(self, Alg):
self.name = ("%sNode")%( Alg.name )
self.Alg=Alg
self.inputs=[]
self.outputs=[]
def addOutput(self, name):
self.outputs.append(name)
def addInput(self, name):
self.inputs.append(name)
def getOutputList(self):
return self.outputs
def getInputList(self):
return self.inputs
def __repr__(self):
return "Node::%s [%s] -> [%s]"%(self.Alg.name, ' '.join(map(str, self.getInputList())), ' '.join(map(str, self.getOutputList())))
class AlgNode(Node):
"""Node class that connects inputs and outputs to basic alg. properties """
def __init__(self, Alg, inputProp, outputProp):
Node.__init__(self, Alg)
self.outputProp=outputProp
self.inputProp=inputProp
def addDefaultOutput(self):
if self.outputProp != '':
self.addOutput(("%s_%s"%(self.Alg.getName(),self.outputProp)))
def setPar(self, propname, value):
cval = getProp( self.Alg, propname)
if isinstance(cval, MutableSequence):
cval.append(value)
return setattr(self.Alg, propname, cval)
else:
return setattr(self.Alg, propname, value)
def resetPar(self, prop):
cval = getProp(self.Alg, prop)
if isinstance(cval, MutableSequence):
return setattr(self.Alg, prop, [])
else:
return setattr(self.Alg, prop, "")
def getPar(self, prop):
return getProp(self.Alg, prop)
def resetOutput(self):
self.resetPar(self.outputProp)
def resetInput(self):
self.resetPar(self.inputProp)
def addOutput(self, name):
outputs = self.readOutputList()
if name in outputs:
log.debug("Output DH not added in %s: %s already set!", self.Alg.getName(), name)
else:
if self.outputProp != '':
self.setPar(self.outputProp,name)
else:
log.error("no OutputProp set")
Node.addOutput(self, name)
def readOutputList(self):
outputs = []
cval = self.getPar(self.outputProp)
if cval == '':
return outputs
if isinstance(cval, MutableSequence):
outputs.extend(cval)
else:
outputs.append(cval)
return outputs
def addInput(self, name):
inputs = self.readInputList()
if name in inputs:
log.debug("Input DH not added in %s: %s already set!", self.Alg.getName(), name)
else:
if self.inputProp != '':
self.setPar(self.inputProp,name)
else:
log.error("no InputProp set")
Node.addInput(self, name)
def readInputList(self):
inputs = []
cval = self.getPar(self.inputProp)
if cval =='':
return inputs
if isinstance(cval, MutableSequence):
inputs.extend(cval)
else:
inputs.append(cval)
return inputs
def __repr__(self):
return "Alg::%s [%s] -> [%s]"%(self.Alg.getName(), ' '.join(map(str, self.getInputList())), ' '.join(map(str, self.getOutputList())))
def algColor(alg):
""" Set given color to Alg type"""
if isComboHypoAlg(alg):
return "darkorange"
if isHypoBase(alg):
return "darkorchid1"
if isInputMakerBase(alg):
return "cyan3"
if isFilterAlg(alg):
return "chartreuse3"
return "cadetblue1"
class HypoToolConf(object):
""" Class to group info on hypotools for ChainDict"""
def __init__(self, hypoToolGen):
self.hypoToolGen = hypoToolGen
self.name=hypoToolGen.__name__
def setConf( self, chainDict):
if type(chainDict) is not dict:
raise RuntimeError("Configuring hypo with %s, not good anymore, use chainDict" % str(chainDict) )
self.chainDict = chainDict
def create(self):
"""creates instance of the hypo tool"""
return self.hypoToolGen( self.chainDict )
def confAndCreate(self, chainDict):
"""sets the configuration and creates instance of the hypo tool"""
self.setConf(chainDict)
return self.create()
class HypoAlgNode(AlgNode):
"""Node for HypoAlgs"""
initialOutput= 'StoreGateSvc+UNSPECIFIED_OUTPUT'
def __init__(self, Alg):
assert isHypoBase(Alg), "Error in creating HypoAlgNode from Alg " + compName(Alg)
AlgNode.__init__(self, Alg, 'HypoInputDecisions', 'HypoOutputDecisions')
self.tools = []
self.previous=[]
def addOutput(self, name):
outputs = self.readOutputList()
if name in outputs:
log.debug("Output DH not added in %s: %s already set!", self.name, name)
elif self.initialOutput in outputs:
AlgNode.addOutput(self, name)
else:
log.error("Hypo " + self.name +" has already %s as configured output: you may want to duplicate the Hypo!" + outputs[0])
def addHypoTool (self, hypoToolConf):
log.debug(" Adding HypoTool %s to %s", hypoToolConf.chainDict['chainName'], compName(self.Alg))
if hypoToolConf.chainDict['chainName'] not in self.tools:
## HypoTools are private, so need to be created when added to the Alg
## this incantation may seem strange, however it is the only one which works
## trying tool = hypoToolConf.create() and then assignement does not work! will be no problem in run3 config
tools = self.Alg.HypoTools
self.Alg.HypoTools = tools+[hypoToolConf.create()]
self.tools.append( self.Alg.HypoTools[-1].getName() ) # should not be needed anymore
else:
raise RuntimeError("The hypo tool of name "+ hypoToolConf.chainDict['chainName'] +" already present")
def setPreviousDecision(self,prev):
self.previous.append(prev)
return self.addInput(prev)
def resetDecisions(self):
self.previous = []
self.resetOutput()
self.resetInput()
def __repr__(self):
return "HypoAlg::%s [%s] -> [%s], previous = [%s], HypoTools=[%s]"%(compName(self.Alg),' '.join(map(str, self.getInputList())),
' '.join(map(str, self.getOutputList())),
' '.join(map(str, self.previous)),
' '.join(map(str, self.tools)))
class SequenceFilterNode(AlgNode):
"""Node for any kind of sequence filter"""
def __init__(self, Alg, inputProp, outputProp):
AlgNode.__init__(self, Alg, inputProp, outputProp)
def addChain(self, name):
return self.setPar("Chains", name)
def getChains(self):
return self.getPar("Chains")
def __repr__(self):
return "SequenceFilter::%s [%s] -> [%s], chains=%s"%(compName(self.Alg),' '.join(map(str, self.getInputList())),' '.join(map(str, self.getOutputList())), self.getChains())
class RoRSequenceFilterNode(SequenceFilterNode):
def __init__(self, name):
Alg= RoRSeqFilter(name)
SequenceFilterNode.__init__(self, Alg, 'Input', 'Output')
class InputMakerNode(AlgNode):
def __init__(self, Alg):
assert isInputMakerBase(Alg), "Error in creating InputMakerNode from Alg " + compName(Alg)
AlgNode.__init__(self, Alg, 'InputMakerInputDecisions', 'InputMakerOutputDecisions')
input_maker_output = CFNaming.inputMakerOutName(compName(self.Alg))
self.addOutput(input_maker_output)
class ComboMaker(AlgNode):
def __init__(self, name, multiplicity, comboHypoCfg):
self.prop="MultiplicitiesMap"
self.mult=list(multiplicity)
self.comboHypoCfg = comboHypoCfg
Alg = RecoFragmentsPool.retrieve( self.create, name )
log.debug("ComboMaker init: Alg %s", name)
AlgNode.__init__(self, Alg, 'HypoInputDecisions', 'HypoOutputDecisions')
def create (self, name):
log.debug("ComboMaker.create %s",name)
return self.comboHypoCfg(name)
def addChain(self, chainDict):
chainName = chainDict['chainName']
log.debug("ComboMaker %s adding chain %s", compName(self.Alg), chainName)
allMultis = self.mult
newdict = {chainName : allMultis}
cval = self.Alg.getProperties()[self.prop] # check necessary to see if chain was added already?
if type(cval) is dict:
if chainName in cval.keys():
log.error("ERROR in cofiguration: ComboAlg %s has already been configured for chain %s", compName(self.Alg), chainName)
else:
cval[chainName]=allMultis
else:
cval=newdict
setattr(self.Alg, self.prop, cval)
def getChains(self):
cval = self.Alg.getProperties()[self.prop]
return cval.keys()
def createComboHypoTools(self, chainDict, comboToolConfs):
"""Created the ComboHypoTools"""
if not len(comboToolConfs):
return
confs = [ HypoToolConf( tool ) for tool in comboToolConfs ]
log.debug("ComboMaker.createComboHypoTools for chain %s, Alg %s with %d tools", chainDict["chainName"],self.Alg.getName(), len(comboToolConfs))
for conf in confs:
tools = self.Alg.ComboHypoTools
self.Alg.ComboHypoTools = tools + [ conf.confAndCreate( chainDict ) ]
#########################################################
# USEFULL TOOLS
#########################################################
def isHypoBase(alg):
if 'HypoInputDecisions' in alg.__class__.__dict__:
return True
prop = alg.__class__.__dict__.get('_properties')
if type(prop) is dict:
return ('HypoInputDecisions' in prop)
else:
return False
def isInputMakerBase(alg):
return ('InputMakerInputDecisions' in alg.__class__.__dict__)
def isFilterAlg(alg):
return isinstance(alg, RoRSeqFilter)
def isComboHypoAlg(alg):
return ('MultiplicitiesMap' in alg.__class__.__dict__)
##########################################################
# Now sequences and chains
##########################################################
class EmptyMenuSequence(object):
""" Class to emulate reco sequences with no Hypo"""
""" By construction it has no Hypo;"""
def __init__(self, the_name):
self._name = the_name
Maker = CompFactory.InputMakerForRoI("IM"+the_name)
Maker.RoITool = CompFactory.ViewCreatorInitialROITool()
self._maker = InputMakerNode( Alg = Maker )
self._seed=''
self._sequence = Node( Alg = seqAND(the_name, [Maker]))
log.debug("Made EmptySequence %s",the_name)
@property
def sequence(self):
return self._sequence
@property
def seed(self):
return self._seed
@property
def name(self):
return self._name
def getOutputList(self):
return self._maker.readOutputList() # Only one since it's merged
def connectToFilter(self, outfilter):
""" Connect filter to the InputMaker"""
self._maker.addInput(outfilter)
def createHypoTools(self, chainDict):
log.debug("This sequence is empty. No Hypo to conficure")
def addToSequencer(self, recoSeq_list, hypo_list):
recoSeq_list.add(self.sequence.Alg)
def buildDFDot(self, cfseq_algs, all_hypos, isCombo, last_step_hypo_nodes, file):
cfseq_algs.append(self._maker)
cfseq_algs.append(self.sequence )
file.write(" %s[fillcolor=%s]\n"%(self._maker.Alg.getName(), algColor(self._maker.Alg)))
file.write(" %s[fillcolor=%s]\n"%(self.sequence.Alg.getName(), algColor(self.sequence.Alg)))
return cfseq_algs, all_hypos, last_step_hypo_nodes
def getTools(self):
log.debug("No tools for empty menu sequences")
def setSeed( self, seed ):
self._seed = seed
def __repr__(self):
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")
class MenuSequence(object):
""" Class to group reco sequences with the Hypo"""
""" By construction it has one Hypo Only; behaviour changed to support muFastOvlpRmSequence() which has two, but this will change"""
def __init__(self, Sequence, Maker, Hypo, HypoToolGen):
assert compName(Maker).startswith("IM"), "The input maker {} name needs to start with letter: IM".format(compName(Maker))
self._sequence = Node( Alg=Sequence)
self._maker = InputMakerNode( Alg = Maker )
self._seed=''
input_maker_output= self.maker.readOutputList()[0] # only one since it's merged
self._name = CFNaming.menuSequenceName(compName(Hypo))
self._hypoToolConf = HypoToolConf( HypoToolGen )
self._hypo = HypoAlgNode( Alg = Hypo )
hypo_output = CFNaming.hypoAlgOutName(compName(Hypo))
self._hypo.addOutput(hypo_output)
self._hypo.setPreviousDecision( input_maker_output)
log.debug("MenuSequence.connect: connecting InputMaker and HypoAlg, adding: \n\
InputMaker::%s.output=%s",\
compName(self.maker.Alg), input_maker_output)
log.debug("HypoAlg::%s.HypoInputDecisions=%s, \n \
HypoAlg::%s.HypoOutputDecisions=%s",\
compName(self.hypo.Alg), self.hypo.readInputList()[0], compName(self.hypo.Alg), self.hypo.readOutputList()[0])
@property
def seed(self):
return self._seed
@property
def name(self):
return self._name
@property
def sequence(self):
return self._sequence
@property
def maker(self):
return self._maker
@property
def hypo(self):
return self._hypo
def getOutputList(self):
outputlist = []
outputlist.append(self._hypo.readOutputList()[0])
return outputlist
def connectToFilter(self, outfilter):
""" Connect filter to the InputMaker"""
self._maker.addInput(outfilter)
def connect(self, Hypo, HypoToolGen):
""" Sets the input and output of the hypo, and links to the input maker """
input_maker_output= self._maker.readOutputList()[0] # only one since it's merged
#### Add input/output Decision to Hypo
self.name = CFNaming.menuSequenceName(compName(Hypo))
self.hypoToolConf = HypoToolConf( HypoToolGen )
self._hypo = HypoAlgNode( Alg = Hypo )
hypo_output = CFNaming.hypoAlgOutName(compName(Hypo))
self._hypo.addOutput(hypo_output)
self._hypo.setPreviousDecision( input_maker_output)
log.debug("MenuSequence.connect: connecting InputMaker and HypoAlg and OverlapRemoverAlg, adding: \n\
InputMaker::%s.output=%s",\
compName(self._maker.Alg), input_maker_output)
log.debug("HypoAlg::%s.previousDecision=%s, \n HypoAlg::%s.output=%s",\
compName(self._hypo.Alg), input_maker_output, compName(self._hypo.Alg), self._hypo.readOutputList()[0])
def createHypoTools(self, chainDict):
if type(self._hypoToolConf) is list:
log.warning ("This sequence %s has %d multiple HypoTools ",self.sequence.name, len(self.hypoToolConf))
for hypo, hypoToolConf in zip(self._hypo, self._hypoToolConf):
hypoToolConf.setConf( chainDict )
hypo.addHypoTool(self._hypoToolConf)
else:
self._hypoToolConf.setConf( chainDict )
self._hypo.addHypoTool(self._hypoToolConf) #this creates the HypoTools
def addToSequencer(self, recoSeq_list, hypo_list):
recoSeq_list.add(self.sequence.Alg)
hypo_list.add(self._hypo.Alg)
def buildDFDot(self, cfseq_algs, all_hypos, isCombo, last_step_hypo_nodes, file):
cfseq_algs.append(self._maker)
cfseq_algs.append(self.sequence )
file.write(" %s[fillcolor=%s]\n"%(self._maker.Alg.getName(), algColor(self._maker.Alg)))
file.write(" %s[fillcolor=%s]\n"%(self.sequence.Alg.getName(), algColor(self.sequence.Alg)))
cfseq_algs.append(self._hypo)
file.write(" %s[color=%s]\n"%(self._hypo.Alg.getName(), algColor(self._hypo.Alg)))
all_hypos.append(self._hypo)
if not isCombo:
last_step_hypo_nodes.append(self._hypo)
return cfseq_algs, all_hypos, last_step_hypo_nodes
def getTools(self):
return self._hypo.tools
def setSeed( self, seed ):
self._seed = seed
def __repr__(self):
hyponame = self._hypo.Alg.getName()
hypotool = self._hypoToolConf.name
return "MenuSequence::%s \n Hypo::%s \n Maker::%s \n Sequence::%s \n HypoTool::%s\n"\
%(self.name, hyponame, self._maker.Alg.getName(), self.sequence.Alg.getName(), hypotool)
class CAMenuSequence(MenuSequence):
''' MenuSequence with Compoment Accumulator '''
def __init__(self, Sequence, Maker, Hypo, HypoToolGen, CA):
self.ca = CA
MenuSequence.__init__(self, Sequence, Maker, Hypo, HypoToolGen)
@property
def sequence(self):
makerAlg = self.ca.getEventAlgo(self._maker.Alg.name)
self._maker.Alg = makerAlg
return self._sequence
@property
def maker(self):
makerAlg = self.ca.getEventAlgo(self._maker.Alg.name)
self._maker.Alg = makerAlg
return self._maker
@property
def hypo(self):
hypoAlg = self.ca.getEventAlgo(self._hypo.Alg.name)
self._hypo.Alg = hypoAlg
return self._hypo
class Chain(object):
"""Basic class to define the trigger menu """
__slots__ ='name','steps','nSteps','alignmentGroups','vseeds','L1decisions'
def __init__(self, name, ChainSteps, L1Thresholds, nSteps = [], alignmentGroups = []):
"""
Construct the Chain from the steps
Out of all arguments the ChainSteps & L1Thresholds are most relevant, the chain name is used in debug messages
"""
self.name = name
self.steps=ChainSteps
self.nSteps = nSteps
self.alignmentGroups = alignmentGroups
self.vseeds=L1Thresholds
from L1Decoder.L1DecoderConfig import mapThresholdToL1DecisionCollection
# L1decisions are used to set the seed type (EM, MU,JET), removing the actual threshold
# in practice it is the L1Decoder Decision output
self.L1decisions = [ mapThresholdToL1DecisionCollection(stri) for stri in L1Thresholds]
log.debug("L1Decisions: %s", ' '.join(self.L1decisions))
self.setSeedsToSequences()
isCombo=False
#TO DO: check that all the steps are combo
for step in self.steps:
if step.isCombo:
isCombo=True
log.debug("Made %s Chain %s with seeds: %s ", "combo" if isCombo else "", name, self.L1decisions)
def numberAllSteps(self):
if len(self.steps)==0:
return
else:
import re
for stepID,step in enumerate(self.steps):
step_name = step.name
if re.search('^Step[0-9]_',step_name):
step_name = step_name[6:]
step.name = 'Step%d_'%(stepID+1)+step_name
return
def insertEmptySteps(self, chainDict, empty_step_name, n_new_steps, start_position):
#start position indexed from 0. if start position is 3 and length is 2, it works like:
# [old1,old2,old3,old4,old5,old6] ==> [old1,old2,old3,empty1,empty2,old4,old5,old6]
import re
if len(self.steps) == 0 :
log.error("I can't insert empty steps because the chain doesn't have any steps yet!")
if len(self.steps) < start_position :
log.error("I can't insert empty steps at step %d because the chain doesn't have that many steps!", start_position)
chain_steps_pre_split = self.steps[:start_position]
chain_steps_post_split = self.steps[start_position:]
next_step_name = ''
prev_step_name = ''
if start_position == 0:
next_step_name = chain_steps_post_split[0].name
if re.search('^Step[0-9]_',next_step_name):
next_step_name = next_step_name[6:]
prev_step_name = 'empty_'+str(len(self.L1decisions))+'L1in'
else:
if len(chain_steps_post_split) == 0:
log.error("Adding empty steps to the end of a chain - why would you do this?")
else:
prev_step_name = chain_steps_pre_split[-1].name
next_step_name = chain_steps_post_split[0].name
steps_to_add = []
for stepID in range(1,n_new_steps+1):
new_step_name = prev_step_name+'_'+empty_step_name+'%d_'%stepID+next_step_name
log.debug("Configuring empty step " + new_step_name)
steps_to_add += [ChainStep(new_step_name, [], [], [chainDict], comboHypoCfg=ComboHypoCfg)]
self.steps = chain_steps_pre_split + steps_to_add + chain_steps_post_split
return
def checkMultiplicity(self):
if len(self.steps) == 0:
return 0
mult=[sum(step.multiplicity) for step in self.steps] # on mult per step
not_empty_mult = [m for m in mult if m!=0]
if len(not_empty_mult) == 0: #empty chain?
log.error("checkMultiplicity: Chain %s has all steps with multiplicity =0: what to do?", self.name)
return 0
if not_empty_mult.count(not_empty_mult[0]) != len(not_empty_mult):
log.error("checkMultiplicity: Chain %s has steps with differnt multiplicities: %s", self.name, ' '.join(mult))
return 0
if not_empty_mult[0] != len(self.vseeds):
log.error("checkMultiplicity: Chain %s has %d multiplicity per step, and %d L1Decisions", self.name, mult, len(self.vseeds))
return 0
return not_empty_mult[0]
def setSeedsToSequences(self):
""" Set the L1 seeds (L1Decisions) to the menu sequences """
if len(self.steps) == 0:
return
# TODO: check if the number of seeds is sufficient for all the seuqences, no action of no steps are configured
for step in self.steps:
for seed, seq in zip(self.L1decisions, step.sequences):
seq.setSeed( seed )
log.debug( "setSeedsToSequences: Chain %s adding seed %s to sequence in step %s", self.name, seed, step.name )
def getChainLegs(self):
""" This is extrapolating the chain legs from the chain dictionary"""
from TriggerMenuMT.HLTMenuConfig.Menu.ChainDictTools import splitChainInDict
listOfChainDictsLegs = splitChainInDict(self.name)
legs = [part['chainName'] for part in listOfChainDictsLegs]
return legs
def createHypoTools(self):
""" This is extrapolating the hypotool configuration from the chain name"""
log.debug("createHypoTools for chain %s", self.name)
from TriggerMenuMT.HLTMenuConfig.Menu.ChainDictTools import splitChainInDict
# this spliting is only needed for chains which don't yet attach
# the dictionaries to the chain steps. It should be removed
# later once that migration is done.
listOfChainDictsLegs = splitChainInDict(self.name)
for step in self.steps:
log.debug("createHypoTools for Step %s", step.name)
if len(step.sequences) == 0:
continue
if sum(step.multiplicity) >1 and not step.isCombo:
log.error("This should be an error, because step mult > 1 (%d), but step is not combo", sum(step.multiplicity))
if len(step.chainDicts) > 0:
# new way to configure hypo tools, works if the chain dictionaries have been attached to the steps
log.debug('%s in new hypo tool creation method, step mult= %d, isCombo=%d', self.name, sum(step.multiplicity), step.isCombo)
log.debug("N(seq)=%d, N(chainDicts)=%d", len(step.sequences), len(step.chainDicts))
assert len(step.sequences)==len(step.chainDicts), "createHypoTools only makes sense if number of sequences == number of chain dicts"
for seq, onePartChainDict in zip(step.sequences, step.chainDicts):
log.debug(' seq: %s, onePartChainDict:', seq.name)
log.debug(' ' + str(onePartChainDict))
seq.createHypoTools( onePartChainDict )
else:
# legacy way, to be removed once all signatures pass the chainDicts to the steps
step_mult = [str(m) for m in step.multiplicity]
log.debug('%s in old hypo tool creation method', self.name)
menu_mult = [ part['chainParts'][0]['multiplicity'] for part in listOfChainDictsLegs ]
if step_mult != menu_mult:
# Probably this shouldn't happen, but it currently does
log.warning("Got multiplicty %s from chain parts, but have %s multiplicity in this step. This is expected only for jet chains, but it has happened for %s, using the first chain dict", menu_mult, step.multiplicity, self.name)
firstChainDict = listOfChainDictsLegs[0]
firstChainDict['chainName']= self.name # rename the chaindict to remove the leg name
for seq in step.sequences:
seq.createHypoTools( firstChainDict )
else:
# add one hypotool per sequence and chain part
for seq, onePartChainDict in zip(step.sequences, listOfChainDictsLegs):
seq.createHypoTools( onePartChainDict )
step.createComboHypoTools(self.name)
def __repr__(self):
return "-*- Chain %s -*- \n + Seeds: %s \n + Steps: \n %s \n"%(\
self.name, ' '.join(map(str, self.L1decisions)), '\n '.join(map(str, self.steps)))
class CFSequence(object):
"""Class to describe the flow of decisions through ChainStep + filter with their connections (input, output)
A Filter can have more than one input/output if used in different chains, so this class stores and manages all of them (when doing the connect)
"""
def __init__(self, ChainStep, FilterAlg):
self.filter = FilterAlg
self.step = ChainStep
if self.step.isCombo:
self.connectCombo()
self.setDecisions()
log.debug("CFSequence.__init: created %s ",self)
def setDecisions(self):
""" Set the output decision of this CFSequence as the hypo outputdecision; In case of combo, takes the Combo outputs"""
self.decisions=[]
# empty steps:
if not len(self.step.sequences):
self.decisions.extend(self.filter.getOutputList())
else:
if self.step.isCombo:
self.decisions.extend(self.step.combo.getOutputList())
else:
for sequence in self.step.sequences:
sequence_outputs=sequence.getOutputList()
for output in sequence_outputs:
self.decisions.append(output)
log.debug("CFSequence: set out decisions: %s", self.decisions)
def connect(self, connections):
"""Connect filter to ChainStep (and all its sequences) through these connections (which are sets of filter outputs)
if a ChainStep contains the same sequence multiple times (for multi-object chains),
the filter is connected only once (to avoid multiple DH links)
"""
log.debug("CFSequence: connect Filter %s with %d menuSequences of step %s, using %d connections", compName(self.filter.Alg), len(self.step.sequences), self.step.name, len(connections))
if len(connections) == 0:
log.error("ERROR, no filter outputs are set!")
if len(self.step.sequences):
# check whether the number of filter outputs are the same as the number of sequences in the step
if len(connections) != len(self.step.sequences):
log.error("Found %d connections and %d MenuSequences in Step %s", len(connections), len(self.step.sequences), self.step.name)
nseq=0
for seq in self.step.sequences:
filter_out = connections[nseq]
log.debug("CFSequence: Found input %s to sequence::%s from Filter::%s (from seed %s)", filter_out, seq.name, compName(self.filter.Alg), seq.seed)
seq.connectToFilter( filter_out )
nseq+=1
else:
log.debug("This CFSequence has no sequences: outputs are the Filter outputs, which are %d", len(self.decisions))
def connectCombo(self):
""" connect Combo to Hypos"""
for seq in self.step.sequences:
if type(seq.getOutputList()) is list:
combo_input=seq.getOutputList()[-1] # last one?
else:
combo_input=seq.getOutputList()[0]
self.step.combo.addInput(combo_input)
log.debug("CFSequence.connectCombo: adding input to %s: %s", self.step.combo.Alg.getName(), combo_input)
# inputs are the output decisions of the hypos of the sequences
combo_output=CFNaming.comboHypoOutputName (self.step.combo.Alg.getName(), combo_input)
self.step.combo.addOutput(combo_output)
log.debug("CFSequence.connectCombo: adding output to %s: %s", self.step.combo.Alg.getName(), combo_output)
def __repr__(self):
return "--- CFSequence ---\n + Filter: %s \n + decisions: %s\n + %s \n"%(\
self.filter.Alg.getName(), self.decisions, self.step)
class StepComponent(object):
""" Class to build hte ChainStep, for including empty sequences"""
def __init__(self, sequence, multiplicity,empty):
self.sequence=sequence
self.multiplicity=multiplicity
self.empty=empty
class ChainStep(object):
"""Class to describe one step of a chain; if multiplicity is greater than 1, the step is combo/combined. Set one multiplicity value per sequence"""
def __init__(self, name, Sequences=[], multiplicity=[1], chainDicts=[], comboHypoCfg=ComboHypoCfg, comboToolConfs=[]):
# include cases of emtpy steps with multiplicity = [] or multiplicity=[0,0,0///]
if sum(multiplicity)==0:
multiplicity=[]
# sanity check on inputs
if len(Sequences) != len(multiplicity):
raise RuntimeError("Tried to configure a ChainStep %s with %i Sequences and %i multiplicities. These lists must have the same size" % (name, len(Sequences), len(multiplicity)) )
self.name = name
self.sequences=Sequences
self.multiplicity = multiplicity
self.comboHypoCfg=comboHypoCfg
self.comboToolConfs=comboToolConfs
self.isCombo=sum(multiplicity)>1
self.combo=None
self.chainDicts = chainDicts
if self.isCombo:
self.makeCombo()
def addComboHypoTools(self, tools):
self.comboToolConfs.append(tools)
def makeCombo(self):
if len(self.sequences)==0:
return
hashableMult = tuple(self.multiplicity)
self.combo = RecoFragmentsPool.retrieve(createComboAlg, None, name=CFNaming.comboHypoName(self.name), multiplicity=hashableMult, comboHypoCfg=self.comboHypoCfg)
def createComboHypoTools(self, chainName):
if self.isCombo:
from TriggerMenuMT.HLTMenuConfig.Menu.TriggerConfigHLT import TriggerConfigHLT
chainDict = TriggerConfigHLT.getChainDictFromChainName(chainName)
self.combo.createComboHypoTools(chainDict, self.comboToolConfs)
def getChainNames(self):
if not self.isCombo:
return [seq.getTools() for seq in self.sequences]
else:
return list(self.combo.getChains())
def __repr__(self):
if len(self.sequences) == 0:
return "--- ChainStep %s ---\n is Empty, ChainDict = %s "%(self.name, ' '.join(map(str, [dic['chainName'] for dic in self.chainDicts])) )
if not self.isCombo:
return "--- ChainStep %s ---\n , multiplicity = %d ChainDict = %s \n + MenuSequences = %s "%(self.name, sum(self.multiplicity), ' '.join(map(str, [dic['chainName'] for dic in self.chainDicts])), ' '.join(map(str, [seq.name for seq in self.sequences]) ))
else:
if self.combo:
calg = self.combo.Alg.name()
else:
calg = 'NONE'
return "--- ChainStep %s ---\n + isCombo, multiplicity = %d ChainDict = %s \n + MenuSequences = %s \n + ComboHypo = %s, ComboHypoTools = %s" %\
(self.name, sum(self.multiplicity),
' '.join(map(str, [dic['chainName'] for dic in self.chainDicts])),
' '.join(map(str, [seq.name for seq in self.sequences]) ),
calg,
' '.join(map(str, [tool.__name__ for tool in self.comboToolConfs])))
def createComboAlg(dummyFlags, name, multiplicity, comboHypoCfg):
return ComboMaker(name, multiplicity, comboHypoCfg)
# this is fragment for New JO
from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
class InEventReco( ComponentAccumulator ):
""" Class to handle in-event reco """
def __init__(self, name, inputMaker=None):
super( InEventReco, self ).__init__()
self.name = name
self.mainSeq = seqAND( name )
self.addSequence( self.mainSeq )
# Details below to be checked
self.inputMakerAlg = inputMaker
# Avoid registering a duplicate
self.addEventAlgo( self.inputMakerAlg, self.mainSeq.name )
self.recoSeq = parOR( "InputSeq_"+self.inputMakerAlg.name )
self.addSequence( self.recoSeq, self.mainSeq.name )
pass
def mergeReco( self, ca ):
""" Merged CA movnig reconstruction algorithms into the right sequence """
return self.merge( ca, sequenceName=self.recoSeq.getName() )
def addHypoAlg(self, alg):
self.addEventAlgo( alg, self.mainSeq.name )
def sequence( self ):
return self.mainSeq
def inputMaker( self ):
return self.inputMakerAlg
class InViewReco( ComponentAccumulator ):
""" Class to handle in-view reco, sets up the View maker if not provided and exposes InputMaker so that more inputs to it can be added in the process of assembling the menu """
def __init__(self, name, viewMaker=None, roisKey=None):
super( InViewReco, self ).__init__()
self.name = name
self.mainSeq = seqAND( name )
self.addSequence( self.mainSeq )
ViewCreatorInitialROITool=CompFactory.ViewCreatorInitialROITool
if viewMaker:
self.viewMakerAlg = viewMaker
else:
self.viewMakerAlg = CompFactory.EventViewCreatorAlgorithm("IM"+name,
ViewFallThrough = True,
RoIsLink = 'initialRoI',
RoITool = ViewCreatorInitialROITool(),
InViewRoIs = roisKey if roisKey else name+'RoIs',
Views = name+'Views',
ViewNodeName = name+"InView")
self.addEventAlgo( self.viewMakerAlg, self.mainSeq.name )
self.viewsSeq = parOR( self.viewMakerAlg.ViewNodeName )
self.addSequence( self.viewsSeq, self.mainSeq.name )
def addInputFromFilter(self, filterAlg ):
assert len(filterAlg.Output) == 1, "Can only oprate on filter algs with one configured output, use addInput to setup specific inputs"
self.addInput( filterAlg.Output[0], "Reco_"+( filterAlg.Output[0].replace("Filtered_", "") ) )
def addInput(self, inKey, outKey ):
"""Adds input (DecisionsContainer) from which the views should be created """
self.viewMakerAlg.InputMakerInputDecisions += [ inKey ]
self.viewMakerAlg.InputMakerOutputDecisions = outKey
def mergeReco( self, ca ):
""" Merged CA movnig reconstruction algorithms into the right sequence """
return self.merge( ca, sequenceName=self.viewsSeq.getName() )
def addHypoAlg(self, alg):
self.addEventAlgo( alg, self.mainSeq.name )
def sequence( self ):
return self.mainSeq
def inputMaker( self ):
return self.viewMakerAlg
class RecoFragmentsPool(object):
""" Class to host all the reco fragments that need to be reused """
fragments = {}
@classmethod
def retrieve( cls, creator, flags, **kwargs ):
""" create, or return created earlier reco fragment
Reco fragment is uniquelly identified by the function and set og **kwargs.
The flags are not part of unique identifier as creation of new reco fragments should not be caused by difference in the unrelated flags.
TODO, if that code survives migration to New JO we need to handle the case when the creator is an inner function
"""
requestHash = hash( ( creator, tuple(kwargs.keys()), tuple(kwargs.values()) ) )
if requestHash not in cls.fragments:
recoFragment = creator( flags, **kwargs )
cls.fragments[requestHash] = recoFragment
return recoFragment
else:
return cls.fragments[requestHash]
def getChainStepName(chainName, stepNumber):
return '{}_step{}'.format(chainName, stepNumber)
def createStepView(stepName):
stepReco = parOR(CFNaming.stepRecoName(stepName))
stepView = seqAND(CFNaming.stepViewName(stepName), [stepReco])
return stepReco, stepView