4 from importlib
import import_module
5 from collections
import defaultdict
as ddict
6 from itertools
import chain
8 from AthenaCommon.Logging
import logging
10 from .Base.L1MenuFlags
import L1MenuFlags
11 from .Base.Limits
import Limits
12 from .Base.Boards
import BoardType
13 from .Base.L1Menu
import L1Menu
14 from .Base.Thresholds
import TopoThreshold
15 from .Base.TopoAlgorithms
import AlgCategory
16 from .Base.L1Menu2JSON
import L1MenuJSONConverter
17 from .Config.TriggerTypeDef
import TT
18 from .Config.TopoAlgoDefMultiplicity
import TopoAlgoDefMultiplicity
19 from .Config.ItemDef
import ItemDef
22 L1MenuConfig is responsible for building the L1 Menu
24 Building a menu happens in two stages
26 1) Configuration objects are defined in the directory TriggerMenuMT/Config/ in *Def.py files
27 * Items are definded in Config/ItemDef.py
28 * Topo algorithms are define in files TopoAlgoDef*.py, with separate files for
29 - multiplicity counting algorithms on the Run 3 topo boards
30 - topological selection algorithms on the Run 3 topo boards
31 - topological selection algorithms on the MUCTPI board
32 - topological selection algorithms on the Run 2 topo boards (legacy boards)
33 * Thresholds are defined in files ThresholdDef.py
36 log = logging.getLogger(__name__)
42 L1MenuFlags.MenuSetup = flags.Trigger.triggerMenuSetup
57 AlgCategory.TOPO : 0, AlgCategory.MUCTPI : 0, AlgCategory.LEGACY : 0 }
61 for cat
in AlgCategory.getAllCategories():
65 L1MenuFlags.CTPVersion = 4
68 self.
l1menu.setBunchGroupSplitting()
71 log.error(
"Generating L1 menu %s is not possible", self.
menuName)
73 log.info(
"=== Generating L1 menu %s ===", self.
menuName)
78 log.info(
"=== Reading definition of algorithms, thresholds, and items ===")
82 log.info(
"=== Reading definition of menu ===")
101 except KeyError
as ex:
102 if name.startswith(
"R2TOPO"):
103 log.error(
"No legacy topo output %s is registered. Make sure it is defined in L1/Config/TopoAlgoDefLegacy.py", name)
104 elif name.startswith(
"TOPO"):
105 log.error(
"No topo output %s is registered. Make sure it is defined in L1/Config/TopoAlgoDef.py", name)
107 isLegacy = any(
filter(
lambda x: name.startswith(x), [
"EM",
"TAU",
"J",
"XE",
"TE",
"XS"]))
108 log.error(
"No threshold %s is registered. Make sure it is defined in L1/Config/ThresholdDef%s.py", name,
"Legacy" if isLegacy
else "")
122 """ Add a L1Topo algo to the set of algos which are registered for further use"""
125 algo.name =
"Mult_" + algo.name
128 raise RuntimeError(
'%s algo %s is already registered as such' % (self.
currentAlgoDef.desc, algo.name))
130 log.debug(
"Added in the %s type the algo: %s", self.
currentAlgoDef.desc, algo.name)
138 Add externally created L1 threshold to the list of available thresholds.
143 raise RuntimeError(
"For threshold %s the run (=2 or 3) was not set!" % thr.name)
148 raise RuntimeError(
"LVL1 threshold of name '%s' already defined, need to abort" % thr.name)
153 isTopo = isinstance(thr, TopoThreshold)
158 isNIM = thr.ttype
in [
'NIM',
'CALREQ',
'MBTSSI',
'MBTS',
'LUCID',
'BCM',
'BCMCMB',
'ZDC',
'BPTX',
'ZB']
163 elif thr.ttype ==
'MU':
173 Add all L1Topo triggers that are part of the menu as allowed input to the menu
179 for cat
in AlgCategory.getAllCategories():
182 outputLines += algo.outputs
if (
type(algo.outputs)
is list)
else [ algo.outputs ]
183 _topoTriggers[cat] =
sorted(outputLines)
184 log.info(
"... found %i topo triggerlines (source: %s)", len(_topoTriggers[cat]), cat )
185 log.debug(
"%r", _topoTriggers[cat])
188 multibitPattern = re.compile(
r"(?P<line>.*)\[(?P<bit>\d+)\]")
189 for cat
in [AlgCategory.TOPO, AlgCategory.MUCTPI, AlgCategory.LEGACY]:
190 multibitTopoTriggers =
set()
191 for topoLineName
in _topoTriggers[cat]:
192 m = multibitPattern.match(topoLineName)
194 topoThrName = cat.prefix + m.groupdict()[
'line']
195 multibitTopoTriggers.add( topoThrName )
197 topoThrName = cat.prefix + topoLineName
200 for topoThrName
in multibitTopoTriggers:
207 """ Adds a LVL1 item to the set of items which are registered for further use"""
209 log.error(
'LVL1 item %s is already registered with ctpid=%d',
220 def writeJSON(self, outputFile, bgsOutputFile = None, destdir="./"):
222 outputFile = destdir.rstrip(
'/') +
'/' + outputFile
223 if bgsOutputFile
is not None:
224 bgsOutputFile = destdir.rstrip(
'/') +
'/' + bgsOutputFile
226 bgsOutputFile = bgsOutputFile).
writeJSON(pretty=
True)
227 return outputFile, bgsOutputFile
229 log.error(
"No menu was generated, can not create json file")
233 """ resolve the menu name to the menu files to load"""
236 if menuToLoadReq.endswith(
'prescale'):
238 log.info(f
'Base menu name {menuToLoadReq} extracted from {self.menuFullName}')
239 from .Menu.MenuMapping
import menuMap
240 if menuToLoadReq
in menuMap:
241 menuToLoad = menuMap[menuToLoadReq]
243 log.info(
"Menu %s was requested, but will load %s as specified in TriggerMenuMT.L1.Menu.menuMap", menuToLoadReq, menuToLoad[0])
245 menuToLoad = [menuToLoadReq,
str(menuToLoadReq)+
"_inputs",
str(menuToLoadReq)+
"_inputs_legacy"]
249 from PyUtils.moduleExists
import moduleExists
251 if not moduleExists (modname):
252 log.error(
"No L1 menu available for %s, module %s does not exist", self.
menuName, modname )
260 Defines the list if item and threshold names that will be in the menu
261 Calls defineMenu() of the correct module 'Menu_<menuname>.py'
262 Menu.defineMenu() defines the menu via L1MenuFlags "items", "thresholds",
266 log.info(
"Reading TriggerMenuMT.Menu.Menu_%s", self.
menuFilesToLoad[0])
267 menumodule = __import__(
'TriggerMenuMT.L1.Menu.Menu_%s' % self.
menuFilesToLoad[0], globals(), locals(), [
'defineMenu'], 0)
268 menumodule.defineMenu()
269 log.info(
"... L1 menu '%s' contains %i items", self.
menuFilesToLoad[0], len(L1MenuFlags.items()))
271 log.info(
"Reading TriggerMenuMT.Menu.Menu_%s", self.
menuFilesToLoad[1])
272 topomenumodule = __import__(
'TriggerMenuMT.L1.Menu.Menu_%s' % self.
menuFilesToLoad[1], globals(), locals(), [
'defineMenu'], 0)
273 topomenumodule.defineInputsMenu()
276 for boardName, boardDef
in L1MenuFlags.boards().
items():
277 if "connectors" in boardDef:
278 connectorCount += len(boardDef[
"connectors"])
279 for c
in boardDef[
"connectors"]:
280 if "thresholds" in c:
281 algoCount += len(c[
"thresholds"])
282 elif "algorithmGroups" in c:
283 for t
in c[
"algorithmGroups"]:
284 algoCount += len(t[
"algorithms"])
286 for t
in c[
"signalGroups"]:
287 algoCount += len(t[
"signals"])
288 log.info(
"... L1Topo menu '%s' contains %i boards (%s)", self.
menuFilesToLoad[0], len(L1MenuFlags.boards()),
', '.
join(L1MenuFlags.boards().
keys()))
289 log.info(
" with %i connectors and %i input signals", connectorCount, algoCount)
292 log.info(
"Reading TriggerMenuMT.Menu.Menu_%s", self.
menuFilesToLoad[2])
293 legacymenumodule = __import__(
'TriggerMenuMT.L1.Menu.Menu_%s' % self.
menuFilesToLoad[2], globals(), locals(), [
'defineMenu'], 0)
294 legacymenumodule.defineLegacyInputsMenu()
295 log.info(
"... L1 legacy menu %s contains %i legacy boards (%s)", self.
menuFilesToLoad[2], len(L1MenuFlags.legacyBoards()),
', '.
join(L1MenuFlags.legacyBoards().
keys()))
296 except ImportError
as ie:
297 if ie.name ==
'TriggerMenuMT.L1.Menu.Menu_%s' % self.
menuFilesToLoad[2]:
298 log.info(
"==> No menu defining the legacy inputs was found, will assume this intended. %s %s %s",
299 ie.msg, ie.name, ie.path)
305 Registers the list if items and thresholds that could be used in the menu
306 Calls registerItem() of the correct module 'ItemDef.py'
308 from .Base.Items
import MenuItem
309 MenuItem.setMenuConfig(self)
310 from .Base.Thresholds
import Threshold
311 Threshold.setMenuConfig(self)
314 for algCat
in [AlgCategory.TOPO, AlgCategory.MUCTPI, AlgCategory.MULTI, AlgCategory.LEGACY]:
316 defFile =
"TriggerMenuMT.L1.Config.%s" % self.
currentAlgoDef.defFile
317 log.info(
"Reading %s", defFile)
318 import_module(defFile).__getattribute__(self.
currentAlgoDef.defFile).registerTopoAlgos(self)
321 log.info(
"Reading TriggerMenuMT.Config.ThreholdDef")
322 from .Config.ThresholdDef
import ThresholdDef
323 ThresholdDef.registerThresholds(self, self.
menuFullName)
328 log.info(
"Reading TriggerMenuMT.Config.ThreholdDefLegacy")
329 from .Config.ThresholdDefLegacy
import ThresholdDefLegacy
330 ThresholdDefLegacy.registerThresholds(self, self.
menuFullName)
333 log.info(
"Turning topo algo outputs into thresholds (except multiplicity counters)")
339 log.info(
"Reading TriggerMenuMT.Config.ItemDef")
347 msg =
"Algorithm of name %s in category %s is not registered. Please add the algorithm to L1/Config/%s.py" % (algoName, category, category.defFile)
350 raise RuntimeError(msg)
355 returns a list of all sorting algorithms that are needed to
356 produce the required output. A missing input will raise a
361 if type(alg.outputs)==list:
362 foundOutput = (input
in alg.outputs)
364 foundOutput = (input == alg.outputs)
368 if len(sortingAlgs)==0:
369 msg =
"No topo sorting algorithm is providing this output: %s. Please add the sorting algorithm to L1/Config/%s.py" % (input, topoAlgCategory.defFile)
370 raise RuntimeError(msg)
371 if len(sortingAlgs)>1:
372 msg =
"More than one sorting algorithm is providing this output: %s. Here the list: %s" % (input,
', '.
join(sortingAlgs))
373 raise RuntimeError(msg)
375 return sortingAlgs[0]
380 this function is called first after the menu definition is imported
381 it can be used to update some of the information or to perform initial checks
383 allBoards = (
list(L1MenuFlags.boards().
items()) +
list(L1MenuFlags.legacyBoards().
items()))
385 for (boardName, boardDef)
in allBoards:
386 boardDef[
"name"] = boardName
387 if "connectors" in boardDef:
388 for connDef
in boardDef[
"connectors"]:
389 connDef[
"board"] = boardName
391 for connDef
in boardDef[
"inputConnectors"]:
392 connDef[
"board"] = boardName
397 allBoardsWithTopo =
list( filter (
398 lambda b : BoardType.fromBoardName(b[0])
in [BoardType.TOPO, BoardType.MUCTPI],
399 chain(L1MenuFlags.boards().
items(), L1MenuFlags.legacyBoards().
items())
407 for (boardName, boardDef)
in allBoardsWithTopo:
408 currentTopoCategory = AlgCategory.getCategoryFromBoardName(boardName)
409 for connDef
in boardDef[
"connectors"]:
410 if connDef[
"format"] ==
'multiplicity':
412 for algGrp
in connDef[
"algorithmGroups"]:
413 for topodef
in algGrp[
"algorithms"]:
415 nLines += len(topodef.outputlines)
417 outputlines =
list(dict.fromkeys(map(
lambda x: re.split(
r"\[\d\]",x)[0], topodef.outputlines)))
418 for lineName
in outputlines:
419 thrName = currentTopoCategory.prefix + lineName
422 if 'legacy' in boardDef:
423 msg =
'Threshold %s is required for board %s, connector %s (file L1/Menu/Menu_%s.py), but it is not registered. ' % (thrName, boardName, connDef[
'name'], self.
menuFilesToLoad[2])
424 msg +=
'Please add L1Topo alg with output %s to L1/Config/TopoAlgoDefLegacy.py.' % (thrName.split(
'_',1)[-1])
426 msg =
'Threshold %s is required for board %s, connector %s (file L1/Menu/Menu_%s.py), but it is not registered. ' % (thrName, boardName, connDef[
'name'], self.
menuFilesToLoad[1])
427 msg +=
'Please add L1Topo alg with output %s to L1/Config/TopoAlgoDef.py.' % (thrName.split(
'_',1)[-1])
429 raise RuntimeError(msg)
431 self.
l1menu.addThreshold( thr )
432 log.info(
"Generating topo menu using %i topo algorithms with %i trigger lines", nAlgos, nLines )
440 allRequiredSortedInputs = {
441 AlgCategory.TOPO :
set(),
442 AlgCategory.MUCTPI :
set(),
443 AlgCategory.LEGACY :
set()
447 for (boardName, boardDef)
in allBoardsWithTopo:
448 for connDef
in boardDef[
"connectors"]:
449 if (
'muctpi' in boardName.lower())
and (connDef[
"format"]==
'multiplicity'):
453 currentTopoCategory = AlgCategory.getCategoryFromBoardName(boardName)
454 if currentTopoCategory == AlgCategory.TOPO
and connDef[
"format"] ==
'multiplicity':
455 currentTopoCategory = AlgCategory.MULTI
459 if connDef[
"format"] ==
'multiplicity':
460 for thrName
in connDef[
"thresholds"]:
463 nBits = connDef[
"nbitsDefault"]
464 if type(thrName)==tuple:
465 (thrName,nBits) = thrName
468 algoname =
"Mult_" + thrName
469 algoNames += [ algoname ]
470 algoNbits += [
int(nBits) ]
472 elif connDef[
"format"] ==
'topological':
473 for algGrp
in connDef[
"algorithmGroups"]:
474 for topodef
in algGrp[
"algorithms"]:
475 algoNames += [ topodef.algoname ]
477 fpgaNames += [
str(algGrp[
'fpga'])]
478 for algoName, algoBits, fpgaName
in zip(algoNames, algoNbits, fpgaNames):
482 if algoBits != algo.nbits:
483 msg =
"Algorithm %s defined with %i bits, but the associated threshold has %i bits " % (algo.name, algo.nbits, algoBits)
484 raise RuntimeError(msg)
485 self.
l1menu.checkBoardInputs(algo, connDef[
"name"], fpgaName)
487 self.
l1menu.addTopoAlgo( algo, category = currentTopoCategory )
490 if algo.isDecisionAlg():
491 allRequiredSortedInputs[currentTopoCategory].update( algo.inputs )
494 for cat
in allRequiredSortedInputs:
495 for input
in allRequiredSortedInputs[cat]:
498 self.
l1menu.addTopoAlgo( sortingAlgo, category = cat )
504 if len(self.
l1menu.items) > 0:
505 log.info(
"L1MenuConfig.generate() has already been called. Will ignore")
509 Generates the menu structure from the list of item and threshold names in the L1MenuFlags
515 from .Base.BunchGroupSet
import createDefaultBunchGroupSet
523 allBoards = (
list(L1MenuFlags.boards().
items()) +
list(L1MenuFlags.legacyBoards().
items()))
529 ItemDef.registerItems_AllCTPIn(self)
531 for (boardName, boardDef)
in L1MenuFlags.legacyBoards().
items():
532 for connDef
in boardDef[
"connectors"]:
534 if connDef[
"type"] ==
"ctpin" and connDef[
"format"] ==
"multiplicity":
535 self.
l1menu.checkL1CaloThresholds(connDef[
"thresholds"], boardName, connDef[
"name"])
537 list_of_undefined_thresholds = []
539 for (boardName, boardDef)
in L1MenuFlags.boards().
items():
540 for connDef
in boardDef[
"connectors"]:
541 if connDef[
"type"] ==
"ctpin" or connDef[
"format"] !=
"multiplicity":
543 for thrName
in connDef[
"thresholds"]:
544 if type(thrName)
is tuple:
545 (thrName, _) = thrName
546 if (thrName
is None)
or (thrName
in self.
l1menu.thresholds):
549 if threshold
is None:
550 log.error(
'Threshold %s is required in menu on board %s, connector %s, but it is not defined', thrName, boardName, connDef[
'name'] )
551 list_of_undefined_thresholds += [ thrName ]
553 self.
l1menu.addThreshold( threshold )
555 zbThrName = connDef[
"zeroBias"]
558 log.error(
'Zero bias threshold %s is listed in menu but not defined', zbThrName )
559 list_of_undefined_thresholds += [ zbThrName ]
561 self.
l1menu.addThreshold( zbThr )
567 for (boardName, boardDef)
in L1MenuFlags.boards().
items():
568 for connDef
in boardDef[
"connectors"]:
569 if connDef[
"format"] !=
"simple":
571 for sGrp
in connDef[
"signalGroups"]:
572 for thrName
in sGrp[
"signals"]:
573 if type(thrName)
is tuple:
574 (thrName, _) = thrName
575 if thrName
is None or thrName
in self.
l1menu.thresholds:
578 if threshold
is None:
579 log.error(
'Threshold %s is required in menu on board %s, connector %s, but it is not defined', thrName, boardName, connDef[
'name'] )
580 list_of_undefined_thresholds += [ thrName ]
582 self.
l1menu.addThreshold( threshold )
585 for (boardName, boardDef)
in allBoards:
586 for connDef
in boardDef[
"connectors"]:
587 if connDef[
"type"] !=
"ctpin":
589 for entry
in connDef[
"thresholds"]:
590 if type(entry)
is dict:
592 thrNames =
sum([x.outputlines
for x
in entry[
"algorithms"]],[])
593 elif type(entry)
is str:
595 elif type(entry)
is tuple:
596 thrNames = [ entry[0] ]
598 for thrName
in thrNames:
599 if thrName
is None or thrName
in self.
l1menu.thresholds:
602 if threshold
is None:
603 log.error(
'Threshold %s is listed in menu but not defined', thrName )
604 list_of_undefined_thresholds += [ thrName ]
606 self.
l1menu.addThreshold( threshold )
608 zbThrName = connDef[
"zeroBias"]
611 log.error(
'Zero bias threshold %s is listed in menu but not defined', zbThrName )
612 list_of_undefined_thresholds += [ zbThrName ]
614 self.
l1menu.addThreshold( zbThr )
618 if len(list_of_undefined_thresholds)>0:
619 raise RuntimeError(
"Found undefined threshold in menu %s, please add these thresholds to l1menu/ThresholdDef.py: %s" % \
620 (self.
l1menu.menuName,
', '.
join(list_of_undefined_thresholds)) )
629 ctpIdMap = L1MenuFlags.CtpIdMap()
630 for itemName
in L1MenuFlags.items():
632 if registeredItem
is None:
633 msg =
"L1 item '%s' has not been defined in L1/Config/ItemDef.py" % itemName
635 raise RuntimeError(msg)
637 if itemName
in ctpIdMap:
638 newCTPID = ctpIdMap[itemName]
639 registeredItem.setCtpid(newCTPID)
641 for thrName
in registeredItem.thresholdNames():
642 if thrName
not in self.
l1menu.thresholds:
643 isLegacyThr = any(
filter(
lambda x: thrName.startswith(x), [
"R2TOPO_",
"EM",
"HA",
"J",
"XE",
"TE",
"XS"]))
645 msg =
"L1 item {item} has been added to the menu L1/Menu/Menu_{menu}.py, but the required threshold {thr} is not listed as input in L1/Menu/Menu_{menu_inputs}.py".
format(item=itemName, thr=thrName, menu=self.
menuFilesToLoad[0], menu_inputs=self.
menuFilesToLoad[2]
if isLegacyThr
else self.
menuFilesToLoad[1])
647 raise RuntimeError(msg)
649 itemsForMenu += [ registeredItem ]
654 assigned_ctpids =
set([item.ctpid
for item
in itemsForMenu])
655 available_ctpids =
sorted(
list(
set(
range(Limits.MaxTrigItems)) - assigned_ctpids ), reverse=
True )
658 for item
in itemsForMenu:
660 if not item.name.startswith(
'L1_CALREQ'):
661 item.setTriggerType( item.trigger_type | TT.phys )
664 if len(available_ctpids)==0:
665 raise RuntimeError(
"No more CTP IDs available at L1!!")
666 item.setCtpid( available_ctpids.pop() )
668 self.
l1menu.addItem( item )
674 for (boardName, boardDef)
in allBoards:
675 for connDef
in boardDef[
"connectors"]:
676 self.
l1menu.addConnector( connDef )
682 for (boardName, boardDef)
in allBoards:
683 self.
l1menu.addBoard(boardDef)
688 legacyThresholdsSet =
set()
689 for conn
in self.
l1menu.connectors:
690 if not conn.isLegacy():
692 legacyThresholdsSet.update(conn.triggerThresholds())
693 for item
in self.
l1menu.items:
694 item.markLegacy(legacyThresholdsSet)
706 self.
l1menu.setupCTPMonitoring()
718 if 'pp' in self.
l1menu.menuName:
719 self.
l1menu.checkLegacyThresholds()
723 TopoAlgoDefMultiplicity.checkMultAlgoFWconstraints(self.
l1menu)
726 self.
l1menu.checkCountCTPInputsOutput()
729 self.
l1menu.checkCTPINconnectors()
732 self.
l1menu.checkPerfThresholds()
735 self.
l1menu.checkPtMinToTopo()
738 self.
l1menu.checkL1TopoParams()
741 self.
l1menu.checkItemsHaveInputs()
744 self.
l1menu.checkTOPObits()
748 self.
l1menu.checkHFmonitoring()
752 Set the correct mapping of thresholds according to the
753 order it was given in L1MenuFlags.thresholds list. That list
754 is usually defined in the setupMenu function of each menu
756 NIM and CALREQ types are not remapped !!
759 alreadyUsedMappingNumbers = ddict(set)
760 for thr
in self.
l1menu.thresholds:
763 alreadyUsedMappingNumbers[thr.ttype].
add(thr.mapping)
765 nextFreeMapping = ddict(
lambda: 0)
766 for k
in alreadyUsedMappingNumbers:
767 nextFreeMapping[k] = 0
768 for thr
in self.
l1menu.thresholds():
770 while nextFreeMapping[thr.ttype]
in alreadyUsedMappingNumbers[thr.ttype]:
771 nextFreeMapping[thr.ttype] += 1
772 log.debug(
'Setting mapping of threshold %s as %i', thr, nextFreeMapping[thr.ttype])
773 thr.mapping = nextFreeMapping[thr.ttype]
774 nextFreeMapping[thr.ttype] += 1
778 for (it_name, ps)
in L1MenuFlags.prescales().
items():
779 item = self.
l1menu.getItem(it_name)
783 log.warning(
'Cannot find item %s to set the prescale', it_name )
787 self.
l1menu.ctp.addMonCounters()
791 pattern = re.compile(
r'_v\d+|DC14')
792 patternPos = pattern.search(menuName)
794 menuName=menuName[:patternPos.end()]
796 log.info(
'Can\'t find pattern to shorten menu name, either non-existent in name or not implemented.')