diff --git a/.gitmodules b/.gitmodules
index 82b8e1319b74172b9299e014eceb3803ae142cde..5f4e81b19df741d282252ef76cb5841311e2477a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
 [submodule "faser-common"]
 	   path = faser-common
-	   url = https://gitlab.cern.ch/faser/faser-common.git
+	   url = ../../faser/faser-common.git
diff --git a/PhysicsAnalysis/NtupleDumper/scripts/analyzeRun2.py b/PhysicsAnalysis/NtupleDumper/scripts/analyzeRun2.py
new file mode 100644
index 0000000000000000000000000000000000000000..011cdbe9f8ae24e3503e34e314e14801ed7092fc
--- /dev/null
+++ b/PhysicsAnalysis/NtupleDumper/scripts/analyzeRun2.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python3
+
+"""
+    Copyright (C) 2002-2022 CERN for the benefit of the FASER collaboration
+"""
+
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+from MagFieldServices.MagFieldServicesConfig import MagneticFieldSvcCfg
+
+
+def NtupleDumperAlgCfg(flags, OutName, **kwargs):
+    # Initialize GeoModel
+    from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg
+    acc = FaserGeometryCfg(flags)
+
+    acc.merge(MagneticFieldSvcCfg(flags))
+    # acc.merge(FaserActsTrackingGeometrySvcCfg(flags))
+    # acc.merge(FaserActsAlignmentCondAlgCfg(flags))
+
+    actsExtrapolationTool = CompFactory.FaserActsExtrapolationTool("FaserActsExtrapolationTool")
+    actsExtrapolationTool.MaxSteps = 10000
+    actsExtrapolationTool.TrackingGeometryTool = CompFactory.FaserActsTrackingGeometryTool("TrackingGeometryTool")
+
+    NtupleDumperAlg = CompFactory.NtupleDumperAlg("NtupleDumperAlg",**kwargs)
+    NtupleDumperAlg.ExtrapolationTool = actsExtrapolationTool
+    acc.addEventAlgo(NtupleDumperAlg)
+
+    thistSvc = CompFactory.THistSvc()
+    thistSvc.Output += [f"HIST2 DATAFILE='{OutName}' OPT='RECREATE'"]
+    acc.addService(thistSvc)
+
+    return acc
+
+if __name__ == "__main__":
+
+    import glob
+    import sys
+    import ROOT
+
+    runno=int(sys.argv[1])
+    num=int(sys.argv[2])
+    filesPerJob=int(sys.argv[3])
+    run_config=str(sys.argv[4]) 
+
+    ptag="p0008"
+
+    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(INFO)
+    Configurable.configurableRun3Behavior = True
+
+    #fName = dataDir+"FaserMC-MDC_FS_Aee_100MeV_1Em5_shift-110001-00000-00009-s0007-r0009-xAOD.root"
+
+
+    dataDir=f"/eos/experiment/faser/sim/mdc/foresee/{ptag}/rec/r0009/"
+    files=sorted(glob.glob(f"{dataDir}/FaserMC-MDC*"))
+    fileListInitial=files[num*filesPerJob:(num+1)*filesPerJob]
+    fileList=[]
+    for fName in fileListInitial:
+        try:
+            fh=ROOT.TFile(fName)
+            fileList.append(fName)
+        except OSError:
+            print("Warning bad file: ",fName)
+
+    log.info(f"Analyzing Run {runno} files {num*filesPerJob} to {(num+1)*filesPerJob} (num={num})")
+    log.info(f"Got {len(fileList)} files out of {len(fileListInitial)}")
+
+    outName=f"Data-tuple-{run_config}-{runno:06d}-{num:05d}-{filesPerJob}.root"
+
+    log.info(f"Analyzing Run {runno} files {num*filesPerJob} to {(num+1)*filesPerJob} (num={num})")
+    log.info(f"Got {len(fileList)} files out of {len(fileListInitial)}")
+
+    outName=f"Data-tuple-{run_config}-{runno:06d}-{num:05d}-{filesPerJob}.root"
+
+    # outName = "MC_MDC_darkphoton_tuple.root"
+
+    # Configure
+    ConfigFlags.Input.Files = fileList
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersionS
+    ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"               # Use MC conditions for now
+    ConfigFlags.Input.ProjectName = "data21"                     # Needed to bypass autoconfig
+    ConfigFlags.Input.isMC = True                                # 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(NtupleDumperAlgCfg(ConfigFlags, outName, UseFlukaWeights=True, CaloConfig=run_config))
+
+    AthenaEventLoopMgr = CompFactory.AthenaEventLoopMgr()
+    AthenaEventLoopMgr.EventPrintoutInterval=1000
+    acc.addService(AthenaEventLoopMgr)
+
+    # # 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/PairVertex/CMakeLists.txt b/Tracker/TrackerRecAlgs/PairVertex/CMakeLists.txt
index 85053a1f49bd551ea136c5cd61f6cee40543ed26..ff270bb69c9defc70b31e5e50f8dbc93ffa3992f 100644
--- a/Tracker/TrackerRecAlgs/PairVertex/CMakeLists.txt
+++ b/Tracker/TrackerRecAlgs/PairVertex/CMakeLists.txt
@@ -5,7 +5,10 @@ atlas_add_component(
         src/PairVertexAlg.h
         src/PairVertexAlg.cxx
         src/component/PairVertex_entries.cxx
-        LINK_LIBRARIES AthenaBaseComps StoreGateLib GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives
+        LINK_LIBRARIES AthenaBaseComps StoreGateLib GeneratorObjects 
+        FaserActsGeometryLib TrackerSimEvent TrackerIdentifier 
+        TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack 
+        xAODFaserWaveform  ScintIdentifier FaserCaloIdentifier
 )
 
 atlas_install_python_modules(python/*.py)
diff --git a/Tracker/TrackerRecAlgs/PairVertex/python/PairVertexAlgConfig.py b/Tracker/TrackerRecAlgs/PairVertex/python/PairVertexAlgConfig.py
index f98af1629a39bfc590ef3ab0929da4b849ad5ce6..4243fe99559a9ff4f16753f0e53c51c9d8ea1d2a 100644
--- a/Tracker/TrackerRecAlgs/PairVertex/python/PairVertexAlgConfig.py
+++ b/Tracker/TrackerRecAlgs/PairVertex/python/PairVertexAlgConfig.py
@@ -7,19 +7,38 @@ from AthenaConfiguration.ComponentFactory import CompFactory
 from FaserSCT_GeoModel.FaserSCT_GeoModelConfig import FaserSCT_GeometryCfg
 from MagFieldServices.MagFieldServicesConfig import MagneticFieldSvcCfg
 
+#Command line config
+from argparse import ArgumentParser
+
+test_file = '/eos/experiment/faser/sim/mdc/foresee/110001/rec/r0009/FaserMC-MDC_FS_Aee_100MeV_1Em5_shift-110001-00000-00009-s0007-r0009-xAOD.root'
+parser=ArgumentParser(description="EventLooper")
+parser.add_argument("-i","--inputfile",type=str,default=test_file)
+parser.add_argument("-e", "--events", type=int, default=1000) # Max events processed in alg. Set to -1 to process all events.
+parser.add_argument("-r","--runnum",type=int,default=110001) # 110001-4 
+ui=parser.parse_args()
+
+inputIsMC = 'FaserMC' in ui.inputfile
+
+#Get/Make input/output file names
+from ROOT import TFile
+from glob import glob
+dataDir=f"/eos/experiment/faser/sim/mdc/foresee/{ui.runnum}/rec/r0009/"
+files=sorted(glob(f"{dataDir}/Faser*"))
+name=files[0].split("r0009/")[-1].split("MDC_FS")[-1].split("shift")[0] #Generate output file name from input file name
+
+#-------------------
+
+
 def PairVertexAlgCfg(flags, **kwargs):
     acc = FaserSCT_GeometryCfg(flags)
     acc.merge(MagneticFieldSvcCfg(flags))
-    # acc.merge(FaserActsTrackingGeometrySvcCfg(flags))
-    # acc.merge(FaserActsAlignmentCondAlgCfg(flags))
-
-    actsExtrapolationTool = CompFactory.FaserActsExtrapolationTool("FaserActsExtrapolationTool")
-    actsExtrapolationTool.MaxSteps = 1000
-    actsExtrapolationTool.TrackingGeometryTool = CompFactory.FaserActsTrackingGeometryTool("TrackingGeometryTool")
-
     PairVertexAlg = CompFactory.Tracker.PairVertexAlg("PairVertexAlg",**kwargs)
-    PairVertexAlg.ExtrapolationTool = actsExtrapolationTool
     acc.addEventAlgo(PairVertexAlg)
+
+    #Hist output
+    thistSvc = CompFactory.THistSvc()
+    thistSvc.Output = [f"HIST DATAFILE='Ntuple{name}.root' OPT='RECREATE'"]
+    acc.addService(thistSvc)
     return acc
 
 if __name__ == "__main__":
@@ -35,17 +54,15 @@ if __name__ == "__main__":
     from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
 
     # Set up logging and new style config
-    log.setLevel(DEBUG)
+    log.setLevel(INFO)
     Configurable.configurableRun3Behavior = True
 
     # Configure
-    ConfigFlags.Input.Files = [
-        '/eos/experiment/faser/sim/mdc/foresee/110004/rec/r0007/FaserMC-MDC_FS_Amm_316MeV_2Em6-110004-00000-s0003-r0007-xAOD.root'
-    ]
+    ConfigFlags.Input.Files = files # [ui.inputfile]
     ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersionS
     ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"               # Use MC conditions for now
     ConfigFlags.Input.ProjectName = "data21"                     # Needed to bypass autoconfig
-    ConfigFlags.Input.isMC = True                                # Needed to bypass autoconfig
+    ConfigFlags.Input.isMC = inputIsMC                                # Needed to bypass autoconfig
     ConfigFlags.GeoModel.FaserVersion     = "FASERNU-03"           # FASER geometry
     ConfigFlags.Common.isOnline = False
     ConfigFlags.GeoModel.Align.Dynamic = False
@@ -62,29 +79,16 @@ if __name__ == "__main__":
     # algorithm
     acc.merge(PairVertexAlgCfg(ConfigFlags))
 
-    # # 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()
+    # Hack to avoid problem with our use of MC databases when isMC = False
+    if not inputIsMC:
+        replicaSvc = acc.getService("DBReplicaSvc")
+        replicaSvc.COOLSQLiteVetoPattern = ""
+        replicaSvc.UseCOOLSQLite = True
+        replicaSvc.UseCOOLFrontier = False
+        replicaSvc.UseGeomSQLite = True
 
     # Execute and finish
-    sc = acc.run(maxEvents=-1)
+    sc = acc.run(maxEvents=ui.events)
 
     # Success should be 0
-    sys.exit(not sc.isSuccess())    
\ No newline at end of file
+    sys.exit(not sc.isSuccess())
\ No newline at end of file
diff --git a/Tracker/TrackerRecAlgs/PairVertex/scripts/PairVertexReader.py b/Tracker/TrackerRecAlgs/PairVertex/scripts/PairVertexReader.py
new file mode 100644
index 0000000000000000000000000000000000000000..2468be886114912fa36555c88abaad99e8e77076
--- /dev/null
+++ b/Tracker/TrackerRecAlgs/PairVertex/scripts/PairVertexReader.py
@@ -0,0 +1,587 @@
+#!/usr/bin/env python
+
+# Set up (Py)ROOT.
+from ROOT import TChain, TH2F, TH1F
+print("Root loaded.")
+import matplotlib.pyplot as plt
+print("MPL loaded.")
+from operator import lt,eq,ne,gt
+from numpy import nan, seterr, inf,arctan,log10,logspace, histogram2d, meshgrid, ones_like,array
+from numpy import sum as npsum
+import numpy as np
+from math import sqrt
+from cmcrameri import cm
+print("Other stuff loaded.")
+
+seterr(all='ignore')
+missingattr=[]
+
+#attr: [>,<,==,!=] if none --> skip
+CUTDICT={
+    'TrackCount':[None,None,2,None],
+    'VetoNu0_integral':[None,None,0,None],
+    'VetoNu1_integral':[None,None,0,None],
+    'VetoSt10_integral':[None,None,0,None],
+    'VetoSt11_integral':[None,None,0,None],
+    'VetoSt20_integral':[None,None,0,None],
+    'nlayersHit':[6,None,None,None],
+    'playersHit':[6,None,None,None],
+    'niftHit':[None,None,0,None],
+    'piftHit':[None,None,0,None],
+    'pDoF':[0,None,None,None],
+    'nDoF':[0,None,None,None],
+    'Preshower0_integral':[0,None,None,None],
+    'Preshower1_integral':[0,None,None,None]
+    }
+
+#CUT DICTIONARIES/LISTS
+LAMBDACUTS=[
+    lambda event : len(event.Tracks[0])==2,
+    lambda event : event.Tracks[0][0]==-1*event.Tracks[0][1],
+    lambda event : event.Timing0_integral+event.Timing1_integral+event.Timing2_integral+event.Timing3_integral>0,
+    lambda event : event.Calo0_integral+event.Calo1_integral+event.Calo2_integral+event.Calo3_integral>0,
+    lambda event : event.nChi2/event.nDoF <10 and event.pChi2/event.pDoF <10,
+    lambda event : sqrt((event.pMom[0]**2)+(event.pMom[1]**2)+(event.pMom[2]**2))>1]
+
+FIDUCIALCUT=[lambda event : event.vertexPos[2]>-1585 and event.vertexPos[2]<-45]
+
+BADCUT=list(LAMBDACUTS)
+#BADCUT[4]=lambda event : event.nChi2/event.nDoF >10 and event.pChi2/event.pDoF >10
+#BADCUT[]
+
+HITCUT= [lambda event : all([abs(i)<200 for i in [event.pPos_S1L0[0],event.pPos_S1L0[1],event.nPos_S1L0[0],event.nPos_S1L0[1]]])]
+
+
+#UTILITY
+def configHitMap(arrayx,arrayy=[],xlabel="X (mm)",ylabel="Y (mm)",xlim=None,ylim=None,title=None,show=False):
+    plt.hist2d(arrayx, arrayy, bins=(50, 50), cmap=cm.imola)
+    plt.colorbar()
+    plt.xlabel(xlabel)
+    plt.ylabel(ylabel)
+    if xlim: plt.xlim(xlim)
+    if ylim: plt.ylim(ylim)#-100,100
+    #plt.text(50,-185,"COUNTS: {}".format(len(arrayx)))
+    if title: plt.title(title)
+    if show: plt.show()
+
+def configHitMap2(arrays,bins=(50,50),xlabel="X (mm)",ylabel="Y (mm)",xlim=None,ylim=None,title=None,show=False):
+    arrayx=arrays[0]
+    arrayy=arrays[1]
+    plt.hist2d(arrayx, arrayy, bins=bins, cmap=cm.imola)
+    plt.colorbar()
+    plt.xlabel(xlabel)
+    plt.ylabel(ylabel)
+    if xlim: plt.xlim(xlim)
+    if ylim: plt.ylim(ylim)#-100,100
+    #plt.text(50,-185,"COUNTS: {}".format(len(arrayx)))
+    if title: plt.title(title)
+    if show: plt.show()
+
+def configBasicHist(arr,bins=25,xscale='linear',yscale='linear',title=None,xlabel=None,legend=False,kwargs={}):
+    if len(arr)==0:
+        print("Empty array.")
+        return
+    if 'log' in xscale:
+        minx,maxx = (min(arr),max(arr))
+        if minx==maxx: 
+            maxx+=1
+            bins=1
+        bins = logspace(start=log10(minx), stop=log10(maxx), num=bins)
+        if xscale=='symlog':
+            bins = [0]+list(logspace(start=log10(1), stop=log10(maxx), num=24))
+            if minx>1: bins = logspace(start=log10(minx), stop=log10(maxx), num=25)
+
+    n, bins, patches = plt.hist(arr,bins=bins,align="left",histtype=u'step',**kwargs)
+    plt.xscale(xscale)
+    #plt.xticks([i for i in range(0,21)])
+    if xlabel: plt.xlabel(xlabel)
+    plt.yscale(yscale)
+    plt.ylabel('Number of Events')
+    if legend: plt.legend()
+    if title:plt.title(title)
+    
+    return n,bins
+
+def configMultiHist(arrlist,bins=25,xscale='linear',yscale='linear',title=None,xlabel=None,label=[],legend=True,kwargs={}):
+    megarr=[]
+    for a in arrlist: megarr+=a
+    if 'log' in xscale:
+            minx,maxx = (min(megarr),max(megarr))
+            if minx==maxx: 
+                maxx+=1
+                bins=1
+            bins = logspace(start=log10(minx), stop=log10(maxx), num=bins)
+            if xscale=='symlog':
+                bins = [0]+list(logspace(start=log10(1), stop=log10(maxx), num=24))
+                if minx>1: bins = logspace(start=log10(minx), stop=log10(maxx), num=25)
+
+    for iarr in range(len(arrlist)):
+        arr=arrlist[iarr]
+        labelused=''
+        if len(megarr)==0:
+            print(f"Empty array.")
+        else: 
+
+            if len(label)==len(arrlist): labelused=label[iarr]
+            else: 
+                print("Insufficient labels given.")
+            plt.hist(arr,bins=bins,align="left",histtype=u'step',label=labelused,**kwargs)
+        
+    plt.xscale(xscale)
+    plt.yscale(yscale)
+    if xlabel: plt.xlabel(xlabel)
+    plt.ylabel('Number of Events')
+    if legend: plt.legend()
+    if title:plt.title(title)
+
+def sortList(tlist):
+    tlist.sort(key = lambda x: x[4], reverse=True)
+    return tlist
+
+def plotLEGO(xarr,yarr):
+    hist = TH2F("hist","Ndiff",20,-10,10,20,-10,10)
+    print("Fill start.")
+    for i in range(len(xarr)):
+        hist.Fill(xarr[i],yarr[i])
+
+    print("Fill done.")
+    hist.Draw("LEGO") #colz
+    input()
+
+def getQPNSorted(event,highestrank=1,lowestrank=3): #Get sorted array containing [q,px,py,pz,p-mag], ranked by p-mag
+    trackQPN=[] # [(q,px,py,pz,p)]
+    for i in range(highestrank-1,lowestrank):
+        #Momenum is in MeV by default, so we convert to GeV since 1 MeV = 10^-3 GeV
+        pmag = sqrt((event.Tracks[4][i]**2)+(event.Tracks[5][i]**2)+(event.Tracks[6][i]**2))
+        trackQPN+=[(event.Tracks[0][i],event.Tracks[4][i]*(10**-3),event.Tracks[5][i]*(10**-3),event.Tracks[6][i]*(10**-3),pmag*(10**-3))]
+
+    return sortList(trackQPN)
+
+def ApplyCut(event, cut):
+    tests=[gt,lt,eq,ne]
+    bool1=[]
+    global missingattr
+    for attr in cut.keys():
+        for i in range(len(tests)):
+            try:
+                if cut[attr][i]!=None: 
+                    value = getattr(event,attr)
+                    bool1 += [tests[i](value,cut[attr][i]) and value != inf and value !=nan and value !=-1*inf]
+                    if not all(bool1): break
+            except AttributeError:
+                if attr not in missingattr:
+                    print("No attribute: ",attr)
+                    missingattr+=[attr]
+            
+        if not all(bool1):
+            break
+    
+    return all(bool1)
+
+def ApplyAllCuts(event,dcuts=[CUTDICT],lcuts=LAMBDACUTS): #Requires all True to be True
+    #cutList = list of dictionaries
+    bool1=True
+    boollist=[]
+    for cut in dcuts:
+        bool1 = ApplyCut(event,cut)
+        if not bool1: break
+    if bool1:
+        for lcut in lcuts:
+            boollist+=[lcut(event)]
+            if not all(boollist): break
+    
+    return all(boollist) and bool1
+
+def RootRatio(narray,darray):
+    #rarray = np.divide(narray,darray)
+    h_numerator=TH1F("num","num",15,0,10000)
+    h_denominator=TH1F("den","den",15,0,10000)
+    for i in narray: h_numerator.Fill(i)
+    for j in darray: h_denominator.Fill(j)
+
+    h_ratio = h_numerator.Clone()
+    h_ratio.Sumw2()
+    h_ratio.Divide(h_denominator)
+    h_ratio.SetStats(0)
+    h_ratio.SetTitle(f"{user_input} Efficiency vs Rho^2;Rho^2 (mm^2);Efficiency(%)")
+    h_ratio.SetMinimum(0.)
+    h_ratio.Draw("ep")
+    input()
+
+def MakeWFDict():
+    WFDict=dict()
+    waveforms={'VetoSt1':(0,1), 'VetoSt2':(0,0),'Timing':(0,3),'Preshower':(0,1),'Calo':(0,3)}
+    meas = 'integral'
+    TODO = list(waveforms.keys())
+    for wf in TODO:
+        st_r=waveforms[wf]
+        for st in range(st_r[0],st_r[1]+1):
+            key=f'{wf}{st}_{meas}'
+            WFDict[key]={
+                    'array':[],
+                    'lkwargs':{'k':key},
+                    'lambda': lambda event,k: getattr(event, k),
+                    'plotterfunc': configBasicHist,
+                    'plotkwargs' : {'xscale':'symlog'}
+            }
+    return WFDict
+
+#SPECIFIC PLOTS
+
+def plotMomentumDistribution(highestrank=1,lowestrank=3):
+
+    arr_pmag=[[] for i in range(highestrank,lowestrank+1)]
+    for event in t:
+        if len(event.Tracks[0])==lowestrank:
+            trackQPNsorted=getQPNSorted(event,highestrank=1,lowestrank=lowestrank)
+            isSimilarP = True # abs(trackQPNsorted[0][4]-trackQPNsorted[1][4])<100
+            if trackQPNsorted[0][0]!=trackQPNsorted[1][0] and isSimilarP:
+                for rank in range(highestrank-1,lowestrank):
+                    arr_pmag[rank]+=[trackQPNsorted[rank][4]]
+
+    for rank in range(highestrank-1,lowestrank):
+        rlabel = f'P Rank {rank+1}'
+        configBasicHist(arr_pmag[rank],label=rlabel,bins=logspace(start=log10(30), stop=log10(7000), num=25),range=(30,7000), legend=True,kwargs={'histtype':u'step'})
+    plt.xscale('log')
+    plt.xlabel('Track Momentum Magnitude (GeV) ')
+    plt.ylabel('Number of Events')
+    plt.title(f"Momentum Distribution of {lowestrank}-track Events ("+user_input+")")
+    plt.show()
+
+def efficiencyP(pmin=1,pmax=4000,pinc=100):
+    arr_eff=[]
+    arr_p=[]
+    for pcut in range(pmin,pmax,pinc):
+        eventcountcut=0
+        eventcount=0
+        for event in t:
+            if ApplyAllCuts(event):
+                trackQPNsorted=getQPNSorted(event,highestrank=1,lowestrank=2) #[()]
+                if trackQPNsorted[0][0]!=trackQPNsorted[1][0]:
+                    eventcount+=1
+                    if trackQPNsorted[0][-1]>pcut and trackQPNsorted[1][-1]>pcut: eventcountcut+=1
+        arr_eff+= [100*eventcountcut/eventcount]
+        arr_p+= [pcut]
+    plt.plot(arr_p,arr_eff)
+    plt.ylabel('Cut Efficiency (%)')
+    plt.xlabel('P-cut (GeV)')
+    plt.title(user_input+'Momentum Cut Efficiency')
+    plt.savefig(user_input+'MomCutEff')
+    plt.show()
+    
+def dpHitMap():
+    arrxp=[]
+    arryp=[]
+    arrxn=[]
+    arryn=[]
+    for event in t:
+        if ApplyAllCuts(event):
+            xp,yp,xn,yn=(event.pPos_S1L0[0],event.pPos_S1L0[1],event.nPos_S1L0[0],event.nPos_S1L0[1])
+            if all([abs(i)<200 for i in [xp,yp,xn,yn]]):
+                arrxp+=[xp]
+                arryp+=[yp]
+                arrxn+=[xn]
+                arryn+=[yn]
+
+    #plt.scatter(arrxp,arryp,color='red',s=0.2)
+    configHitMap(arrxp,arryp,title='Positive Track Upstream Hits')
+    #plotLEGO(arrxp,arryp)
+    plt.savefig(f"{user_input}HitMapP")
+    plt.close()
+    plt.show()
+    #plt.scatter(arrxn,arryn,color='blue',s=0.1)
+    configHitMap(arrxn,arryn,title='Negative Track Upstream Hits')
+    plt.savefig(f"{user_input}HitMapN")
+    #plotLEGO(arrxn,arryn)
+    plt.show()
+
+def CutEfficiency(Ndcuts,Nlcuts,Ddcuts=[],Dlcuts=[]):
+    ecount=0
+    ccount=0
+    for event in t:
+        if ApplyAllCuts(event,Ddcuts,Dlcuts): ecount+=1
+        if ApplyAllCuts(event,Ndcuts,Nlcuts): ccount+=1
+    return ccount,ecount,100*ccount/ecount
+
+def CutEfficiencyArrays(xlab='Z (mm)'):
+    Func = lambda event:  (event.vertexPos[0]**2)+(event.vertexPos[1]**2) #event.vertexPos[2]#
+    narray=[]
+    darray=[]
+    for event in t:
+        if ApplyAllCuts(event,[CUTDICT],LAMBDACUTS): narray+=[Func(event)]
+        if ApplyAllCuts(event,[],FIDUCIALCUT): darray+=[Func(event)]
+
+    # if 'Z' not in xlab:
+    #     dn,dbin = configBasicHist(darray,bins=20,kwargs={'range':(0,10000),'label':'Fiducial Volume'},legend=True)
+    #     nn,nbin = configBasicHist(narray,bins=20,xlabel='Rho^2 (mm)',kwargs={'range':(0,10000),'label':'Selection Cuts'},legend=True)
+    # else:
+    #     dn,dbin = configBasicHist(darray,bins=20,kwargs={'range':(-1600,0),'label':'Fiducial Volume'},legend=True) 
+    #     nn,nbin = configBasicHist(narray,bins=20,xlabel=xlab,kwargs={'range':(-1600,0),'label':'Selection Cuts'},legend=True) 
+
+    # plt.title(user_input+"Efficiency vs "+xlab)
+    # plt.show()
+
+    RootRatio(narray,darray)
+
+    # x=[]
+    # y=[]
+    # err=[]
+    # for i in range(len(nn)):
+    #     x+=[nbin[i]]
+    #     y+=[100*nn[i]/dn[i]]
+    #     err+=[100*(1/dn[i])*sqrt((nn[i]*(dn[i]-nn[i]))/dn[i])]
+    # #plt.plot(x,y)
+    # plt.errorbar(x,y,yerr=err)
+    # plt.ylim(bottom=0)
+    # plt.xlabel(xlab)#Rho^2 (mm)
+    # plt.ylabel("Efficiency")
+    # plt.title(user_input+"Efficiency vs"+xlab)
+    # plt.show()
+
+def EfficiencyPlotter(minx,maxx,stepx=1):
+    arr=[]
+    arrx=[]
+    for i in range(minx+stepx,maxx,stepx):
+        lcut=lambda event: ((event.vertexPos[0]**2)+(event.vertexPos[1]**2))<i
+        arr+=[CutEfficiency([CUTDICT],LAMBDACUTS,dlcuts=[lcut])]
+        arrx+=[i]
+    plt.plot(arrx,arr)
+    plt.ylabel("Fiducial Event Count/Cut-Applied Event Count")
+    plt.show()
+
+#FOR PLOT DICTIONARY (complex lambda functions)
+def TwoMomentum(event,sorte=True):
+    ppmag=sqrt((event.pMom[0]**2)+(event.pMom[1]**2)+(event.pMom[2]**2))*(10**-3)
+    npmag=sqrt((event.nMom[0]**2)+(event.nMom[1]**2)+(event.nMom[2]**2))*(10**-3)
+    final = [ppmag,npmag]
+    if sorte: final.sort()
+    return final
+
+def AngularDistribution(event):
+    px1,py1,pz1 = (event.pMom[0],event.pMom[1],event.pMom[2])
+    px2,py2,pz2 = (event.nMom[0],event.nMom[1],event.nMom[2])
+    return [(arctan(py1/pz1)-arctan(py2/pz2))*1000,(arctan(px1/pz1)-arctan(px2/pz2))*1000]
+
+def PAngleX(event):
+    px1,py1,pz1 = (event.pMom[0],event.pMom[1],event.pMom[2])
+    px2,py2,pz2 = (event.nMom[0],event.nMom[1],event.nMom[2])
+    return [arctan(px1/pz1)*1000,arctan(px2/pz2)*1000]
+
+def PAngleY(event):
+    px1,py1,pz1 = (event.pMom[0],event.pMom[1],event.pMom[2])
+    px2,py2,pz2 = (event.nMom[0],event.nMom[1],event.nMom[2])
+    return [arctan(py1/pz1)*1000,arctan(py2/pz2)*1000]
+    #plotLEGO(ndiff_arrx,ndiff_arry)
+
+def UpstreamHitMap(event,charge=1):
+    xp,yp,xn,yn=(event.pPos_S1L0[0],event.pPos_S1L0[1],event.nPos_S1L0[0],event.nPos_S1L0[1])
+    if charge==1: return [xp,yp]
+    elif charge==-1: return [xn,yn]
+
+
+def TrackSeparation(event):
+    xp,yp,xn,yn=(event.pPos_S1L0[0],event.pPos_S1L0[1],event.nPos_S1L0[0],event.nPos_S1L0[1])
+    if all([abs(i)<200 for i in [xp,yp,xn,yn]]):
+        return sqrt((xp-xn)**2+(yp-yn)**2)
+    else: return nan
+
+def SeparationDistribution(event):
+    xp,yp,zp,xn,yn,zn=(event.pPos_S1L0[0],event.pPos_S1L0[1],event.pPos_S1L0[2],event.nPos_S1L0[0],event.nPos_S1L0[1],event.nPos_S1L0[2])
+    if all([abs(i)<200 for i in [xp,yp,xn,yn]]):
+        return [(arctan(yp/zp)-arctan(yn/zn))*1000,(arctan(xp/zp)-arctan(xn/zn))*1000]
+    else: return [-101,-101] #outside range, therefore not plotted
+
+def Chi2DoF(event):
+    try: return [event.pChi2/event.pDoF,event.nChi2/event.nDoF]
+    except ZeroDivisionError: return [nan,nan]    
+
+def SumCalo(event):
+    Etot=0
+    for st in range(0,4):
+        aname=f'Calo{st}_integral'
+        value = getattr(event, aname)
+        bool1= value != inf and value !=nan and value !=-1*inf
+        if bool1: Etot+=value
+        else: return nan
+    return Etot
+
+#PLOT DICTIONARY FORMAT
+# arrDict={
+#     'ExampleName':{
+#         'array':[],
+#         'lambda': lambda event: event.pChi2/event.pDoF,
+#         'plotterfunc': configBasicHist,
+#         'plotkwargs' : {'edgecolor':'black','linewidth':1.2, 'rwidth':0.9}
+#     }
+# }
+# Veto St1-2, Ch0-1 - VetoStXY_
+# Timing 0-3        - TimingX_
+# Preshower 0-1     - PreshowerX_
+# Calo 0-3          - CaloX_
+# Sum of integrals in calo wave channels --> Calo Edep sort of (missing some callibration constants)
+
+
+#templambda = lambda ti : lambda event : event.vertexPos[2]<ti
+#EfficiencyPlotter(templambda,0,1000)
+
+MainArrDict={
+    'CaloSum':{
+        'array':[],
+        'lambda': SumCalo,
+        'plotterfunc': configBasicHist,
+        'plotkwargs' : {'xscale':'log'}
+    },
+    'TracksPerEvent':{
+        'array':[],
+        'lambda': lambda event: len(event.Tracks[0]),
+        'plotterfunc': configBasicHist,
+        'plotkwargs' : {}
+    },
+    'TrackSeparation':{
+        'array':[],
+        'lambda': TrackSeparation,
+        'plotterfunc': configBasicHist,
+        'plotkwargs' : {'bins':20,'xlabel':'Distance (mm)','kwargs':{'range':(0,10)}}
+    },
+    'TwoMomentum':{
+        'array':[[],[]],
+        'lambda': TwoMomentum,
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'label':['Positive','Negative'],'xscale':'log','xlabel':'Track Momentum Magnitude (GeV) '}
+    },
+    'AngularDistribution':{
+        'array':[[],[]],
+        'lambda': AngularDistribution,
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'label':['Momentum-Y','Momentum-X'],'xlabel':'N-diff (mrad)','kwargs':{'range':(-35,35)}}
+    },
+    'PAngleX':{
+        'array':[[],[]],
+        'lambda': PAngleX,
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'label':['Positive','Negative'],'xlabel':'Angle (mrad)','kwargs':{'range':(-35,35)}}
+    },
+    'PAngleY':{
+        'array':[[],[]],
+        'lambda': PAngleY,
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'label':['Positive','Negative'],'xlabel':'Angle (mrad)','kwargs':{'range':(-35,35)}}
+    },
+    'SeparationDistribution':{
+        'array':[[],[]],
+        'lambda': SeparationDistribution,
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'label':['Y','X'],'kwargs':{'range':(-100,100)}}
+    },
+    'Chi2DoF':{
+        'array':[[],[]],
+        'lambda': Chi2DoF,
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'bins':20,'label':['Positive','Negative'],'kwargs':{'range':(0,11)}}
+    },
+    'DoF':{
+        'array':[[],[]],
+        'lambda': lambda event:[event.pDoF,event.nDoF],
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'bins':30,'label':['Positive','Negative'],'kwargs':{'range':(0,31)}}
+    },
+    'LayersHit':{
+        'array':[[],[]],
+        'lambda': lambda event: [event.playersHit,event.nlayersHit],
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'bins':5,'label':['Positive','Negative'],'kwargs':{'range':(5,10)}}
+    },
+    'VertexZ':{
+        'array':[],
+        'lambda': lambda event: event.vertexPos[2],
+        'plotterfunc': configBasicHist,
+        'plotkwargs' : {'xlabel':'Z (mm)'}
+    },
+    'TrueVertexRho':{
+        'array':[],
+        'lambda': lambda event: (event.vertexPos[0]**2)+(event.vertexPos[1]**2),
+        'plotterfunc': configBasicHist,
+        'plotkwargs' : {'xlabel':'Rho^2 (mm^2)'}
+    },
+    'Rho2vsVertexZ':{
+        'array':[[],[]],
+        'lambda': lambda event:[(event.vertexPos[0]**2)+(event.vertexPos[1]**2),event.vertexPos[2]],
+        'plotterfunc': configHitMap2,
+        'plotkwargs' : {'bins':(10,10),'xlabel':'Rho^2 (mm^2)','ylabel':'Vertex-Z (mm)'}
+    },
+    'UpstreamHitsPositive':{
+        'array':[[],[]],
+        'lambda': UpstreamHitMap,
+        'plotterfunc': configHitMap2,
+        'plotkwargs' : {'xlim':(-100,100),'ylim':(-100,100)},
+        'lkwargs':{'charge':1}
+    },
+    'UpstreamHitsNegative':{
+        'array':[[],[]],
+        'lambda': UpstreamHitMap,
+        'plotterfunc': configHitMap2,
+        'plotkwargs' : {'xlim':(-100,100),'ylim':(-100,100)},
+        'lkwargs':{'charge':-1}
+    },
+    'UpstreamXYPositive':{
+        'array':[[],[]],
+        'lambda': lambda event: (event.pPos_S1L0[0],event.pPos_S1L0[1]),
+        'plotterfunc': configMultiHist,
+        'plotkwargs' : {'label':['Positive','Negative']},
+    },
+}
+MainArrDict.update(MakeWFDict()) #Fill with wf plots
+
+#For testing/plotting purposes, plotting specific members of MainArrDict
+TestArrDict=dict()
+testname='TrueVertexRho'
+TestArrDict[testname]=MainArrDict[testname]
+# for k in MainArrDict.keys():
+#     if 'Calo' in k:
+#         TestArrDict[k]=MainArrDict[k]
+
+def GeneralPlotter(arrDict,dcuts=[CUTDICT],lcuts=LAMBDACUTS,showplot=True,savefig=False):
+    nans=0
+    for event in t:
+        if ApplyAllCuts(event,dcuts,lcuts):
+            for name in arrDict.keys():
+                value=nan
+                if arrDict[name]['plotterfunc'] in [configMultiHist,configHitMap2]:
+                    if 'lkwargs' in list(arrDict[name].keys()): value = arrDict[name]['lambda'](event,**arrDict[name]['lkwargs'])
+                    else: value = arrDict[name]['lambda'](event)
+                    checkReal = all([v != inf and v !=nan and v !=-1*inf for v in value])
+                    if checkReal:
+                        for iv in range(len(value)):
+                            arrDict[name]['array'][iv]+=[value[iv]]
+                    else: nans+=1
+                else:
+                    if 'lkwargs' in list(arrDict[name].keys()): value = arrDict[name]['lambda'](event,**arrDict[name]['lkwargs'])
+                    else: value = arrDict[name]['lambda'](event)
+                    checkReal = value != inf and value !=nan and value !=-1*inf
+                    if checkReal: arrDict[name]['array']+=[value]
+                    else: nans+=1
+    
+    print(f"# of NaNs:{nans}")
+    for name in arrDict.keys():
+        arrDict[name]['plotterfunc']( arrDict[name]['array'], **arrDict[name]['plotkwargs'])
+        plt.title(user_input+name)
+        if savefig: plt.savefig(user_input+name)
+        if showplot: plt.show()
+
+
+
+#Iterate over all models and do a thing
+DP_fnames=['Ntuple_Amm_316MeV_2Em6_','Ntuple_Aee_10MeV_1Em5_','Ntuple_Aee_10MeV_1Em4_','Ntuple_Aee_100MeV_1Em5_']
+for f in DP_fnames:
+    user_input=f
+    t = TChain("events") 
+    nfiles = 0
+    nfiles += t.Add("/eos/home-s/sshively/CALYPZONE/run/"+user_input+".root") 
+    print(f)
+
+    #GeneralPlotter(TestArrDict,dcuts=[CUTDICT],lcuts=LAMBDACUTS)
+    #EfficiencyPlotter(0,200,stepx=10)
+    #print(CutEfficiency([CUTDICT],LAMBDACUTS,Ddcuts=[],Dlcuts=FIDUCIALCUT))
+    CutEfficiencyArrays(xlab='Rho^2 (mm^2)')
+    #EfficiencyTest()
+   
diff --git a/Tracker/TrackerRecAlgs/PairVertex/src/PairVertexAlg.cxx b/Tracker/TrackerRecAlgs/PairVertex/src/PairVertexAlg.cxx
index 99058166c7252ddbff852b0a09ec8d49a15e54fb..8009ce9a72e0efb119b0e5cf52c52d99a2add749 100644
--- a/Tracker/TrackerRecAlgs/PairVertex/src/PairVertexAlg.cxx
+++ b/Tracker/TrackerRecAlgs/PairVertex/src/PairVertexAlg.cxx
@@ -3,49 +3,167 @@
 #include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
 #include "GeoPrimitives/CLHEPtoEigenConverter.h"
 #include "Acts/Surfaces/PerigeeSurface.hpp"
-#include "TrackerIdentifier/FaserSCT_ID.h"
 #include "TrackerReadoutGeometry/SCT_DetectorManager.h"
 #include "TrackerReadoutGeometry/SiDetectorElement.h"
+#include "Identifier/Identifier.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "ScintIdentifier/VetoNuID.h"
+#include "ScintIdentifier/VetoID.h"
+#include "ScintIdentifier/TriggerID.h"
+#include "ScintIdentifier/PreshowerID.h"
+#include "FaserCaloIdentifier/EcalID.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
 #include <cmath>
 
+#include <TTree.h>
+#include <TBranch.h>
+
+#include <typeinfo>
+
+
 
 namespace Tracker {
 
+
 PairVertexAlg::PairVertexAlg(const std::string &name, ISvcLocator *pSvcLocator)
-    : AthReentrantAlgorithm(name, pSvcLocator) {}
+    : AthReentrantAlgorithm(name, pSvcLocator), AthHistogramming(name), m_idHelper(nullptr), m_histSvc("THistSvc/THistSvc", name) {}
+
+void PairVertexAlg::addBranch(const std::string &name,
+				float* var) {
+  m_tree->Branch(name.c_str(),var,(name+"/F").c_str());
+}
+void PairVertexAlg::addBranch(const std::string &name,
+				unsigned int* var) {
+  m_tree->Branch(name.c_str(),var,(name+"/I").c_str());
+}
+void PairVertexAlg::addWaveBranches(const std::string &name,
+				      int nchannels,
+				      int first) {
+  for(int ch=0;ch<nchannels;ch++) {
+    std::string base=name+std::to_string(ch)+"_";
+    addBranch(base+"time",&m_wave_localtime[first]);
+    addBranch(base+"peak",&m_wave_peak[first]);
+    addBranch(base+"width",&m_wave_width[first]);
+    addBranch(base+"integral",&m_wave_integral[first]);
+    addBranch(base+"raw_peak",&m_wave_raw_peak[first]);
+    addBranch(base+"raw_charge",&m_wave_raw_integral[first]);
+    addBranch(base+"baseline",&m_wave_baseline_mean[first]);
+    addBranch(base+"baseline_rms",&m_wave_baseline_rms[first]);
+    addBranch(base+"status",&m_wave_status[first]);
+    first++;
+  }
+}
+
+void PairVertexAlg::FillWaveBranches(const xAOD::WaveformHitContainer &wave) const {
+  for (auto hit : wave) {
+    if (waveformHitOK(hit) && (hit->hit_status()&2)==0) { // dont store secoondary hits as they can overwrite the primary hit
+      int ch=hit->channel();
+      //m_wave_localtime[ch]=hit->localtime()+m_clock_phase;
+      m_wave_peak[ch]=hit->peak();
+      m_wave_width[ch]=hit->width();
+      m_wave_integral[ch]=hit->integral();
+
+      m_wave_raw_peak[ch]=hit->raw_peak();
+      m_wave_raw_integral[ch]=hit->raw_integral();
+      m_wave_baseline_mean[ch]=hit->baseline_mean();
+      m_wave_baseline_rms[ch]=hit->baseline_rms();
+      m_wave_status[ch]=hit->hit_status();  
+    }
+  }
+}
+
+bool PairVertexAlg::waveformHitOK(const xAOD::WaveformHit* hit) const
+{
+    if (hit->status_bit(xAOD::WaveformStatus::THRESHOLD_FAILED) || hit->status_bit(xAOD::WaveformStatus::SECONDARY)) return false;
+    return true;
+}
+
 
 StatusCode PairVertexAlg::initialize() 
 {
   ATH_CHECK(m_mcEventCollectionKey.initialize());
   ATH_CHECK(m_trackCollectionKey.initialize());
-  ATH_CHECK(m_extrapolationTool.retrieve());
+  ATH_CHECK(m_vetoNuContainer.initialize());
+  ATH_CHECK(m_vetoContainer.initialize());
+  ATH_CHECK(m_triggerContainer.initialize());
+  ATH_CHECK(m_preshowerContainer.initialize());
+  ATH_CHECK(m_ecalContainer.initialize());
+
   ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
   ATH_CHECK(detStore()->retrieve(m_detMgr, "SCT"));
+  ATH_CHECK(detStore()->retrieve(m_vetoNuHelper,    "VetoNuID"));
+  ATH_CHECK(detStore()->retrieve(m_vetoHelper,      "VetoID"));
+  ATH_CHECK(detStore()->retrieve(m_triggerHelper,   "TriggerID"));
+  ATH_CHECK(detStore()->retrieve(m_preshowerHelper, "PreshowerID"));
+  ATH_CHECK(detStore()->retrieve(m_ecalHelper,      "EcalID"));
+
+  m_tree = new TTree("events", "Events");
+  m_tree->Branch("Tracks", &m_tracks);
+  m_tree->Branch("TrackCount", &m_trackCount, "trackCount/I");
+
+  //POSITIVE
+  m_tree->Branch("pPos_S1L0", &m_ppos_s1l0); // [x,y,z]
+  m_tree->Branch("pMom", &m_pmom); // [px,py,pz]
+  m_tree->Branch("pChi2", &m_pChi2); // float
+  m_tree->Branch("pDoF", &m_pDoF); // double
+  m_tree->Branch("pstationMap", &m_pstationMap); // set (int)
+  m_tree->Branch("playerMap", &m_playerMap); // vec(vec(int))
+  m_tree->Branch("playersHit", &m_playersHit); // int
+  m_tree->Branch("piftHit", &m_piftHit); // int
+
+  //NEGATIVE
+  m_tree->Branch("nPos_S1L0", &m_npos_s1l0); // [x,y,z]
+  m_tree->Branch("nMom", &m_nmom); // [px,py,pz]
+  m_tree->Branch("nChi2", &m_nChi2); // float
+  m_tree->Branch("nDoF", &m_nDoF); // double
+  m_tree->Branch("nstationMap", &m_nstationMap); // set (int)
+  m_tree->Branch("nlayerMap", &m_nlayerMap); // vec(vec(int))
+  m_tree->Branch("nlayersHit", &m_nlayersHit); // int
+  m_tree->Branch("niftHit", &m_niftHit); // int
+
+  //CALO
+  m_tree->Branch("Calo_total_Edep", &m_Calo_Total_Edep);
+
+  //WAVEFORM
+  addWaveBranches("VetoSt1",2,6);
+  addWaveBranches("VetoSt2",1,14);
+  addWaveBranches("VetoNu",2,4);
+  addWaveBranches("Timing",4,8);
+  addWaveBranches("Preshower",2,12);
+  addWaveBranches("Calo",4,0);
+
+  //TRUTH
+   m_tree->Branch("vertexPos", &m_vertexPos); // [x,y,z]
+
+  ATH_CHECK(histSvc()->regTree("/HIST/events", m_tree));
   return StatusCode::SUCCESS;
 }
 
 StatusCode PairVertexAlg::execute(const EventContext &ctx) const 
 {
+    clearTree();
     m_numberOfEvents++;
-    const Acts::GeometryContext gctx =
-        m_extrapolationTool->trackingGeometryTool()->getNominalGeometryContext().context();
+    //WAVEFORM & CALO
+    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());
 
-    std::vector<double> z_positions {};
-
-    SG::ReadHandle<McEventCollection> mcEvents {m_mcEventCollectionKey, ctx};
-    ATH_CHECK(mcEvents.isValid());
-    if (mcEvents->size() != 1) 
-    {
-        ATH_MSG_ERROR("There should be exactly one event in the McEventCollection.");
-        return StatusCode::FAILURE;
-    }
+    FillWaveBranches(*vetoContainer);
+    FillWaveBranches(*triggerContainer);
+    FillWaveBranches(*preshowerContainer);
+    FillWaveBranches(*ecalContainer);
+    FillWaveBranches(*vetoNuContainer);
 
+    // TRACK COLLECTION
     SG::ReadHandle<TrackCollection> tracks { m_trackCollectionKey, ctx };
     ATH_CHECK(tracks.isValid());
-
-    const Trk::TrackParameters* positive {nullptr};
-    const Trk::TrackParameters* negative {nullptr};
-
     for (auto trk : *tracks)
     {
         const Trk::TrackParameters* upstream {nullptr};
@@ -56,6 +174,8 @@ StatusCode PairVertexAlg::execute(const EventContext &ctx) const
             continue;
         }
         auto parameters = trk->trackParameters();
+
+        //Error Catching
         if (parameters == nullptr || parameters->empty()) 
         {
             m_numberOfNoParameterTracks++;
@@ -84,69 +204,112 @@ StatusCode PairVertexAlg::execute(const EventContext &ctx) const
             m_numberOfNoUpstreamTracks++;
             ATH_MSG_WARNING("Unable to find track parameters on any surface");
             continue;
+            //End error catching; assign state to upstream if everything else is ok.
         }
-        auto momentum = upstream->momentum();
-        double charge = upstream->charge();
-        if (charge > 0 || momentum.mag() > positive->momentum().mag())
-        {
-            positive = upstream;
+    
+
+        // Check for hit in the three downstream stations
+        std::vector<std::vector<int>> layerMap={{0,0,0},{0,0,0},{0,0,0},{0,0,0}};
+        std::set<int> stationMap;
+        std::vector<double> PH_pos_S1L0={0,0,0}; //x,y,z
+
+        for (auto measurement : *(trk->measurementsOnTrack())) {
+            const Tracker::FaserSCT_ClusterOnTrack* cluster = dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack*>(measurement);
+            if (cluster != nullptr) {
+                Identifier id = cluster->identify();
+                int station = m_idHelper->station(id);
+                int layer = m_idHelper->layer(id);
+                stationMap.emplace(station);
+                layerMap[station][layer]+=1;
+            }
         }
-        else if (charge < 0 || momentum.mag() > negative->momentum().mag())
-        {
-            negative = upstream;
+        
+        int layersHit=0;
+        int iftHit=0;
+        for (int st=0;st<4;st++) {
+            for (int lr=0;lr<3;lr++){
+                if (layerMap[st][lr]>0) layersHit++; 
+                if (st==0 && layerMap[st][lr]>0) iftHit++;
+            }
         }
+        if (stationMap.count(1) == 0 || stationMap.count(2) == 0 || stationMap.count(3) == 0) continue;
+        const Trk::TrackParameters* upstreamParameters = trk->trackParameters()->front();
+        const Trk::TrackParameters* downstreamParameters = trk->trackParameters()->back();
+
+
+        auto momentum = upstreamParameters->momentum();
+        double charge = upstreamParameters->charge();
+        auto position = upstreamParameters->position();
+
+        
+        m_tracks[0].push_back(charge);
+        m_tracks[1].push_back(position.x());
+        m_tracks[2].push_back(position.y());
+        m_tracks[3].push_back(position.z());
+        m_tracks[4].push_back(momentum.x());
+        m_tracks[5].push_back(momentum.y());
+        m_tracks[6].push_back(momentum.z());
+
+        if (charge>0){
+                m_ppos_s1l0[0]=position.x();
+                m_ppos_s1l0[1]=position.y();
+                m_ppos_s1l0[2]=position.z();
+                m_pmom[0]=momentum.x();
+                m_pmom[1]=momentum.y();
+                m_pmom[2]=momentum.z();
+                m_pChi2=trk->fitQuality()->chiSquared();
+                m_pDoF=trk->fitQuality()->numberDoF();
+                m_pstationMap=stationMap;
+                m_playerMap=layerMap;
+                m_playersHit=layersHit;
+                m_piftHit=iftHit;
+            }
+        else{
+                m_npos_s1l0[0]=position.x();
+                m_npos_s1l0[1]=position.y();
+                m_npos_s1l0[2]=position.z();
+                m_nmom[0]=momentum.x();
+                m_nmom[1]=momentum.y();
+                m_nmom[2]=momentum.z();
+                m_nChi2=trk->fitQuality()->chiSquared();
+                m_nDoF=trk->fitQuality()->numberDoF();
+                m_nstationMap=stationMap;
+                m_nlayerMap=layerMap;
+                m_nlayersHit=layersHit;
+                m_niftHit=iftHit;
+                
+                }
+
+        ++m_trackCount;
+        
     }
 
-    if (positive != nullptr) m_numberOfPositiveTracks++;
-    if (negative != nullptr) m_numberOfNegativeTracks++;
-
-    if (positive != nullptr && negative != nullptr) m_numberOfOppositeSignPairs++;
-
-    // for (const HepMC::GenParticle* particle : mcEvents->front()->particle_range()) 
-    // {
-    //     if ((std::abs(particle->pdg_id()) != 13)) continue;
-    //     const HepMC::FourVector& vertex = particle->production_vertex()->position();
-    //     if (vertex.z() > 0) continue;
-    //     const HepMC::FourVector& momentum = particle->momentum();
-    //     double phi = momentum.phi();
-    //     double theta = momentum.theta();
-    //     double charge = particle->pdg_id() > 0 ? -1 : 1;
-    //     double abs_momentum = momentum.rho() * m_MeV2GeV;
-    //     double qop = charge / abs_momentum;
-    //     // The coordinate system of the Acts::PlaneSurface is defined as
-    //     // T = Z = normal, U = X x T = -Y, V = T x U = x
-    // //    Acts::BoundVector pars;
-    // //    pars << -vertex.y(), vertex.x(), phi, theta, qop, vertex.t();
-    //     Acts::BoundVector pars = Acts::BoundVector::Zero();
-    //     pars[Acts::eBoundLoc0] = -vertex.y();
-    //     pars[Acts::eBoundLoc1] = vertex.x();
-    //     pars[Acts::eBoundPhi] = phi;
-    //     pars[Acts::eBoundTheta] = theta;
-    //     pars[Acts::eBoundQOverP] = qop;
-    //     pars[Acts::eBoundTime] = vertex.t();
-
-    //     auto startSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
-    //         Acts::Vector3(0, 0, vertex.z()), Acts::Vector3(0, 0, 1));
-    //     auto targetSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
-    //         Acts::Vector3(0, 0, z_mean), Acts::Vector3(0, 0, 1));
-    //     Acts::BoundTrackParameters startParameters(
-    //         std::move(startSurface), pars, charge);
-    //     ATH_MSG_DEBUG("vertex: " << vertex.x() << ", " << vertex.y() << ", " << vertex.z());
-    //     ATH_MSG_DEBUG("vertex momentum: " << momentum.x() * m_MeV2GeV << ", " << momentum.y() * m_MeV2GeV << ", " << momentum.z() * m_MeV2GeV);
-    //     std::unique_ptr<const Acts::BoundTrackParameters> targetParameters =
-    //         m_extrapolationTool->propagate(ctx, startParameters, *targetSurface);
-    //     if (targetParameters) 
-    //     {
-    //         Acts::Vector3 targetPosition = targetParameters->position(gctx);
-    //         Acts::Vector3 targetMomentum = targetParameters->momentum();
-    //         ATH_MSG_DEBUG("vertex: " << vertex.x() << ", " << vertex.y() << ", " << vertex.z());
-    //         ATH_MSG_DEBUG("origin: " << targetPosition.x() << ", " << targetPosition.y() << ", " << targetPosition.z());
-    //         ATH_MSG_DEBUG("vertex momentum: " << momentum.x() * m_MeV2GeV << ", " << momentum.y() * m_MeV2GeV << ", " << momentum.z() * m_MeV2GeV);
-    //         ATH_MSG_DEBUG("origin momentum: " << targetMomentum.x() << ", " << targetMomentum.y() << ", " << targetMomentum.z());
-    //     }
-    // }
 
-  return StatusCode::SUCCESS;
+    // MC TRUTH
+    SG::ReadHandle<McEventCollection> mcEvents {m_mcEventCollectionKey, ctx};
+    ATH_CHECK(mcEvents.isValid());
+    if (mcEvents->size() != 1) 
+    {
+        ATH_MSG_ERROR("There should be exactly one event in the McEventCollection.");
+        return StatusCode::FAILURE;
+    }
+    const HepMC::GenEvent* theEvent = mcEvents->at(0);
+    auto pVertices    = theEvent->vertices_begin();
+    auto pVerticesEnd = theEvent->vertices_end();
+
+    for (; pVertices != pVerticesEnd; ++pVertices)
+    {
+        const HepMC::GenVertex* v = *pVertices;
+        m_vertexPos[0]=v->position().x();
+        m_vertexPos[1]=v->position().y();
+        m_vertexPos[2]=v->position().z();
+        break;
+    }
+    
+
+    m_tree->Fill();
+
+    return StatusCode::SUCCESS;
 }
 
 StatusCode PairVertexAlg::finalize() 
@@ -160,4 +323,46 @@ StatusCode PairVertexAlg::finalize()
     return StatusCode::SUCCESS;
 }
 
-} // Tracker
+void PairVertexAlg::clearTree() const
+{
+    for (int i = 0; i < 7; i++) {m_tracks[i].clear();}
+    m_trackCount=0;
+
+    m_pstationMap.clear();
+    m_playerMap={{},{},{},{}};
+    m_ppos_s1l0= {-1,-1,-1};
+    m_pmom= {-1,-1,-1};
+    m_pChi2=0;
+    m_pDoF=0;
+    m_playersHit=0;
+    m_piftHit=0;
+
+    m_nstationMap.clear();
+    m_nlayerMap={{},{},{},{}};
+    m_npos_s1l0= {-1,-1,-1};
+    m_nmom= {-1,-1,-1};
+    m_nChi2=0;
+    m_nDoF=0;
+    m_nlayersHit=0;
+    m_niftHit=0;
+
+    for(int ii=0;ii<15;ii++) {
+      m_wave_localtime[ii]=0;
+      m_wave_peak[ii]=0;
+      m_wave_width[ii]=0;
+      m_wave_integral[ii]=0;
+
+      m_wave_raw_peak[ii]=0;
+      m_wave_raw_integral[ii]=0;
+      m_wave_baseline_mean[ii]=0;
+      m_wave_baseline_rms[ii]=0;
+      m_wave_status[ii]=0;
+    }
+    m_vertexPos= {-1,-1,-1};
+
+}
+
+} // end Tracker
+
+
+
diff --git a/Tracker/TrackerRecAlgs/PairVertex/src/PairVertexAlg.h b/Tracker/TrackerRecAlgs/PairVertex/src/PairVertexAlg.h
index d15fddf6e288828d143218fa6d2f686a50076ba0..d0700d3a5bf4b4e891a55a7b04b102de07117f79 100644
--- a/Tracker/TrackerRecAlgs/PairVertex/src/PairVertexAlg.h
+++ b/Tracker/TrackerRecAlgs/PairVertex/src/PairVertexAlg.h
@@ -2,15 +2,34 @@
 Copyright (C) 2022 CERN for the benefit of the FASER collaboration
 */
 
+
 #pragma once
 
 #include "GeoPrimitives/GeoPrimitives.h"
 #include "AthenaBaseComps/AthReentrantAlgorithm.h"
-#include "FaserActsGeometryInterfaces/IFaserActsExtrapolationTool.h"
 #include "GeneratorObjects/McEventCollection.h"
 #include "TrkTrack/TrackCollection.h"
+#include "xAODFaserWaveform/WaveformHit.h"
+#include "xAODFaserWaveform/WaveformHitContainer.h"
+
+#include <vector>
+#include "TTree.h"
+#include <TH1.h>
+#include <TProfile.h>
+#include "AthenaBaseComps/AthHistogramming.h"
+#include "StoreGate/ReadHandleKey.h"
+#include "GaudiKernel/ServiceHandle.h"
+#include "GaudiKernel/ToolHandle.h"
+
 
 class FaserSCT_ID;
+class VetoNuID;
+class TTree;
+class VetoID;
+class TriggerID;
+class PreshowerID;
+class EcalID;
+
 namespace  TrackerDD
 {
     class SCT_DetectorManager;
@@ -18,7 +37,7 @@ namespace  TrackerDD
 
 namespace Tracker
 {
-    class PairVertexAlg : public AthReentrantAlgorithm 
+    class PairVertexAlg : public AthReentrantAlgorithm,AthHistogramming  
     {
     public:
         PairVertexAlg(const std::string& name, ISvcLocator* pSvcLocator);
@@ -26,16 +45,35 @@ namespace Tracker
         virtual StatusCode initialize() override;
         virtual StatusCode execute(const EventContext& ctx) const override;
         virtual StatusCode finalize() override;
-
+        const ServiceHandle <ITHistSvc> &histSvc() const;
+        
     private:
+
+        void clearTree() 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;
+        bool waveformHitOK(const xAOD::WaveformHit* hit) const;
+
+        ServiceHandle<ITHistSvc> m_histSvc;
+
         double m_MeV2GeV = 1e-3;
         const FaserSCT_ID* m_idHelper {nullptr};
+        const VetoNuID*    m_vetoNuHelper;
+        const VetoID*      m_vetoHelper;
+        const TriggerID*   m_triggerHelper;
+        const PreshowerID* m_preshowerHelper;
+        const EcalID*      m_ecalHelper;
+        
         const TrackerDD::SCT_DetectorManager* m_detMgr {nullptr};
         SG::ReadHandleKey<McEventCollection> m_mcEventCollectionKey { this, "McEventCollection", "TruthEvent" };
-
         SG::ReadHandleKey<TrackCollection> m_trackCollectionKey { this, "TrackCollection", "CKFTrackCollection" };
-
-        ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool { this, "ExtrapolationTool", "FaserActsExtrapolationTool" };
+        SG::ReadHandleKey<xAOD::WaveformHitContainer> m_vetoNuContainer { this, "VetoNuContainer", "VetoNuWaveformHits", "VetoNu hit container name" };
+        SG::ReadHandleKey<xAOD::WaveformHitContainer> m_vetoContainer { this, "VetoContainer", "VetoWaveformHits", "Veto hit container name" };
+        SG::ReadHandleKey<xAOD::WaveformHitContainer> m_triggerContainer { this, "TriggerContainer", "TriggerWaveformHits", "Trigger hit container name" };
+        SG::ReadHandleKey<xAOD::WaveformHitContainer> m_preshowerContainer { this, "PreshowerContainer", "PreshowerWaveformHits", "Preshower hit container name" };
+        SG::ReadHandleKey<xAOD::WaveformHitContainer> m_ecalContainer { this, "EcalContainer", "CaloWaveformHits", "Ecal hit container name" };
 
         mutable std::atomic<size_t> m_numberOfEvents{0};
         mutable std::atomic<size_t> m_numberOfPositiveTracks{0};
@@ -46,6 +84,62 @@ namespace Tracker
         mutable std::atomic<size_t> m_numberOfNullParameters{0};
         mutable std::atomic<size_t> m_numberOfParametersWithoutSurface{0};
         mutable std::atomic<size_t> m_numberOfNoUpstreamTracks{0};
+        
+        mutable TTree* m_tree;
+        mutable std::vector<std::vector<double>> m_tracks={{},{},{},{},{},{},{}}; //charge, x, y, z, px, py, pz, bS0,bS1,bS2,bS3
+        mutable int m_trackCount=0;
+
+        //POSITIVE
+        mutable std::vector<std::vector<int>> m_playerMap={{},{},{},{}};
+        mutable std::set<int> m_pstationMap;
+        mutable std::vector<double> m_ppos_s1l0 = {-1,-1,-1}; // {x,y,z}
+        mutable std::vector<double> m_pmom;
+        mutable float m_pChi2;
+        mutable double m_pDoF;
+        mutable int m_pnLayers;
+        mutable int m_pnLayersIFT;
+        mutable int m_playersHit;
+        mutable int m_piftHit;
+
+        //NEGATIVE
+        mutable std::vector<std::vector<int>> m_nlayerMap={{},{},{},{}};
+        mutable std::set<int> m_nstationMap;
+        mutable std::vector<double> m_npos_s1l0 = {-1,-1,-1}; // {x,y,z}
+        mutable std::vector<double> m_nmom;
+        mutable float m_nChi2;
+        mutable double m_nDoF;
+        mutable int m_nlayersHit;
+        mutable int m_niftHit;
+
+        //CALORIMETER
+        //StringProperty  m_CaloConfig { this, "CaloConfig", "Low_gain", "Configuration found at http://aagaard.web.cern.ch/aagaard/FASERruns.html (spaces replaced with '_')" };
+        mutable float m_Calo0_Edep;
+        mutable float m_Calo1_Edep;
+        mutable float m_Calo2_Edep;
+        mutable float m_Calo3_Edep;
+        mutable float m_Calo_Total_Edep;
+        mutable float m_MIP_sim_Edep_calo;
+        mutable float m_MIP_sim_Edep_preshower;
+
+
+        //WAVEFORM
+        mutable float m_wave_localtime[15];
+        mutable float m_wave_peak[15];
+        mutable float m_wave_width[15];
+        mutable float m_wave_integral[15];
+        mutable float m_wave_raw_peak[15];
+        mutable float m_wave_raw_integral[15];
+        mutable float m_wave_baseline_mean[15];
+        mutable float m_wave_baseline_rms[15];
+        mutable unsigned int m_wave_status[15];
+
+        //EXTRAPOLATION
+        mutable std::vector<double> m_vertexPos = {-1,-1,-1};
+
     };
+    
+    inline const ServiceHandle<ITHistSvc>& PairVertexAlg::histSvc() const {return m_histSvc;}
+
+    }
+
 
-}
\ No newline at end of file