1254 parser = argparse.ArgumentParser(prog='athenaEF.py', formatter_class=
1255 lambda prog : argparse.ArgumentDefaultsHelpFormatter(prog, max_help_position=32, width=100),
1256 usage = '%(prog)s [OPTION]... -f FILE jobOptions',
1257 add_help=False)
1258 parser.expert_groups = []
1259
1260
1261 g = parser.add_argument_group('Options')
1262 g.add_argument('jobOptions', nargs='?', help='job options: CA module (package.module:function) or JSON file (.json)')
1263 g.add_argument('--threads', metavar='N', type=int, default=1, help='number of threads')
1264 g.add_argument('--concurrent-events', metavar='N', type=int, help='number of concurrent events if different from --threads')
1265 g.add_argument('--log-level', '-l', metavar='LVL', default='INFO', help='OutputLevel of athena')
1266 g.add_argument('--precommand', '-c', metavar='CMD', action='append', default=[],
1267 help='Python commands executed before job options')
1268 g.add_argument('--postcommand', '-C', metavar='CMD', action='append', default=[],
1269 help='Python commands executed after job options')
1270 g.add_argument('--interactive', '-i', action='store_true', help='interactive mode')
1271 g.add_argument('--help', '-h', nargs='?', choices=['all'], action=MyHelp, help='show help')
1272
1273 g = parser.add_argument_group('Input/Output')
1274 g.add_argument('--file', '--filesInput', '-f', action='append', help='input RAW file')
1275 g.add_argument('--save-output', '-o', metavar='FILE', help='output file name')
1276 g.add_argument('--number-of-events', '--evtMax', '-n', metavar='N', type=int, default=None,
1277 help='processes N events (default: from DB/config, -1 means all)')
1278 g.add_argument('--skip-events', '--skipEvents', '-k', metavar='N', type=int, default=None,
1279 help='skip N first events')
1280 g.add_argument('--loop-files', action=argparse.BooleanOptionalAction, default=None,
1281 help='loop over input files if no more events')
1282 g.add_argument('--efdf-interface-library', metavar='LIB', default=None,
1283 help='name of the EFDF interface shared library to load (default: TrigDFEmulator)')
1284
1285
1286 g = parser.add_argument_group('Performance and debugging')
1287 g.add_argument('--perfmon', action='store_true', help='enable PerfMon')
1288 g.add_argument('--tcmalloc', action='store_true', default=True, help='use tcmalloc')
1289 g.add_argument('--stdcmalloc', action='store_true', help='use stdcmalloc')
1290 g.add_argument('--stdcmath', action='store_true', help='use stdcmath library')
1291 g.add_argument('--imf', action='store_true', default=True, help='use Intel math library')
1292 g.add_argument('--timeout', metavar='MSEC', type=int, default=None,
1293 help='event processing timeout (HardTimeout) in milliseconds. '
1294 f'NB: only the soft timeout ({SOFT_TIMEOUT_FRACTION*100:.0f}%% of it) is enforced')
1295
1296
1297 g = parser.add_argument_group('Conditions')
1298 g.add_argument('--run-number', '-R', metavar='RUN', type=int,
1299 help='run number (if None, read from first event)')
1300 g.add_argument('--lb-number', '-L', metavar='LBN', type=int,
1301 help='lumiblock number (if None, read from first event)')
1302 g.add_argument('--conditions-run', metavar='RUN', type=int, default=None,
1303 help='reference run number for conditions lookup (use when IS run number has no COOL data)')
1304 g.add_argument('--sor-time', type=arg_sor_time,
1305 help='The Start Of Run time. Three formats are accepted: '
1306 '1) the string "now", for current time; '
1307 '2) the number of nanoseconds since epoch (e.g. 1386355338658000000 or int(time.time() * 1e9)); '
1308 '3) human-readable "20/11/18 17:40:42.3043". If not specified the sor-time is read from the conditions DB')
1309 g.add_argument('--detector-mask', metavar='MASK', type=arg_detector_mask,
1310 help='detector mask (if None, read from the conditions DB), use string "all" to enable all detectors')
1311
1312
1313 g = parser.add_argument_group('Database')
1314 g.add_argument('--use-database', '-b', action='store_true',
1315 help='configure from trigger database using SMK')
1316 g.add_argument('--db-server', metavar='DB', default='TRIGGERDB_RUN3', help='DB server name (alias)')
1317 g.add_argument('--smk', type=int, default=None, help='Super Master Key')
1318 g.add_argument('--l1psk', type=int, default=None, help='L1 prescale key')
1319 g.add_argument('--hltpsk', type=int, default=None, help='HLT prescale key')
1320 g.add_argument('--use-crest', action='store_true', default=False,
1321 help='Use CREST for trigger configuration')
1322 g.add_argument('--crest-server', metavar='URL', default=None,
1323 help='CREST server URL (default: $CREST_SERVER or crest.cern.ch)')
1324 g.add_argument('--dump-config', action='store_true', help='Dump joboptions JSON file')
1325 g.add_argument('--dump-config-exit', action='store_true', help='Dump joboptions JSON file and exit')
1326
1327
1328 g = parser.add_argument_group('Magnets')
1329 g.add_argument('--solenoid-current', type=float, default=None,
1330 help='Solenoid current in Amperes (default: nominal current for offline running, required from IS online)')
1331 g.add_argument('--toroids-current', type=float, default=None,
1332 help='Toroids current in Amperes (default: nominal current for offline running, required from IS online)')
1333
1334
1335 g = parser.add_argument_group('Online')
1336 g.add_argument('--online-environment', action='store_true',
1337 help='Enable online environment: read run parameters from IS and trigger '
1338 'configuration keys (SMK, L1PSK, HLTPSK) from OKS via WEBDAQ REST API')
1339 g.add_argument('--partition', metavar='NAME', default=None,
1340 help='TDAQ partition name (defaults to TDAQ_PARTITION environment variable)')
1341 g.add_argument('--webdaq-base', metavar='URL', default=None,
1342 help='WEBDAQ base URL (defaults to TDAQ_WEBDAQ_BASE environment variable)')
1343 g.add_argument('--application-name', metavar='NAME', default=os.environ.get('TDAQ_APPLICATION_NAME', 'athenaEF'),
1344 help='Application name for OH/ERS. Defaults to TDAQ_APPLICATION_NAME environment variable, else athenaEF)')
1345
1346
1347 g = parser.add_argument_group('Online Histogramming')
1348 g.add_argument('--oh-monitoring', '-M', action='store_true', default=False,
1349 help='enable online histogram publishing via WebdaqHistSvc')
1350
1351
1352 g = parser.add_argument_group('Expert')
1353 parser.expert_groups.append(g)
1354 (args, unparsed_args) = parser.parse_known_args()
1355
1356 os.environ['TDAQ_APPLICATION_NAME'] = args.application_name
1357 check_args(parser, args)
1358
1359
1360 from PyUtils.Helpers import ROOTSetup
1361 ROOTSetup(batch=True)
1362
1363
1364 import ROOT
1365 ROOT.ROOT.EnableThreadSafety()
1366
1367
1368 import AthenaCommon.Logging
1369 AthenaCommon.Logging.log.setLevel(getattr(logging, args.log_level))
1370 AthenaCommon.Logging.log.setFormat("%(asctime)s Py:%(name)-31s %(levelname)7s %(message)s")
1371
1372
1373 if not args.concurrent_events:
1374 args.concurrent_events = args.threads
1375
1376
1377 is_database = args.use_database
1378 is_json = bool(args.jobOptions) and not is_database and args.jobOptions.endswith('.json')
1379 is_ca = not (is_database or is_json)
1380
1381
1382 log.info("Using CREST for trigger configuration: %s", args.use_crest)
1383 if args.use_crest and args.crest_server is None:
1384 from IOVDbSvc.IOVDbAutoCfgFlags import getCrestConnection
1385 args.crest_server = getCrestConnection()
1386 log.info("Using default CREST server: %s", args.crest_server)
1387
1388 update_run_params(args)
1389
1390
1391
1392
1393
1394 force_psk = args.use_database and ((args.hltpsk is not None) or args.online_environment)
1395
1396 if args.use_database:
1397
1398 update_trigconf_keys(args)
1399
1400
1401 if is_ca:
1402 configure_from_ca(args, unparsed_args)
1403
1404
1408 config_source = "the trigger database" if is_database else "a JSON file"
1409
1410 if unparsed_args:
1411 log.warning("Ignoring flag(s) given on the command line, the configuration is read from %s: %s",
1412 config_source, ' '.join(unparsed_args))
1413
1414
1415
1416
1417 overrides = RuntimeOverrides()
1418
1419 overrides.set('AvalancheSchedulerSvc.ThreadPoolSize', args.threads)
1420 overrides.set('EventDataSvc.NSlots', args.concurrent_events)
1421 overrides.set('DataFlowConfig.DF_ApplicationName', args.application_name)
1422
1423 ef_files = args.file if args.file else []
1424 if ef_files:
1425 overrides.set('EFInterfaceSvc.Files', ef_files)
1426 overrides.set('EFInterfaceSvc.T0ProjectTag', args.T0_project_tag)
1427 overrides.set('EFInterfaceSvc.BeamType', args.beam_type)
1428 overrides.set('EFInterfaceSvc.BeamEnergy', args.beam_energy)
1429 overrides.set('EFInterfaceSvc.TriggerType', args.trigger_type)
1430 overrides.set('EFInterfaceSvc.Stream', args.stream)
1431 overrides.set('EFInterfaceSvc.Lumiblock', args.lumiblock)
1432 overrides.set('EFInterfaceSvc.DetMask', args.file_detector_mask)
1433 if args.run_number is not None:
1434 overrides.set('EFInterfaceSvc.RunNumber', args.run_number)
1435 if args.save_output is not None:
1436 overrides.set('EFInterfaceSvc.OutputFileName', args.save_output)
1437 if args.loop_files is not None:
1438 overrides.set('EFInterfaceSvc.LoopOverFiles', args.loop_files)
1439 if args.number_of_events is not None:
1440 overrides.set('EFInterfaceSvc.NumEvents', args.number_of_events)
1441 if args.skip_events is not None:
1442 overrides.set('EFInterfaceSvc.SkipEvents', args.skip_events)
1443 if args.efdf_interface_library is not None:
1444 overrides.set('EFInterfaceSvc.EFDFInterfaceLibraryName', args.efdf_interface_library)
1445
1446 if args.timeout is not None:
1447 overrides.set('HltEventLoopMgr.HardTimeout', float(args.timeout))
1448 overrides.set('HltEventLoopMgr.SoftTimeoutFraction', SOFT_TIMEOUT_FRACTION)
1449 if args.conditions_run is not None:
1450
1451 overrides.set('HltEventLoopMgr.forceRunNumber', args.conditions_run)
1452
1453 if force_psk:
1454 overrides.set('HLTPrescaleCondAlg.Source', 'DB')
1455
1456
1457
1458
1459 if not args.online_environment:
1460 if args.oh_monitoring:
1461 overrides.declare_type('THistSvc', 'WebdaqHistSvc')
1462 overrides.create_service('WebdaqInfoSvc')
1463 else:
1464 overrides.declare_type('THistSvc', 'THistSvc')
1465 overrides.drop_service('WebdaqInfoSvc')
1466
1467
1468 for cmd in args.postcommand:
1469 overrides.add_command(cmd)
1470
1471
1472 if args.precommand:
1473 log.info("Executing precommand(s)")
1474 for cmd in args.precommand:
1475 log.info(" %s", cmd)
1476 exec(cmd, globals(), {})
1477
1478 if is_database:
1479
1480
1481 if args.use_crest:
1482 from TrigConfStorage.TriggerCrestUtil import TriggerCrestUtil
1483 crestconn = TriggerCrestUtil.getCrestConnection(args.db_server)
1484 db_alias = f"{args.crest_server}/{crestconn}"
1485 log.info("Loading configuration via CREST from %s with SMK %d", db_alias, args.smk)
1486 else:
1487 db_alias = args.db_server
1488 log.info("Loading configuration from database %s with SMK %d", db_alias, args.smk)
1489
1490
1491 run_params = get_run_params(args).to_dict()
1492 acc = load_from_database(db_alias, args.smk, args.l1psk, args.hltpsk, run_params, overrides=overrides)
1493 log.info("Configuration loaded from database")
1494
1495 else:
1496
1497 log.info("Loading configuration from JSON file: %s", args.jobOptions)
1498
1499 run_params = get_run_params(args).to_dict()
1500 acc = load_from_json(args.jobOptions, run_params, overrides=overrides)
1501 log.info("Configuration loaded from JSON")
1502
1503
1504 if args.dump_config or args.dump_config_exit:
1505 fname = "HLTJobOptions"
1506
1507 if is_database:
1508
1509 from TrigConfIO.HLTTriggerConfigAccess import HLTJobOptionsAccess
1510 log.info("Fetching configuration from database for dump...")
1511 jo_access = HLTJobOptionsAccess(dbalias=acc.db_server, smkey=acc.smk)
1512 props = jo_access.algorithms()
1513
1514 log.info("Dumping configuration to %s.json", fname)
1515 hlt_json = {'filetype': 'joboptions', 'properties': props}
1516 with open(f"{fname}.json", "w") as f:
1517 json.dump(hlt_json, f, indent=4, sort_keys=True, ensure_ascii=True)
1518
1519 elif is_json:
1520
1521 props = acc.properties
1522 if props:
1523 log.info("Dumping configuration to %s.json", fname)
1524 hlt_json = {'filetype': 'joboptions', 'properties': props}
1525 with open(f"{fname}.json", "w") as f:
1526 json.dump(hlt_json, f, indent=4, sort_keys=True, ensure_ascii=True)
1527 else:
1528 log.warning("No properties available to dump")
1529
1530 if args.dump_config_exit:
1531 log.info("Configuration dumped. Exiting...")
1532 sys.exit(0)
1533
1534
1535 log.info("Starting Athena execution...")
1536
1537
1538
1539
1540 worker_dir = os.path.join(os.getcwd(), "athenaHLT_workers", "athenaHLT-01")
1541 if not os.path.exists(worker_dir):
1542 log.info("Creating worker directory: %s", worker_dir)
1543 os.makedirs(worker_dir, exist_ok=True)
1544
1545
1546 oh_infra = start_oh_infrastructure(args) if args.oh_monitoring else None
1547
1548 if args.interactive:
1549 log.info("Interactive mode - call acc.run() to execute")
1550 import code
1551 code.interact(local={'acc': acc})
1552 else:
1553
1554 from AthenaCommon import ExitCodes
1555 exitcode = 0
1556 try:
1557
1558 sc = acc.run(args.number_of_events)
1559 if sc.isFailure():
1560 exitcode = ExitCodes.EXE_ALG_FAILURE
1561 except SystemExit as e:
1562 exitcode = ExitCodes.EXE_ALG_FAILURE if e.code == 1 else e.code
1563 except Exception:
1564 traceback.print_exc()
1565 exitcode = ExitCodes.UNKNOWN_EXCEPTION
1566 finally:
1567 stop_oh_infrastructure(oh_infra)
1568
1569 log.info('Leaving with code %d: "%s"', exitcode, ExitCodes.what(exitcode))
1570 sys.exit(exitcode)
1571
1572