diff --git a/Control/CalypsoExample/Generation/python/faser_parser.py b/Control/CalypsoExample/Generation/python/faser_parser.py
index 055dd7200201ce69823d798609cb991d85f4e794..03817efb8d9f8852a1dda650a78f7f4de8283d4e 100644
--- a/Control/CalypsoExample/Generation/python/faser_parser.py
+++ b/Control/CalypsoExample/Generation/python/faser_parser.py
@@ -44,6 +44,10 @@ def faser_pgparser():
                         help="Specify radius (in mm)")
     parser.add_argument("--angle", default=0.005, type=float_or_none,
                         help="Specify angular width (in Rad)")
+    parser.add_argument("--xpos", default=None, type=float,
+                        help="Specify x position of particles (in mm)")
+    parser.add_argument("--ypos", default=None, type=float,
+                        help="Specify y position of particles (in mm)")
     parser.add_argument("--zpos", default=None, type=float,
                         help="Specify z position of particles (in mm) (helpful to avoid FASERnu)")
 
diff --git a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
index 169badecf7cf2a392a95b098a858634c5a2523c6..7ec05e2aaf4df3d434577b3d7f423bcf56856722 100755
--- a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
+++ b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
@@ -148,9 +148,15 @@ if __name__ == '__main__':
 
     # -1000 is safely upstream of detector (to be checked)
     # Note zpos is in mm!
-    if args.zpos:
+    if args.zpos != None:
         sg_dict["z"] = args.zpos
 
+    if args.xpos != None:
+        sg_dict["x"] = args.xpos
+
+    if args.ypos != None:
+        sg_dict["y"] = args.ypos
+
     # Determine energy sampling
     if args.sampler == "lin":
         sg_dict["energy"] = PG.UniformSampler(args.minE*GeV, args.maxE*GeV)
@@ -159,11 +165,28 @@ if __name__ == '__main__':
     elif args.sampler == "const":
         sg_dict["energy"] = PG.ConstSampler(args.maxE*GeV)
     elif args.sampler == "hist":
-        fname, hname = args.hist_name.split(":")
-        sg_dict["energy"] = PG.TH1Sampler(fname, hname)
+        nargs = len(args.hist_name.split(":"))
+        if nargs == 2:
+            fname, hname = args.hist_name.split(":")
+            sg_dict["energy"] = PG.TH1Sampler(fname, hname)
+        elif nargs == 3:
+            fname, hname, scale = args.hist_name.split(":")
+            sg_dict["energy"] = PG.TH1Sampler(fname, hname, scale)
+        else:
+            print(f"Can't parse histogram {args.hist_name}!")
+            sys.exit(1)
+
     elif args.sampler == "hist2D":
-        fname, hname = args.hist_name.split(":")
-        sg_dict["energy"] = PG.TH2Sampler(fname, hname)        
+        nargs = len(args.hist_name.split(":"))
+        if nargs == 2:
+            fname, hname = args.hist_name.split(":")
+            sg_dict["energy"] = PG.TH2Sampler(fname, hname)
+        elif nargs == 4:
+            fname, hname, scalex, scaley = args.hist_name.split(":")
+            sg_dict["energy"] = PG.TH2Sampler(fname, hname, scalex, scaley)
+        else:
+            print(f"Can't parse histogram {args.hist_name}!")
+            sys.exit(1)
     else:
         print(f"Sampler {args.sampler} not known!")
         sys.exit(1)
@@ -255,8 +278,12 @@ if __name__ == '__main__':
 
     b = time.time()
     log.info("Run G4FaserAlg in " + str(b-a) + " seconds")
-#
-# Success should be 0
-#
-    sys.exit(not sc.isSuccess())
+
+# Signal errors
+if sc.isSuccess():
+    log.info("Execution succeeded")
+    sys.exit(0)
+else:
+    log.info("Execution failed, return 1")
+    sys.exit(1)
 
diff --git a/Control/CalypsoExample/Generation/scripts/submit_faser_particlegun.sh b/Control/CalypsoExample/Generation/scripts/submit_faser_particlegun.sh
index 9b8676752be083d66acaaf0d0fa4744be43050b5..441ef2923b397274b74f53b8530adf592dc3e76f 100755
--- a/Control/CalypsoExample/Generation/scripts/submit_faser_particlegun.sh
+++ b/Control/CalypsoExample/Generation/scripts/submit_faser_particlegun.sh
@@ -185,9 +185,12 @@ cd "${config_file_stem}-${seg_str}"
 # Run job
 if [[ -z "$tag" ]]; then
     faser_particlegun.py  "--conf=$config_path" "--segment=$seg_str"
+    gen_code=$?
 else
     faser_particlegun.py  "--conf=$config_path" "--segment=$seg_str" "--tag=$tag"
+    gen_code=$?
 fi
+echo "Return code: $gen_code"
 #
 # Print out ending time
 date
@@ -198,25 +201,51 @@ export EOS_MGM_URL=root://eospublic.cern.ch
 #
 if ! [ -z "$outdest" ]
 then
+    echo "Output directory:"
     ls -l
-    echo "copy *-HITS.root to $outdest"
-    mkdir -p $outdest
-    eos cp *-HITS.root ${outdest}/ || true
+    thefile=`ls *-HITS.root`
+    if [ $? -eq 0 ]; then
+	echo "copy $thefile to $outdest"
+	eos mkdir -p $outdest
+	eos cp $thefile ${outdest}/${thefile} || true
+
+	# Check that it worked
+	eos ls ${outdest}/${thefile} > /dev/null
+	if [ $? -eq 0 ]; then
+	    echo "file $thefile copied to $outdest"
+	    copy_code=0
+	else
+	    echo "didnt find $thefile in $outdest !"
+	    copy_code=1
+	fi
+    else
+	echo "ls *-xAOD.root returned nothing!"
+	copy_code=1
+    fi
+
 fi
 #
 # Also copy log file
 if ! [ -z "$logdest" ]
 then
     cd ..
+    echo "Working directory:"
     ls -l
     echo "copy $logfile to $logdest"
-    mkdir -p $logdest
+    eos mkdir -p $logdest
     eos cp $logfile $logdest/$logfile
 elif ! [ -z "$outdest" ]
 then 
     cd ..
     ls -l
     echo "copy $logfile to $outdest"
-    mkdir -p $outdest
+    eos mkdir -p $outdest
     eos cp $logfile $outdest/$logfile
 fi
+
+# Make sure to return an error is calypso failed
+if [ $gen_code -ne 0 ] || [ $copy_code -ne 0 ]; then
+  exit 1
+else
+  exit 0
+fi
diff --git a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py
index 21e1d51a6af7d92ebac09d051e80c919d1c39687..744aafa0b05213073086939fbf2d75138479b6a6 100755
--- a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py
+++ b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py
@@ -29,6 +29,8 @@ 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("--skip", type=int, default=0,
+                    help="Specify number of events to skip (default: none)")
 parser.add_argument("-v", "--verbose", action='store_true', 
                     help="Turn on DEBUG output")
 parser.add_argument("--isMC", action='store_true',
@@ -37,6 +39,8 @@ parser.add_argument("--MC_calibTag", default="",
                     help="Specify tag used to reconstruct MC calo energy: (WAVE-Calibration-01-LG-nofilt, WAVE-Calibration-01-LG, WAVE-Calibration-01-HG-nofilt, or WAVE-Calibration-01-HG) ")
 parser.add_argument("--testBeam", action='store_true',
                     help="Set geometry for 2021 test beam")
+parser.add_argument("--isOverlay", action='store_true', 
+                    help="Set overlaid data input")
 
 args = parser.parse_args()
 
@@ -81,6 +85,8 @@ else:
 print(f"Starting reconstruction of {filepath.name} with type {runtype}")
 if args.nevents > 0:
     print(f"Reconstructing {args.nevents} events by command-line option")
+if args.skip > 0:
+    print(f"Skipping {args.skip} events by command-line option")
 
 # Start reconstruction
 
@@ -103,6 +109,8 @@ else:
 ConfigFlags.Input.ProjectName = "data20"
 ConfigFlags.GeoModel.Align.Dynamic    = False
 
+ConfigFlags.Exec.SkipEvents = args.skip
+
 # Flags for later
 useCKF = True
 useCal = False
@@ -173,7 +181,7 @@ acc.merge(PoolWriteCfg(ConfigFlags))
 #
 # Set up RAW data access
 
-if args.isMC:
+if args.isMC or args.isOverlay:
     from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
     acc.merge(PoolReadCfg(ConfigFlags))
 else:    
@@ -185,22 +193,24 @@ else:
 from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg
 acc.merge(FaserGeometryCfg(ConfigFlags))
 
-if useLHC:
+if useLHC and not args.isOverlay:
     from LHCDataAlgs.LHCDataAlgConfig import LHCDataAlgCfg
     acc.merge(LHCDataAlgCfg(ConfigFlags))
 
 # Set up algorithms
-from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionCfg    
-acc.merge(WaveformReconstructionCfg(ConfigFlags))
+if not args.isOverlay:
+    from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionCfg    
+    acc.merge(WaveformReconstructionCfg(ConfigFlags))
 
-# Calorimeter Energy reconstruction
-if useCal:
-    from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionCfg
-    acc.merge(CalorimeterReconstructionCfg(ConfigFlags, MC_calibTag=args.MC_calibTag))
+    # Calorimeter Energy reconstruction
+    if useCal:
+        from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionCfg
+        acc.merge(CalorimeterReconstructionCfg(ConfigFlags, MC_calibTag=args.MC_calibTag))
 
 # Tracker clusters
 from TrackerPrepRawDataFormation.TrackerPrepRawDataFormationConfig import FaserSCT_ClusterizationCfg
-acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags, DataObjectName="SCT_RDOs"))
+# acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags, DataObjectName="Pos_SCT_RDOs"))
+acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags, DataObjectName="SCT_RDOs", checkBadChannels=True))
 
 #
 # SpacePoints
@@ -226,7 +236,8 @@ if useCKF:
     #
     # Kalman Filter for tracking
     from FaserActsKalmanFilter.CKF2Config import CKF2Cfg
-    acc.merge(CKF2Cfg(ConfigFlags, noDiagnostics=True))
+    if not args.isOverlay:
+        acc.merge(CKF2Cfg(ConfigFlags, noDiagnostics=True))
 
     # Add tracking collection with no IFT 
     acc.merge(CKF2Cfg(ConfigFlags, maskedLayers=[0, 1, 2], name="CKF_woIFT", 
@@ -246,10 +257,10 @@ itemList = [ "xAOD::EventInfo#*"
              , "TrackCollection#*"
 ]
 #
-if useLHC:
+if useLHC and not args.isOverlay:
     itemList.extend( ["xAOD::FaserLHCData#*", "xAOD::FaserLHCDataAux#*"] )
 
-if args.isMC:
+if args.isMC and not args.isOverlay:
     # Make xAOD versions of truth
     from Reconstruction.xAODTruthCnvAlgConfig import xAODTruthCnvAlgCfg
     acc.merge(xAODTruthCnvAlgCfg(ConfigFlags))
@@ -266,13 +277,14 @@ tagBuilder = CompFactory.EventInfoTagBuilder()
 tagBuilder.PropagateInput=False
 acc.addEventAlgo(tagBuilder)
 
-# Waveform reconstruction output
-from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionOutputCfg    
-acc.merge(WaveformReconstructionOutputCfg(ConfigFlags))
+if not args.isOverlay:
+    # Waveform reconstruction output
+    from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionOutputCfg    
+    acc.merge(WaveformReconstructionOutputCfg(ConfigFlags))
 
-# Calorimeter reconstruction output
-from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionOutputCfg
-acc.merge(CalorimeterReconstructionOutputCfg(ConfigFlags))
+    # Calorimeter reconstruction output
+    from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionOutputCfg
+    acc.merge(CalorimeterReconstructionOutputCfg(ConfigFlags))
 
 # Check what we have
 print( "Writing out xAOD objects:" )
diff --git a/Generators/ParticleGun/python/histsampling.py b/Generators/ParticleGun/python/histsampling.py
index 9f541da7c972ac01170b8acfafc8001b6eea5a14..10549ef0b250b999c7ac66063bdb1d5ae7c90147 100644
--- a/Generators/ParticleGun/python/histsampling.py
+++ b/Generators/ParticleGun/python/histsampling.py
@@ -14,6 +14,7 @@ def load_hist(*args):
     """
     Load a histogram from a filename/TFile and histo name. If a single arg is
     provided, it has to be a histo object and will be cloned before return.
+
     """
     h = None
     if len(args) == 1 and issubclass(type(args[0]), ROOT.TH1):
@@ -25,6 +26,7 @@ def load_hist(*args):
             #f.Close()
         elif type(args[0]) is ROOT.TFile and type(args[1]) is str:
             h = args[0].Get(args[1]).Clone()
+
     if h is None:
         raise Exception("Error in histogram loading from " + args)
     else:
@@ -77,7 +79,7 @@ def get_random_bin(globalbins, cheights):
     raise Exception("Sample fell outside range of cumulative distribution?!?!")
 
 
-def get_random_x(h, globalbins, cheights, globalbin_to_axisbin):
+def get_random_x(h, globalbins, cheights, globalbin_to_axisbin, scale=1.):
     """
     Choose a random bin via get_random_bin, then pick a uniform random x
     point in that bin (without any attempt at estimating the in-bin distribution).
@@ -86,10 +88,10 @@ def get_random_x(h, globalbins, cheights, globalbin_to_axisbin):
     axisids = globalbin_to_axisbin.get(irand)
     assert axisids is not None
     xrand = random.uniform(h.GetXaxis().GetBinLowEdge(axisids[0]), h.GetXaxis().GetBinUpEdge(axisids[0]))
-    return xrand
+    return scale * xrand
 
 
-def get_random_xy(h2, globalbins, cheights, globalbin_to_axisbin):
+def get_random_xy(h2, globalbins, cheights, globalbin_to_axisbin, xscale=1., yscale=1.):
     """
     Choose a random bin via get_random_bin, then pick a uniform random x,y
     point in that bin (without any attempt at estimating the in-bin distribution).
@@ -99,34 +101,71 @@ def get_random_xy(h2, globalbins, cheights, globalbin_to_axisbin):
     assert axisids is not None
     xrand = random.uniform(h2.GetXaxis().GetBinLowEdge(axisids[0]), h2.GetXaxis().GetBinUpEdge(axisids[0]))
     yrand = random.uniform(h2.GetYaxis().GetBinLowEdge(axisids[1]), h2.GetYaxis().GetBinUpEdge(axisids[1]))
-    return xrand, yrand
+    return (xscale*xrand), (yscale*yrand)
 
 
 class TH1(object):
     "Minimal wrapper for ROOT TH1, for sampling consistency and easy loading"
 
     def __init__(self, *args):
-        self.th1 = load_hist(*args)
+        """ Args can be variable length, but these are now kwargs, so order matters
+
+        fname - filename
+        hname - histogram name
+        xscale - scaling factor for x axis variable
+        """
+        if len(args) > 2:
+            self.th1 = load_hist(*(args[0:2]))
+        else:
+            self.th1 = load_hist(*args)
+
         self.globalbins, self.globalbin_to_axisbin, self.cheights = None, None, None
 
+        if len(args) >= 3:
+            self.xscale = float(args[2])
+        else:
+            self.xscale = 1.
+
     def GetRandom(self):
         "A GetRandom that works for TH1s and uses Python random numbers"
         if self.globalbins is None or self.globalbin_to_axisbin is None or self.cheights is None:
             self.globalbins, self.globalbin_to_axisbin, self.cheights = get_sampling_vars(self.th1)
-        return get_random_x(self.th1, self.globalbins, self.cheights, self.globalbin_to_axisbin)
+        return get_random_x(self.th1, self.globalbins, self.cheights, self.globalbin_to_axisbin, self.xscale)
 
 
 class TH2(object):
     "Minimal wrapper for ROOT TH2, for easy loading and to allow 2D sampling"
 
     def __init__(self, *args):
-        self.th2 = load_hist(*args)
+        """ Args can be variable length, but these are now kwargs, so order matters
+
+        fname - filename
+        hname - histogram name
+        xscale - scaling factor for x axis variable
+        yscale - scaling factor for y axis variable
+        """
+        if len(args) > 2:
+            self.th2 = load_hist(*(args[0:2]))
+        else:
+            self.th2 = load_hist(*args)
+
         self.globalbins, self.globalbin_to_axisbin, self.cheights = None, None, None
 
+        if len(args) >= 3:
+            self.xscale = float(args[2])
+        else:
+            self.xscale = 1.
+
+        if len(args) >= 4:
+            self.yscale = float(args[3])
+        else:
+            self.yscale = 1.
+
+
     def GetRandom(self):
         "A GetRandom that works for TH2s"
         if self.globalbins is None or self.globalbin_to_axisbin is None or self.cheights is None:
             self.globalbins, self.globalbin_to_axisbin, self.cheights = get_sampling_vars(self.th2)
-        return get_random_xy(self.th2, self.globalbins, self.cheights, self.globalbin_to_axisbin)
+        return get_random_xy(self.th2, self.globalbins, self.cheights, self.globalbin_to_axisbin, self.xscale, self.yscale)
 
 
diff --git a/Generators/ParticleGun/python/samplers.py b/Generators/ParticleGun/python/samplers.py
index d6e540ea6cf7a747d9152202cd488c135fa27099..9126b607ca7246480e4129669f3933e236c2c126 100644
--- a/Generators/ParticleGun/python/samplers.py
+++ b/Generators/ParticleGun/python/samplers.py
@@ -633,14 +633,23 @@ class EThetaMPhiSampler(MomSampler):
         pt = p sin(theta)
         """
 
-        if self._theta is None:
-            e,theta = self.energy()
-        else:            
-            e = self.energy()
-            theta = self.theta()
-            
         m = self.mass()
-        p = math.sqrt( e**2 - m**2 )
+
+        count = 0
+        e = -1
+        while (e < m and count < 5):
+            count += 1
+            if self._theta is None:
+                e,theta = self.energy()
+            else:            
+                e = self.energy()
+                theta = self.theta()
+
+        try:            
+            p = math.sqrt( e**2 - m**2 )
+        except Exception:
+            raise Exception(f"Error generating E: {e} m: {m}!")
+
         pz = p * math.cos(theta)
         pt = p * math.sin(theta)
         phi = self.phi()
diff --git a/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx b/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx
index de22698589abfbbbfc2ea9aa6d54feacd4661610..1322903932ee6ffdc8d41f31e3a850d680f1c391 100644
--- a/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx
+++ b/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx
@@ -55,6 +55,10 @@ LHCDataAlg::execute(const EventContext& ctx) const {
   lhcDataHandle->set_numBunchBeam2(m_lhcTool->getBeam2Bunches(ctx));
   lhcDataHandle->set_numBunchColliding(m_lhcTool->getCollidingBunches(ctx));
 
+  ATH_MSG_DEBUG("FaserLHCData B1: " << m_lhcTool->getBeam1Bunches(ctx)
+		<< " B2: " << m_lhcTool->getBeam2Bunches(ctx)
+		<< " Coll: " << m_lhcTool->getCollidingBunches(ctx));
+
   // Fill BCID information
 
   // Get the BCID mask
@@ -64,37 +68,68 @@ LHCDataAlg::execute(const EventContext& ctx) const {
   SG::ReadHandle<xAOD::EventInfo> xevt(m_eventInfo, ctx);
   unsigned int bcid = xevt->bcid();
 
-  int nearest = findNearest(bcid, bcid_mask, 3);  // Colliding beams
+  int nearest;
+  if (m_lhcTool->getCollidingBunches(ctx) == 0) {
+    ATH_MSG_INFO("No colliding bunches, can't set nearest");
+    nearest = -3564;
+  } else {
+    nearest = findNearest(bcid, bcid_mask, 3);  // Colliding beams
+  }
   lhcDataHandle->set_distanceToCollidingBCID(nearest);
   ATH_MSG_DEBUG("Found distance of " << nearest << " from BCID " << bcid 
 		<< " to the nearest colliding BCID ");
 
-  nearest = findNearest(bcid, bcid_mask, 1);  // Beam1 unpaired
+  if (m_lhcTool->getBeam1Bunches(ctx) == 0) {
+    ATH_MSG_INFO("No beam 1 bunches, can't set nearest");
+    nearest = -3564;
+  } else {
+    nearest = findNearest(bcid, bcid_mask, 1);  // Beam1 unpaired
+  }
   lhcDataHandle->set_distanceToUnpairedB1(nearest);
   ATH_MSG_DEBUG("Found distance of " << nearest << " from BCID " << bcid 
 		<< " to the nearest unpaired B1 ");
 
-  nearest = findNearest(bcid, bcid_mask, 2);  // Beam2 unpaired
+  if (m_lhcTool->getBeam2Bunches(ctx) == 0) {
+    ATH_MSG_INFO("No beam 2 bunches, can't set nearest");
+    nearest = -3564;
+  } else {
+    nearest = findNearest(bcid, bcid_mask, 2);  // Beam2 unpaired
+  }
   lhcDataHandle->set_distanceToUnpairedB2(nearest);
   ATH_MSG_DEBUG("Found distance of " << nearest << " from BCID " << bcid 
 		<< " to the nearest unpaired B2 ");
 
   // Add 127 to current BCID to check if inbound B1 is nearby FASER
-  int offset_bcid = (bcid + 127) % 3564;
-  nearest = findNearest(offset_bcid, bcid_mask, 1);  // Inbound B1
-  int nearest2 = findNearest(offset_bcid, bcid_mask, 3);  // Inbound B1
-  if (abs(nearest2) < abs(nearest)) nearest = nearest2;
+  if (m_lhcTool->getBeam1Bunches(ctx) == 0) {
+    ATH_MSG_INFO("No beam 1 bunches, can't set nearest");
+    nearest = -3564;
+  } else {
+    int offset_bcid = (bcid + 127) % 3564;
+    nearest = findNearest(offset_bcid, bcid_mask, 1);  // Inbound B1
+    int nearest2 = findNearest(offset_bcid, bcid_mask, 3);  // Inbound B1
+    if (abs(nearest2) < abs(nearest)) nearest = nearest2;
+  }
   lhcDataHandle->set_distanceToInboundB1(nearest);
   ATH_MSG_DEBUG("Found distance of " << nearest << " from BCID " << bcid 
   		<< " to the nearest inbound B1 ");
 
   unsigned int previous;
-  previous = previousColliding(bcid, bcid_mask);
+  if (m_lhcTool->getCollidingBunches(ctx) == 0) {
+    ATH_MSG_INFO("No colliding bunches, can't set nearest");
+    previous = 9999;
+  } else {
+    previous = previousColliding(bcid, bcid_mask);
+  }
   lhcDataHandle->set_distanceToPreviousColliding(previous);
   ATH_MSG_DEBUG("Found distance of " << previous << " from BCID " << bcid 
 		<< " to the previous colliding bunch ");
 
-  previous = previousTrain(bcid, bcid_mask);
+  if (m_lhcTool->getCollidingBunches(ctx) == 0) {
+    ATH_MSG_INFO("No colliding bunches, can't set nearest");
+    previous = 9999;
+  } else {
+    previous = previousTrain(bcid, bcid_mask);
+  }
   lhcDataHandle->set_distanceToTrainStart(previous);
   ATH_MSG_DEBUG("Found distance of " << previous << " from BCID " << bcid 
 		<< " to the start of the previous train ");
@@ -142,8 +177,20 @@ LHCDataAlg::findNearest(unsigned int bcid, const std::vector<unsigned char>& bci
 
   // If we got to here, there was no match
   // Does the BCID make sense?
-  ATH_MSG_WARNING("Couldn't find distance from BCID " << bcid << " and pattern " << mask << "!");
-  ATH_MSG_WARNING(bcid_mask);
+  ATH_MSG_WARNING("Couldn't find distance from BCID " << bcid << " and pattern " << int(mask) << "!");
+  int b1=0;
+  int b2=0;
+  int col = 0;
+  for (int i=0; i<3564; i++) {
+    if (mask & 0x01) b1++;
+    if (mask & 0x02) b2++;
+    if (mask & 0x03) col++;
+
+    ATH_MSG_WARNING("BCID " << i << " - " << int(bcid_mask[i]));
+  }
+
+  //ATH_MSG_WARNING(bcid_mask);
+  ATH_MSG_WARNING("Found B1: " << b1 << " B2: " << b2 << " Coll: " << col);
   return -3565;
 
 }
diff --git a/PhysicsAnalysis/NtupleDumper/CMakeLists.txt b/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
index fab78641d7ae2a3842904122968a80170bca9a28..607c58d892d201731098f64829a82d556f1a302a 100644
--- a/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
+++ b/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
@@ -1,11 +1,14 @@
 atlas_subdir( NtupleDumper )
 
+find_package( nlohmann_json )
+
 atlas_add_component(
         NtupleDumper
         src/NtupleDumperAlg.h
         src/NtupleDumperAlg.cxx
         src/component/NtupleDumper_entries.cxx
         LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform xAODFaserCalorimeter xAODFaserTrigger xAODFaserLHC ScintIdentifier FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack TrackerSpacePoint FaserActsKalmanFilterLib
+	PRIVATE_LINK_LIBRARIES nlohmann_json::nlohmann_json 
 )
 
 atlas_install_python_modules( python/*.py )
diff --git a/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py b/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py
index 27b30e25df16dd8baf1c4ce4b82b6a9e6a0f7b6f..1f4b990c2fb7bbdef7e109898e91bf5e0171d49d 100755
--- a/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py
+++ b/PhysicsAnalysis/NtupleDumper/scripts/faser_ntuple_maker.py
@@ -44,8 +44,20 @@ parser.add_argument("--isMC", action='store_true',
 parser.add_argument("--partial", action='store_true',
                     help="Allow partial input files")
 
+parser.add_argument("--trigFilt", action='store_true',
+                    help="apply trigger event filter")
+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)")
+
 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")
  
 parser.add_argument("--fluka", action='store_true',
                     help="Add FLUKA weights to ntuple")
@@ -138,8 +150,7 @@ if filepath.is_dir():
     print(f"Run   = {runstr}")
     print(f"First = {firstseg}")
     print(f"Last  = {lastseg}")
-    print(f"Args  = {args.tag}")
-    print(f"Blind = {not args.unblind}")
+    print(f"Tag  = {args.tag}")
 
     # Find any tags
     tagstr = firststem.replace(f"{firstfaser}-{firstshort}-{runstr}-{firstseg}", "")
@@ -199,6 +210,13 @@ if len(args.outfile) > 0:
 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
@@ -232,18 +250,25 @@ ConfigFlags.lock()
 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:
-    if args.genie:
-        acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, UseGenieWeights=True))
-    elif args.fluka:
-        acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, UseFlukaWeights=True))
-    else:
-        acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile))
+    acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, **mc_kwargs))
 
 else:
-    acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, DoBlinding=(not args.unblind)))
+    acc.merge(NtupleDumperAlgCfg(ConfigFlags, outfile, DoBlinding=(not args.unblind), OnlyBlinded=args.onlyblind, DoScintFilter = args.scintFilt, DoTrackFilter = (not args.NoTrackFilt), DoTrigFilter = args.trigFilt, **grl_kwargs))
 
 if not args.verbose:
     from AthenaConfiguration.ComponentFactory import CompFactory
@@ -251,6 +276,10 @@ if not args.verbose:
     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")
diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
index 3f476edb40480e5871a2df39a3cd2695ea894e4c..59a2be962c55923976f578c7e403cd2aae64a899 100644
--- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
+++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
@@ -18,9 +18,6 @@
 #include <TH1F.h>
 #include <numeric>
 
-constexpr float NaN = std::numeric_limits<double>::quiet_NaN();
-
-
 NtupleDumperAlg::NtupleDumperAlg(const std::string &name, 
                                     ISvcLocator *pSvcLocator)
     : AthReentrantAlgorithm(name, pSvcLocator), 
@@ -123,6 +120,19 @@ StatusCode NtupleDumperAlg::initialize()
 
   ATH_CHECK(m_spacePointContainerKey.initialize());
 
+  // Read GRL if requested
+  if (!m_goodRunsList.value().empty()) {
+    ATH_MSG_INFO("Opening GRL file " << m_goodRunsList);
+    ATH_MSG_INFO(m_applyGoodRunsList);
+    std::ifstream f(m_goodRunsList);
+    m_grl = nlohmann::json::parse(f);
+    f.close();
+    ATH_MSG_INFO("Read GRL with " << m_grl.size() << " records");
+  } else {
+    // Make sure this is empty
+    m_grl.clear();
+  }
+
   if (m_useFlukaWeights)
   {
     m_baseEventCrossSection = (m_flukaCrossSection * kfemtoBarnsPerMilliBarn)/m_flukaCollisions;
@@ -143,6 +153,7 @@ StatusCode NtupleDumperAlg::initialize()
   m_tree->Branch("eventID", &m_event_number, "eventID/I");
   m_tree->Branch("eventTime", &m_event_time, "eventTime/I");
   m_tree->Branch("BCID", &m_bcid, "BCID/I");
+  m_tree->Branch("inGRL", &m_in_grl, "inGRL/I");
 
   m_tree->Branch("fillNumber", &m_fillNumber, "fillNumber/I");
   m_tree->Branch("betaStar", &m_betaStar, "betaStar/F");
@@ -167,10 +178,15 @@ StatusCode NtupleDumperAlg::initialize()
   addWaveBranches("Preshower",2,12);
   addWaveBranches("Calo",4,0);
 
+  m_tree->Branch("ScintHit", &m_scintHit);
+
   addCalibratedBranches("Calo",4,0);
   addBranch("Calo_total_nMIP", &m_calo_total_nMIP);
   addBranch("Calo_total_E_dep", &m_calo_total_E_dep);
   addBranch("Calo_total_E_EM", &m_calo_total_E_EM);
+  addBranch("Calo_total_raw_E_EM", &m_calo_total_raw_E_EM);
+  addBranch("Calo_total_E_EM_fudged", &m_calo_total_E_EM_fudged);
+  addBranch("Calo_total_raw_E_EM_fudged", &m_calo_total_raw_E_EM_fudged);
 
   addCalibratedBranches("Preshower",2,12);
   addBranch("Preshower_total_nMIP", &m_preshower_total_nMIP);
@@ -261,6 +277,11 @@ StatusCode NtupleDumperAlg::initialize()
   m_tree->Branch("Track_ThetaX_atCalo", &m_thetaxCalo);
   m_tree->Branch("Track_ThetaY_atCalo", &m_thetayCalo);
 
+  m_tree->Branch("Track_x_atMaxRadius", &m_x_atMaxRadius);
+  m_tree->Branch("Track_y_atMaxRadius", &m_y_atMaxRadius);
+  m_tree->Branch("Track_z_atMaxRadius", &m_z_atMaxRadius);
+  m_tree->Branch("Track_r_atMaxRadius", &m_r_atMaxRadius);
+
   //TRUTH
   m_tree->Branch("t_pdg", &m_t_pdg);
   m_tree->Branch("t_barcode", &m_t_barcode);
@@ -385,10 +406,10 @@ StatusCode NtupleDumperAlg::initialize()
   ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge13", m_HistRandomCharge[13]));
   ATH_CHECK(histSvc()->regHist("/HIST2/RandomCharge14", m_HistRandomCharge[14]));
 
-  m_MIP_sim_Edep_calo = 0.0585; // MIP deposits 0.0585 GeV of energy in calo
-  m_MIP_sim_Edep_preshower = 0.004894; // MIP deposits 0.004894 GeV of energy in a preshower layer
-
-  if (m_doBlinding) {
+  if (m_onlyBlinded){
+    ATH_MSG_INFO("Only events that would be blinded are saved in ntuple");
+    m_doBlinding = false;
+  } else if (m_doBlinding) {
     ATH_MSG_INFO("Blinding will be enforced for real data.");
   } else {
     ATH_MSG_INFO("Blinding will NOT be enforced for real data.");
@@ -410,85 +431,162 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     isMC = true;
   }
 
-  // if real data, store charge in histograms from random events and only fill ntuple from coincidence events
-  if (!isMC) {
-    SG::ReadHandle<xAOD::FaserTriggerData> triggerData(m_FaserTriggerData, ctx);
-    m_tap=triggerData->tap();
-    // unpack trigger word: 1=calo, 2=veotnu|veto1|preshower, 4=TimingLayer, 8=(VetoNu|Veto2)&Preshower, 16=random, 32=LED 
-    bool trig_random = false;
-    if ( m_tap == 16 ) trig_random = true;
+  // EventInfo data
+  m_run_number = ctx.eventID().run_number();
+  m_event_number = ctx.eventID().event_number();
+  m_event_time = ctx.eventID().time_stamp();
+  m_bcid = ctx.eventID().bunch_crossing_id();
+
+  // For real data, find if data is in GoodRunsList
+  if (!isMC && !m_grl.empty()) {
+    ATH_MSG_DEBUG("Checking GRL for run " << m_run_number << " event " << m_event_number);
+
+    // JSON keys are always strings
+    std::string run_string = std::to_string(m_run_number);
+    std::string reason("unknown");
+
+    if (m_grl.contains(run_string)) {
+      // m_in_grl set to 0 by clearTree
+      auto jrun = m_grl[run_string];
+
+      // Make sure this is in the stable beams range
+      if (jrun.contains("stable_list")) {
+	for ( auto& slist : jrun["stable_list"]) {
+	  // Check if event falls in this range
+	  if (slist["start_utime"] > m_event_time) continue;
+	  if (slist["stop_utime"] <= m_event_time) continue;
+	  m_in_grl = 1;
+	  break;
+	}
+
+	// Check exclude list (not all runs will have this)
+	if (jrun.contains("excluded_list")) {
+	  ATH_MSG_DEBUG("Checking excluded list");
+	  for ( auto& blist : jrun["excluded_list"]) {
+	    ATH_MSG_DEBUG("Checking excluded list start " << blist["start_utime"] << " stop " << blist["stop_utime"] << " event " << m_event_time);
+
+	    // Check if event falls in this range
+	    if (blist["start_utime"] > m_event_time) continue;
+	    if (blist["stop_utime"] <= m_event_time) continue;
+	    if (blist.contains("reason")) {
+	      reason = blist["reason"];
+	    } 
+	    m_in_grl = 0;
+	    break;
+	  }
+	}
 
-    bool trig_coincidence_preshower_and_vetoes = false;
-    if ( (m_tap&8) != 0 ) trig_coincidence_preshower_and_vetoes = true;
+	if (m_in_grl == 0) {
+	  ATH_MSG_DEBUG("Run " << run_string << " event " << m_event_number << " time " << m_event_time << " excluded for " << reason);
+	}
 
-    bool trig_coincidence_timing_and_vetoesORpreshower = false;
-    if ( ((m_tap&4)!=0) && ((m_tap&2)!=0) ) trig_coincidence_timing_and_vetoesORpreshower = true;
+      } else {
+	ATH_MSG_WARNING("Run " << run_string << " has no stable_list!");
+      }
+    } else {
+      ATH_MSG_WARNING("Run " << run_string << " not found in GRL!");
+    }
+  }
 
-    bool trig_coincidence_timing_and_calo = false;
-    if ( ((m_tap&4)!=0) && ((m_tap&1)!=0) ) trig_coincidence_timing_and_calo = true;
+  // Skip this event?
+  if (m_applyGoodRunsList && (m_in_grl == 0)) {
+    ATH_MSG_DEBUG("Skipping run " << m_run_number 
+		  << " event " << m_event_number << " - outside GRL" );
+    m_eventsFailedGRL += 1;
+    return StatusCode::SUCCESS;
+  }
 
-    bool trig_coincidence_vetoesORpreshower_and_calo = false;
-    if ( ((m_tap&2)!=0) && ((m_tap&1)!=0) ) trig_coincidence_vetoesORpreshower_and_calo = true;
+  // if real data, correct for diigitzer timing jitter with clock phase
+  if (!isMC) {
+    // correct waveform time with clock phase
+    // needs to be done before processing waveform values
+    SG::ReadHandle<xAOD::WaveformClock> clockHandle(m_ClockWaveformContainer, ctx);
+    ATH_CHECK(clockHandle.isValid());
 
-    bool trig_calo = (m_tap & 1);
+    if (clockHandle->phase() < -2.0) { // wrap around clock pahse so -pi goes to pi
+      m_clock_phase = ((clockHandle->phase() + 2*3.14159) / 3.14159) * 12.5;
+    } else {
+      m_clock_phase = (clockHandle->phase() / 3.14159) * 12.5;
+    }
+  }
 
-    // for random trigger, store charge of scintillators in histograms
-    if (trig_random) {
-      // Read in Waveform containers
-      SG::ReadHandle<xAOD::WaveformHitContainer> vetoNuContainer { m_vetoNuContainer, ctx };
-      ATH_CHECK(vetoNuContainer.isValid());
+  // process all waveform data for all scintillator and calorimeter channels
+  SG::ReadHandle<xAOD::WaveformHitContainer> vetoNuContainer { m_vetoNuContainer, ctx };
+  ATH_CHECK(vetoNuContainer.isValid());
 
-      SG::ReadHandle<xAOD::WaveformHitContainer> vetoContainer { m_vetoContainer, ctx };
-      ATH_CHECK(vetoContainer.isValid());
+  SG::ReadHandle<xAOD::WaveformHitContainer> vetoContainer { m_vetoContainer, ctx };
+  ATH_CHECK(vetoContainer.isValid());
+
+  SG::ReadHandle<xAOD::WaveformHitContainer> triggerContainer { m_triggerContainer, ctx };
+  ATH_CHECK(triggerContainer.isValid());
 
-      SG::ReadHandle<xAOD::WaveformHitContainer> triggerContainer { m_triggerContainer, ctx };
-      ATH_CHECK(triggerContainer.isValid());
+  SG::ReadHandle<xAOD::WaveformHitContainer> preshowerContainer { m_preshowerContainer, ctx };
+  ATH_CHECK(preshowerContainer.isValid());
 
-      SG::ReadHandle<xAOD::WaveformHitContainer> preshowerContainer { m_preshowerContainer, ctx };
-      ATH_CHECK(preshowerContainer.isValid());
+  SG::ReadHandle<xAOD::WaveformHitContainer> ecalContainer { m_ecalContainer, ctx };
+  ATH_CHECK(ecalContainer.isValid());
 
-      SG::ReadHandle<xAOD::WaveformHitContainer> ecalContainer { m_ecalContainer, ctx };
-      ATH_CHECK(ecalContainer.isValid());
+  FillWaveBranches(*vetoNuContainer);
+  FillWaveBranches(*vetoContainer);
+  FillWaveBranches(*triggerContainer);
+  FillWaveBranches(*preshowerContainer);
+  FillWaveBranches(*ecalContainer);
 
+  // if real data, store charge in histograms from random events
+  if (!isMC) {
+    SG::ReadHandle<xAOD::FaserTriggerData> triggerData(m_FaserTriggerData, ctx);
+    m_tap=triggerData->tap();
+    // for random trigger, store charge of scintillators in histograms
+    if ((m_tap&16) != 0) { 
       // Fill histograms
-      if (vetoNuContainer.isValid()) {
-        for (auto hit : *vetoNuContainer) {
-          int ch=hit->channel();
-          m_HistRandomCharge[ch]->Fill(hit->raw_integral()/50.0);
-        }
-      }
-      if (vetoContainer.isValid()) {
-        for (auto hit : *vetoContainer) {
-          int ch=hit->channel();
-          m_HistRandomCharge[ch]->Fill(hit->raw_integral()/50.0);
-        }
-      }
-      if (triggerContainer.isValid()) {
-        for (auto hit : *triggerContainer) {
-          int ch=hit->channel();
-          m_HistRandomCharge[ch]->Fill(hit->raw_integral()/50.0);
-        }
-      }
-      if (preshowerContainer.isValid()) {
-        for (auto hit : *preshowerContainer) {
-          int ch=hit->channel();
-          m_HistRandomCharge[ch]->Fill(hit->raw_integral()/50.0);
-        }
-      }
-      if (ecalContainer.isValid()) {
-        for (auto hit : *ecalContainer) {
-          int ch=hit->channel();
-          m_HistRandomCharge[ch]->Fill(hit->raw_integral()/50.0);
-        }
+      for (int chan = 0; chan<15; chan++) {
+        m_HistRandomCharge[chan]->Fill(m_wave_raw_charge[chan]);
       }
+      return StatusCode::SUCCESS; // finished with this randomly triggered event
+    }
+
+    if (m_doTrigFilter) {
+
+      bool trig_coincidence_preshower_and_vetoes = ( (m_tap&8) != 0 );
+      bool trig_coincidence_timing_and_vetoesORpreshower = ( ((m_tap&4)!=0) && ((m_tap&2)!=0) );
+      bool trig_calo = (m_tap & 1);
 
-      return StatusCode::SUCCESS; // finished with this randomly triiggered event
+      if ( !(trig_coincidence_preshower_and_vetoes || trig_coincidence_timing_and_vetoesORpreshower || trig_calo) ) { 
+        // don't process events that fail to activate coincidence triggers
+        ATH_MSG_DEBUG("event did not pass trigger filter");
+        return StatusCode::SUCCESS;
+      }
+    }
 
-      // Try adding calo-only trigger
-    } else if ( !(trig_coincidence_preshower_and_vetoes || trig_coincidence_timing_and_vetoesORpreshower || trig_coincidence_timing_and_calo || trig_coincidence_vetoesORpreshower_and_calo || trig_calo) ) { 
-      // don't process events that fail to activate coincidence triggers
-      return StatusCode::SUCCESS;
+    if (m_doScintFilter) {
+      // filter events, but use waveform peak cuts instead of triggers, as triggers could miss signals slightly out of time
+      // digitizer channels described here: https://twiki.cern.ch/twiki/bin/viewauth/FASER/CaloScintillator
+      bool calo_trig = (m_wave_raw_peak[0] > 3.0 || m_wave_raw_peak[1] > 3.0 || m_wave_raw_peak[2] > 3.0 || m_wave_raw_peak[3] > 3.0);
+      bool vetoNu_trig = (m_wave_raw_peak[4] > 25.0 && m_wave_raw_peak[5] > 25.0);
+      bool vetoSt2_trig = (m_wave_raw_peak[6] > 25.0 && m_wave_raw_peak[7] > 25.0);
+      bool timingScint_trig = ((m_wave_raw_peak[8] > 25.0 && m_wave_raw_peak[9] > 25.0) || (m_wave_raw_peak[10] > 25.0 && m_wave_raw_peak[11] > 25.0));
+      bool preshower_trig = (m_wave_raw_peak[12] > 3.0 && m_wave_raw_peak[13] > 3.0);
+      bool vetoSt1_trig = (m_wave_raw_peak[14] > 25.0);
+
+      bool veto_OR_trig = (vetoNu_trig || vetoSt1_trig || vetoSt2_trig);
+
+      bool passes_ScintFilter = false;
+      if (calo_trig) {
+        passes_ScintFilter = true;
+      } else if (veto_OR_trig && timingScint_trig) {
+        passes_ScintFilter = true;
+      } else if (veto_OR_trig && preshower_trig) {
+        passes_ScintFilter = true;
+      } else if (timingScint_trig && preshower_trig) {
+        passes_ScintFilter = true;
+      } 
+
+      if (!passes_ScintFilter) {
+        ATH_MSG_DEBUG("event did not pass scint filter");
+        return StatusCode::SUCCESS; // only store events that pass filter
+      }
     }
+
     // store trigger data in ntuple variables
     m_tbp=triggerData->tbp();
     m_tap=triggerData->tap();
@@ -530,144 +628,149 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     ATH_MSG_DEBUG("LHC data distanceToTrainStart = " << lhcData->distanceToTrainStart() );
     ATH_MSG_DEBUG("LHC data distanceToPreviousColliding = " << lhcData->distanceToPreviousColliding() );
 
-    // correct waveform time with clock phase
-    SG::ReadHandle<xAOD::WaveformClock> clockHandle(m_ClockWaveformContainer, ctx);
-    ATH_CHECK(clockHandle.isValid());
-
-    if (clockHandle->phase() < -2.0) { // wrap around clock pahse so -pi goes to pi
-      m_clock_phase = ((clockHandle->phase() + 2*3.14159) / 3.14159) * 12.5;
-    } else {
-      m_clock_phase = (clockHandle->phase() / 3.14159) * 12.5;
-    }
-
   } // done with processing only on real data
 
-  m_run_number = ctx.eventID().run_number();
-  m_event_number = ctx.eventID().event_number();
-  m_event_time = ctx.eventID().time_stamp();
-  m_bcid = ctx.eventID().bunch_crossing_id();
+  // fill scintHit word with bits that reflect if a scintillator was hit (1 = vetoNu0, 2 = vetoNu1, 4 = vetoSt1_1, 8 vetoSt2_0, 16 = vetoSt2_1, 32 = Timing scint, 64 = preshower0, 128 = preshower1)
+  if (m_wave_raw_charge[4] > 40.0) {
+    m_scintHit = m_scintHit | 1;
+  }
+  if (m_wave_raw_charge[5] > 40.0) {
+    m_scintHit = m_scintHit | 2;
+  }
+  if (m_wave_raw_charge[14] > 40.0) {
+    m_scintHit = m_scintHit | 4;
+  }
+  if (m_wave_raw_charge[6] > 40.0) {
+    m_scintHit = m_scintHit | 8;
+  }
+  if (m_wave_raw_charge[7] > 40.0) {
+    m_scintHit = m_scintHit | 16;
+  }
+  if (m_wave_raw_charge[8]+m_wave_raw_charge[9]+m_wave_raw_charge[10]+m_wave_raw_charge[11] > 25.0) {
+    m_scintHit = m_scintHit | 32;
+  }
+  if (m_wave_raw_charge[12] > 2.5) {
+    m_scintHit = m_scintHit | 64;
+  }
+  if (m_wave_raw_charge[13] > 2.5) {
+    m_scintHit = m_scintHit | 128;
+  }
 
   if (isMC) { // if simulation find MC cross section and primary lepton
     // Work out effective cross section for MC
-    if (m_useFlukaWeights)
-    {
-        double flukaWeight = truthEventContainer->at(0)->weights()[0];
-        ATH_MSG_ALWAYS("Found fluka weight = " << flukaWeight);
-        m_crossSection = m_baseEventCrossSection * flukaWeight;
-    }
-    else if (m_useGenieWeights)
-    {
-        m_crossSection = m_baseEventCrossSection;
-    }
-    else
-    {
+    if (m_useFlukaWeights) {
+      double flukaWeight = truthEventContainer->at(0)->weights()[0];
+      ATH_MSG_ALWAYS("Found fluka weight = " << flukaWeight);
+      m_crossSection = m_baseEventCrossSection * flukaWeight;
+    } else if (m_useGenieWeights) {
+      m_crossSection = m_baseEventCrossSection;
+    } else {
       //ATH_MSG_WARNING("Monte carlo event with no weighting scheme specified.  Setting crossSection (weight) to " << m_baseEventCrossSection << " fb.");
-        m_crossSection = m_baseEventCrossSection;
+      m_crossSection = m_baseEventCrossSection;
     }
 
-    // Find the M d0 and d1 truth information 
+    // Find truth particle information 
     SG::ReadHandle<xAOD::TruthParticleContainer> truthParticleContainer { m_truthParticleContainer, ctx };
-    if (truthParticleContainer.isValid() && truthParticleContainer->size() > 0)
-    {
-
+    if (truthParticleContainer.isValid() && truthParticleContainer->size() > 0) {
       int ipart(0);
-      for (auto particle : *truthParticleContainer)
-      {      
-
-	// loop over first 10 truth particles (for non A' samples)
-
-	if (ipart++ < 10) {
-	    
-	  m_truth_P.push_back(particle->p4().P());
-	  m_truth_px.push_back(particle->p4().X());
-	  m_truth_py.push_back(particle->p4().Y());
-	  m_truth_pz.push_back(particle->p4().Z());
-	  m_truth_m.push_back(particle->m());
-	  m_truth_pdg.push_back(particle->pdgId());
-
-	    if ( particle->hasProdVtx()) {
-	        m_truth_prod_x.push_back(particle->prodVtx()->x());
-		m_truth_prod_y.push_back(particle->prodVtx()->y());
-		m_truth_prod_z.push_back(particle->prodVtx()->z());
-	      } else {
-		m_truth_prod_x.push_back(NaN);
-		m_truth_prod_y.push_back(NaN);
-		m_truth_prod_z.push_back(NaN);
-	      }
-
-	    if ( particle->hasDecayVtx()) {
-	      m_truth_dec_x.push_back(particle->decayVtx()->x());
-	      m_truth_dec_y.push_back(particle->decayVtx()->y());
-	      m_truth_dec_z.push_back(particle->decayVtx()->z());
-	      } else {
-		m_truth_dec_x.push_back(NaN);
-		m_truth_dec_y.push_back(NaN);
-		m_truth_dec_z.push_back(NaN);
-	    }
-	}
-	
-	     	     
-       if ( particle->barcode() == 1 || particle->barcode() == 2 || particle->barcode() == 3 ) {
-
-	 if ( particle->pdgId() == 32) { // mother particle (A')
-	  
-	   m_truthM_P.push_back(particle->p4().P());
-	   m_truthM_px.push_back(particle->p4().X());
-	   m_truthM_py.push_back(particle->p4().Y());
-	   m_truthM_pz.push_back(particle->p4().Z());
-
-	 if ( particle->hasDecayVtx()) { // decay vertex for A' particle 
-	    m_truthM_x.push_back(particle->decayVtx()->x());
-	    m_truthM_y.push_back(particle->decayVtx()->y());
-	    m_truthM_z.push_back(particle->decayVtx()->z());
-	 } else {
-	    m_truthM_x.push_back(NaN);
-	    m_truthM_y.push_back(NaN);
-	    m_truthM_z.push_back(NaN);
-	  }
-	 }
-
-	if ( particle->pdgId() == 11) // daughter particle (positron)
-	  {
-	    m_truthd0_P.push_back(particle->p4().P());
-	    m_truthd0_px.push_back(particle->p4().X());
-	    m_truthd0_py.push_back(particle->p4().Y());
-	    m_truthd0_pz.push_back(particle->p4().Z());
-
-	  if ( particle->hasProdVtx()) {
-	    m_truthd0_x.push_back(particle->prodVtx()->x());
-	    m_truthd0_y.push_back(particle->prodVtx()->y());
-	    m_truthd0_z.push_back(particle->prodVtx()->z());
-	  } else {
-	    m_truthd0_x.push_back(NaN);
-	    m_truthd0_y.push_back(NaN);
-	    m_truthd0_z.push_back(NaN);
-	  }
-	  }
-	if ( particle->pdgId() == -11) // daughter particle (electron)
-	  {
-	    m_truthd1_P.push_back(particle->p4().P());
-	    m_truthd1_px.push_back(particle->p4().X());
-	    m_truthd1_py.push_back(particle->p4().Y());
-	    m_truthd1_pz.push_back(particle->p4().Z());
-
-	  if ( particle->hasProdVtx()) {
-	    m_truthd1_x.push_back(particle->prodVtx()->x());
-	    m_truthd1_y.push_back(particle->prodVtx()->y());
-	    m_truthd1_z.push_back(particle->prodVtx()->z());
-	  } else {
-	    m_truthd1_x.push_back(NaN);
-	    m_truthd1_y.push_back(NaN);
-	    m_truthd1_z.push_back(NaN);
-	  }
-	  }
-       }
+      for (auto particle : *truthParticleContainer) { 	// loop over first 10 truth particles (for non A' samples)
+        if (ipart++ > 9) break;
+
+        m_truth_P.push_back(particle->p4().P());
+        m_truth_px.push_back(particle->p4().X());
+        m_truth_py.push_back(particle->p4().Y());
+        m_truth_pz.push_back(particle->p4().Z());
+        m_truth_m.push_back(particle->m());
+        m_truth_pdg.push_back(particle->pdgId());
+
+        if ( particle->hasProdVtx()) {
+          m_truth_prod_x.push_back(particle->prodVtx()->x());
+          m_truth_prod_y.push_back(particle->prodVtx()->y());
+          m_truth_prod_z.push_back(particle->prodVtx()->z());
+        } else {
+          m_truth_prod_x.push_back(999999);
+          m_truth_prod_y.push_back(999999);
+          m_truth_prod_z.push_back(999999);
+        }
+
+        if ( particle->hasDecayVtx()) {
+          m_truth_dec_x.push_back(particle->decayVtx()->x());
+          m_truth_dec_y.push_back(particle->decayVtx()->y());
+          m_truth_dec_z.push_back(particle->decayVtx()->z());
+        } else {
+          m_truth_dec_x.push_back(999999);
+          m_truth_dec_y.push_back(999999);
+          m_truth_dec_z.push_back(999999);
+        }
+
+        // Find the M d0 and d1 truth information for dark photon
+        if ( particle->barcode() == 1 || particle->barcode() == 2 || particle->barcode() == 3 ) {
+          auto positions = m_fiducialParticleTool->getTruthPositions(particle->barcode());
+          if ( particle->pdgId() == 32) { // mother particle (A')
+            m_truthM_P.push_back(particle->p4().P());
+      	    m_truthM_px.push_back(particle->p4().X());
+            m_truthM_py.push_back(particle->p4().Y());
+            m_truthM_pz.push_back(particle->p4().Z());
+
+            if ( particle->hasDecayVtx()) { // decay vertex for A' particle 
+              m_truthM_x.push_back(particle->decayVtx()->x());
+              m_truthM_y.push_back(particle->decayVtx()->y());
+              m_truthM_z.push_back(particle->decayVtx()->z());
+            } else {
+              m_truthM_x.push_back(999999);
+              m_truthM_y.push_back(999999);
+              m_truthM_z.push_back(999999);
+            }
+          }
+
+          if ( particle->pdgId() == 11) { // daughter particle (electron) 
+            m_truthd0_P.push_back(particle->p4().P());
+            m_truthd0_px.push_back(particle->p4().X());
+            m_truthd0_py.push_back(particle->p4().Y());
+            m_truthd0_pz.push_back(particle->p4().Z());
+
+            if ( particle->hasProdVtx()) {
+              m_truthd0_x.push_back(particle->prodVtx()->x());
+              m_truthd0_y.push_back(particle->prodVtx()->y());
+              m_truthd0_z.push_back(particle->prodVtx()->z());
+            } else {
+              m_truthd0_x.push_back(999999);
+              m_truthd0_y.push_back(999999);
+              m_truthd0_z.push_back(999999);
+            }
+            for (int station = 1; station < 4; ++station) {
+              m_truthd0_x.push_back(positions[station].x());
+              m_truthd0_y.push_back(positions[station].y());
+              m_truthd0_z.push_back(positions[station].z());
+            }
+          }
+          if ( particle->pdgId() == -11) { // daughter particle (positron)
+            m_truthd1_P.push_back(particle->p4().P());
+            m_truthd1_px.push_back(particle->p4().X());
+            m_truthd1_py.push_back(particle->p4().Y());
+            m_truthd1_pz.push_back(particle->p4().Z());
+
+            if ( particle->hasProdVtx()) {
+              m_truthd1_x.push_back(particle->prodVtx()->x());
+              m_truthd1_y.push_back(particle->prodVtx()->y());
+              m_truthd1_z.push_back(particle->prodVtx()->z());
+            } else {
+              m_truthd1_x.push_back(999999);
+              m_truthd1_y.push_back(999999);
+              m_truthd1_z.push_back(999999);
+            }
+            for (int station = 1; station < 4; ++station) {
+              m_truthd1_x.push_back(positions[station].x());
+              m_truthd1_y.push_back(positions[station].y());
+              m_truthd1_z.push_back(positions[station].z());
+            }
+          }
+        }
       }
     }	  
   }
 
   // load in calibrated calo container
-  if (m_doCalib) {
   SG::ReadHandle<xAOD::CalorimeterHitContainer> ecalCalibratedContainer { m_ecalCalibratedContainer, ctx };
   ATH_CHECK(ecalCalibratedContainer.isValid());
   for (auto hit : *ecalCalibratedContainer) {
@@ -679,6 +782,7 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     m_calo_total_nMIP += hit->Nmip();
     m_calo_total_E_dep += hit->E_dep();
     m_calo_total_E_EM += hit->E_EM();
+    m_calo_total_raw_E_EM += (hit->E_EM()*hit->fit_to_raw_ratio());
 
     ATH_MSG_DEBUG("Calibrated calo: ch is " << ch << ", edep is " << hit->E_dep() << ", E_EM is " << hit->E_EM() << ", Nmip is " << hit->Nmip() << ", fit_to_raw_ratio is " << hit->fit_to_raw_ratio());
 
@@ -694,6 +798,14 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     //}
   }
 
+  // add a fudged energy variable that corrects the MC to agree with the testbeam results
+  m_calo_total_E_EM_fudged = m_calo_total_E_EM;
+  m_calo_total_raw_E_EM_fudged = m_calo_total_raw_E_EM;
+  if (isMC) {
+    m_calo_total_E_EM_fudged = m_calo_total_E_EM_fudged * m_caloMC_FudgeFactor;
+    m_calo_total_raw_E_EM_fudged = m_calo_total_raw_E_EM_fudged * m_caloMC_FudgeFactor;
+  }
+
   // load in calibrated preshower container
   SG::ReadHandle<xAOD::CalorimeterHitContainer> preshowerCalibratedContainer { m_preshowerCalibratedContainer, ctx };
   ATH_CHECK(preshowerCalibratedContainer.isValid());
@@ -707,40 +819,24 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
 
     ATH_MSG_DEBUG("Calibrated preshower: ch is " << ch << ", edep is " << hit->E_dep() << ", E_EM is " << hit->E_EM() << ", Nmip is " << hit->Nmip() << ", fit_to_raw_ratio is " << hit->fit_to_raw_ratio());
   }
-  }
-
-  // process all waveeform data fro all scintillator and calorimeter channels
-  SG::ReadHandle<xAOD::WaveformHitContainer> vetoNuContainer { m_vetoNuContainer, ctx };
-  ATH_CHECK(vetoNuContainer.isValid());
-
-  SG::ReadHandle<xAOD::WaveformHitContainer> vetoContainer { m_vetoContainer, ctx };
-  ATH_CHECK(vetoContainer.isValid());
-
-  SG::ReadHandle<xAOD::WaveformHitContainer> triggerContainer { m_triggerContainer, ctx };
-  ATH_CHECK(triggerContainer.isValid());
-
-  SG::ReadHandle<xAOD::WaveformHitContainer> preshowerContainer { m_preshowerContainer, ctx };
-  ATH_CHECK(preshowerContainer.isValid());
-
-  SG::ReadHandle<xAOD::WaveformHitContainer> ecalContainer { m_ecalContainer, ctx };
-  ATH_CHECK(ecalContainer.isValid());
-
-  FillWaveBranches(*vetoNuContainer);
-  FillWaveBranches(*vetoContainer);
-  FillWaveBranches(*triggerContainer);
-  FillWaveBranches(*preshowerContainer);
-  FillWaveBranches(*ecalContainer);
 
   // enforce blinding such that events that miss the veto layers and have a large calo signal are skipped and not in the output root file
   // Only blind colliding BCIDs (+/- 1)
-  if ( (!isMC) && m_doBlinding && abs(m_distanceToCollidingBCID) <= 1) {
+  bool blinded_event = false;
+  if ((!isMC) && abs(m_distanceToCollidingBCID) <= 1) {
     if (m_calo_total_E_EM > m_blindingCaloThreshold ) { // energy is in MeV
-      if (m_wave_status[4] == 1 and m_wave_status[5] == 1 and m_wave_status[6] == 1 and m_wave_status[7] == 1 and m_wave_status[14] == 1) {  // hit status == 1 means it is below threshold. channles 4 and 5 are vetoNu, channels 6,7, and 14 are veto
-        return StatusCode::SUCCESS;
+      if (m_wave_raw_charge[4] < 40.0 and m_wave_raw_charge[5] < 40.0 and m_wave_raw_charge[6] < 40.0 and m_wave_raw_charge[7] < 40.0 and m_wave_raw_charge[14] < 40.0) {  // channels 4 and 5 are vetoNu, channels 6,7, and 14 are veto
+        blinded_event = true;
       }
     }
   }
 
+  if (blinded_event && m_doBlinding) {
+    return StatusCode::SUCCESS;
+  } else if (!blinded_event && m_onlyBlinded) {
+    return StatusCode::SUCCESS;
+  }
+
   SG::ReadDecorHandle<xAOD::EventInfo,uint32_t> eventInfo (m_eventInfoKey, ctx);
   if (eventInfo->errorState(xAOD::EventInfo_v1::SCT) == xAOD::EventInfo::Error) {
     m_propagationError = 1;
@@ -753,73 +849,6 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
   FaserActsGeometryContext faserGeometryContext = m_trackingGeometryTool->getNominalGeometryContext();
   auto gctx = faserGeometryContext.context();
 
-  // loop over clusters and store how many clusters are in each tracking station
-  SG::ReadHandle<Tracker::FaserSCT_ClusterContainer> clusterContainer { m_clusterContainer, ctx };
-  ATH_CHECK(clusterContainer.isValid());
-
-  for (auto collection : *clusterContainer)
-  {
-    Identifier id = collection->identify();
-    int station = m_sctHelper->station(id);
-    int clusters = (int) collection->size();
-    switch (station)
-    {
-      case 0:
-        m_station0Clusters += clusters;
-        // following lines commented out depict how to access cluster position
-        //for (auto cluster : *collection) {
-        //  if (cluster == nullptr) continue;
-        //  auto pos = cluster->globalPosition();
-        //  m_station0ClusterX.push_back(pos.x());
-        //}
-        break;
-      case 1:
-        m_station1Clusters += clusters;
-        break;
-      case 2:
-        m_station2Clusters += clusters;
-        break;
-      case 3:
-        m_station3Clusters += clusters;
-        break;
-      default:
-        ATH_MSG_FATAL("Unknown tracker station number " << station);
-        break;
-    }
-  }
-
-  // loop over spacepoints and store each space point position
-  SG::ReadHandle<FaserSCT_SpacePointContainer> spacePointContainer {m_spacePointContainerKey, ctx};
-  ATH_CHECK(spacePointContainer.isValid());
-  for (const FaserSCT_SpacePointCollection* spacePointCollection : *spacePointContainer) {
-    m_nspacepoints += spacePointCollection->size();
-    for (const Tracker::FaserSCT_SpacePoint *spacePoint: *spacePointCollection) {
-      auto pos = spacePoint->globalPosition();
-      m_spacepointX.push_back(pos.x());
-      m_spacepointY.push_back(pos.y());
-      m_spacepointZ.push_back(pos.z());
-    }
-  }
-
-  // loop over track segments and store position, momentum, chi2, and nDOF for each segment
-  SG::ReadHandle<TrackCollection> trackSegmentCollection {m_trackSegmentCollection, ctx};
-  ATH_CHECK(trackSegmentCollection.isValid());
-  for (const Trk::Track* trackSeg : *trackSegmentCollection) {
-    if (trackSeg == nullptr) continue;
-    m_ntracksegs += 1;
-    m_trackseg_Chi2.push_back(trackSeg->fitQuality()->chiSquared());
-    m_trackseg_DoF.push_back(trackSeg->fitQuality()->numberDoF());
-    auto SegParameters = trackSeg->trackParameters()->front();
-    const Amg::Vector3D SegPosition = SegParameters->position();
-    const Amg::Vector3D SegMomentum = SegParameters->momentum();
-    m_trackseg_x.push_back(SegPosition.x());
-    m_trackseg_y.push_back(SegPosition.y());
-    m_trackseg_z.push_back(SegPosition.z());
-    m_trackseg_px.push_back(SegMomentum.x());
-    m_trackseg_py.push_back(SegMomentum.y());
-    m_trackseg_pz.push_back(SegMomentum.z());
-  }
-
   // Write out all truth particle barcodes that have a momentum larger than MinMomentum (default is 50 GeV)
   std::map<int, size_t> truthParticleCount {};
   if (isMC) {
@@ -836,7 +865,7 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
   // extrapolate track to all scintillator positions and store extrapolated position and angle
   SG::ReadHandle<TrackCollection> trackCollection;
   if (m_useIFT) {
-    SG::ReadHandle<TrackCollection> tc {m_trackCollection, ctx}; // use track collection that excludes IFT
+    SG::ReadHandle<TrackCollection> tc {m_trackCollection, ctx}; // use track collection that includes IFT
     trackCollection = tc;
   } else {
     SG::ReadHandle<TrackCollection> tc {m_trackCollectionWithoutIFT, ctx}; // use track collection that excludes IFT
@@ -896,6 +925,20 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     m_pzdown.push_back(downstreamParameters->momentum().z());
     m_pdown.push_back(sqrt( pow(downstreamParameters->momentum().x(),2) + pow(downstreamParameters->momentum().y(),2) + pow(downstreamParameters->momentum().z(),2) ));
 
+    // find and store the position of the measurement on track with the largest radius
+    double maxRadius = 0;
+    Amg::Vector3D positionAtMaxRadius {};
+    for (const Trk::TrackParameters* trackParameters : *track->trackParameters()) {
+      if (radius(trackParameters->position()) > maxRadius) {
+        maxRadius = radius(trackParameters->position());
+        positionAtMaxRadius = trackParameters->position();
+      }
+    }
+    m_r_atMaxRadius.push_back(maxRadius);
+    m_x_atMaxRadius.push_back(positionAtMaxRadius.x());
+    m_y_atMaxRadius.push_back(positionAtMaxRadius.y());
+    m_z_atMaxRadius.push_back(positionAtMaxRadius.z());
+
     if (isMC) { // if simulation, store track truth info as well
       auto [truthParticle, hitCount] = m_trackTruthMatchingTool->getTruthParticle(track);
       if (truthParticle != nullptr) {
@@ -917,18 +960,18 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
           m_t_prodVtx_y.push_back(truthParticle->prodVtx()->y());
           m_t_prodVtx_z.push_back(truthParticle->prodVtx()->z());
         } else {
-          m_t_prodVtx_x.push_back(NaN);
-          m_t_prodVtx_y.push_back(NaN);
-          m_t_prodVtx_z.push_back(NaN);
+          m_t_prodVtx_x.push_back(999999);
+          m_t_prodVtx_y.push_back(999999);
+          m_t_prodVtx_z.push_back(999999);
         }
         if (truthParticle->hasDecayVtx()) {
           m_t_decayVtx_x.push_back(truthParticle->decayVtx()->x());
           m_t_decayVtx_y.push_back(truthParticle->decayVtx()->y());
           m_t_decayVtx_z.push_back(truthParticle->decayVtx()->z());
         } else {
-          m_t_decayVtx_x.push_back(NaN);
-          m_t_decayVtx_y.push_back(NaN);
-          m_t_decayVtx_z.push_back(NaN);
+          m_t_decayVtx_x.push_back(999999);
+          m_t_decayVtx_y.push_back(999999);
+          m_t_decayVtx_z.push_back(999999);
         }
         m_t_px.push_back(truthParticle->px());
         m_t_py.push_back(truthParticle->py());
@@ -940,167 +983,175 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
         m_t_eta.push_back(truthParticle->p4().Eta());
       } else {
         ATH_MSG_WARNING("Can not find truthParticle.");
-        setNaN();
+        clearTrackTruth();
       }
     } else {
-      setNaN();
+      clearTrackTruth();
     }
 
-    // fill extrapolation vectors with NaN, will get set to real number if the track extrapolation succeeds
-    m_xVetoNu.push_back(NaN);
-    m_yVetoNu.push_back(NaN);
-    m_thetaxVetoNu.push_back(NaN);
-    m_thetayVetoNu.push_back(NaN);
-    m_xVetoStation1.push_back(NaN);
-    m_yVetoStation1.push_back(NaN);
-    m_thetaxVetoStation1.push_back(NaN);
-    m_thetayVetoStation1.push_back(NaN);
-    m_xVetoStation2.push_back(NaN);
-    m_yVetoStation2.push_back(NaN);
-    m_thetaxVetoStation2.push_back(NaN);
-    m_thetayVetoStation2.push_back(NaN);
-    m_xTrig.push_back(NaN);
-    m_yTrig.push_back(NaN);
-    m_thetaxTrig.push_back(NaN);
-    m_thetayTrig.push_back(NaN);
-    m_xPreshower1.push_back(NaN);
-    m_yPreshower1.push_back(NaN);
-    m_thetaxPreshower1.push_back(NaN);
-    m_thetayPreshower1.push_back(NaN);
-    m_xPreshower2.push_back(NaN);
-    m_yPreshower2.push_back(NaN);
-    m_thetaxPreshower2.push_back(NaN);
-    m_thetayPreshower2.push_back(NaN);
-    m_xCalo.push_back(NaN);
-    m_yCalo.push_back(NaN);
-    m_thetaxCalo.push_back(NaN);
-    m_thetayCalo.push_back(NaN);
-
-    // extrapolate track from first station
-    if (stationMap.count(1) > 0) { // extrapolation crashes if the track parameters are is too far away to extrapolate
-      Amg::Vector3D position = upstreamParameters->position();
-      Amg::Vector3D momentum = upstreamParameters->momentum();
-      Acts::BoundVector params = Acts::BoundVector::Zero();
-      params[Acts::eBoundLoc0] = -position.y();
-      params[Acts::eBoundLoc1] = position.x();
-      params[Acts::eBoundPhi] = momentum.phi();
-      params[Acts::eBoundTheta] = momentum.theta();
-      params[Acts::eBoundQOverP] = upstreamParameters->charge() / momentum.mag();
-      params[Acts::eBoundTime] = 0;
-      auto startSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, position.z()), Acts::Vector3(0, 0, 1));
-      Acts::BoundTrackParameters startParameters(std::move(startSurface), params, upstreamParameters->charge());
-
-      auto targetSurface_VetoNu = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, -3112.0), Acts::Vector3(0, 0, 1)); // -3112 mm is z position of VetoNu planes touching
-      std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_VetoNu =m_extrapolationTool->propagate(ctx, startParameters, *targetSurface_VetoNu, Acts::backward);
-      if (targetParameters_VetoNu != nullptr) {
-        auto targetPosition_VetoNu = targetParameters_VetoNu->position(gctx);
-        auto targetMomentum_VetoNu = targetParameters_VetoNu->momentum();
-        m_xVetoNu[m_longTracks] = targetPosition_VetoNu.x();
-        m_yVetoNu[m_longTracks] = targetPosition_VetoNu.y();
-        m_thetaxVetoNu[m_longTracks] = atan(targetMomentum_VetoNu[0]/targetMomentum_VetoNu[2]);
-        m_thetayVetoNu[m_longTracks] = atan(targetMomentum_VetoNu[1]/targetMomentum_VetoNu[2]);
-      } else {
-        ATH_MSG_INFO("vetoNu null targetParameters");
-      }
-
-      auto targetSurface_Veto1 = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, -1769.65), Acts::Vector3(0, 0, 1)); // -1769.65 mm is z position of center of operational layer in Veto station 1
-      std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Veto1 =m_extrapolationTool->propagate(ctx, startParameters, *targetSurface_Veto1, Acts::backward);
-      if (targetParameters_Veto1 != nullptr) {
-        auto targetPosition_Veto1 = targetParameters_Veto1->position(gctx);
-        auto targetMomentum_Veto1 = targetParameters_Veto1->momentum();
-        m_xVetoStation1[m_longTracks] = targetPosition_Veto1.x();
-        m_yVetoStation1[m_longTracks] = targetPosition_Veto1.y();
-        m_thetaxVetoStation1[m_longTracks] = atan(targetMomentum_Veto1[0]/targetMomentum_Veto1[2]);
-        m_thetayVetoStation1[m_longTracks] = atan(targetMomentum_Veto1[1]/targetMomentum_Veto1[2]);
-      } else {
-        ATH_MSG_INFO("veto1 null targetParameters");
-      }
+    // fill extrapolation vectors with dummy values, will get set to real number if the track extrapolation succeeds
+    m_xVetoNu.push_back(999999);
+    m_yVetoNu.push_back(999999);
+    m_thetaxVetoNu.push_back(999999);
+    m_thetayVetoNu.push_back(999999);
+    m_xVetoStation1.push_back(999999);
+    m_yVetoStation1.push_back(999999);
+    m_thetaxVetoStation1.push_back(999999);
+    m_thetayVetoStation1.push_back(999999);
+    m_xVetoStation2.push_back(999999);
+    m_yVetoStation2.push_back(999999);
+    m_thetaxVetoStation2.push_back(999999);
+    m_thetayVetoStation2.push_back(999999);
+    m_xTrig.push_back(999999);
+    m_yTrig.push_back(999999);
+    m_thetaxTrig.push_back(999999);
+    m_thetayTrig.push_back(999999);
+    m_xPreshower1.push_back(999999);
+    m_yPreshower1.push_back(999999);
+    m_thetaxPreshower1.push_back(999999);
+    m_thetayPreshower1.push_back(999999);
+    m_xPreshower2.push_back(999999);
+    m_yPreshower2.push_back(999999);
+    m_thetaxPreshower2.push_back(999999);
+    m_thetayPreshower2.push_back(999999);
+    m_xCalo.push_back(999999);
+    m_yCalo.push_back(999999);
+    m_thetaxCalo.push_back(999999);
+    m_thetayCalo.push_back(999999);
+
+    // Define paramters for track extrapolation from most upstream measurement
+    Amg::Vector3D position_up = upstreamParameters->position();
+    Amg::Vector3D momentum_up = upstreamParameters->momentum();
+    Acts::BoundVector params_up = Acts::BoundVector::Zero();
+    params_up[Acts::eBoundLoc0] = -position_up.y();
+    params_up[Acts::eBoundLoc1] = position_up.x();
+    params_up[Acts::eBoundPhi] = momentum_up.phi();
+    params_up[Acts::eBoundTheta] = momentum_up.theta();
+    params_up[Acts::eBoundQOverP] = upstreamParameters->charge() / momentum_up.mag();
+    params_up[Acts::eBoundTime] = 0;
+    auto startSurface_up = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, position_up.z()), Acts::Vector3(0, 0, 1));
+    Acts::BoundTrackParameters startParameters_up(std::move(startSurface_up), params_up, upstreamParameters->charge());
+
+    // Define paramters for track extrapolation from most downstream measurement
+    Amg::Vector3D position_down = downstreamParameters->position();
+    Amg::Vector3D momentum_down = downstreamParameters->momentum();
+    Acts::BoundVector params_down = Acts::BoundVector::Zero();
+    params_down[Acts::eBoundLoc0] = -position_down.y();
+    params_down[Acts::eBoundLoc1] = position_down.x();
+    params_down[Acts::eBoundPhi] = momentum_down.phi();
+    params_down[Acts::eBoundTheta] = momentum_down.theta();
+    params_down[Acts::eBoundQOverP] = downstreamParameters->charge() / momentum_down.mag();
+    params_down[Acts::eBoundTime] = 0;
+    auto startSurface_down = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, position_down.z()), Acts::Vector3(0, 0, 1));
+    Acts::BoundTrackParameters startParameters_down(std::move(startSurface_down), params_down, downstreamParameters->charge());
+
+    // Extrapolate track to scintillators
+    auto targetSurface_VetoNu = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, -3112.0), Acts::Vector3(0, 0, 1)); // -3112 mm is z position of VetoNu planes touching
+    std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_VetoNu =m_extrapolationTool->propagate(ctx, startParameters_up, *targetSurface_VetoNu, Acts::backward);
+    if (targetParameters_VetoNu != nullptr) {
+      auto targetPosition_VetoNu = targetParameters_VetoNu->position(gctx);
+      auto targetMomentum_VetoNu = targetParameters_VetoNu->momentum();
+      m_xVetoNu[m_longTracks] = targetPosition_VetoNu.x();
+      m_yVetoNu[m_longTracks] = targetPosition_VetoNu.y();
+      m_thetaxVetoNu[m_longTracks] = atan(targetMomentum_VetoNu[0]/targetMomentum_VetoNu[2]);
+      m_thetayVetoNu[m_longTracks] = atan(targetMomentum_VetoNu[1]/targetMomentum_VetoNu[2]);
+    } else {
+      ATH_MSG_INFO("vetoNu null targetParameters");
+    }
 
-      auto targetSurface_Veto2 = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, -1609.65), Acts::Vector3(0, 0, 1)); // -1609.65 mm is z position of where planes touch in Veto station 2
-      std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Veto2 =m_extrapolationTool->propagate(ctx, startParameters, *targetSurface_Veto2, Acts::backward);
-      if (targetParameters_Veto2 != nullptr) {
-        auto targetPosition_Veto2 = targetParameters_Veto2->position(gctx);
-        auto targetMomentum_Veto2 = targetParameters_Veto2->momentum();
-        m_xVetoStation2[m_longTracks] = targetPosition_Veto2.x();
-        m_yVetoStation2[m_longTracks] = targetPosition_Veto2.y();
-        m_thetaxVetoStation2[m_longTracks] = atan(targetMomentum_Veto2[0]/targetMomentum_Veto2[2]);
-        m_thetayVetoStation2[m_longTracks] = atan(targetMomentum_Veto2[1]/targetMomentum_Veto2[2]);
-      } else {
-        ATH_MSG_INFO("veto2 null targetParameters");
-      }
+    auto targetSurface_Veto1 = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, -1769.65), Acts::Vector3(0, 0, 1)); // -1769.65 mm is z position of center of operational layer in Veto station 1
+    std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Veto1 =m_extrapolationTool->propagate(ctx, startParameters_up, *targetSurface_Veto1, Acts::backward);
+    if (targetParameters_Veto1 != nullptr) {
+      auto targetPosition_Veto1 = targetParameters_Veto1->position(gctx);
+      auto targetMomentum_Veto1 = targetParameters_Veto1->momentum();
+      m_xVetoStation1[m_longTracks] = targetPosition_Veto1.x();
+      m_yVetoStation1[m_longTracks] = targetPosition_Veto1.y();
+      m_thetaxVetoStation1[m_longTracks] = atan(targetMomentum_Veto1[0]/targetMomentum_Veto1[2]);
+      m_thetayVetoStation1[m_longTracks] = atan(targetMomentum_Veto1[1]/targetMomentum_Veto1[2]);
+    } else {
+      ATH_MSG_INFO("veto1 null targetParameters");
+    }
 
-      auto targetSurface_Trig = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, 0.0), Acts::Vector3(0, 0, 1)); // 0 mm is z position of Trig planes overlapping
-      std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Trig =m_extrapolationTool->propagate(ctx, startParameters, *targetSurface_Trig, Acts::backward); // must extrapolate backsards to trig plane if track starts in station 1
-      if (targetParameters_Trig != nullptr) {
-        auto targetPosition_Trig = targetParameters_Trig->position(gctx);
-        auto targetMomentum_Trig = targetParameters_Trig->momentum();
-        m_xTrig[m_longTracks] = targetPosition_Trig.x();
-        m_yTrig[m_longTracks] = targetPosition_Trig.y();
-        m_thetaxTrig[m_longTracks] = atan(targetMomentum_Trig[0]/targetMomentum_Trig[2]);
-        m_thetayTrig[m_longTracks] = atan(targetMomentum_Trig[1]/targetMomentum_Trig[2]);
-      } else {
-        ATH_MSG_INFO("Trig null targetParameters");
-      }
+    auto targetSurface_Veto2 = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, -1609.65), Acts::Vector3(0, 0, 1)); // -1609.65 mm is z position of where planes touch in Veto station 2
+    std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Veto2 =m_extrapolationTool->propagate(ctx, startParameters_up, *targetSurface_Veto2, Acts::backward);
+    if (targetParameters_Veto2 != nullptr) {
+      auto targetPosition_Veto2 = targetParameters_Veto2->position(gctx);
+      auto targetMomentum_Veto2 = targetParameters_Veto2->momentum();
+      m_xVetoStation2[m_longTracks] = targetPosition_Veto2.x();
+      m_yVetoStation2[m_longTracks] = targetPosition_Veto2.y();
+      m_thetaxVetoStation2[m_longTracks] = atan(targetMomentum_Veto2[0]/targetMomentum_Veto2[2]);
+      m_thetayVetoStation2[m_longTracks] = atan(targetMomentum_Veto2[1]/targetMomentum_Veto2[2]);
+    } else {
+      ATH_MSG_INFO("veto2 null targetParameters");
+    }
 
+    auto targetSurface_Trig = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, 0.0), Acts::Vector3(0, 0, 1)); // 0 mm is z position of Trig planes overlapping
+    std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Trig =m_extrapolationTool->propagate(ctx, startParameters_up, *targetSurface_Trig, Acts::backward); // must extrapolate backsards to trig plane if track starts in station 1
+    if (targetParameters_Trig != nullptr) {
+      auto targetPosition_Trig = targetParameters_Trig->position(gctx);
+      auto targetMomentum_Trig = targetParameters_Trig->momentum();
+      m_xTrig[m_longTracks] = targetPosition_Trig.x();
+      m_yTrig[m_longTracks] = targetPosition_Trig.y();
+      m_thetaxTrig[m_longTracks] = atan(targetMomentum_Trig[0]/targetMomentum_Trig[2]);
+      m_thetayTrig[m_longTracks] = atan(targetMomentum_Trig[1]/targetMomentum_Trig[2]);
+    } else {
+      ATH_MSG_INFO("Trig null targetParameters");
     }
 
-    // extrapolate track from tracking station 3
-    if (stationMap.count(3) > 0) { // extrapolation crashes if the track does not end in the Station 3, as it is too far away to extrapolate
-      Amg::Vector3D positionDown = downstreamParameters->position();
-      Amg::Vector3D momentumDown = downstreamParameters->momentum();
-      Acts::BoundVector paramsDown = Acts::BoundVector::Zero();
-      paramsDown[Acts::eBoundLoc0] = -positionDown.y();
-      paramsDown[Acts::eBoundLoc1] = positionDown.x();
-      paramsDown[Acts::eBoundPhi] = momentumDown.phi();
-      paramsDown[Acts::eBoundTheta] = momentumDown.theta();
-      paramsDown[Acts::eBoundQOverP] = downstreamParameters->charge() / momentumDown.mag();
-      paramsDown[Acts::eBoundTime] = 0;
-      auto startSurfaceDown = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, positionDown.z()), Acts::Vector3(0, 0, 1));
-      Acts::BoundTrackParameters startParametersDown(std::move(startSurfaceDown), paramsDown, downstreamParameters->charge());
-
-      auto targetSurface_Preshower1 = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, 2582.68), Acts::Vector3(0, 0, 1)); // 2582.68  mm is z position of center of upstream preshower layer
-      std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Preshower1 =m_extrapolationTool->propagate(ctx, startParametersDown, *targetSurface_Preshower1, Acts::forward);
-      if (targetParameters_Preshower1 != nullptr) {
-        auto targetPosition_Preshower1 = targetParameters_Preshower1->position(gctx);
-        auto targetMomentum_Preshower1 = targetParameters_Preshower1->momentum();
-        m_xPreshower1[m_longTracks] = targetPosition_Preshower1.x();
-        m_yPreshower1[m_longTracks] = targetPosition_Preshower1.y();
-        m_thetaxPreshower1[m_longTracks] = atan(targetMomentum_Preshower1[0]/targetMomentum_Preshower1[2]);
-        m_thetayPreshower1[m_longTracks] = atan(targetMomentum_Preshower1[1]/targetMomentum_Preshower1[2]);
-      } else {
-        ATH_MSG_INFO("Preshower1 null targetParameters");
-      }
+    auto targetSurface_Preshower1 = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, 2582.68), Acts::Vector3(0, 0, 1)); // 2582.68  mm is z position of center of upstream preshower layer
+    std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Preshower1 =m_extrapolationTool->propagate(ctx, startParameters_down, *targetSurface_Preshower1, Acts::forward);
+    if (targetParameters_Preshower1 != nullptr) {
+      auto targetPosition_Preshower1 = targetParameters_Preshower1->position(gctx);
+      auto targetMomentum_Preshower1 = targetParameters_Preshower1->momentum();
+      m_xPreshower1[m_longTracks] = targetPosition_Preshower1.x();
+      m_yPreshower1[m_longTracks] = targetPosition_Preshower1.y();
+      m_thetaxPreshower1[m_longTracks] = atan(targetMomentum_Preshower1[0]/targetMomentum_Preshower1[2]);
+      m_thetayPreshower1[m_longTracks] = atan(targetMomentum_Preshower1[1]/targetMomentum_Preshower1[2]);
+    } else {
+      ATH_MSG_INFO("Preshower1 null targetParameters");
+    }
 
-      auto targetSurface_Preshower2 = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, 2657.68), Acts::Vector3(0, 0, 1)); // 2657.68  mm is z position of center of downstream preshower layer
-      std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Preshower2 =m_extrapolationTool->propagate(ctx, startParametersDown, *targetSurface_Preshower2, Acts::forward);
-      if (targetParameters_Preshower2 != nullptr) {
-        auto targetPosition_Preshower2 = targetParameters_Preshower2->position(gctx);
-        auto targetMomentum_Preshower2 = targetParameters_Preshower2->momentum();
-        m_xPreshower2[m_longTracks] = targetPosition_Preshower2.x();
-        m_yPreshower2[m_longTracks] = targetPosition_Preshower2.y();
-        m_thetaxPreshower2[m_longTracks] = atan(targetMomentum_Preshower2[0]/targetMomentum_Preshower2[2]);
-        m_thetayPreshower2[m_longTracks] =  atan(targetMomentum_Preshower2[1]/targetMomentum_Preshower2[2]);
-      } else {
-        ATH_MSG_INFO("Preshower2 null targetParameters");
-      }
+    auto targetSurface_Preshower2 = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, 2657.68), Acts::Vector3(0, 0, 1)); // 2657.68  mm is z position of center of downstream preshower layer
+    std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Preshower2 =m_extrapolationTool->propagate(ctx, startParameters_down, *targetSurface_Preshower2, Acts::forward);
+    if (targetParameters_Preshower2 != nullptr) {
+      auto targetPosition_Preshower2 = targetParameters_Preshower2->position(gctx);
+      auto targetMomentum_Preshower2 = targetParameters_Preshower2->momentum();
+      m_xPreshower2[m_longTracks] = targetPosition_Preshower2.x();
+      m_yPreshower2[m_longTracks] = targetPosition_Preshower2.y();
+      m_thetaxPreshower2[m_longTracks] = atan(targetMomentum_Preshower2[0]/targetMomentum_Preshower2[2]);
+      m_thetayPreshower2[m_longTracks] =  atan(targetMomentum_Preshower2[1]/targetMomentum_Preshower2[2]);
+    } else {
+      ATH_MSG_INFO("Preshower2 null targetParameters");
+    }
 
-      auto targetSurface_Calo = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, 2760.0), Acts::Vector3(0, 0, 1)); // 2760  mm is estimated z position of calorimeter face
-      std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Calo =m_extrapolationTool->propagate(ctx, startParametersDown, *targetSurface_Calo, Acts::forward);
-      if (targetParameters_Calo != nullptr) {
-        auto targetPosition_Calo = targetParameters_Calo->position(gctx);
-        auto targetMomentum_Calo = targetParameters_Calo->momentum();
-        m_xCalo[m_longTracks] = targetPosition_Calo.x();
-        m_yCalo[m_longTracks] = targetPosition_Calo.y();
-        m_thetaxCalo[m_longTracks] = atan(targetMomentum_Calo[0]/targetMomentum_Calo[2]) ;
-        m_thetayCalo[m_longTracks] = atan(targetMomentum_Calo[1]/targetMomentum_Calo[2]) ;
-      } else {
-        ATH_MSG_INFO("Calo null targetParameters");
-      }
+    auto targetSurface_Calo = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, 2760.0), Acts::Vector3(0, 0, 1)); // 2760  mm is estimated z position of calorimeter face
+    std::unique_ptr<const Acts::BoundTrackParameters> targetParameters_Calo =m_extrapolationTool->propagate(ctx, startParameters_down, *targetSurface_Calo, Acts::forward);
+    if (targetParameters_Calo != nullptr) {
+      auto targetPosition_Calo = targetParameters_Calo->position(gctx);
+      auto targetMomentum_Calo = targetParameters_Calo->momentum();
+      m_xCalo[m_longTracks] = targetPosition_Calo.x();
+      m_yCalo[m_longTracks] = targetPosition_Calo.y();
+      m_thetaxCalo[m_longTracks] = atan(targetMomentum_Calo[0]/targetMomentum_Calo[2]) ;
+      m_thetayCalo[m_longTracks] = atan(targetMomentum_Calo[1]/targetMomentum_Calo[2]) ;
+    } else {
+      ATH_MSG_INFO("Calo null targetParameters");
     }
 
     m_longTracks++;
   }
 
+  if (!isMC) {
+    if (m_doTrackFilter) { // filter events: colliding bunches have at least one long track, non-colliding bunches have at least one long track or one calo module with raw_peak > 3 mV
+      if (m_longTracks == 0 && abs(m_distanceToCollidingBCID) <= 1) {
+        return StatusCode::SUCCESS;
+      } else if (abs(m_distanceToCollidingBCID) > 1) {
+        if (!(m_longTracks > 0 || m_wave_raw_peak[0] > 3.0 || m_wave_raw_peak[1] > 3.0 || m_wave_raw_peak[2] > 3.0 || m_wave_raw_peak[3] > 3.0 )) {
+          return StatusCode::SUCCESS;
+        }
+      }
+    }
+  }
+
   if (isMC) {
     for (auto &tp : truthParticleCount) {
       m_truthParticleBarcode.push_back(tp.first);
@@ -1109,40 +1160,99 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     }
   }
 
+  // loop over clusters and store how many clusters are in each tracking station
+  SG::ReadHandle<Tracker::FaserSCT_ClusterContainer> clusterContainer { m_clusterContainer, ctx };
+  ATH_CHECK(clusterContainer.isValid());
+
+  for (auto collection : *clusterContainer)
+  { 
+    Identifier id = collection->identify();
+    int station = m_sctHelper->station(id);
+    int clusters = (int) collection->size();
+    switch (station)
+    { 
+      case 0:
+        m_station0Clusters += clusters;
+        // following lines commented out depict how to access cluster position
+        //for (auto cluster : *collection) {
+        //  if (cluster == nullptr) continue;
+        //  auto pos = cluster->globalPosition();
+        //  m_station0ClusterX.push_back(pos.x());
+        //}
+        break;
+      case 1:
+        m_station1Clusters += clusters;
+        break;
+      case 2:
+        m_station2Clusters += clusters;
+        break;
+      case 3:
+        m_station3Clusters += clusters;
+        break;
+      default:
+        ATH_MSG_FATAL("Unknown tracker station number " << station);
+        break;
+    }
+  }
+
+  // loop over spacepoints and store each space point position
+  SG::ReadHandle<FaserSCT_SpacePointContainer> spacePointContainer {m_spacePointContainerKey, ctx};
+  ATH_CHECK(spacePointContainer.isValid());
+  for (const FaserSCT_SpacePointCollection* spacePointCollection : *spacePointContainer) {
+    m_nspacepoints += spacePointCollection->size();
+    for (const Tracker::FaserSCT_SpacePoint *spacePoint: *spacePointCollection) {
+      auto pos = spacePoint->globalPosition();
+      m_spacepointX.push_back(pos.x());
+      m_spacepointY.push_back(pos.y());
+      m_spacepointZ.push_back(pos.z());
+    }
+  }
+
+  // loop over track segments and store position, momentum, chi2, and nDOF for each segment
+  SG::ReadHandle<TrackCollection> trackSegmentCollection {m_trackSegmentCollection, ctx};
+  ATH_CHECK(trackSegmentCollection.isValid());
+  for (const Trk::Track* trackSeg : *trackSegmentCollection) {
+    if (trackSeg == nullptr) continue;
+    m_ntracksegs += 1;
+    m_trackseg_Chi2.push_back(trackSeg->fitQuality()->chiSquared());
+    m_trackseg_DoF.push_back(trackSeg->fitQuality()->numberDoF());
+    auto SegParameters = trackSeg->trackParameters()->front();
+    const Amg::Vector3D SegPosition = SegParameters->position();
+    const Amg::Vector3D SegMomentum = SegParameters->momentum();
+    m_trackseg_x.push_back(SegPosition.x());
+    m_trackseg_y.push_back(SegPosition.y());
+    m_trackseg_z.push_back(SegPosition.z());
+    m_trackseg_px.push_back(SegMomentum.x());
+    m_trackseg_py.push_back(SegMomentum.y());
+    m_trackseg_pz.push_back(SegMomentum.z());
+  }
+
   // finished processing event, now fill ntuple tree
   m_tree->Fill();
   m_eventsPassed += 1;
   return StatusCode::SUCCESS;
 }
 
-
 StatusCode NtupleDumperAlg::finalize()
 {
-  ATH_MSG_INFO("Number of events passed Ntuple selectioon = " << m_eventsPassed);
+  ATH_MSG_INFO("Number of events passed Ntuple selection = " << m_eventsPassed);
+  ATH_MSG_INFO("Number of events failing GRL selection   = " << m_eventsFailedGRL);
   return StatusCode::SUCCESS;
 }
 
-bool NtupleDumperAlg::waveformHitOK(const xAOD::WaveformHit* hit) const
-{
-    if (hit->status_bit(xAOD::WaveformStatus::THRESHOLD_FAILED) || hit->status_bit(xAOD::WaveformStatus::SECONDARY)) return false;
-    return true;
-}
-
 void
 NtupleDumperAlg::clearTree() const
 {
-  // set all float variables to NaN
-  // set all int variables to 999999 (can't set int to NaN, because NaN is a double)
-  // set all counter variables to zero
-  // set all trigger words to zero
-  m_run_number = 999999; 
-  m_event_number = 999999;
-  m_event_time = 999999;
-  m_bcid = 999999;
-
-  m_fillNumber = 999999;
-  m_betaStar = NaN;
-  m_crossingAngle = NaN;
+  // don't use NaN, they are annoying when doing cuts
+  m_run_number = 0; 
+  m_event_number = 0;
+  m_event_time = 0;
+  m_bcid = 0;
+  m_in_grl = 0;
+
+  m_fillNumber = 0;
+  m_betaStar = 0;
+  m_crossingAngle = 0;
   m_distanceToCollidingBCID = 999999;
   m_distanceToUnpairedB1 = 999999;
   m_distanceToUnpairedB2 = 999999;
@@ -1156,25 +1266,30 @@ NtupleDumperAlg::clearTree() const
   m_inputBitsNext=0;
 
   for(int ii=0;ii<15;ii++) {
-      m_wave_localtime[ii]=NaN;
-      m_wave_peak[ii]=NaN;
-      m_wave_width[ii]=NaN;
-      m_wave_charge[ii]=NaN;
-
-      m_wave_raw_peak[ii]=NaN;
-      m_wave_raw_charge[ii]=NaN;
-      m_wave_baseline_mean[ii]=NaN;
-      m_wave_baseline_rms[ii]=NaN;
+      m_wave_localtime[ii]=0;
+      m_wave_peak[ii]=0;
+      m_wave_width[ii]=0;
+      m_wave_charge[ii]=0;
+
+      m_wave_raw_peak[ii]=0;
+      m_wave_raw_charge[ii]=0;
+      m_wave_baseline_mean[ii]=0;
+      m_wave_baseline_rms[ii]=0;
       m_wave_status[ii]=1; // default = 1 means below threshold
 
-      m_calibrated_nMIP[ii]=NaN;
-      m_calibrated_E_dep[ii]=NaN;
-      m_calibrated_E_EM[ii]=NaN;
+      m_calibrated_nMIP[ii]=0;
+      m_calibrated_E_dep[ii]=0;
+      m_calibrated_E_EM[ii]=0;
   }
 
+  m_scintHit = 0;
+
   m_calo_total_nMIP=0;
   m_calo_total_E_dep=0;
   m_calo_total_E_EM=0;
+  m_calo_total_raw_E_EM=0;
+  m_calo_total_E_EM_fudged=0;
+  m_calo_total_raw_E_EM_fudged=0;
 
   m_preshower_total_nMIP=0;
   m_preshower_total_E_dep=0;
@@ -1264,6 +1379,11 @@ NtupleDumperAlg::clearTree() const
   m_thetaxCalo.clear();
   m_thetayCalo.clear();
 
+  m_x_atMaxRadius.clear();
+  m_y_atMaxRadius.clear();
+  m_z_atMaxRadius.clear();
+  m_r_atMaxRadius.clear();
+
   m_t_pdg.clear();
   m_t_barcode.clear();
   m_t_truthHitRatio.clear();
@@ -1292,7 +1412,7 @@ NtupleDumperAlg::clearTree() const
   m_truthParticleIsFiducial.clear();
 
   m_truthLeptonMomentum = 0;
-  m_truthBarcode = 0;
+  m_truthBarcode = -1;
   m_truthPdg = 0;
 
   m_truth_P.clear();
@@ -1334,28 +1454,32 @@ NtupleDumperAlg::clearTree() const
 
 }
 
-void NtupleDumperAlg::setNaN() const {
+void NtupleDumperAlg::clearTrackTruth() const {
   m_t_pdg.push_back(0);
   m_t_barcode.push_back(-1);
-  m_t_truthHitRatio.push_back(NaN);
-  m_t_prodVtx_x.push_back(NaN);
-  m_t_prodVtx_y.push_back(NaN);
-  m_t_prodVtx_z.push_back(NaN);
-  m_t_decayVtx_x.push_back(NaN);
-  m_t_decayVtx_y.push_back(NaN);
-  m_t_decayVtx_z.push_back(NaN);
-  m_t_px.push_back(NaN);
-  m_t_py.push_back(NaN);
-  m_t_pz.push_back(NaN);
-  m_t_theta.push_back(NaN);
-  m_t_phi.push_back(NaN);
-  m_t_p.push_back(NaN);
-  m_t_pT.push_back(NaN);
-  m_t_eta.push_back(NaN);
+  m_t_truthHitRatio.push_back(0);
+  m_t_prodVtx_x.push_back(999999);
+  m_t_prodVtx_y.push_back(999999);
+  m_t_prodVtx_z.push_back(999999);
+  m_t_decayVtx_x.push_back(999999);
+  m_t_decayVtx_y.push_back(999999);
+  m_t_decayVtx_z.push_back(999999);
+  m_t_px.push_back(0);
+  m_t_py.push_back(0);
+  m_t_pz.push_back(0);
+  m_t_theta.push_back(999999);
+  m_t_phi.push_back(999999);
+  m_t_p.push_back(0);
+  m_t_pT.push_back(0);
+  m_t_eta.push_back(999999);
   for (int station = 0; station < 4; ++station) {
-    m_t_st_x[station].push_back(NaN);
-    m_t_st_y[station].push_back(NaN);
-    m_t_st_z[station].push_back(NaN);
+    m_t_st_x[station].push_back(999999);
+    m_t_st_y[station].push_back(999999);
+    m_t_st_z[station].push_back(999999);
   }
   m_isFiducial.push_back(false);
 }
+
+double NtupleDumperAlg::radius(const Acts::Vector3 &position) const {
+  return std::sqrt(position.x() * position.x() + position.y() * position.y());
+}
diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
index 6d4ccc381a4b65831678a657c035ecc4e79c2b65..339c0838b22047dd146d71163acebdc472f7d1f3 100644
--- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
+++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
@@ -25,6 +25,7 @@
 #include "StoreGate/ReadDecorHandle.h"
 
 #include <vector>
+#include <nlohmann/json.hpp>
 
 class TTree;
 class TH1;
@@ -52,12 +53,13 @@ private:
 
   bool waveformHitOK(const xAOD::WaveformHit* hit) const;
   void clearTree() const;
-  void setNaN() const;
+  void clearTrackTruth() const;
   void addBranch(const std::string &name,float* var);
   void addBranch(const std::string &name,unsigned int* var);
   void addWaveBranches(const std::string &name, int nchannels, int first);
   void FillWaveBranches(const xAOD::WaveformHitContainer &wave) const;
   void addCalibratedBranches(const std::string &name, int nchannels, int first);
+  double radius(const Acts::Vector3 &position) const;
 
   ServiceHandle <ITHistSvc> m_histSvc;
 
@@ -101,17 +103,29 @@ private:
   const PreshowerID* m_preshowerHelper;
   const EcalID*      m_ecalHelper;
 
-  BooleanProperty m_useIFT            { this, "UseIFT", false, "Use IFT tracks" };
-  BooleanProperty m_doCalib           { this, "DoCalib", true, "Fill calibrated calorimeter quantities" };
-  BooleanProperty m_doBlinding        { this, "DoBlinding", true, "Blinding will not output events with Calo signal > 10 GeV e-" };
-  BooleanProperty m_useFlukaWeights   { this, "UseFlukaWeights", false, "Flag to weight events according to value stored in HepMC::GenEvent" };
-  BooleanProperty m_useGenieWeights   { this, "UseGenieWeights", false, "Flag to weight events according to Genie luminosity" };
-  IntegerProperty m_flukaCollisions   { this, "FlukaCollisions", 137130000, "Number of proton-proton collisions in FLUKA sample." };
-  DoubleProperty  m_flukaCrossSection { this, "FlukaCrossSection", 80.0, "Fluka p-p inelastic cross-section in millibarns." };
-  DoubleProperty  m_genieLuminosity   { this, "GenieLuminosity", 150.0, "Genie luminosity in inverse fb." };
-  DoubleProperty  m_minMomentum       { this, "MinMomentum", 50000.0, "Write out all truth particles with a momentum larger MinMomentum"};
+  BooleanProperty m_useIFT               { this, "UseIFT", false, "Use IFT tracks" };
+  BooleanProperty m_doCalib              { this, "DoCalib", true, "Fill calibrated calorimeter quantities" };
+  BooleanProperty m_doBlinding           { this, "DoBlinding", true, "Blinding will not output events with no veto signal adn Calo signal > 20 GeV e-" };
+  BooleanProperty m_onlyBlinded          { this, "OnlyBlinded", false, "Only events that would be blinded are saved" };
+  BooleanProperty m_doTrigFilter         { this, "DoTrigFilter", false, "Only events that pass trigger cuts are passed" };
+  BooleanProperty m_doScintFilter        { this, "DoScintFilter", false, "Only events that pass scintillator coincidence cuts are passed" };
+  BooleanProperty m_doTrackFilter        { this, "DoTrackFilter", true, "Only events that have >= 1 long track are passed, also non-colliding events with a track or calo signal are passed" };
 
-  DoubleProperty m_blindingCaloThreshold {this, "BlindingCaloThreshold", 25000.0, "Blind events with Ecal energy above threshold (in MeV)"}; 
+  BooleanProperty m_useFlukaWeights      { this, "UseFlukaWeights", false, "Flag to weight events according to value stored in HepMC::GenEvent" };
+  BooleanProperty m_useGenieWeights      { this, "UseGenieWeights", false, "Flag to weight events according to Genie luminosity" };
+  IntegerProperty m_flukaCollisions      { this, "FlukaCollisions", 137130000, "Number of proton-proton collisions in FLUKA sample." };
+  DoubleProperty  m_flukaCrossSection    { this, "FlukaCrossSection", 80.0, "Fluka p-p inelastic cross-section in millibarns." };
+  DoubleProperty  m_genieLuminosity      { this, "GenieLuminosity", 150.0, "Genie luminosity in inverse fb." };
+  DoubleProperty  m_minMomentum          { this, "MinMomentum", 50000.0, "Write out all truth particles with a momentum larger MinMomentum"};
+
+  DoubleProperty m_blindingCaloThreshold {this, "BlindingCaloThreshold", 100000.0, "Blind events with Ecal energy above threshold (in MeV)"}; 
+  DoubleProperty m_caloMC_FudgeFactor    {this, "CaloFudgeFactorMC", 1.088, "Correct the MC energy calibration by this fudge factor"};
+
+  BooleanProperty m_applyGoodRunsList    {this, "ApplyGoodRunsList", false, "Only write out events passing GRL (data only)"};
+  StringProperty m_goodRunsList          {this, "GoodRunsList", "", "GoodRunsList in json format"};
+
+  // json object to hold data read from GRL file (or empty if not)
+  nlohmann::json m_grl;
 
   double m_baseEventCrossSection {1.0};
   const double kfemtoBarnsPerMilliBarn {1.0e12};
@@ -124,6 +138,7 @@ private:
   mutable unsigned int m_event_number;
   mutable unsigned int m_event_time;
   mutable unsigned int m_bcid;
+  mutable unsigned int m_in_grl;
 
   mutable unsigned int m_fillNumber;
   mutable float m_betaStar;
@@ -150,7 +165,9 @@ private:
   mutable float m_wave_baseline_mean[15];
   mutable float m_wave_baseline_rms[15];
   mutable unsigned int m_wave_status[15];
-  
+ 
+  mutable unsigned int m_scintHit;
+ 
   mutable float m_calibrated_nMIP[15];
   mutable float m_calibrated_E_dep[15];
   mutable float m_calibrated_E_EM[15];
@@ -158,6 +175,10 @@ private:
   mutable float m_calo_total_nMIP;
   mutable float m_calo_total_E_dep;
   mutable float m_calo_total_E_EM;
+  mutable float m_calo_total_raw_E_EM;
+
+  mutable float m_calo_total_E_EM_fudged;
+  mutable float m_calo_total_raw_E_EM_fudged;
 
   mutable float m_preshower_total_nMIP;
   mutable float m_preshower_total_E_dep;
@@ -170,9 +191,6 @@ private:
   mutable float m_Preshower12_Edep;
   mutable float m_Preshower13_Edep;
 
-  mutable float m_MIP_sim_Edep_calo;
-  mutable float m_MIP_sim_Edep_preshower;
-
   mutable float m_clock_phase;
 
   mutable unsigned int m_station0Clusters;
@@ -247,6 +265,10 @@ private:
   mutable std::vector<double> m_yCalo;
   mutable std::vector<double> m_thetaxCalo;
   mutable std::vector<double> m_thetayCalo;
+  mutable std::vector<double> m_x_atMaxRadius;
+  mutable std::vector<double> m_y_atMaxRadius;
+  mutable std::vector<double> m_z_atMaxRadius;
+  mutable std::vector<double> m_r_atMaxRadius;
 
   mutable std::vector<int> m_t_pdg; // pdg code of the truth matched particle
   mutable std::vector<int> m_t_barcode; // barcode of the truth matched particle
@@ -324,16 +346,13 @@ private:
 
   mutable std::vector<int> m_truth_pdg; // pdg of first 10 truth particles 
 
-
-
-
   mutable double m_truthLeptonMomentum;
   mutable int    m_truthBarcode;
   mutable int    m_truthPdg;
   mutable double m_crossSection;
 
   mutable int    m_eventsPassed = 0;
-
+  mutable int    m_eventsFailedGRL = 0;
 };
 
 inline const ServiceHandle <ITHistSvc> &NtupleDumperAlg::histSvc() const {
diff --git a/Simulation/G4Faser/G4FaserAlg/test/G4FaserAlgConfigNew_Test.py b/Simulation/G4Faser/G4FaserAlg/test/G4FaserAlgConfigNew_Test.py
index d7ea3c3ab921b57e3df3c5c4be56c102f873c8f7..5ff7e6b96d1c04ce7869062feb8bea942e8f380e 100755
--- a/Simulation/G4Faser/G4FaserAlg/test/G4FaserAlgConfigNew_Test.py
+++ b/Simulation/G4Faser/G4FaserAlg/test/G4FaserAlgConfigNew_Test.py
@@ -20,8 +20,8 @@ if __name__ == '__main__':
 # Import and set config flags
 #
     from CalypsoConfiguration.AllConfigFlags import ConfigFlags
-    ConfigFlags.Exec.MaxEvents = 4  # can be overridden from command line with --maxEvt=<number>
-    ConfigFlags.Exec.SkipEvents = 0 # can be overridden from command line with --skipEvt=<number>
+    ConfigFlags.Exec.MaxEvents = 4  # can be overridden from command line with --evtMax=<number>
+    ConfigFlags.Exec.SkipEvents = 0 # can be overridden from command line with --skipEvents=<number>
     from AthenaConfiguration.Enums import ProductionStep
     ConfigFlags.Common.ProductionStep = ProductionStep.Simulation
 #
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/CMakeLists.txt b/Tracker/TrackerRecAlgs/OverlayRDO/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8d8665ba924345e2fdcdb409384cdb66291446c3
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/CMakeLists.txt
@@ -0,0 +1,12 @@
+atlas_subdir(OverlayRDO)
+
+atlas_add_component(
+        OverlayRDO
+        src/OverlayRDOAlg.h src/SelectRDOAlg.h src/BinRDOAlg.h src/HistoRDOAlg.h
+        src/OverlayRDOAlg.cxx src/SelectRDOAlg.cxx src/BinRDOAlg.cxx src/HistoRDOAlg.cxx
+        src/component/OverlayRDO_entries.cxx
+        LINK_LIBRARIES AthenaBaseComps StoreGateLib TrackerRawData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack xAODFaserWaveform
+)
+
+atlas_install_python_modules(python/*.py)
+# atlas_install_scripts(test/*.py)
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/python/HistoRDOConfig.py b/Tracker/TrackerRecAlgs/OverlayRDO/python/HistoRDOConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..cfb2dfefe48927be61f6416bd206a78e0b754eab
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/python/HistoRDOConfig.py
@@ -0,0 +1,127 @@
+"""
+    Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+"""
+
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+# from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+
+def HistoRDOAlgCfg(flags, **kwargs):
+
+    from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg
+    acc = FaserGeometryCfg(flags)
+    HistoRDOAlg = CompFactory.HistoRDOAlg("HistoRDOAlg",**kwargs)
+    acc.addEventAlgo(HistoRDOAlg)
+    HistoRDOAlg.OutputLevel = VERBOSE
+    # ItemList = []
+    # # ItemList += ["xAOD::EventInfo#" + chargePrefix + "EventInfo"]
+    # # ItemList += ["xAOD::EventAuxInfo#" + chargePrefix + "EventInfoAux."]
+    # ItemList += ["xAOD::EventInfo#EventInfo"]
+    # ItemList += ["xAOD::EventAuxInfo#EventInfoAux."]
+    # ItemList += ["TrackCollection#Orig_CKFTrackCollectionWithoutIFT"]
+    # ItemList += ["FaserSCT_RDO_Container#SCT_RDOs"]
+    # ItemList += ["FaserSCT_RDO_Container#SCT_EDGEMODE_RDOs"]
+    # # ItemList += ["Tracker::FaserSCT_ClusterContainer#" + chargePrefix + "SCT_ClusterContainer"]
+
+    # acc.merge(OutputStreamCfg(ConfigFlags,"RDO", ItemList=ItemList, disableEventTag=True))
+
+    thistSvc = CompFactory.THistSvc()
+    thistSvc.Output += ["HIST2 DATAFILE='RDOtree.root' OPT='RECREATE'"]
+    acc.addService(thistSvc)
+
+    return acc
+
+if __name__ == "__main__":
+
+    import sys
+    from AthenaCommon.Logging import log, logging
+    from AthenaCommon.Constants import DEBUG, VERBOSE, INFO
+    from AthenaCommon.Configurable import Configurable
+    from CalypsoConfiguration.AllConfigFlags import ConfigFlags
+    from AthenaConfiguration.TestDefaults import defaultTestFiles
+    from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
+    from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+    # from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+
+    # Set up logging and new style config
+    log.setLevel(DEBUG)
+    Configurable.configurableRun3Behavior = True
+
+    # Configure
+    # ConfigFlags.Overlay.DataOverlay = False
+    ConfigFlags.Input.Files = [ 
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin0RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin1RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin2RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin3RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin4RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin5RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin6RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin7RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myPos_Bin8RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin0RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin1RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin2RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin3RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin4RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin5RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin6RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin7RDO.pool.root',
+        '/home/dcasper/Work/faser/darkphoton/overlay/myNeg_Bin8RDO.pool.root',
+    ]
+    # ConfigFlags.Input.SecondaryFiles = [ 'Neg_RDO.pool.root' ]
+    # ConfigFlags.Input.Files = [ '/eos/experiment/faser/rec/2022/r0013/009171/Faser-Physics-009171-00006-r0013-xAOD.root']
+    # ConfigFlags.Input.SecondaryFiles = [ '/eos/experiment/faser/rec/2022/r0013/009166/Faser-Physics-009166-00485-r0013-xAOD.root' ]
+    # ConfigFlags.Output.RDOFileName = "Overlay.RDO.pool.root"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersionS
+    ConfigFlags.IOVDb.DatabaseInstance = "CONDBR3"               # Use data conditions for now
+    ConfigFlags.Input.ProjectName = "data21"                     # Needed to bypass autoconfig
+    ConfigFlags.Input.isMC = False                                # 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))
+
+    # algorithm
+    acc.merge(HistoRDOAlgCfg(ConfigFlags))
+
+    # from SGComps.AddressRemappingConfig import AddressRemappingCfg
+    # acc.merge(AddressRemappingCfg([
+    #     "xAOD::EventInfo#EventInfo->" + ConfigFlags.Overlay.SigPrefix + "EventInfo",
+    #     "xAOD::EventAuxInfo#EventInfoAux.->" + ConfigFlags.Overlay.SigPrefix + "EventInfoAux.",
+    # ]))
+
+    # Hack to avoid problem with our use of MC databases when isMC = False
+    replicaSvc = acc.getService("DBReplicaSvc")
+    replicaSvc.COOLSQLiteVetoPattern = ""
+    replicaSvc.UseCOOLSQLite = True
+    replicaSvc.UseCOOLFrontier = False
+    replicaSvc.UseGeomSQLite = True
+
+    # Timing
+    #acc.merge(MergeRecoTimingObjCfg(ConfigFlags))
+
+    # Dump config
+    # logging.getLogger('forcomps').setLevel(VERBOSE)
+    # acc.foreach_component("*").OutputLevel = VERBOSE
+    # acc.foreach_component("*ClassID*").OutputLevel = INFO
+    # acc.getCondAlgo("FaserSCT_AlignCondAlg").OutputLevel = VERBOSE
+    # acc.getCondAlgo("FaserSCT_DetectorElementCondAlg").OutputLevel = VERBOSE
+    # acc.getService("StoreGateSvc").Dump = True
+    # acc.getService("ConditionStore").Dump = True
+    # acc.printConfig(withDetails=True)
+    # ConfigFlags.dump()
+
+    # Execute and finish
+    sc = acc.run(maxEvents=-1)
+
+    # Success should be 0
+    sys.exit(not sc.isSuccess())    
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/python/OverlayRDOConfig.py b/Tracker/TrackerRecAlgs/OverlayRDO/python/OverlayRDOConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..bce33065b965395662707d54a82606d1e7038b9e
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/python/OverlayRDOConfig.py
@@ -0,0 +1,106 @@
+"""
+    Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+"""
+
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+
+def OverlayRDOAlgCfg(flags, **kwargs):
+
+    from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg
+    acc = FaserGeometryCfg(flags)
+    OverlayRDOAlg = CompFactory.OverlayRDOAlg("OverlayRDOAlg",**kwargs)
+    acc.addEventAlgo(OverlayRDOAlg)
+
+    ItemList = []
+    # ItemList += ["xAOD::EventInfo#" + chargePrefix + "EventInfo"]
+    # ItemList += ["xAOD::EventAuxInfo#" + chargePrefix + "EventInfoAux."]
+    ItemList += ["xAOD::EventInfo#EventInfo"]
+    ItemList += ["xAOD::EventAuxInfo#EventInfoAux."]
+    ItemList += ["TrackCollection#Orig_CKFTrackCollectionWithoutIFT"]
+    ItemList += ["FaserSCT_RDO_Container#SCT_RDOs"]
+    ItemList += ["FaserSCT_RDO_Container#SCT_EDGEMODE_RDOs"]
+    # ItemList += ["Tracker::FaserSCT_ClusterContainer#" + chargePrefix + "SCT_ClusterContainer"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,"RDO", ItemList=ItemList, disableEventTag=True))
+
+
+
+    return acc
+
+if __name__ == "__main__":
+
+    import sys
+    from AthenaCommon.Logging import log, logging
+    from AthenaCommon.Constants import DEBUG, VERBOSE, INFO
+    from AthenaCommon.Configurable import Configurable
+    from CalypsoConfiguration.AllConfigFlags import ConfigFlags
+    from AthenaConfiguration.TestDefaults import defaultTestFiles
+    from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
+    from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+    # from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+
+    # Set up logging and new style config
+    log.setLevel(DEBUG)
+    Configurable.configurableRun3Behavior = True
+
+    # Configure
+    ConfigFlags.Overlay.DataOverlay = False
+    ConfigFlags.Input.Files = [ 'Pos_RDO.pool.root']
+    ConfigFlags.Input.SecondaryFiles = [ 'Neg_RDO.pool.root' ]
+    # ConfigFlags.Input.Files = [ '/eos/experiment/faser/rec/2022/r0013/009171/Faser-Physics-009171-00006-r0013-xAOD.root']
+    # ConfigFlags.Input.SecondaryFiles = [ '/eos/experiment/faser/rec/2022/r0013/009166/Faser-Physics-009166-00485-r0013-xAOD.root' ]
+    ConfigFlags.Output.RDOFileName = "Overlay.RDO.pool.root"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersionS
+    ConfigFlags.IOVDb.DatabaseInstance = "CONDBR3"               # Use data conditions for now
+    ConfigFlags.Input.ProjectName = "data21"                     # Needed to bypass autoconfig
+    ConfigFlags.Input.isMC = False                                # 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))
+
+    # algorithm
+    acc.merge(OverlayRDOAlgCfg(ConfigFlags))
+
+    # from SGComps.AddressRemappingConfig import AddressRemappingCfg
+    # acc.merge(AddressRemappingCfg([
+    #     "xAOD::EventInfo#EventInfo->" + ConfigFlags.Overlay.SigPrefix + "EventInfo",
+    #     "xAOD::EventAuxInfo#EventInfoAux.->" + ConfigFlags.Overlay.SigPrefix + "EventInfoAux.",
+    # ]))
+
+    # Hack to avoid problem with our use of MC databases when isMC = False
+    replicaSvc = acc.getService("DBReplicaSvc")
+    replicaSvc.COOLSQLiteVetoPattern = ""
+    replicaSvc.UseCOOLSQLite = True
+    replicaSvc.UseCOOLFrontier = False
+    replicaSvc.UseGeomSQLite = True
+
+    # Timing
+    #acc.merge(MergeRecoTimingObjCfg(ConfigFlags))
+
+    # Dump config
+    # logging.getLogger('forcomps').setLevel(VERBOSE)
+    # acc.foreach_component("*").OutputLevel = VERBOSE
+    # acc.foreach_component("*ClassID*").OutputLevel = INFO
+    # acc.getCondAlgo("FaserSCT_AlignCondAlg").OutputLevel = VERBOSE
+    # acc.getCondAlgo("FaserSCT_DetectorElementCondAlg").OutputLevel = VERBOSE
+    # acc.getService("StoreGateSvc").Dump = True
+    # acc.getService("ConditionStore").Dump = True
+    # acc.printConfig(withDetails=True)
+    # ConfigFlags.dump()
+
+    # Execute and finish
+    sc = acc.run(maxEvents=-1)
+
+    # Success should be 0
+    sys.exit(not sc.isSuccess())    
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/python/SelectRDOConfig.py b/Tracker/TrackerRecAlgs/OverlayRDO/python/SelectRDOConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..29534a9b3293511b430305770da134c073b10213
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/python/SelectRDOConfig.py
@@ -0,0 +1,156 @@
+"""
+    Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
+"""
+
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+
+chargePrefix = "Pos_"
+
+def SelectRDOAlgCfg(flags, **kwargs):
+
+    from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg
+    acc = FaserGeometryCfg(flags)
+    SelectRDOAlg = CompFactory.SelectRDOAlg("SelectRDOAlg",**kwargs)
+    SelectRDOAlg.AcceptPositive = ("Pos_" in chargePrefix)
+    acc.addEventAlgo(SelectRDOAlg)
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin0RDO",Xmin=-66, Xmax=0,  Ymin=-66, Ymax=0))
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin1RDO",Xmin=0,   Xmax=66, Ymin=-66, Ymax=0))
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin2RDO",Xmin=-66, Xmax=0,  Ymin=0,   Ymax=66))
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin3RDO",Xmin=0,   Xmax=66, Ymin=0,   Ymax=66))
+
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin4RDO",Xmin=-33, Xmax=33,  Ymin=-33, Ymax=33))
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin5RDO",Xmin=-33, Xmax=33,  Ymin=33,  Ymax=99))
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin6RDO",Xmin=-33, Xmax=33,  Ymin=-99, Ymax=-33))
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin7RDO",Xmin=-99, Xmax=-33, Ymin=-33, Ymax=33))
+    acc.addEventAlgo(CompFactory.BinRDOAlg("Bin8RDO",Xmin=33,  Xmax=99,  Ymin=-33, Ymax=33))
+
+    ItemList = []
+    # ItemList += ["xAOD::EventInfo#" + chargePrefix + "EventInfo"]
+    # ItemList += ["xAOD::EventAuxInfo#" + chargePrefix + "EventInfoAux."]
+    ItemList += ["xAOD::EventInfo#EventInfo"]
+    ItemList += ["xAOD::EventAuxInfo#EventInfoAux."]
+    ItemList += ["TrackCollection#" + chargePrefix + "CKFTrackCollectionWithoutIFT"]
+    ItemList += ["FaserSCT_RDO_Container#" + chargePrefix + "SCT_RDOs"]
+    ItemList += ["FaserSCT_RDO_Container#" + chargePrefix + "SCT_EDGEMODE_RDOs"]
+    # ItemList += ["Tracker::FaserSCT_ClusterContainer#" + chargePrefix + "SCT_ClusterContainer"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin0RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo0 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin0RDO")
+    osrdo0.RequireAlgs += ["SelectRDOAlg","Bin0RDO"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin1RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo1 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin1RDO")
+    osrdo1.RequireAlgs += ["SelectRDOAlg","Bin1RDO"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin2RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo2 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin2RDO")
+    osrdo2.RequireAlgs += ["SelectRDOAlg","Bin2RDO"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin3RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo3 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin3RDO")
+    osrdo3.RequireAlgs += ["SelectRDOAlg","Bin3RDO"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin4RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo4 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin4RDO")
+    osrdo4.RequireAlgs += ["SelectRDOAlg","Bin4RDO"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin5RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo5 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin5RDO")
+    osrdo5.RequireAlgs += ["SelectRDOAlg","Bin5RDO"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin6RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo6 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin6RDO")
+    osrdo6.RequireAlgs += ["SelectRDOAlg","Bin6RDO"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin7RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo7 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin7RDO")
+    osrdo7.RequireAlgs += ["SelectRDOAlg","Bin7RDO"]
+
+    acc.merge(OutputStreamCfg(ConfigFlags,chargePrefix+"Bin8RDO", ItemList=ItemList, disableEventTag=True))
+    osrdo8 = acc.getEventAlgo("OutputStream" + chargePrefix + "Bin8RDO")
+    osrdo8.RequireAlgs += ["SelectRDOAlg","Bin8RDO"]
+
+    return acc
+
+
+
+if __name__ == "__main__":
+
+    import sys
+    from AthenaCommon.Logging import log, logging
+    from AthenaCommon.Constants import DEBUG, VERBOSE, INFO
+    from AthenaCommon.Configurable import Configurable
+    from CalypsoConfiguration.AllConfigFlags import ConfigFlags
+    from AthenaConfiguration.TestDefaults import defaultTestFiles
+    from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
+    from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+
+    # Set up logging and new style config
+    log.setLevel(DEBUG)
+    Configurable.configurableRun3Behavior = True
+
+    # Configure
+    ConfigFlags.Input.Files = [ #'/eos/experiment/faser/rec/2022/r0013/009171/Faser-Physics-009171-00006-r0013-xAOD.root' 
+                                '/eos/experiment/faser/rec/2022/r0013/009166/Faser-Physics-009166-00485-r0013-xAOD.root'
+                              ]
+    # ConfigFlags.Input.Files = [ 'Faser-Physics-009171-00006-r0013-xAOD.root', 
+    #                             'Faser-Physics-009166-00485-r0013-xAOD.root']
+    # ConfigFlags.Output.RDOFileName = chargePrefix + "RDO.pool.root"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersionS
+    ConfigFlags.IOVDb.DatabaseInstance = "CONDBR3"               # Use data conditions for now
+    ConfigFlags.Input.ProjectName = "data21"                     # Needed to bypass autoconfig
+    ConfigFlags.Input.isMC = False                                # 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))
+
+    # algorithm
+    acc.merge(SelectRDOAlgCfg(ConfigFlags, TrackCollection = "CKFTrackCollectionWithoutIFT", OutputTrackCollection = chargePrefix + "CKFTrackCollectionWithoutIFT"))
+
+    from SGComps.AddressRemappingConfig import AddressRemappingCfg
+    acc.merge(AddressRemappingCfg([
+        # "xAOD::EventInfo#EventInfo->" + chargePrefix + "EventInfo",
+        # "xAOD::EventAuxInfo#EventInfoAux.->" + chargePrefix + "EventInfoAux.",
+        # "TrackCollection#CKFTrackCollectionWithoutIFT->" + chargePrefix + "CKFTrackCollectionWithoutIFT",
+        "FaserSCT_RDO_Container#SCT_RDOs->" + chargePrefix + "SCT_RDOs",
+        "FaserSCT_RDO_Container#SCT_EDGEMODE_RDOs->" + chargePrefix + "SCT_EDGEMODE_RDOs",
+        # "Tracker::FaserSCT_ClusterContainer#SCT_ClusterContainer->" + chargePrefix + "SCT_ClusterContainer"
+    ]))
+
+    # Hack to avoid problem with our use of MC databases when isMC = False
+    replicaSvc = acc.getService("DBReplicaSvc")
+    replicaSvc.COOLSQLiteVetoPattern = ""
+    replicaSvc.UseCOOLSQLite = True
+    replicaSvc.UseCOOLFrontier = False
+    replicaSvc.UseGeomSQLite = True
+
+    # Timing
+    #acc.merge(MergeRecoTimingObjCfg(ConfigFlags))
+
+    # Dump config
+    # logging.getLogger('forcomps').setLevel(VERBOSE)
+    # acc.foreach_component("*").OutputLevel = VERBOSE
+    # acc.foreach_component("*ClassID*").OutputLevel = INFO
+    # acc.getCondAlgo("FaserSCT_AlignCondAlg").OutputLevel = VERBOSE
+    # acc.getCondAlgo("FaserSCT_DetectorElementCondAlg").OutputLevel = VERBOSE
+    # acc.getService("StoreGateSvc").Dump = True
+    # acc.getService("ConditionStore").Dump = True
+    # acc.printConfig(withDetails=True)
+    # ConfigFlags.dump()
+
+    # Execute and finish
+    sc = acc.run(maxEvents=-1)
+
+    # Success should be 0
+    sys.exit(not sc.isSuccess())    
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/BinRDOAlg.cxx b/Tracker/TrackerRecAlgs/OverlayRDO/src/BinRDOAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a917447dde480b9b82a316653fb8e76cf76a2156
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/BinRDOAlg.cxx
@@ -0,0 +1,55 @@
+#include "BinRDOAlg.h"
+#include "TrkTrack/Track.h"
+
+BinRDOAlg::BinRDOAlg(const std::string &name, 
+                           ISvcLocator *pSvcLocator)
+    : AthReentrantAlgorithm(name, pSvcLocator)
+{}
+
+
+StatusCode BinRDOAlg::initialize() 
+{
+  ATH_CHECK(m_trackCollection.initialize());
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode BinRDOAlg::execute(const EventContext &ctx) const 
+{
+
+  setFilterPassed(false, ctx);
+  SG::ReadHandle<TrackCollection> trackCollection {m_trackCollection, ctx};
+  if (!trackCollection.isValid() || trackCollection->size() == 0) return StatusCode::SUCCESS;
+
+  // track cuts
+
+  double xUpstream {0.0};
+  double yUpstream {0.0};
+
+  for (const Trk::Track* track : *trackCollection)
+  {
+    if (track == nullptr) continue;
+    const Trk::TrackParameters* upstreamParameters {nullptr};
+    for (auto params : *(track->trackParameters()))
+    {
+        if (params->position().z() < 0) continue;  // Ignore IFT hits
+        if (upstreamParameters == nullptr || params->position().z() < upstreamParameters->position().z()) upstreamParameters = params;
+    }
+    xUpstream = upstreamParameters->position().x();
+    yUpstream = upstreamParameters->position().y();
+    break;
+  }
+
+
+  // set filter passed
+
+  setFilterPassed(((xUpstream >= m_xMin) && (xUpstream <= m_xMax) && (yUpstream >= m_yMin) && (yUpstream <= m_yMax)), ctx);
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode BinRDOAlg::finalize() 
+{
+  return StatusCode::SUCCESS;
+}
+
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/BinRDOAlg.h b/Tracker/TrackerRecAlgs/OverlayRDO/src/BinRDOAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..f8e86e844adf11e7a1a4021b9e8d7a11757ce58c
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/BinRDOAlg.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "TrkTrack/TrackCollection.h"
+
+class BinRDOAlg : public AthReentrantAlgorithm {
+public:
+  BinRDOAlg(const std::string &name, ISvcLocator *pSvcLocator);
+  virtual ~BinRDOAlg() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext &ctx) const override;
+  virtual StatusCode finalize() override;
+
+private:
+
+  SG::ReadHandleKey<TrackCollection> m_trackCollection { this, "TrackCollection", "CKFTrackCollectionWithoutIFT", "Input track collection name" };
+  DoubleProperty     m_xMin {this, "Xmin", -25.0, "Minimum x position to accept"};
+  DoubleProperty     m_xMax {this, "Xmax",  25.0, "Maximum x position to accept"};
+  DoubleProperty     m_yMin {this, "Ymin", -25.0, "Minimum y position to accept"};
+  DoubleProperty     m_yMax {this, "Ymax",  25.0, "Maximum y position to accept"};
+
+};
+
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/HistoRDOAlg.cxx b/Tracker/TrackerRecAlgs/OverlayRDO/src/HistoRDOAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..78096c60004a9e183bf83aa235ef72501ddee4c4
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/HistoRDOAlg.cxx
@@ -0,0 +1,247 @@
+#include "HistoRDOAlg.h"
+#include "TrkTrack/Track.h"
+#include <TTree.h>
+
+
+HistoRDOAlg::HistoRDOAlg(const std::string &name, 
+                         ISvcLocator *pSvcLocator)
+    : AthReentrantAlgorithm(name, pSvcLocator), 
+      AthHistogramming(name),
+      m_histSvc("THistSvc/THistSvc", name) {}
+
+StatusCode HistoRDOAlg::initialize()
+{
+    m_tree = new TTree("overlay", "Overlay tree");
+
+    m_tree->Branch("longTracks1", &m_longTracks1, "longTracks1/I");
+    m_tree->Branch("x1", &m_x1);
+    m_tree->Branch("y1", &m_y1);
+    m_tree->Branch("p1", &m_p1);
+    m_tree->Branch("theta1", &m_theta1);
+    m_tree->Branch("phi1", &m_phi1);
+    m_tree->Branch("charge1", &m_charge1);
+    m_tree->Branch("chiSquared1", &m_chiSquared1);
+    m_tree->Branch("nDoF1", &m_nDoF1);
+    m_tree->Branch("pPolar1", &m_pTotPolar1, "pPolar1/D");
+
+    m_tree->Branch("longTracks2", &m_longTracks2, "longTracks2/I");
+    m_tree->Branch("x2", &m_x2);
+    m_tree->Branch("y2", &m_y2);
+    m_tree->Branch("p2", &m_p2);
+    m_tree->Branch("theta2", &m_theta2);
+    m_tree->Branch("phi2", &m_phi2);
+    m_tree->Branch("charge2", &m_charge2);
+    m_tree->Branch("chiSquared2", &m_chiSquared2);
+    m_tree->Branch("nDoF2", &m_nDoF2);
+    m_tree->Branch("pPolar2", &m_pTotPolar2, "pPolar2/D");
+
+    m_tree->Branch("yAgreement", &m_yAgreement, "yAgreement/D");
+    m_tree->Branch("matchFraction", &m_matchFraction, "matchFraction/D");
+
+    ATH_CHECK(histSvc()->regTree("/HIST2/tree", m_tree));
+
+    ATH_CHECK(m_trackCollection1.initialize());
+    ATH_CHECK(m_trackCollection2.initialize());
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode HistoRDOAlg::execute(const EventContext& ctx) const
+{
+
+    m_longTracks1 = 0;
+    m_x1.clear();
+    m_y1.clear();
+    m_p1.clear();
+    m_theta1.clear();
+    m_phi1.clear();
+    m_charge1.clear();
+    m_chiSquared1.clear();
+    m_nDoF1.clear();
+    m_pTotPolar1 = 0;
+
+    m_longTracks2 = 0;
+    m_x2.clear();
+    m_y2.clear();
+    m_p2.clear();
+    m_theta2.clear();
+    m_phi2.clear();
+    m_charge2.clear();
+    m_chiSquared2.clear();
+    m_nDoF2.clear();
+    m_pTotPolar2 = 0;
+
+    m_yAgreement = 0;
+    m_matchFraction = 0;
+
+    SG::ReadHandle<TrackCollection> trackCollection1 {m_trackCollection1, ctx};
+    // ATH_CHECK(trackCollection1.isValid());
+    SG::ReadHandle<TrackCollection> trackCollection2 {m_trackCollection2, ctx};
+    // ATH_CHECK(trackCollection2.isValid());
+
+    std::vector<const Trk::TrackParameters*> upstreamSingle {};
+    if (trackCollection1.isValid())
+    {
+        Amg::Vector3D pTot {0, 0, 0};
+        for (const Trk::Track* track : *trackCollection1)
+        {
+            if (track == nullptr) continue;
+            m_longTracks1++;
+            const Trk::TrackParameters* upstreamParameters {nullptr};
+            for (auto params : *(track->trackParameters()))
+            {
+                if (params->position().z() < 0) continue;  // Ignore IFT hits
+                if (upstreamParameters == nullptr || params->position().z() < upstreamParameters->position().z()) upstreamParameters = params;
+                ATH_MSG_VERBOSE("TrackCollection 1, Track " << m_longTracks1 << ", z = " << params->position().z() << ", y = " << params->position().y() << ", q/p(TeV) = " << params->charge()/(params->momentum().mag()/1e6));
+            }
+            upstreamSingle.push_back(upstreamParameters);
+            m_x1.push_back(upstreamParameters->position().x());
+            m_y1.push_back(upstreamParameters->position().y());
+            m_p1.push_back(upstreamParameters->momentum().mag());
+            m_charge1.push_back(upstreamParameters->charge());
+            m_theta1.push_back(asin(upstreamParameters->momentum().perp()/upstreamParameters->momentum().mag()));
+            m_phi1.push_back(atan2(upstreamParameters->momentum().y(), upstreamParameters->momentum().x()));
+            m_chiSquared1.push_back(track->fitQuality()->chiSquared());
+            m_nDoF1.push_back(track->fitQuality()->doubleNumberDoF());
+            pTot += upstreamParameters->momentum();
+        }
+        m_pTotPolar1 = asin(pTot.perp()/pTot.mag());
+    }
+
+    std::vector<const Trk::TrackParameters*> upstreamOverlay {};
+    if (trackCollection2.isValid())
+    {
+        Amg::Vector3D pTot {0, 0, 0};
+        for (const Trk::Track* track : *trackCollection2)
+        {
+            if (track == nullptr) continue;
+            m_longTracks2++;
+            const Trk::TrackParameters* upstreamParameters {nullptr};
+            for (auto params : *(track->trackParameters()))
+            {
+                if (params->position().z() < 0) continue;  // Ignore IFT hits
+                if (upstreamParameters == nullptr || params->position().z() < upstreamParameters->position().z()) upstreamParameters = params;
+                ATH_MSG_VERBOSE("TrackCollection 2, Track " << m_longTracks2 << ", z = " << params->position().z()<< ", y = " << params->position().y()<< ", q/p(TeV) = " << params->charge()/(params->momentum().mag()/1e6));
+            }
+            upstreamOverlay.push_back(upstreamParameters);
+            m_x2.push_back(upstreamParameters->position().x());
+            m_y2.push_back(upstreamParameters->position().y());
+            m_p2.push_back(upstreamParameters->momentum().mag());
+            m_charge2.push_back(upstreamParameters->charge());
+            m_theta2.push_back(asin(upstreamParameters->momentum().perp()/upstreamParameters->momentum().mag()));
+            m_phi2.push_back(atan2(upstreamParameters->momentum().y(), upstreamParameters->momentum().x()));
+            m_chiSquared2.push_back(track->fitQuality()->chiSquared());
+            m_nDoF2.push_back(track->fitQuality()->doubleNumberDoF());
+            pTot += upstreamParameters->momentum();
+        }
+        m_pTotPolar2 = asin(pTot.perp()/pTot.mag());
+    }
+
+    if (trackCollection1.isValid() && trackCollection2.isValid() && m_longTracks2 ==2)
+    {
+        double qOverPSingle1 = upstreamSingle[0]->charge()/upstreamSingle[0]->momentum().mag();
+        double qOverPCovSingle1 = (*upstreamSingle[0]->covariance())(4,4);
+        double qOverPSingle2 = upstreamSingle[1]->charge()/upstreamSingle[1]->momentum().mag();
+        double qOverPCovSingle2 = (*upstreamSingle[1]->covariance())(4,4);
+        double qOverPOverlay1 = upstreamOverlay[0]->charge()/upstreamOverlay[0]->momentum().mag();
+        double qOverPCovOverlay1 = (*upstreamOverlay[0]->covariance())(4,4);
+        double qOverPOverlay2 = upstreamOverlay[1]->charge()/upstreamOverlay[1]->momentum().mag();
+        double qOverPCovOverlay2 = (*upstreamOverlay[1]->covariance())(4,4);
+
+        double agreementDirect = pow(qOverPSingle1 - qOverPOverlay1, 2) / (qOverPCovSingle1 + qOverPCovOverlay1) +
+                                 pow(qOverPSingle2 - qOverPOverlay2, 2) / (qOverPCovSingle2 + qOverPCovOverlay2);
+        double agreementSwap   = pow(qOverPSingle1 - qOverPOverlay2, 2) / (qOverPCovSingle1 + qOverPCovOverlay2) +
+                                 pow(qOverPSingle2 - qOverPOverlay1, 2) / (qOverPCovSingle2 + qOverPCovOverlay1);
+
+        const Trk::Track* firstSingle {nullptr};
+        const Trk::Track* firstOverlay {nullptr};
+        const Trk::Track* secondSingle {nullptr};
+        const Trk::Track* secondOverlay {nullptr};
+        if (agreementDirect <= agreementSwap)
+        {
+            firstSingle = (*trackCollection1)[0];
+            firstOverlay = (*trackCollection2)[0];
+            secondSingle = (*trackCollection1)[1];
+            secondOverlay = (*trackCollection2)[1];
+            ATH_MSG_VERBOSE("Matched q/p = " << qOverPSingle1 << " with " << qOverPOverlay1 << ", and q/p = " << qOverPSingle2 << " with " << qOverPOverlay2);
+        }
+        else
+        {
+            firstSingle = (*trackCollection1)[0];
+            firstOverlay = (*trackCollection2)[1];
+            secondSingle = (*trackCollection1)[1];
+            secondOverlay = (*trackCollection2)[0];
+            ATH_MSG_VERBOSE("Matched q/p = " << qOverPSingle1 << " with " << qOverPOverlay2 << ", and q/p = " << qOverPSingle2 << " with " << qOverPOverlay1);
+        }
+        int nZMatch {0};
+        auto singleParameters = firstSingle->trackParameters();
+        auto overlayParameters = firstOverlay->trackParameters();
+        for (size_t iSingle = 0, iOverlay = 0; (iSingle < singleParameters->size()) && (iOverlay < overlayParameters->size());)
+        {
+            auto singleState = (*singleParameters)[iSingle];
+            auto overlayState = (*overlayParameters)[iOverlay];
+            auto singleCov = singleState->covariance();
+            auto overlayCov = overlayState->covariance();
+            if (abs(singleState->position().z() - overlayState->position().z()) < 0.03)
+            {
+                nZMatch++;
+                m_yAgreement += pow(singleState->position().y() - overlayState->position().y(), 2)/((*singleCov)(1,1) + (*overlayCov)(1,1));
+                iSingle++;
+                iOverlay++;
+                ATH_MSG_VERBOSE("Matched (1): " << singleState->position().z() << " and " << overlayState->position().z());
+            }
+            else if (singleState->position().z() < overlayState->position().z())
+            {
+                ATH_MSG_VERBOSE("z not matched (1): " << singleState->position().z() << " vs " << overlayState->position().z());
+                iSingle++;
+            }
+            else if (overlayState->position().z() < singleState->position().z())
+            {
+                ATH_MSG_VERBOSE("z not matched (1): " << singleState->position().z() << " vs " << overlayState->position().z());
+                iOverlay++;
+            }
+        }
+        double matchFract1 { ((double) nZMatch)/singleParameters->size() };
+        ATH_MSG_VERBOSE("MatchFract1: " << matchFract1);
+        nZMatch = 0;
+        singleParameters = secondSingle->trackParameters();
+        overlayParameters = secondOverlay->trackParameters();
+        for (size_t iSingle = 0, iOverlay = 0; (iSingle < singleParameters->size()) && (iOverlay < overlayParameters->size());)
+        {
+            auto singleState = (*singleParameters)[iSingle];
+            auto overlayState = (*overlayParameters)[iOverlay];
+            auto singleCov = singleState->covariance();
+            auto overlayCov = overlayState->covariance();
+            if (abs(singleState->position().z() - overlayState->position().z()) < 0.03)
+            {
+                nZMatch++;
+                m_yAgreement += pow(singleState->position().y() - overlayState->position().y(), 2)/((*singleCov)(1,1) + (*overlayCov)(1,1));
+                iSingle++;
+                iOverlay++;
+                ATH_MSG_VERBOSE("Matched (2): " << singleState->position().z() << " and " << overlayState->position().z());
+            }
+            else if (singleState->position().z() < overlayState->position().z())
+            {
+                ATH_MSG_VERBOSE("z not matched (2): " << singleState->position().z() << " vs " << overlayState->position().z());
+                iSingle++;
+            }
+            else if (overlayState->position().z() < singleState->position().z())
+            {
+                ATH_MSG_VERBOSE("z not matched (2): " << singleState->position().z() << " vs " << overlayState->position().z());
+                iOverlay++;
+            }
+        }
+        double matchFract2 { ((double) nZMatch)/singleParameters->size() };
+        ATH_MSG_VERBOSE("MatchFract2: " << matchFract2);
+        m_matchFraction = std::min(matchFract1, matchFract2);
+        ATH_MSG_VERBOSE("MatchFraction: " << m_matchFraction);
+    }
+
+    m_tree->Fill();
+    return StatusCode::SUCCESS;
+}
+
+StatusCode HistoRDOAlg::finalize()
+{
+    return StatusCode::SUCCESS;
+}
\ No newline at end of file
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/HistoRDOAlg.h b/Tracker/TrackerRecAlgs/OverlayRDO/src/HistoRDOAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..4fe18f7ad71e075b8b37b56518058da6b7be6f0e
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/HistoRDOAlg.h
@@ -0,0 +1,58 @@
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "AthenaBaseComps/AthHistogramming.h"
+#include "TrkTrack/TrackCollection.h"
+
+#include <vector>
+
+class TTree;
+
+class HistoRDOAlg : public AthReentrantAlgorithm, AthHistogramming {
+public:
+  HistoRDOAlg(const std::string &name, ISvcLocator *pSvcLocator);
+  virtual ~HistoRDOAlg() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext &ctx) const override;
+  virtual StatusCode finalize() override;
+  const ServiceHandle <ITHistSvc> &histSvc() const;
+
+private:
+
+  ServiceHandle <ITHistSvc> m_histSvc;
+
+  SG::ReadHandleKey<TrackCollection> m_trackCollection1 { this, "TrackCollection1", "Pos_CKFTrackCollectionWithoutIFT", "Input track collection 1 name" };
+  SG::ReadHandleKey<TrackCollection> m_trackCollection2 { this, "TrackCollection2", "Neg_CKFTrackCollectionWithoutIFT", "Input track collection 2 name" };
+
+//   mutable TTree* m_tree;
+
+  mutable TTree* m_tree;
+
+  mutable int m_longTracks1;
+  mutable std::vector<double> m_p1;
+  mutable std::vector<double> m_theta1;
+  mutable std::vector<double> m_phi1;
+  mutable std::vector<double> m_x1;
+  mutable std::vector<double> m_y1;
+  mutable std::vector<double> m_charge1;
+  mutable std::vector<double> m_chiSquared1;
+  mutable std::vector<double> m_nDoF1;
+  mutable double m_pTotPolar1;
+
+  mutable int m_longTracks2;
+  mutable std::vector<double> m_p2;
+  mutable std::vector<double> m_theta2;
+  mutable std::vector<double> m_phi2;
+  mutable std::vector<double> m_x2;
+  mutable std::vector<double> m_y2;
+  mutable std::vector<double> m_charge2;
+  mutable std::vector<double> m_chiSquared2;
+  mutable std::vector<double> m_nDoF2;
+  mutable double m_pTotPolar2;
+
+  mutable double m_yAgreement;
+  mutable double m_matchFraction;
+
+};
+
+inline const ServiceHandle <ITHistSvc> &HistoRDOAlg::histSvc() const {
+  return m_histSvc;
+}
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/OverlayRDOAlg.cxx b/Tracker/TrackerRecAlgs/OverlayRDO/src/OverlayRDOAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..9b88a96f9590462d5c03d6ffba601598185ec109
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/OverlayRDOAlg.cxx
@@ -0,0 +1,186 @@
+#include "OverlayRDOAlg.h"
+#include "TrkTrack/Track.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "Identifier/Identifier.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrackerReadoutGeometry/SiDetectorElement.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include <cmath>
+
+
+
+OverlayRDOAlg::OverlayRDOAlg(const std::string &name, 
+                                    ISvcLocator *pSvcLocator)
+    : AthReentrantAlgorithm(name, pSvcLocator)
+    {}
+
+StatusCode OverlayRDOAlg::initialize() 
+{
+  ATH_CHECK(detStore()->retrieve(m_sctHelper, "FaserSCT_ID"));
+  ATH_CHECK(m_posTrackKey.initialize());
+  ATH_CHECK(m_negTrackKey.initialize());
+  ATH_CHECK(m_posRdoKey.initialize());
+  ATH_CHECK(m_negRdoKey.initialize());
+  ATH_CHECK(m_posEdgeModeRdoKey.initialize());
+  ATH_CHECK(m_negEdgeModeRdoKey.initialize());
+  ATH_CHECK(m_outRdoKey.initialize());
+  ATH_CHECK(m_outEdgeModeRdoKey.initialize());
+  ATH_CHECK(m_outputTrackCollection.initialize());
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode OverlayRDOAlg::execute(const EventContext &ctx) const 
+{
+
+  SG::ReadHandle<TrackCollection> posTrackCollection {m_posTrackKey, ctx};
+  ATH_CHECK(posTrackCollection.isValid());
+  SG::ReadHandle<TrackCollection> negTrackCollection {m_negTrackKey, ctx};
+  ATH_CHECK(negTrackCollection.isValid());
+  SG::ReadHandle<FaserSCT_RDO_Container> posRdoContainer {m_posRdoKey, ctx};
+  ATH_CHECK(posRdoContainer.isValid());
+  SG::ReadHandle<FaserSCT_RDO_Container> negRdoContainer {m_negRdoKey, ctx};
+  ATH_CHECK(negRdoContainer.isValid());
+  SG::ReadHandle<FaserSCT_RDO_Container> posEdgeModeRdoContainer {m_posEdgeModeRdoKey, ctx};
+  ATH_CHECK(posEdgeModeRdoContainer.isValid());
+  SG::ReadHandle<FaserSCT_RDO_Container> negEdgeModeRdoContainer {m_negEdgeModeRdoKey, ctx};
+  ATH_CHECK(negEdgeModeRdoContainer.isValid());
+
+  // SCT_RDOs
+
+  std::map<IdentifierHash, std::vector<const FaserSCT_RDO_Collection*> > rdoDB {};
+  processContainer(*posRdoContainer, rdoDB);
+  processContainer(*negRdoContainer, rdoDB);
+
+  SG::WriteHandle<FaserSCT_RDO_Container> outRdoContainer {m_outRdoKey, ctx};
+  ATH_CHECK(outRdoContainer.record(std::make_unique<FaserSCT_RDO_Container>(m_sctHelper->wafer_hash_max())));
+
+  processDB(rdoDB, outRdoContainer, [](int i) {return true;});
+
+  // SCT_EDGEMODE_RDOs
+
+  rdoDB.clear();
+  processContainer(*posEdgeModeRdoContainer, rdoDB);
+  processContainer(*negEdgeModeRdoContainer, rdoDB);
+
+  SG::WriteHandle<FaserSCT_RDO_Container> outEdgeModeRdoContainer {m_outEdgeModeRdoKey, ctx};
+  ATH_CHECK(outEdgeModeRdoContainer.record(std::make_unique<FaserSCT_RDO_Container>(m_sctHelper->wafer_hash_max())));
+
+  processDB(rdoDB, outEdgeModeRdoContainer, [](int hitPattern) { return (((hitPattern & 0x2) == 0 ) || ((hitPattern & 0x4) != 0) ) ? false : true;});
+
+  // Tracks
+
+  SG::WriteHandle<TrackCollection> outputTrackCollection {m_outputTrackCollection, ctx};
+  std::unique_ptr<TrackCollection> outputTracks = std::make_unique<TrackCollection>();
+
+  for (auto theTrack : *posTrackCollection)
+  {
+    outputTracks->push_back(cloneTrack(theTrack) );
+  }
+
+  for (auto theTrack : *negTrackCollection)
+  {
+    outputTracks->push_back(cloneTrack(theTrack) );
+  }
+
+  ATH_CHECK(outputTrackCollection.record(std::move(outputTracks)));
+
+  return StatusCode::SUCCESS;
+}
+
+
+void 
+OverlayRDOAlg::processDB(const std::map<IdentifierHash, std::vector<const FaserSCT_RDO_Collection*> >& rdoDB, SG::WriteHandle<FaserSCT_RDO_Container>& outRdoContainer, std::function<bool(int)> lambda) const
+{
+  for (auto& entry : rdoDB)
+  {
+    IdentifierHash waferHash = entry.first;
+    Identifier id = m_sctHelper->wafer_id(waferHash);
+    auto vec = entry.second;
+    if (vec.size() == 0)
+    {
+      std::cout << "Unexpected zero-length collection vector for ID = " << id << " ..." << std::endl;
+      continue;
+    }
+    std::unique_ptr<FaserSCT_RDO_Collection> current_collection = std::make_unique<FaserSCT_RDO_Collection>(waferHash);
+    current_collection->setIdentifier(id);
+    std::map<int, unsigned int> stripMap;
+    for (auto& collection : vec)
+    {
+      for (auto rawData : *collection)
+      {
+        // const FaserSCT3_RawData* data = dynamic_cast<const FaserSCT3_RawData*>(rawData);
+        Identifier stripID = rawData->identify();
+        int stripNumber = m_sctHelper->strip(stripID);
+        int groupSize = rawData->getGroupSize();
+        unsigned int dataWord = rawData->getWord();
+        // int time = data->getTimeBin();
+        // std::cout << "Word: " << std::hex << dataWord << std::dec << " Time: " << time << std::endl;
+	for (int i = stripNumber; i < stripNumber + groupSize; i++)
+	  stripMap[i] |= dataWord;
+      }
+    }
+
+    for (auto stripEntry : stripMap)
+    {
+      Identifier rdoID {m_sctHelper->strip_id(id, stripEntry.first)};
+      if (!lambda(stripEntry.second>>22)) continue;
+      current_collection->emplace_back(new FaserSCT3_RawData(rdoID, stripEntry.second, std::vector<int> {}));
+    }
+
+    outRdoContainer->getWriteHandle(waferHash).addOrDelete(std::move(current_collection));
+  }
+}
+
+
+Trk::Track* OverlayRDOAlg::cloneTrack(const Trk::Track* theTrack) const
+{
+  Trk::TrackInfo i { theTrack->info() };
+  Trk::FitQuality* q = new Trk::FitQuality { *(theTrack->fitQuality()) };
+  DataVector<const Trk::TrackStateOnSurface>* s = new DataVector<const Trk::TrackStateOnSurface> {};  
+
+  for (auto oldState : (*theTrack->trackStateOnSurfaces()))
+  {
+    const Trk::CurvilinearParameters* oldParam = dynamic_cast<const Trk::CurvilinearParameters*>(oldState->trackParameters());
+    std::unique_ptr<Trk::TrackParameters> newParam { new Trk::CurvilinearParameters { *oldParam } };
+    Trk::TrackStateOnSurface* newState = new Trk::TrackStateOnSurface { nullptr, newParam.release() };
+    s->push_back(newState);
+  }
+  std::unique_ptr<DataVector<const Trk::TrackStateOnSurface>> sink {s};
+  return new Trk::Track {i, std::move(*sink) , q } ;
+}
+
+void OverlayRDOAlg::processContainer(const FaserSCT_RDO_Container& container, std::map<IdentifierHash, std::vector<const FaserSCT_RDO_Collection*> >& rdoDB) const
+{
+  size_t nLong{0};
+  size_t nData{0};
+  for( const auto& collection : container)
+  {
+      if (collection->empty()) continue;
+      Identifier id = collection->identify();
+      IdentifierHash hash = m_sctHelper->wafer_hash(id);
+      if (rdoDB.count(hash))
+      {
+        rdoDB[hash].push_back(collection);
+      }
+      else
+      {
+        rdoDB[hash] = std::vector<const FaserSCT_RDO_Collection*>{collection};
+      }
+      for(const auto& rawdata : *collection)
+      {
+        nData++;
+        if (rawdata->getGroupSize() > 1) nLong++;
+      }
+  }
+  std::cout << nLong << " long data out of " << nData << " total RDOs" << std::endl;
+}
+
+
+StatusCode OverlayRDOAlg::finalize() 
+{
+  return StatusCode::SUCCESS;
+}
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/OverlayRDOAlg.h b/Tracker/TrackerRecAlgs/OverlayRDO/src/OverlayRDOAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..eba2f90d7cde46873bbe9124e17876fef5059e5a
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/OverlayRDOAlg.h
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "TrkTrack/TrackCollection.h"
+#include "TrackerRawData/FaserSCT_RDO_Container.h"
+
+class TTree;
+class FaserSCT_ID;
+
+class OverlayRDOAlg : public AthReentrantAlgorithm {
+public:
+  OverlayRDOAlg(const std::string &name, ISvcLocator *pSvcLocator);
+  virtual ~OverlayRDOAlg() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext &ctx) const override;
+  virtual StatusCode finalize() override;
+
+private:
+
+  void processContainer(const FaserSCT_RDO_Container& container, std::map<IdentifierHash, std::vector<const FaserSCT_RDO_Collection*> >& rdoDB) const;
+  void processDB(const std::map<IdentifierHash, std::vector<const FaserSCT_RDO_Collection*> >& db, SG::WriteHandle<FaserSCT_RDO_Container>& outContainer, std::function<bool(int)> lambda) const;
+  Trk::Track* cloneTrack(const Trk::Track* originalTrack) const;
+
+  SG::ReadHandleKey<TrackCollection> m_posTrackKey { this, "PosTrackCollection", "Pos_CKFTrackCollectionWithoutIFT", "Input track collection name" };
+  SG::ReadHandleKey<TrackCollection> m_negTrackKey { this, "NegTrackCollection", "Neg_CKFTrackCollectionWithoutIFT", "Input track collection name" };
+  SG::ReadHandleKey<FaserSCT_RDO_Container> m_posRdoKey { this, "PosRdoContainer", "Pos_SCT_RDOs"};
+  SG::ReadHandleKey<FaserSCT_RDO_Container> m_negRdoKey { this, "NegRdoContainer", "Neg_SCT_RDOs"};
+  SG::ReadHandleKey<FaserSCT_RDO_Container> m_posEdgeModeRdoKey { this, "PosEdgeModeRdoContainer", "Pos_SCT_EDGEMODE_RDOs"};
+  SG::ReadHandleKey<FaserSCT_RDO_Container> m_negEdgeModeRdoKey { this, "NegEdgeModeRdoContainer", "Neg_SCT_EDGEMODE_RDOs"};
+
+  SG::WriteHandleKey<FaserSCT_RDO_Container> m_outRdoKey{this, "OutputRDOObjectName", "SCT_RDOs", "Output RDO Object name"};
+  SG::WriteHandleKey<FaserSCT_RDO_Container> m_outEdgeModeRdoKey{this, "OutputEDGEMODEObjectName", "SCT_EDGEMODE_RDOs", "Output EDGEMODE Object name"};
+  SG::WriteHandleKey<TrackCollection> m_outputTrackCollection { this, "OutputTrackCollection", "Orig_CKFTrackCollectionWithoutIFT", "Output track collection name"};
+
+  const FaserSCT_ID* m_sctHelper;
+
+};
+
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/SelectRDOAlg.cxx b/Tracker/TrackerRecAlgs/OverlayRDO/src/SelectRDOAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b33bed9d18ac4d51f2e2dc7ae9fc52217cbffc67
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/SelectRDOAlg.cxx
@@ -0,0 +1,187 @@
+#include "SelectRDOAlg.h"
+#include "TrkTrack/Track.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerPrepRawData/FaserSCT_ClusterContainer.h"
+#include "TrackerPrepRawData/FaserSCT_ClusterCollection.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "Identifier/Identifier.h"
+#include <cmath>
+
+SelectRDOAlg::SelectRDOAlg(const std::string &name, 
+                           ISvcLocator *pSvcLocator)
+    : AthReentrantAlgorithm(name, pSvcLocator)
+{}
+
+
+StatusCode SelectRDOAlg::initialize() 
+{
+  ATH_CHECK(m_trackCollection.initialize());
+  ATH_CHECK(m_segmentCollection.initialize());
+  ATH_CHECK(m_clusterContainer.initialize());
+  ATH_CHECK(m_triggerContainer.initialize());
+  ATH_CHECK(m_outputTrackCollection.initialize());
+
+  ATH_CHECK(detStore()->retrieve(m_sctHelper, "FaserSCT_ID"));
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode SelectRDOAlg::execute(const EventContext &ctx) const 
+{
+
+  setFilterPassed(false, ctx);
+  SG::ReadHandle<TrackCollection> trackCollection {m_trackCollection, ctx};
+  if (!trackCollection.isValid() || trackCollection->size() == 0) return StatusCode::SUCCESS;
+
+  SG::ReadHandle<TrackCollection> segmentCollection {m_segmentCollection, ctx};
+  if (!segmentCollection.isValid()) return StatusCode::SUCCESS;
+
+  SG::ReadHandle<Tracker::FaserSCT_ClusterContainer> clusterContainer { m_clusterContainer, ctx };
+  if (!clusterContainer.isValid()) return StatusCode::SUCCESS;
+
+  SG::ReadHandle<xAOD::WaveformHitContainer> triggerContainer { m_triggerContainer, ctx };
+  if (!triggerContainer.isValid()) return StatusCode::SUCCESS;
+
+  // segment cuts
+
+  std::vector<int> segmentCount {0, 0, 0, 0};
+  int nSegments {0};
+
+  for (const Trk::Track* segment : *segmentCollection)
+  {
+    if (segment == nullptr) continue;
+    nSegments++;
+    for (auto measurement : *(segment->measurementsOnTrack()))
+    {
+        const Tracker::FaserSCT_ClusterOnTrack* cluster = dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack*>(measurement);
+        if (cluster != nullptr)
+        {
+            Identifier id = cluster->identify();
+            int station = m_sctHelper->station(id);
+            if (station >= 0 && station <= 3)
+            {
+              segmentCount[station]++;
+              break;
+            }
+        }
+    }
+  }
+  if ((nSegments > m_maxSegmentsTotal) || (segmentCount[1] > m_maxSegmentsStation) || (segmentCount[2] > m_maxSegmentsStation) || (segmentCount[3] > m_maxSegmentsStation))
+    return StatusCode::SUCCESS;
+
+  // track cuts
+
+  int nTracks {0};
+  int nLongTracks {0};
+  double chi2PerDoF {0};
+  double charge {0};
+  double maxRadius {0};
+  double momentum {0};
+  const Trk::Track* theTrack {nullptr};
+
+  for (const Trk::Track* track : *trackCollection)
+  {
+    if (track == nullptr) continue;
+    nTracks++;
+    std::set<int> stationMap;
+    std::set<std::pair<int, int>> layerMap;
+
+    // Check for hit in the three downstream stations
+    for (auto measurement : *(track->measurementsOnTrack()))
+    {
+        const Tracker::FaserSCT_ClusterOnTrack* cluster = dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack*>(measurement);
+        if (cluster != nullptr)
+        {
+            Identifier id = cluster->identify();
+            int station = m_sctHelper->station(id);
+            int layer = m_sctHelper->layer(id);
+            stationMap.emplace(station);
+            layerMap.emplace(station, layer);
+        }
+    }
+    if (stationMap.count(1) == 0 || stationMap.count(2) == 0 || stationMap.count(3) == 0) continue;
+
+    int nLayers = std::count_if(layerMap.begin(), layerMap.end(), [](std::pair<int,int> p){return p.first != 0;});
+    if (nLayers < m_minLayers) continue;
+    nLongTracks++;
+    const Trk::TrackParameters* upstreamParameters {nullptr};
+    for (auto params : *(track->trackParameters()))
+    {
+        if (params->position().z() < 0) continue;  // Ignore IFT hits
+        double radius = sqrt(pow(params->position().x(), 2) + pow(params->position().y(), 2));
+        if (radius > maxRadius) maxRadius = radius;
+        if (upstreamParameters == nullptr || params->position().z() < upstreamParameters->position().z()) upstreamParameters = params;
+    }
+
+    momentum = upstreamParameters->momentum().mag()/1000;
+    chi2PerDoF = track->fitQuality()->chiSquared() / track->fitQuality()->numberDoF();
+    charge = upstreamParameters->charge();
+    theTrack = track;
+  }
+
+  if ((nTracks != 1) || (nLongTracks != 1) || (momentum < m_minMomentum) || (momentum > m_maxMomentum) || (chi2PerDoF > m_maxChi2PerDoF) || ((charge>0) != m_acceptPositive) || (maxRadius > m_maxRadius))
+    return StatusCode::SUCCESS;
+
+  // cluster cuts
+
+  // std::vector<int> clusterCount {0, 0, 0, 0};
+  // for (auto collection : *clusterContainer)
+  // {
+  //   Identifier id = collection->identify();
+  //   int station = m_sctHelper->station(id);
+  //   if (station >= 0 && station <= 3) clusterCount[station] += collection->size();
+  // }
+  // if ((clusterCount[1] > m_maxClustersStation) || (clusterCount[2] > m_maxClustersStation) || (clusterCount[3] > m_maxClustersStation))
+  //   return StatusCode::SUCCESS;
+
+  // waveform charge cuts
+
+  std::vector<double> timingCharge {0, 0, 0, 0};
+  const int baseChannel = 8;
+
+  for (auto hit : *triggerContainer) {
+    if ((hit->hit_status()&2)==0) { // dont store secondary hits as they can overwrite the primary hit
+      int ch=hit->channel();
+      timingCharge[ch - baseChannel] += hit->integral()/50;
+    }
+  }
+  if ((timingCharge[0] + timingCharge[1] > m_maxTimingCharge) || (timingCharge[2] + timingCharge[3] > m_maxTimingCharge))
+    return StatusCode::SUCCESS;
+
+  // passes all cuts
+
+  // copy track without associated clusters
+  
+  SG::WriteHandle<TrackCollection> outputTrackCollection {m_outputTrackCollection, ctx};
+  std::unique_ptr<TrackCollection> outputTracks = std::make_unique<TrackCollection>();
+
+  Trk::TrackInfo i { theTrack->info() };
+  Trk::FitQuality* q = new Trk::FitQuality { *(theTrack->fitQuality()) };
+  DataVector<const Trk::TrackStateOnSurface>* s = new DataVector<const Trk::TrackStateOnSurface> {};  
+
+  for (auto oldState : (*theTrack->trackStateOnSurfaces()))
+  {
+    const Trk::CurvilinearParameters* oldParam = dynamic_cast<const Trk::CurvilinearParameters*>(oldState->trackParameters());
+    std::unique_ptr<Trk::TrackParameters> newParam { new Trk::CurvilinearParameters { *oldParam } };
+    Trk::TrackStateOnSurface* newState = new Trk::TrackStateOnSurface { nullptr, newParam.release() };
+    s->push_back(newState);
+  }
+  std::unique_ptr<DataVector<const Trk::TrackStateOnSurface>> sink {s};
+  outputTracks->push_back(new Trk::Track {i, std::move(*sink) , q } );
+
+  ATH_CHECK(outputTrackCollection.record(std::move(outputTracks)));
+
+  // set filter passed
+
+  setFilterPassed(true, ctx);
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode SelectRDOAlg::finalize() 
+{
+  return StatusCode::SUCCESS;
+}
+
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/SelectRDOAlg.h b/Tracker/TrackerRecAlgs/OverlayRDO/src/SelectRDOAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..842282d534a23159625a1dabf59c3fbc0494c343
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/SelectRDOAlg.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "TrkTrack/TrackCollection.h"
+#include "TrackerPrepRawData/FaserSCT_ClusterContainer.h"
+#include "xAODFaserWaveform/WaveformHitContainer.h"
+#include "xAODFaserWaveform/WaveformHit.h"
+#include <xAODEventInfo/EventInfo.h>
+
+
+class FaserSCT_ID;
+
+class SelectRDOAlg : public AthReentrantAlgorithm {
+public:
+  SelectRDOAlg(const std::string &name, ISvcLocator *pSvcLocator);
+  virtual ~SelectRDOAlg() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext &ctx) const override;
+  virtual StatusCode finalize() override;
+
+private:
+
+  const FaserSCT_ID* m_sctHelper;
+
+  SG::ReadHandleKey<TrackCollection> m_trackCollection { this, "TrackCollection", "CKFTrackCollectionWithoutIFT", "Input track collection name" };
+  SG::ReadHandleKey<TrackCollection> m_segmentCollection { this, "SegmentCollection", "SegmentFit", "Track segment collection name" };
+  SG::ReadHandleKey<Tracker::FaserSCT_ClusterContainer> m_clusterContainer { this, "ClusterContainer", "SCT_ClusterContainer" };
+  SG::ReadHandleKey<xAOD::WaveformHitContainer> m_triggerContainer { this, "TriggerContainer", "TriggerWaveformHits", "Trigger hit container name" };
+  SG::WriteHandleKey<TrackCollection> m_outputTrackCollection { this, "OutputTrackCollection", "Pos_CKFTrackCollectionWithoutIFT", "Output track collection name"};
+
+  IntegerProperty   m_minLayers {this, "MinLayers", 7, "Minimum hit layers to accept track"};
+  DoubleProperty    m_maxChi2PerDoF {this, "MaxChi2PerDoF", 25.0, "Maximum chi2 per degree of freedom to accept track"};
+  DoubleProperty    m_minMomentum {this, "MinMomentumGeV", 50.0, "Minimum momentum in GeV to accept track"};
+  DoubleProperty    m_maxMomentum {this, "MaxMomentumGeV", 5000.0, "Maximum momentum in GeV to accept track"};
+  DoubleProperty    m_maxRadius {this, "MaxRadiusMm", 95.0, "Maximum radius at first measurement to accept track"};
+  IntegerProperty   m_maxSegmentsTotal {this, "MaxSegmentsTotal", 4, "Maximum number of segments in three stations to accept track"};
+  IntegerProperty   m_maxSegmentsStation {this, "MaxSegmentsStation", 2, "Maximum number of segments in any single station to accept track"};
+  // IntegerProperty   m_maxClustersStation {this, "MaxClustersStation", 9, "Maximum number of clusters in any single station to accept track"};
+  DoubleProperty    m_maxTimingCharge {this, "MaxTimingChargePc", 70.0, "Maximum charge in pC recorded by upper or lower timing scintillator"};
+  BooleanProperty   m_acceptPositive {this, "AcceptPositive", true, "Accept positive (true) or negative (false) charged tracks"};
+
+};
+
diff --git a/Tracker/TrackerRecAlgs/OverlayRDO/src/component/OverlayRDO_entries.cxx b/Tracker/TrackerRecAlgs/OverlayRDO/src/component/OverlayRDO_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..13e61cb84654d0c3e4096ffa01b0f6efbeb6184c
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/OverlayRDO/src/component/OverlayRDO_entries.cxx
@@ -0,0 +1,9 @@
+#include "../OverlayRDOAlg.h"
+#include "../SelectRDOAlg.h"
+#include "../BinRDOAlg.h"
+#include "../HistoRDOAlg.h"
+
+DECLARE_COMPONENT(OverlayRDOAlg)
+DECLARE_COMPONENT(SelectRDOAlg)
+DECLARE_COMPONENT(BinRDOAlg)
+DECLARE_COMPONENT(HistoRDOAlg)
diff --git a/Tracker/TrackerRecAlgs/TrackerSegmentFit/python/TrackerSegmentFitConfig.py b/Tracker/TrackerRecAlgs/TrackerSegmentFit/python/TrackerSegmentFitConfig.py
index f0de1582f2ccf47270203d4d1d6eb9a6bca341e9..eeaadd061bbc5b1c3c14af0a1e128e529d687cae 100644
--- a/Tracker/TrackerRecAlgs/TrackerSegmentFit/python/TrackerSegmentFitConfig.py
+++ b/Tracker/TrackerRecAlgs/TrackerSegmentFit/python/TrackerSegmentFitConfig.py
@@ -15,9 +15,9 @@ def SegmentFitAlgBasicCfg(flags, **kwargs):
     Tracker__SegmentFitAlg=CompFactory.Tracker.SegmentFitAlg
     acc.addEventAlgo(Tracker__SegmentFitAlg(**kwargs))
     
-    thistSvc = CompFactory.THistSvc()
-    thistSvc.Output += ["HIST DATAFILE='SegmentFitHistograms.root' OPT='RECREATE'"]
-    acc.addService(thistSvc)
+    # thistSvc = CompFactory.THistSvc()
+    # thistSvc.Output += ["HIST DATAFILE='SegmentFitHistograms.root' OPT='RECREATE'"]
+    # acc.addService(thistSvc)
     return acc
 
 # with output defaults
diff --git a/Tracker/TrackerRecTools/FaserSiSpacePointTool/src/TrackerSpacePointMakerTool.cxx b/Tracker/TrackerRecTools/FaserSiSpacePointTool/src/TrackerSpacePointMakerTool.cxx
index 814fc31a5b82c9705472a283ba8abfd074ed067b..c8a7df00173d6503683ac47148baefd8bf54da4c 100644
--- a/Tracker/TrackerRecTools/FaserSiSpacePointTool/src/TrackerSpacePointMakerTool.cxx
+++ b/Tracker/TrackerRecTools/FaserSiSpacePointTool/src/TrackerSpacePointMakerTool.cxx
@@ -122,7 +122,7 @@ FaserSCT_SpacePoint* TrackerSpacePointMakerTool::makeSCT_SpacePoint(const Tracke
        {
          if (fabs(lambda1) > 1 + m_stripLengthTolerance)
          {
-           ATH_MSG_WARNING("Intersection lies outside the bounds of both strips");
+           ATH_MSG_DEBUG("Intersection lies outside the bounds of both strips");
            ok = false;
          }
        }
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.cxx
index f990be9458e0ee2ac0a5bdef8b670703f1424c26..14616c6f42dd0e60df67c5b73358da599093130c 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.cxx
@@ -50,6 +50,7 @@ StatusCode CKF2::initialize() {
   ATH_CHECK(m_kalmanFitterTool1.retrieve());
   ATH_CHECK(m_createTrkTrackTool.retrieve());
   ATH_CHECK(m_trackCollection.initialize());
+  // ATH_CHECK(m_allTrackCollection.initialize());
   ATH_CHECK(m_eventInfoKey.initialize());
   if (m_performanceWriter && !m_noDiagnostics) {
     ATH_CHECK(m_performanceWriterTool.retrieve());
@@ -85,6 +86,9 @@ StatusCode CKF2::execute() {
   SG::WriteHandle trackContainer{m_trackCollection, ctx};
   std::unique_ptr<TrackCollection> outputTracks = std::make_unique<TrackCollection>();
 
+  // SG::WriteHandle allTrackContainer{m_allTrackCollection, ctx};
+  // std::unique_ptr<TrackCollection> outputAllTracks = std::make_unique<TrackCollection>();
+
   std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry
       = m_trackingGeometryTool->trackingGeometry();
 
@@ -157,10 +161,19 @@ StatusCode CKF2::execute() {
       // TODO use status bits for different errors
       // result.error() == Acts::CombinatorialKalmanFilterError::NoTrackFound
       if (result.error() == Acts::PropagatorError::StepCountLimitReached ||
-          result.error() == Acts::CombinatorialKalmanFilterError::PropagationReachesMaxSteps) {
-        if (!eventInfo->updateErrorState(xAOD::EventInfo::SCT, xAOD::EventInfo::Error)) {
-          ATH_MSG_WARNING (" cannot set error state for SCT");
-        }
+          result.error() == Acts::CombinatorialKalmanFilterError::PropagationReachesMaxSteps) 
+      {
+          try
+          {
+            if (!eventInfo->updateErrorState(xAOD::EventInfo::SCT, xAOD::EventInfo::Error)) 
+            {
+              ATH_MSG_WARNING ("Cannot set error state for SCT.");
+            }
+          }
+          catch (...)
+          {
+              ATH_MSG_DEBUG ("SCT error state is locked.");
+          }
       }
       continue;
     }
@@ -183,6 +196,33 @@ StatusCode CKF2::execute() {
   // select all tracks with at least 13 heats and with 6 or less shared hits, starting from the best track
   // TODO use Gaudi parameters for the number of hits and shared hits
   // TODO allow shared hits only in the first station?
+  // std::vector<FaserActsRecMultiTrajectory> rawTrajectories {};
+  // for (auto raw : allTrajectories)
+  // {
+  //   rawTrajectories.push_back(raw.trajectory);
+  // }
+  // for (const FaserActsRecMultiTrajectory &traj : rawTrajectories) {
+  //   const auto params = traj.trackParameters(traj.tips().front());
+  //   ATH_MSG_DEBUG("Fitted parameters (raw)");
+  //   ATH_MSG_DEBUG("  params:   " << params.parameters().transpose());
+  //   ATH_MSG_DEBUG("  position: " << params.position(gctx).transpose());
+  //   ATH_MSG_DEBUG("  momentum: " << params.momentum().transpose());
+  //   ATH_MSG_DEBUG("  charge:   " << params.charge());
+  //   std::unique_ptr<Trk::Track> track = m_createTrkTrackTool->createTrack(gctx, traj);
+  //   if (track != nullptr) {
+  //     m_numberOfSelectedTracks++;
+  //     std::unique_ptr<Trk::Track> track2 = m_kalmanFitterTool1->fit(ctx, gctx, track.get(), Acts::BoundVector::Zero(), m_isMC, origin);
+  //     if (track2) {
+  //       outputAllTracks->push_back(std::move(track2));
+  //     } else {
+  //       outputAllTracks->push_back(std::move(track));
+  //       ATH_MSG_WARNING("Re-Fit failed.");
+  //     }
+  //   } else {
+  //     ATH_MSG_WARNING("CKF failed.");
+  //   }
+  // }
+
   std::vector<FaserActsRecMultiTrajectory> selectedTrajectories {};
   while (not allTrajectories.empty()) {
     TrajectoryInfo selected = allTrajectories.front();
@@ -224,6 +264,7 @@ StatusCode CKF2::execute() {
   if  (m_performanceWriter && !m_noDiagnostics) {
     ATH_CHECK(m_performanceWriterTool->write(gctx, selectedTrajectories));
   }
+  // ATH_CHECK(allTrackContainer.record(std::move(outputAllTracks)));
   ATH_CHECK(trackContainer.record(std::move(outputTracks)));
 
   return StatusCode::SUCCESS;
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.h b/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.h
index 170f0353904bef82095146c50bf98d5721ef2138..cd78bf72531931fc3ef7ef3b1b67edc854e46596 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.h
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/CKF2.h
@@ -149,6 +149,7 @@ private:
   ToolHandle<CreateTrkTrackTool> m_createTrkTrackTool {this, "CreateTrkTrackTool", "CreateTrkTrackTool"};
   Gaudi::Property<bool> m_isMC {this, "isMC", false};
   SG::WriteHandleKey<TrackCollection> m_trackCollection { this, "OutputCollection", "CKFTrackCollection", "Output track collection name" };
+  // SG::WriteHandleKey<TrackCollection> m_allTrackCollection { this, "AllTrackCollection", "CKFAllTrackCollection", "Output all track collection name" };
   SG::WriteDecorHandleKey<xAOD::EventInfo> m_eventInfoKey{this, "EventInfoKey", "EventInfo"};
 };
 
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx
index 952f75287f594e5df31feb2b6a493bfe235a4086..bbae70e3b10be38ff5de9675796bbe509c8f2483 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx
@@ -130,10 +130,16 @@ StatusCode CircleFitTrackSeedTool::run(std::vector<int> maskedLayers) {
   std::list<Seed> allSeeds;
   for (const Seed &seed : seeds) allSeeds.push_back(seed);
 
+  // allSeeds.sort([](const Seed &left, const Seed &right) {
+  //   if (left.size > right.size) return true;
+  //   if (left.size < right.size) return false;
+  //   if (left.chi2 < right.chi2) return true;
+  //   else return false;
+  // });
   allSeeds.sort([](const Seed &left, const Seed &right) {
-    if (left.size > right.size) return true;
-    if (left.size < right.size) return false;
-    if (left.chi2 < right.chi2) return true;
+    if (left.stations > right.stations) return true;
+    if (left.stations < right.stations) return false;
+    if (left.chi2/std::max(left.positions.size() + left.fakePositions.size() - left.constraints, 1UL) < right.chi2/std::max(right.positions.size() + right.fakePositions.size() - right.constraints, 1UL)) return true;
     else return false;
   });
 
@@ -141,6 +147,9 @@ StatusCode CircleFitTrackSeedTool::run(std::vector<int> maskedLayers) {
   while (not allSeeds.empty()) {
     Seed selected = allSeeds.front();
     selectedSeeds.push_back(selected);
+    // allSeeds.remove_if([&](const Seed &p) {
+    //   return ((p.clusterSet & selected.clusterSet).count() == p.clusterSet.count());
+    // });
     allSeeds.remove_if([&](const Seed &p) {
       return (p.size < 10) || ((p.clusterSet & selected.clusterSet).count() > 6);
     });
@@ -161,7 +170,9 @@ StatusCode CircleFitTrackSeedTool::run(std::vector<int> maskedLayers) {
   double origin = !selectedSeeds.empty() ? minSeed->minZ - 10 : 0;
   m_targetZPosition = origin;
   std::vector<Acts::CurvilinearTrackParameters> initParams {};
+  ATH_MSG_DEBUG("Sorted seed properties:");
   for (const Seed &seed : selectedSeeds) {
+    ATH_MSG_DEBUG("seed size: " << seed.size << ", chi2: " << seed.chi2);
     initParams.push_back(seed.get_params(origin, cov));
   }
 
@@ -264,13 +275,16 @@ CircleFitTrackSeedTool::Seed::Seed(const std::vector<Segment> &segments) :
 
   if (segments.size() > 1) {
     fakeFit();
+    constraints = 5;
   } else {
     momentum = 9999999.;
     charge = 1;
+    constraints = 2;
   }
 
   getChi2();
   size = clusters.size();
+  stations = segments.size();
 }
 
 
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.h b/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.h
index 0467c92620eebd955f0a78f11cfdea3fd0cb0b1d..a4d53c629c24f53e1dfe4488ca7524755f117ad3 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.h
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.h
@@ -66,7 +66,7 @@ private:
 
     double c0, c1, cx, cy, r, chi2, momentum, charge, minZ;
     Acts::Vector3 direction;
-    size_t size;
+    size_t size, stations, constraints;
     Acts::CurvilinearTrackParameters get_params(double origin, Acts::BoundSymMatrix cov) const;
 
   private:
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CreateTrkTrackTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/CreateTrkTrackTool.cxx
index 4928fc917e2b0bfe73e38848939690680fc3c19a..994689010203a269946c3c88392004a61a42adf4 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/CreateTrkTrackTool.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/CreateTrkTrackTool.cxx
@@ -61,9 +61,17 @@ CreateTrkTrackTool::createTrack(const Acts::GeometryContext &gctx, const FaserAc
           };
         }
       }
-      const Trk::TrackStateOnSurface *perState = new Trk::TrackStateOnSurface(clusterOnTrack, parm);
-      if (perState) {
-        finalTrajectory->insert(finalTrajectory->begin(), perState);
+      double nDoF = state.calibratedSize();
+      const Trk::FitQualityOnSurface* quality = new Trk::FitQualityOnSurface(state.chi2(), nDoF);
+      const Trk::TrackStateOnSurface* perState = new Trk::TrackStateOnSurface(
+									      clusterOnTrack, 
+									      parm,
+									      quality,
+									      nullptr,
+									      typePattern);
+      if (perState)
+      {
+	        finalTrajectory->insert(finalTrajectory->begin(), perState);
       }
     }
     return;