ATLAS Offline Software
Loading...
Searching...
No Matches
TrigConfigSvcCfg.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2
3from typing import Any, cast
4from AthenaCommon.Logging import logging
5from AthenaConfiguration.ComponentFactory import CompFactory
6from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
7from AthenaConfiguration.AccumulatorCache import AccumulatorCache
8from TrigConfStorage.TriggerCrestUtil import TriggerCrestUtil
9
10from functools import cache
11import json
12
13log = logging.getLogger('TrigConfigSvcCfg')
14
15# To avoid accidental overrwrite of the L1 JSON menu, only allow one L1 menu generation.
16# Either via JSON conversion from Run-1&2 or native Run-3 (see ATR-24531).
18 try:
19 return l1menu_generated._hasRun # type: ignore
20 except AttributeError:
21 l1menu_generated._hasRun = True # type: ignore
22 return False
23
24
25def getTrigConfFromConditions(runNumber, lumiBlock, flags) -> dict[str, int | str]:
26 if flags.Trigger.useCrest:
27 return getTrigConfFromCrest(runNumber, lumiBlock, flags.Trigger.crestServer)
28 else:
29 return getTrigConfFromCool(runNumber, lumiBlock)
30
31@cache
32def getTrigConfFromCrest(runNumber, lumiBlock, crestServer) -> dict[str, int | str]:
33 trigConf = TriggerCrestUtil.getTrigConfKeys(runNumber, lumiBlock, server=crestServer)
34 log.info("Extracted the following info for run %d and lumi block %d from CREST: %r",
35 runNumber, lumiBlock, trigConf)
36 for key, value in trigConf.items():
37 if value is None:
38 msg: str = f"Did not find {key} for run {runNumber} and lumi block {lumiBlock}"
39 log.error(msg)
40 raise RuntimeError(msg)
41 return trigConf
42
43@cache
44def getTrigConfFromCool(runNumber, lumiBlock) -> dict[str, int | str]:
45 from TrigConfStorage.TriggerCoolUtil import TriggerCoolUtil
46 trigConf = TriggerCoolUtil.getTrigConfKeys(runNumber, lumiBlock)
47 log.info("Extracted the following info for run %d and lumi block %d from COOL: %r",
48 runNumber, lumiBlock, trigConf)
49 for key, value in trigConf.items():
50 if value is None:
51 msg: str = f"Did not find {key} for run {runNumber} and lumi block {lumiBlock}"
52 log.error(msg)
53 raise RuntimeError(msg)
54 return trigConf
55
56def createJsonMenuFiles(run, lb, flags):
57 crestServer: str | None = flags.Trigger.crestServer if flags.Trigger.useCrest else None
58 return _createJsonMenuFiles(run, lb, crestServer)
59
60@cache
61def _createJsonMenuFiles(run, lb, crestServer: str | None = None) -> dict[str, int | str]:
62 """Retrieve Run-2 trigger configuration from the DB and save as Run3 .JSON files
63 returns the trigger DB keys used, or None if the L1 menu has already been generated
64 """
65 import subprocess
66
68 log.error("L1 menu has already been generated")
69 return None # type: ignore
70
71 log.info("Configuring Run-1&2 to Run-3 configuration metadata conversion")
72 if crestServer is not None:
73 triggerDBKeys = getTrigConfFromCrest(run, lb, crestServer)
74 else:
75 triggerDBKeys = getTrigConfFromCool(run, lb)
76 triggerDBKeys['DB'] = 'TRIGGERDB' if run > 230000 else 'TRIGGERDB_RUN1'
77
78 cmd = "TrigConfReadWrite -i {DB} {SMK},{LVL1PSK},{HLTPSK},{BGSK} -o r3json > Run3ConfigFetchJSONFiles.log".format(**triggerDBKeys)
79 log.info("Running command '%s'", cmd)
80 filesFetchStatus = subprocess.run(cmd, shell=True)
81 assert filesFetchStatus.returncode == 0, "TrigConfReadWrite failed to fetch JSON files"
82 return triggerDBKeys
83
84
85# This interprets the Trigger.triggerConfig flag according to
86# https://twiki.cern.ch/twiki/bin/view/Atlas/TriggerConfigFlag#triggerConfig_in_Run_3
88 # run and lb are only needed if source is DB
89 run: int = flags.Input.RunNumbers[0] if flags.Input.RunNumbers else -1
90 lb: int = flags.Input.LumiBlockNumbers[0] if flags.Input.LumiBlockNumbers else 0
91 return _getTrigConfigFromFlag(triggerConfig=flags.Trigger.triggerConfig, run=run, lb=lb,
92 useCrest=flags.Trigger.useCrest, crestServer=flags.Trigger.crestServer)
93
94@cache
95def _getTrigConfigFromFlag(*, triggerConfig, run, lb, useCrest, crestServer) -> dict[str, Any]:
96 log.info("Parsing trigger configuration from flag Trigger.triggerConfig='%s' for run=%d and lb=%d",
97 triggerConfig, run, lb)
98 log.info("Crest usage flags are: Trigger.useCrest=%s, Trigger.crestServer=%s", useCrest, crestServer)
99 # Pad the triggerConfig value and extract available fields:
100 dbconn: str
101 source, dbconn, keys = (triggerConfig+":::").split(":")[:3]
102 smk,l1psk,hltpsk,bgsk = (keys+",,,").split(",")[:4]
103 # Convert to int or None:
104 smk, l1psk, hltpsk, bgsk = (int(k) if k!="" else None for k in (smk, l1psk, hltpsk, bgsk))
105 source: str = source.upper()
106
107 if source == "DB":
108 if run < 0:
109 msg: str = "Run number is required to extract trigger conditions"
110 log.error(msg)
111 raise RuntimeError(msg)
112 # If any of the keys or DB connection is missing, retrieve from conditions:
113 if useCrest:
114 trigConf: dict[str, int | str] = getTrigConfFromCrest(run, lb, crestServer)
115 else:
116 trigConf: dict[str, int | str] = getTrigConfFromCool(run, lb)
117 if dbconn == "":
118 dbconn = cast(str, trigConf["DB"])
119
120 if dbconn in ["TRIGGERDB_RUN3", "TRIGGERDBDEV1_I8", "TRIGGERDBDEV1", "TRIGGERDBDEV2"]:
121 if smk is None:
122 smk = trigConf["SMK"]
123 if l1psk is None:
124 l1psk = trigConf['LVL1PSK']
125 if hltpsk is None:
126 hltpsk = trigConf['HLTPSK']
127 if bgsk is None:
128 bgsk = trigConf['BGSK']
129
130 if useCrest:
131 # need to modify the DB connection alias (e.g. TRIGGERDB_RUN3) to the corresponding CREST server URL
132 crestConn = TriggerCrestUtil.getCrestConnection(dbconn)
133 if crestConn is None:
134 msg: str = f"Could not find CREST triggerdb connection from DB connection alias '{dbconn}'"
135 log.error(msg)
136 raise RuntimeError(msg)
137 dbconn = f"{crestServer}/{crestConn}"
138
139 tcdict = {
140 "SOURCE" : source, # DB, FILE, COOL
141 "DBCONN" : dbconn, # db connection (if origin==DB or COOL) or "JOSVC" if connection is to be taken from TrigConf::IJobOptionsSvc
142 "SMK" : smk,
143 "LVL1PSK": l1psk,
144 "HLTPSK" : hltpsk,
145 "BGSK" : bgsk
146 }
147 return tcdict
148
149
151 return "/TRIGGER/LVL1/Lvl1ConfigKey <tag>HEAD</tag>"
152
153
155 return "/TRIGGER/HLT/PrescaleKey <tag>HEAD</tag>"
156
157
159 """Do JSON menu conversion for Run-1&2 data"""
160 return flags.Input.Files and flags.Trigger.EDMVersion in [1, 2] and not flags.Input.isMC
161
162
164 """Return base name for menu files"""
165 if not _doMenuConversion(flags): # menu created in this release
166 from PyUtils.Helpers import release_metadata
167 return '_'+flags.Trigger.triggerMenuSetup+'_'+release_metadata()['release']
168 else: # menu files created via JSON conversion
169 return ''
170
171# L1 Json file name
173 return 'L1Menu'+_getMenuFileName(flags)+'.json'
174
175# HLT Json file name
177 return 'HLTMenu'+_getMenuFileName(flags)+'.json'
178
179# HLT Monitoring set json file name
181 return 'HLTMonitoring'+_getMenuFileName(flags)+'.json'
182
183# L1 Prescales set json file name
185 return 'L1PrescalesSet'+_getMenuFileName(flags)+'.json'
186
187# HLT Prescales set json file name
189 return 'HLTPrescalesSet'+_getMenuFileName(flags)+'.json'
190
191# L1 Bunchgroups set json file name
193 return 'BunchGroupSet'+_getMenuFileName(flags)+'.json'
194
195# HLT Job options json file name
197 return 'HLTJobOptions.json'
198
199# Creates an L1 Prescale file from the menu
200def createL1PrescalesFileFromMenu(flags, prescales: dict[str, float] | None = None):
201 from TriggerMenuMT.L1.Base.PrescaleHelper import getCutFromPrescale
202
203 menuFN = getL1MenuFileName(flags)
204 with open(menuFN,'r') as fh:
205 data = json.load(fh)
206 pso = { 'filetype': 'l1prescale',
207 'name': data['name'],
208 'cutValues': {} }
209 ps = pso['cutValues']
210 for name in sorted(data['items'].keys()):
211 ps = prescales[name] if prescales and name in prescales else 1
212 pso['cutValues'][name] = {
213 'cut': getCutFromPrescale(ps),
214 'enabled': ps > 0,
215 'info': f'prescale: {ps}'
216 }
217
218 psFN = getL1PrescalesSetFileName( flags )
219 with open(psFN, 'w') as outfile:
220 json.dump(pso, outfile, indent = 4)
221 log.info("Generated default L1 prescale set %s", outfile.name)
222
223# L1 menu generation
224def generateL1Menu( flags ):
225 if l1menu_generated():
226 log.error("L1 menu has already been generated")
227 return
228
229 log.info("Generating L1 menu %s", flags.Trigger.triggerMenuSetup)
230 from TriggerMenuMT.L1.L1MenuConfig import L1MenuConfig
231 l1cfg = L1MenuConfig(flags)
232 l1cfg.writeJSON(outputFile = getL1MenuFileName(flags),
233 bgsOutputFile = getBunchGroupSetFileName(flags))
234
235
236# provide L1 config service in new JO
237@AccumulatorCache
238def L1ConfigSvcCfg( flags ):
239 log.info( "Setting up LVL1ConfigSvc" )
240 acc = ComponentAccumulator()
241
242 cfg = getTrigConfigFromFlag( flags )
243
244 # configure config svc
245 l1ConfigSvc = CompFactory.getComp("TrigConf::LVL1ConfigSvc")("LVL1ConfigSvc") # type: ignore
246
247 if cfg["SOURCE"] == "FILE":
248 if _doMenuConversion(flags):
249 # Save the menu in JSON format
250 dbKeys = createJsonMenuFiles(run = flags.Input.RunNumbers[0],
251 lb = flags.Input.LumiBlockNumbers[0], flags=flags)
252 l1ConfigSvc.SMK = dbKeys['SMK']
253
254 l1ConfigSvc.InputType = "FILE"
255 l1ConfigSvc.L1JsonFileName = getL1MenuFileName(flags)
256 l1ConfigSvc.HLTJsonFileName = getHLTMenuFileName(flags)
257 log.info( "Configured LVL1ConfigSvc with InputType='FILE', L1JsonFileName=%s (and HLT, used to compute SMK:%s) ", l1ConfigSvc.L1JsonFileName, l1ConfigSvc.HLTJsonFileName )
258 elif cfg["SOURCE"] == "DB":
259 l1ConfigSvc.InputType = "DB"
260 l1ConfigSvc.L1JsonFileName = ""
261 l1ConfigSvc.HLTJsonFileName = ""
262 l1ConfigSvc.TriggerDB = cfg["DBCONN"]
263 l1ConfigSvc.SMK = cfg["SMK"]
264 log.info( "Configured LVL1ConfigSvc with InputType='DB', TriggerDB='%s' and SMK %d", l1ConfigSvc.TriggerDB, cfg['SMK'] )
265
266 acc.addService( l1ConfigSvc, create=True )
267 return acc
268
269# provide HLT config service in new JO
270@AccumulatorCache
271def HLTConfigSvcCfg( flags ):
272 log.info( "Setting up HLTConfigSvc" )
273 acc = ComponentAccumulator()
274 cfg = getTrigConfigFromFlag( flags )
275
276 hltConfigSvc = CompFactory.getComp("TrigConf::HLTConfigSvc")("HLTConfigSvc") # type: ignore
277
278 if cfg["SOURCE"] == "FILE":
279 if _doMenuConversion(flags):
280 # Save the menu in JSON format
281 dbKeys = createJsonMenuFiles(run = flags.Input.RunNumbers[0],
282 lb = flags.Input.LumiBlockNumbers[0], flags=flags)
283 hltConfigSvc.SMK = dbKeys['SMK']
284
285 hltConfigSvc.InputType = "FILE"
286 hltConfigSvc.L1JsonFileName = getL1MenuFileName( flags )
287 hltConfigSvc.HLTJsonFileName = getHLTMenuFileName( flags )
288 hltConfigSvc.MonitoringJsonFileName = getHLTMonitoringFileName( flags )
289 log.info("Configured HLTConfigSvc with InputType='FILE', HLTJsonFileName=%s and MonitoringJsonFileName=%s (and L1, used to compute MC-SMK:%s)",
290 hltConfigSvc.HLTJsonFileName, hltConfigSvc.MonitoringJsonFileName, hltConfigSvc.L1JsonFileName)
291 elif cfg["SOURCE"] == "DB":
292 hltConfigSvc.InputType = "DB"
293 hltConfigSvc.L1JsonFileName = ""
294 hltConfigSvc.HLTJsonFileName = ""
295 hltConfigSvc.MonitoringJsonFileName = ""
296 hltConfigSvc.TriggerDB = cfg["DBCONN"]
297 hltConfigSvc.SMK = cfg["SMK"]
298 log.info("Configured HLTConfigSvc with InputType='DB', TriggerDB='%s' and SMK %d", hltConfigSvc.TriggerDB, cfg['SMK'])
299 acc.addService( hltConfigSvc, create=True )
300 return acc
301
302# provide both services in new JO
303def TrigConfigSvcCfg( flags ):
304 acc = ComponentAccumulator()
305 acc.merge( BunchGroupCondAlgCfg( flags ) )
306 acc.merge( L1ConfigSvcCfg( flags ) )
307 acc.merge( HLTConfigSvcCfg( flags ) )
308 acc.merge( L1PrescaleCondAlgCfg( flags ) )
309 acc.merge( HLTPrescaleCondAlgCfg( flags ) )
310 return acc
311
312@AccumulatorCache
314 log.info("Setting up L1PrescaleCondAlg")
315 acc = ComponentAccumulator()
316 TrigConf__L1PrescaleCondAlg = CompFactory.getComp("TrigConf::L1PrescaleCondAlg")
317 l1PrescaleCondAlg = TrigConf__L1PrescaleCondAlg("L1PrescaleCondAlg") # type: ignore
318
319 tc = getTrigConfigFromFlag( flags )
320 l1PrescaleCondAlg.Source = tc["SOURCE"]
321 if flags.Common.isOnline:
322 from IOVDbSvc.IOVDbSvcConfig import addFolders
323 acc.merge(addFolders(flags, getL1PrescaleFolderName(), "TRIGGER_ONL", className="AthenaAttributeList"))
324 log.info("Adding folder %s to CompAcc", getL1PrescaleFolderName() )
325 if tc["SOURCE"] == "COOL":
326 l1PrescaleCondAlg.TriggerDB = tc["DBCONN"]
327 elif tc["SOURCE"] == "DB":
328 l1PrescaleCondAlg.TriggerDB = tc["DBCONN"]
329 l1PrescaleCondAlg.L1Psk = tc["LVL1PSK"]
330 log.info("Configured L1PrescaleCondAlg with InputType='DB', TriggerDB='%s' and L1Psk %d",
331 l1PrescaleCondAlg.TriggerDB, l1PrescaleCondAlg.L1Psk)
332 elif tc["SOURCE"] == "FILE":
333 l1PrescaleCondAlg.Filename = getL1PrescalesSetFileName( flags )
334 if _doMenuConversion(flags):
335 # Save the menu in JSON format
336 dbKeys = createJsonMenuFiles(run = flags.Input.RunNumbers[0],
337 lb = flags.Input.LumiBlockNumbers[0], flags=flags)
338 l1PrescaleCondAlg.L1Psk = dbKeys['LVL1PSK']
339 else:
340 raise RuntimeError("trigger configuration flag 'trigConfig' starts with %s, which is not understood" % tc["SOURCE"])
341 acc.addCondAlgo(l1PrescaleCondAlg)
342 return acc
343
344@AccumulatorCache
346 log.info("Setting up BunchGroupCondAlg")
347 acc = ComponentAccumulator()
348 TrigConf__BunchGroupCondAlg = CompFactory.getComp("TrigConf::BunchGroupCondAlg")
349 bunchGroupCondAlg = TrigConf__BunchGroupCondAlg("TrigConf__BunchGroupCondAlg") # type: ignore
350
351 tc = getTrigConfigFromFlag( flags )
352 bunchGroupCondAlg.Source = tc["SOURCE"]
353 if tc["SOURCE"] == "COOL":
354 bunchGroupCondAlg.TriggerDB = tc["DBCONN"]
355 elif tc["SOURCE"] == "DB":
356 bunchGroupCondAlg.TriggerDB = tc["DBCONN"]
357 bunchGroupCondAlg.BGSK = tc["BGSK"]
358 log.info("Configured BunchGroupCondAlg with InputType='DB', TriggerDB='%s' and BGSK %d",
359 bunchGroupCondAlg.TriggerDB, bunchGroupCondAlg.BGSK)
360 elif tc["SOURCE"] == "FILE":
361 bunchGroupCondAlg.Filename = getBunchGroupSetFileName( flags )
362 if _doMenuConversion(flags):
363 # Save the menu in JSON format
364 dbKeys = createJsonMenuFiles(run = flags.Input.RunNumbers[0],
365 lb = flags.Input.LumiBlockNumbers[0], flags=flags)
366 bunchGroupCondAlg.BGSK = dbKeys['BGSK']
367 else:
368 raise RuntimeError("trigger configuration flag 'trigConfig' starts with %s, which is not understood" % tc["SOURCE"])
369 acc.addCondAlgo(bunchGroupCondAlg)
370 return acc
371
372@AccumulatorCache
374 log.info("Setting up HLTPrescaleCondAlg")
375 acc = ComponentAccumulator()
376 hltPrescaleCondAlg = CompFactory.getComp("TrigConf::HLTPrescaleCondAlg")("HLTPrescaleCondAlg") # type: ignore
377
378 tc = getTrigConfigFromFlag( flags )
379 hltPrescaleCondAlg.Source = tc["SOURCE"]
380 if flags.Common.isOnline or tc["SOURCE"]=="COOL":
381 from IOVDbSvc.IOVDbSvcConfig import addFolders
382 acc.merge(addFolders(flags, getHLTPrescaleFolderName(), "TRIGGER_ONL",
383 className = "AthenaAttributeList",
384 extensible = flags.Trigger.Online.isPartition))
385 log.info("Adding folder %s to CompAcc", getHLTPrescaleFolderName() )
386 if tc["SOURCE"] == "COOL":
387 hltPrescaleCondAlg.TriggerDB = tc["DBCONN"]
388 elif tc["SOURCE"] == "DB":
389 hltPrescaleCondAlg.TriggerDB = tc["DBCONN"]
390 hltPrescaleCondAlg.HLTPsk = tc["HLTPSK"]
391 log.info("Configured HLTPrescaleCondAlg with InputType='DB', TriggerDB='%s' and HLTPsk %d",
392 hltPrescaleCondAlg.TriggerDB, hltPrescaleCondAlg.HLTPsk)
393 elif tc["SOURCE"] == "FILE":
394 hltPrescaleCondAlg.Filename = getHLTPrescalesSetFileName( flags )
395 if _doMenuConversion(flags):
396 # Save the menu in JSON format
397 dbKeys = createJsonMenuFiles(run = flags.Input.RunNumbers[0],
398 lb = flags.Input.LumiBlockNumbers[0], flags=flags)
399 hltPrescaleCondAlg.HLTPsk = dbKeys['HLTPSK']
400 else:
401 raise RuntimeError("trigger configuration flag 'trigConfig' starts with %s, which is not understood" % tc["SOURCE"])
402 acc.addCondAlgo(hltPrescaleCondAlg)
403 return acc
404
405
406if __name__ == "__main__":
407 import unittest
408
409 class Tests(unittest.TestCase):
410
411 def setUp(self):
412 # Allow multiple L1 menu generations for these tests
413 l1menu_generated._hasRun = False # type: ignore
414
416 from AthenaConfiguration.AllConfigFlags import initConfigFlags
417 flags = initConfigFlags()
418 flags.Trigger.EDMVersion = 3
419 from AthenaConfiguration.TestDefaults import defaultTestFiles
420 flags.Input.Files = defaultTestFiles.RAW_RUN2
421 flags.lock()
422 TrigConfigSvcCfg( flags )
423
425 from AthenaConfiguration.AllConfigFlags import initConfigFlags
426 flags = initConfigFlags()
427 from AthenaConfiguration.TestDefaults import defaultTestFiles
428 flags.Input.Files = defaultTestFiles.RAW_RUN2
429 flags.lock()
430 TrigConfigSvcCfg( flags )
431
433 keys = _createJsonMenuFiles(run=360026, lb=151, crestServer=None)
434 assert keys is not None, "No keys returned"
435 for k,v in {"SMK" : 2749, "LVL1PSK" : 23557, "HLTPSK" : 17824, "BGSK" : 2181}.items():
436 assert k in keys, "Missing key {}".format(k)
437 assert v == keys[k], "Wrong value {}".format(v)
438
439 unittest.main(verbosity=2)
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:177
dict[str, int|str] _createJsonMenuFiles(run, lb, str|None crestServer=None)
dict[str, Any] _getTrigConfigFromFlag(*, triggerConfig, run, lb, useCrest, crestServer)
createL1PrescalesFileFromMenu(flags, dict[str, float]|None prescales=None)
dict[str, int|str] getTrigConfFromCool(runNumber, lumiBlock)
dict[str, int|str] getTrigConfFromConditions(runNumber, lumiBlock, flags)
createJsonMenuFiles(run, lb, flags)
dict[str, int|str] getTrigConfFromCrest(runNumber, lumiBlock, crestServer)