From 0d1e8a4b15afa7ae69743fbf8bddae4fae99bffc Mon Sep 17 00:00:00 2001
From: Eric Torrence <eric.torrence@cern.ch>
Date: Thu, 12 May 2022 20:18:49 -0700
Subject: [PATCH] Add MDC scripts

---
 .../Digitization/scripts/faserMDC_digi.py     | 144 ++++++++++++
 .../scripts/submit_faserMDC_digi.sh           | 128 +++++++++++
 .../CalypsoExample/Generation/CMakeLists.txt  |  12 +
 .../mdc/FaserMC-MDC_PG_emi_100GeV-101104.json |  13 ++
 .../mdc/FaserMC-MDC_PG_emi_logE-101102.json   |  13 ++
 .../mdc/FaserMC-MDC_PG_epl_100GeV-101103.json |  13 ++
 .../mdc/FaserMC-MDC_PG_epl_logE-101101.json   |  13 ++
 .../mdc/FaserMC-MDC_PG_gam_100GeV-102201.json |  13 ++
 .../mdc/FaserMC-MDC_PG_mumi_logE-101302.json  |  13 ++
 .../mdc/FaserMC-MDC_PG_mupl_logE-101301.json  |  13 ++
 .../FaserMC-MDC_PG_pimi_100GeV-121102.json    |  13 ++
 .../FaserMC-MDC_PG_pipl_100GeV-121101.json    |  13 ++
 .../Generation/python/__init__.py             |   2 +
 .../Generation/python/faserMDC_pgparser.py    |  90 ++++++++
 .../scripts/faserMDC_particlegun.py           | 191 ++++++++++++++++
 .../Generation/scripts/faser_particlegun.py   | 178 +++++++++++++++
 .../scripts/submit_faserMDC_particlegun.sh    | 129 +++++++++++
 .../Reconstruction/scripts/faserMDC_reco.py   | 207 ++++++++++++++++++
 .../scripts/submit_faserMDC_reco.sh           | 134 ++++++++++++
 .../scripts/submit_faser_reco.sh              |  10 +-
 20 files changed, 1339 insertions(+), 3 deletions(-)
 create mode 100755 Control/CalypsoExample/Digitization/scripts/faserMDC_digi.py
 create mode 100755 Control/CalypsoExample/Digitization/scripts/submit_faserMDC_digi.sh
 create mode 100644 Control/CalypsoExample/Generation/CMakeLists.txt
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_emi_100GeV-101104.json
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_emi_logE-101102.json
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_epl_100GeV-101103.json
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_epl_logE-101101.json
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_gam_100GeV-102201.json
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_mumi_logE-101302.json
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_mupl_logE-101301.json
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pimi_100GeV-121102.json
 create mode 100644 Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pipl_100GeV-121101.json
 create mode 100644 Control/CalypsoExample/Generation/python/__init__.py
 create mode 100644 Control/CalypsoExample/Generation/python/faserMDC_pgparser.py
 create mode 100755 Control/CalypsoExample/Generation/scripts/faserMDC_particlegun.py
 create mode 100755 Control/CalypsoExample/Generation/scripts/faser_particlegun.py
 create mode 100755 Control/CalypsoExample/Generation/scripts/submit_faserMDC_particlegun.sh
 create mode 100755 Control/CalypsoExample/Reconstruction/scripts/faserMDC_reco.py
 create mode 100755 Control/CalypsoExample/Reconstruction/scripts/submit_faserMDC_reco.sh

diff --git a/Control/CalypsoExample/Digitization/scripts/faserMDC_digi.py b/Control/CalypsoExample/Digitization/scripts/faserMDC_digi.py
new file mode 100755
index 00000000..805fd649
--- /dev/null
+++ b/Control/CalypsoExample/Digitization/scripts/faserMDC_digi.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+# Run with:
+# ./faser_digi.py filepath runtype
+# 
+# filepath - fully qualified path, including url if needed, to the input HITS file
+#   example: "root://eospublic.cern.ch//eos/experiment/faser/sim/GeniePilot/HITS/1/faser.150fbInv.1.001.HITS.pool.root"
+# 
+# For MDC, we assume this is TI12 geometry
+#
+import sys
+import argparse
+
+parser = argparse.ArgumentParser(description="Run FASER reconstruction")
+
+parser.add_argument("file_path",
+                    help="Fully qualified path of the raw input file")
+parser.add_argument("run_type", nargs="?", default="",
+                    help="Specify run type (if it can't be parsed from path)")
+parser.add_argument("-t", "--tag", default="",
+                    help="Specify digi tag (to append to output filename)")
+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)
+
+# runtype has been provided
+if len(args.run_type) > 0:
+    runtype=args.run_type
+
+else:
+    runtype = "TI12MC"
+    print(f"Assuming {runtype} geometry for MDC")
+
+print(f"Starting digitization of {filepath.name} with type {runtype}")
+if args.nevents > 0:
+    print(f"Reconstructing {args.nevents} events by command-line option")
+
+# Start digitization
+
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+from AthenaCommon.Constants import VERBOSE, INFO
+
+from AthenaCommon.Configurable import Configurable
+from CalypsoConfiguration.AllConfigFlags import ConfigFlags
+
+Configurable.configurableRun3Behavior = True
+    
+# Flags for this job
+ConfigFlags.Input.isMC = True                    # Needed to bypass autoconfig
+ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"   # Use MC conditions for now
+
+ConfigFlags.Input.ProjectName = "mc20"
+ConfigFlags.GeoModel.Align.Dynamic    = False
+ConfigFlags.Beam.NumberOfCollisions = 0.
+ConfigFlags.Digitization.TruthOutput = True
+
+# TI12 old geometry
+if runtype == "TI12OldMC":
+    ConfigFlags.GeoModel.FaserVersion = "FASER-01" 
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-01"
+
+# Testbeam setup 
+elif runtype == "TestBeamMC" :
+    ConfigFlags.GeoModel.FaserVersion = "FASER-TB00" 
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-TB00"
+
+# New TI12 geometry (ugh)
+elif runtype == "TI12MC":
+    ConfigFlags.GeoModel.FaserVersion = "FASERNU-02" 
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+
+else:
+    print("Invalid run type found:", runtype)
+    print("Specify correct type or update list")
+    sys.exit(-1)
+
+
+# Must use original input string here, as pathlib mangles double // in path names
+ConfigFlags.Input.Files = [ args.file_path ]
+
+filestem = filepath.stem
+# Remove any filetype modifier
+if filestem[-5:] == "-HITS":
+    filestem = filestem[:-5]
+
+if len(args.tag) > 0:
+    filestem += f"-{args.tag}"
+
+ConfigFlags.Output.RDOFileName = f"{filestem}-RDO.root"
+
+#
+# Play around with this?
+# ConfigFlags.Concurrency.NumThreads = 2
+# ConfigFlags.Concurrency.NumConcurrentEvents = 2
+ConfigFlags.lock()
+
+#
+# Configure components
+from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
+from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg
+    
+acc = MainServicesCfg(ConfigFlags)
+acc.merge(PoolReadCfg(ConfigFlags))
+acc.merge(PoolWriteCfg(ConfigFlags))
+
+#
+# Needed, or move to MainServicesCfg?
+from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg
+acc.merge(FaserGeometryCfg(ConfigFlags))
+
+# Set up algorithms
+from FaserSCT_Digitization.FaserSCT_DigitizationConfigNew import FaserSCT_DigitizationCfg
+acc.merge(FaserSCT_DigitizationCfg(ConfigFlags))
+
+from CaloDigiAlgs.CaloDigiAlgsConfig import CaloWaveformDigitizationCfg
+acc.merge(CaloWaveformDigitizationCfg(ConfigFlags))
+
+from ScintDigiAlgs.ScintDigiAlgsConfig import ScintWaveformDigitizationCfg
+acc.merge(ScintWaveformDigitizationCfg(ConfigFlags))
+
+# Configure verbosity    
+# ConfigFlags.dump()
+if args.verbose:
+    acc.foreach_component("*").OutputLevel = VERBOSE
+
+else:
+    acc.foreach_component("*").OutputLevel = INFO
+
+acc.foreach_component("*ClassID*").OutputLevel = INFO
+
+acc.getService("MessageSvc").Format = "% F%40W%S%7W%R%T %0W%M"
+
+# Execute and finish
+sys.exit(int(acc.run(maxEvents=args.nevents).isFailure()))
diff --git a/Control/CalypsoExample/Digitization/scripts/submit_faserMDC_digi.sh b/Control/CalypsoExample/Digitization/scripts/submit_faserMDC_digi.sh
new file mode 100755
index 00000000..c03a4329
--- /dev/null
+++ b/Control/CalypsoExample/Digitization/scripts/submit_faserMDC_digi.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+# Used with a condor file to submit to vanilla universe
+#
+# Usage:
+# submit_faserMDC_digi.sh filepath [release_directory] [working_directory] 
+# 
+# filepath - full file name (with path)
+# release_directory - optional path to release install directory (default pwd)
+# working_directory - optional path to output directory location (default pwd)
+#
+# 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???? it will be passed to the job
+#
+#----------------------------------------
+#
+# Parse command-line options
+file_path=${1}
+release_directory=${2}
+working_directory=${3}
+#
+# Set defaults if arguments aren't provided
+if [ -z "$file_path" ]
+then
+  echo "No file specified!"
+  echo "Usage: submit_faserMDC_digi.sh file [release dir] [output dir]"
+  exit 1
+fi
+#
+if [ -z "$release_directory" ]
+then
+  release_directory=`pwd`
+fi
+#
+if [ -z "$working_directory" ]
+then
+  working_directory=`pwd`
+fi
+#
+starting_directory=`pwd`
+#
+# Now extract the run number and file stem
+#
+# First, get the filename
+file_name=$(basename "$file_path")
+# 
+# Now split based on '.' to get stem
+defaultIFS=$IFS
+IFS='.'
+read file_stem ext <<< "$file_name"
+#
+# Finally extract the run number
+IFS='-'
+# Read the split words into an array based on delimiter
+read faser short run_number segment <<< "$file_stem"
+#
+# Set the IFS delimeter back or else echo doesn't work...
+IFS=$defaultIFS
+#
+# Make output directory if needed
+output_directory="$working_directory/$run_number"
+mkdir -p "$output_directory"
+#
+# This magic redirects everything in this script to our log file
+exec >& "$output_directory/$file_stem.log"
+echo `date` - $HOSTNAME
+echo "File: $file_name"
+echo "Release: $release_directory"
+echo "Output: $output_directory"
+echo "Starting: $starting_directory"
+#
+# 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"
+asetup
+source build/x8*/setup.sh
+#
+# Do this by hand
+# asetup --input="$release_directory/calypso/asetup.faser" Athena,22.0.49
+# source "$release_directory/build/x8*/setup.sh"
+#
+#
+# Try to find a release tag
+cd calypso
+recotag=`git describe`
+if [[ "$recotag" == "reco/r"???? ]]; then
+  tag=`echo "$recotag" | cut -c 6-11`
+  echo "Found reco tag: $tag"
+fi
+if [[ "$recotag" == "digi/d"???? ]]; then
+  tag=`echo "$recotag" | cut -c 6-11`
+  echo "Found reco tag: $tag"
+fi
+#
+# 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
+    faserMDC_digi.py "$file_path"
+else
+    faserMDC_digi.py "--tag=$tag" "$file_path"
+fi
+#
+# Print out ending time
+date
diff --git a/Control/CalypsoExample/Generation/CMakeLists.txt b/Control/CalypsoExample/Generation/CMakeLists.txt
new file mode 100644
index 00000000..4ffb523f
--- /dev/null
+++ b/Control/CalypsoExample/Generation/CMakeLists.txt
@@ -0,0 +1,12 @@
+################################################################################
+# Package: Generation
+################################################################################
+
+# Declare the package name:
+atlas_subdir( Generation )
+
+# Install files from the package:
+atlas_install_python_modules( python/*.py )
+atlas_install_scripts( scripts/*.sh scripts/*.py )
+
+
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_emi_100GeV-101104.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_emi_100GeV-101104.json
new file mode 100644
index 00000000..6a457b29
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_emi_100GeV-101104.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 100,
+    "mass": 0.511,
+    "maxE": 100.0,
+    "minE": 100.0,
+    "pid":  11,
+    "radius": 100.0,
+    "run": 101104,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_emi_100GeV",
+    "zpos": -1000.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_emi_logE-101102.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_emi_logE-101102.json
new file mode 100644
index 00000000..06e0725e
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_emi_logE-101102.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 100,
+    "mass": 0.511,
+    "maxE": 1000.0,
+    "minE": 10.0,
+    "pid": 11,
+    "radius": 100.0,
+    "run": 101102,
+    "sampler": "log",
+    "segment": 0,
+    "short": "MDC_PG_emi_logE",
+    "zpos": -1000.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_epl_100GeV-101103.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_epl_100GeV-101103.json
new file mode 100644
index 00000000..02de3383
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_epl_100GeV-101103.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 100,
+    "mass": 0.511,
+    "maxE": 100.0,
+    "minE": 100.0,
+    "pid": -11,
+    "radius": 100.0,
+    "run": 101103,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_epl_100GeV",
+    "zpos": -1000.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_epl_logE-101101.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_epl_logE-101101.json
new file mode 100644
index 00000000..fda3f14a
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_epl_logE-101101.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 100,
+    "mass": 0.511,
+    "maxE": 1000.0,
+    "minE": 10.0,
+    "pid": -11,
+    "radius": 100.0,
+    "run": 101101,
+    "sampler": "log",
+    "segment": 0,
+    "short": "MDC_PG_epl_logE",
+    "zpos": -1000.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_gam_100GeV-102201.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_gam_100GeV-102201.json
new file mode 100644
index 00000000..11b14b31
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_gam_100GeV-102201.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 100,
+    "mass": 0.0,
+    "maxE": 100.0,
+    "minE": 100.0,
+    "pid":  22,
+    "radius": 100.0,
+    "run": 102201,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_gam_100GeV",
+    "zpos": -1000.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_mumi_logE-101302.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_mumi_logE-101302.json
new file mode 100644
index 00000000..7c9e9631
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_mumi_logE-101302.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 1000,
+    "mass": 105.66,
+    "maxE": 2000.0,
+    "minE": 10.0,
+    "pid": 13,
+    "radius": 100.0,
+    "run": 101302,
+    "sampler": "log",
+    "segment": 0,
+    "short": "MDC_PG_mumi_logE",
+    "zpos": null
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_mupl_logE-101301.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_mupl_logE-101301.json
new file mode 100644
index 00000000..47f0f142
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_mupl_logE-101301.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 1000,
+    "mass": 105.66,
+    "maxE": 2000.0,
+    "minE": 10.0,
+    "pid": -13,
+    "radius": 100.0,
+    "run": 101301,
+    "sampler": "log",
+    "segment": 0,
+    "short": "MDC_PG_mupl_logE",
+    "zpos": null
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pimi_100GeV-121102.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pimi_100GeV-121102.json
new file mode 100644
index 00000000..cd8abc7e
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pimi_100GeV-121102.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 500,
+    "mass": 139.6,
+    "maxE": 100.0,
+    "minE": 100.0,
+    "pid":  -211,
+    "radius": 100.0,
+    "run": 121102,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_pimi_100GeV",
+    "zpos": -1000.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pipl_100GeV-121101.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pipl_100GeV-121101.json
new file mode 100644
index 00000000..9874ba20
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pipl_100GeV-121101.json
@@ -0,0 +1,13 @@
+{
+    "file_length": 500,
+    "mass": 139.6,
+    "maxE": 100.0,
+    "minE": 100.0,
+    "pid":  211,
+    "radius": 100.0,
+    "run": 121101,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_pipl_100GeV",
+    "zpos": -1000.0
+}
diff --git a/Control/CalypsoExample/Generation/python/__init__.py b/Control/CalypsoExample/Generation/python/__init__.py
new file mode 100644
index 00000000..f0782136
--- /dev/null
+++ b/Control/CalypsoExample/Generation/python/__init__.py
@@ -0,0 +1,2 @@
+# Generation
+
diff --git a/Control/CalypsoExample/Generation/python/faserMDC_pgparser.py b/Control/CalypsoExample/Generation/python/faserMDC_pgparser.py
new file mode 100644
index 00000000..7b406cc8
--- /dev/null
+++ b/Control/CalypsoExample/Generation/python/faserMDC_pgparser.py
@@ -0,0 +1,90 @@
+#
+# Copyright (C) 2022 CERN for the benefit of the ATLAS collaboration
+# Copyright (C) 2022 CERN for the benefit of the FASER collaboration
+#
+# Parser function for MDC particle gun samples
+#
+def faserMDC_pgparser():
+
+    import sys
+    import json
+    import argparse
+
+    parser = argparse.ArgumentParser(description="Run FASER ParticleGun Simulation")
+
+    parser.add_argument("--conf", action='append',
+                        help="Specify configuration file with default values")
+    parser.add_argument("--run", default=123456, type=int,
+                        help="Run number to generate")
+    parser.add_argument("--segment", default=00000, type=int,
+                        help="Segment number to generate")
+    parser.add_argument("--file_length", default=1000, type=int,
+                        help="Total events per file segement")
+
+    parser.add_argument("--short", default="MDCPG_logE",
+                        help="Short description for filename")
+    parser.add_argument("--tag", default=None,
+                        help="Generator tag (g0000)")
+
+    parser.add_argument("--pid", default=13, type=int,
+                        help="Specify PDG ID of particle (note plus/minus different)")
+    parser.add_argument("--mass", default=105.66, type=float,
+                        help="Specify particle mass (in MeV)")
+    parser.add_argument("--radius", default=100., type=float,
+                        help="Specify radius (in mm)")
+    parser.add_argument("--zpos", default=None, type=float,
+                        help="Specify z position of particles (in mm) (helpful to avoid FASERnu)")
+
+    parser.add_argument("--sampler", default="log",
+                        help="Specify energy sampling (log, lin, const)")
+    parser.add_argument("--minE", default=10., type=float,
+                        help="Minimum energy in GeV (for log or lin sampler)")
+    parser.add_argument("--maxE", default=1000., type=float,
+                        help="Maximum energy (or constant) in GeV")
+
+    parser.add_argument("--nevts", default=-1, type=int,
+                        help="Number of events to generate (for debugging)")
+    parser.add_argument("--dump", action='store_true',
+                        help="Write out full configuration")
+    parser.add_argument("--noexec", action='store_true',
+                        help="Exit after parsing configuration (no execution)")
+
+    args = parser.parse_args()
+
+    # Get defaults
+    if args.conf is not None:
+        for conf_fname in args.conf:
+            with open(conf_fname, 'r') as f:
+                parser.set_defaults(**json.load(f))
+
+        # Reload arguments to override config file with command line
+        args = parser.parse_args()
+
+    # Print out configuration if requested
+    if args.dump:
+        tmp_args = vars(args).copy()
+        del tmp_args['dump']  # Don't dump the dump value
+        del tmp_args['conf']  # Don't dump the conf file name either
+        del tmp_args['nevts'] # Debugging, not part of configuration
+        del tmp_args['noexec'] # Debugging, not part of configuration
+        print("Configuration:")
+        print(json.dumps(tmp_args, indent=4, sort_keys=False))
+
+    if args.noexec:
+        sys.exit(0)
+
+    #
+    # Add some derived quantities
+    #
+
+    # Create the file name also (could add gentag here)
+        args.outfile = f"FaserMC-{args.short}-{args.run:06}-{args.segment:05}"
+
+    if args.tag:
+        args.outfile += f"-{args.tag}"
+
+    args.outfile += "-HITS.root"
+
+    return args
+
+# All done
diff --git a/Control/CalypsoExample/Generation/scripts/faserMDC_particlegun.py b/Control/CalypsoExample/Generation/scripts/faserMDC_particlegun.py
new file mode 100755
index 00000000..943278e4
--- /dev/null
+++ b/Control/CalypsoExample/Generation/scripts/faserMDC_particlegun.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+"""
+Produce particle gun samples
+Derived from G4FaserAlgConfigNew
+
+Usage:
+faserMDC_particlegun.py --conf=<config_file>
+
+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
+#
+    from Generation.faserMDC_pgparser import faserMDC_pgparser
+    args = faserMDC_pgparser()
+#
+# Figure out events to run and skip
+#
+    nskipped = args.segment*args.file_length
+    if args.nevts > 0:
+        nevents = args.nevts
+    else:
+        nevents = args.file_length
+#
+# Print out what we are doing
+#
+    print(f"Generating {nevents} in file {args.outfile}")
+#
+# 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
+    ConfigFlags.Exec.MaxEvents = nevents
+    ConfigFlags.Exec.SkipEvents = nskipped
+    from AthenaConfiguration.Enums import ProductionStep
+    ConfigFlags.Common.ProductionStep = ProductionStep.Simulation
+#
+# All these must be specified to avoid auto-configuration
+#
+    ConfigFlags.Input.RunNumber = [args.run] #Isn't updating - todo: investigate
+    ConfigFlags.Input.OverrideRunNumber = True
+    ConfigFlags.Input.LumiBlockNumber = [(args.segment+1)]
+    ConfigFlags.Input.isMC = True
+#
+# Output file name
+# 
+
+    ConfigFlags.Output.HITSFileName = args.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
+    ConfigFlags.addFlag("Sim.Gun",{"Generator" : "SingleParticle"})  # Property bag for particle gun keyword:argument pairs
+    ConfigFlags.addFlag("Sim.Beam.xangle", 0)  # Potential beam crossing angles
+    ConfigFlags.addFlag("Sim.Beam.yangle", 0)    
+
+    ConfigFlags.GeoModel.FaserVersion = "FASERNU-02"             # Geometry set-up
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Conditions set-up
+    ConfigFlags.addFlag("Input.InitialTimeStamp", 0)             # To avoid autoconfig 
+    ConfigFlags.GeoModel.Align.Dynamic = False
+
+#
+# Preset particle gun parameters
+#
+    from math import atan
+    from AthenaCommon.SystemOfUnits import GeV, TeV, cm, m
+    from AthenaCommon.PhysicalConstants import pi
+
+    import ParticleGun as PG
+    ConfigFlags.Sim.Gun = {
+        "Generator" : "SingleParticle", 
+        "pid" : args.pid, "mass" : args.mass, 
+        "theta" :  PG.GaussianSampler(0, atan((10*cm)/(7*m)), oneside = True), 
+        "phi" : [0, 2*pi], "radius" : args.radius, 
+        "randomSeed" : args.outfile }
+
+    if args.zpos:
+        ConfigFlags.Sim.Gun["z"] = args.zpos
+
+    # Determine energy sampling
+    if args.sampler == "lin":
+        ConfigFlags.Sim.Gun["energy"] = PG.UniformSampler(args.minE*GeV, args.maxE*GeV)
+    elif args.sampler == "log":
+        ConfigFlags.Sim.Gun["energy"] = PG.LogSampler(args.minE*GeV, args.maxE*GeV)
+    elif args.sampler == "const":
+        ConfigFlags.Sim.Gun["energy"] = PG.ConstSampler(args.maxE*GeV)
+    else:
+        print(f"Sampler {args.sampler} not known!")
+        sys.exit(1)
+
+    # import sys
+    # ConfigFlags.fillFromArgs(sys.argv[1:])
+
+#
+# MDC geometry configuration
+#
+    detectors = ['Veto', 'Preshower', 'FaserSCT', 'Ecal', 'Trigger', 'Dipole', 'Emulsion']
+#
+# 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)
+#
+# Configure the particle gun as requested, or using defaults
+#
+
+#
+# Particle gun generators - the generator, energy, angle, particle type, position, etc can be modified by passing keyword arguments
+#
+    from FaserParticleGun.FaserParticleGunConfig import FaserParticleGunCfg
+    cfg.merge(FaserParticleGunCfg(ConfigFlags))
+    from McEventSelector.McEventSelectorConfig import McEventSelectorCfg
+    cfg.merge(McEventSelectorCfg(ConfigFlags))
+
+#
+# Output file
+#
+    from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg
+    cfg.merge(PoolWriteCfg(ConfigFlags))
+
+#
+# Shift LOS
+#
+
+    if ConfigFlags.Sim.Beam.xangle or ConfigFlags.Sim.Beam.yangle:
+        MCEventKey = "BeamTruthEventShifted"
+        import McParticleEvent.Pythonizations
+        from GeneratorUtils.ShiftLOSConfig import ShiftLOSCfg
+        cfg.merge(ShiftLOSCfg(ConfigFlags, OutputMCEventKey = MCEventKey,
+                              xcross = ConfigFlags.Sim.Beam.xangle, ycross = ConfigFlags.Sim.Beam.yangle))
+    else:    
+        MCEventKey = "BeamTruthEvent"
+    
+#
+# Add the G4FaserAlg
+#
+    from G4FaserAlg.G4FaserAlgConfigNew import G4FaserAlgCfg
+    cfg.merge(G4FaserAlgCfg(ConfigFlags, InputTruthCollection = MCEventKey))
+#
+# 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
+#
+
+    #cfg.foreach_component("*").OutputLevel = "INFO"  # Use warning for production
+
+    sc = cfg.run()
+
+    b = time.time()
+    log.info("Run G4FaserAlg in " + str(b-a) + " seconds")
+#
+# Success should be 0
+#
+    sys.exit(not sc.isSuccess())
+
diff --git a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
new file mode 100755
index 00000000..ee38005d
--- /dev/null
+++ b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+"""
+Produce particle gun samples
+Derived from G4FaserAlgConfigNew
+This is a general low-level script, 
+although this could be useful for testing.
+
+Usage:
+faser_particlegun.py <options>
+
+Control using command-line options:
+--evtMax=1000
+--skipEvt=1000
+
+Output.HITSFileName=<name>
+Sim.Gun='{"pid" : 11, "z": -1500.}'
+
+Copyright (C) 2002-2021 CERN for the benefit of the ATLAS and FASER collaborations
+"""
+
+if __name__ == '__main__':
+
+    import time
+    a = time.time()
+#
+# 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
+    ConfigFlags.Exec.MaxEvents = 10  # can be overridden from command line with --evtMax=<number>
+    ConfigFlags.Exec.SkipEvents = 0  # can be overridden from command line with --skipEvt=<number>
+    from AthenaConfiguration.Enums import ProductionStep
+    ConfigFlags.Common.ProductionStep = ProductionStep.Simulation
+#
+# All these must be specified to avoid auto-configuration
+#
+    ConfigFlags.Input.RunNumber = [12345] #Isn't updating - todo: investigate
+    ConfigFlags.Input.OverrideRunNumber = True
+    ConfigFlags.Input.LumiBlockNumber = [1]
+    ConfigFlags.Input.isMC = True
+#
+# Output file name
+# 
+    ConfigFlags.Output.HITSFileName = "my.HITS.pool.root" # can be overridden from command line with Output.HITSFileName=<name>
+#
+# 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
+    ConfigFlags.addFlag("Sim.Gun",{"Generator" : "SingleParticle"})  # Property bag for particle gun keyword:argument pairs
+    ConfigFlags.addFlag("Sim.Beam.xangle", 0)  # Potential beam crossing angles
+    ConfigFlags.addFlag("Sim.Beam.yangle", 0)    
+
+    ConfigFlags.GeoModel.FaserVersion = "FASERNU-02"             # Geometry set-up
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Conditions set-up
+    ConfigFlags.addFlag("Input.InitialTimeStamp", 0)             # To avoid autoconfig 
+    ConfigFlags.GeoModel.Align.Dynamic = False
+
+#
+# Preset particle gun parameters
+#
+    from math import atan
+    from AthenaCommon.SystemOfUnits import GeV, TeV, cm, m
+    from AthenaCommon.PhysicalConstants import pi
+
+    # 11 - electron, 13 - muon, 22 - photon
+    import ParticleGun as PG
+    ConfigFlags.Sim.Gun = {
+        "Generator" : "SingleParticle", "pid" : 13, 
+        "energy" : PG.LogSampler(10*GeV, 1*TeV), 
+        "theta" :  PG.GaussianSampler(0, atan((10*cm)/(7*m)), oneside = True), 
+        "phi" : [0, 2*pi], "mass" : 0.511, "radius" : -10*cm, #"z": -2.0*m,
+        "randomSeed" : 12345}
+
+#
+# Command-line overrides
+#
+# These have the format: Sim.Gun='{"pid" : 11}'
+# Filename: Output.HITSFileName="test.muon.001.root"
+# Also number of events: --evtMax 100
+# Starting at z = -1.5m (-1500.) will miss the veto (for electrons)
+
+    import sys
+    ConfigFlags.fillFromArgs(sys.argv[1:])
+
+#
+# By being a little clever, we can steer the geometry setup from the command line using GeoModel.FaserVersion
+#
+# MDC configuration
+#
+    detectors = ['Veto', 'Preshower', 'FaserSCT', 'Ecal', 'Trigger', 'Dipole', 'Emulsion']
+#
+# 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)
+#
+# Configure the particle gun as requested, or using defaults
+#
+
+#
+# Particle gun generators - the generator, energy, angle, particle type, position, etc can be modified by passing keyword arguments
+#
+    from FaserParticleGun.FaserParticleGunConfig import FaserParticleGunCfg
+    cfg.merge(FaserParticleGunCfg(ConfigFlags))
+    from McEventSelector.McEventSelectorConfig import McEventSelectorCfg
+    cfg.merge(McEventSelectorCfg(ConfigFlags))
+
+#
+# Output file
+#
+    from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg
+    cfg.merge(PoolWriteCfg(ConfigFlags))
+
+#
+# Shift LOS
+#
+
+    if ConfigFlags.Sim.Beam.xangle or ConfigFlags.Sim.Beam.yangle:
+        MCEventKey = "BeamTruthEventShifted"
+        import McParticleEvent.Pythonizations
+        from GeneratorUtils.ShiftLOSConfig import ShiftLOSCfg
+        cfg.merge(ShiftLOSCfg(ConfigFlags, OutputMCEventKey = MCEventKey,
+                              xcross = ConfigFlags.Sim.Beam.xangle, ycross = ConfigFlags.Sim.Beam.yangle))
+    else:    
+        MCEventKey = "BeamTruthEvent"
+    
+#
+# Add the G4FaserAlg
+#
+    from G4FaserAlg.G4FaserAlgConfigNew import G4FaserAlgCfg
+    cfg.merge(G4FaserAlgCfg(ConfigFlags, InputTruthCollection = MCEventKey))
+#
+# 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
+#
+
+    #cfg.foreach_component("*").OutputLevel = "INFO"  # Use warning for production
+
+    sc = cfg.run()
+
+    b = time.time()
+    log.info("Run G4FaserAlg in " + str(b-a) + " seconds")
+#
+# Success should be 0
+#
+    sys.exit(not sc.isSuccess())
+
diff --git a/Control/CalypsoExample/Generation/scripts/submit_faserMDC_particlegun.sh b/Control/CalypsoExample/Generation/scripts/submit_faserMDC_particlegun.sh
new file mode 100755
index 00000000..1b676699
--- /dev/null
+++ b/Control/CalypsoExample/Generation/scripts/submit_faserMDC_particlegun.sh
@@ -0,0 +1,129 @@
+#!/bin/bash
+# Used with a condor file to submit to vanilla universe
+#
+# Usage:
+# submit_faserMDC_particlegun.sh config_file segment [release_directory] [working_directory] 
+# 
+# config_file - full file name (with path)
+# segment - segment number (file segment)
+# release_directory - optional path to release install directory (default pwd)
+# working_directory - optional path to output directory location (default pwd)
+#
+# 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???? it will be passed to the job
+#
+#----------------------------------------
+#
+# Parse command-line options
+config_path=${1}
+segment=${2}
+release_directory=${3}
+working_directory=${4}
+#
+# Set defaults if arguments aren't provided
+if [ -z "$config_path" ]
+then
+  echo "No config_path specified!"
+  echo "Usage: submit_faserMDC_particlegun.sh config_file segment [release dir] [output dir]"
+  exit 1
+fi
+#
+if [ -z "$segment" ]
+then
+  segment=0
+fi
+#
+if [ -z "$release_directory" ]
+then
+  release_directory=`pwd`
+fi
+#
+if [ -z "$working_directory" ]
+then
+  working_directory=`pwd`
+fi
+# 
+# Apply padding to segment number
+printf -v seg_str "%05d" $segment
+#
+starting_directory=`pwd`
+#
+# Now extract the file stem
+#
+# First, get the filename
+config_file=$(basename "$config_path")
+# 
+# Now split based on '.' to get stem
+defaultIFS=$IFS
+IFS='.'
+read config_file_stem ext <<< "$config_file"
+# Set the IFS delimeter back or else echo doesn't work...
+IFS=$defaultIFS
+#
+# Make output directory if needed
+output_directory="$working_directory/${config_file_stem}"
+mkdir -p "$output_directory"
+#
+# This magic redirects everything in this script to our log file
+exec >& "$output_directory/${config_file_stem}-${seg_str}.log"
+echo `date` - $HOSTNAME
+echo "File: $config_file"
+echo "Segment: $seg_str"
+echo "Release: $release_directory"
+echo "Output: $output_directory"
+echo "Starting: $starting_directory"
+#
+# 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"
+asetup
+source build/x8*/setup.sh
+#
+# Do this by hand
+# asetup --input="$release_directory/calypso/asetup.faser" Athena,22.0.49
+# source "$release_directory/build/x8*/setup.sh"
+#
+#
+# Try to find a release tag
+cd calypso
+gentag=`git describe`
+if [[ "$gentag" == "gen/g"???? ]]; then
+  gtag=`echo "$gentag" | cut -c 5-10`
+  echo "Found gen tag: $gtag"
+fi
+#
+# 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 "${config_file_stem}-${seg_str}" ]]; then
+    echo "Directory ${config_file_stem}-${seg_str} already exists"
+else
+    mkdir "${config_file_stem}-${seg_str}"
+fi
+cd "${config_file_stem}-${seg_str}"
+#
+# Run job
+if [[ -z "$gtag" ]]; then
+    faserMDC_particlegun.py  "--conf=$config_path" "--segment=$seg_str"
+else
+    faserMDC_particlegun.py  "--conf=$config_path" "--segment=$seg_str" "--tag=$gtag"
+fi
+#
+# Print out ending time
+date
diff --git a/Control/CalypsoExample/Reconstruction/scripts/faserMDC_reco.py b/Control/CalypsoExample/Reconstruction/scripts/faserMDC_reco.py
new file mode 100755
index 00000000..30b40afc
--- /dev/null
+++ b/Control/CalypsoExample/Reconstruction/scripts/faserMDC_reco.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
+# Run with:
+# ./faser_reco.py filepath [runtype]
+# 
+# filepath - fully qualified path, including url if needed, to the input raw data file
+#   example: "root://hepatl30//atlas/local/torrence/faser/commissioning/TestBeamData/Run-004150/Faser-Physics-004150-00000.raw"
+# 
+# runtype - optionally specify the data type (TI12Data, TI12Data02, or TestBeamData).
+#   In a normal file system location, this will be extracted from the directory name,
+#   but runtype will override this assignment. 
+#   TI12Data02 is needed for the IFT geometry.  
+#   MDC will assume this geometry.
+#
+import sys
+import argparse
+
+parser = argparse.ArgumentParser(description="Run FASER reconstruction")
+
+parser.add_argument("file_path",
+                    help="Fully qualified path of the raw input file")
+parser.add_argument("run_type", nargs="?", default="",
+                    help="Specify run type (if it can't be parsed from path)")
+parser.add_argument("-r", "--reco", default="",
+                    help="Specify reco tag (to append to output filename)")
+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)
+
+# runtype has been provided
+if len(args.run_type) > 0:
+    runtype=args.run_type
+
+# Assume based on MDC reco
+else:
+    runtype = "TI12Data02"
+
+# Assume this is MC
+args.isMC = True
+
+print(f"Starting reconstruction of {filepath.name} with type {runtype}")
+if args.nevents > 0:
+    print(f"Reconstructing {args.nevents} events by command-line option")
+
+# Start reconstruction
+
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+from AthenaCommon.Constants import VERBOSE, INFO
+
+from AthenaCommon.Configurable import Configurable
+from CalypsoConfiguration.AllConfigFlags import ConfigFlags
+
+Configurable.configurableRun3Behavior = True
+    
+# Flags for this job
+ConfigFlags.Input.isMC = args.isMC               # Needed to bypass autoconfig
+ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"   # Use MC conditions for now
+
+ConfigFlags.Input.ProjectName = "data20"
+ConfigFlags.GeoModel.Align.Dynamic    = False
+
+# TI12 Cosmics geometry
+if runtype == "TI12Data":
+    ConfigFlags.GeoModel.FaserVersion = "FASER-01" 
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-01"
+
+# Testbeam setup 
+elif runtype == "TestBeamData" or runtype == "TestBeam2021":
+    ConfigFlags.GeoModel.FaserVersion = "FASER-TB00" 
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-TB00"
+
+# New TI12 geometry (ugh)
+elif runtype == "TI12Data02":
+    ConfigFlags.GeoModel.FaserVersion = "FASER-02" 
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+
+else:
+    print("Invalid run type found:", runtype)
+    print("Specify correct type or update list")
+    sys.exit(-1)
+
+
+# Must use original input string here, as pathlib mangles double // in path names
+ConfigFlags.Input.Files = [ args.file_path ]
+
+filestem = filepath.stem
+if len(args.reco) > 0:
+    filestem += f"-{args.reco}"
+
+ConfigFlags.addFlag("Output.xAODFileName", f"{filestem}-xAOD.root")
+ConfigFlags.Output.ESDFileName = f"{filestem}-ESD.root"
+
+#
+# Play around with this?
+# ConfigFlags.Concurrency.NumThreads = 2
+# ConfigFlags.Concurrency.NumConcurrentEvents = 2
+ConfigFlags.lock()
+
+#
+# Configure components
+from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
+from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg
+    
+acc = MainServicesCfg(ConfigFlags)
+acc.merge(PoolWriteCfg(ConfigFlags))
+
+#
+# Set up RAW data access
+
+if args.isMC:
+    from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+    acc.merge(PoolReadCfg(ConfigFlags))
+else:    
+    from FaserByteStreamCnvSvc.FaserByteStreamCnvSvcConfig import FaserByteStreamCnvSvcCfg
+    acc.merge(FaserByteStreamCnvSvcCfg(ConfigFlags))
+
+#
+# Needed, or move to MainServicesCfg?
+from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg
+acc.merge(FaserGeometryCfg(ConfigFlags))
+
+# Set up algorithms
+from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionCfg    
+acc.merge(WaveformReconstructionCfg(ConfigFlags))
+
+# Not ready for primetime
+# from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionCfg
+# acc.merge(CalorimeterReconstructionCfg(ConfigFlags))
+
+# Tracker clusters
+from TrackerPrepRawDataFormation.TrackerPrepRawDataFormationConfig import FaserSCT_ClusterizationCfg
+acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags))
+
+# SpacePoints
+from TrackerSpacePointFormation.TrackerSpacePointFormationConfig import TrackerSpacePointFinderCfg
+acc.merge(TrackerSpacePointFinderCfg(ConfigFlags))
+
+print("Configuring TrackerSegmentFit (new)")
+# Try Dave's new fitter
+from TrackerSegmentFit.TrackerSegmentFitConfig import SegmentFitAlgCfg
+acc.merge(SegmentFitAlgCfg(ConfigFlags))
+
+#
+# Configure output
+from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+itemList = [ "xAOD::EventInfo#*"
+             , "xAOD::EventAuxInfo#*"
+             , "xAOD::FaserTriggerData#*"
+             , "xAOD::FaserTriggerDataAux#*"
+             , "FaserSCT_RDO_Container#*"
+             , "Tracker::FaserSCT_ClusterContainer#*"
+             , "FaserSCT_SpacePointContainer#*"
+             #, "FaserSCT_SpacePointOverlapCollection#*"
+             , "TrackCollection#*"
+]
+acc.merge(OutputStreamCfg(ConfigFlags, "xAOD", itemList))
+
+# Waveform reconstruction output
+from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionOutputCfg    
+acc.merge(WaveformReconstructionOutputCfg(ConfigFlags))
+
+# Calorimeter reconstruction output
+# from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionOutputCfg
+# acc.merge(CalorimeterReconstructionOutputCfg(ConfigFlags))
+
+# Check what we have
+print( "Writing out xAOD objects:" )
+print( acc.getEventAlgo("OutputStreamxAOD").ItemList )
+
+# 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
+
+# Configure verbosity    
+# ConfigFlags.dump()
+if args.verbose:
+    acc.foreach_component("*").OutputLevel = VERBOSE
+
+    #acc.getService("FaserByteStreamInputSvc").DumpFlag = True
+    #acc.getService("FaserEventSelector").OutputLevel = VERBOSE
+    #acc.getService("FaserByteStreamInputSvc").OutputLevel = VERBOSE
+    #acc.getService("FaserByteStreamCnvSvc").OutputLevel = VERBOSE
+    #acc.getService("FaserByteStreamAddressProviderSvc").OutputLevel = VERBOSE
+
+else:
+    acc.foreach_component("*").OutputLevel = INFO
+
+acc.foreach_component("*ClassID*").OutputLevel = INFO
+
+acc.getService("MessageSvc").Format = "% F%40W%S%7W%R%T %0W%M"
+
+# Execute and finish
+sys.exit(int(acc.run(maxEvents=args.nevents).isFailure()))
diff --git a/Control/CalypsoExample/Reconstruction/scripts/submit_faserMDC_reco.sh b/Control/CalypsoExample/Reconstruction/scripts/submit_faserMDC_reco.sh
new file mode 100755
index 00000000..b969fcf2
--- /dev/null
+++ b/Control/CalypsoExample/Reconstruction/scripts/submit_faserMDC_reco.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+# Used with a condor file to submit to vanilla universe
+#
+# Usage:
+# submit_faser_reco.sh file_path [release_directory] [working_directory] [nevents]
+# 
+# file_path - full file name (with path)
+# release_directory - optional path to release install directory (default pwd)
+# working_directory - optional path to output directory location (default pwd)
+# nevents - optional number of events to process (default: -1 - all)
+#
+# 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 reco/r???? it will be passed to the reco job
+#
+#----------------------------------------
+#
+# Parse command-line options
+file_path=${1}
+release_directory=${2}
+working_directory=${3}
+nevents=${4}
+#
+# Set defaults if arguments aren't provided
+if [ -z "$file_path" ]
+then
+  echo "No file_path specified!"
+  exit 1
+fi
+#
+if [ -z "$release_directory" ]
+then
+  release_directory=`pwd`
+fi
+#
+if [ -z "$working_directory" ]
+then
+  working_directory=`pwd`
+fi
+#
+if [ -z "$nevents" ]
+then
+  nevents="-1"
+fi
+#
+starting_directory=`pwd`
+#
+# Now extract the run number and file stem
+#
+# First, get the filename
+file_name=$(basename "$file_path")
+# 
+# Now split based on '.' to get stem
+defaultIFS=$IFS
+IFS='.'
+read file_stem ext <<< "$file_name"
+#
+# Finally extract the run number
+IFS='-'
+# Read the split words into an array based on delimiter
+read faser desc run_number segment <<< "$file_stem"
+#
+# Set the IFS delimeter back or else echo doesn't work...
+IFS=$defaultIFS
+#
+# Make output directory if needed
+output_directory="$working_directory/${run_number}"
+mkdir -p "$output_directory"
+#
+# This magic redirects everything in this script to our log file
+exec >& "$output_directory/$file_stem.log"
+echo `date` - $HOSTNAME
+echo "File: $file_name"
+echo "Release: $release_directory"
+echo "Output: $output_directory"
+echo "Starting: $starting_directory"
+#
+# 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"
+asetup
+source build/x8*/setup.sh
+#
+# Do this by hand
+# asetup --input="$release_directory/calypso/asetup.faser" Athena,22.0.49
+# source "$release_directory/build/x8*/setup.sh"
+#
+#
+# Try to find a release tag
+cd calypso
+recotag=`git describe`
+if [[ "$recotag" == "reco/r"???? ]]; then
+  tag=`echo "$recotag" | cut -c 6-11`
+  echo "Found reco tag: $tag"
+fi
+if [[ "$recotag" == "reco/p"???? ]]; then
+  tag=`echo "$recotag" | cut -c 6-11`
+  echo "Found reco tag: $tag"
+fi
+#
+# 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 "$rtag" ]]; then
+    faserMDC_reco.py "--nevents=$nevents" "$file_path"
+else
+    faserMDC_reco.py "--nevents=$nevents" "--reco=$tag" "$file_path"
+fi
+#
+# Print out ending time
+date
diff --git a/Control/CalypsoExample/Reconstruction/scripts/submit_faser_reco.sh b/Control/CalypsoExample/Reconstruction/scripts/submit_faser_reco.sh
index 64cca556..277d6eaa 100755
--- a/Control/CalypsoExample/Reconstruction/scripts/submit_faser_reco.sh
+++ b/Control/CalypsoExample/Reconstruction/scripts/submit_faser_reco.sh
@@ -97,8 +97,12 @@ source build/x8*/setup.sh
 cd calypso
 recotag=`git describe`
 if [[ "$recotag" == "reco/r"???? ]]; then
-  rtag=`echo "$recotag" | cut -c 6-11`
-  echo "Found reco tag: $rtag"
+  tag=`echo "$recotag" | cut -c 6-11`
+  echo "Found reco tag: $tag"
+fi
+if [[ "$recotag" == "reco/p"???? ]]; then
+  tag=`echo "$recotag" | cut -c 6-11`
+  echo "Found reco tag: $tag"
 fi
 #
 # Move to the run directory
@@ -123,7 +127,7 @@ cd "$file_stem"
 if [[ -z "$rtag" ]]; then
     faser_reco.py "--nevents=$nevents" "$file_path"
 else
-    faser_reco.py "--nevents=$nevents" "--reco=$rtag" "$file_path"
+    faser_reco.py "--nevents=$nevents" "--reco=$tag" "$file_path"
 fi
 #
 # Print out ending time
-- 
GitLab