ATLAS Offline Software
menu_config_tests.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 '''
4 Tests to verify generated menus are valid.
5 
6 Ported from TrigConfStorage/ConfigurationCheck.cxx and
7 HLT/Config/Utility/HLTMenuConfig.py, see [ATR-19830].
8 
9 Designed to be used by the `verify_menu_config.py` script.
10 '''
11 
12 import re
13 from enum import Enum
14 from collections import Counter
15 
16 from AthenaCommon.Logging import logging
17 log = logging.getLogger( 'TriggerMenuConfigTest' )
18 log.info("Importing %s", __name__)
19 
20 class TriggerLevel(Enum):
21  HLT = "HLT"
22  L1 = "L1"
23 
24 
26  def __init__(self, description):
27  self.description = description
28  self.failures = []
29 
30  def run(self, config):
31  raise NotImplementedError("ConfigVerification subclass must implement run()")
32 
33 
35  def __init__(self):
36  super(UniqueChainNames, self).__init__(
37  description="Chain names are unique")
38 
39  def run(self, config):
40  names = config["chains"].keys()
41  counts = Counter(names)
42  self.failures = [chain for chain, count
43  in counts.items() if count > 1]
44 
45 
47  def __init__(self):
48  super(ConsecutiveChainCounters, self).__init__(
49  description="Chain counters are consecutive 1..N")
50 
51  def run(self, config):
52  counters = [chain["counter"] for chain
53  in config["chains"].values()]
54  prev_counter = 0
55  for count in counters:
56  if count != prev_counter + 1:
57  self.failures.append(count)
58  prev_counter = count
59 
60 
62  '''
63  Verifies that chain names start with the expected prefix, and have their
64  parts in the correct order by type, as well as any trigger level specific
65  restrictions.
66  '''
67 
68  _SIGNATURE_TYPE_ORDER = {
69  TriggerLevel.HLT: [
70  "e", "g", "mu", "tau", "j", "xe", "ht", "isotrk", "fslrt", "dedxtrk", "hitdvjet", "fsvsi", "distrk", "dispjet"
71  ],
72  TriggerLevel.L1: [
73  "EM", "MU", "TAU", "J", "XE", "HT"
74  ],
75  }
76 
77  def __init__(self, trigger_level):
78  super(StructuredChainNames, self).__init__(
79  description="Chain names are structured in the expected way")
80  self._trigger_level = trigger_level
82  self._SIGNATURE_TYPE_ORDER[trigger_level]
83 
84  def run(self, config):
85  if self._trigger_level == TriggerLevel.HLT:
86  names = config["chains"].keys()
87  self.failures = [n for n in names
88  if not self._name_matches_hlt_convention(n)]
89  elif self._trigger_level == TriggerLevel.L1:
90  names = [item["name"] for item in config["items"].values()]
91  self.failures = [n for n in names
92  if not self._name_matches_l1_convention(n)]
93 
95  return "_L1" in name and self._matches_shared_conventions(name)
96 
97  def _name_matches_l1_convention(self, name):
98  def FEX_items_in_order(name):
99  '''
100  Where multiple FEX algorithms are used for the same item type,
101  they should be in alphabetical order.
102  '''
103  fex_pattern = r"\d*([egj])({})[A-Z]*\d+s?".format(
104  "|".join(self._signature_type_order))
105  fex_items = re.findall(fex_pattern, name)
106 
107  for item_type in self._signature_type_order:
108  fexes_of_type = [fex for fex, match_type in fex_items
109  if match_type == item_type]
110  if not fexes_of_type == sorted(fexes_of_type):
111  return False
112  return True
113 
114  return self._matches_shared_conventions(name) and FEX_items_in_order(name)
115 
117  '''
118  True if name starts with level prefix, and all signature
119  types are in the correct order, otherwise False.
120  '''
121  # The signature objects in each item should be ordered by type, in the
122  # order defined in _SIGNATURE_TYPE_ORDER.
123  signature_types = "|".join(self._signature_type_order)
124  sig_type_pattern = re.compile(
125  r"_\d*[egj]?({})\d+s?".format(signature_types))
126  def items_in_order(part):
127  #part = part.replace("leg","p") #if we leave the word leg, the findall(..) function will find a 'g'
128  indices = [self._signature_type_order.index(x) for x in
129  sig_type_pattern.findall(part)]
130  rr = indices == sorted(indices)
131  if not rr:
132  log.error("[StructuredChainNames::items_in_order] %s NOT SORTED!", indices)
133 
134  return rr
135 
136  def are_signatures_in_order(name_parts):
137  to_check = ["_".join(p for p in name_parts if "-" not in p)]
138  # Sections of topo item parts are checked for ordering independently.
139  topo_parts = [p for p in name_parts if "-" in p]
140  for topo in topo_parts:
141  to_check.extend(topo.split("-"))
142  res = all(items_in_order(part) for part in to_check)
143  if not res:
144  for part in to_check:
145  if not items_in_order(part):
146  log.error("[StructuredChainNames::are_signatures_in_order] %s not in order!", part)
147  return res
148 
149  # Name must begin with the trigger level, and contain at least one item.
150  parts = name.split("_")
151 
152  result= all((len(parts) > 1,
153  parts[0] == self._trigger_level.value,
154  are_signatures_in_order(parts[1:])))
155  if not result:
156  log.error("[StructuredChainNames::_matches_shared_conventions] chain deosn't match convention: parts[0] = %s, value = %s, parts[1:] = %s, signature_types = %s",
157  parts[0], self._trigger_level.value, parts[1:], signature_types)
158  return result
159 
160 
162  def __init__(self):
163  super(RestrictedCTPIDs, self).__init__(
164  description="Less than 512 CTP items, and no CTP id greater than 512")
165 
166  def run(self, config):
167  ctp_ids = {name: item["ctpid"] for
168  name, item in config["items"].items()}
169  if len(ctp_ids) > 512:
170  self.failures.append(
171  "More than 512 CTP items defined")
172  over_max_ids = [name for name, ctp_id in ctp_ids.items()
173  if ctp_id > 512]
174  self.failures.extend(over_max_ids)
175 
176 
178  def __init__(self):
179  super(PartialEventBuildingChecks, self).__init__(
180  description='Config consistency of Partial Event Building')
181 
182  def run(self, config):
183  from TriggerMenuMT.HLT.Menu import EventBuildingInfo
184  eb_identifiers = EventBuildingInfo.getAllEventBuildingIdentifiers()
185 
186  for chain_name, chain_config in config['chains'].items():
187  peb_identifiers = [idf for idf in eb_identifiers if '_'+idf+'_' in chain_name]
188  peb_writers = [seq for seq in chain_config['sequencers'] if 'PEBInfoWriter' in seq]
189 
190  is_peb_chain = (len(peb_identifiers) > 0 or len(peb_writers) > 0)
191 
192  # Check streaming configuration
193  for stream_name in chain_config['streams']:
194  if stream_name not in config['streams']:
195  self.failures.append(
196  'Stream {:s} for chain {:s} is not defined in streaming configuration'.format(
197  stream_name, chain_name))
198 
199  is_feb_stream = config['streams'][stream_name]['forceFullEventBuilding']
200 
201  if is_peb_chain and is_feb_stream:
202  self.failures.append(
203  'PEB chain {:s} streamed to a full-event-building stream {:s} '
204  '(forceFullEventBuilding=True)'.format(
205  chain_name, stream_name))
206 
207  elif not is_peb_chain and not is_feb_stream:
208  self.failures.append(
209  'Full-event-building chain {:s} streamed to the stream {:s} which allows partial '
210  'event building (forceFullEventBuilding=False)'.format(
211  chain_name, stream_name))
212 
213  if not is_peb_chain:
214  # Not a PEB chain, skip further PEB-specific checks
215  continue
216 
217  if len(peb_identifiers) != 1:
218  self.failures.append(
219  '{:s} has {:d} event building identifiers'.format(chain_name, len(peb_identifiers)))
220 
221  if len(peb_writers) != 1:
222  self.failures.append(
223  '{:s} has {:d} PEBInfoWriter sequences'.format(chain_name, len(peb_writers)))
224 
225  if peb_identifiers and peb_writers and not peb_writers[0].endswith(peb_identifiers[0]):
226  self.failures.append(
227  '{:s} PEB sequence name {:s} doesn\'t end with PEB identifier {:s}'.format(
228  chain_name, peb_writers[0], peb_identifiers[0]))
229 
230 
231 
232 menu_tests = {
233  TriggerLevel.HLT: [
236  StructuredChainNames(TriggerLevel.HLT),
238  ],
239  TriggerLevel.L1: [
241  StructuredChainNames(TriggerLevel.L1),
242  ]
243 }
menu_config_tests.PartialEventBuildingChecks.run
def run(self, config)
Definition: menu_config_tests.py:182
vtune_athena.format
format
Definition: vtune_athena.py:14
menu_config_tests.MenuVerification.description
description
Definition: menu_config_tests.py:27
index
Definition: index.py:1
menu_config_tests.MenuVerification.failures
failures
Definition: menu_config_tests.py:28
menu_config_tests.PartialEventBuildingChecks.__init__
def __init__(self)
Definition: menu_config_tests.py:178
menu_config_tests.RestrictedCTPIDs.__init__
def __init__(self)
Definition: menu_config_tests.py:162
menu_config_tests.UniqueChainNames.__init__
def __init__(self)
Definition: menu_config_tests.py:35
menu_config_tests.TriggerLevel
Definition: menu_config_tests.py:20
menu_config_tests.PartialEventBuildingChecks
Definition: menu_config_tests.py:177
menu_config_tests.StructuredChainNames.__init__
def __init__(self, trigger_level)
Definition: menu_config_tests.py:77
menu_config_tests.MenuVerification
Definition: menu_config_tests.py:25
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
menu_config_tests.MenuVerification.run
def run(self, config)
Definition: menu_config_tests.py:30
menu_config_tests.MenuVerification.__init__
def __init__(self, description)
Definition: menu_config_tests.py:26
python.Bindings.values
values
Definition: Control/AthenaPython/python/Bindings.py:797
menu_config_tests.StructuredChainNames._trigger_level
_trigger_level
Definition: menu_config_tests.py:80
menu_config_tests.RestrictedCTPIDs
Definition: menu_config_tests.py:161
menu_config_tests.UniqueChainNames
Definition: menu_config_tests.py:34
menu_config_tests.StructuredChainNames._matches_shared_conventions
def _matches_shared_conventions(self, name)
Definition: menu_config_tests.py:116
menu_config_tests.UniqueChainNames.run
def run(self, config)
Definition: menu_config_tests.py:39
menu_config_tests.StructuredChainNames._name_matches_l1_convention
def _name_matches_l1_convention(self, name)
Definition: menu_config_tests.py:97
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename T::value_type > sorted(T begin, T end)
Helper function to create a sorted vector from an unsorted one.
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
menu_config_tests.ConsecutiveChainCounters.__init__
def __init__(self)
Definition: menu_config_tests.py:47
TrigJetMonitorAlgorithm.items
items
Definition: TrigJetMonitorAlgorithm.py:79
menu_config_tests.StructuredChainNames
Definition: menu_config_tests.py:61
Cut::all
@ all
Definition: SUSYToolsAlg.cxx:64
menu_config_tests.ConsecutiveChainCounters.run
def run(self, config)
Definition: menu_config_tests.py:51
menu_config_tests.StructuredChainNames._signature_type_order
_signature_type_order
Definition: menu_config_tests.py:81
menu_config_tests.StructuredChainNames.run
def run(self, config)
Definition: menu_config_tests.py:84
pickleTool.object
object
Definition: pickleTool.py:30
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:790
menu_config_tests.ConsecutiveChainCounters
Definition: menu_config_tests.py:46
menu_config_tests.StructuredChainNames._SIGNATURE_TYPE_ORDER
_SIGNATURE_TYPE_ORDER
Definition: menu_config_tests.py:68
menu_config_tests.StructuredChainNames._name_matches_hlt_convention
def _name_matches_hlt_convention(self, name)
Definition: menu_config_tests.py:94
menu_config_tests.RestrictedCTPIDs.run
def run(self, config)
Definition: menu_config_tests.py:166