Skip to content
Snippets Groups Projects
faser_ntuple_maker.py 9.66 KiB
Newer Older
#!/usr/bin/env python
# 
# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration 
#
# Run with
# faser_ntuple_maker.py [options] 
#
# Options:
#   --isMC - needed to reconstruct MC data
#
import sys
import time
import argparse

a = time.time()

parser = argparse.ArgumentParser(description="Run PHYS ntuple production")

parser.add_argument("path",
                    help="Fully qualified input path (directory or file)")

parser.add_argument("--slice", type=int, default=0,
                    help="Specify ordinal output file to produce")
parser.add_argument("--files", type=int, default=1,
FaserMC's avatar
FaserMC committed
                    help="Specify reco files per slice")
parser.add_argument("--merge", type=int, default=1,
                    help="Specify merged files per reco file (MC only)")
parser.add_argument("--last", type=int, default=0,
                    help="Specify last file in slice (normally --files)")

parser.add_argument("--outfile", default="",
                    help="Override output file name")
                    
parser.add_argument("-v", "--verbose", action='store_true', 
                    help="Turn on DEBUG output")
parser.add_argument("-n", "--nevents", type=int, default=-1,
                    help="Specify number of events to process (default: all)")

parser.add_argument("-t", "--tag", default="",
                    help="Specify tag (to append to output filename)")

parser.add_argument("--isMC", action='store_true',
                    help="Running on digitised MC rather than data")
parser.add_argument("--partial", action='store_true',
                    help="Allow partial input files")
parser.add_argument("-c", "--cond", default="",
                    help="Specify global conditions tag (default: OFLCOND-FASER-04)")
parser.add_argument("--trigFilt", action='store_true',
parser.add_argument("--scintFilt", action='store_true',
                    help="apply scintillator event filter")
parser.add_argument("--NoTrackFilt", action='store_true',
                    help="Don't apply track event filter (default: do)")
FASER Reco's avatar
FASER Reco committed
parser.add_argument("--unblind", action='store_true',
                    help="Don't apply signal blinding (default: do)")
parser.add_argument("--onlyblind", action='store_true',
                    help="Only store events that were blinded (will override unblind arg)")

parser.add_argument("--grl", default="",
                    help="Specify GRL to apply")
FaserMC's avatar
FaserMC committed
parser.add_argument("--fluka", action='store_true',
                    help="Add FLUKA weights to ntuple")
parser.add_argument("--genie", action='store_true',
                    help="Add Genie weights to ntuple")

args = parser.parse_args()

from pathlib import Path

# Must figure out what we are doing here
filepath = Path(args.path)

filelist = []
# If this is a directory, need to create file list
if filepath.is_dir():

FaserMC's avatar
FaserMC committed
    # Use expected data pattern to find files (data only)
FaserMC's avatar
FaserMC committed

    # Make list of segments to search for
    seglist = []
    if args.merge > 1:
        start = args.slice * args.files * args.merge

        # Number of files to combine
        if args.last > 0:
            num = args.last
        else:
            num = args.files

        # Make segment list
        for i in range(start, start+num*args.merge, args.merge):
            seg = f"{i:05d}-{(i+args.merge-1):05d}"
            seglist.append(seg)

FaserMC's avatar
FaserMC committed
        start = args.slice * args.files
        if args.last > 0:
            end = start + args.last
        else:
            end = start + args.files

        seglist = [f'{seg:05d}' for seg in range(start, end)]


    for seg in seglist:

        if args.isMC:
            searchstr = f"FaserMC-*-{seg}-*xAOD.root"
FaserMC's avatar
FaserMC committed
        else:
            searchstr = f"Faser-Physics-{runstr}-{seg}-*xAOD.root"

        flist = list(filepath.glob(searchstr))
        if len(flist) == 0:
            print(f"Didn't find file {searchstr}!")
            if args.partial: continue
            sys.exit(1)

        elif len(flist) > 1:
            print(f"Found multiple matches for {searchstr}!")
            print(flist)
            sys.exit(1)

        filestr = str(flist[0].resolve())
        # Use proper EOS file path here?
        if filestr[:4] == '/eos':
            filestr = f"root://eospublic.cern.ch/{filestr}"
        filelist.append(filestr)
    # End of loop over segments

FaserMC's avatar
FaserMC committed
    # Parse name to create outfile
    firstfile = Path(filelist[0])
    firststem = str(firstfile.stem)
FaserMC's avatar
FaserMC committed
    firstfaser = firststem.split('-')[0]
    firstshort = firststem.split('-')[1]
FaserMC's avatar
FaserMC committed
    runstr = firststem.split('-')[2]
    firstseg = firststem.split('-')[3]
FaserMC's avatar
FaserMC committed
    if args.merge > 1:
        firstseg2 = firststem.split('-')[4]

    lastfile = Path(filelist[-1])
    laststem = str(lastfile.stem)
    lastseg = laststem.split('-')[3]
FaserMC's avatar
FaserMC committed
    if args.merge > 1:
        lastseg = laststem.split('-')[4]
FaserMC's avatar
FaserMC committed
    print(f"Faser = {firstfaser}")
    print(f"Short = {firstshort}")
    print(f"Run   = {runstr}")
    print(f"First = {firstseg}")
    print(f"Last  = {lastseg}")
FaserMC's avatar
FaserMC committed

FaserMC's avatar
FaserMC committed
    tagstr = firststem.replace(f"{firstfaser}-{firstshort}-{runstr}-{firstseg}", "")
    if args.merge > 1:
        tagstr = tagstr.replace(f"-{firstseg2}", "")

    tagstr = tagstr.replace("-xAOD", "")
FaserMC's avatar
FaserMC committed

    print(f"Tag   = {tagstr}")
FaserMC's avatar
FaserMC committed
    outfile = f"{firstfaser}-{firstshort}-{runstr}-{firstseg}-{lastseg}"

    # This will include the leading -
    if len(tagstr) > 0:
        outfile += f"{tagstr}"

    if len(args.tag) > 0 and args.tag not in tagstr:
        outfile += f"-{args.tag}"

    outfile += "-PHYS.root"

FaserMC's avatar
FaserMC committed

# If this is a single file, just process that
# Could be a url, so don't check if this is a file
else:
    filelist.append(args.path)

    # Build output name
    filestem = str(filepath.stem)

    # Remove -xAOD from end
    if filestem[-5:] == "-xAOD":
        filestem = filestem[:-5]

    # Do we want to add a tag?
    if len(args.tag) > 0 and args.tag not in filestem:
        filestem += f"-{args.tag}"

    outfile = f"{filestem}-PHYS.root"

# Print out what we found
if len(filelist) == 0:
    printf("Found no files for {args.path}!")
    sys.exit(1)
elif len(filelist) == 1:
    print("Processing file:")
else:
    print("Processing files:")
for f in filelist:
    print(f)

# Override output file?
if len(args.outfile) > 0:
    outfile = args.outfile

print("Output file:")
print(outfile)

print(f"Trigger Filter  = {args.trigFilt}")
print(f"Scintillator Filter  = {args.scintFilt}")
print(f"Track Filter = {not args.NoTrackFilt}")
print(f"Blind = {not args.unblind}")
print(f"OnlyBlinded = {args.onlyblind}")
print(f"GRL = {args.grl}")

# OK, lets run the job here
from AthenaCommon.Logging import log, logging
from AthenaCommon.Constants import DEBUG, VERBOSE, INFO
from AthenaCommon.Configurable import Configurable
from CalypsoConfiguration.AllConfigFlags import ConfigFlags

from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg

Configurable.configurableRun3Behavior = True

# Configure
ConfigFlags.Input.Files = filelist
if len(args.cond) == 0:
    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-04"         # Always needed; must match FaserVersionS
else:
    print(f"Setting ConfigFlags.IOVDb.GlobalTag = {args.cond}")
    ConfigFlags.IOVDb.GlobalTag = args.cond
FaserMC's avatar
FaserMC committed
ConfigFlags.Input.isMC = args.isMC
if args.isMC:
    ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"           # Use MC conditions 
else:
    ConfigFlags.IOVDb.DatabaseInstance = "CONDBR3"           # Use data conditions

ConfigFlags.Input.ProjectName = "data21"                     # Needed to bypass autoconfig

ConfigFlags.GeoModel.FaserVersion   = "FASERNU-03"           # FASER geometry
ConfigFlags.Common.isOnline = False
ConfigFlags.GeoModel.Align.Dynamic = False
ConfigFlags.Beam.NumberOfCollisions = 0.
ConfigFlags.Detector.GeometryFaserSCT = True
ConfigFlags.lock()

# Core components
acc = MainServicesCfg(ConfigFlags)
acc.merge(PoolReadCfg(ConfigFlags))

# Create kwargs for NtupleDumper
grl_kwargs = {}
if args.grl:
    grl_kwargs['GoodRunsList'] = args.grl
    grl_kwargs['ApplyGoodRunsList'] = True

mc_kwargs = {}
if args.genie:
    mc_kwargs['UseGenieWeights'] = True
if args.fluka:
    mc_kwargs['UseFlukaWeights'] = True

# algorithm
from NtupleDumper.NtupleDumperConfig import NtupleDumperAlgCfg
if args.isMC:
    acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, **mc_kwargs))
    acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, DoBlinding=(not args.unblind), OnlyBlinded=args.onlyblind, DoScintFilter = args.scintFilt, DoTrackFilter = (not args.NoTrackFilt), DoTrigFilter = args.trigFilt, **grl_kwargs))
FaserMC's avatar
FaserMC committed
if not args.verbose:
    from AthenaConfiguration.ComponentFactory import CompFactory
    AthenaEventLoopMgr = CompFactory.AthenaEventLoopMgr()
    AthenaEventLoopMgr.EventPrintoutInterval=1000
    acc.addService(AthenaEventLoopMgr)
else:
    nd = acc.getEventAlgo("NtupleDumperAlg")
    nd.OutputLevel = VERBOSE

# Hack to avoid problem with our use of MC databases when isMC = False
if not args.isMC:
    replicaSvc = acc.getService("DBReplicaSvc")
    replicaSvc.COOLSQLiteVetoPattern = ""
    replicaSvc.UseCOOLSQLite = True
    replicaSvc.UseCOOLFrontier = False
    replicaSvc.UseGeomSQLite = True

if args.verbose:
    log.setLevel(VERBOSE)
    acc.printConfig(withDetails=True)
    ConfigFlags.dump()
else:
    log.setLevel(INFO)

acc.getService("MessageSvc").Format = "% F%40W%S%7W%R%T %0W%M"

# Execute and finish
sc = acc.run(maxEvents=args.nevents)

b = time.time()
log.info(f"Finish execution in {b-a} seconds")

# Success should be 0
if sc.isSuccess():
    log.info("Execution succeeded")
    sys.exit(0)
else:
    log.info("Execution failed, return 1")
    sys.exit(1)