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 ===")
102 except KeyError
as ex:
103 if name.startswith(
"R2TOPO"):
104 log.error(
"No legacy topo output %s is registered. Make sure it is defined in L1/Config/TopoAlgoDefLegacy.py", name)
105 elif name.startswith(
"TOPO"):
106 log.error(
"No topo output %s is registered. Make sure it is defined in L1/Config/TopoAlgoDef.py", name)
108 isLegacy = any(
filter(
lambda x: name.startswith(x), [
"EM",
"TAU",
"J",
"XE",
"TE",
"XS"]))
109 log.error(
"No threshold %s is registered. Make sure it is defined in L1/Config/ThresholdDef%s.py", name,
"Legacy" if isLegacy
else "")
123 """ Add a L1Topo algo to the set of algos which are registered for further use"""
126 algo.name =
"Mult_" + algo.name
129 raise RuntimeError(
'%s algo %s is already registered as such' % (self.
currentAlgoDef.desc, algo.name))
131 log.debug(
"Added in the %s type the algo: %s", self.
currentAlgoDef.desc, algo.name)
139 Add externally created L1 threshold to the list of available thresholds.
144 raise RuntimeError(
"For threshold %s the run (=2 or 3) was not set!" % thr.name)
149 raise RuntimeError(
"LVL1 threshold of name '%s' already defined, need to abort" % thr.name)
154 isTopo = isinstance(thr, TopoThreshold)
159 isNIM = thr.ttype
in [
'NIM',
'CALREQ',
'MBTSSI',
'MBTS',
'LUCID',
'BCM',
'BCMCMB',
'ZDC',
'BPTX',
'ZB']
164 elif thr.ttype ==
'MU':
174 Add all L1Topo triggers that are part of the menu as allowed input to the menu
180 for cat
in AlgCategory.getAllCategories():
183 outputLines += algo.outputs
if (
type(algo.outputs)
is list)
else [ algo.outputs ]
184 _topoTriggers[cat] =
sorted(outputLines)
185 log.info(
"... found %i topo triggerlines (source: %s)", len(_topoTriggers[cat]), cat )
186 log.debug(
"%r", _topoTriggers[cat])
189 multibitPattern = re.compile(
r"(?P<line>.*)\[(?P<bit>\d+)\]")
190 for cat
in [AlgCategory.TOPO, AlgCategory.MUCTPI, AlgCategory.LEGACY]:
191 multibitTopoTriggers =
set()
192 for topoLineName
in _topoTriggers[cat]:
193 m = multibitPattern.match(topoLineName)
195 topoThrName = cat.prefix + m.groupdict()[
'line']
196 multibitTopoTriggers.add( topoThrName )
198 topoThrName = cat.prefix + topoLineName
201 for topoThrName
in multibitTopoTriggers:
208 """ Adds a LVL1 item to the set of items which are registered for further use"""
210 log.error(
'LVL1 item %s is already registered with ctpid=%d',
221 def writeJSON(self, outputFile, bgsOutputFile = None, destdir="./"):
223 outputFile = destdir.rstrip(
'/') +
'/' + outputFile
224 if bgsOutputFile
is not None:
225 bgsOutputFile = destdir.rstrip(
'/') +
'/' + bgsOutputFile
227 bgsOutputFile = bgsOutputFile).
writeJSON(pretty=
True)
228 return outputFile, bgsOutputFile
230 log.error(
"No menu was generated, can not create json file")
234 """ resolve the menu name to the menu files to load"""
237 if menuToLoadReq.endswith(
'prescale'):
239 log.info(f
'Base menu name {menuToLoadReq} extracted from {self.menuFullName}')
240 from .Menu.MenuMapping
import menuMap
241 if menuToLoadReq
in menuMap:
242 menuToLoad = menuMap[menuToLoadReq]
244 log.info(
"Menu %s was requested, but will load %s as specified in TriggerMenuMT.L1.Menu.menuMap", menuToLoadReq, menuToLoad[0])
246 menuToLoad = [menuToLoadReq,
str(menuToLoadReq)+
"_inputs",
str(menuToLoadReq)+
"_inputs_legacy"]
250 from PyUtils.moduleExists
import moduleExists
252 if not moduleExists (modname):
253 log.error(
"No L1 menu available for %s, module %s does not exist", self.
menuName, modname )
261 Defines the list if item and threshold names that will be in the menu
262 Calls defineMenu() of the correct module 'Menu_<menuname>.py'
263 Menu.defineMenu() defines the menu via L1MenuFlags "items", "thresholds",
267 log.info(
"Reading TriggerMenuMT.Menu.Menu_%s", self.
menuFilesToLoad[0])
268 menumodule = __import__(
'TriggerMenuMT.L1.Menu.Menu_%s' % self.
menuFilesToLoad[0], globals(), locals(), [
'defineMenu'], 0)
269 menumodule.defineMenu()
270 log.info(
"... L1 menu '%s' contains %i items", self.
menuFilesToLoad[0], len(L1MenuFlags.items()))
272 log.info(
"Reading TriggerMenuMT.Menu.Menu_%s", self.
menuFilesToLoad[1])
273 topomenumodule = __import__(
'TriggerMenuMT.L1.Menu.Menu_%s' % self.
menuFilesToLoad[1], globals(), locals(), [
'defineMenu'], 0)
274 topomenumodule.defineInputsMenu()
277 for boardName, boardDef
in L1MenuFlags.boards().
items():
278 if "connectors" in boardDef:
279 connectorCount += len(boardDef[
"connectors"])
280 for c
in boardDef[
"connectors"]:
281 if "thresholds" in c:
282 algoCount += len(c[
"thresholds"])
283 elif "algorithmGroups" in c:
284 for t
in c[
"algorithmGroups"]:
285 algoCount += len(t[
"algorithms"])
287 for t
in c[
"signalGroups"]:
288 algoCount += len(t[
"signals"])
289 log.info(
"... L1Topo menu '%s' contains %i boards (%s)", self.
menuFilesToLoad[0], len(L1MenuFlags.boards()),
', '.
join(L1MenuFlags.boards().
keys()))
290 log.info(
" with %i connectors and %i input signals", connectorCount, algoCount)
293 log.info(
"Reading TriggerMenuMT.Menu.Menu_%s", self.
menuFilesToLoad[2])
294 legacymenumodule = __import__(
'TriggerMenuMT.L1.Menu.Menu_%s' % self.
menuFilesToLoad[2], globals(), locals(), [
'defineMenu'], 0)
295 legacymenumodule.defineLegacyInputsMenu()
296 log.info(
"... L1 legacy menu %s contains %i legacy boards (%s)", self.
menuFilesToLoad[2], len(L1MenuFlags.legacyBoards()),
', '.
join(L1MenuFlags.legacyBoards().
keys()))
297 except ImportError
as ie:
298 if ie.name ==
'TriggerMenuMT.L1.Menu.Menu_%s' % self.
menuFilesToLoad[2]:
299 log.info(
"==> No menu defining the legacy inputs was found, will assume this intended. %s %s %s",
300 ie.msg, ie.name, ie.path)
306 Registers the list if items and thresholds that could be used in the menu
307 Calls registerItem() of the correct module 'ItemDef.py'
309 from .Base.Items
import MenuItem
310 MenuItem.setMenuConfig(self)
311 from .Base.Thresholds
import Threshold
312 Threshold.setMenuConfig(self)
315 for algCat
in [AlgCategory.TOPO, AlgCategory.MUCTPI, AlgCategory.MULTI, AlgCategory.LEGACY]:
317 defFile =
"TriggerMenuMT.L1.Config.%s" % self.
currentAlgoDef.defFile
318 log.info(
"Reading %s", defFile)
319 import_module(defFile).__getattribute__(self.
currentAlgoDef.defFile).registerTopoAlgos(self)
322 log.info(
"Reading TriggerMenuMT.Config.ThreholdDef")
323 from .Config.ThresholdDef
import ThresholdDef
324 ThresholdDef.registerThresholds(self, self.
menuFullName)
329 log.info(
"Reading TriggerMenuMT.Config.ThreholdDefLegacy")
330 from .Config.ThresholdDefLegacy
import ThresholdDefLegacy
331 ThresholdDefLegacy.registerThresholds(self, self.
menuFullName)
334 log.info(
"Turning topo algo outputs into thresholds (except multiplicity counters)")
340 log.info(
"Reading TriggerMenuMT.Config.ItemDef")
348 msg =
"Algorithm of name %s in category %s is not registered. Please add the algorithm to L1/Config/%s.py" % (algoName, category, category.defFile)
351 raise RuntimeError(msg)
356 returns a list of all sorting algorithms that are needed to
357 produce the required output. A missing input will raise a
362 if type(alg.outputs)==list:
363 foundOutput = (input
in alg.outputs)
365 foundOutput = (input == alg.outputs)
369 if len(sortingAlgs)==0:
370 msg =
"No topo sorting algorithm is providing this output: %s. Please add the sorting algorithm to L1/Config/%s.py" % (input, topoAlgCategory.defFile)
371 raise RuntimeError(msg)
372 if len(sortingAlgs)>1:
373 msg =
"More than one sorting algorithm is providing this output: %s. Here the list: %s" % (input,
', '.
join(sortingAlgs))
374 raise RuntimeError(msg)
376 return sortingAlgs[0]
381 this function is called first after the menu definition is imported
382 it can be used to update some of the information or to perform initial checks
384 allBoards = (
list(L1MenuFlags.boards().
items()) +
list(L1MenuFlags.legacyBoards().
items()))
386 for (boardName, boardDef)
in allBoards:
387 boardDef[
"name"] = boardName
388 if "connectors" in boardDef:
389 for connDef
in boardDef[
"connectors"]:
390 connDef[
"board"] = boardName
392 for connDef
in boardDef[
"inputConnectors"]:
393 connDef[
"board"] = boardName
398 allBoardsWithTopo =
list( filter (
399 lambda b : BoardType.fromBoardName(b[0])
in [BoardType.TOPO, BoardType.MUCTPI],
400 chain(L1MenuFlags.boards().
items(), L1MenuFlags.legacyBoards().
items())
408 for (boardName, boardDef)
in allBoardsWithTopo:
409 currentTopoCategory = AlgCategory.getCategoryFromBoardName(boardName)
410 for connDef
in boardDef[
"connectors"]:
411 if connDef[
"format"] ==
'multiplicity':
413 for algGrp
in connDef[
"algorithmGroups"]:
414 for topodef
in algGrp[
"algorithms"]:
416 nLines += len(topodef.outputlines)
418 outputlines =
list(dict.fromkeys(map(
lambda x: re.split(
r"\[\d\]",x)[0], topodef.outputlines)))
419 for lineName
in outputlines:
420 thrName = currentTopoCategory.prefix + lineName
423 if 'legacy' in boardDef:
424 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])
425 msg +=
'Please add L1Topo alg with output %s to L1/Config/TopoAlgoDefLegacy.py.' % (thrName.split(
'_',1)[-1])
427 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])
428 msg +=
'Please add L1Topo alg with output %s to L1/Config/TopoAlgoDef.py.' % (thrName.split(
'_',1)[-1])
430 raise RuntimeError(msg)
432 self.
l1menu.addThreshold( thr )
433 log.info(
"Generating topo menu using %i topo algorithms with %i trigger lines", nAlgos, nLines )
441 allRequiredSortedInputs = {
442 AlgCategory.TOPO :
set(),
443 AlgCategory.MUCTPI :
set(),
444 AlgCategory.LEGACY :
set()
448 for (boardName, boardDef)
in allBoardsWithTopo:
449 for connDef
in boardDef[
"connectors"]:
450 if (
'muctpi' in boardName.lower())
and (connDef[
"format"]==
'multiplicity'):
454 currentTopoCategory = AlgCategory.getCategoryFromBoardName(boardName)
455 if currentTopoCategory == AlgCategory.TOPO
and connDef[
"format"] ==
'multiplicity':
456 currentTopoCategory = AlgCategory.MULTI
460 if connDef[
"format"] ==
'multiplicity':
461 for thrName
in connDef[
"thresholds"]:
464 nBits = connDef[
"nbitsDefault"]
465 if type(thrName)==tuple:
466 (thrName,nBits) = thrName
469 algoname =
"Mult_" + thrName
470 algoNames += [ algoname ]
471 algoNbits += [
int(nBits) ]
473 elif connDef[
"format"] ==
'topological':
474 for algGrp
in connDef[
"algorithmGroups"]:
475 for topodef
in algGrp[
"algorithms"]:
476 algoNames += [ topodef.algoname ]
478 fpgaNames += [
str(algGrp[
'fpga'])]
479 for algoName, algoBits, fpgaName
in zip(algoNames, algoNbits, fpgaNames):
483 if algoBits != algo.nbits:
484 msg =
"Algorithm %s defined with %i bits, but the associated threshold has %i bits " % (algo.name, algo.nbits, algoBits)
485 raise RuntimeError(msg)
486 self.
l1menu.checkBoardInputs(algo, connDef[
"name"], fpgaName)
488 self.
l1menu.addTopoAlgo( algo, category = currentTopoCategory )
491 if algo.isDecisionAlg():
492 allRequiredSortedInputs[currentTopoCategory].
update( algo.inputs )
495 for cat
in allRequiredSortedInputs:
496 for input
in allRequiredSortedInputs[cat]:
499 self.
l1menu.addTopoAlgo( sortingAlgo, category = cat )
505 if len(self.
l1menu.items) > 0:
506 log.info(
"L1MenuConfig.generate() has already been called. Will ignore")
510 Generates the menu structure from the list of item and threshold names in the L1MenuFlags
516 from .Base.BunchGroupSet
import createDefaultBunchGroupSet
524 allBoards = (
list(L1MenuFlags.boards().
items()) +
list(L1MenuFlags.legacyBoards().
items()))
530 ItemDef.registerItems_AllCTPIn(self)
532 for (boardName, boardDef)
in L1MenuFlags.legacyBoards().
items():
533 for connDef
in boardDef[
"connectors"]:
535 if connDef[
"type"] ==
"ctpin" and connDef[
"format"] ==
"multiplicity":
536 self.
l1menu.checkL1CaloThresholds(connDef[
"thresholds"], boardName, connDef[
"name"])
538 list_of_undefined_thresholds = []
540 for (boardName, boardDef)
in L1MenuFlags.boards().
items():
541 for connDef
in boardDef[
"connectors"]:
542 if connDef[
"type"] ==
"ctpin" or connDef[
"format"] !=
"multiplicity":
544 for thrName
in connDef[
"thresholds"]:
545 if type(thrName)
is tuple:
546 (thrName, _) = thrName
547 if (thrName
is None)
or (thrName
in self.
l1menu.thresholds):
550 if threshold
is None:
551 log.error(
'Threshold %s is required in menu on board %s, connector %s, but it is not defined', thrName, boardName, connDef[
'name'] )
552 list_of_undefined_thresholds += [ thrName ]
554 self.
l1menu.addThreshold( threshold )
556 zbThrName = connDef[
"zeroBias"]
559 log.error(
'Zero bias threshold %s is listed in menu but not defined', zbThrName )
560 list_of_undefined_thresholds += [ zbThrName ]
562 self.
l1menu.addThreshold( zbThr )
568 for (boardName, boardDef)
in L1MenuFlags.boards().
items():
569 for connDef
in boardDef[
"connectors"]:
570 if connDef[
"format"] !=
"simple":
572 for sGrp
in connDef[
"signalGroups"]:
573 for thrName
in sGrp[
"signals"]:
574 if type(thrName)
is tuple:
575 (thrName, _) = thrName
576 if thrName
is None or thrName
in self.
l1menu.thresholds:
579 if threshold
is None:
580 log.error(
'Threshold %s is required in menu on board %s, connector %s, but it is not defined', thrName, boardName, connDef[
'name'] )
581 list_of_undefined_thresholds += [ thrName ]
583 self.
l1menu.addThreshold( threshold )
586 for (boardName, boardDef)
in allBoards:
587 for connDef
in boardDef[
"connectors"]:
588 if connDef[
"type"] !=
"ctpin":
590 for entry
in connDef[
"thresholds"]:
591 if type(entry)
is dict:
593 thrNames =
sum([x.outputlines
for x
in entry[
"algorithms"]],[])
594 elif type(entry)
is str:
596 elif type(entry)
is tuple:
597 thrNames = [ entry[0] ]
599 for thrName
in thrNames:
600 if thrName
is None or thrName
in self.
l1menu.thresholds:
603 if threshold
is None:
604 log.error(
'Threshold %s is listed in menu but not defined', thrName )
605 list_of_undefined_thresholds += [ thrName ]
607 self.
l1menu.addThreshold( threshold )
609 zbThrName = connDef[
"zeroBias"]
612 log.error(
'Zero bias threshold %s is listed in menu but not defined', zbThrName )
613 list_of_undefined_thresholds += [ zbThrName ]
615 self.
l1menu.addThreshold( zbThr )
619 if len(list_of_undefined_thresholds)>0:
620 raise RuntimeError(
"Found undefined threshold in menu %s, please add these thresholds to l1menu/ThresholdDef.py: %s" % \
621 (self.
l1menu.menuName,
', '.
join(list_of_undefined_thresholds)) )
630 ctpIdMap = L1MenuFlags.CtpIdMap()
631 for itemName
in L1MenuFlags.items():
633 if registeredItem
is None:
634 msg =
"L1 item '%s' has not been defined in L1/Config/ItemDef.py" % itemName
636 raise RuntimeError(msg)
638 if itemName
in ctpIdMap:
639 newCTPID = ctpIdMap[itemName]
640 registeredItem.setCtpid(newCTPID)
642 for thrName
in registeredItem.thresholdNames():
643 if thrName
not in self.
l1menu.thresholds:
644 isLegacyThr = any(
filter(
lambda x: thrName.startswith(x), [
"R2TOPO_",
"EM",
"HA",
"J",
"XE",
"TE",
"XS"]))
646 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])
648 raise RuntimeError(msg)
650 itemsForMenu += [ registeredItem ]
655 assigned_ctpids =
set([item.ctpid
for item
in itemsForMenu])
656 available_ctpids =
sorted(
list(
set(
range(Limits.MaxTrigItems)) - assigned_ctpids ), reverse=
True )
659 for item
in itemsForMenu:
661 if not item.name.startswith(
'L1_CALREQ'):
662 item.setTriggerType( item.trigger_type | TT.phys )
665 if len(available_ctpids)==0:
666 raise RuntimeError(
"No more CTP IDs available at L1!!")
667 item.setCtpid( available_ctpids.pop() )
669 self.
l1menu.addItem( item )
675 for (boardName, boardDef)
in allBoards:
676 for connDef
in boardDef[
"connectors"]:
677 self.
l1menu.addConnector( connDef )
683 for (boardName, boardDef)
in allBoards:
684 self.
l1menu.addBoard(boardDef)
689 legacyThresholdsSet =
set()
690 for conn
in self.
l1menu.connectors:
691 if not conn.isLegacy():
693 legacyThresholdsSet.update(conn.triggerThresholds())
694 for item
in self.
l1menu.items:
695 item.markLegacy(legacyThresholdsSet)
707 self.
l1menu.setupCTPMonitoring()
719 if 'pp' in self.
l1menu.menuName:
720 self.
l1menu.checkLegacyThresholds()
724 TopoAlgoDefMultiplicity.checkMultAlgoFWconstraints(self.
l1menu)
727 self.
l1menu.checkCountCTPInputsOutput()
730 self.
l1menu.checkCTPINconnectors()
733 self.
l1menu.checkPerfThresholds()
736 self.
l1menu.checkPtMinToTopo()
739 self.
l1menu.checkL1TopoParams()
742 self.
l1menu.checkItemsHaveInputs()
746 self.
l1menu.checkHFmonitoring()
750 Set the correct mapping of thresholds according to the
751 order it was given in L1MenuFlags.thresholds list. That list
752 is usually defined in the setupMenu function of each menu
754 NIM and CALREQ types are not remapped !!
757 alreadyUsedMappingNumbers = ddict(set)
758 for thr
in self.
l1menu.thresholds:
761 alreadyUsedMappingNumbers[thr.ttype].
add(thr.mapping)
763 nextFreeMapping = ddict(
lambda: 0)
764 for k
in alreadyUsedMappingNumbers:
765 nextFreeMapping[k] = 0
766 for thr
in self.
l1menu.thresholds():
768 while nextFreeMapping[thr.ttype]
in alreadyUsedMappingNumbers[thr.ttype]:
769 nextFreeMapping[thr.ttype] += 1
770 log.debug(
'Setting mapping of threshold %s as %i', thr, nextFreeMapping[thr.ttype])
771 thr.mapping = nextFreeMapping[thr.ttype]
772 nextFreeMapping[thr.ttype] += 1
776 for (it_name, ps)
in L1MenuFlags.prescales().
items():
777 item = self.
l1menu.getItem(it_name)
781 log.warning(
'Cannot find item %s to set the prescale', it_name )
785 self.
l1menu.ctp.addMonCounters()
789 pattern = re.compile(
r'_v\d+|DC14')
790 patternPos = pattern.search(menuName)
792 menuName=menuName[:patternPos.end()]
794 log.info(
'Can\'t find pattern to shorten menu name, either non-existent in name or not implemented.')