diff --git a/Control/CalypsoExample/Simulation/scripts/faser_simulate.py b/Control/CalypsoExample/Simulation/scripts/faser_simulate.py new file mode 100755 index 0000000000000000000000000000000000000000..1c8320245e6ecfa61857a6d60d04d1cf8d538d54 --- /dev/null +++ b/Control/CalypsoExample/Simulation/scripts/faser_simulate.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python +""" +Produce simulated hits from input 4-vectors +Derived from G4FaserAlgConfigNew + +Usage: +faser_simulate.py filepath outfile + +filepath - full path, including url if needed, to the input 4-vector file +outfile - output filename, parameters will be inferred from this name + +Copyright (C) 2002-2021 CERN for the benefit of the ATLAS and FASER collaborations +""" + +if __name__ == '__main__': + + import sys + import time + a = time.time() +# +# Parse command-line options +# + import argparse + + parser = argparse.ArgumentParser(description="Run FASER simulation") + + parser.add_argument("file_path", + help="Fully qualified path of the raw input file") + + parser.add_argument("output", + help="Output file name") + + #parser.add_argument("--run", type=int, default=123456, + # help="Specify run number to use in simulated data") + + parser.add_argument("--geom", default="TI12MC", + help="Specify geomtery to simulation (default: TI12MC, alt: TestBeamMC)") + + + parser.add_argument("--xangle", type=float, default=0.0, + help="Specify H crossing angle (in Radians)") + parser.add_argument("--yangle", type=float, default=0.0, + help="Specify V crossing angle (in Radians)") + parser.add_argument("--xshift", type=float, default=0.0, + help="Specify H shift of events wrt FASER (in mm)") + parser.add_argument("--yshift", type=float, default=0.0, + help="Specify V shift of events wrt FASER (in mm)") + + parser.add_argument("-t", "--tag", default="", + help="Specify sim tag (to append to output filename)") + parser.add_argument("-s", "--skip", type=int, default=0, + help="Specify number of events to skip (default: none)") + parser.add_argument("-n", "--nevents", type=int, default=-1, + help="Specify number of events to process (default: all)") + parser.add_argument("-v", "--verbose", action='store_true', + help="Turn on DEBUG output") + + args = parser.parse_args() + + from pathlib import Path + + filepath = Path(args.file_path) + +# +# Parse input file +# + print(f"Starting digitization of {filepath.name}") + + filestem = filepath.stem + if len(args.tag) > 0: + if args.tag in filestem: + print(f"Not adding tag {args.tag} to {filestem}") + else: + filestem += f"-{args.tag}" + + if args.output: + # Just directly specify the output filename + outfile = args.output + + spl = outfile.split('-') + if len(spl) < 4: + print(f"Can't get run number from {outfile}!") + sys.exit(1) + + runnum = int(spl[2]) + segnum = int(spl[3]) + + else: + outfile = f"{filestem}-HITS.root" + print(f"Output file name not specified") + sys.exit(1) + + print(f"Outfile: {outfile}") + +# +# Figure out events to run +# + if args.skip > 0: + print(f"Skipping {args.skip} events by command-line option") + + if args.nevents > 0: + print(f"Reconstructing {args.nevents} events by command-line option") +# +# Set up logging and config behaviour +# + from AthenaCommon.Logging import log + from AthenaCommon.Constants import DEBUG, VERBOSE + from AthenaCommon.Configurable import Configurable + log.setLevel(DEBUG) + Configurable.configurableRun3Behavior = 1 +# +# Import and set config flags +# + from CalypsoConfiguration.AllConfigFlags import ConfigFlags + from AthenaConfiguration.Enums import ProductionStep + ConfigFlags.Common.ProductionStep = ProductionStep.Simulation +# +# All these must be specified to avoid auto-configuration +# + ConfigFlags.Input.RunNumber = [ runnum ] + ConfigFlags.Input.OverrideRunNumber = True + ConfigFlags.Input.LumiBlockNumber = [ (segnum+1) ] + ConfigFlags.Input.isMC = True +# +# Input file name +# + # Path mangles // in url, so use direct file_path here + ConfigFlags.Input.Files = [ args.file_path ] +# +# Skip events +# + ConfigFlags.Exec.SkipEvents = args.skip + ConfigFlags.Exec.MaxEvents = args.nevents +# +# Output file name +# + ConfigFlags.Output.HITSFileName = outfile +# +# Sim ConfigFlags +# + ConfigFlags.Sim.Layout = "FASER" + ConfigFlags.Sim.PhysicsList = "FTFP_BERT" + ConfigFlags.Sim.ReleaseGeoModel = False + ConfigFlags.Sim.IncludeParentsInG4Event = True # Controls whether BeamTruthEvent is written to output HITS file + +# +# Figure out configuration +# + if args.geom == "TI12MC": + # 2022 TI12 geometry + ConfigFlags.GeoModel.FaserVersion = "FASERNU-03" # Geometry set-up + ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03" # Conditions set-up + # TI12 detectors + detectors = ['Veto', 'VetoNu', 'Preshower', 'FaserSCT', 'Ecal', + 'Trigger', 'Dipole', 'Emulsion', 'Trench'] + + elif args.geom == "TestBeamMC": + # Define 2021 test beam geometry + ConfigFlags.GeoModel.FaserVersion = "FASER-TB00" # Geometry set-up + ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-TB00" # Conditions set-up + # Testbeam detectors (trigger layers are actually veto counters) + detectors = ['Veto', 'Preshower', 'FaserSCT', 'Ecal'] + + else: + print(f"Unknown geometry {args.geom}!") + sys.exit(1) + + + # + # Units are radians and mm + # Probably should only be allowed in TI12, but leave it here for now + ConfigFlags.addFlag("Sim.Beam.xangle", args.xangle) # Potential beam crossing angles + ConfigFlags.addFlag("Sim.Beam.yangle", args.yangle) + ConfigFlags.addFlag("Sim.Beam.xshift", args.xshift) # Potential beam shift + ConfigFlags.addFlag("Sim.Beam.yshift", args.yshift) + + ConfigFlags.addFlag("Input.InitialTimeStamp", 0) # To avoid autoconfig + ConfigFlags.GeoModel.Align.Dynamic = False + + # import sys + # ConfigFlags.fillFromArgs(sys.argv[1:]) + + doShiftLOS = (ConfigFlags.Sim.Beam.xangle or ConfigFlags.Sim.Beam.yangle or + ConfigFlags.Sim.Beam.xshift or ConfigFlags.Sim.Beam.yshift) +# +# Setup detector flags +# + from CalypsoConfiguration.DetectorConfigFlags import setupDetectorsFromList + setupDetectorsFromList(ConfigFlags, detectors, toggle_geometry=True) +# +# Finalize flags +# + ConfigFlags.lock() +# +# Initialize a new component accumulator +# + from CalypsoConfiguration.MainServicesConfig import MainServicesCfg + cfg = MainServicesCfg(ConfigFlags) +# +# Check whether a known input file was specified +# + if ConfigFlags.Input.Files[0].endswith(".events") or ConfigFlags.Input.Files[0].endswith(".hepmc"): + + from HEPMCReader.HepMCReaderConfig import HepMCReaderCfg + + if doShiftLOS: + cfg.merge(HepMCReaderCfg(ConfigFlags, McEventKey = "BeamTruthEvent_ATLASCoord")) + else: + cfg.merge(HepMCReaderCfg(ConfigFlags)) + + from McEventSelector.McEventSelectorConfig import McEventSelectorCfg + cfg.merge(McEventSelectorCfg(ConfigFlags)) +# +# Else, set up to read it as a pool.root file +# + else: + from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg + cfg.merge(PoolReadCfg(ConfigFlags)) + + if doShiftLOS: + from SGComps.AddressRemappingConfig import InputOverwriteCfg + # Rename old truth collection to add ATLAS coord to can still use BeamTruthEvent for the one in FASER Coords + cfg.merge(InputOverwriteCfg("McEventCollection", "BeamTruthEvent", "McEventCollection", "BeamTruthEvent_ATLASCoord")) + +# +# Output file +# + from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg + cfg.merge(PoolWriteCfg(ConfigFlags)) + +# +# Shift LOS +# + if doShiftLOS: + + import McParticleEvent.Pythonizations + from GeneratorUtils.ShiftLOSConfig import ShiftLOSCfg + cfg.merge(ShiftLOSCfg(ConfigFlags, + xcross = ConfigFlags.Sim.Beam.xangle, + ycross = ConfigFlags.Sim.Beam.yangle, + xshift = ConfigFlags.Sim.Beam.xshift, + yshift = ConfigFlags.Sim.Beam.yshift)) + + +# +# Add the G4FaserAlg +# + from G4FaserAlg.G4FaserAlgConfigNew import G4FaserAlgCfg + cfg.merge(G4FaserAlgCfg(ConfigFlags)) +# +# Dump config +# + from AthenaConfiguration.ComponentFactory import CompFactory + cfg.addEventAlgo(CompFactory.JobOptsDumperAlg(FileName="G4FaserTestConfig.txt")) + cfg.getService("StoreGateSvc").Dump = True + cfg.getService("ConditionStore").Dump = True + cfg.printConfig(withDetails=True, summariseProps = False) # gags on ParticleGun if summariseProps = True? + + ConfigFlags.dump() + #f = open("test.pkl","wb") + #cfg.store(f) + #f.close() +# +# Execute and finish +# + + # This fails with ShiftLOSCfg... + #if args.verbose: + # cfg.foreach_component("*").OutputLevel = "DEBUG" + #else: + # cfg.foreach_component("*").OutputLevel = "INFO" + + sc = cfg.run(maxEvents=args.nevents) + + b = time.time() + log.info("Finish execution in " + str(b-a) + " seconds") +# +# Success should be 0 +# + sys.exit(int(sc.isFailure())) + diff --git a/Control/CalypsoExample/Simulation/scripts/submit_faser_simulate.sh b/Control/CalypsoExample/Simulation/scripts/submit_faser_simulate.sh new file mode 100755 index 0000000000000000000000000000000000000000..83a2470f1e93bd9703e0e44c5abca3a9aeebe7c1 --- /dev/null +++ b/Control/CalypsoExample/Simulation/scripts/submit_faser_simulate.sh @@ -0,0 +1,221 @@ +#!/bin/bash +# Used with a condor file to submit to vanilla universe +# +# Usage: +# submit_faserMDC_simluate.sh [--shift] input_file output_file [release_directory] [working_directory] [skip] [nevts] +# +# Options: +# --shift - apply crossing angle (and FASER shift) +# --out - specify output location (in EOS) to copy output HITS file +# --log - specify output location (in EOS) for log file +# +# input_file - full file name (with path) +# output_file - full output file name +# release_directory - optional path to release install directory (default pwd) +# working_directory - optional path to output directory location (default pwd) +# skip - events in input file to skip +# nevts = events in input file to process +# +# The release directory must already be set up +# (so an unqualified asetup can set up the release properly) +# +# Script will use git describe to find the release tag. +# If this matches gen/g???? or sim/s???? it will be passed to the job +# +#---------------------------------------- +# Keep track of time +SECONDS=0 +# +# Parse command-line options +while [ -n "$1" ] +do + case "$1" in + -s | --shift) + echo "Applying crossing-angle shift" + xangle=1 + shift;; # This 'eats' the argument + + -l | --log) + logdest="$2"; + shift; + shift;; # Must eat 2 options here + + -o | --out) + outdest="$2"; + shift; + shift;; + + --) # End of options + shift; # Eat this + break;; # And stop parsing + + -*) + echo "Unknown option $1" + shift;; + + *) break;; # Not an option, don't shift + esac +done + +# +# Parse command-line arguments +infile=${1} +outfile=${2} +release_directory=${3} +working_directory=${4} +skip_events=${5} +nevts=${6} +# +# Set defaults if arguments aren't provided +if [ -z "$infile" ] +then + echo "No input file specified!" + echo "Usage: submit_faser_simulate.sh input_file output_file [release dir] [output dir]" + exit 1 +fi +# +if [ -z "$outfile" ] +then + outfile="FaserMC-Test-123456-00000-HITS.root" + echo "No output file specified, using $outfile !" +fi +# +if [ -z "$release_directory" ] +then + release_directory=`pwd` +fi +# +if [ -z "$working_directory" ] +then + working_directory=`pwd` +fi +# +if [ -z "$skip_events" ] +then + skip_events=0 +fi +# +if [ -z "$nevts" ] +then + nevts=-1 +fi +# +starting_directory=`pwd` +# +# Now extract the file information +# Here we do this on the output file, as the input files can be non-standard +# +# First, get the filename +outfilename=$(basename "$outfile") +# +# Now split based on '.' to get stem +defaultIFS=$IFS +IFS='.' +read file_stem ext <<< "$outfilename" +# +# Try to find the run number +IFS='-' +# Read the split words into an array based on delimeter +read faser short run_number seg <<< "$file_stem" +# +# Set the IFS delimeter back or else echo doesn't work... +IFS=$defaultIFS +# +# Check if we found a number, use full input file name if not +output_directory="$working_directory/${run_number}" +re='^[0-9]+$' +if ! [[ $run_number =~ $re ]] ; then + # Not a number... + output_directory="$working_directory/${file_stem}" +fi +# +# Make output directory if needed +mkdir -p "$output_directory" +# +# This magic redirects everything in this script to our log file +logfile=${file_stem}.sim.log +exec >& "$output_directory/$logfile" +echo `date` - $HOSTNAME +echo "Input File: $infile" +echo "Output File: $outfilename" +echo "Release: $release_directory" +echo "Output: $output_directory" +echo "Starting: $starting_directory" +echo "Skip: $skip_events" +echo "Nevts: $nevts" +# +# Set up the release (do this automatically)? +export ATLAS_LOCAL_ROOT_BASE=/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase +source ${ATLAS_LOCAL_ROOT_BASE}/user/atlasLocalSetup.sh +# +# Try automatic +# Always go back to the starting directory in case paths are relative +cd "$starting_directory" +cd "$release_directory" +# This doesn't seem to work, as we need the --input argument +#asetup +#source build/x8*/setup.sh +# +# Do this by hand +asetup --input=calypso/asetup.faser Athena,22.0.49 +source build/x86*/setup.sh +# +# Move to the run directory +cd "$starting_directory" +cd "$output_directory" +# +# Remove any previous directory if it exists +#if [[ -e "$file_stem" ]]; then +# echo "Remove previous directory $file_stem" +# rm -rf "$file_stem" +#fi +# +# Make run directory +if [[ -e "${file_stem}" ]]; then + echo "Directory ${file_stem} already exists" +else + mkdir "${file_stem}" +fi +cd "${file_stem}" +# +# Run job +#if [[ -z "$tag" ]]; then +#fi +if [[ -z "$xangle" ]]; then + faser_simulate.py --skip "$skip_events" -n "$nevts" "$infile" "$outfile" +else + faser_simulate.py --yangle -0.000150 --yshift 12.0 --skip "$skip_events" -n "$nevts" "$infile" "$outfile" +fi +# +# Print out ending time +date +echo "Job finished after $SECONDS seconds" +# +# Copy output to EOS if desired +export EOS_MGM_URL=root://eospublic.cern.ch +# +if ! [ -z "$outdest" ] +then + ls -l + echo "copy *-HITS.root to $outdest" + eos mkdir -p $outdest + eos cp *-HITS.root ${outdest}/ || true +fi +# +# Also copy log file +if ! [ -z "$logdest" ] +then + cd .. + ls -l + echo "copy $logfile to $logdest" + eos mkdir -p $logdest + eos cp $logfile $logdest/$logfile +elif ! [ -z "$outdest" ] +then + cd .. + ls -l + echo "copy $logfile to $outdest" + eos mkdir -p $outdest + eos cp $logfile $outdest/$logfile +fi +