diff --git a/Calorimeter/CaloRecAlgs/CMakeLists.txt b/Calorimeter/CaloRecAlgs/CMakeLists.txt
index 219db92a3a8927ae9cc025bea46985387e072ce0..75d9d09a23eb857555e55848ee16b1d2b98b89f3 100644
--- a/Calorimeter/CaloRecAlgs/CMakeLists.txt
+++ b/Calorimeter/CaloRecAlgs/CMakeLists.txt
@@ -9,7 +9,7 @@ atlas_subdir( CaloRecAlgs )
 atlas_add_component( CaloRecAlgs
                      src/*.cxx src/*.h
                      src/components/*.cxx
-                     LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserCalorimeter xAODFaserWaveform)
+                     LINK_LIBRARIES AthenaKernel GaudiKernel AthenaBaseComps AthenaPoolUtilities StoreGateLib xAODFaserCalorimeter xAODFaserWaveform CaloRecToolsLib)
 
 atlas_install_python_modules( python/*.py )
 
diff --git a/Calorimeter/CaloRecAlgs/python/CaloRecAlgsConfig.py b/Calorimeter/CaloRecAlgs/python/CaloRecAlgsConfig.py
index 034bf08b5b36a4620567f6716eca5d31338a1c04..83839b9cba1a4c6a50eef52852ed8ee2127dfaa7 100644
--- a/Calorimeter/CaloRecAlgs/python/CaloRecAlgsConfig.py
+++ b/Calorimeter/CaloRecAlgs/python/CaloRecAlgsConfig.py
@@ -1,30 +1,31 @@
-""" Define methods used to instantiate configured Waveform reconstruction tools and algorithms
+""" Define methods used to instantiate configured Calorimeter Calibration reconstruction tools and algorithms
 
-Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+Copyright (C) 2022 CERN for the benefit of the FASER collaboration
 """
 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
 from AthenaConfiguration.ComponentFactory import CompFactory
-
+from IOVDbSvc.IOVDbSvcConfig import addFolders
 from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
 
-#CalorimeterReconstructionTool = CompFactory.CalorimeterReconstructionTool
+from CaloRecTools.CaloRecToolConfig import CaloRecToolCfg
 
 # One stop shopping for normal FASER data
 def CalorimeterReconstructionCfg(flags, **kwargs):
     """ Return all algorithms and tools for Waveform reconstruction """
     acc = ComponentAccumulator()
 
-    #tool = CalorimeterReconstructionTool(name="CaloRecTool", **kwargs)
-
     kwargs.setdefault("CaloWaveHitContainerKey", "CaloWaveformHits")
     kwargs.setdefault("PreshowerWaveHitContainerKey", "PreshowerWaveformHits")
     kwargs.setdefault("CaloHitContainerKey", "CaloHits")
-    #kwargs.setdefault("CalorimeterReconstructionTool", tool)
+    kwargs.setdefault("PreshowerHitContainerKey", "PreshowerHits")
 
     recoAlg = CompFactory.CaloRecAlg("CaloRecAlg", **kwargs)
-    #recoAlg.CalorimeterReconstructionTool = tool
     acc.addEventAlgo(recoAlg)
 
+    dbInstance = kwargs.get("dbInstance", "TRIGGER_OFL") # what should this be set to???
+
+    acc.merge(CaloRecToolCfg(flags))
+
     return acc
 
 def CalorimeterReconstructionOutputCfg(flags, **kwargs):
diff --git a/Calorimeter/CaloRecAlgs/src/CaloRecAlg.cxx b/Calorimeter/CaloRecAlgs/src/CaloRecAlg.cxx
index 4b4d6f158f29478a60443381c5292d254d1c665f..f4f78d488d6c542637003e38ea0d8959f89b6b1c 100644
--- a/Calorimeter/CaloRecAlgs/src/CaloRecAlg.cxx
+++ b/Calorimeter/CaloRecAlgs/src/CaloRecAlg.cxx
@@ -1,4 +1,5 @@
 #include "CaloRecAlg.h"
+#include <math.h>
 
 CaloRecAlg::CaloRecAlg(const std::string& name, 
 		       ISvcLocator* pSvcLocator)
@@ -6,13 +7,9 @@ CaloRecAlg::CaloRecAlg(const std::string& name,
 
 }
 
-StatusCode 
-CaloRecAlg::initialize() {
+StatusCode CaloRecAlg::initialize() {
   ATH_MSG_INFO(name() << "::initalize()" );
 
-  // Initalize tools
-  //ATH_CHECK( m_recoTool.retrieve() );
-
   // Set key to read calo hits from
   ATH_CHECK( m_caloWaveHitContainerKey.initialize() );
 
@@ -21,19 +18,27 @@ CaloRecAlg::initialize() {
 
   // Set key to write container
   ATH_CHECK( m_caloHitContainerKey.initialize() );
+  ATH_CHECK( m_preshowerHitContainerKey.initialize() );
+
+  // Initalize tools
+  ATH_CHECK( m_recoCalibTool.retrieve() );
+
+  // Store calibrattiion factos in a vector for ease of access
+  m_EM_mu_Map[0] = m_calo_ch0_EM_mu;
+  m_EM_mu_Map[1] = m_calo_ch1_EM_mu;
+  m_EM_mu_Map[2] = m_calo_ch2_EM_mu;
+  m_EM_mu_Map[3] = m_calo_ch3_EM_mu;
 
   return StatusCode::SUCCESS;
 }
-
-StatusCode 
-CaloRecAlg::finalize() {
+//----------------------------------------------------------------------
+StatusCode CaloRecAlg::finalize() {
   ATH_MSG_INFO(name() << "::finalize()");
 
   return StatusCode::SUCCESS;
 }
-
-StatusCode 
-CaloRecAlg::execute(const EventContext& ctx) const {
+//----------------------------------------------------------------------
+StatusCode CaloRecAlg::execute(const EventContext& ctx) const {
   ATH_MSG_DEBUG("Executing");
 
   ATH_MSG_DEBUG("Run: " << ctx.eventID().run_number() 
@@ -63,61 +68,112 @@ CaloRecAlg::execute(const EventContext& ctx) const {
   ATH_CHECK( caloHitContainerHandle.record( std::make_unique<xAOD::CalorimeterHitContainer>(),
 					std::make_unique<xAOD::CalorimeterHitAuxContainer>() ) );
 
+  SG::WriteHandle<xAOD::CalorimeterHitContainer> preshowerHitContainerHandle(m_preshowerHitContainerKey, ctx);
+  ATH_CHECK( preshowerHitContainerHandle.record( std::make_unique<xAOD::CalorimeterHitContainer>(),
+                    std::make_unique<xAOD::CalorimeterHitAuxContainer>() ) );
+
   ATH_MSG_DEBUG("WaveformsHitContainer '" << caloHitContainerHandle.name() << "' initialized");
+  ATH_MSG_DEBUG("WaveformsHitContainer '" << preshowerHitContainerHandle.name() << "' initialized");
+
+  // Loop over calo hits and calibrate each primary hit
+  for( const auto& hit : *caloWaveHitHandle ) {
+    if (hit->status_bit(xAOD::WaveformStatus::SECONDARY)) continue;
+
+    // Create a new calo hit
+    xAOD::CalorimeterHit* calo_hit = new xAOD::CalorimeterHit();
+    caloHitContainerHandle->push_back(calo_hit);
 
-  // Reconstruct all waveforms
-  //CHECK( m_recoTool->reconstructAll(*waveformHandle, clockptr, hitContainerHandle.ptr()) );
-
-  // Find peak time (most significant hit)
-  const xAOD::WaveformHit* peakHit = findPeakHit(*caloWaveHitHandle);
-  if (peakHit == NULL) return StatusCode::SUCCESS;
-
-  // Create a new calo hit
-  xAOD::CalorimeterHit* calo_hit = new xAOD::CalorimeterHit();
-  caloHitContainerHandle->push_back(calo_hit);
-
-  calo_hit->set_raw_energy(-1.);  // Dummy value
-
-  // Find closest hits in time per channel
-  std::map<int, const xAOD::WaveformHit*> hitMap;
-  for ( const auto& hit : *caloWaveHitHandle ) {
-    int channel = hit->channel();
-    if (hitMap.count(channel) == 0)
-      hitMap[channel] = hit;
-    else {
-      if (abs(hitMap[channel]->localtime() - peakHit->localtime()) > 
-	  abs(hit->localtime() - peakHit->localtime()))
-	hitMap[channel] = hit;
+    ATH_MSG_DEBUG("calo_hit in channel " << hit->channel() );
+
+    float MIPcharge_ref = m_recoCalibTool->getMIPcharge_ref(hit->channel()); // get reference MIP charge from database
+
+    float charge = hit->integral()/50.0; // divide by 50 ohms to get charge
+    ATH_MSG_DEBUG("calo_hit filled has charge of " << charge << " pC");
+
+    float gainRatio = 1.0;
+    if (m_isMC) {
+      gainRatio = 1.0; // put dummy value for now, this will end up being ratio of digi scale factors
+    } else {
+      gainRatio = extrapolateHVgain(hit->channel());
+      ATH_MSG_DEBUG("HV gain ratio = " << gainRatio );
     }
-  }
 
-  // For each hit found, insert these into the caloHit
-  // Clear before association
-  calo_hit->clearCaloWaveformLinks();
-  for ( const auto& [chan, hit] : hitMap ) {
-    ATH_MSG_VERBOSE("Found hit " << *hit);
-    calo_hit->addCaloHit(caloWaveHitHandle.get(), hit);
+    float Nmip = (charge * gainRatio) / MIPcharge_ref;
+    ATH_MSG_DEBUG("Nmip = " << Nmip );
+    calo_hit->set_Nmip(Nmip); // set Nmip value
+
+    float E_dep = Nmip * m_MIP_sim_Edep_calo;
+    ATH_MSG_DEBUG("E_dep in MeV = " << E_dep );
+    calo_hit->set_E_dep(E_dep);  // set calibrated E_dep value
+
+    float E_EM = Nmip * m_EM_mu_Map[hit->channel()];
+    ATH_MSG_DEBUG("Em E in MeV = " << E_EM );
+    calo_hit->set_E_EM(E_EM);  // set calibrated E_EM value
+
+    float fit_to_raw_ratio = 1.0;
+    if (hit->integral() != 0.0) { // avoid possibility of division by zero error
+      fit_to_raw_ratio = hit->raw_integral() / hit->integral();
+    }
+    calo_hit->set_fit_to_raw_ratio(fit_to_raw_ratio); // set fit-to-raw-ratio that can be used to take any of the calibrated values to what they would be if we used the raw integral instead of the fit integral
+
+    calo_hit->set_channel(hit->channel());  // set channel number
+
+    calo_hit->clearWaveformLinks();
+    calo_hit->addHit(caloWaveHitHandle.get(), hit);  // create link to calo waveform hit
   }
 
   ATH_MSG_DEBUG("CaloHitContainer '" << caloHitContainerHandle.name() << "' filled with "<< caloHitContainerHandle->size() <<" items");
 
-  return StatusCode::SUCCESS;
-}
+  for( const auto& hit : *preshowerWaveHitHandle ) {
+    if (hit->status_bit(xAOD::WaveformStatus::SECONDARY)) continue;
 
-const xAOD::WaveformHit* 
-CaloRecAlg::findPeakHit(const xAOD::WaveformHitContainer& hitContainer) const {
+    // Create a new preshower hit
+    xAOD::CalorimeterHit* preshower_hit = new xAOD::CalorimeterHit();
+    preshowerHitContainerHandle->push_back(preshower_hit);
 
-  const xAOD::WaveformHit* peakHit = NULL;
-  for( const auto& hit : hitContainer ) {
-    if (peakHit == NULL) {
-      peakHit = hit;
-    } else {
-      if ( hit->peak() > peakHit->peak() ) peakHit = hit;
+    ATH_MSG_DEBUG("preshower_hit in channel " << hit->channel() );
+
+    float MIPcharge_ref = m_recoCalibTool->getMIPcharge_ref(hit->channel()); // get reference MIP charge from database
+
+    float charge = hit->integral()/50.0; // divide by 50 ohms to get charge
+    ATH_MSG_DEBUG("preshower_hit filled has charge of " << charge << " pC");
+
+    float Nmip = charge / MIPcharge_ref;
+    ATH_MSG_DEBUG("Nmip = " << Nmip );
+    preshower_hit->set_Nmip(Nmip); // set Nmip value
+
+    float E_dep = Nmip * m_MIP_sim_Edep_preshower;
+    ATH_MSG_DEBUG("E_dep in GeV = " << E_dep );
+    preshower_hit->set_E_dep(E_dep);  // set calibrated E_dep value
+
+    float fit_to_raw_ratio = 1.0;
+    if (hit->integral() != 0.0) { // avoid possibility of division by zero error
+      fit_to_raw_ratio = hit->raw_integral() / hit->integral();
     }
+    preshower_hit->set_fit_to_raw_ratio(fit_to_raw_ratio); // set fit-to-raw-ratio that can be used to take any of the calibrated values to what they would be if we used the raw integral instead of the fit integral
+    
+    preshower_hit->set_channel(hit->channel());  // set channel number
+
+    preshower_hit->clearWaveformLinks();
+    preshower_hit->addHit(preshowerWaveHitHandle.get(), hit);  // create link to preshower waveform hit
   }
 
-  // Didn't find anything?
-  if (peakHit == NULL) return NULL;
-  if (peakHit->status_bit(xAOD::WaveformStatus::THRESHOLD_FAILED)) return NULL;
-  return peakHit;
+  ATH_MSG_DEBUG("PreshowerHitContainer '" << preshowerHitContainerHandle.name() << "' filled with "<< preshowerHitContainerHandle->size() <<" items");
+
+
+  return StatusCode::SUCCESS;
 }
+
+//----------------------------------------------------------------------
+float CaloRecAlg::extrapolateHVgain(int channel) const {
+  float PMT_hv = m_recoCalibTool->getHV(channel);
+  float PMT_hv_ref = m_recoCalibTool->getHV_ref(channel);
+  TF1 gaincurve = m_recoCalibTool->get_PMT_HV_curve(channel);
+
+  float gaincurve_atHV = gaincurve.Eval(PMT_hv);
+  float gaincurve_atHVref = gaincurve.Eval(PMT_hv_ref);
+
+  return ( gaincurve_atHVref / gaincurve_atHV ) * pow( PMT_hv_ref / PMT_hv , 6.6);
+}
+//----------------------------------------------------------------------
+
diff --git a/Calorimeter/CaloRecAlgs/src/CaloRecAlg.h b/Calorimeter/CaloRecAlgs/src/CaloRecAlg.h
index bd98e9afd6b693b8cc4518ebd87ddd72207fddef..721f521621ae519778a8ddadd8b5711f1beeac9b 100644
--- a/Calorimeter/CaloRecAlgs/src/CaloRecAlg.h
+++ b/Calorimeter/CaloRecAlgs/src/CaloRecAlg.h
@@ -12,7 +12,11 @@
 #include "xAODFaserCalorimeter/CalorimeterHitAuxContainer.h"
 
 // Tool classes
-//#include "CaloRecTools/ICalorimeterReconstructionTool.h"
+#include "CaloRecTools/ICaloRecTool.h"
+
+// Include Athena stuff for Conditions db reading
+#include "AthenaPoolUtilities/CondAttrListCollection.h"
+#include "StoreGate/ReadCondHandleKey.h"
 
 // Handles
 #include "StoreGate/ReadHandleKey.h"
@@ -21,9 +25,16 @@
 // Gaudi
 #include "GaudiKernel/ServiceHandle.h"
 #include "GaudiKernel/ToolHandle.h"
+#include "GaudiKernel/EventContext.h"
+#include "GaudiKernel/ICondSvc.h"
+#include "Gaudi/Property.h"
+
+// ROOT
+#include "TF1.h"
 
 // STL
 #include <string>
+#include <vector>
 
 class CaloRecAlg : public AthReentrantAlgorithm {
 
@@ -51,32 +62,40 @@ class CaloRecAlg : public AthReentrantAlgorithm {
   /**
    * @name Reconstruction tool
    */
-  //ToolHandle<ICalorimeterReconstructionTool> m_recoTool
-  //{this, "CalorimeterReconstructionTool", "CalorimeterReconstructionTool"};
+  ToolHandle<ICaloRecTool> m_recoCalibTool {this, "CaloRecTool", "CaloRecTool"};
 
   /**
    * @name Input raw waveform data using SG::ReadHandleKey
    */
   //@{
-  SG::ReadHandleKey<xAOD::WaveformHitContainer> m_caloWaveHitContainerKey
-    {this, "CaloWaveHitContainerKey", ""};
+  SG::ReadHandleKey<xAOD::WaveformHitContainer> m_caloWaveHitContainerKey {this, "CaloWaveHitContainerKey", "CaloWaveformHits"};
   //@}
 
   //@{
-  SG::ReadHandleKey<xAOD::WaveformHitContainer> m_preshowerWaveHitContainerKey
-    {this, "PreshowerWaveHitContainerKey", ""};
+  SG::ReadHandleKey<xAOD::WaveformHitContainer> m_preshowerWaveHitContainerKey {this, "PreshowerWaveHitContainerKey", "PreshowerWaveformHits"};
   //@}
 
   /**
    * @name Output data using SG::WriteHandleKey
    */
   //@{
-  SG::WriteHandleKey<xAOD::CalorimeterHitContainer> m_caloHitContainerKey
-    {this, "CaloHitContainerKey", ""};
+  SG::WriteHandleKey<xAOD::CalorimeterHitContainer> m_caloHitContainerKey {this, "CaloHitContainerKey", "CaloHits"};
+  SG::WriteHandleKey<xAOD::CalorimeterHitContainer> m_preshowerHitContainerKey {this, "PreshowerHitContainerKey", "PreshowerHits"};
   //@}
 
-  const xAOD::WaveformHit* 
-    findPeakHit(const xAOD::WaveformHitContainer& hitContainer) const;
+  float extrapolateHVgain(int channel) const;
+
+  FloatProperty m_MIP_sim_Edep_calo {this, "MIP_sim_Edep_calo", 58.5}; // MIP deposits 5.85 MeV of energy in calo
+  FloatProperty m_MIP_sim_Edep_preshower {this, "MIP_sim_Edep_preshower", 4.894}; // MIP deposits 4.894 MeV of energy in a preshower layer
+
+  FloatProperty m_calo_ch0_EM_mu {this, "m_calo_ch0_EM_mu", 330.0}; // factor used to do rough calibration of calo ch0 to EM energy: 0.33 GeV or 330 MeV
+  FloatProperty m_calo_ch1_EM_mu {this, "m_calo_ch1_EM_mu", 330.0}; // factor used to do rough calibration of calo ch1 to EM energy: 0.33 GeV or 330 MeV
+  FloatProperty m_calo_ch2_EM_mu {this, "m_calo_ch2_EM_mu", 330.0}; // factor used to do rough calibration of calo ch2 to EM energy: 0.33 GeV or 330 MeV
+  FloatProperty m_calo_ch3_EM_mu {this, "m_calo_ch3_EM_mu", 330.0}; // factor used to do rough calibration of calo ch3 to EM energy: 0.33 GeV or 330 MeV
+
+  float m_EM_mu_Map[4]; // vector that holds EM_mu calibration factors for calo channels
+
+  Gaudi::Property<bool> m_isMC {this, "isMC", false};
 
 };
 
diff --git a/Calorimeter/CaloRecTools/CMakeLists.txt b/Calorimeter/CaloRecTools/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f0e9f07b53f5426b360c2639d4f7953a44d57792
--- /dev/null
+++ b/Calorimeter/CaloRecTools/CMakeLists.txt
@@ -0,0 +1,30 @@
+################################################################################
+# Package: CaloRecTools
+################################################################################
+
+# Declare the package name:
+atlas_subdir( CaloRecTools )
+
+# External dependencies:
+find_package( ROOT )
+
+# Component(s) in the package:
+atlas_add_library( CaloRecToolsLib
+                   CaloRecTools/*.h src/*.cxx src/*.h
+                   PUBLIC_HEADERS CaloRecTools
+                   PRIVATE_INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+                   LINK_LIBRARIES AthenaBaseComps AthenaPoolUtilities AthenaKernel GaudiKernel
+                   PRIVATE_LINK_LIBRARIES ${ROOT_LIBRARIES}
+       		   )
+
+atlas_add_component( CaloRecTools
+		     src/components/*.cxx 
+		     INCLUDE_DIRS ${ROOT_INCLUDE_DIRS}
+                     LINK_LIBRARIES ${ROOT_LIBRARIES} 
+		     AthenaBaseComps AthenaPoolUtilities AthenaKernel GaudiKernel 
+		     CaloRecToolsLib)
+
+# Install files from the package:
+atlas_install_python_modules( python/*.py )
+
+
diff --git a/Calorimeter/CaloRecTools/CaloRecTools/ICaloRecTool.h b/Calorimeter/CaloRecTools/CaloRecTools/ICaloRecTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..feb141d915d6214a681f437d94b8118e14c33820
--- /dev/null
+++ b/Calorimeter/CaloRecTools/CaloRecTools/ICaloRecTool.h
@@ -0,0 +1,45 @@
+/*
+  Copyright (C) 2022 CERN for the benefit of the FASER collaboration
+*/
+
+/**
+ * @file ICaloRecTool.h
+ * Header file for the ICaloRecTool class
+ * @author Deion Fellers, 2022
+ */
+
+
+#ifndef CALORECTOOL_ICALORECTOOL_H
+#define CALORECTOOL_ICALORECTOOL_H
+
+// Base class
+#include "GaudiKernel/IAlgTool.h"
+#include "GaudiKernel/ToolHandle.h"
+#include "GaudiKernel/EventContext.h"
+
+#include "TF1.h"
+
+///Interface for Calo reco algorithms
+class ICaloRecTool : virtual public IAlgTool 
+{
+  public:
+
+  // InterfaceID
+  DeclareInterfaceID(ICaloRecTool, 1, 0);
+
+  virtual ~ICaloRecTool() = default; //!< Destructor
+
+  // methods to return calibration database data
+  virtual float getHV(const EventContext& ctx, int channel) const = 0;
+  virtual float getHV(int channel) const = 0;
+
+  virtual float getHV_ref(const EventContext& ctx, int channel) const = 0;
+  virtual float getHV_ref(int channel) const = 0;
+
+  virtual float getMIPcharge_ref(const EventContext& ctx, int channel) const = 0;
+  virtual float getMIPcharge_ref(int channel) const = 0;
+
+  virtual TF1 get_PMT_HV_curve(int channel) const = 0;
+};
+
+#endif // CALORECTOOL_ICALORECTOOL_H
diff --git a/Calorimeter/CaloRecTools/python/CaloRecToolConfig.py b/Calorimeter/CaloRecTools/python/CaloRecToolConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..65be875da12b6deee3cbf9f35217cee6f40784ff
--- /dev/null
+++ b/Calorimeter/CaloRecTools/python/CaloRecToolConfig.py
@@ -0,0 +1,27 @@
+""" Define methods to configure CaloRecTool
+
+Copyright (C) 2022 CERN for the benefit of the FASER collaboration
+"""
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+from IOVDbSvc.IOVDbSvcConfig import addFolders
+CaloRecTool=CompFactory.CaloRecTool
+
+
+def CaloRecToolCfg(flags, name="CaloRecTool", **kwargs):
+    """ Return configured ComponentAccumulator and tool for Calo Calibration 
+
+    CaloRecTool may be provided in kwargs
+    """
+
+    acc = ComponentAccumulator()
+    # tool = kwargs.get("CaloRecTool", CaloRecTool(flags))
+    # Probably need to figure this out!
+    dbInstance = kwargs.get("dbInstance", "TRIGGER_OFL")
+
+    acc.merge(addFolders(flags, "/WAVE/Calibration/HV", dbInstance, className="CondAttrListCollection"))
+    acc.merge(addFolders(flags, "/WAVE/Calibration/MIP_ref", dbInstance, className="CondAttrListCollection"))
+
+    return acc
+
+
diff --git a/Calorimeter/CaloRecTools/src/CaloRecTool.cxx b/Calorimeter/CaloRecTools/src/CaloRecTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..916011019805ab9087c57427859086dec456eb80
--- /dev/null
+++ b/Calorimeter/CaloRecTools/src/CaloRecTool.cxx
@@ -0,0 +1,194 @@
+/*
+  Copyright (C) 2022 CERN for the benefit of the FASER collaboration
+*/
+
+/**
+ * @file CaloRecTool.cxx
+ * Implementation file for the CaloRecTool.cxx
+ * @ author Deion Fellers, 2022
+ **/
+
+#include "CaloRecTool.h"
+
+// Constructor
+CaloRecTool::CaloRecTool(const std::string& type, const std::string& name, const IInterface* parent) :
+  base_class(type, name, parent)
+{
+}
+
+//--------------------------------------------------------------
+StatusCode
+CaloRecTool::initialize() {
+  ATH_MSG_INFO( name() << "::initalize()" );
+
+  // Set keys to read calibratiion variables from data base
+  ATH_CHECK( m_PMT_HV_ReadKey.initialize() );
+  ATH_CHECK( m_MIP_ref_ReadKey.initialize() );
+
+  // access and store calo PMT HV gain curves
+  HVgaincurves_rootFile = TFile::Open(m_PMT_HV_Gain_Curve_file.value().c_str(),"read");   // open root file that has TF1 gain curves stored
+
+  chan0_HVgaincurve_pntr = HVgaincurves_rootFile->Get<TF1>(m_chan0_HVgaincurve_name.value().c_str()); // make pointers to the gain curves
+  chan1_HVgaincurve_pntr = HVgaincurves_rootFile->Get<TF1>(m_chan1_HVgaincurve_name.value().c_str());
+  chan2_HVgaincurve_pntr = HVgaincurves_rootFile->Get<TF1>(m_chan2_HVgaincurve_name.value().c_str());
+  chan3_HVgaincurve_pntr = HVgaincurves_rootFile->Get<TF1>(m_chan3_HVgaincurve_name.value().c_str());
+
+  m_HVgainCurveMap[0] = *chan0_HVgaincurve_pntr;  // store TF1 objects in an array mapped to the calo channel numbers
+  m_HVgainCurveMap[1] = *chan1_HVgaincurve_pntr;
+  m_HVgainCurveMap[2] = *chan2_HVgaincurve_pntr;
+  m_HVgainCurveMap[3] = *chan3_HVgaincurve_pntr;
+
+  HVgaincurves_rootFile->Close(); // close the root file
+
+
+  return StatusCode::SUCCESS;
+}
+
+//--------------------------------------------------------------
+StatusCode
+CaloRecTool::finalize() {
+  // Print where you are
+  return StatusCode::SUCCESS;
+}
+
+//--------------------------------------------------------------
+TF1 CaloRecTool::get_PMT_HV_curve(int channel) const {
+  if (channel <= 4) {
+    return m_HVgainCurveMap[channel];
+  } else {
+    ATH_MSG_WARNING("channel "<<channel<<" is not <= 4 and thus not a calorimeter channel, so no HV gain-curve exists!");
+  }
+  return TF1("default", "1.0", 0.0, 2000.0);
+}
+
+//--------------------------------------------------------------
+float CaloRecTool::getHV(const EventContext& ctx, int channel) const {
+
+  ATH_MSG_DEBUG("in getHV("<<channel<<")");
+
+  float HV=0.;
+
+  // Read Cond Handle
+  SG::ReadCondHandle<CondAttrListCollection> readHandle{m_PMT_HV_ReadKey, ctx};
+  const CondAttrListCollection* readCdo{*readHandle}; 
+  if (readCdo==nullptr) {
+    ATH_MSG_FATAL("Null pointer to the read conditions object");
+    return HV;
+  }
+  // Get the validitiy range
+  EventIDRange rangeW;
+  if (not readHandle.range(rangeW)) {
+    ATH_MSG_FATAL("Failed to retrieve validity range for " << readHandle.key());
+    return HV;
+  }
+  ATH_MSG_DEBUG("Size of CondAttrListCollection " << readHandle.fullKey() << " readCdo->size()= " << readCdo->size());
+  ATH_MSG_DEBUG("Range of input is " << rangeW);
+
+  // Read offset for specific channel
+  const CondAttrListCollection::AttributeList& payload{readCdo->attributeList(channel)};
+
+  if (payload.exists("HV") and not payload["HV"].isNull()) {
+    HV = payload["HV"].data<float>();
+    ATH_MSG_DEBUG("Found digitizer channel " << channel << ", HV as " << HV);
+  } else {
+    ATH_MSG_WARNING("No valid HV found for channel "<<channel<<"!");
+  }
+
+  return HV;
+
+}
+
+float CaloRecTool::getHV(int channel) const {
+  const EventContext& ctx(Gaudi::Hive::currentContext());
+  return CaloRecTool::getHV(ctx, channel);
+}
+
+//----------------------------------------------------------------------
+float CaloRecTool::getHV_ref(const EventContext& ctx, int channel) const {
+
+  ATH_MSG_DEBUG("in getHV_ref("<<channel<<")");
+
+  float HV_ref=0.;
+
+  // Read Cond Handle
+  SG::ReadCondHandle<CondAttrListCollection> readHandle{m_MIP_ref_ReadKey, ctx};
+  const CondAttrListCollection* readCdo{*readHandle};
+  if (readCdo==nullptr) {
+    ATH_MSG_FATAL("Null pointer to the read conditions object");
+    return HV_ref;
+  }
+  // Get the validitiy range
+  EventIDRange rangeW;
+  if (not readHandle.range(rangeW)) {
+    ATH_MSG_FATAL("Failed to retrieve validity range for " << readHandle.key());
+    return HV_ref;
+  }
+  ATH_MSG_DEBUG("Size of CondAttrListCollection " << readHandle.fullKey() << " readCdo->size()= " << readCdo->size());
+  ATH_MSG_DEBUG("Range of input is " << rangeW);
+
+  // Read offset for specific channel
+  const CondAttrListCollection::AttributeList& payload{readCdo->attributeList(channel)};
+
+  if (payload.exists("HV_ref") and not payload["HV_ref"].isNull()) {
+    HV_ref = payload["HV_ref"].data<float>();
+    ATH_MSG_DEBUG("Found digitizer channel " << channel << ", HV_ref as " << HV_ref);
+  } else {
+    ATH_MSG_WARNING("No valid HV_ref found for channel "<<channel<<"!");
+  }
+
+  return HV_ref;
+
+}
+
+float CaloRecTool::getHV_ref(int channel) const {
+  const EventContext& ctx(Gaudi::Hive::currentContext());
+  return CaloRecTool::getHV_ref(ctx, channel);
+}
+
+//----------------------------------------------------------------------
+float CaloRecTool::getMIPcharge_ref(const EventContext& ctx, int channel) const {
+
+  ATH_MSG_DEBUG("in getMIPcharge_ref("<<channel<<")");
+
+  float MIP_charge_ref =0.;
+
+  // Read Cond Handle
+  SG::ReadCondHandle<CondAttrListCollection> readHandle{m_MIP_ref_ReadKey, ctx};
+  const CondAttrListCollection* readCdo{*readHandle};
+  if (readCdo==nullptr) {
+    ATH_MSG_FATAL("Null pointer to the read conditions object");
+    return MIP_charge_ref;
+  }
+  // Get the validitiy range
+  EventIDRange rangeW;
+  if (not readHandle.range(rangeW)) {
+    ATH_MSG_FATAL("Failed to retrieve validity range for " << readHandle.key());
+    return MIP_charge_ref;
+  }
+  ATH_MSG_DEBUG("Size of CondAttrListCollection " << readHandle.fullKey() << " readCdo->size()= " << readCdo->size());
+  ATH_MSG_DEBUG("Range of input is " << rangeW);
+
+  // Read offset for specific channel
+  const CondAttrListCollection::AttributeList& payload{readCdo->attributeList(channel)};
+
+  if (payload.exists("charge_ref") and not payload["charge_ref"].isNull()) {
+    MIP_charge_ref = payload["charge_ref"].data<float>();
+    ATH_MSG_DEBUG("Found digitizer channel " << channel << ", MIP_charge_ref as " << MIP_charge_ref);
+  } else {
+    ATH_MSG_WARNING("No valid MIP_charge_ref found for channel "<<channel<<"!");
+  }
+
+  return MIP_charge_ref;
+
+}
+
+float CaloRecTool::getMIPcharge_ref(int channel) const {
+  const EventContext& ctx(Gaudi::Hive::currentContext());
+  return CaloRecTool::getMIPcharge_ref(ctx, channel);
+}
+
+//--------------------------------------------------------------
+
+
+
+
diff --git a/Calorimeter/CaloRecTools/src/CaloRecTool.h b/Calorimeter/CaloRecTools/src/CaloRecTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ab891ad42784e3388d933f42de9267545fab3d9
--- /dev/null
+++ b/Calorimeter/CaloRecTools/src/CaloRecTool.h
@@ -0,0 +1,85 @@
+/*
+   Copyright (C) 2021 CERN for the benefit of the FASER collaboration
+*/
+
+/** @file CaloRecTool.h
+ *  Header file for CaloRecTool.h
+ *
+ */
+#ifndef CALORECTOOLS_CALORECTOOL_H
+#define CALORECTOOLS_CALORECTOOL_H
+
+// Include interface class
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "CaloRecTools/ICaloRecTool.h"
+
+// Include Athena stuff
+#include "AthenaPoolUtilities/CondAttrListCollection.h"
+#include "StoreGate/ReadCondHandleKey.h"
+
+#include "GaudiKernel/ICondSvc.h"
+#include "Gaudi/Property.h"
+
+// Include Gaudi classes
+#include "GaudiKernel/ICondSvc.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/EventContext.h"
+
+// Include ROOT classes
+#include "TF1.h"
+#include "TFile.h"
+
+#include <string>
+
+class CaloRecTool: public extends<AthAlgTool, ICaloRecTool> {
+ public:
+
+  /// Normal constructor for an AlgTool; 'properties' are also declared here
+  CaloRecTool(const std::string& type, const std::string& name, const IInterface* parent);
+
+  // Standard Gaudi functions
+  virtual StatusCode initialize() override; //!< Gaudi initialiser
+  virtual StatusCode finalize() override; //!< Gaudi finaliser
+
+  // methods for returning calibration data
+  // Channels indexed by digitizer channel number
+  // HV is in Volts and MIPcharge is in pC
+  //
+  virtual float getHV(const EventContext& ctx, int channel) const override;
+  virtual float getHV(int channel) const override;
+
+  virtual float getHV_ref(const EventContext& ctx, int channel) const override;
+  virtual float getHV_ref(int channel) const override;
+
+  virtual float getMIPcharge_ref(const EventContext& ctx, int channel) const override;
+  virtual float getMIPcharge_ref(int channel) const override;
+
+  // method for returning PMT HV calibration curves from root file
+  virtual TF1 get_PMT_HV_curve(int channel) const override;
+
+  TFile* HVgaincurves_rootFile;
+
+  TF1* chan0_HVgaincurve_pntr;
+  TF1* chan1_HVgaincurve_pntr;
+  TF1* chan2_HVgaincurve_pntr;
+  TF1* chan3_HVgaincurve_pntr;
+
+  TF1 m_HVgainCurveMap[4];
+
+  private:
+  // Propert that points to the location of the root file which contains the HV gain curves for the calorimeter PMTs
+  StringProperty m_PMT_HV_Gain_Curve_file{this, "PMT_HV_Gain_Curve_file", "/cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/pmtgain/CaloGainCurves.root"};
+
+  // properties that map channel name to PMT HV gain curves. PMt names found at https://twiki.cern.ch/twiki/bin/viewauth/FASER/CaloScintillator
+  StringProperty m_chan0_HVgaincurve_name{this, "Chan0HVgaincurve", "pol5_HV_Gain_Curve_PMT_LB8770"};
+  StringProperty m_chan1_HVgaincurve_name{this, "Chan1HVgaincurve", "pol5_HV_Gain_Curve_PMT_LB8733"};
+  StringProperty m_chan2_HVgaincurve_name{this, "Chan2HVgaincurve", "pol5_HV_Gain_Curve_PMT_LB8786"};
+  StringProperty m_chan3_HVgaincurve_name{this, "Chan3HVgaincurve", "pol5_HV_Gain_Curve_PMT_LB8732"};
+
+  // Read Cond Handle
+  SG::ReadCondHandleKey<CondAttrListCollection> m_PMT_HV_ReadKey{this, "PMT_HV_ReadKey", "/WAVE/Calibration/HV", "Key of folder for PMT HV reading"};
+  SG::ReadCondHandleKey<CondAttrListCollection> m_MIP_ref_ReadKey{this, "MIP_ref_ReadKey", "/WAVE/Calibration/MIP_ref", "Key of folder for MIP charge calibration measurment, also stores PMT HV used to measure the reference MIP charge"};
+
+};
+
+#endif // CALORECTOOLS_CALORECTOOL_H
diff --git a/Calorimeter/CaloRecTools/src/components/CaloRecTools_entries.cxx b/Calorimeter/CaloRecTools/src/components/CaloRecTools_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c9c5438eb029be7532820e35dee8e41710f5e781
--- /dev/null
+++ b/Calorimeter/CaloRecTools/src/components/CaloRecTools_entries.cxx
@@ -0,0 +1,3 @@
+#include "../CaloRecTool.h"
+
+DECLARE_COMPONENT( CaloRecTool )
diff --git a/Control/CalypsoExample/Digitization/scripts/faserMDC_digi.py b/Control/CalypsoExample/Digitization/scripts/faserMDC_digi.py
index 1154edd8aad0cd4ac8daadf5559e05ef1cc362b9..4c7dba7e427cb0b545a0cbc0526ed4b6c6918e76 100755
--- a/Control/CalypsoExample/Digitization/scripts/faserMDC_digi.py
+++ b/Control/CalypsoExample/Digitization/scripts/faserMDC_digi.py
@@ -81,7 +81,7 @@ elif runtype == "TestBeamMC" :
 # New TI12 geometry (ugh)
 elif runtype == "TI12MC":
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03" 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 
 else:
     print("Invalid run type found:", runtype)
diff --git a/Control/CalypsoExample/Digitization/scripts/faserMDC_digi_merge.py b/Control/CalypsoExample/Digitization/scripts/faserMDC_digi_merge.py
index a421cead6c379b8dd56fc2d80ea86bb9ca8ab342..8ee8d6f744d79bfdcbf9eb4177a89fc3021583e5 100755
--- a/Control/CalypsoExample/Digitization/scripts/faserMDC_digi_merge.py
+++ b/Control/CalypsoExample/Digitization/scripts/faserMDC_digi_merge.py
@@ -154,7 +154,7 @@ elif runtype == "TestBeamMC" :
 # New TI12 geometry (ugh)
 elif runtype == "TI12MC":
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03" 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 
 else:
     print("Invalid run type found:", runtype)
diff --git a/Control/CalypsoExample/Digitization/scripts/faser_digi.py b/Control/CalypsoExample/Digitization/scripts/faser_digi.py
index 72f890a3bd3b7f352f60c66207de15aaa2ec4961..40fb1915d50a8b3e6fd25bb6808b04ef1c5d9c16 100755
--- a/Control/CalypsoExample/Digitization/scripts/faser_digi.py
+++ b/Control/CalypsoExample/Digitization/scripts/faser_digi.py
@@ -78,7 +78,7 @@ elif runtype == "TestBeamMC" :
 # New TI12 geometry (ugh)
 elif runtype == "TI12MC":
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03" 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 
 else:
     print("Invalid geometry type found:", runtype)
diff --git a/Control/CalypsoExample/Digitization/scripts/faser_digi_merge.py b/Control/CalypsoExample/Digitization/scripts/faser_digi_merge.py
index ea135db8346438f9a392959d7791744997353a1e..1be2af2d92bcd37edeb2d2d20517e1254069822f 100755
--- a/Control/CalypsoExample/Digitization/scripts/faser_digi_merge.py
+++ b/Control/CalypsoExample/Digitization/scripts/faser_digi_merge.py
@@ -168,7 +168,7 @@ elif runtype == "TestBeamMC" :
 # New TI12 geometry (ugh)
 elif runtype == "TI12MC":
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03" 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 
 else:
     print("Invalid run type found:", runtype)
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_1000GeV-101312.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_1000GeV-101312.json
new file mode 100644
index 0000000000000000000000000000000000000000..ee3ac4eefd1264fb4553b29090772a1b9353282e
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_1000GeV-101312.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 5000,
+    "mass": 105.66,
+    "maxE": 1000.0,
+    "minE": 1000.0,
+    "pid": [-13, 13],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 101312,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_muon_fasernu_1000GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_100GeV-101310.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_100GeV-101310.json
new file mode 100644
index 0000000000000000000000000000000000000000..8c949c0bcdc862ec4b97bfa934e85ad590b78647
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_100GeV-101310.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 5000,
+    "mass": 105.66,
+    "maxE": 100.0,
+    "minE": 100.0,
+    "pid": [-13, 13],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 101310,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_muon_fasernu_100GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_10GeV-101308.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_10GeV-101308.json
new file mode 100644
index 0000000000000000000000000000000000000000..66abf1a6f5a248b875a779e0888d936aa06263b4
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_10GeV-101308.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 5000,
+    "mass": 105.66,
+    "maxE": 10.0,
+    "minE": 10.0,
+    "pid": [-13, 13],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 101308,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_muon_fasernu_10GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_300GeV-101311.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_300GeV-101311.json
new file mode 100644
index 0000000000000000000000000000000000000000..5307647e9ca6c5a3d0b1fa311dc0800b23febf51
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_300GeV-101311.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 5000,
+    "mass": 105.66,
+    "maxE": 300.0,
+    "minE": 300.0,
+    "pid": [-13, 13],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 101311,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_muon_fasernu_300GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_30GeV-101309.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_30GeV-101309.json
new file mode 100644
index 0000000000000000000000000000000000000000..6eda07ec333b0ef2a0685c7cd7d9b6120e65ed43
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_muon_fasernu_30GeV-101309.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 5000,
+    "mass": 105.66,
+    "maxE": 30.0,
+    "minE": 30.0,
+    "pid": [-13, 13],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 101309,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_muon_fasernu_30GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_1000GeV-121106.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_1000GeV-121106.json
new file mode 100644
index 0000000000000000000000000000000000000000..513974d4cdbdcc23e5750a06d382a0633c9a2bf1
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_1000GeV-121106.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 500,
+    "mass": 105.66,
+    "maxE": 1000.0,
+    "minE": 1000.0,
+    "pid": [211, -211],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 121106,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_pion_fasernu_1000GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_100GeV-121104.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_100GeV-121104.json
new file mode 100644
index 0000000000000000000000000000000000000000..79b23b151fa1cfce149904689cd1c220ac9ea4e2
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_100GeV-121104.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 1000,
+    "mass": 105.66,
+    "maxE": 100.0,
+    "minE": 100.0,
+    "pid": [211, -211],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 121104,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_pion_fasernu_100GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_10GeV-121102.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_10GeV-121102.json
new file mode 100644
index 0000000000000000000000000000000000000000..7cec4dd2dcbb6af17ac8a26efb3a90e2d2b6b6ab
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_10GeV-121102.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 1000,
+    "mass": 105.66,
+    "maxE": 10.0,
+    "minE": 10.0,
+    "pid": [211, -211],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 121102,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_pion_fasernu_10GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_300GeV-121105.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_300GeV-121105.json
new file mode 100644
index 0000000000000000000000000000000000000000..255af7e27c014ffc94b16b068af5bc90fb45e824
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_300GeV-121105.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 1000,
+    "mass": 105.66,
+    "maxE": 300.0,
+    "minE": 300.0,
+    "pid": [211, -211],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 121105,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_pion_fasernu_300GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_30GeV-121103.json b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_30GeV-121103.json
new file mode 100644
index 0000000000000000000000000000000000000000..6c25133024940086163302a453aff06e15153f76
--- /dev/null
+++ b/Control/CalypsoExample/Generation/data/mdc/FaserMC-MDC_PG_pion_fasernu_30GeV-121103.json
@@ -0,0 +1,14 @@
+{
+    "file_length": 1000,
+    "mass": 105.66,
+    "maxE": 30.0,
+    "minE": 30.0,
+    "pid": [211, -211],
+    "radius": -25.0,
+    "angle": 0.0006,
+    "run": 121103,
+    "sampler": "const",
+    "segment": 0,
+    "short": "MDC_PG_pion_fasernu_30GeV",
+    "zpos": -3990.0
+}
diff --git a/Control/CalypsoExample/Generation/scripts/faserMDC_foresee.py b/Control/CalypsoExample/Generation/scripts/faserMDC_foresee.py
index f16b6a40a0413278097824bb7af63b51811eaa9c..bec911031cfec291fe523d5ded2916c688538ea2 100755
--- a/Control/CalypsoExample/Generation/scripts/faserMDC_foresee.py
+++ b/Control/CalypsoExample/Generation/scripts/faserMDC_foresee.py
@@ -76,7 +76,7 @@ if __name__ == '__main__':
     ConfigFlags.addFlag("Sim.Beam.yshift", 12.) 
 
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"   # Geometry set-up
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"   # Conditions set-up
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"   # Conditions set-up
     ConfigFlags.addFlag("Input.InitialTimeStamp", 0)   # To avoid autoconfig 
     ConfigFlags.GeoModel.Align.Dynamic = False
 
diff --git a/Control/CalypsoExample/Generation/scripts/faserMDC_particlegun.py b/Control/CalypsoExample/Generation/scripts/faserMDC_particlegun.py
index 98975a548601209a81b932c7cbbbe03da7184035..32d68b601f4fc43ef9d2942efff26f4562e83813 100755
--- a/Control/CalypsoExample/Generation/scripts/faserMDC_particlegun.py
+++ b/Control/CalypsoExample/Generation/scripts/faserMDC_particlegun.py
@@ -72,7 +72,7 @@ if __name__ == '__main__':
     ConfigFlags.addFlag("Sim.Beam.yshift", 0)        
 
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"   # Geometry set-up
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"   # Conditions set-up
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"   # Conditions set-up
     ConfigFlags.addFlag("Input.InitialTimeStamp", 0)   # To avoid autoconfig 
     ConfigFlags.GeoModel.Align.Dynamic = False
 
diff --git a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
index 6abdfdd4a8c8ae18547462b68532bd51cc4a9d71..cc1628efdb9245e32b7abbbc9ca60a60b2151fad 100755
--- a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
+++ b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
@@ -74,8 +74,8 @@ if __name__ == '__main__':
 
     if args.geom == "TI12MC":
         # 2022 TI12 geometry
-        ConfigFlags.GeoModel.FaserVersion = "FASERNU-02"  # Geometry set-up
-        ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"  # Conditions set-up
+        ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"  # Geometry set-up
+        ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"  # Conditions set-up
         # TI12 detectors
         detectors = ['Veto', 'VetoNu', 'Preshower', 'FaserSCT', 'Ecal', 'Trigger', 
                      'Dipole', 'Emulsion', 'Trench']
diff --git a/Control/CalypsoExample/Generation/scripts/submit_faserMDC_particlegun.sh b/Control/CalypsoExample/Generation/scripts/submit_faserMDC_particlegun.sh
index 5d704e9a51445d2ae408512c4eb0df83c71a8b39..08f21dbfc5b08fcc1225ef2594099bb0e51f0d38 100755
--- a/Control/CalypsoExample/Generation/scripts/submit_faserMDC_particlegun.sh
+++ b/Control/CalypsoExample/Generation/scripts/submit_faserMDC_particlegun.sh
@@ -199,7 +199,7 @@ if ! [ -z "$outdest" ]
 then
     ls -l
     echo "copy *-HITS.root to $outdest"
-    mkdir -p $outdest
+    eos mkdir -p $outdest 
     eos cp *-HITS.root ${outdest}/ || true
 fi
 #
@@ -209,13 +209,13 @@ then
     cd ..
     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
diff --git a/Control/CalypsoExample/GeoModelTest/python/Faser03TestConfig.py b/Control/CalypsoExample/GeoModelTest/python/Faser03TestConfig.py
index 70322eb150975256868a84bf7bd2174697d0a0da..bf562e1b7aba756ebdab8e65568a180321c5de12 100644
--- a/Control/CalypsoExample/GeoModelTest/python/Faser03TestConfig.py
+++ b/Control/CalypsoExample/GeoModelTest/python/Faser03TestConfig.py
@@ -33,7 +33,7 @@ if __name__ == "__main__":
     
 # Flags for this job
     ConfigFlags.Input.isMC = True                                # Needed to bypass autoconfig
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersion
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersion
     ConfigFlags.GeoModel.FaserVersion     = "FASERNU-03"           # Default FASER geometry
     ConfigFlags.GeoModel.GeoExportFile    = "FaserNu03.db"        # Writes out a GeoModel file with the full geometry tree (optional, comment out to skip)
     ConfigFlags.Detector.GeometryEmulsion = True
diff --git a/Control/CalypsoExample/Reconstruction/scripts/faserMDC_reco.py b/Control/CalypsoExample/Reconstruction/scripts/faserMDC_reco.py
index 6f5b224ee7b2de4bd7dfe66aa277a8b5b755157c..eddc43e211437c8d313510b919a167a7089a2066 100755
--- a/Control/CalypsoExample/Reconstruction/scripts/faserMDC_reco.py
+++ b/Control/CalypsoExample/Reconstruction/scripts/faserMDC_reco.py
@@ -88,12 +88,12 @@ elif runtype == "TestBeamData" or runtype == "TestBeam2021":
 # New TI12 geometry (ugh)
 elif runtype == "TI12Data02":
     ConfigFlags.GeoModel.FaserVersion = "FASER-02" 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 
 # Final 2022 TI12 geometry
 elif runtype == "TI12Data03":
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03" 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 
 else:
     print("Invalid run type found:", runtype)
diff --git a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py
index 4e233bf4fcb8a014393bf8858c55ef25d7690519..72235c1f2bb0d6f1167e4b12350ecb5214ecd580 100755
--- a/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py
+++ b/Control/CalypsoExample/Reconstruction/scripts/faser_reco.py
@@ -101,7 +101,11 @@ else:
 ConfigFlags.Input.ProjectName = "data20"
 ConfigFlags.GeoModel.Align.Dynamic    = False
 
+# Flags for later
 useCKF = True
+useCal = False
+useLHC = False
+
 # Enable ACTS material corrections, this crashes testbeam geometries
 ConfigFlags.TrackingGeometry.MaterialSource = "/cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/acts/material-maps.json"
 
@@ -115,16 +119,21 @@ elif runtype == "TestBeamData" or runtype == "TestBeamMC":
     ConfigFlags.GeoModel.FaserVersion = "FASER-TB00" 
     ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-TB00"
     useCKF = False
+    useCal = True
 
 # New TI12 geometry (ugh)
 elif runtype == "TI12Data02":
     ConfigFlags.GeoModel.FaserVersion = "FASER-02" 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 
 # Final 2022 TI12 geometry
 elif runtype == "TI12Data03":
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03" 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    # ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+    # Use the updated field map
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
+    useCal = True
+    useLHC = True
 
 else:
     print("Invalid run type found:", runtype)
@@ -176,13 +185,21 @@ else:
 from FaserGeoModel.FaserGeoModelConfig import FaserGeometryCfg
 acc.merge(FaserGeometryCfg(ConfigFlags))
 
+if useLHC:
+    from LHCDataAlgs.LHCDataAlgConfig import LHCDataAlgCfg
+    acc.merge(LHCDataAlgCfg(ConfigFlags))
+
 # Set up algorithms
 from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionCfg    
 acc.merge(WaveformReconstructionCfg(ConfigFlags))
 
-# Not ready for primetime
-# from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionCfg
-# acc.merge(CalorimeterReconstructionCfg(ConfigFlags))
+# Calorimeter reconstruction
+if args.isMC:
+    # Not ready for MC quite yet
+    pass
+elif useCal:
+    from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionCfg
+    acc.merge(CalorimeterReconstructionCfg(ConfigFlags))
 
 # Tracker clusters
 from TrackerPrepRawDataFormation.TrackerPrepRawDataFormationConfig import FaserSCT_ClusterizationCfg
@@ -230,6 +247,9 @@ itemList = [ "xAOD::EventInfo#*"
              , "TrackCollection#*"
 ]
 #
+if useLHC:
+    itemList.extend( ["xAOD::FaserLHCData#*", "xAOD::FaserLHCDataAux#*"] )
+
 if args.isMC:
     # Make xAOD versions of truth
     from Reconstruction.xAODTruthCnvAlgConfig import xAODTruthCnvAlgCfg
@@ -252,8 +272,8 @@ from WaveRecAlgs.WaveRecAlgsConfig import WaveformReconstructionOutputCfg
 acc.merge(WaveformReconstructionOutputCfg(ConfigFlags))
 
 # Calorimeter reconstruction output
-# from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionOutputCfg
-# acc.merge(CalorimeterReconstructionOutputCfg(ConfigFlags))
+from CaloRecAlgs.CaloRecAlgsConfig import CalorimeterReconstructionOutputCfg
+acc.merge(CalorimeterReconstructionOutputCfg(ConfigFlags))
 
 # Check what we have
 print( "Writing out xAOD objects:" )
diff --git a/Control/CalypsoExample/Reconstruction/scripts/submit_faserMDC_reco.sh b/Control/CalypsoExample/Reconstruction/scripts/submit_faserMDC_reco.sh
index e4d93ba6487000c7c939ee57d79dd704dad96324..0f9cb4479a5130508a58f91efd11b3538b97ebf2 100755
--- a/Control/CalypsoExample/Reconstruction/scripts/submit_faserMDC_reco.sh
+++ b/Control/CalypsoExample/Reconstruction/scripts/submit_faserMDC_reco.sh
@@ -163,7 +163,7 @@ else
     tagstr="--reco=$tag"
 fi
 #
-faser_reco.py "--nevents=$nevents" $tagstr "$file_path" 
+faser_reco.py "--nevents=$nevents" --isMC $tagstr "$file_path" 
 #
 # Print out ending time
 date
diff --git a/Control/CalypsoExample/SimHitExample/python/SimHitExampleConfig.py b/Control/CalypsoExample/SimHitExample/python/SimHitExampleConfig.py
index 5e71b860f80281d8075e993cc3a41c535bfb4c71..aec94afcf8e3007eb454fe0c28f9a2c42ee7ed28 100644
--- a/Control/CalypsoExample/SimHitExample/python/SimHitExampleConfig.py
+++ b/Control/CalypsoExample/SimHitExample/python/SimHitExampleConfig.py
@@ -32,7 +32,7 @@ if __name__ == "__main__":
 # Flags for this job
     ConfigFlags.Input.Files = ["my.HITS.pool.root"]              # input file(s)
     ConfigFlags.Input.isMC = True                                # Needed to bypass autoconfig
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersion
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersion
     ConfigFlags.GeoModel.FaserVersion     = "FASERNU-03"         # Default FASER geometry
     ConfigFlags.Detector.GeometryEmulsion = True
     ConfigFlags.Detector.GeometryTrench   = True
diff --git a/Control/CalypsoExample/Simulation/scripts/faserMDC_simulate.py b/Control/CalypsoExample/Simulation/scripts/faserMDC_simulate.py
index 0fd9622db5de8630ffaef34bd2ae4b6eabe88a5c..1cdb68251fd2f35df0e66382b76822a9bedfc5ec 100755
--- a/Control/CalypsoExample/Simulation/scripts/faserMDC_simulate.py
+++ b/Control/CalypsoExample/Simulation/scripts/faserMDC_simulate.py
@@ -147,7 +147,7 @@ if __name__ == '__main__':
     ConfigFlags.addFlag("Sim.Beam.yshift", args.yshift)    
 
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"     # Geometry set-up
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"     # Conditions set-up
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"     # Conditions set-up
     ConfigFlags.addFlag("Input.InitialTimeStamp", 0)     # To avoid autoconfig 
     ConfigFlags.GeoModel.Align.Dynamic = False
 
diff --git a/Control/CalypsoExample/xAODTruthConversion/scripts/runTruthCnv.py b/Control/CalypsoExample/xAODTruthConversion/scripts/runTruthCnv.py
index b51b4d4ba059d571921fe6328e13144ccf104f23..5e63eccee4f4887ff0a986dfe7cdd02e90f59942 100644
--- a/Control/CalypsoExample/xAODTruthConversion/scripts/runTruthCnv.py
+++ b/Control/CalypsoExample/xAODTruthConversion/scripts/runTruthCnv.py
@@ -52,7 +52,7 @@ if __name__ == "__main__":
     
 # Flags for this job
     ConfigFlags.Input.isMC = True                                # Needed to bypass autoconfig
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersion
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersion
     ConfigFlags.GeoModel.FaserVersion     = "FASERNU-03"           # Default FASER geometry
     ConfigFlags.Input.Files = ['my.HITS.pool.root']
     ConfigFlags.Output.doWriteAOD = True
diff --git a/Database/ConnectionManagement/FaserAuthentication/CMakeLists.txt b/Database/ConnectionManagement/FaserAuthentication/CMakeLists.txt
index ecf7b9bb3985ff15c82df3794cf93b627ccbb915..eb1c72ae4cfdfd5b7a8a8da914da78a154f9446d 100644
--- a/Database/ConnectionManagement/FaserAuthentication/CMakeLists.txt
+++ b/Database/ConnectionManagement/FaserAuthentication/CMakeLists.txt
@@ -39,6 +39,7 @@ set( FaserAuthentication_home
 # Apparently not needed, because we don't have it...
 #atlas_install_xmls( ${FaserAuthentication_home}/authentication.xml )
 
+
 # Configure the environment setup module:
 configure_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/FaserAuthenticationEnvironmentConfig.cmake.in
diff --git a/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml b/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml
index 958fb2ba600c0384381a06460487cb5e83efac98..1518267eeab17bed6f96f3673f6e309c01401dad 100644
--- a/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml
+++ b/Database/ConnectionManagement/FaserAuthentication/data/dblookup.xml
@@ -26,14 +26,24 @@
   <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/CABP200.db" accessMode="read" />
  </logicalservice>
 
+ <logicalservice name="COOLOFL_DIGITIZER">
+  <service name="sqlite_file:data/sqlite200/ALLP200.db" accessMode="read" />
+  <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/ALLP200.db" accessMode="read" />
+ </logicalservice>
+
  <logicalservice name="COOLOFL_INDET">
   <service name="sqlite_file:data/sqlite200/ALLP200.db" accessMode="read" />
   <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/ALLP200.db" accessMode="read" />
  </logicalservice>
 
 <logicalservice name="COOLOFL_TRIGGER">
-   <service name="sqlite_file:data/sqlite200/ALLP200.db" accessMode="read" />
-   <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/ALLP200.db" accessMode="read" />   
+  <service name="sqlite_file:data/sqlite200/ALLP200.db" accessMode="read" />
+  <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/ALLP200.db" accessMode="read" />   
+</logicalservice>
+
+<logicalservice name="COOLOFL_LHC">
+ <service name="sqlite_file:data/sqlite200/ALLP200.db" accessMode="read" />
+  <service name="sqlite_file:///cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/ALLP200.db" accessMode="read" />   
 </logicalservice>
 
 </servicelist>
diff --git a/Derviation/DerivationAlgs/share/runDerive.py b/Derviation/DerivationAlgs/share/runDerive.py
index b8b22c187c15a39993a68aa722e5d6bae3362f7b..019c6ad29ec5fc1134723c8219cba3caaa440be9 100644
--- a/Derviation/DerivationAlgs/share/runDerive.py
+++ b/Derviation/DerivationAlgs/share/runDerive.py
@@ -48,7 +48,7 @@ if __name__ == "__main__":
         #"/bundle/data/FASER/Ti12data/filter/r0008/007983/Faser-Physics-007983-TrigMask08-r0008-xAOD.root"
         ]
 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersionS
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # 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 = False                               # Needed to bypass autoconfig
diff --git a/Derviation/DerivationAlgs/share/run_streaming.py b/Derviation/DerivationAlgs/share/run_streaming.py
index 3ebacb7892ab51e1d2b5c775bae917dd9e4fa6c3..13fcf2d7011a32bd8a3f14f166c8ff86ca5596a5 100644
--- a/Derviation/DerivationAlgs/share/run_streaming.py
+++ b/Derviation/DerivationAlgs/share/run_streaming.py
@@ -23,7 +23,7 @@ if __name__ == "__main__":
         "/eos/experiment/faser/rec/2022/p0008/007984/Faser-Physics-007984-00000-p0008-xAOD.root"
         ]
 
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersionS
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # 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 = False                               # Needed to bypass autoconfig
diff --git a/DetectorDescription/GeoModel/FaserGeoModel/python/GeoModelInit.py b/DetectorDescription/GeoModel/FaserGeoModel/python/GeoModelInit.py
index fbee2144d985412d2624d31e745eec881f3fb54d..4823fcde4cd212a0f3e6446bf690e29c8cf90616 100644
--- a/DetectorDescription/GeoModel/FaserGeoModel/python/GeoModelInit.py
+++ b/DetectorDescription/GeoModel/FaserGeoModel/python/GeoModelInit.py
@@ -95,9 +95,9 @@ def _setupGeoModel():
 
 # Deal with SCT alignment conditions folders and algorithms
 
-    #conddb.addFolderSplitOnline("SCT","/Tracker/Onl/Align","/Tracker/Align",className="AlignableTransformContainer")
-    print("Override Alignment dbname to OFLP200, fix this when alignment available in CONDBR3")
-    conddb.addFolder("/Tracker/Align", "SCT_OFL",className="AlignableTransformContainer")
+    conddb.addFolderSplitOnline("SCT","/Tracker/Onl/Align","/Tracker/Align",className="AlignableTransformContainer")
+    # print("Override Alignment dbname to OFLP200, fix this when alignment available in CONDBR3")
+    # conddb.addFolder("/Tracker/Align", "SCT_OFL",className="AlignableTransformContainer")
     from AthenaCommon.AlgSequence import AthSequencer
     condSeq = AthSequencer("AthCondSeq")
     if not hasattr(condSeq, "FaserSCT_AlignCondAlg"):
diff --git a/LHCData/LHCDataAlgs/CMakeLists.txt b/LHCData/LHCDataAlgs/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..17abef2b21c0be98b310d45ed210fba0c52421db
--- /dev/null
+++ b/LHCData/LHCDataAlgs/CMakeLists.txt
@@ -0,0 +1,19 @@
+###############################################################################
+# Package: LHCDataAlgs
+################################################################################
+
+# Declare the package name:
+atlas_subdir( LHCDataAlgs )
+
+# External dependencies:
+find_package( Boost COMPONENTS filesystem thread system )
+
+# Component(s) in the package:
+atlas_add_component( LHCDataAlgs
+                     src/*.h
+                     src/*.cxx
+                     src/components/*.cxx
+                     LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserLHC LHCDataToolsLib )
+
+# Install files from the package:
+atlas_install_python_modules( python/*.py )
diff --git a/LHCData/LHCDataAlgs/python/LHCDataAlgConfig.py b/LHCData/LHCDataAlgs/python/LHCDataAlgConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..195fbbd12fe448c71e544bc619d503b7a468eae6
--- /dev/null
+++ b/LHCData/LHCDataAlgs/python/LHCDataAlgConfig.py
@@ -0,0 +1,20 @@
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+
+from LHCDataTools.LHCDataToolsConfig import LHCDataCfg
+
+#LHCDataTool = CompFactory.LHCDataTool
+
+def LHCDataAlgCfg(flags, name="LHCDataAlg", **kwargs):
+
+    acc = ComponentAccumulator()
+
+    tool = CompFactory.LHCDataTool(name="LHCDataTool")
+    alg = CompFactory.LHCDataAlg(name, **kwargs)
+    alg.LHCDataTool = tool
+
+    acc.addEventAlgo(alg)
+    acc.merge(LHCDataCfg(flags))
+
+    return acc
diff --git a/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx b/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..de22698589abfbbbfc2ea9aa6d54feacd4661610
--- /dev/null
+++ b/LHCData/LHCDataAlgs/src/LHCDataAlg.cxx
@@ -0,0 +1,237 @@
+#include "LHCDataAlg.h"
+
+#include "xAODFaserLHC/FaserLHCDataAux.h"
+
+LHCDataAlg::LHCDataAlg(const std::string& name, ISvcLocator* pSvcLocator)
+  : AthReentrantAlgorithm(name, pSvcLocator) { 
+}
+
+StatusCode
+LHCDataAlg::initialize() {
+  ATH_MSG_INFO(name() << "::initalize()" );
+
+  // Initalize tools
+  ATH_CHECK( m_lhcTool.retrieve() );
+
+  // Read handle
+  ATH_CHECK( m_eventInfo.initialize() );
+
+  // Write handle
+  ATH_CHECK( m_lhcDataKey.initialize() );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode
+LHCDataAlg::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+StatusCode
+LHCDataAlg::execute(const EventContext& ctx) const {
+
+  ATH_MSG_DEBUG("Executing");
+
+  SG::WriteHandle<xAOD::FaserLHCData> lhcDataHandle(m_lhcDataKey, ctx);
+  ATH_CHECK( lhcDataHandle.record( std::make_unique<xAOD::FaserLHCData>(),
+				   std::make_unique<xAOD::FaserLHCDataAux>() ) );
+
+  ATH_MSG_DEBUG("FaserLHCData " << lhcDataHandle.name() << " initialized");
+
+  lhcDataHandle->set_fillNumber(m_lhcTool->getFillNumber(ctx));
+  lhcDataHandle->set_machineMode(m_lhcTool->getMachineMode(ctx));
+  lhcDataHandle->set_beamMode(m_lhcTool->getBeamMode(ctx));
+  lhcDataHandle->set_beamType1(m_lhcTool->getBeamType1(ctx));
+  lhcDataHandle->set_beamType2(m_lhcTool->getBeamType2(ctx));
+
+  lhcDataHandle->set_betaStar(m_lhcTool->getBetaStar(ctx));
+  lhcDataHandle->set_crossingAngle(m_lhcTool->getCrossingAngle(ctx));
+
+  lhcDataHandle->set_stableBeams(m_lhcTool->getStableBeams(ctx));
+  lhcDataHandle->set_injectionScheme(m_lhcTool->getInjectionScheme(ctx));
+
+  // Slight naming mismatch here, but oh well
+  lhcDataHandle->set_numBunchBeam1(m_lhcTool->getBeam1Bunches(ctx));
+  lhcDataHandle->set_numBunchBeam2(m_lhcTool->getBeam2Bunches(ctx));
+  lhcDataHandle->set_numBunchColliding(m_lhcTool->getCollidingBunches(ctx));
+
+  // Fill BCID information
+
+  // Get the BCID mask
+  std::vector<unsigned char> bcid_mask = m_lhcTool->getBCIDMasks(ctx);
+
+  // Get the event bcid value
+  SG::ReadHandle<xAOD::EventInfo> xevt(m_eventInfo, ctx);
+  unsigned int bcid = xevt->bcid();
+
+  int 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
+  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
+  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;
+  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);
+  lhcDataHandle->set_distanceToPreviousColliding(previous);
+  ATH_MSG_DEBUG("Found distance of " << previous << " from BCID " << bcid 
+		<< " to the previous colliding bunch ");
+
+  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 ");
+
+  return StatusCode::SUCCESS;
+}
+
+// Function to find distance to nearest BCID
+// mask indicates the condition: 1 - unpaired B1, 2 - unpaired B2, 3 - colliding
+int
+LHCDataAlg::findNearest(unsigned int bcid, const std::vector<unsigned char>& bcid_mask, unsigned char mask) const {
+
+  // Does the BCID make sense?
+  if ((bcid > 3564) || (bcid <= 0)) {
+    ATH_MSG_WARNING("Requested distance from invalid BCID " << bcid << "!");
+    return -3565;
+  }
+
+  unsigned int test_bcid;
+
+  // Move outwards from the bcid (starting at offset of 0)
+  for (unsigned int i=0; i < 3564/2; i++) {
+
+    // Try positive offsets
+    test_bcid = (bcid + i) % 3564;
+
+    // BCID 0 doesn't exist in the real machine
+    // BCID 3564 doesn't exist in our array (but will always be empty in real machine)
+    // So avoid these pathologies
+    if (test_bcid != 0) 
+      if (mask == bcid_mask[test_bcid]) return i;
+
+    if(i == 0) continue;  // No need to check -0
+
+    // Try negative offsets
+    test_bcid = (bcid - i) % 3564;
+
+    // BCID 0 doesn't exist in the real machine
+    // BCID 3564 doesn't exist in our array (but will always be empty)
+    // So avoid these pathologies
+    if (test_bcid != 0) 
+      if (mask == bcid_mask[test_bcid]) return -i;
+
+  }
+
+  // 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);
+  return -3565;
+
+}
+
+// Function to find previous colliding BCID
+unsigned int
+LHCDataAlg::previousColliding(unsigned int bcid, const std::vector<unsigned char>& bcid_mask) const {
+
+  // Does the BCID make sense?
+  if ((bcid > 3564) || (bcid <= 0)) {
+    ATH_MSG_WARNING("Requested distance from invalid BCID " << bcid << "!");
+    return 9999;
+  }
+
+  unsigned int test_bcid;
+
+  // Move backwards from the bcid (starting at offset of 0)
+  for (unsigned int i=0; i < 3564; i++) {
+
+    // Try positive offsets
+    test_bcid = (bcid - i) % 3564;
+
+    // BCID 3564 doesn't exist in our array (but will always be empty)
+    // So avoid these pathologies
+    if (test_bcid != 0) 
+      if (0x03 == bcid_mask[test_bcid]) return i;
+
+  }
+
+  // If we got to here, there was no match
+  // Does the BCID make sense?
+  ATH_MSG_WARNING("Couldn't find colliding from BCID " << bcid << "!");
+  ATH_MSG_WARNING(bcid_mask);
+  return 9999;
+
+}
+
+unsigned int
+LHCDataAlg::previousTrain(unsigned int bcid, const std::vector<unsigned char>& bcid_mask) const {
+
+  // Does the BCID make sense?
+  if ((bcid > 3564) || (bcid <= 0)) {
+    ATH_MSG_WARNING("Requested distance from invalid BCID " << bcid << "!");
+    return 9999;
+  }
+
+  unsigned int test_bcid;
+
+  bool in_train = false;
+  // Move backwards from the bcid (starting at offset of 0)
+  for (unsigned int i=0; i < 3564; i++) {
+
+    // BCID to test
+    test_bcid = (bcid - i) % 3564;
+
+    // BCID 3564 doesn't exist in our array (but will always be empty)
+    // So avoid these pathologies
+    ATH_MSG_DEBUG("Test " << test_bcid << " In train: " << in_train << " mask[test_bcid]");
+
+    // If we are not in a train before, lets see if we are in a train now
+    if (!in_train) {
+
+      // If we found a colliding bunch, set in_train, otherwise we are still not in a train
+      if ((test_bcid != 0) && (0x03 == bcid_mask[test_bcid]))
+	in_train = true;
+
+      // Either way, move to next BCID
+      continue;
+
+    } else {
+
+      // Currently in a train, lets see if now we are not
+      if ((test_bcid == 0) || (0x03 != bcid_mask[test_bcid])) {
+	in_train = false;
+	// Previous BCID was first in train
+	return i-1;
+      } else {
+	in_train = true;
+      }
+	
+    }
+
+  }
+
+  // If we got to here, there was no match
+  // Does the BCID make sense?
+  ATH_MSG_WARNING("Couldn't find first in train from BCID " << bcid << "!");
+  ATH_MSG_WARNING(bcid_mask);
+  return 9999;
+
+}
diff --git a/LHCData/LHCDataAlgs/src/LHCDataAlg.h b/LHCData/LHCDataAlgs/src/LHCDataAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..a8785725aad2eec117a51e87ba54b83e5bd5ef4f
--- /dev/null
+++ b/LHCData/LHCDataAlgs/src/LHCDataAlg.h
@@ -0,0 +1,47 @@
+#ifndef LHCDATAALG_H
+#define LHCDATAALG_H
+
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+
+#include "StoreGate/ReadCondHandleKey.h"
+#include "StoreGate/WriteCondHandleKey.h"
+
+#include "GaudiKernel/ToolHandle.h"
+#include "GaudiKernel/ICondSvc.h"
+#include "GaudiKernel/ServiceHandle.h"
+
+#include "xAODFaserLHC/FaserLHCData.h"
+#include "xAODEventInfo/EventInfo.h"
+
+#include "LHCDataTools/ILHCDataTool.h"
+
+class LHCDataAlg : public AthReentrantAlgorithm {
+ public:
+  LHCDataAlg(const std::string& name, ISvcLocator* pSvcLocator);
+  virtual ~LHCDataAlg() = default;
+
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext& ctx) const override;
+  virtual StatusCode finalize() override;
+  virtual bool isClonable() const override { return true; };
+
+ private:
+  ToolHandle<ILHCDataTool> m_lhcTool{this, "LHCDataTool", "LHCDataTool"};
+  SG::ReadHandleKey<xAOD::EventInfo> m_eventInfo{ this, "EventInfoKey", "EventInfo", "ReadHandleKey for xAOD::EventInfo"};
+  SG::WriteHandleKey<xAOD::FaserLHCData> m_lhcDataKey
+    {this, "FaserLHCDataKey", "FaserLHCData"};
+
+  // Utility function to find nearest BCID
+  int findNearest(unsigned int bcid, const std::vector<unsigned char>& bcid_mask, 
+		  unsigned char mask) const;
+
+  unsigned int 
+    previousColliding(unsigned int bcid, const std::vector<unsigned char>& bcid_mask) const;
+
+  // Find the distance to the first BCID of the previous train
+  unsigned int 
+    previousTrain(unsigned int bcid, const std::vector<unsigned char>& bcid_mask) const;
+
+};
+
+#endif // LHCDATAALG_H
diff --git a/LHCData/LHCDataAlgs/src/components/LHCDataAlgs_entries.cxx b/LHCData/LHCDataAlgs/src/components/LHCDataAlgs_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1620e8dcfc1af5dc0ae3f2bc4af406684b120896
--- /dev/null
+++ b/LHCData/LHCDataAlgs/src/components/LHCDataAlgs_entries.cxx
@@ -0,0 +1,3 @@
+#include "../LHCDataAlg.h"
+
+DECLARE_COMPONENT( LHCDataAlg )
diff --git a/LHCData/LHCDataTools/CMakeLists.txt b/LHCData/LHCDataTools/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..248abc91d58df409f4bc1e65324743c2f66755dd
--- /dev/null
+++ b/LHCData/LHCDataTools/CMakeLists.txt
@@ -0,0 +1,27 @@
+###############################################################################
+# Package: LHCDataTools
+################################################################################
+
+# Declare the package name:
+atlas_subdir( LHCDataTools )
+
+# External dependencies:
+find_package( CLHEP )
+find_package( ROOT COMPONENTS Core Tree MathCore Hist RIO pthread )
+
+# Component(s) in the package:
+atlas_add_component ( LHCDataTools
+                      src/components/*.cxx
+                      INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${CLHEP_INCLUDE_DIRS}
+                      LINK_LIBRARIES ${ROOT_LIBRARIES} ${CLHEP_LIBRARIES} AthenaKernel LHCDataToolsLib GaudiKernel AthenaBaseComps AthenaPoolUtilities StoreGateLib xAODEventInfo )
+
+
+atlas_add_library( LHCDataToolsLib
+                  src/*.cxx
+                  PUBLIC_HEADERS LHCDataTools
+                  INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${CLHEP_INCLUDE_DIRS}
+                  LINK_LIBRARIES ${ROOT_LIBRARIES} ${CLHEP_LIBRARIES} AthenaKernel  GaudiKernel AthenaBaseComps AthenaPoolUtilities StoreGateLib xAODEventInfo )
+
+# Install files from the package:
+atlas_install_python_modules( python/*.py )
+
diff --git a/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h b/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..6e469d0ebecd41d08333d20eb0778f5bfef5d404
--- /dev/null
+++ b/LHCData/LHCDataTools/LHCDataTools/ILHCDataTool.h
@@ -0,0 +1,75 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS and FAsER collaborations
+*/
+
+/** @file ILHCDataTool.h Interface file for LHCDataTool.
+ */
+
+// Multiple inclusion protection
+#ifndef ILHCDATATOOL
+#define ILHCDATATOOL
+
+//STL includes
+#include <map>
+
+//Gaudi Includes
+#include "GaudiKernel/IAlgTool.h"
+#include "GaudiKernel/EventContext.h"
+
+class ILHCDataTool: virtual public IAlgTool {
+
+ public:
+  
+  //----------Public Member Functions----------//
+  // Structors
+  virtual ~ILHCDataTool() = default; //!< Destructor
+
+  /// Creates the InterfaceID and interfaceID() method
+  DeclareInterfaceID(ILHCDataTool, 1, 0);
+
+  // Methods to return fill data
+  virtual int getFillNumber(const EventContext& ctx) const = 0;
+  virtual int getFillNumber(void) const = 0;
+
+  virtual std::string getMachineMode(const EventContext& ctx) const = 0;
+  virtual std::string getMachineMode(void) const = 0;
+
+  virtual std::string getInjectionScheme(const EventContext& ctx) const = 0;
+  virtual std::string getInjectionScheme(void) const = 0;
+
+  virtual int getBeamType1(const EventContext& ctx) const = 0;
+  virtual int getBeamType1(void) const = 0;
+
+  virtual int getBeamType2(const EventContext& ctx) const = 0;
+  virtual int getBeamType2(void) const = 0;
+
+  // Methods to return beam data
+  virtual std::string getBeamMode(const EventContext& ctx) const = 0;
+  virtual std::string getBeamMode(void) const = 0;
+
+  virtual float getBetaStar(const EventContext& ctx) const = 0;
+  virtual float getBetaStar(void) const = 0;
+
+  virtual float getCrossingAngle(const EventContext& ctx) const = 0;
+  virtual float getCrossingAngle(void) const = 0;
+
+  virtual bool getStableBeams(const EventContext& ctx) const = 0;
+  virtual bool getStableBeams(void) const = 0;
+
+  // Methods to return BCID data
+  virtual unsigned int getBeam1Bunches(const EventContext& ctx) const = 0;
+  virtual unsigned int getBeam1Bunches(void) const = 0;
+
+  virtual unsigned int getBeam2Bunches(const EventContext& ctx) const = 0;
+  virtual unsigned int getBeam2Bunches(void) const = 0;
+
+  virtual unsigned int getCollidingBunches(const EventContext& ctx) const = 0;
+  virtual unsigned int getCollidingBunches(void) const = 0;
+
+  virtual std::vector<unsigned char> getBCIDMasks(const EventContext& ctx) const = 0;
+  virtual std::vector<unsigned char> getBCIDMasks(void) const = 0;
+
+};
+
+//---------------------------------------------------------------------- 
+#endif // LHCDATATOOL
diff --git a/LHCData/LHCDataTools/python/LHCDataToolsConfig.py b/LHCData/LHCDataTools/python/LHCDataToolsConfig.py
new file mode 100644
index 0000000000000000000000000000000000000000..cdd2d267cb63d5ba05378eff252afe4c6d97fe8e
--- /dev/null
+++ b/LHCData/LHCDataTools/python/LHCDataToolsConfig.py
@@ -0,0 +1,19 @@
+# Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
+from AthenaConfiguration.ComponentFactory import CompFactory
+from IOVDbSvc.IOVDbSvcConfig import addFolders
+
+def LHCDataCfg(flags, **kwargs):
+    acc = ComponentAccumulator()
+    dbInstance = kwargs.get("dbInstance", "COOLOFL_LHC")
+    dbName = flags.IOVDb.DatabaseInstance # e.g. CONDBR3 
+
+    #acc.merge(addFolders(flags, folder_list, dbInstance, className="AthenaAttributeList"))
+    # COOLOFL_LHC is not known to ATLAS IOVDBSvc
+    # Must use non-shorthand folder specification here
+    folder_list = ["/LHC/FillData", "/LHC/BeamData", "/LHC/BCIDData"]
+    for folder_name in folder_list:
+        folder_string = f"<db>{dbInstance}/{dbName}</db> {folder_name}"
+        acc.merge(addFolders(flags, folder_string, className="AthenaAttributeList"))
+    return acc
+
diff --git a/LHCData/LHCDataTools/src/LHCDataTool.cxx b/LHCData/LHCDataTools/src/LHCDataTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..7b0fafe00d7137d0d62e81c28f77d812d2501001
--- /dev/null
+++ b/LHCData/LHCDataTools/src/LHCDataTool.cxx
@@ -0,0 +1,237 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS and FASER collaborations
+*/
+
+/** @file LHCDataTool.cxx Implementation file for LHCDataTool.
+    @author Eric Torrence (05/02/22)
+*/
+
+#include "LHCDataTool.h"
+#include "CoralBase/Blob.h"
+
+//----------------------------------------------------------------------
+LHCDataTool::LHCDataTool (const std::string& type, const std::string& name, const IInterface* parent) :
+  base_class(type, name, parent)
+{
+}
+
+//----------------------------------------------------------------------
+StatusCode 
+LHCDataTool::initialize() {
+
+  ATH_MSG_DEBUG("LHCDataTool::initialize()");
+
+  // Read Handles
+  //ATH_CHECK(m_fillDataKey.initialize());
+  ATH_CHECK(m_beamDataKey.initialize());
+  ATH_CHECK(m_bcidDataKey.initialize());
+
+  return StatusCode::SUCCESS;
+}
+
+//----------------------------------------------------------------------
+StatusCode
+LHCDataTool::finalize() {
+  // Print where you are
+  return StatusCode::SUCCESS;
+}
+
+//----------------------------------------------------------------------
+int
+LHCDataTool::getFillNumber(const EventContext& ctx) const {
+  // Read Cond Handle
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return (**readHandle)["FillNumber"].data<int>();
+} 
+
+int
+LHCDataTool::getFillNumber(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getFillNumber(ctx);
+}
+
+//----------------------------------------------------------------------
+std::string
+LHCDataTool::getMachineMode(const EventContext& ctx) const {
+  // Read Cond Handle
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return (**readHandle)["MachineMode"].data<std::string>();
+} 
+
+std::string
+LHCDataTool::getMachineMode(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getMachineMode(ctx);
+}
+
+//----------------------------------------------------------------------
+std::string
+LHCDataTool::getBeamMode(const EventContext& ctx) const {
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return(**readHandle)["BeamMode"].data<std::string>();
+}
+
+std::string
+LHCDataTool::getBeamMode(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getBeamMode(ctx);
+}
+
+//----------------------------------------------------------------------
+int
+LHCDataTool::getBeamType1(const EventContext& ctx) const {
+  // Read Cond Handle
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return (**readHandle)["BeamType1"].data<int>();
+} 
+
+int
+LHCDataTool::getBeamType1(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getBeamType1(ctx);
+}
+
+//----------------------------------------------------------------------
+int
+LHCDataTool::getBeamType2(const EventContext& ctx) const {
+  // Read Cond Handle
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return (**readHandle)["BeamType2"].data<int>();
+} 
+
+int
+LHCDataTool::getBeamType2(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getBeamType2(ctx);
+}
+
+//----------------------------------------------------------------------
+float
+LHCDataTool::getBetaStar(const EventContext& ctx) const {
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return(**readHandle)["BetaStar"].data<float>();
+}
+
+float
+LHCDataTool::getBetaStar(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getBetaStar(ctx);
+}
+
+//----------------------------------------------------------------------
+float
+LHCDataTool::getCrossingAngle(const EventContext& ctx) const {
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return(**readHandle)["CrossingAngle"].data<float>();
+}
+
+float
+LHCDataTool::getCrossingAngle(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getCrossingAngle(ctx);
+}
+
+//----------------------------------------------------------------------
+bool
+LHCDataTool::getStableBeams(const EventContext& ctx) const {
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return(**readHandle)["StableBeams"].data<bool>();
+}
+
+bool
+LHCDataTool::getStableBeams(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getStableBeams(ctx);
+}
+
+//----------------------------------------------------------------------
+std::string
+LHCDataTool::getInjectionScheme(const EventContext& ctx) const {
+  // Read Cond Handle
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_beamDataKey, ctx};
+  return (**readHandle)["InjectionScheme"].data<std::string>();
+} 
+
+std::string
+LHCDataTool::getInjectionScheme(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getInjectionScheme(ctx);
+}
+
+//----------------------------------------------------------------------
+unsigned int
+LHCDataTool::getBeam1Bunches(const EventContext& ctx) const {
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_bcidDataKey, ctx};
+  return(**readHandle)["Beam1Bunches"].data<unsigned int>();
+}
+
+unsigned int
+LHCDataTool::getBeam1Bunches(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getBeam1Bunches(ctx);
+}
+
+unsigned int
+LHCDataTool::getBeam2Bunches(const EventContext& ctx) const {
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_bcidDataKey, ctx};
+  return(**readHandle)["Beam2Bunches"].data<unsigned int>();
+}
+
+unsigned int
+LHCDataTool::getBeam2Bunches(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getBeam2Bunches(ctx);
+}
+
+unsigned int
+LHCDataTool::getCollidingBunches(const EventContext& ctx) const {
+  SG::ReadCondHandle<AthenaAttributeList> readHandle{m_bcidDataKey, ctx};
+  return(**readHandle)["CollidingBunches"].data<unsigned int>();
+}
+
+unsigned int
+LHCDataTool::getCollidingBunches(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getCollidingBunches(ctx);
+}
+
+
+//----------------------------------------------------------------------
+// This function unpacks the blob every time this is accesed
+// Should probably cache this using a callback
+std::vector<unsigned char>
+LHCDataTool::getBCIDMasks(const EventContext& ctx) const {
+
+  SG::ReadCondHandle<AthenaAttributeList> bcidHandle{m_bcidDataKey, ctx};
+  const coral::Blob& blob = (**bcidHandle)["BCIDmasks"].data<coral::Blob>();
+  const unsigned char* p = static_cast<const unsigned char*>(blob.startingAddress());
+
+  // Should always be 3564 BCIDs
+  if (blob.size() != 3564) {
+    ATH_MSG_WARNING("Found BCID blob with size " << blob.size() << "!");
+  }
+
+  std::vector<unsigned char> bcid_vector(3564);
+
+  bool first = true;
+  for (int i=0; i<blob.size(); i++) {
+    // First BCID is 1, but this is stored at location i=1
+    // So you can index this vector as bcid_vector[bcid_number]
+    bcid_vector[i] = *p++;  
+    if (first && (bcid_vector[i] == 3)) {
+      first = false;
+      ATH_MSG_DEBUG("Found first colliding BCID at " << i);
+    }
+  }
+
+  return bcid_vector;
+}
+
+std::vector<unsigned char>
+LHCDataTool::getBCIDMasks(void) const {
+  const EventContext& ctx{Gaudi::Hive::currentContext()};
+  return getBCIDMasks(ctx);
+}
+
+
+
diff --git a/LHCData/LHCDataTools/src/LHCDataTool.h b/LHCData/LHCDataTools/src/LHCDataTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..3acabfb7502ac6b33d5b6d8a918fe8be97582b74
--- /dev/null
+++ b/LHCData/LHCDataTools/src/LHCDataTool.h
@@ -0,0 +1,101 @@
+// -*- C++ -*-
+
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS and CERN collaborations
+*/
+
+/** @file LHCDataTool.h Header file for LHCDataTool.
+    @author Eric Torrence, 20/04/22
+*/
+
+// Multiple inclusion protection
+#ifndef LHCDATA_TOOL
+#define LHCDATA_TOOL
+
+// Include interface class
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "LHCDataTools/ILHCDataTool.h"
+
+// Include Athena stuff
+#include "AthenaPoolUtilities/CondAttrListCollection.h"
+#include "StoreGate/ReadCondHandleKey.h"
+
+#include "GaudiKernel/ICondSvc.h"
+#include "Gaudi/Property.h"
+
+// Include Gaudi classes
+#include "GaudiKernel/EventContext.h"
+
+/** This class contains a Tool that reads Waveform range data and makes it available to 
+    other algorithms. The current implementation reads the data from a COOL database. 
+*/
+
+class LHCDataTool: public extends<AthAlgTool, ILHCDataTool> {
+
+ public:
+  //----------Public Member Functions----------//
+  // Structors
+  LHCDataTool(const std::string& type, const std::string& name, const IInterface* parent); //!< Constructor
+  virtual ~LHCDataTool() = default; //!< Destructor
+
+  // Standard Gaudi functions
+  virtual StatusCode initialize() override; //!< Gaudi initialiser
+  virtual StatusCode finalize() override; //!< Gaudi finaliser
+
+  // Methods to return fill data
+  // Methods to return beam data
+  virtual int getFillNumber(const EventContext& ctx) const override;
+  virtual int getFillNumber(void) const override;
+
+  virtual std::string getMachineMode(const EventContext& ctx) const override;
+  virtual std::string getMachineMode(void) const override;
+
+  virtual std::string getBeamMode(const EventContext& ctx) const override;
+  virtual std::string getBeamMode(void) const override;
+
+  virtual int getBeamType1(const EventContext& ctx) const override;
+  virtual int getBeamType1(void) const override;
+
+  virtual int getBeamType2(const EventContext& ctx) const override;
+  virtual int getBeamType2(void) const override;
+
+  virtual float getBetaStar(const EventContext& ctx) const override;
+  virtual float getBetaStar(void) const override;
+
+  virtual float getCrossingAngle(const EventContext& ctx) const override;
+  virtual float getCrossingAngle(void) const override;
+
+  virtual bool getStableBeams(const EventContext& ctx) const override;
+  virtual bool getStableBeams(void) const override;
+
+  virtual std::string getInjectionScheme(const EventContext& ctx) const override;
+  virtual std::string getInjectionScheme(void) const override;
+
+  // Methods to return BCID data
+  virtual unsigned int getBeam1Bunches(const EventContext& ctx) const override;
+  virtual unsigned int getBeam1Bunches(void) const override;
+
+  virtual unsigned int getBeam2Bunches(const EventContext& ctx) const override;
+  virtual unsigned int getBeam2Bunches(void) const override;
+
+  virtual unsigned int getCollidingBunches(const EventContext& ctx) const override;
+  virtual unsigned int getCollidingBunches(void) const override;
+
+  // This returns a char for each BCID encoding beam1/beam2
+  // A colliding BCID will have value 3
+  // BCIDs always count starting at 1
+  virtual std::vector<unsigned char> getBCIDMasks(const EventContext& ctx) const override;
+  virtual std::vector<unsigned char> getBCIDMasks(void) const override;
+
+ private:
+  // Read Cond Handles
+  //SG::ReadCondHandleKey<AthenaAttributeList> m_fillDataKey{this, "FillDataKey", "/LHC/FillData", "Key of fill data folder"};
+  SG::ReadCondHandleKey<AthenaAttributeList> m_beamDataKey{this, "BeamDataKey", "/LHC/BeamData", "Key of fill data folder"};
+  SG::ReadCondHandleKey<AthenaAttributeList> m_bcidDataKey{this, "BcidDataKey", "/LHC/BCIDData", "Key of fill data folder"};
+
+  ServiceHandle<ICondSvc> m_condSvc{this, "CondSvc", "CondSvc"};
+
+};
+
+//---------------------------------------------------------------------- 
+#endif // LHCDATA_TOOL
diff --git a/LHCData/LHCDataTools/src/components/LHCDataTools_entries.cxx b/LHCData/LHCDataTools/src/components/LHCDataTools_entries.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..1f44bf0909b4a2be2b03f8e276baab0c5ed58cf9
--- /dev/null
+++ b/LHCData/LHCDataTools/src/components/LHCDataTools_entries.cxx
@@ -0,0 +1,3 @@
+#include "../LHCDataTool.h"
+
+DECLARE_COMPONENT( LHCDataTool )
diff --git a/MagneticField/FaserBFieldData/share/FieldSimulation.py b/MagneticField/FaserBFieldData/share/FieldSimulation.py
new file mode 100644
index 0000000000000000000000000000000000000000..e17f617bc727f42de17c5aada7addde94761ac7f
--- /dev/null
+++ b/MagneticField/FaserBFieldData/share/FieldSimulation.py
@@ -0,0 +1,254 @@
+# Calculation of the magnetic field from first principles
+# @author Dave Casper, Tobias Boeckh
+# see also https://github.com/dwcasper/KalmanFilter/blob/master/Field/BField/__init__.py
+
+
+from math import sin, cos, atan2, sqrt, pi
+from auto_diff import true_np as np
+from scipy.special import iv, kn
+from scipy.integrate import quad
+
+junk = np.seterr()
+
+# Geometry description
+rMin = 0.104
+rMax = rMin + 0.0825
+zMax = [1.5, 1.0, 1.0]
+magnetZ = [-0.81232, 0.637726, 1.837726]
+m0 = 1.00  # mag
+sectorPhi = np.linspace(0, 2 * pi, 17, True)  # central phi value for each sector
+magPhi = 2 * sectorPhi  # angle of the magnetization in each sector
+phiConst = 4 * sqrt(2 - sqrt(2))  # numerical result of phiPrime integration on curved surface
+rhoLimit = rMin / 100  # region to use linear approx for rho
+proj = np.zeros(17)
+magVector = {}
+
+for j in range(0, 16, True):
+    magLo = m0 * np.array([cos(magPhi[j]), sin(magPhi[j]), 0])
+    magVector[j] = magLo
+    magHi = m0 * np.array([cos(magPhi[j + 1]), sin(magPhi[j + 1]), 0])
+    phiHat = np.array([-sin(j * pi / 8 + pi / 16), cos(j * pi / 8 + pi / 16), 0])
+    proj[j] = np.matmul((magLo - magHi), phiHat)
+
+
+def Field(position):
+    # expects position in millimeters
+    return bFieldXYZ(position[0] / 1000, position[1] / 1000, position[2] / 1000)
+
+
+# negative derivative (with respect to rho) of integrand over curved surface
+def cylDrho(rho, phi, z, rhoPrime, L, k):
+    if rho < rhoPrime:
+        bessel_k = kn(1, k * rhoPrime)
+        if bessel_k == 0:
+            return 0
+        return (
+            -m0 * (phiConst / pi**2) * rhoPrime * (iv(0, k * rho) + iv(2, k * rho))
+            * bessel_k * cos(k * z) * cos(phi) * sin(k * L / 2)
+        )
+
+    else:
+        bessel_k = kn(0, k * rho) + kn(2, k * rho)
+        if bessel_k == 0:
+            return 0
+        return (
+            -m0 * (phiConst / pi**2) * rhoPrime * iv(1, k * rhoPrime)
+            * bessel_k * cos(k * z) * cos(phi) * sin(k * L / 2)
+        )
+
+
+# negative derivative (with respect to phi) of integrand over curved surface, divided by rho
+def cylDphi(rho, phi, z, rhoPrime, L, k):
+    if rho < rhoLimit:
+        bessel_k = kn(1, k * rhoPrime)
+        if bessel_k == 0:
+            return 0
+        return (
+            m0 * (phiConst / pi**2) * rhoPrime * bessel_k * cos(k * z)
+            * sin(k * L / 2) * sin(phi)
+        )
+    elif rho < rhoPrime:
+        bessel_k = kn(1, k * rhoPrime)
+        if bessel_k == 0:
+            return 0
+        return (
+            m0 * (2 * phiConst / (k * pi**2)) * rhoPrime * iv(1, k * rho)
+            * bessel_k * cos(k * z) * sin(k * L / 2) * sin(phi) / rho
+        )
+    else:
+        bessel_k = kn(1, k * rho)
+        if bessel_k == 0:
+            return 0
+        return (
+            m0 * (2 * phiConst / (k * pi**2)) * rhoPrime * iv(1, k * rhoPrime)
+            * bessel_k * cos(k * z) * sin(k * L / 2) * sin(phi) / rho
+        )
+
+
+# negative derivative (with respect to z) of integrand over curved surface; valid for rho < rhoPrime
+def cylDz(rho, phi, z, rhoPrime, L, k):
+    if rho < rhoPrime:
+        bessel_k = kn(1, k * rhoPrime)
+        if bessel_k == 0:
+            return 0
+        return (
+            -m0 * (2 * phiConst / pi**2) * rhoPrime * iv(1, k * rho)
+            * bessel_k * sin(k * z) * sin(k * L / 2) * cos(phi)
+        )
+    else:
+        bessel_k = kn(1, k * rho)
+        if bessel_k == 0:
+            return 0
+        return (
+            m0 * (2 * phiConst / pi**2) * rhoPrime * iv(1, k * rhoPrime)
+            * bessel_k * sin(k * z) * sin(k * L / 2) * cos(phi)
+        )
+
+
+# negative derivative (with respect to rho) of integrand over sector boundary surface
+def planeDrho(rho, phi, z, j, L, rhoPrime):
+    phiPrime = j * pi / 8 + pi / 16
+    aSq = rho**2 + rhoPrime**2 - 2 * rho * rhoPrime * cos(phi - phiPrime)
+    aSqMinus = aSq + (z - L / 2) ** 2
+    aSqPlus = aSq + (z + L / 2) ** 2
+    return (
+        (proj[j] / (4 * pi)) * (rho - rhoPrime * cos(phi - phiPrime))
+        * (
+            1 / ((sqrt(aSqPlus) + z + L / 2) * sqrt(aSqPlus))
+            - 1 / ((sqrt(aSqMinus) + z - L / 2) * sqrt(aSqMinus))
+        )
+    )
+
+
+# negative derivative (with respect to phi) of integrand over sector boundary surface, divided by rho
+def planeDphi(rho, phi, z, j, L, rhoPrime):
+    phiPrime = j * pi / 8 + pi / 16
+    aSq = rho**2 + rhoPrime**2 - 2 * rho * rhoPrime * cos(phi - phiPrime)
+    aSqMinus = aSq + (z - L / 2) ** 2
+    aSqPlus = aSq + (z + L / 2) ** 2
+    return (
+        (proj[j] / (4 * pi)) * rhoPrime
+        * (
+            1 / ((sqrt(aSqPlus) + z + L / 2) * sqrt(aSqPlus))
+            - 1 / ((sqrt(aSqMinus) + z - L / 2) * sqrt(aSqMinus))
+        )
+        * sin(phi - phiPrime)
+    )
+
+
+# negative derivative (with respect to z) of integrand over sector boundary surface, divided by rho
+def planeDz(rho, phi, z, j, L, rhoPrime):
+    phiPrime = j * pi / 8 + pi / 16
+    aSq = rho**2 + rhoPrime**2 - 2 * rho * rhoPrime * cos(phi - phiPrime)
+    aSqMinus = aSq + (z - L / 2) ** 2
+    aSqPlus = aSq + (z + L / 2) ** 2
+    return (proj[j] / (4 * pi)) * (1 / sqrt(aSqPlus) - 1 / sqrt(aSqMinus))
+
+
+def field(rho, phi, z, L):
+    fieldCylRho = lambda k: -cylDrho(rho, phi, z, rMin, L, k) + cylDrho(
+        rho, phi, z, rMax, L, k
+    )
+    fieldPlaneRho = lambda rhoPrime: sum(
+        list(map(lambda j: planeDrho(rho, phi, z, j, L, rhoPrime), range(16)))
+    )
+
+    fieldCylPhi = lambda k: -cylDphi(rho, phi, z, rMin, L, k) + cylDphi(
+        rho, phi, z, rMax, L, k
+    )
+    fieldPlanePhi = lambda rhoPrime: sum(
+        list(map(lambda j: planeDphi(rho, phi, z, j, L, rhoPrime), range(16)))
+    )
+
+    fieldCylZ = lambda k: -cylDz(rho, phi, z, rMin, L, k) + cylDz(
+        rho, phi, z, rMax, L, k
+    )
+    fieldPlaneZ = lambda rhoPrime: sum(
+        list(map(lambda j: planeDz(rho, phi, z, j, L, rhoPrime), range(16)))
+    )
+    myLimit = 1000
+    return np.array(
+        [
+            -quad(fieldCylRho, 0, np.inf, limit=myLimit)[0]
+            + quad(fieldPlaneRho, rMin, rMax, limit=myLimit)[0],
+            -quad(fieldCylPhi, 0, np.inf, limit=myLimit)[0]
+            + quad(fieldPlanePhi, rMin, rMax, limit=myLimit)[0],
+            quad(fieldCylZ, 0, np.inf, limit=myLimit)[0]
+            + quad(fieldPlaneZ, rMin, rMax, limit=myLimit)[0],
+        ]
+    )
+
+
+def mField(rho, phi, z):
+    magnetization = np.zeros(3)
+    if phi < 0:
+        phi = phi + 2 * pi
+    for iz in range(3):
+        if (
+            z < (magnetZ[iz] - zMax[iz] / 2)
+            or z > (magnetZ[iz] + zMax[iz] / 2)
+            or rho < rMin
+            or rho > rMax
+        ):
+            continue
+        j = (phi + pi / 16) // (pi / 8)
+        j = j % 16
+        magnetization = magVector[j]  # this is cartesian!
+        break
+    rhoUnit = np.array([cos(phi), sin(phi), 0])
+    phiUnit = np.array([-sin(phi), cos(phi), 0])
+    zUnit = np.array([0, 0, 1])
+    return np.array(
+        [
+            np.matmul(magnetization, rhoUnit),
+            np.matmul(magnetization, phiUnit),
+            np.matmul(magnetization, zUnit),
+        ]
+    )
+
+
+def mFieldXYZ(x, y, z):
+    rho = sqrt(x**2 + y**2)
+    if rho > 0:
+        phi = atan2(y, x)
+    else:
+        phi = 0
+
+    m = mField(rho, phi, z)
+    return np.array(
+        [m[0] * cos(phi) - m[1] * sin(phi), m[0] * sin(phi) + m[1] * cos(phi), m[2]]
+    )
+
+
+def bField(rho, phi, z):
+    return (
+        hField(rho, phi, z - magnetZ[0], zMax[0])
+        + hField(rho, phi, z - magnetZ[1], zMax[1])
+        + hField(rho, phi, z - magnetZ[2], zMax[2])
+        + mField(rho, phi, z)
+    )
+
+
+def bFieldXYZ(x, y, z):
+    return hFieldXYZ(x, y, z) + mFieldXYZ(x, y, z)
+
+
+def hField(rho, phi, z):
+    return (
+        field(rho, phi, z - magnetZ[0], zMax[0])
+        + field(rho, phi, z - magnetZ[1], zMax[1])
+        + field(rho, phi, z - magnetZ[2], zMax[2])
+    )
+
+
+def hFieldXYZ(x, y, z):
+    rho = sqrt(x**2 + y**2)
+    if rho > 0:
+        phi = atan2(y, x)
+    else:
+        phi = 0
+
+    h = hField(rho, phi, z)
+    return np.array(
+        [h[0] * cos(phi) - h[1] * sin(phi), h[0] * sin(phi) + h[1] * cos(phi), h[2]]
+    )
diff --git a/PhysicsAnalysis/NeutrinoSearch/CMakeLists.txt b/PhysicsAnalysis/NeutrinoSearch/CMakeLists.txt
index cff2ab8575553261c18eb0736c211528e44f22bb..29ea0c53b37e81e5cf4b4ec63e958ac0a344f299 100644
--- a/PhysicsAnalysis/NeutrinoSearch/CMakeLists.txt
+++ b/PhysicsAnalysis/NeutrinoSearch/CMakeLists.txt
@@ -5,7 +5,7 @@ atlas_add_component(
         src/NeutrinoSearchAlg.h
         src/NeutrinoSearchAlg.cxx
         src/component/NeutrinoSearch_entries.cxx
-        LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform ScintIdentifier FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack
+        LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform ScintIdentifier ScintSimEvent FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack
 )
 
 atlas_install_python_modules(python/*.py)
diff --git a/PhysicsAnalysis/NeutrinoSearch/python/FilterSearchConfig.py b/PhysicsAnalysis/NeutrinoSearch/python/FilterSearchConfig.py
index 9b76c14b8b6682b6cfe77ed3331467d4f9737cfe..1bea526ebb4ba14c750d89a652ee6fbe166db355 100644
--- a/PhysicsAnalysis/NeutrinoSearch/python/FilterSearchConfig.py
+++ b/PhysicsAnalysis/NeutrinoSearch/python/FilterSearchConfig.py
@@ -482,7 +482,7 @@ if __name__ == "__main__":
 
 
     ]
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersionS
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # 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 = False                                # Needed to bypass autoconfig
diff --git a/PhysicsAnalysis/NeutrinoSearch/python/FlukaSearchConfig.py b/PhysicsAnalysis/NeutrinoSearch/python/FlukaSearchConfig.py
index 4e196a3794670d241bf8a26b91d409d260ac193d..4e81f6bcf7ba871622430d5ae246045f9fc043f6 100644
--- a/PhysicsAnalysis/NeutrinoSearch/python/FlukaSearchConfig.py
+++ b/PhysicsAnalysis/NeutrinoSearch/python/FlukaSearchConfig.py
@@ -47,51 +47,52 @@ if __name__ == "__main__":
 
     # Configure
     ConfigFlags.Input.Files = [
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00000-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00001-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00002-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00003-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00004-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00005-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00006-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00007-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00008-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00009-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00010-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00011-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00012-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00013-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00014-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00015-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00016-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00017-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00018-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00019-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00020-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00021-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00022-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00023-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00024-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00025-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00026-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0008/FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00027-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00000-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00001-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00002-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00003-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00004-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00005-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00006-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00007-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00008-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00009-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00010-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00011-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00012-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00013-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00014-s0005-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0008/FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00015-s0005-r0008-xAOD.root'
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00011-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00003-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00001-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00015-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00010-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00014-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00009-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00000-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00008-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00012-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00002-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00004-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00013-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00007-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00005-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210002/rec/r0009/./FaserMC-MDC_Fluka_unit30_Pm_71m_m3750_v3-210002-00006-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00017-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00020-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00010-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00012-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00001-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00018-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00004-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00019-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00007-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00003-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00008-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00005-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00022-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00011-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00006-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00013-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00021-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00015-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00026-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00027-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00014-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00024-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00000-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00009-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00025-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00016-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00023-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/fluka/210001/rec/r0009/./FaserMC-MDC_Fluka_unit30_Nm_71m_m3750_v3-210001-00002-s0007-r0009-xAOD.root'
     ]
+    # Update this for samples with new field map
     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
diff --git a/PhysicsAnalysis/NeutrinoSearch/python/GenieSearchConfig.py b/PhysicsAnalysis/NeutrinoSearch/python/GenieSearchConfig.py
index a80de32bbc0643dbaca1ac12e28688535951b709..94d860f75196b82b1c5ca3cdd6b3c84e1e06f171 100644
--- a/PhysicsAnalysis/NeutrinoSearch/python/GenieSearchConfig.py
+++ b/PhysicsAnalysis/NeutrinoSearch/python/GenieSearchConfig.py
@@ -47,14 +47,27 @@ if __name__ == "__main__":
 
     # Configure
     ConfigFlags.Input.Files = [
-        '/run/media/dcasper/Data/faser/genie/rec/FaserMC-MDC_Genie_all_150invfb_v1-200001-00000-00007-s0006-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/genie/rec/FaserMC-MDC_Genie_all_150invfb_v1-200001-00008-00015-s0006-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/genie/rec/FaserMC-MDC_Genie_all_150invfb_v1-200001-00016-00023-s0006-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/genie/rec/FaserMC-MDC_Genie_all_150invfb_v1-200001-00024-00031-s0006-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/genie/rec/FaserMC-MDC_Genie_all_150invfb_v1-200001-00032-00039-s0006-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/genie/rec/FaserMC-MDC_Genie_all_150invfb_v1-200001-00040-00047-s0006-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/genie/rec/FaserMC-MDC_Genie_all_150invfb_v1-200001-00048-00055-s0006-r0008-xAOD.root',
-        '/run/media/dcasper/Data/faser/genie/rec/FaserMC-MDC_Genie_all_150invfb_v1-200001-00056-00063-s0006-r0008-xAOD.root'
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00040-00047-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00056-00063-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00120-00127-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00112-00119-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00000-00007-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00208-00210-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00200-00207-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00064-00071-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00080-00087-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00024-00031-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00072-00079-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00008-00015-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00176-00183-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00096-00103-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00104-00111-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00032-00039-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00048-00055-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00192-00199-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00088-00095-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00016-00023-s0007-r0009-xAOD.root',
+        '/run/media/dcasper/Data/faser/genie/r0009/rec/./FaserMC-MDC_Genie_all_600fbInv_v1-200003-00168-00175-s0007-r0009-xAOD.root'
     ]
     ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersionS
     ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"               # Use MC conditions for now
diff --git a/PhysicsAnalysis/NeutrinoSearch/src/NeutrinoSearchAlg.cxx b/PhysicsAnalysis/NeutrinoSearch/src/NeutrinoSearchAlg.cxx
index e782dda9e3eb7d97da42edb1b5a79528268dac9f..e3d33d683e0b47d064216515cf83fb280cb9edd4 100644
--- a/PhysicsAnalysis/NeutrinoSearch/src/NeutrinoSearchAlg.cxx
+++ b/PhysicsAnalysis/NeutrinoSearch/src/NeutrinoSearchAlg.cxx
@@ -28,6 +28,8 @@ StatusCode NeutrinoSearchAlg::initialize()
 {
   ATH_CHECK(m_truthEventContainer.initialize());
   ATH_CHECK(m_truthParticleContainer.initialize());
+  ATH_CHECK( m_vetoNuHitKey.initialize() );
+
   ATH_CHECK(m_trackCollection.initialize());
   ATH_CHECK(m_vetoNuContainer.initialize());
   ATH_CHECK(m_vetoContainer.initialize());
@@ -46,6 +48,7 @@ StatusCode NeutrinoSearchAlg::initialize()
 
   ATH_CHECK(detStore()->retrieve(m_detMgr, "SCT"));
   ATH_CHECK(m_extrapolationTool.retrieve());
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
 
   if (m_useFlukaWeights)
   {
@@ -64,6 +67,9 @@ StatusCode NeutrinoSearchAlg::initialize()
   m_tree->Branch("run_number", &m_run_number, "run_number/I");
   m_tree->Branch("event_number", &m_event_number, "event_number/I");
 
+//   m_tree->Branch("VetoNuTruthX", &m_vetoNuHitsMeanX, "vetoNuTruthX/D");
+//   m_tree->Branch("VetoNuTruthY", &m_vetoNuHitsMeanY, "vetoNuTruthY/D");
+
   m_tree->Branch("VetoNuPmt0", &m_vetoNu0, "vetoNu0/D");
   m_tree->Branch("VetoNuPmt1", &m_vetoNu1, "vetoNu1/D");
 
@@ -106,9 +112,17 @@ StatusCode NeutrinoSearchAlg::initialize()
   m_tree->Branch("ndof", &m_ndof, "ndof/I");
   m_tree->Branch("longTracks", &m_longTracks, "longTracks/I");
   m_tree->Branch("pTruthLepton", &m_truthLeptonMomentum, "pTruthLepton/D");
+  m_tree->Branch("xTruthLepton", &m_truthLeptonX, "xTruthLepton/D");
+  m_tree->Branch("yTruthLepton", &m_truthLeptonY, "yTruthLepton/D");
   m_tree->Branch("truthBarcode", &m_truthBarcode, "truthBarcode/I");
   m_tree->Branch("truthPdg", &m_truthPdg, "truthPdg/I");
   m_tree->Branch("CrossSection", &m_crossSection, "crossSection/D");
+  m_tree->Branch("xVetoNu", &m_xVetoNu, "xVetoNu/D");
+  m_tree->Branch("yVetoNu", &m_yVetoNu, "yVetoNu/D");
+  m_tree->Branch("sxVetoNu", &m_sxVetoNu, "sxVetoNu/D");
+  m_tree->Branch("syVetoNu", &m_syVetoNu, "syVetoNu/D");
+  m_tree->Branch("thetaxVetoNu", &m_thetaxVetoNu, "thetaxVetoNu/D");
+  m_tree->Branch("thetayVetoNu", &m_thetayVetoNu, "thetayVetoNu/D");
 
   ATH_CHECK(histSvc()->regTree("/HIST2/tree", m_tree));
 
@@ -145,6 +159,23 @@ StatusCode NeutrinoSearchAlg::execute(const EventContext &ctx) const
         double flukaWeight = truthEventContainer->at(0)->weights()[0];
         ATH_MSG_ALWAYS("Found fluka weight = " << flukaWeight);
         m_crossSection = m_baseEventCrossSection * flukaWeight;
+
+        // // Find crossing position of VetoNu so we can check extrapolation
+        // SG::ReadHandle<ScintHitCollection> h_vetoNuHits {m_vetoNuHitKey, ctx};
+        // if (h_vetoNuHits.isValid() && h_vetoNuHits->size()!=0){
+        //     double totalEnergyLoss {0.0};
+        //     for (const ScintHit& hit : *h_vetoNuHits)
+        //     {
+        //         m_vetoNuHitsMeanX += hit.energyLoss() * (hit.localStartPosition().x() + hit.localEndPosition().x()) / 2;
+        //         m_vetoNuHitsMeanY += hit.energyLoss() * (hit.localStartPosition().y() + hit.localEndPosition().y()) / 2;
+        //         totalEnergyLoss += hit.energyLoss();
+        //     }
+        //     if (totalEnergyLoss > 0)
+        //     {
+        //         m_vetoNuHitsMeanX /= totalEnergyLoss;
+        //         m_vetoNuHitsMeanY /= totalEnergyLoss;                
+        //     }
+        // }
     }
     else if (m_useGenieWeights)
     {
@@ -166,8 +197,14 @@ StatusCode NeutrinoSearchAlg::execute(const EventContext &ctx) const
         if ( particle->absPdgId() == 11 || particle->absPdgId() == 13 || particle->absPdgId() == 15 )
         {
             if (particle->status() == 1 && (particle->nParents() == 0 || particle->nParents() == 2) )
+            {
                 m_truthLeptonMomentum = particle->p4().P();
-
+                double deltaZ = m_zExtrapolate - particle->prodVtx()->z();
+                double thetaX = particle->px()/particle->pz();
+                double thetaY = particle->py()/particle->pz();
+                m_truthLeptonX = particle->prodVtx()->x() + thetaX * deltaZ;
+                m_truthLeptonY = particle->prodVtx()->y() + thetaY * deltaZ;
+            }
             break;
         }
     }
@@ -435,6 +472,7 @@ StatusCode NeutrinoSearchAlg::execute(const EventContext &ctx) const
     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;
     }
     if (candidateParameters == nullptr || upstreamParameters->momentum().mag() > candidateParameters->momentum().mag())
@@ -457,98 +495,98 @@ StatusCode NeutrinoSearchAlg::execute(const EventContext &ctx) const
         if (cluster != nullptr)
         {
             // ATH_MSG_INFO("ClusterOnTrack is OK");
-            cluster->dump(msg());
+            // cluster->dump(msg());
 
 // Hack to work around issue with cluster navigation
 
-            auto idRDO = cluster->identify();
+            // auto idRDO = cluster->identify();
+
+            // if (simDataCollection->count(idRDO) > 0)
+            // {
+            //     // ATH_MSG_INFO("rdo entry found");
+            //     const auto& simdata = simDataCollection->find(idRDO)->second;
+            //     const auto& deposits = simdata.getdeposits();
+            //     //loop through deposits and record contributions
+            //     HepMcParticleLink primary{};
+            //     for( const auto& depositPair : deposits)
+            //     {
+            //         // ATH_MSG_INFO("Deposit found");
+            //         float eDep = depositPair.second;
+            //         int barcode = depositPair.first->barcode();
+            //         // if( depositPair.second > highestDep)
+            //         // {
+            //         //     highestDep = depositPair.second;
+            //         //     barcode = depositPair.first->barcode();
+            //         //     primary = depositPair.first;
+            //         //     depositPair.first->print(std::cout);
+            //         //     ATH_MSG_INFO("pdg id "<<depositPair.first->pdg_id());
+            //         // }
+            //         if (truthMap.count(barcode) > 0)
+            //         {
+            //             truthMap[barcode] += eDep;
+            //         }
+            //         else
+            //         {
+            //             truthMap[barcode] = eDep;
+            //         }
+            //     }
+            // }
+                    
+                
+
 
-            if (simDataCollection->count(idRDO) > 0)
-            {
-                // ATH_MSG_INFO("rdo entry found");
-                const auto& simdata = simDataCollection->find(idRDO)->second;
-                const auto& deposits = simdata.getdeposits();
-                //loop through deposits and record contributions
-                HepMcParticleLink primary{};
-                for( const auto& depositPair : deposits)
+                // const Tracker::FaserSCT_Cluster* origCluster = dynamic_cast<const Tracker::FaserSCT_Cluster*>(cluster->prepRawData());
+                auto origCluster = cluster->prepRawData();
+                if (origCluster != nullptr)
                 {
-                    // ATH_MSG_INFO("Deposit found");
-                    float eDep = depositPair.second;
-                    int barcode = depositPair.first->barcode();
-                    // if( depositPair.second > highestDep)
-                    // {
-                    //     highestDep = depositPair.second;
-                    //     barcode = depositPair.first->barcode();
-                    //     primary = depositPair.first;
-                    //     depositPair.first->print(std::cout);
-                    //     ATH_MSG_INFO("pdg id "<<depositPair.first->pdg_id());
-                    // }
-                    if (truthMap.count(barcode) > 0)
+                    // ATH_MSG_INFO("Orig Cluster is OK");
+                    auto rdoList = origCluster->rdoList();
+                    for (auto idRDO : rdoList)
                     {
-                        truthMap[barcode] += eDep;
-                    }
-                    else
-                    {
-                        truthMap[barcode] = eDep;
+                        // ATH_MSG_INFO("rdoList not empty");
+                        if (simDataCollection->count(idRDO) > 0)
+                        {
+                            // ATH_MSG_INFO("rdo entry found");
+                            const auto& simdata = simDataCollection->find(idRDO)->second;
+                            const auto& deposits = simdata.getdeposits();
+                            //loop through deposits and record contributions
+                            HepMcParticleLink primary{};
+                            for( const auto& depositPair : deposits)
+                            {
+                                // ATH_MSG_INFO("Deposit found");
+                                float eDep = depositPair.second;
+                                int barcode = depositPair.first->barcode();
+                                // if( depositPair.second > highestDep)
+                                // {
+                                //     highestDep = depositPair.second;
+                                //     barcode = depositPair.first->barcode();
+                                //     primary = depositPair.first;
+                                //     depositPair.first->print(std::cout);
+                                //     ATH_MSG_INFO("pdg id "<<depositPair.first->pdg_id());
+                                // }
+                                if (truthMap.count(barcode) > 0)
+                                {
+                                    truthMap[barcode] += eDep;
+                                }
+                                else
+                                {
+                                    truthMap[barcode] = eDep;
+                                }
+                            }
+                        }
                     }
                 }
-            }
-                    
-                
-
-
-                // // const Tracker::FaserSCT_Cluster* origCluster = dynamic_cast<const Tracker::FaserSCT_Cluster*>(cluster->prepRawData());
-                // auto origCluster = cluster->prepRawData();
-                // if (origCluster != nullptr)
-                // {
-                //     ATH_MSG_INFO("Orig Cluster is OK");
-                //     auto rdoList = origCluster->rdoList();
-                //     for (auto idRDO : rdoList)
-                //     {
-                //         ATH_MSG_INFO("rdoList not empty");
-                //         if (simDataCollection->count(idRDO) > 0)
-                //         {
-                //             ATH_MSG_INFO("rdo entry found");
-                //             const auto& simdata = simDataCollection->find(idRDO)->second;
-                //             const auto& deposits = simdata.getdeposits();
-                //             //loop through deposits and record contributions
-                //             HepMcParticleLink primary{};
-                //             for( const auto& depositPair : deposits)
-                //             {
-                //                 ATH_MSG_INFO("Deposit found");
-                //                 float eDep = depositPair.second;
-                //                 int barcode = depositPair.first->barcode();
-                //                 // if( depositPair.second > highestDep)
-                //                 // {
-                //                 //     highestDep = depositPair.second;
-                //                 //     barcode = depositPair.first->barcode();
-                //                 //     primary = depositPair.first;
-                //                 //     depositPair.first->print(std::cout);
-                //                 //     ATH_MSG_INFO("pdg id "<<depositPair.first->pdg_id());
-                //                 // }
-                //                 if (truthMap.count(barcode) > 0)
-                //                 {
-                //                     truthMap[barcode] += eDep;
-                //                 }
-                //                 else
-                //                 {
-                //                     truthMap[barcode] = eDep;
-                //                 }
-                //             }
-                //         }
-                //     }
-                // }
         }
     }
     std::vector<std::pair<int, float>> truth(truthMap.begin(), truthMap.end());
     std::sort(truth.begin(), truth.end(), [](auto v1, auto v2) { return v1.second > v2.second; });
-    if (truth.size()>0) ATH_MSG_ALWAYS("Selected track truth info:");
+    // if (truth.size()>0) ATH_MSG_ALWAYS("Selected track truth info:");
     for (auto v : truth)
     {
         auto truthParticle = (*(std::find_if(truthParticleContainer->cbegin(), truthParticleContainer->cend(), [v](const xAOD::TruthParticle* p){ return p->barcode() == v.first; })));
         if (m_truthPdg == 0) m_truthPdg = truthParticle->pdgId();
         if (m_truthBarcode == 0) m_truthBarcode = v.first;
-        ATH_MSG_ALWAYS("truth info: barcode = " << v.first << " ( " << truthParticle->p4().P()/1000 << " GeV/c, Id code = " << truthParticle->pdgId() << ") -> deposited energy: " << v.second/1000);
+        // ATH_MSG_ALWAYS("truth info: barcode = " << v.first << " ( " << truthParticle->p4().P()/1000 << " GeV/c, Id code = " << truthParticle->pdgId() << ") -> deposited energy: " << v.second/1000);
     }
   }
 
@@ -562,6 +600,63 @@ StatusCode NeutrinoSearchAlg::execute(const EventContext &ctx) const
     m_pz = candidateParameters->momentum().z();
     m_p = sqrt(m_px * m_px + m_py * m_py + m_pz * m_pz);
     m_charge = (int) candidateParameters->charge();
+
+    FaserActsGeometryContext faserGeometryContext = m_trackingGeometryTool->getNominalGeometryContext();
+    auto gctx = faserGeometryContext.context();
+
+
+    Amg::Vector3D position = candidateParameters->position();
+    Amg::Vector3D momentum = candidateParameters->momentum();
+    auto covariance = *candidateParameters->covariance();
+    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] = candidateParameters->charge() / momentum.mag();
+    params[Acts::eBoundTime] = 0;
+
+    using namespace Acts::UnitLiterals;
+    std::optional<AmgSymMatrix(6)> cov = std::nullopt;
+    Acts::BoundMatrix newCov = Acts::BoundMatrix::Zero();
+    for (size_t i = 0; i < 5; i++)
+        for (size_t j = 0; j < 5; j++)
+            newCov(i, j) = covariance(i, j);
+    // ATH_MSG_ALWAYS("Covariance: " << covariance(1,1) << " newCov: " << newCov(1,1));
+    // Convert the covariance matrix from GeV
+    for(int i=0; i < newCov.rows(); i++){
+      newCov(i, 4) = newCov(i, 4)/1_MeV;
+    }
+    for(int i=0; i < newCov.cols(); i++){
+      newCov(4, i) = newCov(4, i)/1_MeV;
+    }
+    cov =  std::optional<AmgSymMatrix(6)>(newCov);
+    // ATH_MSG_ALWAYS("cov: " << cov.value()(1,1));
+    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, candidateParameters->charge(), cov);
+    auto targetSurface_VetoNu = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3(0, 0, m_zExtrapolate), 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();
+        // Acts::BoundSymMatrix targetCovariance_VetoNu { targetParameters_VetoNu->covariance() };
+        m_xVetoNu = targetPosition_VetoNu.x();
+        m_yVetoNu = targetPosition_VetoNu.y();
+        if (targetParameters_VetoNu->covariance().has_value())
+        {
+            auto targetCovariance_VetoNu = targetParameters_VetoNu->covariance().value() ;
+            m_sxVetoNu = sqrt(targetCovariance_VetoNu(Acts::eBoundLoc0, Acts::eBoundLoc0));
+            m_syVetoNu = sqrt(targetCovariance_VetoNu(Acts::eBoundLoc1, Acts::eBoundLoc1));
+        }
+        m_thetaxVetoNu = atan(targetMomentum_VetoNu[0]/targetMomentum_VetoNu[2]);
+        m_thetayVetoNu = atan(targetMomentum_VetoNu[1]/targetMomentum_VetoNu[2]);
+        ATH_MSG_INFO("vetoNu good targetParameters xV,yV = (" << m_xVetoNu << ", " << m_yVetoNu << ")");
+    } 
+    else 
+    {
+        ATH_MSG_INFO("vetoNu null targetParameters with p = " << momentum.mag() << ", theta = " << momentum.theta() << ", x,y = (" << position.x() << ", " << position.y() << ")");
+    }
   }
 
   // Here we apply the signal selection
@@ -571,7 +666,18 @@ StatusCode NeutrinoSearchAlg::execute(const EventContext &ctx) const
         m_preshower0 == 0 || m_preshower1 == 0 ||
         // m_ecalTotal == 0 ||
         candidateParameters == nullptr)
-      return StatusCode::SUCCESS;
+    {
+        // if (m_longTracks > 0)
+        // {
+        //     ATH_MSG_ALWAYS(m_vetoUpstream);
+        //     ATH_MSG_ALWAYS(m_vetoDownstream);
+        //     ATH_MSG_ALWAYS(m_triggerTotal);
+        //     ATH_MSG_ALWAYS(m_preshower0);
+        //     ATH_MSG_ALWAYS(m_preshower1);
+        //     ATH_MSG_ALWAYS((candidateParameters == nullptr ? "Null" : "Not Null"));
+        // }
+        return StatusCode::SUCCESS;
+    }
 
   m_tree->Fill();
 
@@ -595,6 +701,8 @@ NeutrinoSearchAlg::clearTree() const
 {
   m_run_number = 0;
   m_event_number = 0;
+//   m_vetoNuHitsMeanX = 0;
+//   m_vetoNuHitsMeanY = 0;
   m_vetoNu0 = 0;
   m_vetoNu1 = 0;
   m_veto00 = 0;
@@ -631,7 +739,15 @@ NeutrinoSearchAlg::clearTree() const
   m_y = 0;
   m_z = 0;
   m_longTracks = 0;
+  m_truthLeptonX = 0;
+  m_truthLeptonY = 0;
   m_truthLeptonMomentum = 0;
   m_truthBarcode = 0;
   m_truthPdg = 0;
+  m_xVetoNu = 0;
+  m_yVetoNu = 0;
+  m_sxVetoNu = 0;
+  m_syVetoNu = 0;
+  m_thetaxVetoNu = 0;
+  m_thetayVetoNu = 0;
 }
\ No newline at end of file
diff --git a/PhysicsAnalysis/NeutrinoSearch/src/NeutrinoSearchAlg.h b/PhysicsAnalysis/NeutrinoSearch/src/NeutrinoSearchAlg.h
index d1732eb1238a15bb7b2fb397869b3c4486aa50b3..4c8ca653e2b7832dac95645135a10c83c57a846a 100644
--- a/PhysicsAnalysis/NeutrinoSearch/src/NeutrinoSearchAlg.h
+++ b/PhysicsAnalysis/NeutrinoSearch/src/NeutrinoSearchAlg.h
@@ -8,10 +8,11 @@
 #include "xAODFaserWaveform/WaveformHit.h"
 #include "xAODTruth/TruthEventContainer.h"
 #include "xAODTruth/TruthParticleContainer.h"
+#include "ScintSimEvent/ScintHitCollection.h" 
 #include "TrackerPrepRawData/FaserSCT_ClusterContainer.h"
 #include "TrackerSimData/TrackerSimDataCollection.h"
 #include "FaserActsGeometryInterfaces/IFaserActsExtrapolationTool.h"
-
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
 
 class TTree;
 class FaserSCT_ID;
@@ -44,6 +45,7 @@ private:
   SG::ReadHandleKey<xAOD::TruthEventContainer> m_truthEventContainer { this, "EventContainer", "TruthEvents", "Truth event container name." };
   SG::ReadHandleKey<xAOD::TruthParticleContainer> m_truthParticleContainer { this, "ParticleContainer", "TruthParticles", "Truth particle container name." };
   SG::ReadHandleKey<TrackerSimDataCollection> m_simDataCollection {this, "TrackerSimDataCollection", "SCT_SDO_Map"};
+  SG::ReadHandleKey<ScintHitCollection> m_vetoNuHitKey { this, "VetoNuHitCollection", "VetoNuHits" };
 
   SG::ReadHandleKey<TrackCollection> m_trackCollection { this, "TrackCollection", "CKFTrackCollection", "Input track collection name" };
   SG::ReadHandleKey<xAOD::WaveformHitContainer> m_vetoNuContainer { this, "VetoNuContainer", "VetoNuWaveformHits", "VetoNu hit container name" };
@@ -54,6 +56,7 @@ private:
   SG::ReadHandleKey<Tracker::FaserSCT_ClusterContainer> m_clusterContainer { this, "ClusterContainer", "SCT_ClusterContainer", "Tracker cluster container name" };
 
   ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool { this, "ExtrapolationTool", "FaserActsExtrapolationTool" };  
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
   const TrackerDD::SCT_DetectorManager* m_detMgr {nullptr};
 
   const FaserSCT_ID* m_sctHelper;
@@ -75,7 +78,8 @@ private:
   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_genieLuminosity   { this, "GenieLuminosity", 600.0, "Genie luminosity in inverse fb." };
+  DoubleProperty  m_zExtrapolate      { this, "ZExtrapolationPlane", -3112.0, "Plane to extrapolate tracks for VetoNu" };
 
 //   BooleanProperty m_enforceBlinding   { this, "EnforceBlinding", true, "Ignore data events with no VetoNu signals." };
   const bool m_enforceBlinding {true};
@@ -111,6 +115,9 @@ private:
   mutable int m_station2Clusters;
   mutable int m_station3Clusters;
 
+  // mutable double m_vetoNuHitsMeanX;
+  // mutable double m_vetoNuHitsMeanY;
+
   mutable double m_x;
   mutable double m_y;
   mutable double m_z;
@@ -123,10 +130,17 @@ private:
   mutable int    m_ndof;
   mutable int    m_longTracks;
   mutable double m_truthLeptonMomentum;
+  mutable double m_truthLeptonX;
+  mutable double m_truthLeptonY;
   mutable int    m_truthBarcode;
   mutable int    m_truthPdg;
   mutable double m_crossSection;
-
+  mutable double m_xVetoNu;
+  mutable double m_yVetoNu;
+  mutable double m_sxVetoNu;
+  mutable double m_syVetoNu;
+  mutable double m_thetaxVetoNu;
+  mutable double m_thetayVetoNu;
 };
 
 inline const ServiceHandle <ITHistSvc> &NeutrinoSearchAlg::histSvc() const {
diff --git a/PhysicsAnalysis/NtupleDumper/CMakeLists.txt b/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
index 944ed9689053915dd526251fabf1c5b1af5dfa4e..b81b98183b68fc67cf0df4592c7f1cad6917e6f4 100644
--- a/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
+++ b/PhysicsAnalysis/NtupleDumper/CMakeLists.txt
@@ -5,7 +5,7 @@ atlas_add_component(
         src/NtupleDumperAlg.h
         src/NtupleDumperAlg.cxx
         src/component/NtupleDumper_entries.cxx
-        LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform xAODFaserTrigger ScintIdentifier FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack TrackerSpacePoint
+        LINK_LIBRARIES AthenaBaseComps StoreGateLib xAODFaserWaveform xAODFaserTrigger ScintIdentifier FaserCaloIdentifier GeneratorObjects FaserActsGeometryLib TrackerSimEvent TrackerSimData TrackerIdentifier TrackerReadoutGeometry TrkTrack GeoPrimitives TrackerRIO_OnTrack TrackerSpacePoint FaserActsKalmanFilterLib
 )
 
 atlas_install_python_modules(python/*.py)
diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
index d1a07f428c73dfb7d6c7f1bf69ab20b73f12a196..587a894b905e3e75e9e8c5cf708fc72b49c7c046 100644
--- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
+++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.cxx
@@ -16,6 +16,9 @@
 #include "xAODTruth/TruthParticle.h"
 #include <cmath>
 #include <TH1F.h>
+#include <numeric>
+
+constexpr float NaN = std::numeric_limits<double>::quiet_NaN();
 
 
 NtupleDumperAlg::NtupleDumperAlg(const std::string &name, 
@@ -70,7 +73,7 @@ void NtupleDumperAlg::FillWaveBranches(const xAOD::WaveformHitContainer &wave) c
   }
 }
 
-StatusCode NtupleDumperAlg::initialize() 
+StatusCode NtupleDumperAlg::initialize()
 {
   ATH_CHECK(m_truthEventContainer.initialize());
   ATH_CHECK(m_truthParticleContainer.initialize());
@@ -85,6 +88,7 @@ StatusCode NtupleDumperAlg::initialize()
   ATH_CHECK(m_simDataCollection.initialize());
   ATH_CHECK(m_FaserTriggerData.initialize());
   ATH_CHECK(m_ClockWaveformContainer.initialize());
+  ATH_CHECK(m_siHitCollectionKey.initialize());
 
   ATH_CHECK(detStore()->retrieve(m_sctHelper,       "FaserSCT_ID"));
   ATH_CHECK(detStore()->retrieve(m_vetoNuHelper,    "VetoNuID"));
@@ -96,6 +100,8 @@ StatusCode NtupleDumperAlg::initialize()
   ATH_CHECK(detStore()->retrieve(m_detMgr, "SCT"));
   ATH_CHECK(m_extrapolationTool.retrieve());
   ATH_CHECK(m_trackingGeometryTool.retrieve());
+  ATH_CHECK(m_trackTruthMatchingTool.retrieve());
+  ATH_CHECK(m_fiducialParticleTool.retrieve());
 
   ATH_CHECK(m_spacePointContainerKey.initialize());
 
@@ -220,6 +226,41 @@ StatusCode NtupleDumperAlg::initialize()
   m_tree->Branch("Track_ThetaX_atCalo", &m_thetaxCalo);
   m_tree->Branch("Track_ThetaY_atCalo", &m_thetayCalo);
 
+  m_tree->Branch("t_pdg", &m_t_pdg);
+  m_tree->Branch("t_barcode", &m_t_barcode);
+  m_tree->Branch("t_truthHitRatio", &m_t_truthHitRatio);
+  m_tree->Branch("t_prodVtx_x", &m_t_prodVtx_x);
+  m_tree->Branch("t_prodVtx_y", &m_t_prodVtx_y);
+  m_tree->Branch("t_prodVtx_z", &m_t_prodVtx_z);
+  m_tree->Branch("t_decayVtx_x", &m_t_decayVtx_x);
+  m_tree->Branch("t_decayVtx_y", &m_t_decayVtx_y);
+  m_tree->Branch("t_decayVtx_z", &m_t_decayVtx_z);
+  m_tree->Branch("t_px", &m_t_px);
+  m_tree->Branch("t_py", &m_t_py);
+  m_tree->Branch("t_pz", &m_t_pz);
+  m_tree->Branch("t_theta", &m_t_theta);
+  m_tree->Branch("t_phi", &m_t_phi);
+  m_tree->Branch("t_p", &m_t_p);
+  m_tree->Branch("t_pT", &m_t_pT);
+  m_tree->Branch("t_eta", &m_t_eta);
+  m_tree->Branch("t_st0_x", &m_t_st_x[0]);
+  m_tree->Branch("t_st0_y", &m_t_st_y[0]);
+  m_tree->Branch("t_st0_z", &m_t_st_z[0]);
+  m_tree->Branch("t_st1_x", &m_t_st_x[1]);
+  m_tree->Branch("t_st1_y", &m_t_st_y[1]);
+  m_tree->Branch("t_st1_z", &m_t_st_z[1]);
+  m_tree->Branch("t_st2_x", &m_t_st_x[2]);
+  m_tree->Branch("t_st2_y", &m_t_st_y[2]);
+  m_tree->Branch("t_st2_z", &m_t_st_z[2]);
+  m_tree->Branch("t_st3_x", &m_t_st_x[3]);
+  m_tree->Branch("t_st3_y", &m_t_st_y[3]);
+  m_tree->Branch("t_st3_z", &m_t_st_z[3]);
+  m_tree->Branch("isFiducial", &m_isFiducial);
+
+  m_tree->Branch("truthParticleBarcode", &m_truthParticleBarcode);
+  m_tree->Branch("truthParticleMatchedTracks", &m_truthParticleMatchedTracks);
+  m_tree->Branch("truthParticleIsFiducial", &m_truthParticleIsFiducial);
+
   m_tree->Branch("pTruthLepton", &m_truthLeptonMomentum, "pTruthLepton/D");
   m_tree->Branch("truthBarcode", &m_truthBarcode, "truthBarcode/I");
   m_tree->Branch("truthPdg", &m_truthPdg, "truthPdg/I");
@@ -273,7 +314,7 @@ StatusCode NtupleDumperAlg::initialize()
 }
 
 
-StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const 
+StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
 {
   clearTree();
 
@@ -339,7 +380,7 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
 
       return StatusCode::SUCCESS; // finished with this event
 
-    } else if ( ((m_tap&8)==0) && (((m_tap&4)==0)||((m_tap&2)==0)) && (((m_tap&4)==0)||((m_tap&1)==0)) && (((m_tap&2)==0)||((m_tap&1)==0)) ) { // don't process events that don't trigger coincidence triggers: 1=calo, 2=veotnu|neto1|preshower, 4=TimingLayer, 8=(VetoNu|Veto2)&Preshower 
+    } else if ( ((m_tap&8)==0) && (((m_tap&4)==0)||((m_tap&2)==0)) && (((m_tap&4)==0)||((m_tap&1)==0)) && (((m_tap&2)==0)||((m_tap&1)==0)) ) { // don't process events that don't trigger coincidence triggers: 1=calo, 2=veotnu|neto1|preshower, 4=TimingLayer, 8=(VetoNu|Veto2)&Preshower
       return StatusCode::SUCCESS;
     }
     m_tbp=triggerData->tbp();
@@ -420,7 +461,7 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
   FillWaveBranches(*triggerContainer);
   FillWaveBranches(*preshowerContainer);
   FillWaveBranches(*ecalContainer);
-  
+
   m_calo_total=m_wave_charge[0]+m_wave_charge[1]+m_wave_charge[2]+m_wave_charge[3];
   m_calo_rawtotal=m_wave_raw_charge[0]+m_wave_raw_charge[1]+m_wave_raw_charge[2]+m_wave_raw_charge[3];
 
@@ -430,13 +471,13 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     m_Calo1_Edep = (m_wave_charge[1] / 24.333) * m_MIP_sim_Edep_calo;
     m_Calo2_Edep = (m_wave_charge[2] / 24.409) * m_MIP_sim_Edep_calo;
     m_Calo3_Edep = (m_wave_charge[3] / 25.555) * m_MIP_sim_Edep_calo;
-  } else if (m_CaloConfig == "Low_gain") { // assume low gain calo 
+  } else if (m_CaloConfig == "Low_gain") { // assume low gain calo
     m_Calo0_Edep = (m_wave_charge[0] / 0.7909) * m_MIP_sim_Edep_calo;
     m_Calo1_Edep = (m_wave_charge[1] / 0.8197) * m_MIP_sim_Edep_calo;
     m_Calo2_Edep = (m_wave_charge[2] / 0.8256) * m_MIP_sim_Edep_calo;
     m_Calo3_Edep = (m_wave_charge[3] / 0.8821) * m_MIP_sim_Edep_calo;
   } else {
-   ATH_MSG_WARNING("Run config is neither High_gain nor Low_gain, it is " << m_CaloConfig << ", calo calibration will be zero"); 
+   ATH_MSG_WARNING("Run config is neither High_gain nor Low_gain, it is " << m_CaloConfig << ", calo calibration will be zero");
   }
   m_Calo_Total_Edep = m_Calo0_Edep + m_Calo1_Edep + m_Calo2_Edep + m_Calo3_Edep;
 
@@ -517,6 +558,16 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     m_trackseg_pz.push_back(SegMomentum.z());
   }
 
+  std::map<int, size_t> truthParticleCount {};
+  if (!realData) {
+    SG::ReadHandle<xAOD::TruthParticleContainer> truthParticleContainer { m_truthParticleContainer, ctx };
+    ATH_CHECK(truthParticleContainer.isValid() && truthParticleContainer->size() > 0);
+    for (const xAOD::TruthParticle *tp : *truthParticleContainer) {
+      if (tp->p4().P() > m_minMomentum)
+        truthParticleCount[tp->barcode()] = 0;
+    }
+  }
+
   SG::ReadHandle<TrackCollection> trackCollection {m_trackCollection, ctx};
   ATH_CHECK(trackCollection.isValid());
   const Trk::TrackParameters* candidateParameters {nullptr};
@@ -580,6 +631,56 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     m_pzdown.push_back(candidateDownParameters->momentum().z());
     m_pdown.push_back(sqrt( pow(candidateDownParameters->momentum().x(),2) + pow(candidateDownParameters->momentum().y(),2) + pow(candidateDownParameters->momentum().z(),2) ));
 
+    if (!realData) {
+      auto [truthParticle, hitCount] = m_trackTruthMatchingTool->getTruthParticle(track);
+      if (truthParticle != nullptr) {
+        if (truthParticleCount.count(truthParticle->barcode()) > 0)
+          truthParticleCount[truthParticle->barcode()] += 1;
+        m_t_pdg.push_back(truthParticle->pdgId());
+        m_t_barcode.push_back(truthParticle->barcode());
+        // the track fit eats up 5 degrees of freedom, thus the number of hits on track is m_DoF + 5
+        m_t_truthHitRatio.push_back(hitCount / (m_DoF.back() + 5));
+        m_isFiducial.push_back(m_fiducialParticleTool->isFiducial(truthParticle->barcode()));
+        auto positions = m_fiducialParticleTool->getTruthPositions(truthParticle->barcode());
+        for (int station = 0; station < 4; ++station) {
+          m_t_st_x[station].push_back(positions[station].x());
+          m_t_st_y[station].push_back(positions[station].y());
+          m_t_st_z[station].push_back(positions[station].z());
+        }
+        if (truthParticle->hasProdVtx()) {
+          m_t_prodVtx_x.push_back(truthParticle->prodVtx()->x());
+          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);
+        }
+        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_px.push_back(truthParticle->px());
+        m_t_py.push_back(truthParticle->py());
+        m_t_pz.push_back(truthParticle->pz());
+        m_t_theta.push_back(truthParticle->p4().Theta());
+        m_t_phi.push_back(truthParticle->p4().Phi());
+        m_t_p.push_back(truthParticle->p4().P());
+        m_t_pT.push_back(truthParticle->p4().Pt());
+        m_t_eta.push_back(truthParticle->p4().Eta());
+      } else {
+        setNaN();
+      }
+    } else {
+      ATH_MSG_WARNING("Can not find truthParticle.");
+      setNaN();
+    }
+
     // fill extrapolation vectors with filler values that get changed iif the track extrapolation succeeds
     m_xVetoNu.push_back(-10000);
     m_yVetoNu.push_back(-10000);
@@ -676,7 +777,7 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
         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
@@ -735,6 +836,14 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
     m_longTracks++;
   }
 
+  if (!realData) {
+    for (auto &tp : truthParticleCount) {
+      m_truthParticleBarcode.push_back(tp.first);
+      m_truthParticleMatchedTracks.push_back(tp.second);
+      m_truthParticleIsFiducial.push_back(m_fiducialParticleTool->isFiducial(tp.first));
+    }
+  }
+
   /*
   // Here we apply the signal selection
   // Very simple/unrealistic to start
@@ -751,7 +860,7 @@ StatusCode NtupleDumperAlg::execute(const EventContext &ctx) const
 }
 
 
-StatusCode NtupleDumperAlg::finalize() 
+StatusCode NtupleDumperAlg::finalize()
 {
   return StatusCode::SUCCESS;
 }
@@ -843,7 +952,7 @@ NtupleDumperAlg::clearTree() const
   m_charge.clear();
   m_nLayers.clear();
   m_longTracks = 0;
- 
+
   m_nHit0.clear();
   m_nHit1.clear();
   m_nHit2.clear();
@@ -884,7 +993,60 @@ NtupleDumperAlg::clearTree() const
   m_thetaxCalo.clear();
   m_thetayCalo.clear();
 
+  m_t_pdg.clear();
+  m_t_barcode.clear();
+  m_t_truthHitRatio.clear();
+  m_t_prodVtx_x.clear();
+  m_t_prodVtx_y.clear();
+  m_t_prodVtx_z.clear();
+  m_t_decayVtx_x.clear();
+  m_t_decayVtx_y.clear();
+  m_t_decayVtx_z.clear();
+  m_t_px.clear();
+  m_t_py.clear();
+  m_t_pz.clear();
+  m_t_theta.clear();
+  m_t_phi.clear();
+  m_t_p.clear();
+  m_t_pT.clear();
+  m_t_eta.clear();
+  m_isFiducial.clear();
+  for (int station = 0; station < 4; ++station) {
+    m_t_st_x[station].clear();
+    m_t_st_y[station].clear();
+    m_t_st_z[station].clear();
+  }
+  m_truthParticleBarcode.clear();
+  m_truthParticleMatchedTracks.clear();
+  m_truthParticleIsFiducial.clear();
+
   m_truthLeptonMomentum = 0;
   m_truthBarcode = 0;
   m_truthPdg = 0;
 }
+
+void NtupleDumperAlg::setNaN() 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);
+  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_isFiducial.push_back(false);
+}
\ No newline at end of file
diff --git a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
index 1b26caadeb7290c34a199ef0e941f9203b600bba..fb805829e264d1d52a9e797306b7857592b1c977 100644
--- a/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
+++ b/PhysicsAnalysis/NtupleDumper/src/NtupleDumperAlg.h
@@ -15,6 +15,9 @@
 #include "TrackerSimData/TrackerSimDataCollection.h"
 #include "FaserActsGeometryInterfaces/IFaserActsExtrapolationTool.h"
 #include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/IFiducialParticleTool.h"
+#include "FaserActsKalmanFilter/ITrackTruthMatchingTool.h"
+#include "TrackerSimEvent/FaserSiHitCollection.h"
 
 #include <vector>
 
@@ -44,6 +47,7 @@ private:
 
   bool waveformHitOK(const xAOD::WaveformHit* hit) const;
   void clearTree() const;
+  void setNaN() 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);
@@ -64,11 +68,14 @@ private:
   SG::ReadHandleKey<xAOD::WaveformHitContainer> m_ecalContainer { this, "EcalContainer", "CaloWaveformHits", "Ecal hit container name" };
   SG::ReadHandleKey<Tracker::FaserSCT_ClusterContainer> m_clusterContainer { this, "ClusterContainer", "SCT_ClusterContainer", "Tracker cluster container name" };
   SG::ReadHandleKey<FaserSCT_SpacePointContainer> m_spacePointContainerKey { this, "SpacePoints", "SCT_SpacePointContainer", "space point container"};
+  SG::ReadHandleKey<FaserSiHitCollection> m_siHitCollectionKey{this, "FaserSiHitCollection", "SCT_Hits"};
 
   SG::ReadHandleKey<xAOD::FaserTriggerData> m_FaserTriggerData     { this, "FaserTriggerDataKey", "FaserTriggerData", "ReadHandleKey for xAOD::FaserTriggerData"};
   SG::ReadHandleKey<xAOD::WaveformClock> m_ClockWaveformContainer     { this, "WaveformClockKey", "WaveformClock", "ReadHandleKey for ClockWaveforms Container"};
   ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool { this, "ExtrapolationTool", "FaserActsExtrapolationTool" };  
   ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  ToolHandle<ITrackTruthMatchingTool> m_trackTruthMatchingTool {this, "TrackTruthMatchingTool", "TrackTruthMatchingTool"};
+  ToolHandle<IFiducialParticleTool> m_fiducialParticleTool {this, "FiducialParticleTool", "FiducialParticleTool"};
 
   const TrackerDD::SCT_DetectorManager* m_detMgr {nullptr};
 
@@ -86,6 +93,7 @@ private:
   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"};
 
   double m_baseEventCrossSection {1.0};
   const double kfemtoBarnsPerMilliBarn {1.0e12};
@@ -203,6 +211,32 @@ private:
   mutable std::vector<double> m_thetaxCalo;
   mutable std::vector<double> m_thetayCalo;
 
+  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
+  mutable std::vector<double> m_t_truthHitRatio; // ratio of hits on track matched to the truth particle over all hits on track
+  mutable std::vector<double> m_t_prodVtx_x; // x component of the production vertex in mm
+  mutable std::vector<double> m_t_prodVtx_y; // y component of the production vertex in mm
+  mutable std::vector<double> m_t_prodVtx_z; // z component of the production vertex in mm
+  mutable std::vector<double> m_t_decayVtx_x; // x component of the decay vertex in mm
+  mutable std::vector<double> m_t_decayVtx_y; // y component of the decay vertex in mm
+  mutable std::vector<double> m_t_decayVtx_z; // z component of the decay vertex in mm
+  mutable std::vector<double> m_t_px; // truth momentum px in MeV
+  mutable std::vector<double> m_t_py;  // truth momentum py in MeV
+  mutable std::vector<double> m_t_pz;  // truth momentum pz in MeV
+  mutable std::vector<double> m_t_theta; // angle of truth particle with respsect to the beam axis in rad, theta = arctan(sqrt(px * px + py * py) / pz)
+  mutable std::vector<double> m_t_phi; // polar angle of truth particle in rad, phi = arctan(py / px)
+  mutable std::vector<double> m_t_p; // truth momentum p in MeV
+  mutable std::vector<double> m_t_pT; // transverse truth momentum pT in MeV
+  mutable std::vector<double> m_t_eta; // eta of truth particle
+  mutable std::array<std::vector<double>, 4> m_t_st_x; // vector of the x components of the simulated hits of the truth particle for each station
+  mutable std::array<std::vector<double>, 4> m_t_st_y; // vector of the y components of the simulated hits of the truth particle for each station
+  mutable std::array<std::vector<double>, 4> m_t_st_z; // vector of the z components of the simulated hits of the truth particle for each station
+  mutable std::vector<bool> m_isFiducial; // track is fiducial if there are simulated hits for stations 1 - 3 and the distance from the center is smaller than 100 mm
+
+  mutable std::vector<int> m_truthParticleBarcode; // vector of barcodes of all truth particles with a momentum larger 50 GeV
+  mutable std::vector<int> m_truthParticleMatchedTracks; // vector of number of tracks to which a truth particle is matched to
+  mutable std::vector<bool> m_truthParticleIsFiducial; // vector of boolean showing whether a truth particle is fiducial
+
   mutable double m_truthLeptonMomentum;
   mutable int    m_truthBarcode;
   mutable int    m_truthPdg;
diff --git a/README.md b/README.md
index 2d7e57abb75b58112939efb7b2726607bcfb6ec3..bcd917f069ec308e9657f013a9398dba24285145 100644
--- a/README.md
+++ b/README.md
@@ -50,6 +50,8 @@ When compiling, CERN IT recommends using condor to submit batch jobs. The basics
 
 ** `ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"` and `ConfigFlags.IOVDb.GlobalTag = OFLCOND-FASER-02` enables the full FaserNu (IFT + emulsion) setup
 
+** `ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"` and `ConfigFlags.IOVDb.GlobalTag = OFLCOND-FASER-03` enables the full FaserNu (IFT + emulsion) setup with updated (10Nov22) magnetic field map
+
 ** `ConfigFlags.GeoModel.FaserVersion = "FASER-TB00"` and `ConfigFlags.IOVDb.GlobalTag = OFLCOND-FASER-TB00` enables the 2021 Test-beam setup.
 
 * The command `source /cvmfs/sft.cern.ch/lcg/releases/LCG_101_ATLAS_6/sqlite/3320300/x86_64-centos7-gcc11-opt/sqlite-env.sh` may be necessary to avoid errors when generating a database
diff --git a/Simulation/G4Faser/G4FaserAlg/CMakeLists.txt b/Simulation/G4Faser/G4FaserAlg/CMakeLists.txt
index 35042d814e913677a0ddf4d643e792a75b362f2d..979897a94c4b476433d0ef3fe2ceeb31105f9ef5 100644
--- a/Simulation/G4Faser/G4FaserAlg/CMakeLists.txt
+++ b/Simulation/G4Faser/G4FaserAlg/CMakeLists.txt
@@ -36,6 +36,11 @@ atlas_add_test( G4FaserAlgConfig_TestFaserNu
                 PROPERTIES TIMEOUT 300 
                 PROPERTIES WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
 
+atlas_add_test( G4FaserAlgConfig_TestFaserNu_NewField
+                SCRIPT python ${CMAKE_CURRENT_SOURCE_DIR}/test/G4FaserAlgConfigNew_Test.py GeoModel.FaserVersion="'FASERNU-03'" IOVDb.GlobalTag="'OFLCOND-FASER-03'" Output.HITSFileName='faserNu.HITS.pool.root'
+                PROPERTIES TIMEOUT 300 
+                PROPERTIES WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
+
 atlas_add_test( G4FaserAlgConfig_TestTestbeam
                 SCRIPT python ${CMAKE_CURRENT_SOURCE_DIR}/test/G4FaserAlgConfigNew_Test.py GeoModel.FaserVersion="'FASER-TB00'" IOVDb.GlobalTag="'OFLCOND-FASER-TB00'" Output.HITSFileName='tb.HITS.pool.root'
                 PROPERTIES TIMEOUT 300 
diff --git a/Simulation/G4Faser/G4FaserAlg/test/G4FaserAlgConfigNew_Test.py b/Simulation/G4Faser/G4FaserAlg/test/G4FaserAlgConfigNew_Test.py
index 998b40c286837a3436e6fc714f4977b355ccdd9d..d7ea3c3ab921b57e3df3c5c4be56c102f873c8f7 100755
--- a/Simulation/G4Faser/G4FaserAlg/test/G4FaserAlgConfigNew_Test.py
+++ b/Simulation/G4Faser/G4FaserAlg/test/G4FaserAlgConfigNew_Test.py
@@ -49,7 +49,7 @@ if __name__ == '__main__':
     ConfigFlags.addFlag("Sim.Beam.yshift", 0)        
 
     ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"             # Geometry set-up
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Conditions set-up
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Conditions set-up
     ConfigFlags.addFlag("Input.InitialTimeStamp", 0)             # To avoid autoconfig 
     ConfigFlags.GeoModel.Align.Dynamic = False
 #
diff --git a/Tracker/TrackerConditions/FaserSCT_ConditionsData/README.md b/Tracker/TrackerConditions/FaserSCT_ConditionsData/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8025a4b1d3244a86163371a4fae298a2108d47ef
--- /dev/null
+++ b/Tracker/TrackerConditions/FaserSCT_ConditionsData/README.md
@@ -0,0 +1,33 @@
+# FaserSCT_ConditionsData
+
+For more information on conditions in general, see [[https://twiki.cern.ch/twiki/bin/view/FASER/OfflineDatabases][OfflineDatabases]].
+
+## Creating databases
+
+The scripts 
+```
+data/SCT_Conditions.py
+data/BField_DataConditions.py
+```
+were used to create the intial databases for MC (OFLP200) and data (CONDBR3).  
+
+## Updating field map
+
+The following was used to update the magnetic field map on 10 Nov. 22:
+```
+cp /cvmfs/faser.cern.ch/repo/sw/database/DBRelease/current/sqlite200/ALLP200.db .
+
+# This creates new field and scale entries with tag OFLCOND-FASER-03 in mysqlite.db
+BField_Conditions_Update03.py
+
+# Now we need to merge this into the existing DB
+AtlCoolCopy "sqlite://;schema=mysqlite.db;dbname=OFLP200" "sqlite://;schema=ALLP200.db;dbname=OFLP200"
+AtlCoolCopy "sqlite://;schema=mysqlite.db;dbname=CONDBR3" "sqlite://;schema=ALLP200.db;dbname=CONDBR3"
+
+# For the MC instance, we also need to associate the existing /SCT and /Tracker tags
+AtlCoolConsole.py "sqlite://;schema=ALLP200.db;dbname=OFLP200"
+settag /SCT SCT-02 OFLCOND-FASER-03
+settag /Tracker TRACKER-02 OFLCOND-FASER-03
+```
+
+After installing the new tags in ALLP200.db, we can test this locally by copying it to the data/sqlite200 subdirectory of the run directory, and eventually install it on cvmfs.
diff --git a/Tracker/TrackerConditions/FaserSCT_ConditionsData/data/BField_Conditions_Update03.py b/Tracker/TrackerConditions/FaserSCT_ConditionsData/data/BField_Conditions_Update03.py
new file mode 100755
index 0000000000000000000000000000000000000000..8a99a5fba6709ab66c42587507657cb67802030c
--- /dev/null
+++ b/Tracker/TrackerConditions/FaserSCT_ConditionsData/data/BField_Conditions_Update03.py
@@ -0,0 +1,158 @@
+#!/bin/env python
+
+# This script was used to add a new tag for the BField to both 
+# the real data (CONDBR3) and MC (OFLP200) instances.
+# This writes out to mysqlite.db which then needs to be merged
+# with the production database using:
+# AtlCoolCopy "sqlite://;schema=mysqlite.db;dbname=CONDBR3" "sqlite://;schema=ALLP200.db;dbname=CONDBR3"
+
+description = '<timeStamp>run-lumi</timeStamp><addrHeader><address_header clid="1238547719" service_type="71" /></addrHeader><typeName>CondAttrListCollection</typeName>'
+
+descriptionDCS = '<timeStamp>time</timeStamp><addrHeader><address_header service_type="71" clid="1238547719" /></addrHeader><typeName>CondAttrListCollection</typeName><cache>600</cache>'
+
+descriptionAlign = '<timeStamp>run-lumi</timeStamp><addrHeader><address_header service_type="256" clid="1170039409" /></addrHeader><typeName>AlignableTransformContainer</typeName>'
+
+import sys
+from PyCool import cool, coral
+from CoolConvUtilities.AtlCoolLib import forceOpen
+
+print('generating new field database')
+
+dbSvc = cool.DatabaseSvcFactory.databaseService()
+connectString = 'sqlite://;schema=mysqlite.db;dbname=CONDBR3'
+
+# This should open or create as needed
+try:
+    print(f'forceOpen({connectString})')
+    db = forceOpen( connectString )
+except Exception as e:
+    print(e)
+    print('Problem opening DB!')
+    sys.exit(1)
+
+# Hierarchy of new tag is:
+# OFLCOND-FASER-02 : GLOBAL-03 : GLOBAL-BField-03 : GLOBAL-BField-Maps-03
+
+# Create new tag
+glob = db.createFolderSet("/GLOBAL")
+glob_bfield = db.createFolderSet("/GLOBAL/BField")
+
+glob_bfield.createTagRelation("GLOBAL-03", "GLOBAL-BField-03")
+glob.createTagRelation("OFLCOND-FASER-03", "GLOBAL-03")
+
+print("Created tag GLOBAL-BField-03 and associated to OFLCOND-FASER-03")
+
+mapSpec = cool.RecordSpecification()
+mapSpec.extend( 'FieldType', cool.StorageType.String4k )
+mapSpec.extend( 'MapFileName', cool.StorageType.String4k )
+
+mapFolderSpec = cool.FolderSpecification(cool.FolderVersioning.MULTI_VERSION, mapSpec)
+mapFolder = db.createFolder('/GLOBAL/BField/Maps', mapFolderSpec, descriptionDCS, True )
+
+# New entry
+mapRecord = cool.Record(mapSpec)
+mapRecord['FieldType'] = "GlobalMap"
+mapRecord['MapFileName'] = "file:MagneticFieldMaps/FaserFieldTable_v2.root"
+
+mapFolder.storeObject( cool.ValidityKeyMin, cool.ValidityKeyMax, mapRecord, 1, "GLOBAL-BField-Maps-03", True )
+mapFolder.createTagRelation("GLOBAL-BField-03", "GLOBAL-BField-Maps-03")
+
+print("Created new entry in /GLOBAL/BField/Maps with tag GLOBAL-BField-Maps-03")
+
+# Also update the scale (since it sits in the same tag area)
+scaleSpec = cool.RecordSpecification()
+scaleSpec.extend( 'value', cool.StorageType.Float )
+
+scaleRecord = cool.Record(scaleSpec)
+scaleRecord['value'] = 1.0
+
+scaleFolderSpec = cool.FolderSpecification(cool.FolderVersioning.MULTI_VERSION, scaleSpec)
+scaleFolder = db.createFolder('/GLOBAL/BField/Scales', scaleFolderSpec, descriptionDCS, True )
+
+# Channel names don't seem to be handled properly by Athena
+scaleFolder.createChannel( 1, "Dipole_Scale" )
+
+scaleFolder.storeObject( cool.ValidityKeyMin, cool.ValidityKeyMax, scaleRecord, 1, "GLOBAL-BField-Scale-03", True )
+scaleFolder.createTagRelation("GLOBAL-BField-03", "GLOBAL-BField-Scale-03")
+
+print("Created new entry in /GLOBAL/BField/Maps with tag GLOBAL-BField-Scale-03")
+
+# These are the only tags in the CONDBR3 instance, so we are done
+
+db.closeDatabase()
+
+# Now do it again for the MC instance
+
+connectString = 'sqlite://;schema=mysqlite.db;dbname=OFLP200'
+
+# This should open or create as needed
+try:
+    print(f'forceOpen({connectString})')
+    db = forceOpen( connectString )
+except Exception as e:
+    print(e)
+    print('Problem opening DB!')
+    sys.exit(1)
+
+# Create new tag
+glob = db.createFolderSet("/GLOBAL")
+glob_bfield = db.createFolderSet("/GLOBAL/BField")
+
+glob_bfield.createTagRelation("GLOBAL-03", "GLOBAL-BField-03")
+glob.createTagRelation("OFLCOND-FASER-03", "GLOBAL-03")
+
+print("Created tag GLOBAL-BField-03 and associated to OFLCOND-FASER-03")
+
+mapSpec = cool.RecordSpecification()
+mapSpec.extend( 'FieldType', cool.StorageType.String4k )
+mapSpec.extend( 'MapFileName', cool.StorageType.String4k )
+
+mapFolderSpec = cool.FolderSpecification(cool.FolderVersioning.MULTI_VERSION, mapSpec)
+mapFolder = db.createFolder('/GLOBAL/BField/Maps', mapFolderSpec, descriptionDCS, True )
+
+# New entry
+mapRecord = cool.Record(mapSpec)
+mapRecord['FieldType'] = "GlobalMap"
+mapRecord['MapFileName'] = "file:MagneticFieldMaps/FaserFieldTable_v2.root"
+
+mapFolder.storeObject( cool.ValidityKeyMin, cool.ValidityKeyMax, mapRecord, 1, "GLOBAL-BField-Maps-03", True )
+mapFolder.createTagRelation("GLOBAL-BField-03", "GLOBAL-BField-Maps-03")
+
+print("Created new entry in /GLOBAL/BField/Maps with tag GLOBAL-BField-Maps-03")
+
+# Also update the scale (since it sits in the same tag area)
+scaleSpec = cool.RecordSpecification()
+scaleSpec.extend( 'value', cool.StorageType.Float )
+
+scaleRecord = cool.Record(scaleSpec)
+scaleRecord['value'] = 1.0
+
+scaleFolderSpec = cool.FolderSpecification(cool.FolderVersioning.MULTI_VERSION, scaleSpec)
+scaleFolder = db.createFolder('/GLOBAL/BField/Scales', scaleFolderSpec, descriptionDCS, True )
+
+# Channel names don't seem to be handled properly by Athena
+scaleFolder.createChannel( 1, "Dipole_Scale" )
+
+scaleFolder.storeObject( cool.ValidityKeyMin, cool.ValidityKeyMax, scaleRecord, 1, "GLOBAL-BField-Scale-03", True )
+scaleFolder.createTagRelation("GLOBAL-BField-03", "GLOBAL-BField-Scale-03")
+
+print("Created new entry in /GLOBAL/BField/Maps with tag GLOBAL-BField-Scale-03")
+
+# Also make associations to /SCT and /TRACKER tags
+#sct = db.createFolderSet("/SCT")
+#sct.createTagRelation("OFLCOND-FASER-03", "SCT-02")
+#tracker = db.createFolderSet("/Tracker")
+#tracker.createTagRelation("OFLCOND-FASER-03", "TRACKER-02")
+#print("Associated old tags for /SCT and /Tracker to OFLCOND-FASER-03")
+
+# This doesn't work. 
+# Instead, we need to go into AtlCoolConsole.py and 
+# set the association there.
+#
+# settag /SCT SCT-02 OFLCOND-FASER-03
+# settag /Tracker TRACKER-02 OFLCOND-FASER-03
+#
+# Check that this worked:
+# tracetags / OFLCOND-FASER-03
+
+db.closeDatabase()
diff --git a/Tracker/TrackerConditions/FaserSCT_ConditionsData/data/BField_DataConditions.py b/Tracker/TrackerConditions/FaserSCT_ConditionsData/data/BField_DataConditions.py
index 94846ee80dedb41dbc2354a6662873203aad9cb6..9fb21099b9dbeeb97f8632dc5a9e64919e40e3a1 100755
--- a/Tracker/TrackerConditions/FaserSCT_ConditionsData/data/BField_DataConditions.py
+++ b/Tracker/TrackerConditions/FaserSCT_ConditionsData/data/BField_DataConditions.py
@@ -3,6 +3,10 @@
 # Use this to add a field map to the CONDBR3 database for real data
 # Copied the result from the OFLP200 DB
 # Note that the testbeam turns off the field by setting scale = 0
+
+# This file creates the DB from scratch
+# To add new tags, look at BField_Conditions_Update03.py
+
 description = '<timeStamp>run-lumi</timeStamp><addrHeader><address_header clid="1238547719" service_type="71" /></addrHeader><typeName>CondAttrListCollection</typeName>'
 
 descriptionDCS = '<timeStamp>time</timeStamp><addrHeader><address_header service_type="71" clid="1238547719" /></addrHeader><typeName>CondAttrListCollection</typeName><cache>600</cache>'
@@ -11,7 +15,7 @@ descriptionAlign = '<timeStamp>run-lumi</timeStamp><addrHeader><address_header s
 
 import sys
 from PyCool import cool, coral
-from CoolConvUtilities.AtlCoolLib import indirectOpen
+from CoolConvUtilities.AtlCoolLib import forceOpen
 
 dbSvc = cool.DatabaseSvcFactory.databaseService()
 connectString = 'sqlite://;schema=ALLP200.db;dbname=CONDBR3'
@@ -20,8 +24,9 @@ print('generating field database')
 #dbSvc.dropDatabase( connectString )
 try:
     # Open existing instead?
-    print('Try indirectOpen')
-    db = indirectOpen( connectString, readOnly=False )
+    # This actually should open or create as needed
+    print(f'Try forceOpen({connectString})')
+    db = forceOpen( connectString )
 except Exception as e:
     print(e)
     print('Problem opening DB, create instead')
@@ -36,6 +41,9 @@ glob.createTagRelation("OFLCOND-FASER-01", "GLOBAL-01")
 glob_bfield.createTagRelation("GLOBAL-02", "GLOBAL-BField-02")
 glob.createTagRelation("OFLCOND-FASER-02", "GLOBAL-02")
 
+glob_bfield.createTagRelation("GLOBAL-03", "GLOBAL-BField-03")
+glob.createTagRelation("OFLCOND-FASER-03", "GLOBAL-03")
+
 glob_bfield.createTagRelation("GLOBAL-TB00", "GLOBAL-BField-TB00")
 glob.createTagRelation("OFLCOND-FASER-TB00", "GLOBAL-TB00")
 
@@ -43,13 +51,13 @@ mapSpec = cool.RecordSpecification()
 mapSpec.extend( 'FieldType', cool.StorageType.String4k )
 mapSpec.extend( 'MapFileName', cool.StorageType.String4k )
 
-mapRecord = cool.Record(mapSpec)
-mapRecord['FieldType'] = "GlobalMap"
-mapRecord['MapFileName'] = "file:MagneticFieldMaps/FaserFieldTable.root"
-
 mapFolderSpec = cool.FolderSpecification(cool.FolderVersioning.MULTI_VERSION, mapSpec)
 mapFolder = db.createFolder('/GLOBAL/BField/Maps', mapFolderSpec, descriptionDCS, True )
 
+mapRecord = cool.Record(mapSpec)
+mapRecord['FieldType'] = "GlobalMap"
+mapRecord['MapFileName'] = "file:MagneticFieldMaps/FaserFieldTable_v101.root"
+
 mapFolder.storeObject( cool.ValidityKeyMin, cool.ValidityKeyMax, mapRecord, 1, "GLOBAL-BField-Maps-01", True )
 mapFolder.createTagRelation("GLOBAL-BField-01", "GLOBAL-BField-Maps-01")
 
@@ -59,6 +67,12 @@ mapFolder.createTagRelation("GLOBAL-BField-02", "GLOBAL-BField-Maps-02")
 mapFolder.storeObject( cool.ValidityKeyMin, cool.ValidityKeyMax, mapRecord, 1, "GLOBAL-BField-Maps-TB00", True )
 mapFolder.createTagRelation("GLOBAL-BField-TB00", "GLOBAL-BField-Maps-TB00")
 
+# New record
+mapRecord['MapFileName'] = "file:MagneticFieldMaps/FaserFieldTable_v2.root"
+
+mapFolder.storeObject( cool.ValidityKeyMin, cool.ValidityKeyMax, mapRecord, 1, "GLOBAL-BField-Maps-03", True )
+mapFolder.createTagRelation("GLOBAL-BField-03", "GLOBAL-BField-Maps-03")
+
 scaleSpec = cool.RecordSpecification()
 scaleSpec.extend( 'value', cool.StorageType.Float )
 
diff --git a/Tracker/TrackerRecAlgs/NoisyStripFinder/share/NoisyStripFinderJob.py b/Tracker/TrackerRecAlgs/NoisyStripFinder/share/NoisyStripFinderJob.py
index b6dc5c514d05c558310bf1108e505deca1e8098c..e8c304455b27156d4c0b8983b9c04cd61367b2ce 100755
--- a/Tracker/TrackerRecAlgs/NoisyStripFinder/share/NoisyStripFinderJob.py
+++ b/Tracker/TrackerRecAlgs/NoisyStripFinder/share/NoisyStripFinderJob.py
@@ -39,7 +39,7 @@ for filename in args.file:
         filelist.append(filename)
 
 ConfigFlags.Input.Files = args.file
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 #ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"
 ConfigFlags.IOVDb.DatabaseInstance = "CONDBR3"
 ConfigFlags.Input.ProjectName = "data22"
diff --git a/Tracker/TrackerRecAlgs/NoisyStripFinder/share/submitNoisyStripJobs.py b/Tracker/TrackerRecAlgs/NoisyStripFinder/share/submitNoisyStripJobs.py
index fa034fd773f32751a71c04538775a1f5ab148e58..4bcb0acf24a53e2c1e88da7c523767ac34fcab4d 100755
--- a/Tracker/TrackerRecAlgs/NoisyStripFinder/share/submitNoisyStripJobs.py
+++ b/Tracker/TrackerRecAlgs/NoisyStripFinder/share/submitNoisyStripJobs.py
@@ -226,9 +226,10 @@ if __name__ == "__main__":
                 f.write('if [[ $1 != "0" ]]; then\n')
                 f.write(' exit $1\n')
                 f.write('fi\n')
+                # Removing files used by the DAG will make this hang...
                 # f.write('rm noise_{runstr}.dag.* \n')
-                f.write('rm *.log\n')
-                f.write('rm *.err\n')
+                # f.write('rm *.log\n')
+                # f.write('rm *.err\n')
                 f.write('rm eventLoopHeartBeat.txt\n')
                 f.write('rm *.cc\n')
                 for job in submit_list:
diff --git a/Tracker/TrackerRecAlgs/NoisyStripFinder/test/NoisyStripFinderDbg.py b/Tracker/TrackerRecAlgs/NoisyStripFinder/test/NoisyStripFinderDbg.py
index f1d2ef1434110fa4a87a4567055e8f3d112f30c0..8b31e1b3db8f98e4cf8a535a7ca5f839fb142799 100755
--- a/Tracker/TrackerRecAlgs/NoisyStripFinder/test/NoisyStripFinderDbg.py
+++ b/Tracker/TrackerRecAlgs/NoisyStripFinder/test/NoisyStripFinderDbg.py
@@ -24,7 +24,7 @@ log.setLevel(DEBUG)
 Configurable.configurableRun3Behavior = True
 
 ConfigFlags.Input.Files = args.file
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"
 ConfigFlags.Input.ProjectName = "data21"
 ConfigFlags.Input.isMC = False
diff --git a/Tracker/TrackerRecAlgs/TrackerData/test/TI12TrackerSegmentFitDataDbg.py b/Tracker/TrackerRecAlgs/TrackerData/test/TI12TrackerSegmentFitDataDbg.py
index b20d313f99654d07487651c645616b93a266789f..e69cdca114c30d2d72f0c27fbeed21746524e51b 100644
--- a/Tracker/TrackerRecAlgs/TrackerData/test/TI12TrackerSegmentFitDataDbg.py
+++ b/Tracker/TrackerRecAlgs/TrackerData/test/TI12TrackerSegmentFitDataDbg.py
@@ -22,7 +22,7 @@ Configurable.configurableRun3Behavior = True
 ConfigFlags.Input.Files = ['/home/tboeckh/tmp/Faser-Physics-006470-00093.raw_middleStation.SPs']
 ConfigFlags.Output.ESDFileName = "TrackerSegmentFitData.ESD.pool.root"
 ConfigFlags.addFlag("Output.xAODFileName", f"TrackerSegmentFitData_xAOD.root")
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"
 ConfigFlags.Input.ProjectName = "data21"
 ConfigFlags.Input.isMC = False
diff --git a/Tracker/TrackerRecAlgs/TrackerSegmentFit/test/TrackerSegmentFitDbg.py b/Tracker/TrackerRecAlgs/TrackerSegmentFit/test/TrackerSegmentFitDbg.py
index 75c46defbbb45d3c815f9d11e0091c83836a6e1e..7ca10a8b71dd3e80e0c705a09299c84537305876 100644
--- a/Tracker/TrackerRecAlgs/TrackerSegmentFit/test/TrackerSegmentFitDbg.py
+++ b/Tracker/TrackerRecAlgs/TrackerSegmentFit/test/TrackerSegmentFitDbg.py
@@ -29,7 +29,7 @@ ConfigFlags.Input.Files = [
     '/eos/project-f/faser-commissioning/TI12Data/Run-005684/Faser-Physics-005684-00000.raw',
 ]
 ConfigFlags.Output.ESDFileName = "run005684-00000.ESD.pool.root"
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersion
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersion
 ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"               # Use MC conditions for now
 ConfigFlags.Input.ProjectName = "data21"                     # Needed to bypass autoconfig
 ConfigFlags.Input.isMC = False                               # Needed to bypass autoconfig
@@ -98,4 +98,4 @@ replicaSvc.UseGeomSQLite = True
 sc = acc.run(maxEvents=-1)
 
 # Success should be 0
-sys.exit(not sc.isSuccess())
\ No newline at end of file
+sys.exit(not sc.isSuccess())
diff --git a/Tracking/Acts/FaserActsGeometry/python/FaserActsMaterialMapping_jobOptions.py b/Tracking/Acts/FaserActsGeometry/python/FaserActsMaterialMapping_jobOptions.py
index 92c1b6039a754f68dd5c562575a0dc3b9cc2e9d5..9a7e4995a8b28b4139826f8274b20ed1911bcf86 100644
--- a/Tracking/Acts/FaserActsGeometry/python/FaserActsMaterialMapping_jobOptions.py
+++ b/Tracking/Acts/FaserActsGeometry/python/FaserActsMaterialMapping_jobOptions.py
@@ -61,7 +61,7 @@ if "__main__" == __name__:
   ConfigFlags.Input.isMC             = True
   ConfigFlags.Beam.Type = "collisions" 
   ConfigFlags.GeoModel.FaserVersion  = "FASERNU-03"
-  ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersion
+  ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersion
   ConfigFlags.TrackingGeometry.MaterialSource = "geometry-maps.json"
   ConfigFlags.GeoModel.Align.Dynamic = False
 
diff --git a/Tracking/Acts/FaserActsGeometry/test/FaserActsWriteTrackingGeometry.py b/Tracking/Acts/FaserActsGeometry/test/FaserActsWriteTrackingGeometry.py
index ed998b3b4607cf633e2825b5cb868c08039308d6..196a86099b66e0750a2b91f4803acbf0252bc321 100644
--- a/Tracking/Acts/FaserActsGeometry/test/FaserActsWriteTrackingGeometry.py
+++ b/Tracking/Acts/FaserActsGeometry/test/FaserActsWriteTrackingGeometry.py
@@ -19,7 +19,7 @@ Configurable.configurableRun3Behavior = True
 # Configure
 ConfigFlags.Input.Files = ["myevt4.HITS.pool.root"]
 #ConfigFlags.Output.RDOFileName = "myRDO_sp.pool.root"
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersion
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersion
 ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"               # Always needed
 #ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"               # Always needed
 # Workaround for bug/missing flag; unimportant otherwise 
diff --git a/Tracking/Acts/FaserActsKalmanFilter/CMakeLists.txt b/Tracking/Acts/FaserActsKalmanFilter/CMakeLists.txt
index 9377c4716810301c16fe7ca606ba31e6f5ba318c..f93c094287a4966f84689abaa2615da2b2ef8882 100755
--- a/Tracking/Acts/FaserActsKalmanFilter/CMakeLists.txt
+++ b/Tracking/Acts/FaserActsKalmanFilter/CMakeLists.txt
@@ -39,8 +39,10 @@ atlas_add_component(FaserActsKalmanFilter
     MyTrackSeedTool.h
     FaserActsKalmanFilter/IdentifierLink.h
     FaserActsKalmanFilter/IndexSourceLink.h
+    FaserActsKalmanFilter/IFiducialParticleTool.h
     FaserActsKalmanFilter/ITrackFinderTool.h
     FaserActsKalmanFilter/ITrackSeedTool.h
+    FaserActsKalmanFilter/ITrackTruthMatchingTool.h
     KalmanFitterTool.h
     LinearFit.h
 #    ClusterTrackSeedTool.h
@@ -77,6 +79,8 @@ atlas_add_component(FaserActsKalmanFilter
     src/CombinatorialKalmanFilterAlg.cxx
     src/EffPlotTool.cxx
     src/FaserActsKalmanFilterAlg.cxx
+    src/FiducialParticleTool.h
+    src/FiducialParticleTool.cxx
     src/GhostBusters.cxx
     src/MyTrackSeedTool.cxx
     src/KalmanFitterTool.cxx
@@ -101,6 +105,8 @@ atlas_add_component(FaserActsKalmanFilter
     src/TrackClassification.cxx
     src/TrackSeedWriterTool.cxx
     src/TrackSelection.cxx
+    src/TrackTruthMatchingTool.h
+    src/TrackTruthMatchingTool.cxx
 #    src/TruthTrackFinderTool.cxx
 #    src/TruthSeededTrackFinderTool.cxx
     src/ThreeStationTrackSeedTool.cxx
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IFiducialParticleTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IFiducialParticleTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..1df9aeab2ce991ee5d683d92fa5efb72eca7a5dd
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IFiducialParticleTool.h
@@ -0,0 +1,28 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS and FASER
+  collaborations
+*/
+
+#ifndef FASERACTSKALMANFILTER_IFIDUCIALEVENTSELECTIONTOOL_H
+#define FASERACTSKALMANFILTER_IFIDUCIALEVENTSELECTIONTOOL_H
+
+#include "CLHEP/Geometry/Point3D.h"
+
+class IFiducialParticleTool : virtual public IAlgTool {
+public:
+  DeclareInterfaceID(IFiducialParticleTool, 1, 0);
+
+  /** Check if truth hits for the given barcode exist and are in the
+   * fiducial volume (r < 100mm) in stations 1 to 3.
+   * @param barcode of a xAOD::TruthParticle
+   */
+  virtual bool isFiducial(int barcode) const = 0;
+
+  /** Return average truth position in each station.
+   * @param barcode of a xAOD::TruthParticle
+   */
+  virtual std::array<HepGeom::Point3D<double>, 4>
+  getTruthPositions(int barcode) const = 0;
+};
+
+#endif // FASERACTSKALMANFILTER_IFIDUCIALEVENTSELECTIONTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackTruthMatchingTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackTruthMatchingTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..045e597a9ebf916bad29665c6e07a4217c027ce8
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackTruthMatchingTool.h
@@ -0,0 +1,21 @@
+/*
+  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS and FASER
+  collaborations
+*/
+
+#ifndef FASERACTSKALMANFILTER_ITRACKTRUTHMATCHINGTOOL_H
+#define FASERACTSKALMANFILTER_ITRACKTRUTHMATCHINGTOOL_H
+
+#include "GaudiKernel/IAlgTool.h"
+#include "TrkTrack/Track.h"
+#include "xAODTruth/TruthParticle.h"
+
+class ITrackTruthMatchingTool : virtual public IAlgTool {
+public:
+  DeclareInterfaceID(ITrackTruthMatchingTool, 1, 0);
+
+  virtual std::pair<const xAOD::TruthParticle*, int>
+  getTruthParticle(const Trk::Track *track) const = 0;
+};
+
+#endif /* FASERACTSKALMANFILTER_ITRACKTRUTHMATCHINGTOOL_H */
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx
index 066698a478fbea03a6565a056afafd253e449a0d..41611bad5882de7e15ce3de15f6c4833fbf10581 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/CircleFitTrackSeedTool.cxx
@@ -7,6 +7,7 @@
 #include "Identifier/Identifier.h"
 #include "Acts/Geometry/GeometryIdentifier.hpp"
 #include "CircleFit.h"
+#include "Identifier/Identifier.h"
 #include "LinearFit.h"
 #include "TrackClassification.h"
 #include <array>
@@ -264,12 +265,13 @@ CircleFitTrackSeedTool::Seed::Seed(const std::vector<Segment> &segments) :
   size = clusters.size();
 }
 
-
 void CircleFitTrackSeedTool::Seed::fit() {
   CircleFit::CircleData circleData(positions);
   CircleFit::Circle circle = CircleFit::circleFit(circleData);
   momentum = circle.r > 0 ? circle.r * 0.001 * 0.3 * 0.55 : 9999999.;
-  charge = circle.cy < 0 ? -1 : 1;
+  // the magnetic field bends a positively charged particle downwards, thus the
+  // y-component of the center of a fitted circle is negative.
+  charge = circle.cy < 0 ? 1 : -1;
 }
 
 void CircleFitTrackSeedTool::Seed::fakeFit(double B) {
@@ -279,7 +281,9 @@ void CircleFitTrackSeedTool::Seed::fakeFit(double B) {
   cy = circle.cy;
   r = circle.r;
   momentum = r * 0.3 * B * m_MeV2GeV;
-  charge = circle.cy > 0 ? 1 : -1;
+  // the magnetic field bends a positively charged particle downwards, thus the
+  // y-component of the center of a fitted circle is negative.
+  charge = circle.cy < 0 ? 1 : -1;
 }
 
 void CircleFitTrackSeedTool::Seed::linearFit(const std::vector<Acts::Vector2> &points) {
@@ -288,7 +292,6 @@ void CircleFitTrackSeedTool::Seed::linearFit(const std::vector<Acts::Vector2> &p
   c0 = origin[1] - origin[0] * c1;
 }
 
-
 double CircleFitTrackSeedTool::Seed::getY(double z) {
   double sqt = std::sqrt(-cx*cx + 2*cx*z + r*r - z*z);
   return abs(cy - sqt) < abs(cy + sqt) ? cy - sqt : cy + sqt;
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/FiducialParticleTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/FiducialParticleTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..101ddfdf99a645aaef69f6d824e782ff7525928f
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/FiducialParticleTool.cxx
@@ -0,0 +1,91 @@
+#include "FiducialParticleTool.h"
+#include "GeoPrimitives/CLHEPtoEigenConverter.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrackerReadoutGeometry/SiDetectorElement.h"
+
+constexpr float NaN = std::numeric_limits<double>::quiet_NaN();
+
+FiducialParticleTool::FiducialParticleTool(const std::string &type,
+                                           const std::string &name,
+                                           const IInterface *parent)
+    : base_class(type, name, parent) {}
+
+StatusCode FiducialParticleTool::initialize() {
+  ATH_CHECK(m_siHitCollectionKey.initialize());
+  ATH_CHECK(detStore()->retrieve(m_sctHelper, "FaserSCT_ID"));
+  ATH_CHECK(detStore()->retrieve(m_detMgr, "SCT"));
+  return StatusCode::SUCCESS;
+}
+
+StatusCode FiducialParticleTool::finalize() { return StatusCode::SUCCESS; }
+
+bool FiducialParticleTool::isFiducial(int barcode) const {
+  auto positions = getTruthPositions(barcode);
+  bool isFiducial{true};
+  for (int station = 1; station < 4; ++station) {
+    double x{positions[station].x()};
+    double y{positions[station].y()};
+    if (std::isnan(x) || std::isnan(y) || (x * x + y * y > 100 * 100))
+      isFiducial = false;
+  }
+  return isFiducial;
+}
+
+HepGeom::Point3D<double>
+FiducialParticleTool::getGlobalPosition(const FaserSiHit &hit) const {
+  Identifier waferId =
+      m_sctHelper->wafer_id(hit.getStation(), hit.getPlane(), hit.getRow(),
+                            hit.getModule(), hit.getSensor());
+  auto localStartPos = hit.localStartPosition();
+  auto localEndPos = hit.localEndPosition();
+  HepGeom::Point3D<double> localPos = 0.5 * (localEndPos + localStartPos);
+  const TrackerDD::SiDetectorElement *element =
+      m_detMgr->getDetectorElement(waferId);
+  auto globalPosition =
+      Amg::EigenTransformToCLHEP(element->transformHit()) * localPos;
+  return globalPosition;
+}
+
+std::array<HepGeom::Point3D<double>, 4>
+FiducialParticleTool::getTruthPositions(int barcode) const {
+  // initialize positions as NaN
+  std::array<HepGeom::Point3D<double>, 4> positions{};
+  for (auto &station : positions) {
+    station.setX(NaN);
+    station.setY(NaN);
+    station.setZ(NaN);
+  }
+
+  // get simulated hits
+  SG::ReadHandle<FaserSiHitCollection> siHitCollection(m_siHitCollectionKey);
+  if (!siHitCollection.isValid()) {
+    ATH_MSG_WARNING("FaserSiHitCollection not valid.");
+    return positions;
+  }
+
+  // create map with truth positions in each station
+  std::array<std::vector<HepGeom::Point3D<double>>, 4> hitMap{};
+  for (const FaserSiHit &hit : *siHitCollection) {
+    if (hit.trackNumber() == barcode) {
+      auto position = getGlobalPosition(hit);
+      hitMap[hit.getStation()].push_back(position);
+    }
+  }
+
+  // calculate average position in each station
+  for (int station = 0; station < 4; ++station) {
+    std::vector<HepGeom::Point3D<double>> &hits{hitMap[station]};
+    if (hits.empty()) {
+      continue;
+    } else {
+      // calculate average position of all FaserSiHits in a station
+      auto const count = static_cast<double>(hits.size());
+      HepGeom::Point3D<double> sums{};
+      for (const HepGeom::Point3D<double> &hit : hits)
+        sums += hit;
+      positions[station] = sums / count;
+    }
+  }
+  return positions;
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/FiducialParticleTool.h b/Tracking/Acts/FaserActsKalmanFilter/src/FiducialParticleTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d1435734c6982e9dcbd0abd52acb07003914366
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/FiducialParticleTool.h
@@ -0,0 +1,37 @@
+#ifndef FASERACTSKALMANFILTER_FIDUCIALEVENTSELECTIONTOOL_H
+#define FASERACTSKALMANFILTER_FIDUCIALEVENTSELECTIONTOOL_H
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "FaserActsKalmanFilter/IFiducialParticleTool.h"
+#include "GeoPrimitives/GeoPrimitives.h"
+#include "TrackerSimEvent/FaserSiHitCollection.h"
+
+class FaserSCT_ID;
+namespace TrackerDD {
+class SCT_DetectorManager;
+}
+
+class FiducialParticleTool : public extends<AthAlgTool, IFiducialParticleTool> {
+public:
+  FiducialParticleTool(const std::string &type, const std::string &name,
+                       const IInterface *parent);
+  virtual ~FiducialParticleTool() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+
+  bool isFiducial(int barcode) const override;
+
+  std::array<HepGeom::Point3D<double>, 4>
+  getTruthPositions(int barcode) const override;
+
+private:
+  HepGeom::Point3D<double> getGlobalPosition(const FaserSiHit &hit) const;
+
+  SG::ReadHandleKey<FaserSiHitCollection> m_siHitCollectionKey{
+      this, "FaserSiHitCollection", "SCT_Hits"};
+
+  const FaserSCT_ID *m_sctHelper{nullptr};
+  const TrackerDD::SCT_DetectorManager *m_detMgr{nullptr};
+};
+
+#endif // FASERACTSKALMANFILTER_FIDUCIALEVENTSELECTIONTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TrackTruthMatchingTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TrackTruthMatchingTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..502faa3d4bb4dde421f63d2105f8d5525c67a0be
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TrackTruthMatchingTool.cxx
@@ -0,0 +1,103 @@
+#include "TrackTruthMatchingTool.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+
+TrackTruthMatchingTool::TrackTruthMatchingTool(const std::string &type,
+                                               const std::string &name,
+                                               const IInterface *parent)
+    : base_class(type, name, parent) {}
+
+StatusCode TrackTruthMatchingTool::initialize() {
+  ATH_CHECK(m_simDataCollectionKey.initialize());
+  ATH_CHECK(m_truthParticleContainerKey.initialize());
+  return StatusCode::SUCCESS;
+}
+
+std::pair<const xAOD::TruthParticle *, int>
+TrackTruthMatchingTool::getTruthParticle(const Trk::Track *track) const {
+  const xAOD::TruthParticle *truthParticle = nullptr;
+  const EventContext &ctx = Gaudi::Hive::currentContext();
+  SG::ReadHandle<xAOD::TruthParticleContainer> truthParticleContainer{
+      m_truthParticleContainerKey, ctx};
+  if (!truthParticleContainer.isValid()) {
+    ATH_MSG_WARNING("xAOD::TruthParticleContainer is not valid.");
+    return {truthParticle, -1};
+  }
+  SG::ReadHandle<TrackerSimDataCollection> simDataCollection{
+      m_simDataCollectionKey, ctx};
+  if (!simDataCollection.isValid()) {
+    ATH_MSG_WARNING("TrackerSimDataCollection is not valid.");
+    return {truthParticle, -1};
+  }
+  std::vector<ParticleHitCount> particleHitCounts{};
+  identifyContributingParticles(*track, *simDataCollection, particleHitCounts);
+  if (particleHitCounts.empty()) {
+    ATH_MSG_WARNING("Cannot find any truth particle matched to the track.");
+    return {truthParticle, -1};
+  }
+  int barcode = particleHitCounts.front().barcode;
+  int hitCount = particleHitCounts.front().hitCount;
+  auto it = std::find_if(truthParticleContainer->begin(),
+                         truthParticleContainer->end(),
+                         [barcode](const xAOD::TruthParticle_v1 *particle) {
+                           return particle->barcode() == barcode;
+                         });
+  if (it == truthParticleContainer->end()) {
+    ATH_MSG_WARNING("Cannot find particle with barcode "
+                    << barcode << " in truth particle container.");
+    return {truthParticle, -1};
+  }
+  truthParticle = *it;
+  return {truthParticle, hitCount};
+}
+
+StatusCode TrackTruthMatchingTool::finalize() { return StatusCode::SUCCESS; }
+
+void TrackTruthMatchingTool::increaseHitCount(
+    std::vector<ParticleHitCount> &particleHitCounts, int barcode) {
+  auto it = std::find_if(
+      particleHitCounts.begin(), particleHitCounts.end(),
+      [=](const ParticleHitCount &phc) { return (phc.barcode == barcode); });
+  // either increase count if we saw the particle before or add it
+  if (it != particleHitCounts.end()) {
+    it->hitCount += 1u;
+  } else {
+    particleHitCounts.push_back({barcode, 1u});
+  }
+}
+
+void TrackTruthMatchingTool::sortHitCount(
+    std::vector<ParticleHitCount> &particleHitCounts) {
+  std::sort(particleHitCounts.begin(), particleHitCounts.end(),
+            [](const ParticleHitCount &lhs, const ParticleHitCount &rhs) {
+              return (lhs.hitCount > rhs.hitCount);
+            });
+}
+
+void TrackTruthMatchingTool::identifyContributingParticles(
+    const Trk::Track &track, const TrackerSimDataCollection &simDataCollection,
+    std::vector<ParticleHitCount> &particleHitCounts) {
+  for (const Trk::MeasurementBase *meas : *track.measurementsOnTrack()) {
+    const auto *clusterOnTrack =
+        dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack *>(meas);
+    if (!clusterOnTrack)
+      continue;
+    std::vector<int> barcodes{};
+    const Tracker::FaserSCT_Cluster *cluster = clusterOnTrack->prepRawData();
+    for (Identifier id : cluster->rdoList()) {
+      if (simDataCollection.count(id) == 0)
+        continue;
+      const auto &deposits = simDataCollection.at(id).getdeposits();
+      for (const TrackerSimData::Deposit &deposit : deposits) {
+        int barcode = deposit.first->barcode();
+        // count each barcode only once for a wafer
+        if (std::find(barcodes.begin(), barcodes.end(), barcode) ==
+            barcodes.end()) {
+          barcodes.push_back(barcode);
+          increaseHitCount(particleHitCounts, barcode);
+        }
+      }
+    }
+  }
+  sortHitCount(particleHitCounts);
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TrackTruthMatchingTool.h b/Tracking/Acts/FaserActsKalmanFilter/src/TrackTruthMatchingTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..94ab440588b4353a4d45a8d7a15a61aac434f6a3
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TrackTruthMatchingTool.h
@@ -0,0 +1,40 @@
+#ifndef FASERACTSKALMANFILTER_TRACKTRUTHMATCHINGTOOL_H
+#define FASERACTSKALMANFILTER_TRACKTRUTHMATCHINGTOOL_H
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "FaserActsKalmanFilter/ITrackTruthMatchingTool.h"
+#include "TrackerSimData/TrackerSimDataCollection.h"
+#include "TrkTrack/Track.h"
+#include "xAODTruth/TruthParticleContainer.h"
+
+class TrackTruthMatchingTool
+    : public extends<AthAlgTool, ITrackTruthMatchingTool> {
+public:
+  TrackTruthMatchingTool(const std::string &type, const std::string &name,
+                         const IInterface *parent);
+  virtual ~TrackTruthMatchingTool() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+
+  std::pair<const xAOD::TruthParticle*, int>
+  getTruthParticle(const Trk::Track *track) const;
+
+private:
+  struct ParticleHitCount {
+    int barcode;
+    size_t hitCount;
+  };
+  static void increaseHitCount(std::vector<ParticleHitCount> &particleHitCounts,
+                               int particleId);
+  static void sortHitCount(std::vector<ParticleHitCount> &particleHitCounts);
+  static void identifyContributingParticles(
+      const Trk::Track &track, const TrackerSimDataCollection &simDataCollection,
+      std::vector<ParticleHitCount> &particleHitCounts);
+
+  SG::ReadHandleKey<TrackerSimDataCollection> m_simDataCollectionKey{
+      this, "TrackerSimDataCollection", "SCT_SDO_Map"};
+  SG::ReadHandleKey<xAOD::TruthParticleContainer> m_truthParticleContainerKey{
+      this, "ParticleContainer", "TruthParticles"};
+};
+
+#endif /* FASERACTSKALMANFILTER_TRACKTRUTHMATCHINGTOOL_H */
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/components/FaserActsKalmanFilter_entries.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/components/FaserActsKalmanFilter_entries.cxx
index 967f80641f7846d303be4632c252ca30eaab6436..d9fefbddfb3b5e651f5ac697ac231bb9e41918d3 100755
--- a/Tracking/Acts/FaserActsKalmanFilter/src/components/FaserActsKalmanFilter_entries.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/components/FaserActsKalmanFilter_entries.cxx
@@ -29,6 +29,8 @@
 #include "../CircleFitTrackSeedTool.h"
 #include "../GhostBusters.h"
 #include "../CreateTrkTrackTool.h"
+#include "../TrackTruthMatchingTool.h"
+#include "../FiducialParticleTool.h"
 
 DECLARE_COMPONENT(FaserActsKalmanFilterAlg)
 DECLARE_COMPONENT(CombinatorialKalmanFilterAlg)
@@ -57,3 +59,5 @@ DECLARE_COMPONENT(SeedingAlg)
 DECLARE_COMPONENT(CircleFitTrackSeedTool)
 DECLARE_COMPONENT(GhostBusters)
 DECLARE_COMPONENT(CreateTrkTrackTool)
+DECLARE_COMPONENT(TrackTruthMatchingTool)
+DECLARE_COMPONENT(FiducialParticleTool)
diff --git a/Tracking/Acts/FaserActsKalmanFilter/test/CKF2.py b/Tracking/Acts/FaserActsKalmanFilter/test/CKF2.py
index 27c50d9a8b397776ccf19e810ff981afa47a6b67..3d8e9e3fb2f03e126b4d7c39f3680126592eaa61 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/test/CKF2.py
+++ b/Tracking/Acts/FaserActsKalmanFilter/test/CKF2.py
@@ -27,7 +27,7 @@ Configurable.configurableRun3Behavior = True
 ConfigFlags.Input.Files = args.file
 ConfigFlags.addFlag("Output.xAODFileName", f"CKF.xAOD.root")
 ConfigFlags.Output.ESDFileName = "CKF.ESD.pool.root"
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 ConfigFlags.GeoModel.FaserVersion = "FASERNU-03"
 ConfigFlags.GeoModel.Align.Dynamic = False
 ConfigFlags.Beam.NumberOfCollisions = 0.
diff --git a/Tracking/Acts/FaserActsKalmanFilter/test/TI12CKF2.py b/Tracking/Acts/FaserActsKalmanFilter/test/TI12CKF2.py
index cf63c3e400de073ad330df6498129996863b6c3a..37ac15b268712bc98cea7ccd4134fe0545176cde 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/test/TI12CKF2.py
+++ b/Tracking/Acts/FaserActsKalmanFilter/test/TI12CKF2.py
@@ -30,7 +30,7 @@ Configurable.configurableRun3Behavior = True
 ConfigFlags.Input.Files = args.file
 ConfigFlags.Output.ESDFileName = "CKF.ESD.pool.root"
 ConfigFlags.addFlag("Output.xAODFileName", f"CKF.xAOD.root")
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"
 ConfigFlags.Input.ProjectName = "data22"
 ConfigFlags.Input.isMC = False
diff --git a/Tracking/Acts/FaserActsKalmanFilter/test/TI12KalmanFilter.py b/Tracking/Acts/FaserActsKalmanFilter/test/TI12KalmanFilter.py
index 11b7e4217bed8400426f4257ef5cf43f5b7adbfd..ed4be62cd88dcb5fa2280509da8af121b9eaaf85 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/test/TI12KalmanFilter.py
+++ b/Tracking/Acts/FaserActsKalmanFilter/test/TI12KalmanFilter.py
@@ -24,7 +24,7 @@ Configurable.configurableRun3Behavior = True
 ConfigFlags.Input.Files = ['/home/tboeckh/tmp/Faser-Physics-006470-00093.raw_middleStation.SPs']
 ConfigFlags.Output.ESDFileName = "MiddleStation-KalmanFilter.ESD.pool.root"
 ConfigFlags.Output.AODFileName = "MiddleStation-KalmanFilter.AOD.pool.root"
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"
 ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"
 ConfigFlags.Input.ProjectName = "data21"
 ConfigFlags.Input.isMC = False
diff --git a/Waveform/WaveDigiTools/share/WaveformDigiAndRecoExample_jobOptions.py b/Waveform/WaveDigiTools/share/WaveformDigiAndRecoExample_jobOptions.py
index 779031828c9c5736f9403e403514e19285029499..cbe155043db36bc9dc91ef6383ebd2db1d89edf7 100644
--- a/Waveform/WaveDigiTools/share/WaveformDigiAndRecoExample_jobOptions.py
+++ b/Waveform/WaveDigiTools/share/WaveformDigiAndRecoExample_jobOptions.py
@@ -21,7 +21,7 @@ if __name__ == "__main__":
 
     log.setLevel(VERBOSE)
     
-    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"             # Always needed; must match FaserVersion
+    ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-03"             # Always needed; must match FaserVersion
     ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"               # Use MC conditions for now
     ConfigFlags.Input.ProjectName = "mc21"                       # Needed to bypass autoconfig
     ConfigFlags.Input.isMC = True                                # Needed to bypass autoconfig
diff --git a/Waveform/WaveformConditions/WaveformConditionsTools/python/WaveformRangeConfig.py b/Waveform/WaveformConditions/WaveformConditionsTools/python/WaveformRangeConfig.py
index 6450bcec88b3cb8c7194b01edeb59aa6b97e242f..8ac69d912af498475d443f58b3583fc6fdb614c8 100644
--- a/Waveform/WaveformConditions/WaveformConditionsTools/python/WaveformRangeConfig.py
+++ b/Waveform/WaveformConditions/WaveformConditionsTools/python/WaveformRangeConfig.py
@@ -20,8 +20,14 @@ def WaveformRangeCfg(flags, **kwargs):
     acc = ComponentAccumulator()
     # tool = kwargs.get("WaveformRangeTool", WaveformRangeTool(flags))
     # Probably need to figure this out!
-    dbInstance = kwargs.get("dbInstance", "TDAQ_OFL")
+
+    dbInstance = kwargs.get("dbInstance", "COOLOFL_DIGITIZER")
     dbFolder = kwargs.get("dbFolder", "/WAVE/DAQ/Range")
-    acc.merge(addFolders(flags, dbFolder, dbInstance, className="CondAttrListCollection"))
+    dbName = flags.IOVDb.DatabaseInstance # e.g. CONDBR3 
+
+    # COOLOFL_DIGITIZER is not known to ATLAS IOVDBSvc
+    # Must use non-shorthand folder specification here
+    folder_string = f"<db>{dbInstance}/{dbName}</db> {dbFolder}"
+    acc.merge(addFolders(flags, folder_string, className="CondAttrListCollection"))
     return acc
 
diff --git a/xAOD/xAODFaserCalorimeter/Root/CalorimeterHitAuxContainer_v1.cxx b/xAOD/xAODFaserCalorimeter/Root/CalorimeterHitAuxContainer_v1.cxx
index 6f6edf3e4c520dbf32bb816addb74f4ad3eae23f..277ecd25c0d1e2056a6c2a1eeef101f5570f80c2 100644
--- a/xAOD/xAODFaserCalorimeter/Root/CalorimeterHitAuxContainer_v1.cxx
+++ b/xAOD/xAODFaserCalorimeter/Root/CalorimeterHitAuxContainer_v1.cxx
@@ -10,11 +10,13 @@ namespace xAOD {
   CalorimeterHitAuxContainer_v1::CalorimeterHitAuxContainer_v1() 
     : AuxContainerBase() {
 
-    AUX_VARIABLE(localtime);
-    AUX_VARIABLE(bcid_time);
-    AUX_VARIABLE(raw_energy);
+    AUX_VARIABLE(channel);
+    AUX_VARIABLE(Nmip);
+    AUX_VARIABLE(E_dep);
+    AUX_VARIABLE(E_EM);
+    AUX_VARIABLE(fit_to_raw_ratio);
 
-    AUX_VARIABLE(caloLinks);
+    AUX_VARIABLE(WaveformLink);
   }
 
 } // namespace xAOD
diff --git a/xAOD/xAODFaserCalorimeter/Root/CalorimeterHit_v1.cxx b/xAOD/xAODFaserCalorimeter/Root/CalorimeterHit_v1.cxx
index 5e7a69ecc024dc3ecb70df95f1e623c4bc4e09fc..ff592dff67ca5c37bf0ea954dcc9eb63f3a26a28 100644
--- a/xAOD/xAODFaserCalorimeter/Root/CalorimeterHit_v1.cxx
+++ b/xAOD/xAODFaserCalorimeter/Root/CalorimeterHit_v1.cxx
@@ -13,41 +13,45 @@ namespace xAOD {
   CalorimeterHit_v1::CalorimeterHit_v1() : SG::AuxElement() {
   }
 
-  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( CalorimeterHit_v1, float, localtime, set_localtime )
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( CalorimeterHit_v1, unsigned int, channel, set_channel )
 
-  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( CalorimeterHit_v1, float, bcidtime, set_bcidtime )
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( CalorimeterHit_v1, float, Nmip, set_Nmip )
 
-  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( CalorimeterHit_v1, float, raw_energy, set_raw_energy )
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( CalorimeterHit_v1, float, E_dep, set_E_dep )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( CalorimeterHit_v1, float, E_EM, set_E_EM )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( CalorimeterHit_v1, float, fit_to_raw_ratio, set_fit_to_raw_ratio )
 
   // setters and getters for the Calo WaveformHit links
 
   AUXSTORE_OBJECT_SETTER_AND_GETTER( CalorimeterHit_v1,
 				     CalorimeterHit_v1::WaveformHitLinks_t,
-				     caloWaveformLinks,
-				     setCaloWaveformLinks )
+				     WaveformLinks,
+				     setWaveformLinks )
 
-  static const SG::AuxElement::Accessor< CalorimeterHit_v1::WaveformHitLinks_t > caloHitAcc( "caloWaveformLinks" );
+  static const SG::AuxElement::Accessor< CalorimeterHit_v1::WaveformHitLinks_t > HitAcc( "WaveformLinks" );
 
-  const WaveformHit* CalorimeterHit_v1::caloHit( size_t i ) const {
-    return ( *caloHitAcc( *this )[ i ] );
+  const WaveformHit* CalorimeterHit_v1::Hit( size_t i ) const {
+    return ( *HitAcc( *this )[ i ] );
   }
 
-  size_t CalorimeterHit_v1::nCaloHits() const {
-    return caloHitAcc( *this ).size();
+  size_t CalorimeterHit_v1::nHits() const {
+    return HitAcc( *this ).size();
   }
 
-  void CalorimeterHit_v1::addCaloHit( const xAOD::WaveformHitContainer* pWaveformHitContainer, 
+  void CalorimeterHit_v1::addHit( const xAOD::WaveformHitContainer* pWaveformHitContainer, 
 				      const xAOD::WaveformHit* pWaveformHit) {
     ElementLink< xAOD::WaveformHitContainer > linkToWaveformHit;
     linkToWaveformHit.toContainedElement(*pWaveformHitContainer, pWaveformHit);
 
-    caloHitAcc( *this ).push_back( linkToWaveformHit );
+    HitAcc( *this ).push_back( linkToWaveformHit );
 
     return;
   }
 
-  void CalorimeterHit_v1::clearCaloWaveformLinks() {
-    caloHitAcc( *this ).clear();
+  void CalorimeterHit_v1::clearWaveformLinks() {
+    HitAcc( *this ).clear();
     return;
   }
 
@@ -57,8 +61,8 @@ namespace xAOD {
 
   std::ostream& operator<<(std::ostream& s, const xAOD::CalorimeterHit_v1& hit) {
     s << "xAODCalorimeterHit:"
-      << " local time=" << hit.localtime()
-      << " raw_energy=" << hit.raw_energy()
+      << " channel = " << hit.channel()
+      << ", E_dep = " << hit.E_dep()
       << std::endl;
 
     return s;
diff --git a/xAOD/xAODFaserCalorimeter/xAODFaserCalorimeter/versions/CalorimeterHitAuxContainer_v1.h b/xAOD/xAODFaserCalorimeter/xAODFaserCalorimeter/versions/CalorimeterHitAuxContainer_v1.h
index 7e61261eb4bd10f106d03b2a85f9918b82ce7944..230da2716246909fcf85653c288dc043aa9929a5 100644
--- a/xAOD/xAODFaserCalorimeter/xAODFaserCalorimeter/versions/CalorimeterHitAuxContainer_v1.h
+++ b/xAOD/xAODFaserCalorimeter/xAODFaserCalorimeter/versions/CalorimeterHitAuxContainer_v1.h
@@ -29,13 +29,16 @@ namespace xAOD {
   private:
     /// @name Basic variables
     ///@ {
-    std::vector<float> localtime;
-    std::vector<float> bcid_time;
-    std::vector<float> raw_energy;
+    std::vector<unsigned int> channel;
+    std::vector<float> Nmip;
+    std::vector<float> E_dep;
+    std::vector<float> E_EM;
+    std::vector<float> fit_to_raw_ratio;
 
     typedef std::vector< ElementLink< WaveformHitContainer > > WaveformHitLink_t;
-    std::vector< WaveformHitLink_t > caloLinks;
-    std::vector< WaveformHitLink_t > preshowerLinks;
+    std::vector< WaveformHitLink_t > WaveformLink;
+    //std::vector< WaveformHitLink_t > caloLinks;
+    //std::vector< WaveformHitLink_t > preshowerLinks;
 
     ///@}
 
diff --git a/xAOD/xAODFaserCalorimeter/xAODFaserCalorimeter/versions/CalorimeterHit_v1.h b/xAOD/xAODFaserCalorimeter/xAODFaserCalorimeter/versions/CalorimeterHit_v1.h
index d9bb35047e6a75d6dac12dc0ba0eb47e3bceb684..c27d9f033efc6eebaeb2120440a8855362b77700 100644
--- a/xAOD/xAODFaserCalorimeter/xAODFaserCalorimeter/versions/CalorimeterHit_v1.h
+++ b/xAOD/xAODFaserCalorimeter/xAODFaserCalorimeter/versions/CalorimeterHit_v1.h
@@ -35,29 +35,35 @@ namespace xAOD {
     /// @{
 
     /// Best results
-    float localtime() const;
-    void set_localtime(float value);
+    unsigned int channel() const;
+    void set_channel(unsigned int value);
 
-    float bcidtime() const;
-    void set_bcidtime(float value);
+    float Nmip() const;
+    void set_Nmip(float value);
 
-    float raw_energy() const;
-    void set_raw_energy(float value);
+    float E_dep() const;
+    void set_E_dep(float value);
+
+    float E_EM() const;
+    void set_E_EM(float value);
+
+    float fit_to_raw_ratio() const;
+    void set_fit_to_raw_ratio(float value);
 
     // Waveform Hits
     typedef std::vector< ElementLink< xAOD::WaveformHitContainer > > WaveformHitLinks_t;
 
     // Contributing Calorimeter Waveform Hits
-    const WaveformHitLinks_t& caloWaveformLinks() const;
-    void setCaloWaveformLinks( const WaveformHitLinks_t& caloWaveforms );
+    const WaveformHitLinks_t& WaveformLinks() const;
+    void setWaveformLinks( const WaveformHitLinks_t& Waveforms );
     // Remove all waveform hits
-    void clearCaloWaveformLinks();
+    void clearWaveformLinks();
     // Get the pointer to a given waveform hit
-    const WaveformHit* caloHit( size_t i ) const;
+    const WaveformHit* Hit( size_t i ) const;
     // Get the number of waveform hits
-    size_t nCaloHits() const;
+    size_t nHits() const;
     // Add a waveform hit
-    void addCaloHit( const xAOD::WaveformHitContainer*, const xAOD::WaveformHit*);
+    void addHit( const xAOD::WaveformHitContainer*, const xAOD::WaveformHit*);
 
     // // Contributing Preshower Waveform Hits
     // const WaveformHitLinks_t& preshowerWaveformLinks() const;
diff --git a/xAOD/xAODFaserLHC/CMakeLists.txt b/xAOD/xAODFaserLHC/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..577da2cc72aa0ecb03a16a9b0b440b2eaf2da3eb
--- /dev/null
+++ b/xAOD/xAODFaserLHC/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+
+# Declare the package name.
+atlas_subdir( xAODFaserLHC )
+
+# External dependencies.
+find_package( xAODUtilities )
+
+# Component(s) in the package.
+atlas_add_library( xAODFaserLHC
+   xAODFaserLHC/*.h xAODFaserLHC/versions/*.h Root/*.cxx
+   PUBLIC_HEADERS xAODFaserLHC
+   LINK_LIBRARIES xAODCore )
+
+atlas_add_xaod_smart_pointer_dicts(
+   INPUT xAODFaserLHC/selection.xml
+   OUTPUT _selectionFile
+   OBJECTS "xAOD::FaserLHCData_v1" "xAOD::FaserLHCDataAux_v1")
+
+atlas_add_dictionary( xAODFaserLHCDict
+   xAODFaserLHC/xAODFaserLHCDict.h
+   ${_selectionFile}
+   LINK_LIBRARIES xAODCore xAODFaserLHC
+   EXTRA_FILES Root/dict/*.cxx )
+
+# Understand what this does...
+atlas_generate_cliddb( xAODFaserLHC )
diff --git a/xAOD/xAODFaserLHC/Root/FaserLHCDataAux_v1.cxx b/xAOD/xAODFaserLHC/Root/FaserLHCDataAux_v1.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e9b981b33b5252b2cd5d627096462468c517e7e3
--- /dev/null
+++ b/xAOD/xAODFaserLHC/Root/FaserLHCDataAux_v1.cxx
@@ -0,0 +1,54 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// $Id: $
+
+// Local include(s):
+#include "xAODFaserLHC/versions/FaserLHCDataAux_v1.h"
+
+namespace xAOD {
+
+  FaserLHCDataAux_v1::FaserLHCDataAux_v1()
+    : AuxInfoBase(),
+      fillNumber(0), 
+      machineMode(""),
+      beamMode(""),
+      beamType1(0),
+      beamType2(0),
+      betaStar(0),
+      crossingAngle(0),
+      stableBeams(false),
+      injectionScheme(""),
+      numBunchBeam1(0),
+      numBunchBeam2(0),
+      numBunchColliding(0),
+      distanceToCollidingBCID(0),
+      distanceToUnpairedB1(0),
+      distanceToUnpairedB2(0),
+      distanceToInboundB1(0),
+      distanceToTrainStart(0),
+      distanceToPreviousColliding(0)
+  {
+    AUX_VARIABLE( fillNumber );
+    AUX_VARIABLE( machineMode );
+    AUX_VARIABLE( beamMode );
+    AUX_VARIABLE( beamType1 );
+    AUX_VARIABLE( beamType2 );
+    AUX_VARIABLE( betaStar );
+    AUX_VARIABLE( crossingAngle );
+    AUX_VARIABLE( stableBeams );
+    AUX_VARIABLE( injectionScheme );
+    AUX_VARIABLE( numBunchBeam1 );
+    AUX_VARIABLE( numBunchBeam2 );
+    AUX_VARIABLE( numBunchColliding );
+    AUX_VARIABLE( distanceToCollidingBCID );
+    AUX_VARIABLE( distanceToUnpairedB1 );
+    AUX_VARIABLE( distanceToUnpairedB2 );
+    AUX_VARIABLE( distanceToInboundB1 );
+    AUX_VARIABLE( distanceToTrainStart );
+    AUX_VARIABLE( distanceToPreviousColliding );
+
+  }
+
+} // namespace xAOD
diff --git a/xAOD/xAODFaserLHC/Root/FaserLHCData_v1.cxx b/xAOD/xAODFaserLHC/Root/FaserLHCData_v1.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b09c8c85dcc0b19ae7fea0120c79a706336d0387
--- /dev/null
+++ b/xAOD/xAODFaserLHC/Root/FaserLHCData_v1.cxx
@@ -0,0 +1,92 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// $Id: $
+
+// xAOD include(s):
+#include "xAODCore/AuxStoreAccessorMacros.h"
+
+// Local include(s):
+#include "xAODFaserLHC/versions/FaserLHCData_v1.h"
+
+namespace xAOD {
+
+  FaserLHCData_v1::FaserLHCData_v1() 
+    : SG::AuxElement() {
+  }
+
+  /////////////////////////////////////////////////////////////////////////////
+  //
+  //              Implementation for the start time functions
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int,
+					fillNumber, set_fillNumber )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, std::string,
+					machineMode, set_machineMode )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, std::string, 
+					beamMode, set_beamMode )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int,
+					beamType1, set_beamType1 )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int,
+					beamType2, set_beamType2 )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, float, 
+					betaStar, set_betaStar )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, float, 
+					crossingAngle, set_crossingAngle )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, bool, 
+					stableBeams, set_stableBeams )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, std::string, 
+					injectionScheme, set_injectionScheme )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, 
+					numBunchBeam1, set_numBunchBeam1 )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, 
+					numBunchBeam2, set_numBunchBeam2 )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, 
+					numBunchColliding, set_numBunchColliding )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, 
+					distanceToCollidingBCID, set_distanceToCollidingBCID )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, 
+					distanceToUnpairedB1, set_distanceToUnpairedB1 )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, 
+					distanceToUnpairedB2, set_distanceToUnpairedB2 )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, int, 
+					distanceToInboundB1, set_distanceToInboundB1 )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, 
+					distanceToTrainStart, set_distanceToTrainStart )
+
+  AUXSTORE_PRIMITIVE_SETTER_AND_GETTER( FaserLHCData_v1, unsigned int, 
+					distanceToPreviousColliding, set_distanceToPreviousColliding )
+
+
+
+} // namespace xAOD
+
+namespace xAOD {
+
+  std::ostream& operator<<(std::ostream& s, const xAOD::FaserLHCData_v1& td) {
+    s << "xAODFaserLHCData: fill=" << td.fillNumber()
+      << " beamMode=" << td.beamMode()
+      << " betaStar=" << td.betaStar()
+      << " distanceToCollidingBCID=" << td.distanceToCollidingBCID()
+      << std::endl;
+
+    return s;
+  }
+
+} // namespace xAOD
diff --git a/xAOD/xAODFaserLHC/Root/xAODFaserLHCCLIDs.cxx b/xAOD/xAODFaserLHC/Root/xAODFaserLHCCLIDs.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..86811f0c3f95dc6b511ecfb2059561ea2b875c53
--- /dev/null
+++ b/xAOD/xAODFaserLHC/Root/xAODFaserLHCCLIDs.cxx
@@ -0,0 +1,8 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+//simple includes to force the CLASS_DEF etc to be encountered during compile
+
+#include "xAODFaserLHC/FaserLHCData.h"
+#include "xAODFaserLHC/FaserLHCDataAux.h"
diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCData.h b/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCData.h
new file mode 100644
index 0000000000000000000000000000000000000000..561c8782ca68c12381928815f999d45264d73168
--- /dev/null
+++ b/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCData.h
@@ -0,0 +1,22 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERLHC_FASERLHCDATA_H
+#define XAODFASERLHC_FASERLHCDATA_H
+
+// Local include(s):
+#include "xAODFaserLHC/versions/FaserLHCData_v1.h"
+
+namespace xAOD {
+  /// Declare the latest version of the class
+  typedef FaserLHCData_v1 FaserLHCData;
+}
+
+// Set up a CLID for the container:
+#include "xAODCore/CLASS_DEF.h"
+CLASS_DEF( xAOD::FaserLHCData, 255278152, 1 )
+
+#endif // XAODFASERLHC_FASERLHCDATA_H
diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCDataAux.h b/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCDataAux.h
new file mode 100644
index 0000000000000000000000000000000000000000..6eeac626679c0e95a000d01f96e436b8ad250eb6
--- /dev/null
+++ b/xAOD/xAODFaserLHC/xAODFaserLHC/FaserLHCDataAux.h
@@ -0,0 +1,22 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERLHC_FASERLHCDATAAUX_H
+#define XAODFASERLHC_FASERLHCDATAAUX_H
+
+// Local include(s):
+#include "xAODFaserLHC/versions/FaserLHCDataAux_v1.h"
+
+namespace xAOD {
+  /// Declare the latest version of the class
+  typedef FaserLHCDataAux_v1 FaserLHCDataAux;
+}
+
+// Set up a CLID for the container:
+#include "xAODCore/CLASS_DEF.h"
+CLASS_DEF( xAOD::FaserLHCDataAux, 151779941, 1 )
+
+#endif // XAODFASERLHC_FASERLHCDATAAUX_H
diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/selection.xml b/xAOD/xAODFaserLHC/xAODFaserLHC/selection.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c4ba39f9a6acad53d40208f74cb387ed89b3c36f
--- /dev/null
+++ b/xAOD/xAODFaserLHC/xAODFaserLHC/selection.xml
@@ -0,0 +1,13 @@
+<!-- Copyright (C) 2020 CERN for the benefit of the FASER collaboration -->
+<!-- Missing id values here, need to figure out where these come from... -->
+<lcgdict>
+
+  <class name="xAOD::FaserLHCData_v1" 
+	 id="6c373036-fad1-476c-9a8b-15591faccf00"/>
+  <typedef name="xAOD::FaserLHCData" />
+
+  <class name="xAOD::FaserLHCDataAux_v1" 
+	 id="484ef174-8efb-426a-888c-827f72ed972a"/>
+  <typedef name="xAOD::FaserLHCDataAux" />
+
+</lcgdict>
diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h
new file mode 100644
index 0000000000000000000000000000000000000000..bc1034143f4338ba39403ea82a7dde8561c636e7
--- /dev/null
+++ b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCDataAux_v1.h
@@ -0,0 +1,74 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERLHC_VERSIONS_FASERLHCDATAAUX_V1_H
+#define XAODFASERLHC_VERSIONS_FASERLHCDATAAUX_V1_H
+
+// System include(s):
+extern "C" {
+#   include "stdint.h"
+}
+
+// xAOD include(s):
+#include "xAODCore/AuxInfoBase.h"
+
+namespace xAOD {
+
+  /// Class holding the data handled by FaserLHCData_v1
+  ///
+  /// This class holds the payload of the TLB raw data
+  ///
+  /// @author Eric Torrence <torrence@uoregon.edu>
+  ///
+  /// $Revision:  $
+  /// $Date: $
+
+  class FaserLHCDataAux_v1 : public AuxInfoBase {
+
+  public:
+    /// Default constructor
+    FaserLHCDataAux_v1();
+
+  private:
+    /// @name LHCData payload
+    /// @{
+    unsigned int fillNumber;
+    std::string machineMode;
+    std::string beamMode;
+    int beamType1;
+    int beamType2;
+
+    float betaStar;
+    float crossingAngle;
+
+    bool stableBeams;
+    std::string injectionScheme;
+
+    unsigned int numBunchBeam1;
+    unsigned int numBunchBeam2;
+    unsigned int numBunchColliding;
+
+    int distanceToCollidingBCID;
+    int distanceToUnpairedB1;
+    int distanceToUnpairedB2;
+    int distanceToInboundB1;
+
+    unsigned int distanceToTrainStart;
+    unsigned int distanceToPreviousColliding;
+
+    /// @}
+
+  }; // class FaserLHCDataAuxContainer_v1
+
+} // namespace xAOD
+
+// Set up the StoreGate inheritance of the class:
+#include "xAODCore/BaseInfo.h"
+SG_BASE( xAOD::FaserLHCDataAux_v1, xAOD::AuxInfoBase );
+
+#endif // XAODFASERLHC_VERSIONS_FASERLHCDATAAUX_V1_H
+
+
diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h
new file mode 100644
index 0000000000000000000000000000000000000000..fd6c68cc0545bc8e5f06c92fb5abb288d9ce4e9f
--- /dev/null
+++ b/xAOD/xAODFaserLHC/xAODFaserLHC/versions/FaserLHCData_v1.h
@@ -0,0 +1,127 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERLHC_VERSIONS_FASERLHCDATA_V1_H
+#define XAODFASERLHC_VERSIONS_FASERLHCDATA_V1_H
+
+// System include(s):
+extern "C" {
+#   include "stdint.h"
+}
+
+// Core EDM include(s):
+#include "AthContainers/AuxElement.h"
+
+namespace xAOD {
+
+  /// Class describing the Faser LHC information
+  ///
+  ///
+  class FaserLHCData_v1 : public SG::AuxElement {
+
+  public:
+    /// Default constructor
+    FaserLHCData_v1();
+
+    /// @name Access LHC elements
+    /// @{
+
+    /// First batch comes from ATLAS /LHC/DCS/FILLSTATE folder
+    /// which is a copy of information sent to ATLAS by LHC over DIP
+    /// This is from the FASER COOL folder /LHC/BeamData
+
+    /// LHC fill number
+    unsigned int fillNumber() const;
+    void set_fillNumber(unsigned int value);
+
+    /// LHC machine mode (i.e. PROTON PHYSICS)
+    std::string machineMode() const; 
+    void set_machineMode(std::string value);
+
+    /// LHC Beam Mode (i.e. STABLE BEAMS)
+    std::string beamMode() const; 
+    void set_beamMode(std::string value);
+
+    /// LHC Beam type (charge of particle, 1-proton, 82-lead)
+    int beamType1() const;
+    void set_beamType1(int value);
+
+    int beamType2() const;
+    void set_beamType2(int value);
+
+    // Beta* at IP1, in cm
+    float betaStar() const;
+    void set_betaStar(float value);
+
+    // Crossing angle at IP1, in uRad
+    float crossingAngle() const;
+    void set_crossingAngle(float value);
+
+    // LHC stable beam flag
+    bool stableBeams() const;
+    void set_stableBeams(bool value);
+
+    // Injection scheme
+    std::string injectionScheme() const;
+    void set_injectionScheme(std::string value);
+
+    // Second batch is information is related to the BCID structure
+    // This comes from the LPC filling scheme (nominal values)
+    // and is stored in /LHC/BCIDData
+
+    /// Nominal bunches (derived from DIP "Circulating Bunch Configuration")
+    unsigned int numBunchBeam1() const;
+    void set_numBunchBeam1(unsigned int value);
+    unsigned int numBunchBeam2() const;
+    void set_numBunchBeam2(unsigned int value);
+    unsigned int numBunchColliding() const;
+    void set_numBunchColliding(unsigned int value);
+
+    // Number of BCIDs difference between current event BCID (from EventInfo)
+    // and a colliding BCID in the nominal bunch pattern
+
+    int distanceToCollidingBCID() const;
+    void set_distanceToCollidingBCID(int value);
+
+    int distanceToUnpairedB1() const;
+    void set_distanceToUnpairedB1(int value);
+
+    int distanceToUnpairedB2() const;
+    void set_distanceToUnpairedB2(int value);
+
+    int distanceToInboundB1() const;
+    void set_distanceToInboundB1(int value);
+
+    /// Distance of current BCID to start of previous bunch train
+    unsigned int distanceToTrainStart() const;
+    void set_distanceToTrainStart(unsigned int value);
+
+    /// Distance of current BCID frmo previous colliding BCID (0 if this is colliding)
+    unsigned int distanceToPreviousColliding() const;
+    void set_distanceToPreviousColliding(unsigned int value);
+
+    /// Simple boolean functions to access useful values
+    /// In most cases, these check that the distance above is zero
+    bool isStable() const { return this->stableBeams(); }
+    bool isColliding() const { return this->distanceToCollidingBCID() == 0; }
+    bool isUnpairedBeam1() const { return this->distanceToUnpairedB1() == 0; }
+    bool isUnpairedBeam2() const { return this->distanceToUnpairedB2() == 0; }
+    bool isInboundBeam1() const { return this->distanceToInboundB1() == 0; }
+
+    /// @}
+
+
+  }; // class FaserLHCData_v1
+
+  std::ostream& operator<<(std::ostream& s, const xAOD::FaserLHCData_v1& td);
+
+} // namespace xAOD
+
+// Declare the inheritance of the type:
+#include "xAODCore/BaseInfo.h"
+SG_BASE( xAOD::FaserLHCData_v1, SG::AuxElement );
+
+#endif // XAODFASERLHC_VERSIONS_FASERLHCDATA_V1_H
diff --git a/xAOD/xAODFaserLHC/xAODFaserLHC/xAODFaserLHCDict.h b/xAOD/xAODFaserLHC/xAODFaserLHC/xAODFaserLHCDict.h
new file mode 100644
index 0000000000000000000000000000000000000000..4715ad862918741fd85966378b821f18229ac66a
--- /dev/null
+++ b/xAOD/xAODFaserLHC/xAODFaserLHC/xAODFaserLHCDict.h
@@ -0,0 +1,27 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERLHC_XAODFASERLHCDICT_H
+#define XAODFASERLHC_XAODFASERLHCDICT_H
+
+// Local include(s):
+#include "xAODFaserLHC/FaserLHCData.h"
+#include "xAODFaserLHC/FaserLHCDataAux.h"
+#include "xAODFaserLHC/versions/FaserLHCData_v1.h"
+#include "xAODFaserLHC/versions/FaserLHCDataAux_v1.h"
+
+// EDM include(s).
+#include "xAODCore/tools/DictHelpers.h"
+
+namespace {
+  struct GCCXML_DUMMY_INSTANTIATION_XAODFASERLHC {
+    // Local type(s).
+    XAOD_INSTANTIATE_NS_OBJECT_TYPES( xAOD, FaserLHCData_v1 );
+
+  };
+}
+
+
+#endif // XAODFASERLHC_XAODFASERLHCDICT_H
+
diff --git a/xAOD/xAODFaserLHCAthenaPool/CMakeLists.txt b/xAOD/xAODFaserLHCAthenaPool/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d15a49ccdcaeb1312417a6b0834eadb395296169
--- /dev/null
+++ b/xAOD/xAODFaserLHCAthenaPool/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+
+# Declare the package name.
+atlas_subdir( xAODFaserLHCAthenaPool )
+
+# Component(s) in the package:
+atlas_add_poolcnv_library( xAODFaserLHCAthenaPoolPoolCnv
+   src/*.h src/*.cxx
+   FILES xAODFaserLHC/FaserLHCData.h xAODFaserLHC/FaserLHCDataAux.h
+   TYPES_WITH_NAMESPACE xAOD::FaserLHCData xAOD::FaserLHCDataAux
+   CNV_PFX xAOD
+   LINK_LIBRARIES AthenaPoolCnvSvcLib AthenaPoolUtilities xAODFaserLHC )
+
+
diff --git a/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.cxx b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2ae9467033ddabd5b6c0e0fab94a5fb20c9c99de
--- /dev/null
+++ b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.cxx
@@ -0,0 +1,5 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// Dummy source file so that cmake will know this is a custom converter.
diff --git a/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.h b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc3f3f9afc0e6cf99a6348eef0dc5bc3b1764501
--- /dev/null
+++ b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataAuxCnv.h
@@ -0,0 +1,16 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATAAUXCNV_H
+#define XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATAAUXCNV_H
+
+#include "xAODFaserLHC/FaserLHCDataAux.h"
+#include "AthenaPoolCnvSvc/T_AthenaPoolAuxContainerCnv.h"
+
+typedef T_AthenaPoolAuxContainerCnv<xAOD::FaserLHCDataAux> xAODFaserLHCDataAuxCnv;
+
+
+#endif // XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATAAUXCNV_H
diff --git a/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.cxx b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..2ae9467033ddabd5b6c0e0fab94a5fb20c9c99de
--- /dev/null
+++ b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.cxx
@@ -0,0 +1,5 @@
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+// Dummy source file so that cmake will know this is a custom converter.
diff --git a/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.h b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.h
new file mode 100644
index 0000000000000000000000000000000000000000..a73c439c688c0ef5879a84cd300ab15622d45c5e
--- /dev/null
+++ b/xAOD/xAODFaserLHCAthenaPool/src/xAODFaserLHCDataCnv.h
@@ -0,0 +1,17 @@
+// Dear emacs, this is -*- c++ -*-
+
+/*
+  Copyright (C) 2020 CERN for the benefit of the FASER collaboration
+*/
+
+#ifndef XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATACNV_H
+#define XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATACNV_H
+
+#include "xAODFaserLHC/FaserLHCData.h"
+#include "AthenaPoolCnvSvc/T_AthenaPoolxAODCnv.h"
+
+
+typedef T_AthenaPoolxAODCnv<xAOD::FaserLHCData> xAODFaserLHCDataCnv;
+
+
+#endif // XAODFASERLHCDATAATHENAPOOL_XAODFASERLHCDATACNV_H