ATLAS Offline Software
Loading...
Searching...
No Matches
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'''
4Tests to verify generated menus are valid.
5
6Ported from TrigConfStorage/ConfigurationCheck.cxx and
7HLT/Config/Utility/HLTMenuConfig.py, see [ATR-19830].
8
9Designed to be used by the `verify_menu_config.py` script.
10'''
11
12import re
13from enum import Enum
14from collections import Counter
15
16from TriggerMenuMT.HLT.Menu.SignatureDicts import getListOfSignatureStrings
17
18from AthenaCommon.Logging import logging
19log = logging.getLogger( 'TriggerMenuConfigTest' )
20log.info("Importing %s", __name__)
21
22class TriggerLevel(Enum):
23 HLT = "HLT"
24 L1 = "L1"
25
26
27class MenuVerification(object):
28 def __init__(self, description):
29 self.description = description
30 self.failures = []
31
32 def run(self, config):
33 raise NotImplementedError("ConfigVerification subclass must implement run()")
34
35
37 def __init__(self):
38 super(UniqueChainNames, self).__init__(
39 description="Chain names are unique")
40
41 def run(self, config):
42 names = config["chains"].keys()
43 counts = Counter(names)
44 self.failures = [chain for chain, count
45 in counts.items() if count > 1]
46
47
49 def __init__(self):
50 super(ConsecutiveChainCounters, self).__init__(
51 description="Chain counters are consecutive 1..N")
52
53 def run(self, config):
54 counters = [chain["counter"] for chain
55 in config["chains"].values()]
56 prev_counter = 0
57 for count in counters:
58 if count != prev_counter + 1:
59 self.failures.append(count)
60 prev_counter = count
61
62
64 '''
65 Verifies that chain names start with the expected prefix, and have their
66 parts in the correct order by type, as well as any trigger level specific
67 restrictions.
68 '''
69
70 _SIGNATURE_TYPE_ORDER = {
71 TriggerLevel.HLT: getListOfSignatureStrings(),
72 # TODO: import list of signatures items from L1 code
73 TriggerLevel.L1: [
74 "EM", "MU", "TAU", "J", "XE", "HT"
75 ],
76 }
77
78 def __init__(self, trigger_level):
79 super(StructuredChainNames, self).__init__(
80 description="Chain names are structured in the expected way")
81 self._trigger_level = trigger_level
82 self._allowed_prefixes = [trigger_level.value]
83 if trigger_level == TriggerLevel.HLT:
84 self._allowed_prefixes.append("EF")
86 self._SIGNATURE_TYPE_ORDER[trigger_level]
87
88 def run(self, config):
89 if self._trigger_level == TriggerLevel.HLT:
90 names = config["chains"].keys()
91 self.failures = [n for n in names
92 if not self._name_matches_hlt_convention(n)]
93 elif self._trigger_level == TriggerLevel.L1:
94 names = [item["name"] for item in config["items"].values()]
95 self.failures = [n for n in names
96 if not self._name_matches_l1_convention(n)]
97
99 return "_L1" in name and self._matches_shared_conventions(name)
100
102 def FEX_items_in_order(name):
103 '''
104 Where multiple FEX algorithms are used for the same item type,
105 they should be in alphabetical order.
106 '''
107 fex_pattern = r"\d*([egj])({})[A-Z]*\d+s?".format(
108 "|".join(self._signature_type_order))
109 fex_items = re.findall(fex_pattern, name)
110
111 for item_type in self._signature_type_order:
112 fexes_of_type = [fex for fex, match_type in fex_items
113 if match_type == item_type]
114 if not fexes_of_type == sorted(fexes_of_type):
115 return False
116 return True
117
118 return self._matches_shared_conventions(name) and FEX_items_in_order(name)
119
121 '''
122 True if name starts with level prefix, and all signature
123 types are in the correct order, otherwise False.
124 '''
125 # The signature objects in each item should be ordered by type, in the
126 # order defined in _SIGNATURE_TYPE_ORDER.
127 signature_types = "|".join(self._signature_type_order)
128 sig_type_pattern = re.compile(
129 r"_\d*[egj]?({})\d+s?".format(signature_types))
130
131# this is commented because needs to be discussed (it will be removed/changed in next dev)
132 # re to find the signature that has the probe leg
133 #sig_probe_pattern = re.compile(r"_\d*?({})\d+s?[\D]*?_probe".format(signature_types))
134
135
136 def items_in_order(part):
137 #part = part.replace("leg","p") #if we leave the word leg, the findall(..) function will find a 'g'
138 indices = [self._signature_type_order.index(x) for x in
139 sig_type_pattern.findall(part)]
140
141# this is commented because needs to be discussed (it will be removed/changed in next dev)
142 # this finds the signatures with the probe leg
143 # matches = sig_type_pattern.findall(part)
144 # matches_probe = sig_probe_pattern.findall(part)
145 # if len(matches_probe):
146 # assert(len(matches_probe)==1)
147 # probe_leg = matches_probe[0]
148 # matches_after_probe = matches.copy()
149 # # force the probe leg to be the last one
150 # if probe_leg in matches_after_probe:
151 # # Remove the element
152 # matches_after_probe.remove(probe_leg)
153 # # Append it to the end
154 # matches_after_probe.append(probe_leg)
155 # indices_after_probe = [self._signature_type_order.index(x) for x in matches_after_probe]
156 # # copy the new indices
157 # #indices = indices_after_probe.copy()
158
159
160 rr = indices == sorted(indices)
161 if not rr:
162 log.error("[StructuredChainNames::items_in_order] %s NOT SORTED!", indices)
163
164 return rr
165
166 def are_signatures_in_order(name_parts):
167
168 to_check = ["".join(f"_{p}" for p in name_parts if "-" not in p)]
169
170 # Sections of topo item parts are checked for ordering independently.
171 topo_parts = [p for p in name_parts if "-" in p]
172 for topo in topo_parts:
173 to_check.extend(topo.split("-"))
174 res = all(items_in_order(part) for part in to_check)
175 if not res:
176 for part in to_check:
177 if not items_in_order(part):
178 log.error("[StructuredChainNames::are_signatures_in_order] %s not in order!", part)
179 return res
180
181 # Name must begin with the trigger level, and contain at least one item.
182 parts = name.split("_")
183
184 result= all((len(parts) > 1,
185 parts[0] in self._allowed_prefixes,
186 are_signatures_in_order(parts[1:])))
187 if not result:
188 log.error("[StructuredChainNames::_matches_shared_conventions] chain deosn't match convention: parts[0] = %s, value = %s, parts[1:] = %s, signature_types = %s",
189 parts[0], self._trigger_level.value, parts[1:], signature_types)
190
191 return result
192
193
195 def __init__(self):
196 super(RestrictedCTPIDs, self).__init__(
197 description="Less than 512 CTP items, and no CTP id greater than 512")
198
199 def run(self, config):
200 ctp_ids = {name: item["ctpid"] for
201 name, item in config["items"].items()}
202 if len(ctp_ids) > 512:
203 self.failures.append(
204 "More than 512 CTP items defined")
205 over_max_ids = [name for name, ctp_id in ctp_ids.items()
206 if ctp_id > 512]
207 self.failures.extend(over_max_ids)
208
209
211 def __init__(self):
212 super(PartialEventBuildingChecks, self).__init__(
213 description='Config consistency of Partial Event Building')
214
215 def run(self, config):
216 from TriggerMenuMT.HLT.Menu import EventBuildingInfo
217 eb_identifiers = EventBuildingInfo.getAllEventBuildingIdentifiers()
218
219 for chain_name, chain_config in config['chains'].items():
220 peb_identifiers = [idf for idf in eb_identifiers if '_'+idf+'_' in chain_name]
221 peb_writers = [seq for seq in chain_config['sequencers'] if 'PEBInfoWriter' in seq]
222
223 is_peb_chain = (len(peb_identifiers) > 0 or len(peb_writers) > 0)
224
225 # Check streaming configuration
226 for stream_name in chain_config['streams']:
227 if stream_name not in config['streams']:
228 self.failures.append(
229 'Stream {:s} for chain {:s} is not defined in streaming configuration'.format(
230 stream_name, chain_name))
231
232 is_feb_stream = config['streams'][stream_name]['forceFullEventBuilding']
233
234 if is_peb_chain and is_feb_stream:
235 self.failures.append(
236 'PEB chain {:s} streamed to a full-event-building stream {:s} '
237 '(forceFullEventBuilding=True)'.format(
238 chain_name, stream_name))
239
240 elif not is_peb_chain and not is_feb_stream:
241 self.failures.append(
242 'Full-event-building chain {:s} streamed to the stream {:s} which allows partial '
243 'event building (forceFullEventBuilding=False)'.format(
244 chain_name, stream_name))
245
246 if not is_peb_chain:
247 # Not a PEB chain, skip further PEB-specific checks
248 continue
249
250 if len(peb_identifiers) != 1:
251 self.failures.append(
252 '{:s} has {:d} event building identifiers'.format(chain_name, len(peb_identifiers)))
253
254 if len(peb_writers) != 1:
255 self.failures.append(
256 '{:s} has {:d} PEBInfoWriter sequences'.format(chain_name, len(peb_writers)))
257
258 if peb_identifiers and peb_writers and not peb_writers[0].endswith(peb_identifiers[0]):
259 self.failures.append(
260 '{:s} PEB sequence name {:s} doesn\'t end with PEB identifier {:s}'.format(
261 chain_name, peb_writers[0], peb_identifiers[0]))
262
263
264
265menu_tests = {
266 TriggerLevel.HLT: [
269 StructuredChainNames(TriggerLevel.HLT),
271 ],
272 TriggerLevel.L1: [
274 StructuredChainNames(TriggerLevel.L1),
275 ]
276}
Definition index.py:1