diff --git a/TileCalorimeter/TileTBRec/python/RunTileTBRec.py b/TileCalorimeter/TileTBRec/python/RunTileTBRec.py new file mode 100644 index 0000000000000000000000000000000000000000..6f73d393d7c5b97d7881ef1859308f0e86434da4 --- /dev/null +++ b/TileCalorimeter/TileTBRec/python/RunTileTBRec.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +# +# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration +# +''' +@file RunTileTBRec.py +@brief Script to run Tile TestBeam Reconstruction/Monitoring +''' + +from AthenaConfiguration.Enums import BeamType +from AthenaConfiguration.AutoConfigFlags import GetFileMD +from TileConfiguration.TileConfigFlags import TileRunType +from TileRecEx import TileInputFiles +from AthenaCommon.SystemOfUnits import GeV + +import sys + +epiLog = """ +Examples: + + RunTileTBRec.py --run RUNNUMBER --evtMax 1 + RunTileTBRec.py --filesInput=FILE1,FILE2 Exec.SkipEvents=100 + +At least one should provide the following arguments or Athena configuration flags (flags have higher priority): + Input file(s), e.g.: --run RUNNUMBER | --filesInput=FILE1,FILE2 | Input.Files="['FILE1','FILE2']" +""" + +if __name__ == '__main__': + + # Setup logs + from AthenaCommon.Logging import logging + log = logging.getLogger('RunTileTBRec') + from AthenaCommon.Constants import INFO + log.setLevel(INFO) + + # Set the Athena configuration flags + from AthenaConfiguration.AllConfigFlags import initConfigFlags + + import argparse + + flags = initConfigFlags() + parserParents = [flags.getArgumentParser(), TileInputFiles.getArgumentParser(add_help=False)] + parser = argparse.ArgumentParser(parents=parserParents, add_help=False, fromfile_prefix_chars='@', epilog=epiLog, formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument('--preExec', help='Code to execute before locking configs') + parser.add_argument('--postExec', help='Code to execute after setup') + parser.add_argument('--postInclude', nargs='+', help='Configuration fragment to include after main job options') + parser.add_argument('--printConfig', action='store_true', help='Print detailed Athena configuration') + parser.add_argument('--dumpArguments', action='store_true', help='Print arguments and exit') + parser.add_argument('--outputVersion', type=str, default="", help='Version to be used in output files for ntuple and monitoring') + parser.add_argument('--outputDirectory', default='.', help='Output directory for produced files') + + parser.add_argument('--frag-ids', dest='fragIDs', nargs="*", default=['0x100', '0x101', '0x200', '0x201', '0x402'], + help='Tile Frag IDs of modules to be monitored. Empty=ALL') + parser.add_argument('--demo-cabling', dest='demoCabling', type=int, default=2018, help='Time Demonatrator cabling to be used') + parser.add_argument('--nsamples', type=int, default=None, help='Number of samples') + parser.add_argument('--use-sqlite', dest='useSqlite', default='/afs/cern.ch/user/t/tiledemo/public/efmon/condb/tileSqlite.db', + help='Providing local SQlite file, conditions constants will be used from it') + parser.add_argument('--mon', default=False, help='Run Tile TB monitoring', action=argparse.BooleanOptionalAction) + parser.add_argument('--offline-units', type=int, choices=[0, 1, 2, 3], default=None, + help='Offline units in ntuple: 0 (ADC), 1 (pC), 2 (Cesium pC), 3 (MeV)') + + # Set up Tile run type + run_type_group = parser.add_argument_group('Tile Run Type') + run_type = run_type_group.add_mutually_exclusive_group() + run_type.add_argument('--cis', action='store_true', help='Tile CIS run type') + run_type.add_argument('--mono-cis', action='store_true', dest='mono_cis', help='Tile mono CIS run type') + run_type.add_argument('--laser', action='store_true', help='Tile laser run type') + run_type.add_argument('--pedestals', action='store_true', help='Tile pedestals run type') + run_type.add_argument('--physics', action='store_true', help='Tile physics run type') + + # Set up Tile reconstuction method + method = parser.add_argument_group('Tile reconstuction method') + method.add_argument('--opt2', default=False, help='Use Tile Opt2 reconstuction method', action=argparse.BooleanOptionalAction) + method.add_argument('--opt-atlas', dest='opt_atlas', default=False, help='Use Tile OptATLAS reconstuction method', action=argparse.BooleanOptionalAction) + method.add_argument('--fit', default=True, help='Use Tile Fit reconstuction method', action=argparse.BooleanOptionalAction) + + args, _ = parser.parse_known_args() + + if args.dumpArguments: + log.info('=====>>> FINAL ARGUMENTS FOLLOW') + print('{:40} : {}'.format('Argument Name', 'Value')) + for a, v in (vars(args)).items(): + print(f'{a:40} : {v}') + sys.exit(0) + + fragIDs = [int(fragID, base=16) for fragID in args.fragIDs] + + # Initially the following flags are not set up (they must be provided) + flags.Input.Files = [] + + # Initial configuration flags from command line arguments (to be used to set up defaults) + flags.fillFromArgs(parser=parser) + + # =======>>> Set the Athena configuration flags to defaults (can be overriden via comand line) + flags.Exec.MaxEvents = 3 + flags.Common.isOnline = True + flags.GeoModel.AtlasVersion = 'ATLAS-R2-2015-04-00-00' + flags.DQ.useTrigger = False + flags.DQ.enableLumiAccess = False + flags.Exec.PrintAlgsSequence = True + + flags.Tile.doFit = True + flags.Tile.useDCS = False + flags.Tile.NoiseFilter = 0 + flags.Tile.correctTime = False + flags.Tile.correctTimeJumps = False + flags.Tile.BestPhaseFromCOOL = False + flags.Tile.doOverflowFit = False + + flags.Tile.RunType = TileRunType.PHY + flags.Beam.Type = BeamType.Collisions + # Get beam energy from meta data (Tile TB setup: [GeV]) + beamEnergy = GetFileMD(flags.Input.Files).get("beam_energy", 100) + flags.Beam.Energy = beamEnergy * GeV + + # Set up the Tile input files + if not flags.Input.Files and args.run: + flags.Input.Files = TileInputFiles.findFilesFromAgruments(args) + if not flags.Input.Files: + log.error('Input files must be provided! For example: --filesInput=file1,file2,... or --run RUNNUMBER') + sys.exit(-1) + + # Set up the Tile run type using arguments if it was not set up via configuration flags + if args.cis: + flags.Tile.RunType = TileRunType.CIS + elif args.mono_cis: + flags.Tile.RunType = TileRunType.MONOCIS + elif args.laser: + flags.Tile.RunType = TileRunType.LAS + elif args.pedestals: + flags.Tile.RunType = TileRunType.PED + elif args.physics: + flags.Tile.RunType = TileRunType.PHY + + # Set up Tile reconstuction method + flags.Tile.doOpt2 = args.opt2 + flags.Tile.doOptATLAS = args.opt_atlas + flags.Tile.doFit = args.fit + + # =======>>> Override default configuration flags from command line arguments + flags.fillFromArgs(parser=parser) + + runNumber = flags.Input.RunNumbers[0] + if not flags.Output.HISTFileName: + flags.Output.HISTFileName = f'{args.outputDirectory}/tiletbmon_{runNumber}{args.outputVersion}.root' + + if args.preExec: + log.info(f'Executing preExec: {args.preExec}') + exec(args.preExec) + + flags.lock() + + log.info('=====>>> FINAL CONFIG FLAGS SETTINGS FOLLOW:') + flags.dump(pattern='Tile.*|Beam.*|Input.*|Exec.*|IOVDb.[D|G].*', evaluate=True) + + # Configure number of samples + nSamples = args.nsamples + if not nSamples: + nSamples = 7 if flags.Tile.RunType.isBiGain() or runNumber <= 2110820 else 15 + log.info(f'Auto configure number of samples: {nSamples}') + + # =======>>> Initialize configuration object, add accumulator, merge, and run + from AthenaConfiguration.MainServicesConfig import MainServicesCfg + cfg = MainServicesCfg(flags) + + # =======>>> Configure Tile raw data (digits) reading + from TileByteStream.TileByteStreamConfig import TileRawDataReadingCfg + cfg.merge( TileRawDataReadingCfg(flags, readMuRcv=False, + readDigits=True, + readRawChannel=True, + readDigitsFlx=True, + readBeamElem=True) ) + + # =======>>> Configure reconstruction of Tile TestBeam data + from TileTBRec.TileTestBeamRecoConfig import TileTestBeamRawChannelMakerCfg + cfg.merge( TileTestBeamRawChannelMakerCfg(flags, nsamples=nSamples) ) + cfg.merge( TileTestBeamRawChannelMakerCfg(flags, nsamples=16, useFELIX=True) ) + + if args.useSqlite: + cfg.getService('IOVDbSvc').overrideTags += [ + f'<prefix>/TILE</prefix> <db>sqlite://;schema={args.useSqlite};dbname={flags.IOVDb.DatabaseInstance}</db>', + # ROD folder does not exist in Sqlite file at the moment (should be added) + f'<prefix>/TILE/ONL01/STATUS/ROD</prefix> <db>COOLONL_TILE/{flags.IOVDb.DatabaseInstance}</db>' + ] + + # =======>>> Configure Tile TestBeam monitoring + if args.mon: + from TileTBRec.TileTestBeamRecoConfig import TileTestBeamRecoCfg + cfg.merge( TileTestBeamRecoCfg(flags, useDemoCabling=args.demoCabling, nsamples=nSamples) ) + cfg.merge( TileTestBeamRecoCfg(flags, useDemoCabling=args.demoCabling, nsamples=16, useFELIX=True) ) + + from TileMonitoring.RunTileTBMonitoring import TileTestBeamMonitoringCfg + cfg.merge(TileTestBeamMonitoringCfg(flags, fragIDs=fragIDs, useFELIX=True)) + + # =======>>> Configure Tile TestBeam h1000 Ntuple + ntupleFile = f'{args.outputDirectory}/tiletb_{runNumber}{args.outputVersion}.aan.root' + offlineUnits = args.offline_units + if not offlineUnits: + if flags.Tile.RunType in [TileRunType.PED, TileRunType.CIS]: + offlineUnits = 0 # ADC + elif flags.Tile.RunType in [TileRunType.LAS, TileRunType.MONOCIS]: + offlineUnits = 1 # pC + else: + offlineUnits = 3 # MeV + unitsName = {0: "ADC", 1: "pC", 2: "Cesium pC", 3: "MeV"}.get(offlineUnits) + log.info(f'Auto configure offline units: {offlineUnits} => {unitsName}') + + calibrateEnergy = (offlineUnits != 0) + + from TileTBRec.TileTBAANtupleConfig import TileTBAANtupleCfg + cfg.merge( TileTBAANtupleCfg(flags, outputFile=ntupleFile, OfflineUnits=offlineUnits, CalibrateEnergy=calibrateEnergy, NSamples=nSamples) ) + + # =======>>> Configure Tile CIS calibration + if flags.Tile.RunType in [TileRunType.CIS, TileRunType.MONOCIS]: + cisCalibFile = f'{args.outputDirectory}/tileCalibCIS_{runNumber}_CIS{args.outputVersion}.root' + from TileCalibAlgs.TileCisCalibAlgConfig import TileCisCalibAlgCfg + cfg.merge( TileCisCalibAlgCfg(flags, FileName=cisCalibFile) ) + + tileCisTool = cfg.getEventAlgo('TileCisCalibAlg').TileCalibTools['TileCisDefaultCalibTool'] + if runNumber >= 2200000: + tileCisTool.FragIDsDemonstrators = [ 0x201, 0x402 ] + elif runNumber >= 2100000: + tileCisTool.FragIDsDemonstrators = [ 0x201 ] + + # =======>>> Configure Tile noise calibration + if flags.Tile.RunType is TileRunType.PED: + from TileCalibAlgs.TileDigiNoiseCalibAlgConfig import TileDigiNoiseCalibAlgCfg + cfg.merge( TileDigiNoiseCalibAlgCfg(flags) ) + + # =======>>> Configure ROD to ROB mapping + # Scan first event for all fragments to create proper ROD to ROB map + cfg.getCondAlgo('TileHid2RESrcIDCondAlg').RODStatusProxy = None + + # =======>>> Process post includes + if args.postInclude: + log.info(f'Process postInclude: {args.postInclude}') + from PyJobTransforms.TransformUtils import processPostInclude + processPostInclude(args, flags, cfg) + + # =======>>> Any last things to do? + if args.postExec: + log.info(f'Executing postExec: {args.postExec}') + exec(args.postExec) + + if args.printConfig: + cfg.printConfig(withDetails=True, summariseProps=True, printDefaults=True) + + if args.config_only: + cfg.store(open('TileTestBeamMonitoring.pkl', 'wb')) + else: + sc = cfg.run() + # Success should be 0 + sys.exit(not sc.isSuccess())