diff --git a/Tracking/Acts/FaserActsGeometry/src/CuboidVolumeBuilder.cxx b/Tracking/Acts/FaserActsGeometry/src/CuboidVolumeBuilder.cxx
index 76416ff7b7a917a9e47a18a87ac125686b6d386a..a8a8bd2dec4016916236128d6c78e815b8015bf2 100644
--- a/Tracking/Acts/FaserActsGeometry/src/CuboidVolumeBuilder.cxx
+++ b/Tracking/Acts/FaserActsGeometry/src/CuboidVolumeBuilder.cxx
@@ -75,12 +75,12 @@ std::shared_ptr<const Acts::Layer> CuboidVolumeBuilder::buildLayer(
 
   if ( !cfg.surfaces.empty() ) {
     return layerCreator.planeLayer(gctx, cfg.surfaces, cfg.binsX, cfg.binsY,
-                                 Acts::BinningValue::binZ, std::nullopt, trafo, 
-				 std::move(ap));
+                                 Acts::BinningValue::binZ, std::nullopt, trafo);
+//				 std::move(ap));
   } else {
     return layerCreator.planeLayer(gctx, {cfg.surface}, cfg.binsX, cfg.binsY,
-                                 Acts::BinningValue::binZ, std::nullopt, trafo, 
-				 std::move(ap));
+                                 Acts::BinningValue::binZ, std::nullopt, trafo);
+//				 std::move(ap));
   }
 }
 
diff --git a/Tracking/Acts/FaserActsGeometry/src/FaserActsExtrapolationTool.cxx b/Tracking/Acts/FaserActsGeometry/src/FaserActsExtrapolationTool.cxx
index e84a69c6a3fe54ee30ecb046a0606a1e264da5e5..c5351bfaaea0780c843fb1aa3a6ec6fd31cdb3ce 100644
--- a/Tracking/Acts/FaserActsGeometry/src/FaserActsExtrapolationTool.cxx
+++ b/Tracking/Acts/FaserActsGeometry/src/FaserActsExtrapolationTool.cxx
@@ -71,7 +71,8 @@ FaserActsExtrapolationTool::initialize()
 
   ATH_MSG_INFO("Initializing ACTS extrapolation");
 
-  m_logger = makeActsAthenaLogger(this, "Prop", "FaserActsExtrapTool");
+  m_logger = Acts::getDefaultLogger("ExtrapolationTool", Acts::Logging::INFO);
+//  m_logger = makeActsAthenaLogger(this, "Prop", "FaserActsExtrapTool");
 
   ATH_CHECK( m_trackingGeometryTool.retrieve() );
   std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry
@@ -318,8 +319,8 @@ FaserActsExtrapolationTool::propagate(const EventContext& ctx,
   ATH_MSG_VERBOSE(name() << "::" << __FUNCTION__ << " begin");
 
   Acts::MagneticFieldContext mctx = getMagneticFieldContext(ctx);
-  const FaserActsGeometryContext& gctx
-    = m_trackingGeometryTool->getGeometryContext(ctx);
+  const FaserActsGeometryContext& gctx = m_trackingGeometryTool->getNominalGeometryContext();
+//  const FaserActsGeometryContext& gctx = m_trackingGeometryTool->getGeometryContext(ctx);
 
   auto anygctx = gctx.context();
 
diff --git a/Tracking/Acts/FaserActsGeometry/src/FaserActsTrackingGeometrySvc.cxx b/Tracking/Acts/FaserActsGeometry/src/FaserActsTrackingGeometrySvc.cxx
index f32a02990835e96bc0d56bfe887604bf3667a4eb..94c852743ebd4dec46d3ee734a7f800dd4384a3e 100644
--- a/Tracking/Acts/FaserActsGeometry/src/FaserActsTrackingGeometrySvc.cxx
+++ b/Tracking/Acts/FaserActsGeometry/src/FaserActsTrackingGeometrySvc.cxx
@@ -25,6 +25,8 @@
 #include "FaserActsGeometry/CuboidVolumeBuilder.h"
 #include "Acts/ActsVersion.hpp"
 #include "Acts/Definitions/Units.hpp"
+#include <Acts/Plugins/Json/JsonMaterialDecorator.hpp>
+#include <Acts/Plugins/Json/MaterialMapJsonConverter.hpp>
 
 // PACKAGE
 #include "FaserActsGeometry/FaserActsLayerBuilder.h"
@@ -73,6 +75,21 @@ FaserActsTrackingGeometrySvc::initialize()
         cvhConfig, makeActsAthenaLogger(this, "CylVolHlpr", "ActsTGSvc"));
 
   Acts::TrackingGeometryBuilder::Config tgbConfig;
+
+  if (m_useMaterialMap) {
+    std::shared_ptr<const Acts::IMaterialDecorator> matDeco = nullptr;
+    std::string matFile = m_materialMapInputFile;
+    ATH_MSG_INFO("Configured to use material input: " << matFile);
+    if (matFile.find(".json") != std::string::npos) {
+      // Set up the converter first
+      Acts::MaterialMapJsonConverter::Config jsonGeoConvConfig;
+      // Set up the json-based decorator
+      matDeco = std::make_shared<const Acts::JsonMaterialDecorator>(
+          jsonGeoConvConfig, m_materialMapInputFile, Acts::Logging::INFO);
+    }
+    tgbConfig.materialDecorator = matDeco;
+  }
+
   try {
     // SCT
     tgbConfig.trackingVolumeBuilders.push_back([&](
diff --git a/Tracking/Acts/FaserActsKalmanFilter/CMakeLists.txt b/Tracking/Acts/FaserActsKalmanFilter/CMakeLists.txt
index 21e5ed799d3caeaf33d4fc85102b9be48e18e599..34d9584f9f6b30b1683c962d6baa59037bc8d6f3 100755
--- a/Tracking/Acts/FaserActsKalmanFilter/CMakeLists.txt
+++ b/Tracking/Acts/FaserActsKalmanFilter/CMakeLists.txt
@@ -23,22 +23,64 @@ atlas_add_library( FaserActsKalmanFilterLib
 
 atlas_add_component(FaserActsKalmanFilter
     FaserActsKalmanFilter/CombinatorialKalmanFilterAlg.h
+    FaserActsKalmanFilter/EffPlotTool.h
+    FaserActsKalmanFilter/FASERSourceLink.h
     FaserActsKalmanFilter/FaserActsGeometryContainers.h
+    FaserActsKalmanFilter/FaserActsKalmanFilterAlg.h
     FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h
+    FaserActsKalmanFilter/IdentifierLink.h
     FaserActsKalmanFilter/IndexSourceLink.h
+    FaserActsKalmanFilter/ITrackFinderTool.h
+    FaserActsKalmanFilter/ITrackSeedTool.h
+#    FaserActsKalmanFilter/ClusterTrackSeedTool.h
+#    FaserActsKalmanFilter/TruthTrackFinderTool.h
     FaserActsKalmanFilter/Measurement.h
+#    FaserActsKalmanFilter/MultiTrackFinderTool.h
+    FaserActsKalmanFilter/MyAmbiguitySolver.h
+    FaserActsKalmanFilter/PerformanceWriterTool.h
+    FaserActsKalmanFilter/PlotHelpers.h
+    FaserActsKalmanFilter/ResPlotTool.h
+    FaserActsKalmanFilter/RootTrajectoryStatesWriterTool.h
+    FaserActsKalmanFilter/RootTrajectorySummaryWriterTool.h
+#    FaserActsKalmanFilter/SegmentFitClusterTrackFinderTool.h
+#    FaserActsKalmanFilter/SegmentFitTrackFinderTool.h
     FaserActsKalmanFilter/SimWriterTool.h
     FaserActsKalmanFilter/SPSeedBasedInitialParameterTool.h
     FaserActsKalmanFilter/SPSimpleInitialParameterTool.h
+    FaserActsKalmanFilter/SummaryPlotTool.h
+    FaserActsKalmanFilter/TrackClassification.h
+    FaserActsKalmanFilter/TrackSelection.h
     FaserActsKalmanFilter/TrajectoryWriterTool.h
+#    FaserActsKalmanFilter/ProtoTrackWriterTool.h
     FaserActsKalmanFilter/TruthBasedInitialParameterTool.h
-    src/CombinatorialKalmbanFilterAlg.cxx
+#    FaserActsKalmanFilter/TruthSeededTrackFinderTool.h
+    FaserActsKalmanFilter/ThreeStationTrackSeedTool.h
+#    src/ClusterTrackSeedTool.cxx
+    src/CombinatorialKalmanFilterAlg.cxx
+    src/EffPlotTool.cxx
+    src/FaserActsKalmanFilterAlg.cxx
+#    src/MultiTrackFinderTool.cxx
+    src/PerformanceWriterTool.cxx
+    src/PlotHelpers.cxx
+    src/ResPlotTool.cxx
+    src/RootTrajectoryStatesWriterTool.cxx
+    src/RootTrajectorySummaryWriterTool.cxx
+#    src/SegmentFitClusterTrackFinderTool.cxx
+#    src/SegmentFitTrackFinderTool.cxx
     src/SimWriterTool.cxx
     src/SPSeedBasedInitialParameterTool.cxx
     src/SPSimpleInitialParameterTool.cxx
+#    src/ProtoTrackWriterTool.cxx
     src/TrackFindingAlgorithmFunction.cxx
+    src/TrackFittingFunction.cxx
     src/TrajectoryWriterTool.cxx
     src/TruthBasedInitialParameterTool.cxx
+    src/SummaryPlotTool.cxx
+    src/TrackClassification.cxx
+    src/TrackSelection.cxx
+#    src/TruthTrackFinderTool.cxx
+#    src/TruthSeededTrackFinderTool.cxx
+    src/ThreeStationTrackSeedTool.cxx
     src/components/FaserActsKalmanFilter_entries.cxx
     PUBLIC_HEADERS FaserActsKalmanFilter
     INCLUDE_DIRS ${CLHEP_INCLUDE_DIRS} ${EIGEN_INCLUDE_DIRS} ${BOOST_INCLUDE_DIRS}
@@ -63,6 +105,7 @@ atlas_add_component(FaserActsKalmanFilter
     TrackerIdentifier
     TrackerReadoutGeometry
     Identifier
+    TrackerSimEvent
     TrackerSimData
     TrackerSeedFinderLib
 )
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ClusterTrackSeedTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ClusterTrackSeedTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..fa869dc1bd7242f5096a16d879b73ab86a9e2173
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ClusterTrackSeedTool.h
@@ -0,0 +1,100 @@
+#ifndef FASERACTSKALMANFILTER_CLUSTERTRACKFINDERTOOL_H
+#define FASERACTSKALMANFILTER_CLUSTERTRACKFINDERTOOL_H
+
+#include "TrackerPrepRawData/FaserSCT_ClusterContainer.h"
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/ITrackSeedTool.h"
+#include "TrkTrack/TrackCollection.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+class FaserSCT_ID;
+namespace TrackerDD { class SCT_DetectorManager; }
+
+class ClusterTrackSeedTool : public extends<AthAlgTool, ITrackSeedTool> {
+public:
+  ClusterTrackSeedTool(const std::string& type, const std::string& name, const IInterface* parent);
+  virtual ~ClusterTrackSeedTool() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+  virtual StatusCode run() override;
+
+  const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> initialTrackParameters() const override;
+  const std::shared_ptr<const Acts::Surface> initialSurface() const override;
+  const std::shared_ptr<std::vector<IndexSourceLink>> sourceLinks() const override;
+  const std::shared_ptr<IdentifierLink> idLinks() const override;
+  const std::shared_ptr<std::vector<Measurement>> measurements() const override;
+  const std::shared_ptr<std::vector<const Tracker::FaserSCT_Cluster*>> clusters() const override;
+
+private:
+  std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> m_initialTrackParameters;
+  std::shared_ptr<const Acts::Surface> m_initialSurface;
+  std::shared_ptr<std::vector<IndexSourceLink>> m_sourceLinks {};
+  std::shared_ptr<IdentifierLink> m_idLinks {};
+  std::shared_ptr<std::vector<Measurement>> m_measurements {};
+  std::shared_ptr<std::vector<const Tracker::FaserSCT_Cluster*>> m_clusters {};
+
+  const FaserSCT_ID* m_idHelper {nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detManager {nullptr};
+
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {
+      this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  SG::ReadHandleKey<TrackCollection> m_trackCollection {
+      this, "TrackCollection", "SegmentFit", "Input track collection name" };
+  SG::ReadHandleKey<Tracker::FaserSCT_ClusterContainer> m_clusterContainerKey {
+    this, "ClusterContainer", "SCT_ClusterContainer"};
+
+  // position resolution of a cluster
+  Gaudi::Property<double> m_std_cluster {this, "std_cluster", 0.04};
+
+  // covariance of the initial parameters
+  Gaudi::Property<double> m_covLoc0 {this, "covLoc0", 1};
+  Gaudi::Property<double> m_covLoc1 {this, "covLoc1", 1};
+  Gaudi::Property<double> m_covPhi {this, "covPhi", 1};
+  Gaudi::Property<double> m_covTheta {this, "covTheta", 1};
+  Gaudi::Property<double> m_covQOverP {this, "covQOverP", 1};
+  Gaudi::Property<double> m_covTime {this, "covTime", 1};
+
+  Gaudi::Property<double> m_origin {this, "origin", 0, "z position of the reference surface"};
+
+  static Acts::CurvilinearTrackParameters get_params(const Amg::Vector3D& position_st1, const Amg::Vector3D& position_st2, const Acts::BoundSymMatrix& cov, double origin);
+};
+
+inline const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>>
+ClusterTrackSeedTool::initialTrackParameters() const {
+  return m_initialTrackParameters;
+}
+
+inline const std::shared_ptr<const Acts::Surface>
+ClusterTrackSeedTool::initialSurface() const {
+  return m_initialSurface;
+}
+
+inline const std::shared_ptr<std::vector<IndexSourceLink>>
+ClusterTrackSeedTool::sourceLinks() const {
+  return m_sourceLinks;
+}
+
+inline const std::shared_ptr<IdentifierLink>
+ClusterTrackSeedTool::idLinks() const {
+  return m_idLinks;
+}
+
+inline const std::shared_ptr<std::vector<Measurement>>
+ClusterTrackSeedTool::measurements() const {
+  return m_measurements;
+}
+
+inline const std::shared_ptr<std::vector<const Tracker::FaserSCT_Cluster*>>
+ClusterTrackSeedTool::clusters() const {
+  return m_clusters;
+}
+
+
+#endif  // FASERACTSKALMANFILTER_CLUSTERTRACKFINDERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/CombinatorialKalmanFilterAlg.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/CombinatorialKalmanFilterAlg.h
index 4b750614a3f496a9cabef0352c223d9b9e5a9658..50453e26cf7b4ce21ca8b3b4a6b4c70bceb19d40 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/CombinatorialKalmanFilterAlg.h
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/CombinatorialKalmanFilterAlg.h
@@ -2,82 +2,97 @@
 #define COMBINATORIALKALMANFILTERALG_H
 
 #include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "AthenaBaseComps/AthAlgorithm.h"
 #include "TrackerSpacePoint/FaserSCT_SpacePointContainer.h"
 #include "TrackerPrepRawData/FaserSCT_ClusterContainer.h"
 #include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
 #include "FaserActsKalmanFilter/TruthBasedInitialParameterTool.h"
-#include "FaserActsKalmanFilter/SPSimpleInitialParameterTool.h"
-#include "FaserActsKalmanFilter/SPSeedBasedInitialParameterTool.h"
+//#include "FaserActsKalmanFilter/SPSimpleInitialParameterTool.h"
+//#include "FaserActsKalmanFilter/SPSeedBasedInitialParameterTool.h"
 #include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp"
 #include "Acts/TrackFinding/MeasurementSelector.hpp"
 #include "FaserActsKalmanFilter/Measurement.h"
 #include "MagFieldConditions/FaserFieldCacheCondObj.h"
 #include "FaserActsKalmanFilter/TrajectoryWriterTool.h"
 #include "TrkTrack/TrackCollection.h"
+#include "FaserActsKalmanFilter/ITrackSeedTool.h"
+#include "FaserActsKalmanFilter/RootTrajectoryStatesWriterTool.h"
+#include "FaserActsKalmanFilter/RootTrajectorySummaryWriterTool.h"
+#include "FaserActsKalmanFilter/PerformanceWriterTool.h"
+
 
 class FaserSCT_ID;
 
-namespace Trk
-{
+namespace Trk {
 class TrackStateOnSurface;
 }
 
 namespace TrackerDD {
-  class SCT_DetectorManager;
+class SCT_DetectorManager;
 } 
 
-class CombinatorialKalmanFilterAlg : public AthReentrantAlgorithm { 
- public:
+class CombinatorialKalmanFilterAlg : public AthAlgorithm {
+public:
   CombinatorialKalmanFilterAlg(const std::string& name, ISvcLocator* pSvcLocator);
   virtual ~CombinatorialKalmanFilterAlg() = default;
 
   StatusCode initialize() override;
-  StatusCode execute(const EventContext& ctx) const override;
+  StatusCode execute() override;
   StatusCode finalize() override;
 
   using TrackFinderOptions =
-      Acts::CombinatorialKalmanFilterOptions<IndexSourceLinkAccessor, MeasurementCalibrator, Acts::MeasurementSelector>;
-      using FitterResult = Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>;
-      using TrackFinderResult = std::vector<FitterResult>;
-//  using TrackFinderResult = std::vector<
-//      Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>>;
-  using TrackFinderFunction = std::function<TrackFinderResult(
-      const IndexSourceLinkContainer&, const std::vector<Acts::CurvilinearTrackParameters>&,
-      const TrackFinderOptions&)>;
-
-  static TrackFinderFunction makeTrackFinderFunction(
-      std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry);
+      Acts::CombinatorialKalmanFilterOptions<IndexSourceLinkAccessor,
+                                             MeasurementCalibrator,
+                                             Acts::MeasurementSelector>;
+  using TrackFitterResult =
+      Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>;
+  using TrackFinderResult = std::vector<TrackFitterResult>;
+  using TrackParameters = Acts::CurvilinearTrackParameters;
+  using TrackParametersContainer = std::vector<TrackParameters>;
 
-  Acts::MagneticFieldContext getMagneticFieldContext(const EventContext& ctx) const;
+  class TrackFinderFunction {
+  public:
+    virtual ~TrackFinderFunction() = default;
+    virtual TrackFinderResult operator()(const IndexSourceLinkContainer&,
+                                         const TrackParametersContainer&,
+                                         const TrackFinderOptions&) const = 0;
+  };
 
+  static std::shared_ptr<TrackFinderFunction> makeTrackFinderFunction(
+      std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry,
+      bool resolvePassive, bool resolveMaterial, bool resolveSensitive);
+  virtual Acts::MagneticFieldContext getMagneticFieldContext(const EventContext& ctx) const;
 
  private:
-  const FaserSCT_ID* m_idHelper{nullptr};
-  const TrackerDD::SCT_DetectorManager* m_detManager{nullptr};
-
-//  std::unique_ptr<Trk::Track> makeTrack(Acts::GeometryContext& tgContext, FitterResult& fitResult, std::vector<Tracker::FaserSCT_Cluster>  seed_spcollection) const;
-  std::unique_ptr<Trk::Track> makeTrack(Acts::GeometryContext& tgContext, FitterResult& fitResult, std::vector<Tracker::FaserSCT_SpacePoint>  seed_spcollection) const;
-  const Trk::TrackParameters* ConvertActsTrackParameterToATLAS(const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext& gctx) const;
+  void computeSharedHits(std::vector<IndexSourceLink>* sourceLinks, TrackFinderResult& results) const;
+  std::shared_ptr<TrackFinderFunction> m_fit;
+  std::unique_ptr<const Acts::Logger> m_logger;
+  const FaserSCT_ID* m_idHelper {nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detManager {nullptr};
 
-  SG::WriteHandleKey<TrackCollection> m_trackCollection { this, "FaserActsCKFTrackCollection", "FaserActsCKFTrackCollection", "Output trackcollectionname" };
-
-  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool{this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
-//  ToolHandle<SPSeedBasedInitialParameterTool> m_initialParameterTool{this, "InitialParameterTool", "SPSeedBasedInitialParameterTool"};
-//  ToolHandle<SPSimpleInitialParameterTool> m_initialParameterTool{this, "InitialParameterTool", "SPSimpleInitialParameterTool"};
-  ToolHandle<TruthBasedInitialParameterTool> m_initialParameterTool{this, "InitialParameterTool", "TruthBasedInitialParameterTool"};
-  ToolHandle<TrajectoryWriterTool> m_trajectoryWriterTool{this, "OutputTool", "TrajectoryWriterTool"};
+  Gaudi::Property<std::string> m_actsLogging {this, "ActsLogging", "VERBOSE"};
+  Gaudi::Property<int> m_minNumberMeasurements {this, "MinNumberMeasurements", 12};
+  Gaudi::Property<bool> m_backwardPropagation {this, "BackwardPropagation", false};
+  Gaudi::Property<bool> m_performanceWriter {this, "PerformanceWriter", true};
+  Gaudi::Property<bool> m_summaryWriter {this, "SummaryWriter", true};
+  Gaudi::Property<bool> m_statesWriter {this, "StatesWriter", false};
+  Gaudi::Property<bool> m_resolvePassive {this, "resolvePassive", false};
+  Gaudi::Property<bool> m_resolveMaterial {this, "resolveMaterial", true};
+  Gaudi::Property<bool> m_resolveSensitive {this, "resolveSensitive", true};
+  Gaudi::Property<double> m_maxSteps {this, "maxSteps", 10000};
+  Gaudi::Property<double> m_chi2Max {this, "chi2Max", 15};
+  Gaudi::Property<unsigned long> m_nMax {this, "nMax", 10};
+  Gaudi::Property<size_t> m_nTrajectories {this, "nTrajectories", 2};
   SG::ReadCondHandleKey<FaserFieldCacheCondObj> m_fieldCondObjInputKey {this, "FaserFieldCacheCondObj", "fieldCondObj", "Name of the Magnetic Field conditions object key"};
-  SG::ReadHandleKey<FaserSCT_SpacePointContainer> m_SpacePointContainerKey{this, "SpacePointsSCTName", "SCT_SpacePointContainer", "SCT space point container"};
-  SG::ReadHandleKey<Tracker::FaserSCT_ClusterContainer>  m_Sct_clcontainerKey{this, "SCT_ClusterContainer", "SCT_ClusterContainer"};
-  mutable int m_nevents;
-  mutable int m_ntracks;
-  mutable int m_nseeds;
-  mutable int m_nsp1;
-  mutable int m_nsp2;
-  mutable int m_nsp3;
-  mutable int m_nsp10;
-  mutable int m_nsp11;
-  mutable int m_ncom;
+  ToolHandle<ITrackSeedTool> m_trackSeedTool {this, "TrackSeed", "ClusterTrackSeedTool"};
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  ToolHandle<PerformanceWriterTool> m_performanceWriterTool {this, "PerformanceWriterTool", "PerformanceWriterTool"};
+  ToolHandle<RootTrajectoryStatesWriterTool> m_trajectoryStatesWriterTool {this, "RootTrajectoryStatesWriterTool", "RootTrajectoryStatesWriterTool"};
+  ToolHandle<RootTrajectorySummaryWriterTool> m_trajectorySummaryWriterTool {this, "RootTrajectorySummaryWriterTool", "RootTrajectorySummaryWriterTool"};
+
+   std::unique_ptr<Trk::Track> makeTrack(Acts::GeometryContext& tgContext, TrackFitterResult& fitResult) const;
+   const Trk::TrackParameters* ConvertActsTrackParameterToATLAS(const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext& gctx) const;
+   SG::WriteHandleKey<TrackCollection> m_trackCollection { this, "CKFTrackCollection", "CKFTrackCollection" };
 };
 
 #endif // COMBINATORIALKALMANFILTERALG_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/EffPlotTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/EffPlotTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5af125d6021d5ab19b71f73eebc63a99604aea5
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/EffPlotTool.h
@@ -0,0 +1,57 @@
+#ifndef FASERACTSKALMANFILTER_EFFPLOTTOOL_H
+#define FASERACTSKALMANFILTER_EFFPLOTTOOL_H
+
+#include "FaserActsKalmanFilter/PlotHelpers.h"
+#include "Acts/EventData/TrackParameters.hpp"
+#include "HepMC/GenParticle.h"
+#include "TEfficiency.h"
+#include "TProfile.h"
+#include <map>
+#include <string>
+
+class EffPlotTool {
+public:
+  std::map<std::string, PlotHelpers::Binning> m_varBinning = {
+      {"Eta", PlotHelpers::Binning("#eta", 40, 4, 12)},
+      {"Phi", PlotHelpers::Binning("#phi", 100, -3.15, 3.15)},
+      {"Pt", PlotHelpers::Binning("pT [GeV/c]", 40, 0, 20)}
+  };
+
+  /// @brief Nested Cache struct
+  struct EffPlotCache {
+    TEfficiency* trackEff_vs_pT;   ///< Tracking efficiency vs pT
+    TEfficiency* trackEff_vs_eta;  ///< Tracking efficiency vs eta
+    TEfficiency* trackEff_vs_phi;  ///< Tracking efficiency vs phi
+  };
+
+  /// Constructor
+  ///
+  EffPlotTool() = default;
+
+  /// @brief book the efficiency plots
+  ///
+  /// @param effPlotCache the cache for efficiency plots
+  void book(EffPlotCache& effPlotCache) const;
+
+  /// @brief fill efficiency plots
+  ///
+  /// @param effPlotCache cache object for efficiency plots
+  /// @param truthParticle the truth Particle
+  /// @param status the reconstruction status
+  void fill(EffPlotCache& effPlotCache, const HepMC::GenParticle* truthParticle, bool status) const;
+
+  /// @brief write the efficiency plots to file
+  ///
+  /// @param effPlotCache cache object for efficiency plots
+  void write(const EffPlotCache& effPlotCache) const;
+
+  /// @brief delete the efficiency plots
+  ///
+  /// @param effPlotCache cache object for efficiency plots
+  void clear(EffPlotCache& effPlotCache) const;
+
+private:
+  const double m_MeV2GeV = 0.001;
+};
+
+#endif // FASERACTSKALMANFILTER_EFFPLOTTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FASERSourceLink.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FASERSourceLink.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ec1c46b9ad12ff6822c070a4ea320d6079dd387
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FASERSourceLink.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+#include <cassert>
+#include <boost/container/flat_map.hpp>
+#include "FaserActsKalmanFilter/FaserActsGeometryContainers.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+
+using Index = uint32_t;
+
+class FASERSourceLink final {
+public:
+  /// Construct from geometry identifier and index.
+  constexpr FASERSourceLink(Acts::GeometryIdentifier gid, Index idx, Tracker::FaserSCT_Cluster hit)
+      : m_geometryId(gid), m_index(idx), m_hit(hit) {}
+
+  // Construct an invalid source link. Must be default constructible to
+  /// satisfy SourceLinkConcept.
+  FASERSourceLink() = default;
+  FASERSourceLink(const FASERSourceLink&) = default;
+  FASERSourceLink(FASERSourceLink&&) = default;
+  FASERSourceLink& operator=(const FASERSourceLink&) = default;
+  FASERSourceLink& operator=(FASERSourceLink&&) = default;
+
+  /// Access the geometry identifier.
+  constexpr Acts::GeometryIdentifier geometryId() const { return m_geometryId; }
+  /// Access the index.
+  constexpr Index index() const { return m_index; }
+  /// Access the Tracker::FaserSCT_Cluster hit
+  constexpr Tracker::FaserSCT_Cluster hit() const { return m_hit; }
+
+private:
+  Acts::GeometryIdentifier m_geometryId;
+  Index m_index;
+  Tracker::FaserSCT_Cluster m_hit;
+
+  friend constexpr bool operator==(const FASERSourceLink& lhs,
+                                   const FASERSourceLink& rhs) {
+    return (lhs.m_geometryId == rhs.m_geometryId) and
+           (lhs.m_index == rhs.m_index);
+  }
+  friend constexpr bool operator!=(const FASERSourceLink& lhs,
+                                   const FASERSourceLink& rhs) {
+    return not(lhs == rhs);
+  }
+};
+
+/// Container of index source links.
+///
+/// Since the source links provide a `.geometryId()` accessor, they can be
+/// stored in an ordered geometry container.
+using FASERSourceLinkContainer = GeometryIdMultiset<FASERSourceLink>;
+/// Accessor for the above source link container
+///
+/// It wraps up a few lookup methods to be used in the Combinatorial Kalman
+/// Filter
+using FASERSourceLinkAccessor = GeometryIdMultisetAccessor<FASERSourceLink>;
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FaserActsKalmanFilterAlg.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FaserActsKalmanFilterAlg.h
index 8e90ed858c09efc3d46985a7edb354ea2de6c280..34dd7c63e7ef5842ac1416dc30d895bef3ecf997 100755
--- a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FaserActsKalmanFilterAlg.h
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FaserActsKalmanFilterAlg.h
@@ -6,8 +6,8 @@
 #define FASERACTSKALMANFILTER_FASERACTSKALMANFILTERALG_H
 
 // ATHENA
-#include "AthenaBaseComps/AthAlgorithm.h"
 #include "AthenaBaseComps/AthReentrantAlgorithm.h"
+#include "AthenaBaseComps/AthAlgorithm.h"
 #include "GaudiKernel/ServiceHandle.h"
 #include "GaudiKernel/ITHistSvc.h"
 #include "Gaudi/Property.h"  /*no forward decl: typedef*/
@@ -24,6 +24,7 @@
 #include "GeneratorObjects/McEventCollection.h"
 #include "TrackerSimData/TrackerSimDataCollection.h"
 #include "TrkTrack/TrackCollection.h"
+#include "FaserActsKalmanFilter/TrajectoryWriterTool.h"
 
 // ACTS
 #include "Acts/MagneticField/ConstantBField.hpp"
@@ -47,6 +48,9 @@
 #include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
 #include "FaserActsKalmanFilter/IndexSourceLink.h"
 #include "FaserActsKalmanFilter/Measurement.h"
+#include "FaserActsKalmanFilter/ITrackFinderTool.h"
+//#include "FaserActsKalmanFilter/ProtoTrackWriterTool.h"
+#include "FaserActsKalmanFilter/RootTrajectoryStatesWriterTool.h"
 
 // STL
 #include <memory>
@@ -73,216 +77,59 @@ using BField_t = FASERMagneticFieldWrapper;
 //class FaserActsKalmanFilterAlg : public AthReentrantAlgorithm {
 class FaserActsKalmanFilterAlg : public AthAlgorithm {
 public:
-  FaserActsKalmanFilterAlg (const std::string& name, ISvcLocator* pSvcLocator);
+  FaserActsKalmanFilterAlg(const std::string& name, ISvcLocator* pSvcLocator);
+  virtual ~FaserActsKalmanFilterAlg() = default;
+
   StatusCode initialize() override;
-  //StatusCode execute(const EventContext& ctx) const override;
+//  StatusCode execute(const EventContext& ctx) const override;
   StatusCode execute() override;
   StatusCode finalize() override;
 
-  using FitterResult = Acts::Result<Acts::KalmanFitterResult<IndexSourceLink>>;
-  // Fit function that takes input measurements, initial trackstate and fitter
-  // options and returns some fit-specific result.
-  using FitterFunction = std::function<FitterResult(
-      const std::vector<IndexSourceLink>&,
-      const Acts::CurvilinearTrackParameters&,
-      const Acts::KalmanFitterOptions<MeasurementCalibrator, Acts::VoidOutlierFinder>&,
-      const std::vector<const Acts::Surface*>&)>;
+  using IndexedParams = std::unordered_map<size_t, Acts::BoundTrackParameters>;
+  using TrackFitterOptions =
+    Acts::KalmanFitterOptions<MeasurementCalibrator, Acts::VoidOutlierFinder,
+                              Acts::VoidReverseFilteringLogic>;
+  using TrackFitterResult =
+    Acts::Result<Acts::KalmanFitterResult<IndexSourceLink>>;
 
-  using BoundVector = Acts::ActsVector<6>;
+  using TrackParameters = Acts::CurvilinearTrackParameters;
 
-  // Create the fitter function implementation.
-  static FitterFunction
-  makeFitterFunction(
-      ActsExtrapolationDetail::VariantPropagator* varProp);
+  class TrackFitterFunction {
+  public:
+    virtual ~TrackFitterFunction() = default;
+    virtual TrackFitterResult operator()(const std::vector<IndexSourceLink>&,
+                                         const TrackParameters&,
+                                         const TrackFitterOptions&) const = 0;
+  };
 
-  virtual
-  Acts::MagneticFieldContext
-  //getMagneticFieldContext(const EventContext& ctx) const;
-  getMagneticFieldContext() const;
+  static std::shared_ptr<TrackFitterFunction> makeTrackFitterFunction(
+    std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry);
 
-  void initializeTree();
+  virtual Acts::MagneticFieldContext getMagneticFieldContext(const EventContext& ctx) const;
 
-  void fillFitResult(const Acts::GeometryContext& geoctx, const TrajectoryContainer& trajectories, const Acts::BoundTrackParameters& truthParam);
+private:
+  const FaserSCT_ID* m_idHelper {nullptr};
+  std::shared_ptr<TrackFitterFunction> m_fit;
+  std::unique_ptr<const Acts::Logger> m_logger;
 
-  // Create a track from the fitter result
-  std::unique_ptr<Trk::Track> makeTrack(Acts::GeometryContext& tgContext, FitterResult& fitResult,const SpacePointForSeedCollection*  seed_spcollection ) const;                                                                 
+  Gaudi::Property<std::string> m_actsLogging {this, "ActsLogging", "VERBOSE"};
+  std::unique_ptr<Trk::Track> makeTrack(Acts::GeometryContext& tgContext, TrackFitterResult& fitResult, std::vector<const Tracker::FaserSCT_Cluster*> clusters) const;
   const Trk::TrackParameters* ConvertActsTrackParameterToATLAS(const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext& gctx) const;
 
-  void clearTrackVariables();
-
-private:
-  const FaserSCT_ID* m_idHelper{nullptr};
-
   // Read handle for conditions object to get the field cache
   SG::ReadCondHandleKey<FaserFieldCacheCondObj> m_fieldCondObjInputKey {this, "FaserFieldCacheCondObj", "fieldCondObj", "Name of the Magnetic Field conditions object key"};
 
-  ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool{this, "ExtrapolationTool", "FaserActsExtrapolationTool"};
-
-  Gaudi::Property<std::string> m_fieldMode{this, "FieldMode", "FASER"};
-  Gaudi::Property<std::vector<double>> m_constantFieldVector{this, "ConstantFieldVector", {0, 0, 0}};
+  ToolHandle<ITrackFinderTool> m_trackFinderTool {this, "TrackFinderTool", "TruthTrackFinderTool"};
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  ToolHandle<TrajectoryWriterTool> m_trajectoryWriterTool {this, "OutputTool", "TrajectoryWriterTool"};
+  ToolHandle<RootTrajectoryStatesWriterTool> m_trajectoryStatesWriterTool {this, "RootTrajectoryStatesWriterTool", "RootTrajectoryStatesWriterTool"};
+//  ToolHandle<ProtoTrackWriterTool> m_protoTrackWriterTool {this, "ProtoTrackWriterTool", "ProtoTrackWriterTool"};
 
-  SG::ReadHandleKey<SpacePointForSeedCollection>  m_seed_spcollectionKey{this, "FaserSpacePointsSeedsName", "SpacePointForSeedCollection", "SpacePointForSeedCollection"};
-
-  SG::ReadHandleKey<McEventCollection> m_mcEventKey       { this, "McEventCollection", "BeamTruthEvent" };
+  SG::ReadHandleKey<McEventCollection> m_mcEventKey { this, "McEventCollection", "BeamTruthEvent" };
   SG::ReadHandleKey<TrackerSimDataCollection> m_sctMap {this, "TrackerSimDataCollection", "SCT_SDO_Map"};
   const TrackerDD::SCT_DetectorManager* m_detManager{nullptr};
 
-  SG::WriteHandleKey<TrackCollection> m_trackCollection { this, "FaserActsCKFTrackCollection", "FaserActsCKFTrackCollection", "Output trackcollection name" };
-
-  ServiceHandle<ITHistSvc> m_thistSvc;
-
-  TTree *m_trackTree{nullptr};
-
-  Acts::GeometryIdentifier getGeometryIdentifier(const Identifier id);
-  int getGeometryIdentifierVolume(int station);
-  int getGeometryIdentifierLayer(int layer);
-  int getGeometryIdentifierSensitive(int row, int column);
-
-  /// Acts tree values
-  int m_eventNr{0};
-  int m_trajNr{0};
-  int m_trackNr{0};
-
-  unsigned long m_t_barcode{0};  /// Truth particle barcode
-  int m_t_charge{0};             /// Truth particle charge
-  float m_t_eT{0};               /// Truth particle time on the first layer
-  float m_t_eLOC0{-99.};         /// Truth local x on the first layer
-  float m_t_eLOC1{-99.};         /// Truth local y on the first layer
-  float m_t_eTHETA{-99.};        /// Truth particle momentum theta on the first layer
-  float m_t_ePHI{-99.};          /// Truth particle momentum phi on the first layer
-  float m_t_eQOP{-99.};          /// Truth particle momentum qop on the first layer
-  float m_t_x{-99.};             /// Truth particle position x on the first layer
-  float m_t_y{-99.};             /// Truth particle position y on the first layer
-  float m_t_z{-99.};             /// Truth particle position z on the first layer
-  float m_t_px{-99.};            /// Truth particle momentum px on the first layer
-  float m_t_py{-99.};            /// Truth particle momentum py on the first layer
-  float m_t_pz{-99.};            /// Truth particle momentum pz on the first layer
-
-  int m_nHoles{0};                  /// number of holes in the track fit
-  int m_nOutliers{0};               /// number of outliers in the track fit
-  int m_nStates{0};                 /// number of all states
-  int m_nMeasurements{0};           /// number of states with measurements
-  std::vector<int> m_volumeID;      /// volume identifier
-  std::vector<int> m_layerID;       /// layer identifier
-  std::vector<int> m_moduleID;      /// surface identifier
-  std::vector<float> m_lx_hit;      /// uncalibrated measurement local x
-  std::vector<float> m_ly_hit;      /// uncalibrated measurement local y
-  std::vector<float> m_x_hit;       /// uncalibrated measurement global x
-  std::vector<float> m_y_hit;       /// uncalibrated measurement global y
-  std::vector<float> m_z_hit;       /// uncalibrated measurement global z
-  std::vector<float> m_res_x_hit;   /// hit residual x
-  std::vector<float> m_res_y_hit;   /// hit residual y
-  std::vector<float> m_err_x_hit;   /// hit err x
-  std::vector<float> m_err_y_hit;   /// hit err y
-  std::vector<float> m_pull_x_hit;  /// hit pull x
-  std::vector<float> m_pull_y_hit;  /// hit pull y
-  std::vector<int> m_dim_hit;       /// dimension of measurement
-
-  bool m_hasFittedParams{false};  /// if the track has fitted parameter
-  float m_eLOC0_fit{-99.};        /// fitted parameter eLOC_0
-  float m_eLOC1_fit{-99.};        /// fitted parameter eLOC_1
-  float m_ePHI_fit{-99.};         /// fitted parameter ePHI
-  float m_eTHETA_fit{-99.};       /// fitted parameter eTHETA
-  float m_eQOP_fit{-99.};         /// fitted parameter eQOP
-  float m_eT_fit{-99.};           /// fitted parameter eT
-  float m_err_eLOC0_fit{-99.};    /// fitted parameter eLOC_-99.err
-  float m_err_eLOC1_fit{-99.};    /// fitted parameter eLOC_1 err
-  float m_err_ePHI_fit{-99.};     /// fitted parameter ePHI err
-  float m_err_eTHETA_fit{-99.};   /// fitted parameter eTHETA err
-  float m_err_eQOP_fit{-99.};     /// fitted parameter eQOP err
-  float m_err_eT_fit{-99.};       /// fitted parameter eT err
-  float m_px_fit{-99.};           /// fitted parameter global px
-  float m_py_fit{-99.};           /// fitted parameter global py
-  float m_pz_fit{-99.};           /// fitted parameter global pz
-  float m_x_fit{-99.};            /// fitted parameter global PCA x
-  float m_y_fit{-99.};            /// fitted parameter global PCA y
-  float m_z_fit{-99.};            /// fitted parameter global PCA z
-  float m_chi2_fit{-99.};         /// fitted parameter chi2
-  float m_ndf_fit{-99.};          /// fitted parameter ndf
-  int m_charge_fit{-99};          /// fitted parameter charge
-
-  int m_nPredicted{0};                   /// number of states with predicted parameter
-  std::vector<bool> m_prt;               /// predicted status
-  std::vector<float> m_eLOC0_prt;        /// predicted parameter eLOC0
-  std::vector<float> m_eLOC1_prt;        /// predicted parameter eLOC1
-  std::vector<float> m_ePHI_prt;         /// predicted parameter ePHI
-  std::vector<float> m_eTHETA_prt;       /// predicted parameter eTHETA
-  std::vector<float> m_eQOP_prt;         /// predicted parameter eQOP
-  std::vector<float> m_eT_prt;           /// predicted parameter eT
-  std::vector<float> m_res_eLOC0_prt;    /// predicted parameter eLOC0 residual
-  std::vector<float> m_res_eLOC1_prt;    /// predicted parameter eLOC1 residual
-  std::vector<float> m_err_eLOC0_prt;    /// predicted parameter eLOC0 error
-  std::vector<float> m_err_eLOC1_prt;    /// predicted parameter eLOC1 error
-  std::vector<float> m_err_ePHI_prt;     /// predicted parameter ePHI error
-  std::vector<float> m_err_eTHETA_prt;   /// predicted parameter eTHETA error
-  std::vector<float> m_err_eQOP_prt;     /// predicted parameter eQOP error
-  std::vector<float> m_err_eT_prt;       /// predicted parameter eT error
-  std::vector<float> m_pull_eLOC0_prt;   /// predicted parameter eLOC0 pull
-  std::vector<float> m_pull_eLOC1_prt;   /// predicted parameter eLOC1 pull
-  std::vector<float> m_x_prt;            /// predicted global x
-  std::vector<float> m_y_prt;            /// predicted global y
-  std::vector<float> m_z_prt;            /// predicted global z
-  std::vector<float> m_px_prt;           /// predicted momentum px
-  std::vector<float> m_py_prt;           /// predicted momentum py
-  std::vector<float> m_pz_prt;           /// predicted momentum pz
-  std::vector<float> m_eta_prt;          /// predicted momentum eta
-  std::vector<float> m_pT_prt;           /// predicted momentum pT
-
-  int m_nFiltered{0};                    /// number of states with filtered parameter
-  std::vector<bool> m_flt;               /// filtered status
-  std::vector<float> m_eLOC0_flt;        /// filtered parameter eLOC0
-  std::vector<float> m_eLOC1_flt;        /// filtered parameter eLOC1
-  std::vector<float> m_ePHI_flt;         /// filtered parameter ePHI
-  std::vector<float> m_eTHETA_flt;       /// filtered parameter eTHETA
-  std::vector<float> m_eQOP_flt;         /// filtered parameter eQOP
-  std::vector<float> m_eT_flt;           /// filtered parameter eT
-  std::vector<float> m_res_eLOC0_flt;    /// filtered parameter eLOC0 residual
-  std::vector<float> m_res_eLOC1_flt;    /// filtered parameter eLOC1 residual
-  std::vector<float> m_err_eLOC0_flt;    /// filtered parameter eLOC0 error
-  std::vector<float> m_err_eLOC1_flt;    /// filtered parameter eLOC1 error
-  std::vector<float> m_err_ePHI_flt;     /// filtered parameter ePHI error
-  std::vector<float> m_err_eTHETA_flt;   /// filtered parameter eTHETA error
-  std::vector<float> m_err_eQOP_flt;     /// filtered parameter eQOP error
-  std::vector<float> m_err_eT_flt;       /// filtered parameter eT error
-  std::vector<float> m_pull_eLOC0_flt;   /// filtered parameter eLOC0 pull
-  std::vector<float> m_pull_eLOC1_flt;   /// filtered parameter eLOC1 pull
-  std::vector<float> m_x_flt;            /// filtered global x
-  std::vector<float> m_y_flt;            /// filtered global y
-  std::vector<float> m_z_flt;            /// filtered global z
-  std::vector<float> m_px_flt;           /// filtered momentum px
-  std::vector<float> m_py_flt;           /// filtered momentum py
-  std::vector<float> m_pz_flt;           /// filtered momentum pz
-  std::vector<float> m_eta_flt;          /// filtered momentum eta
-  std::vector<float> m_pT_flt;           /// filtered momentum pT
-  std::vector<float> m_chi2;             /// chisq from filtering
-
-  int m_nSmoothed{0};                    /// number of states with smoothed parameter
-  std::vector<bool> m_smt;               /// smoothed status
-  std::vector<float> m_eLOC0_smt;        /// smoothed parameter eLOC0
-  std::vector<float> m_eLOC1_smt;        /// smoothed parameter eLOC1
-  std::vector<float> m_ePHI_smt;         /// smoothed parameter ePHI
-  std::vector<float> m_eTHETA_smt;       /// smoothed parameter eTHETA
-  std::vector<float> m_eQOP_smt;         /// smoothed parameter eQOP
-  std::vector<float> m_eT_smt;           /// smoothed parameter eT
-  std::vector<float> m_res_eLOC0_smt;    /// smoothed parameter eLOC0 residual
-  std::vector<float> m_res_eLOC1_smt;    /// smoothed parameter eLOC1 residual
-  std::vector<float> m_err_eLOC0_smt;    /// smoothed parameter eLOC0 error
-  std::vector<float> m_err_eLOC1_smt;    /// smoothed parameter eLOC1 error
-  std::vector<float> m_err_ePHI_smt;     /// smoothed parameter ePHI error
-  std::vector<float> m_err_eTHETA_smt;   /// smoothed parameter eTHETA error
-  std::vector<float> m_err_eQOP_smt;     /// smoothed parameter eQOP error
-  std::vector<float> m_err_eT_smt;       /// smoothed parameter eT error
-  std::vector<float> m_pull_eLOC0_smt;   /// smoothed parameter eLOC0 pull
-  std::vector<float> m_pull_eLOC1_smt;   /// smoothed parameter eLOC1 pull
-  std::vector<float> m_x_smt;            /// smoothed global x
-  std::vector<float> m_y_smt;            /// smoothed global y
-  std::vector<float> m_z_smt;            /// smoothed global z
-  std::vector<float> m_px_smt;           /// smoothed momentum px
-  std::vector<float> m_py_smt;           /// smoothed momentum py
-  std::vector<float> m_pz_smt;           /// smoothed momentum pz
-  std::vector<float> m_eta_smt;          /// smoothed momentum eta
-  std::vector<float> m_pT_smt;           /// smoothed momentum pT
-
+  SG::WriteHandleKey<TrackCollection> m_trackCollection { this, "FaserActsKFTrackCollection", "FaserActsKFTrackCollection", "Output track collection" };
 };
 
 #endif
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h
index 35baa239600d5f273dd91c2145eecfd8e1c8b52a..06924bed8e4adbe304697a4f71019d9134be72f8 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h
@@ -6,82 +6,88 @@
 
 #pragma once
 
-#include <unordered_map>
-#include <utility>
-
-// ACTS
 #include "Acts/EventData/MultiTrajectory.hpp"
 #include "Acts/EventData/TrackParameters.hpp"
-
-// PACKAGE
 #include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include <unordered_map>
+#include <utility>
 
-using IndexedParams = std::unordered_map<size_t, Acts::BoundTrackParameters>;
-
-struct FaserActsRecMultiTrajectory
-{
+/// Store reconstructed trajectories from track finding/fitting.
+///
+/// It contains a MultiTrajectory with a vector of entry indices for
+/// individual trajectories, and a map of fitted parameters indexed by the
+/// entry index. In case of track fitting, there is at most one trajectory
+/// in the MultiTrajectory; In case of track finding, there could be
+/// multiple trajectories in the MultiTrajectory.
+struct FaserActsRecMultiTrajectory {
  public:
-  FaserActsRecMultiTrajectory() = default;
+  /// (Reconstructed) trajectory with multiple states.
+  using MultiTrajectory = ::Acts::MultiTrajectory<IndexSourceLink>;
+  /// Fitted parameters identified by indices in the multi trajectory.
+  using IndexedParams = std::unordered_map<size_t, Acts::BoundTrackParameters>;
 
-  FaserActsRecMultiTrajectory(const Acts::MultiTrajectory<IndexSourceLink>& multiTraj,
-                     const std::vector<size_t>& tTips,
-                     const IndexedParams& parameters)
+  /// Default construct an empty object. Required for container compatibility
+  /// and to signal an error.
+  FaserActsRecMultiTrajectory() = default;
+  /// Construct from fitted multi trajectory and parameters.
+  ///
+  /// @param multiTraj The multi trajectory
+  /// @param tTips Tip indices that identify valid trajectories
+  /// @param parameters Fitted track parameters indexed by trajectory index
+  FaserActsRecMultiTrajectory(const MultiTrajectory& multiTraj,
+                              const std::vector<size_t>& tTips,
+                              const IndexedParams& parameters)
       : m_multiTrajectory(multiTraj),
         m_trackTips(tTips),
         m_trackParameters(parameters) {}
 
-  FaserActsRecMultiTrajectory(const FaserActsRecMultiTrajectory& rhs)
-      : m_multiTrajectory(rhs.m_multiTrajectory),
-        m_trackTips(rhs.m_trackTips),
-        m_trackParameters(rhs.m_trackParameters) {}
-
-  FaserActsRecMultiTrajectory(FaserActsRecMultiTrajectory&& rhs)
-      : m_multiTrajectory(std::move(rhs.m_multiTrajectory)),
-        m_trackTips(std::move(rhs.m_trackTips)),
-        m_trackParameters(std::move(rhs.m_trackParameters)) {}
-
-  ~FaserActsRecMultiTrajectory() = default;
-
-  FaserActsRecMultiTrajectory& operator=(const FaserActsRecMultiTrajectory& rhs) {
-    m_multiTrajectory = rhs.m_multiTrajectory;
-    m_trackTips = rhs.m_trackTips;
-    m_trackParameters = rhs.m_trackParameters;
-    return *this;
-  }
+  /// Return true if there exists no valid trajectory.
+  bool empty() const { return m_trackTips.empty(); }
 
-  FaserActsRecMultiTrajectory& operator=(FaserActsRecMultiTrajectory&& rhs) {
-    m_multiTrajectory = std::move(rhs.m_multiTrajectory);
-    m_trackTips = std::move(rhs.m_trackTips);
-    m_trackParameters = std::move(rhs.m_trackParameters);
-    return *this;
-  }
+  /// Access the underlying multi trajectory.
+  const MultiTrajectory& multiTrajectory() const { return m_multiTrajectory; }
 
-  bool hasTrajectory(const size_t& entryIndex) const {
-    return std::count(m_trackTips.begin(), m_trackTips.end(), entryIndex) > 0;
-  }
+  /// Access the tip indices that identify valid trajectories.
+  const std::vector<size_t>& tips() const { return m_trackTips; }
 
-  bool hasTrackParameters(const size_t& entryIndex) const {
-    return m_trackParameters.count(entryIndex) > 0;
+  /// Check if a trajectory exists for the given index.
+  ///
+  /// @param entryIndex The trajectory entry index
+  /// @return Whether there is trajectory with provided entry index
+  bool hasTrajectory(size_t entryIndex) const {
+    return (0 < std::count(m_trackTips.begin(), m_trackTips.end(), entryIndex));
   }
 
-  std::pair<std::vector<size_t>, Acts::MultiTrajectory<IndexSourceLink>>
-  trajectory() const {
-    return std::make_pair(m_trackTips, m_multiTrajectory);
+  /// Check if fitted track parameters exists for the given index.
+  ///
+  /// @param entryIndex The trajectory entry index
+  /// @return Whether having fitted track parameters or not
+  bool hasTrackParameters(size_t entryIndex) const {
+    return (0 < m_trackParameters.count(entryIndex));
   }
 
-  const Acts::BoundTrackParameters& trackParameters(const size_t& entryIndex) const {
+  /// Access the fitted track parameters for the given index.
+  ///
+  /// @param entryIndex The trajectory entry index
+  /// @return The fitted track parameters of the trajectory
+  const Acts::BoundTrackParameters& trackParameters(size_t entryIndex) const {
     auto it = m_trackParameters.find(entryIndex);
-    if (it != m_trackParameters.end()) {
-      return it->second;
-    } else {
+    if (it == m_trackParameters.end()) {
       throw std::runtime_error(
           "No fitted track parameters for trajectory with entry index = " +
           std::to_string(entryIndex));
     }
+    return it->second;
   }
 
- private:
-  Acts::MultiTrajectory<IndexSourceLink> m_multiTrajectory;
+private:
+  // The multiTrajectory
+  MultiTrajectory m_multiTrajectory;
+  // The entry indices of trajectories stored in multiTrajectory
   std::vector<size_t> m_trackTips = {};
+  // The fitted parameters at the provided surface for individual trajectories
   IndexedParams m_trackParameters = {};
 };
+
+/// Container for multiple trajectories.
+using TrajectoriesContainer = std::vector<FaserActsRecMultiTrajectory>;
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackFinderTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackFinderTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..6fbc80c422cb3d83122a7c9ec9524da527221d15
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackFinderTool.h
@@ -0,0 +1,27 @@
+#ifndef FASERACTSKALMANFILTER_ITRACKFINDERTOOL_H
+#define FASERACTSKALMANFILTER_ITRACKFINDERTOOL_H
+
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/IAlgTool.h"
+#include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include "FaserActsKalmanFilter/IdentifierLink.h"
+#include "FaserActsKalmanFilter/Measurement.h"
+#include "Acts/EventData/TrackParameters.hpp"
+
+class ITrackFinderTool : virtual public IAlgTool {
+public:
+  DeclareInterfaceID(ITrackFinderTool, 1, 0);
+
+  // TODO use Acts::BoundTrackParameters instead?
+  virtual StatusCode run() = 0;
+  virtual const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> initialTrackParameters() const = 0;
+  virtual const std::shared_ptr<const Acts::Surface> initialSurface() const = 0;
+  virtual const std::shared_ptr<std::vector<std::vector<IndexSourceLink>>> sourceLinks() const = 0;
+  virtual const std::shared_ptr<std::vector<IdentifierLink>> idLinks() const = 0;
+  virtual const std::shared_ptr<std::vector<std::vector<Measurement>>> measurements() const = 0;
+  virtual const std::shared_ptr<std::vector<std::vector<Tracker::FaserSCT_SpacePoint>>> spacePoints() const = 0;
+  virtual const std::shared_ptr<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>> clusters() const = 0;
+};
+
+#endif  // FASERACTSKALMANFILTER_ITRACKFINDERTOOL_H
\ No newline at end of file
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackSeedTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackSeedTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..34b5c23c2516ec620a73ab74d192e7f76ae278fc
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ITrackSeedTool.h
@@ -0,0 +1,25 @@
+#ifndef FASERACTSKALMANFILTER_ITRACKSEEDTOOL_H
+#define FASERACTSKALMANFILTER_ITRACKSEEDTOOL_H
+
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/IAlgTool.h"
+#include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include "FaserActsKalmanFilter/IdentifierLink.h"
+#include "FaserActsKalmanFilter/Measurement.h"
+#include "Acts/EventData/TrackParameters.hpp"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+
+class ITrackSeedTool : virtual public IAlgTool {
+public:
+  DeclareInterfaceID(ITrackSeedTool, 1, 0);
+
+  virtual StatusCode run() = 0;
+  virtual const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> initialTrackParameters() const = 0;
+  virtual const std::shared_ptr<const Acts::Surface> initialSurface() const = 0;
+  virtual const std::shared_ptr<std::vector<IndexSourceLink>> sourceLinks() const = 0;
+  virtual const std::shared_ptr<IdentifierLink> idLinks() const = 0;
+  virtual const std::shared_ptr<std::vector<Measurement>> measurements() const = 0;
+  virtual const std::shared_ptr<std::vector<const Tracker::FaserSCT_Cluster*>> clusters() const = 0;
+};
+
+#endif  // FASERACTSKALMANFILTER_ITRACKSEEDTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IdentifierLink.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IdentifierLink.h
new file mode 100644
index 0000000000000000000000000000000000000000..5d236afec358b683ee5812889656d353a62d4924
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IdentifierLink.h
@@ -0,0 +1,10 @@
+#ifndef FASERACTSKALMANFILTER_IDENTIFIERLINK_H
+#define FASERACTSKALMANFILTER_IDENTIFIERLINK_H
+
+#include "Identifier/Identifier.h"
+
+using Index = uint32_t;
+
+using IdentifierLink = std::map<Index, Identifier>;
+
+#endif  // FASERACTSKALMANFILTER_IDENTIFIERLINK_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IndexSourceLink.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IndexSourceLink.h
index 99d101519030fca1d4fd7904bc7549b22a39a6c2..79973020943627dc32655fecb3a24437bb0c1284 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IndexSourceLink.h
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/IndexSourceLink.h
@@ -15,6 +15,7 @@
 #include <boost/container/flat_map.hpp>
 
 #include "FaserActsKalmanFilter/FaserActsGeometryContainers.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
 
 /// Index type to reference elements in a container.
 ///
@@ -34,10 +35,10 @@ using Index = uint32_t;
 class IndexSourceLink final {
 public:
     /// Construct from geometry identifier and index.
-    constexpr IndexSourceLink(Acts::GeometryIdentifier gid, Index idx)
-            : m_geometryId(gid), m_index(idx) {}
+  IndexSourceLink(Acts::GeometryIdentifier gid, Index idx, const Tracker::FaserSCT_Cluster* hit)
+  : m_geometryId(gid), m_index(idx), m_hit(hit) {}
 
-    // Construct an invalid source link. Must be default constructible to
+  // Construct an invalid source link. Must be default constructible to
     /// satisfy SourceLinkConcept.
     IndexSourceLink() = default;
     IndexSourceLink(const IndexSourceLink&) = default;
@@ -49,10 +50,13 @@ public:
     constexpr Acts::GeometryIdentifier geometryId() const { return m_geometryId; }
     /// Access the index.
     constexpr Index index() const { return m_index; }
+    /// Access the Tracker::FaserSCT_Cluster hit
+    constexpr const Tracker::FaserSCT_Cluster* hit() const { return m_hit; }
 
 private:
     Acts::GeometryIdentifier m_geometryId;
     Index m_index;
+    const Tracker::FaserSCT_Cluster* m_hit;
 
     friend constexpr bool operator==(const IndexSourceLink& lhs,
                                      const IndexSourceLink& rhs) {
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/MultiTrackFinderTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/MultiTrackFinderTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..56e5db2666157c330e91258776b3a6b34af44278
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/MultiTrackFinderTool.h
@@ -0,0 +1,232 @@
+#ifndef FASERACTSKALMANFILTER_MULTITRACKFINDERTOOL_H
+#define FASERACTSKALMANFILTER_MULTITRACKFINDERTOOL_H
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/EventContext.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/ReadHandleKey.h"
+#include <string>
+#include <vector>
+
+#include "TrackerSpacePoint/FaserSCT_SpacePointContainer.h"
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/ITrackFinderTool.h"
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "TrkTrack/TrackCollection.h"
+
+class FaserSCT_ID;
+namespace TrackerDD {
+class SCT_DetectorManager;
+}
+
+
+class MultiTrackFinderTool : public extends<AthAlgTool, ITrackFinderTool> {
+public:
+  MultiTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent);
+  virtual ~MultiTrackFinderTool() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+  virtual StatusCode run() override;
+
+  virtual const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> initialTrackParameters() const override;
+  virtual const std::shared_ptr<const Acts::Surface> initialSurface() const override;
+  virtual const std::shared_ptr<std::vector<std::vector<IndexSourceLink>>> sourceLinks() const override;
+  virtual const std::shared_ptr<std::vector<IdentifierLink>> idLinks() const override;
+  virtual const std::shared_ptr<std::vector<std::vector<Measurement>>> measurements() const override;
+  virtual const std::shared_ptr<std::vector<std::vector<Tracker::FaserSCT_SpacePoint>>> spacePoints() const override;
+  virtual const std::shared_ptr<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>> clusters() const override;
+
+  struct Tracklet {
+  public:
+    Tracklet(const std::vector<Identifier>& ids,
+             const std::vector<Acts::GeometryIdentifier>& geoIds,
+             const std::vector<double>& positions,
+             const Amg::Vector3D& fitPosition,
+             const std::vector<const Tracker::FaserSCT_Cluster*>& clusters)
+        : m_ids(ids), m_geoIds(geoIds), m_positions(positions), m_fitPosition(fitPosition), m_clusters(clusters) {};
+
+    std::vector<Identifier> ids() const { return m_ids; };
+    std::vector<Acts::GeometryIdentifier> geoIds() const { return m_geoIds; }
+    std::vector<double> clusterPositions() const { return m_positions; }
+    const std::vector<const Tracker::FaserSCT_Cluster*>& clusters() const { return  m_clusters; }
+    Amg::Vector3D position() const { return  m_fitPosition; }
+
+  private:
+    std::vector<Identifier> m_ids;
+    std::vector<Acts::GeometryIdentifier> m_geoIds;
+    std::vector<double> m_positions;
+    Amg::Vector3D m_fitPosition;
+    std::vector<const Tracker::FaserSCT_Cluster*> m_clusters;
+  };
+
+  struct ProtoTrack {
+  public:
+    ProtoTrack(const MultiTrackFinderTool::Tracklet& t1,
+               const MultiTrackFinderTool::Tracklet& t2,
+               const MultiTrackFinderTool::Tracklet& t3)
+        : m_t1(t1), m_t2(t2), m_t3(t3) {}
+
+    Acts::CurvilinearTrackParameters initialTrackParameters(
+        double covLoc0, double covLoc1, double covPhi, double covTheta, double covQOverP, double covTime) const {
+      Acts::Vector3 dir = m_t2.position() - m_t1.position();
+      Acts::Vector3 pos = m_t1.position() - m_t1.position().z()/dir.z() * dir;
+      Acts::Vector4 pos4 {pos.x(), pos.y(), pos.z(), 0};
+      auto [abs_momentum, charge] = momentum({{1, m_t1.position()}, {2, m_t2.position()}, {3, m_t3.position()}});
+
+      Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Zero();
+      cov(Acts::eBoundLoc0, Acts::eBoundLoc0) = covLoc0;
+      cov(Acts::eBoundLoc1, Acts::eBoundLoc1) = covLoc1;
+      cov(Acts::eBoundPhi, Acts::eBoundPhi) = covPhi;
+      cov(Acts::eBoundTheta, Acts::eBoundTheta) = covTheta;
+      cov(Acts::eBoundQOverP, Acts::eBoundQOverP) = covQOverP;
+      cov(Acts::eBoundTime, Acts::eBoundTime) = covTime;
+
+      Acts::CurvilinearTrackParameters params =
+          Acts::CurvilinearTrackParameters(pos4, dir, abs_momentum, charge, cov);
+      return params;
+    }
+
+     std::tuple<std::vector<Measurement>, std::vector<IndexSourceLink>, std::map<Index, Identifier>, std::vector<const Tracker::FaserSCT_Cluster*>> run() const {
+       const int kSize = 1;
+       using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, kSize>;
+       std::array<Acts::BoundIndices, kSize> Indices = {Acts::eBoundLoc0};
+       std::vector<IndexSourceLink> sourceLinks;
+       std::map<Index, Identifier> idLinks;
+       std::vector<Measurement> measurements;
+       std::vector<const Tracker::FaserSCT_Cluster*> clusters;
+       clusters.reserve(m_t1.clusters().size() + m_t2.clusters().size() + m_t3.clusters().size());
+       clusters.insert(clusters.end(), m_t1.clusters().begin(), m_t1.clusters().end());
+       clusters.insert(clusters.end(), m_t2.clusters().begin(), m_t2.clusters().end());
+       clusters.insert(clusters.end(), m_t3.clusters().begin(), m_t3.clusters().end());
+
+       for (const MultiTrackFinderTool::Tracklet& tracklet : {m_t1, m_t2, m_t3}) {
+         // FIXME check that ids, geoIds and positions have the same size
+         auto ids = tracklet.ids();
+         auto geoIds = tracklet.geoIds();
+         auto positions = tracklet.clusterPositions();
+         for (size_t i = 0; i < tracklet.ids().size(); ++i) {
+           idLinks[measurements.size()] = ids[i];
+           IndexSourceLink sourceLink(geoIds[i], measurements.size());
+           Eigen::Matrix<double, 1, 1> clusterPos {positions[i]};
+           Eigen::Matrix<double, 1, 1> clusterCov {0.04 * 0.04,};
+           ThisMeasurement meas(sourceLink, Indices, clusterPos, clusterCov);
+           sourceLinks.push_back(sourceLink);
+           measurements.emplace_back(std::move(meas));
+         }
+       }
+       return std::make_tuple(measurements, sourceLinks, idLinks, clusters);
+    }
+
+    double chi2() const {
+      return calc_chi2({m_t1.position(), m_t2.position(), m_t3.position()});
+    }
+
+  private:
+    Tracklet m_t1, m_t2, m_t3;
+
+    static std::pair<double, double> momentum(const std::map<int, Amg::Vector3D>& pos, double B=0.57) {
+      Acts::Vector3 vec_l = pos.at(3) - pos.at(1);
+      double abs_l = std::sqrt(vec_l.y() * vec_l.y() + vec_l.z() * vec_l.z());
+      double t = (pos.at(2).z() - pos.at(1).z()) / (pos.at(3).z() - pos.at(1).z());
+      Acts::Vector3 vec_m = pos.at(1) + t * vec_l;
+      Acts::Vector3 vec_s = pos.at(2) - vec_m;
+      double abs_s = std::sqrt(vec_s.y() * vec_s.y() + vec_s.z() * vec_s.z());
+      double p_yz = 0.3 * abs_l * abs_l * B / (8 * abs_s * 1000);
+      double charge = vec_s.y() < 0 ? 1 : -1;
+      return std::make_pair(p_yz, charge);
+    }
+
+    static std::pair<Acts::Vector3, Acts::Vector3> linear_fit(const std::vector<Acts::Vector3>& hits) {
+      size_t n_hits = hits.size();
+      Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> centers(n_hits, 3);
+      for (size_t i = 0; i < n_hits; ++i) centers.row(i) = hits[i];
+      Acts::Vector3 origin = centers.colwise().mean();
+      Eigen::MatrixXd centered = centers.rowwise() - origin.transpose();
+      Eigen::MatrixXd cov = centered.adjoint() * centered;
+      Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eig(cov);
+      Acts::Vector3 axis = eig.eigenvectors().col(2).normalized();
+      return std::make_pair(origin, axis);
+    }
+
+    double calc_chi2(const std::vector<Acts::Vector3>& hits) const {
+      auto [origin, axis] = linear_fit(hits);
+      double chi2 = 0;
+      for (const Acts::Vector3& hit : hits) {
+        Acts::Vector3 exp = origin + (hit.z() - origin.z()) * axis/axis.z();
+        chi2 += (exp.x() - hit.x()) * (exp.x() - hit.x()) + (exp.y() - hit.y()) * (exp.y() - hit.y());
+      }
+      return chi2;
+    }
+  };
+
+  struct sort_chi2 {
+    inline bool operator() (const ProtoTrack& track1, const ProtoTrack& track2) {
+      return (track1.chi2() < track2.chi2());
+    }
+  };
+
+private:
+  std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> m_initialTrackParameters;
+  std::shared_ptr<const Acts::Surface> m_initialSurface;
+  std::shared_ptr<std::vector<std::vector<IndexSourceLink>>> m_sourceLinks {};
+  std::shared_ptr<std::vector<IdentifierLink>> m_idLinks {};
+  std::shared_ptr<std::vector<std::vector<Measurement>>> m_measurements {};
+  std::shared_ptr<std::vector<std::vector<Tracker::FaserSCT_SpacePoint>>> m_spacePoints {};
+  std::shared_ptr<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>> m_clusters {};
+
+  const FaserSCT_ID* m_idHelper {nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detManager {nullptr};
+
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {
+      this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  SG::ReadHandleKey<TrackCollection> m_trackCollection {
+      this, "TrackCollection", "SegmentFit", "Input track collection name" };
+
+  // covariance of the initial parameters
+  Gaudi::Property<double> m_covLoc0 {this, "covLoc0", 1};
+  Gaudi::Property<double> m_covLoc1 {this, "covLoc1", 1};
+  Gaudi::Property<double> m_covPhi {this, "covPhi", 1};
+  Gaudi::Property<double> m_covTheta {this, "covTheta", 1};
+  Gaudi::Property<double> m_covQOverP {this, "covQOverP", 1};
+  Gaudi::Property<double> m_covTime {this, "covTime", 1};
+};
+
+inline const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>>
+MultiTrackFinderTool::initialTrackParameters() const {
+  return m_initialTrackParameters;
+}
+
+inline const std::shared_ptr<const Acts::Surface>
+MultiTrackFinderTool::initialSurface() const {
+  return m_initialSurface;
+}
+
+inline const std::shared_ptr<std::vector<std::vector<IndexSourceLink>>>
+MultiTrackFinderTool::sourceLinks() const {
+  return m_sourceLinks;
+}
+
+inline const std::shared_ptr<std::vector<IdentifierLink>>
+MultiTrackFinderTool::idLinks() const {
+  return m_idLinks;
+}
+
+inline const std::shared_ptr<std::vector<std::vector<Measurement>>>
+MultiTrackFinderTool::measurements() const {
+  return m_measurements;
+}
+
+inline const std::shared_ptr<std::vector<std::vector<Tracker::FaserSCT_SpacePoint>>>
+MultiTrackFinderTool::spacePoints() const {
+  return m_spacePoints;
+}
+
+inline const std::shared_ptr<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>>
+MultiTrackFinderTool::clusters() const {
+  return m_clusters;
+}
+
+#endif // FASERACTSKALMANFILTER_MULTITRACKFINDERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/MyAmbiguitySolver.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/MyAmbiguitySolver.h
new file mode 100644
index 0000000000000000000000000000000000000000..47257befa2fcbd70da95d1ee88bd41d0fdabeb2a
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/MyAmbiguitySolver.h
@@ -0,0 +1,80 @@
+#ifndef FASERACTSKALMANFILTER_AMBIGUITYSOLVER_H
+#define FASERACTSKALMANFILTER_AMBIGUITYSOLVER_H
+
+#include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp"
+#include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
+
+using CombinatorialKalmanFilterResult = Acts::CombinatorialKalmanFilterResult<IndexSourceLink>;
+using TrackFitterResult = Acts::Result<CombinatorialKalmanFilterResult>;
+using TrackFinderResult = std::vector<TrackFitterResult>;
+
+
+size_t numberMeasurements(const CombinatorialKalmanFilterResult& ckfResult) {
+  auto traj = FaserActsRecMultiTrajectory(ckfResult.fittedStates, ckfResult.lastMeasurementIndices, ckfResult.fittedParameters);
+  const auto& mj = traj.multiTrajectory();
+  const auto& trackTips = traj.tips();
+  size_t maxMeasurements = 0;
+  for (const auto& trackTip : trackTips) {
+    auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip);
+    size_t nMeasurements = trajState.nMeasurements;
+    if (nMeasurements > maxMeasurements) {
+      maxMeasurements = nMeasurements;
+    }
+    std::cout << "# measurements: " << trajState.nMeasurements << std::endl;
+  }
+  return maxMeasurements;
+}
+
+int countSharedHits(const CombinatorialKalmanFilterResult& result1, const CombinatorialKalmanFilterResult& result2) {
+  int count = 0;
+  std::vector<size_t> hitIndices {};
+
+  for (auto measIndex : result1.lastMeasurementIndices) {
+    result1.fittedStates.visitBackwards(measIndex, [&](const auto& state) {
+      if (not state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag))
+        return;
+      size_t hitIndex = state.uncalibrated().index();
+      hitIndices.emplace_back(hitIndex);
+    });
+  }
+
+  for (auto measIndex : result2.lastMeasurementIndices) {
+    result2.fittedStates.visitBackwards(measIndex, [&](const auto& state) {
+      if (not state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag))
+        return;
+      size_t hitIndex = state.uncalibrated().index();
+      if (std::find(hitIndices.begin(), hitIndices.end(), hitIndex) != hitIndices.end()) {
+        count += 1;
+      }
+    });
+  }
+  return count;
+}
+
+
+std::pair<int, int> solveAmbiguity(TrackFinderResult& results, size_t minMeasurements = 13) {
+  std::map<std::pair<size_t, size_t>, size_t> trackPairs {};
+  for (size_t i = 0; i < results.size(); ++i) {
+    // if (not results.at(i).ok()) continue;
+    // if (numberMeasurements(results.at(i).value()) < minMeasurements) continue;
+    for (size_t j = i+1; j < results.size(); ++j) {
+      // if (not results.at(j).ok()) continue;
+      // if (numberMeasurements(results.at(j).value()) < minMeasurements) continue;
+      int n = countSharedHits(results.at(i).value(), results.at(j).value());
+      trackPairs[std::make_pair(i, j)] = n;
+    }
+  }
+
+  std::pair<size_t, size_t> bestTrackPair;
+  size_t minSharedHits = std::numeric_limits<size_t>::max();
+  for (const auto& trackPair : trackPairs) {
+    if (trackPair.second < minSharedHits) {
+      minSharedHits = trackPair.second;
+      bestTrackPair = trackPair.first;
+    }
+  }
+
+  return bestTrackPair;
+}
+
+#endif //FASERACTSKALMANFILTER_AMBIGUITYSOLVER_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/PerformanceWriterTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/PerformanceWriterTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..037a7da407d4c09d2e4cd20cba31731af455f52d
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/PerformanceWriterTool.h
@@ -0,0 +1,50 @@
+#ifndef FASERACTSKALMANFILTER_PERFORMANCEWRITER_H
+#define FASERACTSKALMANFILTER_PERFORMANCEWRITER_H
+
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "FaserActsKalmanFilter/ResPlotTool.h"
+#include "FaserActsKalmanFilter/EffPlotTool.h"
+#include "FaserActsKalmanFilter/SummaryPlotTool.h"
+#include "FaserActsGeometryInterfaces/IFaserActsExtrapolationTool.h"
+#include "TrackerSimData/TrackerSimDataCollection.h"
+#include "GeneratorObjects/McEventCollection.h"
+#include "Acts/Geometry/GeometryContext.hpp"
+class TFile;
+struct FaserActsRecMultiTrajectory;
+using TrajectoriesContainer = std::vector<FaserActsRecMultiTrajectory>;
+
+class PerformanceWriterTool : public AthAlgTool {
+public:
+  PerformanceWriterTool(const std::string& type, const std::string& name, const IInterface* parent);
+  ~PerformanceWriterTool() override =  default;
+
+  StatusCode initialize() override;
+  StatusCode finalize() override;
+
+  StatusCode write(const Acts::GeometryContext& geoContext, const TrajectoriesContainer& trajectories);
+
+private:
+  std::unique_ptr<const Acts::BoundTrackParameters> extrapolateToReferenceSurface(
+      const EventContext& ctx, const HepMC::GenParticle* particle) const;
+  SG::ReadHandleKey<TrackerSimDataCollection> m_simDataCollectionKey {
+      this, "TrackerSimDataCollection", "SCT_SDO_Map"};
+  SG::ReadHandleKey<McEventCollection> m_mcEventCollectionKey {
+      this, "McEventCollection", "BeamTruthEvent"};
+  ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool {
+      this, "ExtrapolationTool", "FaserActsExtrapolationTool"};
+  Gaudi::Property<std::string> m_filePath{this, "FilePath", "performance_ckf.root"};
+  TFile* m_outputFile{nullptr};
+
+  /// Plot tool for residuals and pulls.
+  ResPlotTool m_resPlotTool;
+  ResPlotTool::ResPlotCache m_resPlotCache;
+  /// Plot tool for efficiency
+  EffPlotTool m_effPlotTool;
+  EffPlotTool::EffPlotCache m_effPlotCache;
+  /// Plot tool for track hit info
+  SummaryPlotTool m_summaryPlotTool;
+  SummaryPlotTool::SummaryPlotCache m_summaryPlotCache;
+};
+
+#endif  // FASERACTSKALMANFILTER_PERFORMANCEWRITER_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/PlotHelpers.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/PlotHelpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..c3e8a1caf22d1debbea709c8fbbfe3c8a1b695c2
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/PlotHelpers.h
@@ -0,0 +1,113 @@
+#ifndef FASERACTSKALMANFILTER_PLOTHELPERS_H
+#define FASERACTSKALMANFILTER_PLOTHELPERS_H
+
+#include "TEfficiency.h"
+#include "TFitResult.h"
+#include "TFitResultPtr.h"
+#include "TH1F.h"
+#include "TH2F.h"
+#include "TProfile.h"
+#include <string>
+
+namespace PlotHelpers {
+
+struct Binning {
+  Binning() = default;
+
+  Binning(std::string bTitle, int bins, float bMin, float bMax)
+      : title(bTitle), nBins(bins), min(bMin), max(bMax) {};
+
+  std::string title;  ///< title to be displayed
+  int nBins;          ///< number of bins
+  float min;          ///< minimum value
+  float max;          ///< maximum value
+};
+
+
+/// @brief book a 1D histogram
+/// @param histName the name of histogram
+/// @param histTitle the title of histogram
+/// @param varBinning the binning info of variable
+/// @return histogram pointer
+TH1F *bookHisto(const char *histName, const char *histTitle,
+                const Binning &varBinning);
+
+/// @brief book a 2D histogram
+/// @param histName the name of histogram
+/// @param histTitle the title of histogram
+/// @param varXBinning the binning info of variable at x axis
+/// @param varYBinning the binning info of variable at y axis
+/// @return histogram pointer
+TH2F* bookHisto(const char* histName, const char* histTitle,
+                const Binning& varXBinning, const Binning& varYBinning);
+
+/// @brief fill a 1D histogram
+/// @param hist histogram to fill
+/// @param value value to fill
+/// @param weight weight to fill
+void fillHisto(TH1F* hist, float value, float weight = 1.0);
+
+/// @brief fill a 2D histogram
+/// @param hist histogram to fill
+/// @param xValue x value to fill
+/// @param yValue y value to fill
+/// @param weight weight to fill
+void fillHisto(TH2F* hist, float xValue, float yValue, float weight = 1.0);
+
+/// @brief extract details, i.e. mean and width of a 1D histogram and fill
+/// them into histograms
+/// @param inputHist histogram to investigate
+/// @param j  which bin number of meanHist and widthHist to fill
+/// @param meanHist histogram to fill the mean value of inputHist
+/// @param widthHist  histogram to fill the width value of inputHist
+///
+void anaHisto(TH1D* inputHist, int j, TH1F* meanHist, TH1F* widthHist);
+
+/// @brief book a 1D efficiency plot
+/// @param effName the name of plot
+/// @param effTitle the title of plot
+/// @param varBinning the binning info of variable
+/// @return TEfficiency pointer
+TEfficiency* bookEff(const char* effName, const char* effTitle, const Binning& varBinning);
+
+/// @brief book a 2D efficiency plot
+/// @param effName the name of plot
+/// @param effTitle the title of plot
+/// @param varXBinning the binning info of variable at x axis
+/// @param varYBinning the binning info of variable at y axis
+/// @return TEfficiency pointer
+TEfficiency* bookEff(const char* effName, const char* effTitle,
+                     const Binning& varXBinning, const Binning& varYBinning);
+
+/// @brief fill a 1D efficiency plot
+/// @param efficiency plot to fill
+/// @param value value to fill
+/// @param status bool to denote passed or not
+void fillEff(TEfficiency* efficiency, float value, bool status);
+
+/// @brief fill a 2D efficiency plot
+/// @param efficiency plot to fill
+/// @param xValue x value to fill
+/// @param yValue y value to fill
+/// @param status bool to denote passed or not
+void fillEff(TEfficiency* efficiency, float xValue, float yValue, bool status);
+
+/// @brief book a TProfile plot
+/// @param profName the name of plot
+/// @param profTitle the title of plot
+/// @param varXBinning the binning info of variable at x axis
+/// @param varYBinning the binning info of variable at y axis
+/// @return TProfile pointer
+TProfile* bookProf(const char* profName, const char* profTitle,
+                   const Binning& varXBinning, const Binning& varYBinning);
+
+/// @brief fill a TProfile plot
+/// @param profile plot to fill
+/// @param xValue  xvalue to fill
+/// @param yValue  yvalue to fill
+/// @param weight weight to fill
+void fillProf(TProfile* profile, float xValue, float yValue, float weight = 1.0);
+
+}  // namespace PlotHelpers
+
+#endif  // FASERACTSKALMANFILTER_PLOTHELPERS_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ProtoTrackWriterTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ProtoTrackWriterTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..a71ddb56ab7b70af4930fc5cd1369010a5e7446b
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ProtoTrackWriterTool.h
@@ -0,0 +1,61 @@
+#ifndef FASERACTSKALMANFILTER_PROTOTRACKWRITERTOOL_H
+#define FASERACTSKALMANFILTER_PROTOTRACKWRITERTOOL_H
+
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/StatusCode.h"
+#include "GaudiKernel/ToolHandle.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "Acts/EventData/TrackParameters.hpp"
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/ITrackFinderTool.h"
+#include "FaserActsKalmanFilter/Measurement.h"
+
+class FaserSCT_ID;
+class TFile;
+class TTree;
+
+class ProtoTrackWriterTool : public AthAlgTool {
+public:
+  ProtoTrackWriterTool(const std::string& type, const std::string& name,
+                       const IInterface* parent);
+  virtual ~ProtoTrackWriterTool() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+  StatusCode write(std::shared_ptr<const Acts::CurvilinearTrackParameters> protoTrackParameters,
+                   std::shared_ptr<std::vector<Measurement>> measurements,
+                   const Acts::GeometryContext& ctx) const;
+
+private:
+  const FaserSCT_ID* m_idHelper{nullptr};
+
+  ToolHandle<ITrackFinderTool> m_trackFinderTool {this, "TrackFinderTool", "TruthTrackFinderTool"};
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  Gaudi::Property<std::string> m_filePath {this, "File", "ProtoTracks.root", "Root file"};
+  TFile* m_file;
+  TTree* m_params;
+  TTree* m_meas;
+
+  mutable int m_run_number;
+  mutable int m_event_number;
+  mutable double m_x;
+  mutable double m_y;
+  mutable double m_z;
+  mutable double m_px;
+  mutable double m_py;
+  mutable double m_pz;
+  mutable int m_station;
+  mutable int m_layer;
+  mutable int m_phi;
+  mutable int m_eta;
+  mutable int m_side;
+  mutable double m_meas_eLOC0;
+  mutable double m_meas_eLOC1;
+};
+
+#endif // FASERACTSKALMANFILTER_PROTOTRACKWRITERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ResPlotTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ResPlotTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..0b65984bed7e7de4c7975fdf9cc67248c253dab7
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ResPlotTool.h
@@ -0,0 +1,87 @@
+#ifndef FASERACTSKALMANFILTER_RESPLOTTOOL_H
+#define FASERACTSKALMANFILTER_RESPLOTTOOL_H
+
+#include "FaserActsKalmanFilter/PlotHelpers.h"
+#include "Acts/EventData/TrackParameters.hpp"
+#include "Acts/Geometry/GeometryContext.hpp"
+#include "HepMC/GenParticle.h"
+#include "TH1F.h"
+#include "TH2F.h"
+#include <map>
+#include <string>
+#include <vector>
+
+class ResPlotTool {
+public:
+  std::vector<std::string> m_paramNames = {"x0", "y0", "phi", "theta", "qop", "t"};
+  std::map<std::string, PlotHelpers::Binning> m_varBinning {
+      {"Eta", PlotHelpers::Binning("#eta", 40, 4, 12)},
+      {"Pt", PlotHelpers::Binning("pT [GeV/c]", 40, 0, 20)},
+      {"Pull", PlotHelpers::Binning("pull", 100, -5, 5)},
+      {"Residual_x0", PlotHelpers::Binning("r_{x0} [mm]", 100, -0.1, 0.1)},
+      {"Residual_y0", PlotHelpers::Binning("r_{y0} [mm]", 100, -4, 4)},
+      {"Residual_phi", PlotHelpers::Binning("r_{#phi} [rad]", 100, -0.3, 0.3)},
+      {"Residual_theta", PlotHelpers::Binning("r_{#theta} [rad]", 100, -0.002, 0.002)},
+      {"Residual_qop", PlotHelpers::Binning("r_{q/p} [c/GeV]", 100, -0.002, 0.002)},
+      {"Residual_t", PlotHelpers::Binning("r_{t} [s]", 100, -1000, 1000)},
+  };
+
+  /// @brief Nested Cache struct
+  struct ResPlotCache {
+    std::map<std::string, TH1F*> res;               ///< Residual distribution
+    std::map<std::string, TH2F*> res_vs_eta;        ///< Residual vs eta scatter plot
+    std::map<std::string, TH1F*> resMean_vs_eta;    ///< Residual mean vs eta distribution
+    std::map<std::string, TH1F*> resWidth_vs_eta;   ///< Residual width vs eta distribution
+    std::map<std::string, TH2F*> res_vs_pT;         ///< Residual vs pT scatter plot
+    std::map<std::string, TH1F*> resMean_vs_pT;     ///< Residual mean vs pT distribution
+    std::map<std::string, TH1F*> resWidth_vs_pT;    ///< Residual width vs pT distribution
+
+    std::map<std::string, TH1F*> pull;              ///< Pull distribution
+    std::map<std::string, TH2F*> pull_vs_eta;       ///< Pull vs eta scatter plot
+    std::map<std::string, TH1F*> pullMean_vs_eta;   ///< Pull mean vs eta distribution
+    std::map<std::string, TH1F*> pullWidth_vs_eta;  ///< Pull width vs eta distribution
+    std::map<std::string, TH2F*> pull_vs_pT;        ///< Pull vs pT scatter plot
+    std::map<std::string, TH1F*> pullMean_vs_pT;    ///< Pull mean vs pT distribution
+    std::map<std::string, TH1F*> pullWidth_vs_pT;   ///< Pull width vs pT distribution
+  };
+
+  /// Constructor
+  ///
+  ResPlotTool() = default;
+
+  /// @brief book the histograms
+  ///
+  /// @param resPlotCache the cache for residual/pull histograms
+  void book(ResPlotCache& resPlotCache) const;
+
+  /// @brief fill the histograms
+  ///
+  /// @param resPlotCache the cache for residual/pull histograms
+  /// @param gctx the geometry context
+  /// @param truthParticle the truth particle
+  /// @param fittedParamters the fitted parameters at perigee surface
+  void fill(ResPlotCache& resPlotCache, const Acts::GeometryContext& gctx,
+            std::unique_ptr<const Acts::BoundTrackParameters> truthParameters,
+            const Acts::BoundTrackParameters& fittedParameters) const;
+
+  /// @brief extract the details of the residual/pull plots and fill details
+  ///
+  /// into separate histograms
+  /// @param resPlotCache the cache object for residual/pull histograms
+  void refinement(ResPlotCache& resPlotCache) const;
+
+  /// @brief write the histograms to output file
+  ///
+  /// @param resPlotCache the cache object for residual/pull histograms
+  void write(const ResPlotCache& resPlotCache) const;
+
+  /// @brief delele the histograms
+  ///
+  /// @param resPlotCache the cache object for residual/pull histograms
+  void clear(ResPlotCache& resPlotCache) const;
+
+private:
+  const double m_MeV2GeV = 0.001;
+};
+
+#endif // FASERACTSKALMANFILTER_RESPLOTTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/RootTrajectoryStatesWriterTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/RootTrajectoryStatesWriterTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..280762a89a23fdf0555615aa0a7e82c23d3ccfa5
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/RootTrajectoryStatesWriterTool.h
@@ -0,0 +1,129 @@
+#ifndef FASERACTSKALMANFILTER_ROOTTRAJECTORYSTATESWRITERTOOL_H
+#define FASERACTSKALMANFILTER_ROOTTRAJECTORYSTATESWRITERTOOL_H
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Acts/EventData/TrackParameters.hpp"
+#include "Acts/Geometry/GeometryContext.hpp"
+#include "TrackerSimEvent/FaserSiHitCollection.h"
+#include "TrackerSimData/TrackerSimDataCollection.h"
+#include "GeneratorObjects/McEventCollection.h"
+#include <array>
+#include <string>
+#include <vector>
+
+
+class FaserSCT_ID;
+namespace  TrackerDD {
+class SCT_DetectorManager;
+}
+class TFile;
+class TTree;
+struct FaserActsRecMultiTrajectory;
+using TrajectoriesContainer = std::vector<FaserActsRecMultiTrajectory>;
+
+class RootTrajectoryStatesWriterTool : public AthAlgTool {
+public:
+  RootTrajectoryStatesWriterTool(const std::string& type, const std::string& name, const IInterface* parent);
+  ~RootTrajectoryStatesWriterTool() override = default;
+
+  StatusCode initialize() override;
+  StatusCode finalize() override;
+
+  StatusCode write(const Acts::GeometryContext& gctx, const TrajectoriesContainer& trajectories) const;
+
+private:
+  SG::ReadHandleKey<McEventCollection> m_mcEventCollectionKey {this, "McEventCollection", "TruthEvent"};
+  SG::ReadHandleKey<TrackerSimDataCollection> m_simDataCollectionKey {this, "TrackerSimDataCollection", "SCT_SDO_Map"};
+  SG::ReadHandleKey <FaserSiHitCollection> m_faserSiHitKey {this, "FaserSiHitCollection", "SCT_Hits"};
+
+  const double m_MeV2GeV = 0.001;
+
+  const FaserSCT_ID* m_idHelper{nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detMgr {nullptr};
+  Gaudi::Property<std::string> m_filePath {this, "FilePath", "track_states_ckf.root", "Output root file"};
+  Gaudi::Property<std::string> m_treeName {this, "TreeName", "tree", "Tree name"};
+  Gaudi::Property<bool> m_mc {this, "MC", false};
+  TFile* m_outputFile;
+  TTree* m_outputTree;
+
+  mutable uint32_t m_eventNr{0};         ///< the event number
+  mutable uint32_t m_multiTrajNr{0};     ///< the multi-trajectory number
+  mutable unsigned int m_subTrajNr{0};   ///< the multi-trajectory sub-trajectory number
+
+  mutable std::vector<float> m_t_x;  ///< Global truth hit position x
+  mutable std::vector<float> m_t_y;  ///< Global truth hit position y
+  mutable std::vector<float> m_t_z;  ///< Global truth hit position z
+  mutable std::vector<float> m_t_dx;  ///< Truth particle direction x at global hit position
+  mutable std::vector<float> m_t_dy;  ///< Truth particle direction y at global hit position
+  mutable std::vector<float> m_t_dz;  ///< Truth particle direction z at global hit position
+
+  mutable std::vector<float> m_t_eLOC0;   ///< truth parameter eBoundLoc0
+  mutable std::vector<float> m_t_eLOC1;   ///< truth parameter eBoundLoc1
+  mutable std::vector<float> m_t_ePHI;    ///< truth parameter ePHI
+  mutable std::vector<float> m_t_eTHETA;  ///< truth parameter eTHETA
+  mutable std::vector<float> m_t_eQOP;    ///< truth parameter eQOP
+  mutable std::vector<float> m_t_eT;      ///< truth parameter eT
+
+  mutable unsigned int m_nStates{0};        ///< number of all states
+  mutable unsigned int m_nMeasurements{0};  ///< number of states with measurements
+  mutable std::vector<int> m_volumeID;      ///< volume identifier
+  mutable std::vector<int> m_layerID;       ///< layer identifier
+  mutable std::vector<int> m_moduleID;      ///< surface identifier
+  mutable std::vector<int> m_station;       ///< station
+  mutable std::vector<int> m_layer;         ///< layer
+  mutable std::vector<int> m_phi_module;    ///< phi module (rows)
+  mutable std::vector<int> m_eta_module;    ///< eta module (columns)
+  mutable std::vector<int> m_side;          ///< module side
+  mutable std::vector<float> m_pathLength;  ///< path length
+  mutable std::vector<float> m_lx_hit;      ///< uncalibrated measurement local x
+  mutable std::vector<float> m_ly_hit;      ///< uncalibrated measurement local y
+  mutable std::vector<float> m_x_hit;       ///< uncalibrated measurement global x
+  mutable std::vector<float> m_y_hit;       ///< uncalibrated measurement global y
+  mutable std::vector<float> m_z_hit;       ///< uncalibrated measurement global z
+  mutable std::vector<float> m_res_x_hit;   ///< hit residual x
+  mutable std::vector<float> m_res_y_hit;   ///< hit residual y
+  mutable std::vector<float> m_err_x_hit;   ///< hit err x
+  mutable std::vector<float> m_err_y_hit;   ///< hit err y
+  mutable std::vector<float> m_pull_x_hit;  ///< hit pull x
+  mutable std::vector<float> m_pull_y_hit;  ///< hit pull y
+  mutable std::vector<int> m_dim_hit;       ///< dimension of measurement
+
+  mutable std::array<int, 3> m_nParams;  ///< number of states which have filtered/predicted/smoothed parameters
+  mutable std::array<std::vector<bool>, 3> m_hasParams;  ///< status of the filtered/predicted/smoothed parameters
+  mutable std::array<std::vector<float>, 3> m_eLOC0;  ///< predicted/filtered/smoothed parameter eLOC0
+  mutable std::array<std::vector<float>, 3> m_eLOC1;  ///< predicted/filtered/smoothed parameter eLOC1
+  mutable std::array<std::vector<float>, 3> m_ePHI;  ///< predicted/filtered/smoothed parameter ePHI
+  mutable std::array<std::vector<float>, 3> m_eTHETA;  ///< predicted/filtered/smoothed parameter eTHETA
+  mutable std::array<std::vector<float>, 3> m_eQOP;  ///< predicted/filtered/smoothed parameter eQOP
+  mutable std::array<std::vector<float>, 3> m_eT;  ///< predicted/filtered/smoothed parameter eT
+  mutable std::array<std::vector<float>, 3> m_res_eLOC0;  ///< predicted/filtered/smoothed parameter eLOC0 residual
+  mutable std::array<std::vector<float>, 3> m_res_eLOC1;  ///< predicted/filtered/smoothed parameter eLOC1 residual
+  mutable std::array<std::vector<float>, 3> m_res_ePHI;  ///< predicted/filtered/smoothed parameter ePHI residual
+  mutable std::array<std::vector<float>, 3> m_res_eTHETA;  ///< predicted/filtered/smoothed parameter eTHETA residual
+  mutable std::array<std::vector<float>, 3> m_res_eQOP;  ///< predicted/filtered/smoothed parameter eQOP residual
+  mutable std::array<std::vector<float>, 3> m_res_eT;  ///< predicted/filtered/smoothed parameter eT residual
+  mutable std::array<std::vector<float>, 3> m_err_eLOC0;  ///< predicted/filtered/smoothed parameter eLOC0 error
+  mutable std::array<std::vector<float>, 3> m_err_eLOC1;  ///< predicted/filtered/smoothed parameter eLOC1 error
+  mutable std::array<std::vector<float>, 3> m_err_ePHI;  ///< predicted/filtered/smoothed parameter ePHI error
+  mutable std::array<std::vector<float>, 3> m_err_eTHETA;  ///< predicted/filtered/smoothed parameter eTHETA error
+  mutable std::array<std::vector<float>, 3> m_err_eQOP;  ///< predicted/filtered/smoothed parameter eQOP error
+  mutable std::array<std::vector<float>, 3> m_err_eT;  ///< predicted/filtered/smoothed parameter eT error
+  mutable std::array<std::vector<float>, 3> m_pull_eLOC0;  ///< predicted/filtered/smoothed parameter eLOC0 pull
+  mutable std::array<std::vector<float>, 3> m_pull_eLOC1;  ///< predicted/filtered/smoothed parameter eLOC1 pull
+  mutable std::array<std::vector<float>, 3> m_pull_ePHI;  ///< predicted/filtered/smoothed parameter ePHI pull
+  mutable std::array<std::vector<float>, 3> m_pull_eTHETA;  ///< predicted/filtered/smoothed parameter eTHETA pull
+  mutable std::array<std::vector<float>, 3> m_pull_eQOP;  ///< predicted/filtered/smoothed parameter eQOP pull
+  mutable std::array<std::vector<float>, 3> m_pull_eT;  ///< predicted/filtered/smoothed parameter eT pull
+  mutable std::array<std::vector<float>, 3> m_x;  ///< predicted/filtered/smoothed parameter global x
+  mutable std::array<std::vector<float>, 3> m_y;  ///< predicted/filtered/smoothed parameter global y
+  mutable std::array<std::vector<float>, 3> m_z;  ///< predicted/filtered/smoothed parameter global z
+  mutable std::array<std::vector<float>, 3> m_px;  ///< predicted/filtered/smoothed parameter px
+  mutable std::array<std::vector<float>, 3> m_py;  ///< predicted/filtered/smoothed parameter py
+  mutable std::array<std::vector<float>, 3> m_pz;  ///< predicted/filtered/smoothed parameter pz
+  mutable std::array<std::vector<float>, 3> m_eta;  ///< predicted/filtered/smoothed parameter eta
+  mutable std::array<std::vector<float>, 3> m_pT;  ///< predicted/filtered/smoothed parameter pT
+
+  mutable  std::vector<float> m_chi2;  ///< chisq from filtering
+};
+
+#endif  // FASERACTSKALMANFILTER_ROOTTRAJECTORYSTATESWRITERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/RootTrajectorySummaryWriterTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/RootTrajectorySummaryWriterTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..2ce72c2429164d9ff8af8e77cf95aaac36d8df26
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/RootTrajectorySummaryWriterTool.h
@@ -0,0 +1,117 @@
+#ifndef FASERACTSKALMANFILTER_ROOTTRAJECTORYSUMMARYWRITERTOOL_H
+#define FASERACTSKALMANFILTER_ROOTTRAJECTORYSUMMARYWRITERTOOL_H
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Acts/EventData/TrackParameters.hpp"
+#include "Acts/Geometry/GeometryContext.hpp"
+#include "FaserActsKalmanFilter/IdentifierLink.h"
+#include "TrackerSimData/TrackerSimDataCollection.h"
+#include "GeneratorObjects/McEventCollection.h"
+#include "FaserActsGeometryInterfaces/IFaserActsExtrapolationTool.h"
+#include <array>
+#include <string>
+#include <vector>
+
+class FaserSCT_ID;
+namespace  TrackerDD {
+class SCT_DetectorManager;
+}
+class TFile;
+class TTree;
+struct FaserActsRecMultiTrajectory;
+using TrajectoriesContainer = std::vector<FaserActsRecMultiTrajectory>;
+
+class RootTrajectorySummaryWriterTool : public AthAlgTool {
+public:
+  RootTrajectorySummaryWriterTool(const std::string& type, const std::string& name, const IInterface* parent);
+  ~RootTrajectorySummaryWriterTool() override = default;
+  StatusCode write(const Acts::GeometryContext& geoContext, const TrajectoriesContainer& trajectories) const;
+  StatusCode initialize() override;
+  StatusCode finalize() override;
+
+private:
+  std::unique_ptr<const Acts::BoundTrackParameters> extrapolateToReferenceSurface(
+      const EventContext& ctx, const HepMC::GenParticle* particle) const;
+  const FaserSCT_ID* m_idHelper {nullptr};
+  SG::ReadHandleKey<TrackerSimDataCollection> m_simDataCollectionKey {
+    this, "TrackerSimDataCollection", "SCT_SDO_Map"};
+  SG::ReadHandleKey<McEventCollection> m_mcEventCollectionKey {
+    this, "McEventCollection", "TruthEvent"};
+  ToolHandle<IFaserActsExtrapolationTool> m_extrapolationTool {
+      this, "ExtrapolationTool", "FaserActsExtrapolationTool"};
+
+  Gaudi::Property<std::string> m_filePath{this, "FilePath", "track_summary_ckf.root", "Output root file"};
+  Gaudi::Property<std::string> m_treeName{this, "TreeName", "tree", "Tree name"};
+
+  const double m_MeV2GeV = 0.001;
+
+  TFile* m_outputFile;
+  TTree* m_outputTree;
+  mutable uint32_t m_eventNr{0};         ///< The event number
+  mutable std::vector<uint32_t> m_multiTrajNr;  ///< The multi-trajectory numbers in event
+  mutable std::vector<unsigned int> m_subTrajNr;  ///< The multi-trajectory sub-trajectory number in event
+
+  mutable std::vector<unsigned int> m_nStates;        ///< The number of states
+  mutable std::vector<unsigned int> m_nMeasurements;  ///< The number of measurements
+  mutable std::vector<unsigned int> m_nOutliers;      ///< The number of outliers
+  mutable std::vector<unsigned int> m_nHoles;         ///< The number of holes
+  mutable std::vector<unsigned int> m_nSharedHits;    ///< The number of shared hits
+  mutable std::vector<float> m_chi2Sum;               ///< The total chi2
+  mutable std::vector<unsigned int> m_NDF;  ///< The number of ndf of the measurements+outliers
+  mutable std::vector<std::vector<double>> m_measurementChi2;  ///< The chi2 on all measurement states
+  mutable std::vector<std::vector<double>> m_outlierChi2;  ///< The chi2 on all outlier states
+  // FIXME replace volume, layer, ... with station, layer, ...
+  mutable std::vector<std::vector<double>> m_measurementVolume;  ///< The volume id of the measurements
+  mutable std::vector<std::vector<double>> m_measurementLayer;  ///< The layer id of the measurements
+  mutable std::vector<std::vector<double>> m_outlierVolume;  ///< The volume id of the outliers
+  mutable std::vector<std::vector<double>> m_outlierLayer;  ///< The layer id of the outliers
+
+  // The majority truth particle info
+  mutable std::vector<unsigned int> m_nMajorityHits;  ///< The number of hits from majority particle
+  mutable std::vector<uint64_t> m_majorityParticleId;      ///< The particle Id of the majority particle
+  mutable std::vector<int> m_t_charge;   ///< Charge of majority particle
+  mutable std::vector<float> m_t_time;   ///< Time of majority particle
+  mutable std::vector<float> m_t_vx;     ///< Vertex x positions of majority particle
+  mutable std::vector<float> m_t_vy;     ///< Vertex y positions of majority particle
+  mutable std::vector<float> m_t_vz;     ///< Vertex z positions of majority particle
+  mutable std::vector<float> m_t_px;     ///< Initial momenta px of majority particle
+  mutable std::vector<float> m_t_py;     ///< Initial momenta py of majority particle
+  mutable std::vector<float> m_t_pz;     ///< Initial momenta pz of majority particle
+  mutable std::vector<float> m_t_theta;  ///< In * m_MeV2GeVitial momenta theta of majority particle
+  mutable std::vector<float> m_t_phi;    ///< Initial momenta phi of majority particle
+  mutable std::vector<float> m_t_p;      ///< Initial abs momenta of majority particle
+  mutable std::vector<float> m_t_pT;     ///< Initial momenta pT of majority particle
+  mutable std::vector<float> m_t_eta;    ///< Initial momenta eta of majority particle
+
+  mutable std::vector<bool> m_hasFittedParams;  ///< If the track has fitted parameter
+  // The fitted parameters
+  mutable std::vector<float> m_eLOC0_fit;   ///< Fitted parameters eBoundLoc0 of track
+  mutable std::vector<float> m_eLOC1_fit;   ///< Fitted parameters eBoundLoc1 of track
+  mutable std::vector<float> m_ePHI_fit;    ///< Fitted parameters ePHI of track
+  mutable std::vector<float> m_eTHETA_fit;  ///< Fitted parameters eTHETA of track
+  mutable std::vector<float> m_eQOP_fit;    ///< Fitted parameters eQOP of track
+  mutable std::vector<float> m_eT_fit;      ///< Fitted parameters eT of track
+  // The error of fitted parameters
+  mutable std::vector<float> m_err_eLOC0_fit;   ///< Fitted parameters eLOC err of track
+  mutable std::vector<float> m_err_eLOC1_fit;   ///< Fitted parameters eBoundLoc1 err of track
+  mutable std::vector<float> m_err_ePHI_fit;    ///< Fitted parameters ePHI err of track
+  mutable std::vector<float> m_err_eTHETA_fit;  ///< Fitted parameters eTHETA err of track
+  mutable std::vector<float> m_err_eQOP_fit;    ///< Fitted parameters eQOP err of track
+  mutable std::vector<float> m_err_eT_fit;      ///< Fitted parameters eT err of track
+  // The residual of fitted parameters
+  mutable std::vector<float> m_res_eLOC0_fit;   ///< Fitted parameters eLOC res of track
+  mutable std::vector<float> m_res_eLOC1_fit;   ///< Fitted parameters eBoundLoc1 res of track
+  mutable std::vector<float> m_res_ePHI_fit;    ///< Fitted parameters ePHI res of track
+  mutable std::vector<float> m_res_eTHETA_fit;  ///< Fitted parameters eTHETA res of track
+  mutable std::vector<float> m_res_eQOP_fit;    ///< Fitted parameters eQOP res of track
+  mutable std::vector<float> m_res_eT_fit;      ///< Fitted parameters eT res of track
+  // The pull of fitted parameters
+  mutable std::vector<float> m_pull_eLOC0_fit;    ///< Fitted parameters eLOC pull of track
+  mutable std::vector<float> m_pull_eLOC1_fit;    ///< Fitted parameters eBoundLoc1 pull of track
+  mutable std::vector<float> m_pull_ePHI_fit;    ///< Fitted parameters ePHI pull of track
+  mutable std::vector<float> m_pull_eTHETA_fit;  ///< Fitted parameters eTHETA pull of track
+  mutable std::vector<float> m_pull_eQOP_fit;    ///< Fitted parameters eQOP pull of track
+  mutable std::vector<float> m_pull_eT_fit;      ///< Fitted parameters eT pull of track
+};
+
+#endif  // FASERACTSKALMANFILTER_ROOTTRAJECTORYSUMMARYWRITERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SegmentFitClusterTrackFinderTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SegmentFitClusterTrackFinderTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..6bf7ba6984300ae218a39015786d99d034922e64
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SegmentFitClusterTrackFinderTool.h
@@ -0,0 +1,106 @@
+#ifndef FASERACTSKALMANFILTER_SEGMENTFITCLUSTERTRACKFINDERTOOL_H
+#define FASERACTSKALMANFILTER_SEGMENTFITCLUSTERTRACKFINDERTOOL_H
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/EventContext.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/ReadHandleKey.h"
+#include <string>
+#include <vector>
+
+#include "TrackerSpacePoint/FaserSCT_SpacePointContainer.h"
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/ITrackFinderTool.h"
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "TrkTrack/TrackCollection.h"
+
+class FaserSCT_ID;
+namespace TrackerDD {
+class SCT_DetectorManager;
+}
+
+
+class SegmentFitClusterTrackFinderTool : public extends<AthAlgTool, ITrackFinderTool> {
+public:
+  SegmentFitClusterTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent);
+  virtual ~SegmentFitClusterTrackFinderTool() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+  virtual StatusCode run() override;
+
+  virtual const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> initialTrackParameters() const override;
+  virtual const std::shared_ptr<const Acts::Surface> initialSurface() const override;
+  virtual const std::shared_ptr<std::vector<std::vector<IndexSourceLink>>> sourceLinks() const override;
+  virtual const std::shared_ptr<std::vector<IdentifierLink>> idLinks() const override;
+  virtual const std::shared_ptr<std::vector<std::vector<Measurement>>> measurements() const override;
+  virtual const std::shared_ptr<std::vector<std::vector<Tracker::FaserSCT_SpacePoint>>> spacePoints() const override;
+  virtual const std::shared_ptr<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>> clusters() const override;
+
+private:
+  std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> m_initialTrackParameters;
+  std::shared_ptr<const Acts::Surface> m_initialSurface;
+  std::shared_ptr<std::vector<std::vector<IndexSourceLink>>> m_sourceLinks {};
+  std::shared_ptr<std::vector<IdentifierLink>> m_idLinks {};
+  std::shared_ptr<std::vector<std::vector<Measurement>>> m_measurements {};
+  std::shared_ptr<std::vector<std::vector<Tracker::FaserSCT_SpacePoint>>> m_spacePoints {};
+  std::shared_ptr<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>> m_clusters {};
+
+  const FaserSCT_ID* m_idHelper {nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detManager {nullptr};
+  static std::pair<double, double> momentum(const std::map<int, Amg::Vector3D>& pos, double B=0.57);
+
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {
+      this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  SG::ReadHandleKey<TrackCollection> m_trackCollection {
+      this, "TrackCollection", "SegmentFit", "Input track collection name" };
+
+  // covariance of the initial parameters
+  Gaudi::Property<double> m_sigmaCluster {this, "sigmaCluster", 0.04};
+  Gaudi::Property<double> m_covLoc0 {this, "covLoc0", 1};
+  Gaudi::Property<double> m_covLoc1 {this, "covLoc1", 1};
+  Gaudi::Property<double> m_covPhi {this, "covPhi", 1};
+  Gaudi::Property<double> m_covTheta {this, "covTheta", 1};
+  Gaudi::Property<double> m_covQOverP {this, "covQOverP", 1};
+  Gaudi::Property<double> m_covTime {this, "covTime", 1};
+};
+
+
+inline const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>>
+SegmentFitClusterTrackFinderTool::initialTrackParameters() const {
+  return m_initialTrackParameters;
+}
+
+inline const std::shared_ptr<const Acts::Surface>
+SegmentFitClusterTrackFinderTool::initialSurface() const {
+  return m_initialSurface;
+}
+
+inline const std::shared_ptr<std::vector<std::vector<IndexSourceLink>>>
+SegmentFitClusterTrackFinderTool::sourceLinks() const {
+  return m_sourceLinks;
+}
+
+inline const std::shared_ptr<std::vector<IdentifierLink>>
+SegmentFitClusterTrackFinderTool::idLinks() const {
+  return m_idLinks;
+}
+
+inline const std::shared_ptr<std::vector<std::vector<Measurement>>>
+SegmentFitClusterTrackFinderTool::measurements() const {
+  return m_measurements;
+}
+
+inline const std::shared_ptr<std::vector<std::vector<Tracker::FaserSCT_SpacePoint>>>
+SegmentFitClusterTrackFinderTool::spacePoints() const {
+  return m_spacePoints;
+}
+
+inline const std::shared_ptr<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>>
+SegmentFitClusterTrackFinderTool::clusters() const {
+  return m_clusters;
+}
+
+#endif // FASERACTSKALMANFILTER_SEGMENTFITCLUSTERTRACKFINDERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SegmentFitTrackFinderTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SegmentFitTrackFinderTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..3f555be285fc792c42db3b8903f9e208e4fa7902
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SegmentFitTrackFinderTool.h
@@ -0,0 +1,98 @@
+#ifndef FASERACTSKALMANFILTER_SEGMENTFITTRACKFINDERTOOL_H
+#define FASERACTSKALMANFILTER_SEGMENTFITTRACKFINDERTOOL_H
+
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/EventContext.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/ReadHandleKey.h"
+#include <string>
+#include <vector>
+
+#include "TrackerSpacePoint/FaserSCT_SpacePointContainer.h"
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/ITrackFinderTool.h"
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "TrkTrack/TrackCollection.h"
+
+class FaserSCT_ID;
+namespace TrackerDD {
+class SCT_DetectorManager;
+}
+
+
+class SegmentFitTrackFinderTool : public extends<AthAlgTool, ITrackFinderTool> {
+public:
+  SegmentFitTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent);
+  virtual ~SegmentFitTrackFinderTool() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+  virtual StatusCode run() override;
+
+  virtual const std::shared_ptr<const Acts::CurvilinearTrackParameters> initialTrackParameters() const override;
+  virtual const std::shared_ptr<const Acts::Surface> initialSurface() const override;
+  virtual const std::shared_ptr<std::vector<IndexSourceLink>> sourceLinks() const override;
+  virtual const std::shared_ptr<IdentifierLink> idLinks() const override;
+  virtual const std::shared_ptr<std::vector<Measurement>> measurements() const override;
+  virtual const std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>> spacePoints() const override;
+
+private:
+  std::shared_ptr<const Acts::CurvilinearTrackParameters> m_initialTrackParameters;
+  std::shared_ptr<const Acts::Surface> m_initialSurface;
+  std::shared_ptr<std::vector<IndexSourceLink>> m_sourceLinks {};
+  std::shared_ptr<IdentifierLink> m_idLinks {};
+  std::shared_ptr<std::vector<Measurement>> m_measurements {};
+  std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>> m_spacePoints {};
+
+  const FaserSCT_ID* m_idHelper {nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detManager {nullptr};
+  static double momentum(const std::map<int, Amg::Vector3D>& pos, double B=0.57) ;
+
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {
+      this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  SG::ReadHandleKey<TrackCollection> m_trackCollection {
+      this, "TrackCollection", "SegmentFit", "Input track collection name" };
+  SG::ReadHandleKey<FaserSCT_SpacePointContainer> m_spacePointContainerKey {
+      this, "SpacePoints", "SCT_SpacePointContainer", "Space point container"};
+
+  // covariance of the initial parameters
+  Gaudi::Property<double> m_covLoc0 {this, "covLoc0", 1};
+  Gaudi::Property<double> m_covLoc1 {this, "covLoc1", 1};
+  Gaudi::Property<double> m_covPhi {this, "covPhi", 1};
+  Gaudi::Property<double> m_covTheta {this, "covTheta", 1};
+  Gaudi::Property<double> m_covQOverP {this, "covQOverP", 1};
+  Gaudi::Property<double> m_covTime {this, "covTime", 1};
+};
+
+
+inline const std::shared_ptr<const Acts::CurvilinearTrackParameters>
+    SegmentFitTrackFinderTool::initialTrackParameters() const {
+  return m_initialTrackParameters;
+}
+
+inline const std::shared_ptr<const Acts::Surface>
+    SegmentFitTrackFinderTool::initialSurface() const {
+return m_initialSurface;
+}
+
+inline const std::shared_ptr<std::vector<IndexSourceLink>>
+   SegmentFitTrackFinderTool::sourceLinks() const {
+  return m_sourceLinks;
+}
+
+inline const std::shared_ptr<IdentifierLink>
+SegmentFitTrackFinderTool::idLinks() const {
+  return m_idLinks;
+}
+
+inline const std::shared_ptr<std::vector<Measurement>>
+    SegmentFitTrackFinderTool::measurements() const {
+  return m_measurements;
+}
+
+inline const std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>>
+    SegmentFitTrackFinderTool::spacePoints() const {
+  return m_spacePoints;
+}
+#endif // FASERACTSKALMANFILTER_SEGMENTFITTRACKFINDERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SummaryPlotTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SummaryPlotTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea54ffc0f424409ce16887c8824ce271f9a342c2
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/SummaryPlotTool.h
@@ -0,0 +1,67 @@
+#ifndef FASERACTSKALMANFILTER_SUMMARYPLOTTOOL_H
+#define FASERACTSKALMANFILTER_SUMMARYPLOTTOOL_H
+
+#include "FaserActsKalmanFilter/PlotHelpers.h"
+#include "Acts/EventData/TrackParameters.hpp"
+#include "TProfile.h"
+#include <map>
+#include <string>
+
+class SummaryPlotTool {
+public:
+  std::map<std::string, PlotHelpers::Binning> m_varBinning {
+      {"Eta", PlotHelpers::Binning("#eta", 40, 4, 12)},
+      {"Phi", PlotHelpers::Binning("#phi", 100, -3.15, 3.15)},
+      {"Pt", PlotHelpers::Binning("pT [GeV/c]", 40, 0, 20)},
+      {"Num", PlotHelpers::Binning("N", 30, -0.5, 29.5)}
+  };
+
+  /// @brief Nested Cache struct
+  struct SummaryPlotCache {
+    TProfile* nStates_vs_eta;        ///< Number of total states vs eta
+    TProfile* nMeasurements_vs_eta;  ///< Number of non-outlier measurements vs eta
+    TProfile* nHoles_vs_eta;         ///< Number of holes vs eta
+    TProfile* nOutliers_vs_eta;      ///< Number of outliers vs eta
+    TProfile* nSharedHits_vs_eta;    ///< Number of Shared Hits vs eta
+    TProfile* nStates_vs_pt;         ///< Number of total states vs pt
+    TProfile* nMeasurements_vs_pt;   ///< Number of non-outlier measurements vs pt
+    TProfile* nHoles_vs_pt;          ///< Number of holes vs pt
+    TProfile* nOutliers_vs_pt;       ///< Number of outliers vs pt
+    TProfile* nSharedHits_vs_pt;     ///< Number of Shared Hits vs pt
+  };
+
+  /// Constructor
+  ///
+  SummaryPlotTool() = default;
+
+  /// @brief book the track info plots
+  ///
+  /// @param trackSummaryPlotCache the cache for track info plots
+  void book(SummaryPlotCache& trackSummaryPlotCache) const;
+
+  /// @brief fill reco track info w.r.t. fitted track parameters
+  ///
+  /// @param trackSummaryPlotCache cache object for track info plots
+  /// @param fittedParameters fitted track parameters of this track
+  /// @param nStates number of track states
+  /// @param nMeasurements number of measurements
+  /// @param nOutliers number of outliers
+  /// @param nHoles number of holes
+  /// @param nSharedHits number of shared hits
+  void fill(SummaryPlotCache& trackSummaryPlotCache,
+            const Acts::BoundTrackParameters& fittedParameters, size_t nStates,
+            size_t nMeasurements, size_t nOutliers, size_t nHoles,
+            size_t nSharedHits) const;
+
+  /// @brief write the track info plots to file
+  ///
+  /// @param trackSummaryPlotCache cache object for track info plots
+  void write(const SummaryPlotCache& trackSummaryPlotCache) const;
+
+  /// @brief delete the track info plots
+  ///
+  /// @param trackSummaryPlotCache cache object for track info plots
+  void clear(SummaryPlotCache& trackSummaryPlotCache) const;
+};
+
+#endif // FASERACTSKALMANFILTER_SUMMARYPLOTTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ThreeStationTrackSeedTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ThreeStationTrackSeedTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..a37f1dbf1061e636402e1069c49ac22a479c610b
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/ThreeStationTrackSeedTool.h
@@ -0,0 +1,102 @@
+#ifndef FASERACTSKALMANFILTER_THREESTATIONTRACKSEEDTOOL_H
+#define FASERACTSKALMANFILTER_THREESTATIONTRACKSEEDTOOL_H
+
+#include "TrackerPrepRawData/FaserSCT_ClusterContainer.h"
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/StatusCode.h"
+
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/ITrackSeedTool.h"
+#include "TrkTrack/TrackCollection.h"
+#include <memory>
+#include <string>
+#include <vector>
+
+class FaserSCT_ID;
+namespace TrackerDD { class SCT_DetectorManager; }
+
+class ThreeStationTrackSeedTool : public extends<AthAlgTool, ITrackSeedTool> {
+public:
+  ThreeStationTrackSeedTool(const std::string& type, const std::string& name, const IInterface* parent);
+  virtual ~ThreeStationTrackSeedTool() = default;
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+  virtual StatusCode run() override;
+
+  const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> initialTrackParameters() const override;
+  const std::shared_ptr<const Acts::Surface> initialSurface() const override;
+  const std::shared_ptr<std::vector<IndexSourceLink>> sourceLinks() const override;
+  const std::shared_ptr<IdentifierLink> idLinks() const override;
+  const std::shared_ptr<std::vector<Measurement>> measurements() const override;
+  const std::shared_ptr<std::vector<const Tracker::FaserSCT_Cluster*>> clusters() const override;
+
+private:
+  std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> m_initialTrackParameters;
+  std::shared_ptr<const Acts::Surface> m_initialSurface;
+  std::shared_ptr<std::vector<IndexSourceLink>> m_sourceLinks {};
+  std::shared_ptr<IdentifierLink> m_idLinks {};
+  std::shared_ptr<std::vector<Measurement>> m_measurements {};
+  std::shared_ptr<std::vector<const Tracker::FaserSCT_Cluster*>> m_clusters {};
+
+  const FaserSCT_ID* m_idHelper {nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detManager {nullptr};
+
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {
+      this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+  SG::ReadHandleKey<TrackCollection> m_trackCollection {
+      this, "TrackCollection", "SegmentFit", "Input track collection name" };
+  SG::ReadHandleKey<Tracker::FaserSCT_ClusterContainer> m_clusterContainerKey {
+      this, "ClusterContainer", "SCT_ClusterContainer"};
+
+  // position resolution of a cluster
+  Gaudi::Property<double> m_std_cluster {this, "std_cluster", 0.04};
+
+  // covariance of the initial parameters
+  Gaudi::Property<double> m_covLoc0 {this, "covLoc0", 1};
+  Gaudi::Property<double> m_covLoc1 {this, "covLoc1", 1};
+  Gaudi::Property<double> m_covPhi {this, "covPhi", 1};
+  Gaudi::Property<double> m_covTheta {this, "covTheta", 1};
+  Gaudi::Property<double> m_covQOverP {this, "covQOverP", 1};
+  Gaudi::Property<double> m_covTime {this, "covTime", 1};
+
+  Gaudi::Property<double> m_origin {this, "origin", 0, "z position of the reference surface"};
+
+  static Acts::CurvilinearTrackParameters get_params(
+      const Amg::Vector3D& position_st1, const Amg::Vector3D& position_st2, const Amg::Vector3D& position_st3, const Acts::BoundSymMatrix& cov, double origin);
+  static std::pair<double, double> momentum(const std::map<int, Amg::Vector3D>& pos, double B=0.57);
+};
+
+inline const std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>>
+ThreeStationTrackSeedTool::initialTrackParameters() const {
+  return m_initialTrackParameters;
+}
+
+inline const std::shared_ptr<const Acts::Surface>
+ThreeStationTrackSeedTool::initialSurface() const {
+  return m_initialSurface;
+}
+
+inline const std::shared_ptr<std::vector<IndexSourceLink>>
+ThreeStationTrackSeedTool::sourceLinks() const {
+  return m_sourceLinks;
+}
+
+inline const std::shared_ptr<IdentifierLink>
+ThreeStationTrackSeedTool::idLinks() const {
+  return m_idLinks;
+}
+
+inline const std::shared_ptr<std::vector<Measurement>>
+ThreeStationTrackSeedTool::measurements() const {
+  return m_measurements;
+}
+
+inline const std::shared_ptr<std::vector<const Tracker::FaserSCT_Cluster*>>
+ThreeStationTrackSeedTool::clusters() const {
+  return m_clusters;
+}
+
+
+#endif  // FASERACTSKALMANFILTER_THREESTATIONTRACKSEEDTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrackClassification.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrackClassification.h
new file mode 100644
index 0000000000000000000000000000000000000000..790cdb8f95643d270fe12e6e7a38ba0b6739382e
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrackClassification.h
@@ -0,0 +1,18 @@
+#ifndef FASERACTSKALMANFILTER_TRACKCLASSIFICATION_H
+#define FASERACTSKALMANFILTER_TRACKCLASSIFICATION_H
+
+#include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
+#include "TrackerSimData/TrackerSimDataCollection.h"
+
+struct ParticleHitCount {
+  int particleId;
+  size_t hitCount;
+};
+
+/// Identify all particles that contribute to a trajectory.
+void identifyContributingParticles(
+    const TrackerSimDataCollection& simDataCollection,
+    const FaserActsRecMultiTrajectory& trajectories, size_t tip,
+    std::vector<ParticleHitCount>& particleHitCounts);
+
+#endif  // FASERACTSKALMANFILTER_TRACKCLASSIFICATION_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrackSelection.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrackSelection.h
new file mode 100644
index 0000000000000000000000000000000000000000..858bb15c82b1e1d58ce8d92698e17aacac0b9baf
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrackSelection.h
@@ -0,0 +1,20 @@
+#ifndef FASERACTSKALMANFILTER_TRACKSELECTION_H
+#define FASERACTSKALMANFILTER_TRACKSELECTION_H
+
+#include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
+#include "TrackerSimData/TrackerSimDataCollection.h"
+#include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp"
+
+using TrackFitterResult =
+Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>;
+using TrackFinderResult = std::vector<TrackFitterResult>;
+
+struct TrackQuality {
+  Acts::CombinatorialKalmanFilterResult<IndexSourceLink> track;
+  size_t nMeasurements;
+  double chi2;
+};
+
+void selectTracks(TrackFinderResult& results, std::vector<TrackQuality>& trackQuality);
+
+#endif  // FASERACTSKALMANFILTER_TRACKSELECTION_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrajectoryWriterTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrajectoryWriterTool.h
index 659cea7fa08321bb43a38fcc598a7054b119262c..0643ad9ce973f65453e72063701befaa3b2789ec 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrajectoryWriterTool.h
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TrajectoryWriterTool.h
@@ -7,7 +7,7 @@
 
 class TFile;
 class TTree;
-class FaserActsRecMultiTrajectory;
+struct FaserActsRecMultiTrajectory;
 using TrajectoriesContainer = std::vector<FaserActsRecMultiTrajectory>;
 
 class TrajectoryWriterTool : public AthAlgTool {
@@ -21,7 +21,7 @@ class TrajectoryWriterTool : public AthAlgTool {
   virtual StatusCode finalize() override;
 
   void writeout(TrajectoriesContainer trajectories,
-      Acts::GeometryContext geoContext,std::vector<Acts::CurvilinearTrackParameters> traj ) const;
+      Acts::GeometryContext geoContext, std::vector<Acts::CurvilinearTrackParameters> traj ) const;
   
   void clearVariables() const;
 
@@ -60,6 +60,10 @@ class TrajectoryWriterTool : public AthAlgTool {
   mutable std::vector<float> m_x_hit;
   mutable std::vector<float> m_y_hit;
   mutable std::vector<float> m_z_hit;
+  mutable std::vector<float> m_meas_eLOC0;
+  mutable std::vector<float> m_meas_eLOC1;
+  mutable std::vector<float> m_meas_cov_eLOC0;
+  mutable std::vector<float> m_meas_cov_eLOC1;
   mutable std::vector<float> m_res_x_hit;
   mutable std::vector<float> m_res_y_hit;
   mutable std::vector<float> m_err_x_hit;
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TruthSeededTrackFinderTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TruthSeededTrackFinderTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..79425aa17c64a68e4c517170587a6fc84ac973c3
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TruthSeededTrackFinderTool.h
@@ -0,0 +1,113 @@
+#ifndef FASERACTSKALMANFILTER_TRUTHSEEDEDTRACKFINDERTOOL_H
+#define FASERACTSKALMANFILTER_TRUTHSEEDEDTRACKFINDERTOOL_H
+
+#include "Acts/Definitions/Units.hpp"
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/ITrackFinderTool.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/EventContext.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/StatusCode.h"
+#include "GeneratorObjects/McEventCollection.h"
+#include "StoreGate/ReadHandleKey.h"
+#include "TrackerSpacePoint/SpacePointForSeedCollection.h"
+#include <string>
+#include <vector>
+
+class FaserSCT_ID;
+namespace TrackerDD {
+class SCT_DetectorManager;
+}
+
+using namespace Acts::UnitLiterals;
+
+class TruthSeededTrackFinderTool : public extends<AthAlgTool, ITrackFinderTool> {
+public:
+  TruthSeededTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent);
+  virtual ~TruthSeededTrackFinderTool() = default;
+
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+  virtual StatusCode run() override;
+
+  virtual const std::shared_ptr<const Acts::CurvilinearTrackParameters> initialTrackParameters() const override {
+    return m_initialTrackParameters;
+  }
+
+  virtual const std::shared_ptr<const Acts::Surface> initialSurface() const override {
+    return m_initialSurface;
+  }
+
+  virtual const std::shared_ptr<std::vector<IndexSourceLink>> sourceLinks() const override {
+    return m_sourceLinks;
+  }
+
+  virtual const std::shared_ptr<IdentifierLink> idLinks() const override;
+
+  virtual const std::shared_ptr<std::vector<Measurement>> measurements() const override {
+    return m_measurements;
+  }
+
+  virtual const std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>> spacePoints() const override;
+
+private:
+  std::shared_ptr<const Acts::CurvilinearTrackParameters> m_initialTrackParameters;
+  std::shared_ptr<const Acts::Surface> m_initialSurface;
+  std::shared_ptr<std::vector<IndexSourceLink>> m_sourceLinks {};
+  std::shared_ptr<IdentifierLink> m_idLinks {};
+  std::shared_ptr<std::vector<Measurement>> m_measurements {};
+  std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>> m_spacePoints {};
+
+  const FaserSCT_ID* m_idHelper{nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detManager{nullptr};
+
+  static std::vector<Acts::Vector3> map2vector(const std::map<int, Acts::Vector3>& positions, int station);
+  static Acts::Vector3 average(const std::vector<Acts::Vector3>& spacePoints);
+  static std::pair<Acts::Vector3, Acts::Vector3> linear_fit(const std::vector<Acts::Vector3>& position);
+  static std::pair<double, double> momentum2(const std::map<int, Acts::Vector3>& hits, double B=0.57) ;
+
+  SG::ReadHandleKey<SpacePointForSeedCollection>  m_spacePointCollectionKey {
+    this, "FaserSpacePointsSeedsName", "Seeds_SpacePointContainer"};
+  SG::ReadHandleKey<McEventCollection> m_mcEventCollectionKey {this, "McEventCollection", "BeamTruthEvent"};
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {
+      this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+
+  Gaudi::Property<bool> m_useTrueInitialParameters {this, "useTrueInitialParameters", true};
+
+  // covariance matrix of measurement
+  Gaudi::Property<double> m_covMeas00 {this, "covMeas00", 0.0004};
+  Gaudi::Property<double> m_covMeas01 {this, "covMeas01", 0.01};
+  Gaudi::Property<double> m_covMeas10 {this, "covMeas10", 0.01};
+  Gaudi::Property<double> m_covMeas11 {this, "covMeas11", 0.64};
+
+  // smearing of initial parameters
+  Gaudi::Property<double> m_sigmaLoc0 {this, "sigmaLoc0", 0};
+  Gaudi::Property<double> m_sigmaLoc1 {this, "sigmaLoc1", 0};
+  Gaudi::Property<double> m_sigmaPhi {this, "sigmaPhi", 0};
+  Gaudi::Property<double> m_sigmaTheta {this, "sigmaTheta", 0};
+  Gaudi::Property<double> m_sigmaP {this, "sigmaP", 0};
+  Gaudi::Property<double> m_sigmaTime {this, "sigmaTime", 0};
+
+  // covariance parameters
+  Gaudi::Property<double> m_covLoc0 {this, "covLoc0", 1};
+  Gaudi::Property<double> m_covLoc1 {this, "covLoc1", 1};
+  Gaudi::Property<double> m_covPhi {this, "covPhi", 1};
+  Gaudi::Property<double> m_covTheta {this, "covTheta", 1};
+  Gaudi::Property<double> m_covQOverP {this, "covQOverP", 1};
+  Gaudi::Property<double> m_covTime {this, "covTime", 1};
+};
+
+inline const std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>>
+TruthSeededTrackFinderTool::spacePoints() const {
+  return m_spacePoints;
+}
+
+inline const std::shared_ptr<IdentifierLink>
+TruthSeededTrackFinderTool::idLinks() const {
+  return m_idLinks;
+}
+
+
+#endif // FASERACTSKALMANFILTER_TRUTHSEEDEDTRACKFINDERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TruthTrackFinderTool.h b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TruthTrackFinderTool.h
new file mode 100644
index 0000000000000000000000000000000000000000..3bfe35b68013a3ef323a43c245e046b7248be094
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/FaserActsKalmanFilter/TruthTrackFinderTool.h
@@ -0,0 +1,109 @@
+#ifndef FASERACTSKALMANFILTER_TRUTHTRACKFINDERTOOL_H
+#define FASERACTSKALMANFILTER_TRUTHTRACKFINDERTOOL_H
+
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "AthenaBaseComps/AthAlgTool.h"
+#include "FaserActsGeometryInterfaces/IFaserActsTrackingGeometryTool.h"
+#include "FaserActsKalmanFilter/ITrackFinderTool.h"
+#include "Gaudi/Property.h"
+#include "GaudiKernel/EventContext.h"
+#include "GaudiKernel/IInterface.h"
+#include "GaudiKernel/StatusCode.h"
+#include "StoreGate/ReadHandleKey.h"
+#include "TrackerSimEvent/FaserSiHitCollection.h"
+#include <string>
+#include <vector>
+
+class FaserSCT_ID;
+namespace TrackerDD {
+class SCT_DetectorManager;
+}
+
+
+class TruthTrackFinderTool : public extends<AthAlgTool, ITrackFinderTool> {
+public:
+  TruthTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent);
+  virtual ~TruthTrackFinderTool() = default;
+
+  virtual StatusCode initialize() override;
+  virtual StatusCode finalize() override;
+  virtual StatusCode run() override;
+
+  virtual const std::shared_ptr<const Acts::CurvilinearTrackParameters> initialTrackParameters() const override {
+    return m_initialTrackParameters;
+  }
+
+  virtual const std::shared_ptr<const Acts::Surface> initialSurface() const override {
+    return m_initialSurface;
+  }
+
+  virtual const std::shared_ptr<std::vector<IndexSourceLink>> sourceLinks() const override {
+    return m_sourceLinks;
+  }
+
+  virtual const std::shared_ptr<IdentifierLink> idLinks() const override;
+
+  virtual const std::shared_ptr<std::vector<Measurement>> measurements() const override {
+    return m_measurements;
+  }
+
+  virtual const std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>> spacePoints() const override;
+
+private:
+  std::shared_ptr<const Acts::CurvilinearTrackParameters> m_initialTrackParameters;
+  std::shared_ptr<const Acts::Surface> m_initialSurface;
+  std::shared_ptr<std::vector<IndexSourceLink>> m_sourceLinks {};
+  std::shared_ptr<IdentifierLink> m_idLinks {};
+  std::shared_ptr<std::vector<Measurement>> m_measurements {};
+  std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>> m_spacePoints {};
+
+  const FaserSCT_ID* m_idHelper{nullptr};
+  const TrackerDD::SCT_DetectorManager* m_detManager{nullptr};
+
+  SG::ReadHandleKey<FaserSiHitCollection> m_siHitCollectionKey {
+    this, "FaserSiHitCollection", "SCT_Hits"};
+  ToolHandle<IFaserActsTrackingGeometryTool> m_trackingGeometryTool {
+    this, "TrackingGeometryTool", "FaserActsTrackingGeometryTool"};
+
+  // smearing of measurements
+  Gaudi::Property<double> m_sigma0 {this, "sigmaMeasLoc0", 0};
+  Gaudi::Property<double> m_sigma1 {this, "sigmaMeasLoc1", 0};
+
+  // covariance of the measurements
+  Gaudi::Property<double> m_covMeasLoc0 {this, "covMeasLoc0", 0.01};
+  Gaudi::Property<double> m_covMeasLoc1 {this, "covMeasLoc1", 0.01};
+
+  // smearing of initial parameters
+  Gaudi::Property<double> m_sigmaLoc0 {this, "sigmaLoc0", 0};
+  Gaudi::Property<double> m_sigmaLoc1 {this, "sigmaLoc1", 0};
+  Gaudi::Property<double> m_sigmaPhi {this, "sigmaPhi", 0};
+  Gaudi::Property<double> m_sigmaTheta {this, "sigmaTheta", 0};
+  Gaudi::Property<double> m_sigmaP {this, "sigmaP", 0};
+  Gaudi::Property<double> m_sigmaTime {this, "sigmaTime", 0};
+
+  // covariance of the initial parameters
+  Gaudi::Property<double> m_covLoc0 {this, "covLoc0", 1};
+  Gaudi::Property<double> m_covLoc1 {this, "covLoc1", 1};
+  Gaudi::Property<double> m_covPhi {this, "covPhi", 1};
+  Gaudi::Property<double> m_covTheta {this, "covTheta", 1};
+  Gaudi::Property<double> m_covQOverP {this, "covQOverP", 1};
+  Gaudi::Property<double> m_covTime {this, "covTime", 1};
+
+  // TODO get parameters of first layer from tracker geometry
+  Gaudi::Property<int> m_first_station {this, "first_station", 1, "first station for which the initial track parameters are calculated"};
+  Gaudi::Property<int> m_first_layer {this, "first_layer", 0, "first layer for which the initial track parameters are calculated"};
+  Gaudi::Property<int> m_first_side {this, "first_side", 1, "first side for which the initial track parameters are calculated"};
+};
+
+inline const std::shared_ptr<std::vector<Tracker::FaserSCT_SpacePoint>>
+    TruthTrackFinderTool::spacePoints() const {
+  return m_spacePoints;
+}
+
+inline const std::shared_ptr<IdentifierLink>
+TruthTrackFinderTool::idLinks() const {
+  return m_idLinks;
+}
+
+
+#endif // FASERACTSKALMANFILTER_TRUTHTRACKFINDERTOOL_H
diff --git a/Tracking/Acts/FaserActsKalmanFilter/python/CombinatorialKalmanFilterConfig.py b/Tracking/Acts/FaserActsKalmanFilter/python/CombinatorialKalmanFilterConfig.py
index 46c01fd06268dddf34867f76d358e4ab10f961e3..8021e9319d7dcf7d7205a0c1547f9bbffd4d5625 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/python/CombinatorialKalmanFilterConfig.py
+++ b/Tracking/Acts/FaserActsKalmanFilter/python/CombinatorialKalmanFilterConfig.py
@@ -1,43 +1,78 @@
 # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS and FASER collaborations
 
+from FaserSCT_GeoModel.FaserSCT_GeoModelConfig import FaserSCT_GeometryCfg
 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
 from AthenaConfiguration.ComponentFactory import CompFactory
 from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+from MagFieldServices.MagFieldServicesConfig import MagneticFieldSvcCfg
+from FaserActsGeometry.ActsGeometryConfig import ActsTrackingGeometrySvcCfg
 
 
-def CombinatorialKalmanFilter_OutputCfg(flags):
+def FaserActsAlignmentCondAlgCfg(flags, **kwargs):
     acc = ComponentAccumulator()
-    acc.merge(OutputStreamCfg(flags, "ESD"))
-    ostream = acc.getEventAlgo("OutputStreamESD")
-    ostream.TakeItemsFromInput = True
+    acc.addCondAlgo(CompFactory.FaserActsAlignmentCondAlg(name="FaserActsAlignmentCondAlg", **kwargs))
     return acc
 
-def CombinatorialKalmanFilter_OutputAODCfg(flags):
+
+def CombinatorialKalmanFilter_OutputCfg(flags):
     acc = ComponentAccumulator()
     itemList = ["xAOD::EventInfo#*",
                 "xAOD::EventAuxInfo#*",
-                "FaserSCT_RDO_Container#*",
-                "xAOD::FaserTriggerData#*",
-                "xAOD::FaserTriggerDataAux#*",
-                "ScintWaveformContainer#*",
                 "TrackCollection#*",
-                "xAOD::WaveformHitContainer#*",
-                "xAOD::WaveformHitAuxContainer#*",
-                "xAOD::WaveformClock#*",
-                "xAOD::WaveformClockAuxInfo#*"
                 ]
-    acc.merge(OutputStreamCfg(flags, "AOD",itemList))
-    ostream = acc.getEventAlgo("OutputStreamAOD")
+    acc.merge(OutputStreamCfg(flags, "ESD", itemList))
+    ostream = acc.getEventAlgo("OutputStreamESD")
     ostream.TakeItemsFromInput = True
     return acc
 
 
-
 def CombinatorialKalmanFilterCfg(flags, **kwargs):
-    acc = ComponentAccumulator()
-    kwargs.setdefault("SpacePointsSCTName", "SCT_SpacePointContainer")
-    combinatorialKalmanFilterAlg = CompFactory.CombinatorialKalmanFilterAlg(**kwargs)
-    acc.addEventAlgo(combinatorialKalmanFilterAlg)
+    # acc = ComponentAccumulator()
+    acc = FaserSCT_GeometryCfg(flags)
+    acc.merge(MagneticFieldSvcCfg(flags))
+    # acc.merge(FaserActsAlignmentCondAlgCfg(flags))
+    acts_tracking_geometry_svc = ActsTrackingGeometrySvcCfg(flags)
+    acc.merge(acts_tracking_geometry_svc )
+
+# track_seed_tool = CompFactory.ClusterTrackSeedTool()
+    track_seed_tool = CompFactory.ThreeStationTrackSeedTool()
+    sigma_loc0 = 1.9e-2
+    sigma_loc1 = 9e-1
+    sigma_phi = 3.3e-2
+    sigma_theta = 2.9e-4
+    p = 1000
+    sigma_p = 0.1 * p
+    sigma_qop = sigma_p / (p * p)
+    initial_variance_inflation = [100, 100, 100, 100, 100]
+    track_seed_tool.covLoc0 = initial_variance_inflation[0] * sigma_loc1 * sigma_loc1
+    track_seed_tool.covLoc1 = initial_variance_inflation[1] * sigma_loc0 * sigma_loc0
+    track_seed_tool.covPhi = initial_variance_inflation[2] * sigma_phi * sigma_phi
+    track_seed_tool.covTheta = initial_variance_inflation[3] * sigma_theta * sigma_theta
+    track_seed_tool.covQOverP = initial_variance_inflation[4] * sigma_qop * sigma_qop
+    track_seed_tool.std_cluster = 0.023
+    track_seed_tool.origin = 0
+
+    trajectory_states_writer_tool = CompFactory.RootTrajectoryStatesWriterTool()
+    trajectory_states_writer_tool.MC = True
+
+    trajectory_summary_writer_tool = CompFactory.RootTrajectorySummaryWriterTool()
+
+    actsExtrapolationTool = CompFactory.FaserActsExtrapolationTool("FaserActsExtrapolationTool")
+    actsExtrapolationTool.MaxSteps = 1000
+    actsExtrapolationTool.TrackingGeometryTool = CompFactory.FaserActsTrackingGeometryTool("TrackingGeometryTool")
+    performance_writer_tool = CompFactory.PerformanceWriterTool("PerformanceWriterTool")
+    performance_writer_tool.ExtrapolationTool = actsExtrapolationTool
+
+    ckf = CompFactory.CombinatorialKalmanFilterAlg(**kwargs)
+    ckf.TrackSeed = track_seed_tool
+    ckf.ActsLogging = "INFO"
+    ckf.RootTrajectoryStatesWriterTool = trajectory_states_writer_tool
+    ckf.RootTrajectorySummaryWriterTool = trajectory_summary_writer_tool
+    ckf.PerformanceWriterTool = performance_writer_tool
+    ckf.nTrajectories = 2
+
+    ckf.nMax = 1
+    ckf.chi2Max = 15
+    acc.addEventAlgo(ckf)
     acc.merge(CombinatorialKalmanFilter_OutputCfg(flags))
-    acc.merge(CombinatorialKalmanFilter_OutputAODCfg(flags))
     return acc
diff --git a/Tracking/Acts/FaserActsKalmanFilter/python/FaserActsKalmanFilterConfig.py b/Tracking/Acts/FaserActsKalmanFilter/python/FaserActsKalmanFilterConfig.py
index 6d6167c051b868d6032355e5bfd58d2a0f8296bd..cc44658845f4202f7295faf93de273cee776c552 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/python/FaserActsKalmanFilterConfig.py
+++ b/Tracking/Acts/FaserActsKalmanFilter/python/FaserActsKalmanFilterConfig.py
@@ -1,50 +1,161 @@
 # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS and FASER collaborations
 
+from math import pi
 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
 from AthenaConfiguration.ComponentFactory import CompFactory
 from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
 from MagFieldServices.MagFieldServicesConfig import MagneticFieldSvcCfg
 
-FaserActsKalmanFilterAlg,FaserActsTrackingGeometrySvc, FaserActsTrackingGeometryTool, FaserActsExtrapolationTool,FaserActsAlignmentCondAlg, THistSvc = CompFactory.getComps("FaserActsKalmanFilterAlg","FaserActsTrackingGeometrySvc", "FaserActsTrackingGeometryTool", "FaserActsExtrapolationTool","FaserActsAlignmentCondAlg", "THistSvc")
+# FaserActsKalmanFilterAlg,FaserActsTrackingGeometrySvc, FaserActsTrackingGeometryTool, FaserActsExtrapolationTool,FaserActsAlignmentCondAlg, THistSvc = CompFactory.getComps("FaserActsKalmanFilterAlg","FaserActsTrackingGeometrySvc", "FaserActsTrackingGeometryTool", "FaserActsExtrapolationTool","FaserActsAlignmentCondAlg", "THistSvc")
+#
+# def FaserActsTrackingGeometrySvcCfg(flags, **kwargs):
+#     acc = ComponentAccumulator()
+#     acc.addService(FaserActsTrackingGeometrySvc(name = "FaserActsTrackingGeometrySvc", **kwargs))
+#     return acc
+#
+# def FaserActsAlignmentCondAlgCfg(flags, **kwargs):
+#     acc = ComponentAccumulator()
+#     acc.addCondAlgo(CompFactory.FaserActsAlignmentCondAlg(name = "FaserActsAlignmentCondAlg", **kwargs))
+#     #acc.addCondAlgo(CompFactory.NominalAlignmentCondAlg(name = "NominalAlignmentCondAlg", **kwargs))
+#     return acc
+#
+# def FaserActsKalmanFilterCfg(flags, **kwargs):
+#     acc = ComponentAccumulator()
+#     # Initialize field service
+#     acc.merge(MagneticFieldSvcCfg(flags))
+#
+#     acc.merge(FaserActsTrackingGeometrySvcCfg(flags, **kwargs))
+#     acc.merge(FaserActsAlignmentCondAlgCfg(flags, **kwargs))
+#
+#     kwargs.setdefault("FaserSpacePointsSeedsName", "Seeds_SpacePointContainer")
+#     actsKalmanFilterAlg = FaserActsKalmanFilterAlg(**kwargs)
+#     #actsKalmanFilterAlg.TrackingGeometryTool = FaserActsTrackingGeometryTool("TrackingGeometryTool")
+#     actsExtrapolationTool=FaserActsExtrapolationTool("FaserActsExtrapolationTool")
+#     actsExtrapolationTool.FieldMode="FASER"
+#     #actsExtrapolationTool.FieldMode="Constant"
+#     actsExtrapolationTool.TrackingGeometryTool=FaserActsTrackingGeometryTool("TrackingGeometryTool")
+#     actsKalmanFilterAlg.ExtrapolationTool=actsExtrapolationTool
+#     acc.addEventAlgo(actsKalmanFilterAlg)
+#     histSvc= THistSvc()
+#     histSvc.Output += [ "KalmanTracks DATAFILE='KalmanTracks.root' OPT='RECREATE'" ]
+#     acc.addService(histSvc)
+#     acc.merge(FaserActsKalmanFilter_OutputCfg(flags))
+#     return  acc
+#
+# def FaserActsKalmanFilter_OutputCfg(flags):
+#     """Return ComponentAccumulator with Output for SCT. Not standalone."""
+#     acc = ComponentAccumulator()
+#     acc.merge(OutputStreamCfg(flags, "ESD"))
+#     ostream = acc.getEventAlgo("OutputStreamESD")
+#     ostream.TakeItemsFromInput = True
+#     return acc
 
-def FaserActsTrackingGeometrySvcCfg(flags, **kwargs):
+def FaserActsKalmanFilter_OutputCfg(flags):
     acc = ComponentAccumulator()
-    acc.addService(FaserActsTrackingGeometrySvc(name = "FaserActsTrackingGeometrySvc", **kwargs))
-    return acc 
+    itemList = ["xAOD::EventInfo#*",
+                "xAOD::EventAuxInfo#*",
+                "TrackCollection#*",
+                ]
+    acc.merge(OutputStreamCfg(flags, "ESD", itemList))
+    ostream = acc.getEventAlgo("OutputStreamESD")
+    ostream.TakeItemsFromInput = True
+    return acc
 
-def FaserActsAlignmentCondAlgCfg(flags, **kwargs):
+def FaserActsKalmanFilter_OutputAODCfg(flags):
     acc = ComponentAccumulator()
-    acc.addCondAlgo(CompFactory.FaserActsAlignmentCondAlg(name = "FaserActsAlignmentCondAlg", **kwargs))
-    #acc.addCondAlgo(CompFactory.NominalAlignmentCondAlg(name = "NominalAlignmentCondAlg", **kwargs))
+    itemList = ["xAOD::EventInfo#*",
+                "xAOD::EventAuxInfo#*",
+                "FaserSCT_RDO_Container#*",
+                "xAOD::FaserTriggerData#*",
+                "xAOD::FaserTriggerDataAux#*",
+                "ScintWaveformContainer#*",
+                "TrackCollection#*",
+                "xAOD::WaveformHitContainer#*",
+                "xAOD::WaveformHitAuxContainer#*",
+                "xAOD::WaveformClock#*",
+                "xAOD::WaveformClockAuxInfo#*"
+                ]
+    acc.merge(OutputStreamCfg(flags, "AOD",itemList))
+    ostream = acc.getEventAlgo("OutputStreamAOD")
+    ostream.TakeItemsFromInput = True
     return acc
 
+
 def FaserActsKalmanFilterCfg(flags, **kwargs):
     acc = ComponentAccumulator()
-    # Initialize field service
-    acc.merge(MagneticFieldSvcCfg(flags))
-
-    acc.merge(FaserActsTrackingGeometrySvcCfg(flags, **kwargs))
-    acc.merge(FaserActsAlignmentCondAlgCfg(flags, **kwargs))
-    
-    kwargs.setdefault("FaserSpacePointsSeedsName", "Seeds_SpacePointContainer")
-    actsKalmanFilterAlg = FaserActsKalmanFilterAlg(**kwargs)
-    #actsKalmanFilterAlg.TrackingGeometryTool = FaserActsTrackingGeometryTool("TrackingGeometryTool")
-    actsExtrapolationTool=FaserActsExtrapolationTool("FaserActsExtrapolationTool")
-    actsExtrapolationTool.FieldMode="FASER"
-    #actsExtrapolationTool.FieldMode="Constant"
-    actsExtrapolationTool.TrackingGeometryTool=FaserActsTrackingGeometryTool("TrackingGeometryTool")
-    actsKalmanFilterAlg.ExtrapolationTool=actsExtrapolationTool
-    acc.addEventAlgo(actsKalmanFilterAlg)
-    histSvc= THistSvc()
-    histSvc.Output += [ "KalmanTracks DATAFILE='KalmanTracks.root' OPT='RECREATE'" ]
-    acc.addService(histSvc)
-    acc.merge(FaserActsKalmanFilter_OutputCfg(flags))
-    return  acc
+    track_finder_tool = CompFactory.MultiTrackFinderTool()
+    # track_finder_tool = CompFactory.SegmentFitClusterTrackFinderTool()
+    # track_finder_tool = CompFactory.SegmentFitTrackFinderTool()
+    sigma_loc0 = 1.9e-2
+    sigma_loc1 = 9e-1
+    sigma_phi = 3.3e-2
+    sigma_theta = 2.9e-4
+    sigma_p_rel = 0.1
+    p = 1
+    sigma_qop = 0.1 * p / (p * p)
+    initial_variance_inflation = [100, 100, 1000, 1000, 1000]
+    track_finder_tool.covLoc0 = initial_variance_inflation[0] * sigma_loc1 * sigma_loc1
+    track_finder_tool.covLoc1 = initial_variance_inflation[1] * sigma_loc0 * sigma_loc0
+    track_finder_tool.covPhi = initial_variance_inflation[2] * sigma_phi * sigma_phi
+    track_finder_tool.covTheta = initial_variance_inflation[3] * sigma_theta * sigma_theta
+    track_finder_tool.covQOverP = initial_variance_inflation[4] * sigma_qop * sigma_qop
+    # track_finder_tool.sigmaCluster = 0.04
+    # track_finder_tool.covLoc0 = 1e-1
+    # track_finder_tool.covLoc1 = 1e-1
+    # track_finder_tool.covPhi = 1e-1
+    # track_finder_tool.covTheta = 1e-1
+    # track_finder_tool.covQOverP = 1e-1
 
-def FaserActsKalmanFilter_OutputCfg(flags):
-    """Return ComponentAccumulator with Output for SCT. Not standalone."""
-    acc = ComponentAccumulator()
-    acc.merge(OutputStreamCfg(flags, "ESD"))
-    ostream = acc.getEventAlgo("OutputStreamESD")
-    ostream.TakeItemsFromInput = True
+    # track_finder_tool = CompFactory.TruthSeededTrackFinderTool()
+    # track_finder_tool.useTrueInitialParameters = False
+    # track_finder_tool.covMeas00 = 0.0004
+    # track_finder_tool.covMeas01 = 0.01
+    # track_finder_tool.covMeas10 = 0.01
+    # track_finder_tool.covMeas11 = 0.64
+    # track_finder_tool.covLoc0 = 1e-4
+    # track_finder_tool.covLoc1 = 1e-4
+    # track_finder_tool.covPhi = 1e-4
+    # track_finder_tool.covTheta = 1e-4
+    # track_finder_tool.covQOverP = 1e-4
+
+    # track_finder_tool.covLoc0 = 1e-1
+    # track_finder_tool.covLoc1 = 1e-1
+    # track_finder_tool.covPhi = 1e-1
+    # track_finder_tool.covTheta = 1e-1
+    # track_finder_tool.covQOverP = 1e-1
+
+    # track_finder_tool.sigmaLoc0 = 0.02
+    # track_finder_tool.sigmaLoc1 = 0.8
+    # track_finder_tool.sigmaPhi = 2 * pi/180
+    # track_finder_tool.sigmaTheta = 2 * pi/180
+    # track_finder_tool.sigmaP = 0.1
+
+    # track_finder_tool = CompFactory.TruthTrackFinderTool()
+    # track_finder_tool.first_side = 1
+    # track_finder_tool.sigmaMeasLoc0 = 0.02
+    # track_finder_tool.sigmaMeasLoc1 = 0.8
+    # track_finder_tool.covMeasLoc0 = 0.02 * 0.02 * 10
+    # track_finder_tool.covMeasLoc1 = 0.8 * 0.8 * 10
+    # track_finder_tool.sigmaLoc0 = 0
+    # track_finder_tool.sigmaLoc1 = 0
+    # track_finder_tool.sigmaPhi = 0
+    # track_finder_tool.sigmaTheta = 0
+    # track_finder_tool.sigmaP = 0
+    # track_finder_tool.covLoc0 = 1e-3
+    # track_finder_tool.covLoc1 = 1e-3
+    # track_finder_tool.covPhi = 1e-3
+    # track_finder_tool.covTheta = 1e-3
+    # track_finder_tool.covQOverP = 1e-3
+    trajectory_writer_tool = CompFactory.TrajectoryWriterTool()
+    trajectory_writer_tool.FilePath = "KalmanFilterTrajectories.root"
+    # trajectory_states_writer_tool = CompFactory.RootTrajectoryStatesWriterTool()
+    # trajectory_states_writer_tool.FilePath = "TrajectoryStates.root"
+    kalman_filter = CompFactory.FaserActsKalmanFilterAlg(**kwargs)
+    # kalman_filter.RootTrajectoryStatesWriterTool = trajectory_states_writer_tool
+    kalman_filter.OutputTool = trajectory_writer_tool
+    kalman_filter.TrackFinderTool = track_finder_tool
+    kalman_filter.ActsLogging = "VERBOSE"
+    acc.addEventAlgo(kalman_filter)
+    acc.merge(FaserActsKalmanFilter_OutputCfg(flags))
+    # acc.merge(FaserActsKalmanFilter_OutputAODCfg(flags))
     return acc
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/ClusterTrackSeedTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/ClusterTrackSeedTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8d78c9f0eaa1088675806b0a9aee1472ce3396ff
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/ClusterTrackSeedTool.cxx
@@ -0,0 +1,129 @@
+#include "FaserActsKalmanFilter/ClusterTrackSeedTool.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrackerPrepRawData/FaserSCT_ClusterCollection.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "Identifier/Identifier.h"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+
+
+// FIXME make this a method of ClusterTrackSeedTool
+std::pair<double, double> momentum(const std::map<int, Amg::Vector3D>& pos, double B=0.57) {
+  Acts::Vector3 vec_l = pos.at(3) - pos.at(1);
+  double abs_l = std::sqrt(vec_l.y() * vec_l.y() + vec_l.z() * vec_l.z());
+  double t = (pos.at(2).z() - pos.at(1).z()) / (pos.at(3).z() - pos.at(1).z());
+  Acts::Vector3 vec_m = pos.at(1) + t * vec_l;
+  Acts::Vector3 vec_s = pos.at(2) - vec_m;
+  double abs_s = std::sqrt(vec_s.y() * vec_s.y() + vec_s.z() * vec_s.z());
+  double p_yz = 0.3 * abs_l * abs_l * B / (8 * abs_s * 1000);
+  double charge = vec_s.y() < 0 ? 1 : -1;
+  return std::make_pair(p_yz, charge);
+}
+
+
+ClusterTrackSeedTool::ClusterTrackSeedTool(
+    const std::string& type, const std::string& name, const IInterface* parent)
+    : base_class(type, name, parent) {}
+
+
+StatusCode ClusterTrackSeedTool::initialize() {
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+  ATH_CHECK(detStore()->retrieve(m_detManager, "SCT"));
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+  ATH_CHECK(m_trackCollection.initialize());
+  ATH_CHECK(m_clusterContainerKey.initialize());
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ClusterTrackSeedTool::run() {
+  // create track seeds for multiple tracks
+  SG::ReadHandle<TrackCollection> trackCollection {m_trackCollection};
+  ATH_CHECK(trackCollection.isValid());
+
+  SG::ReadHandle<Tracker::FaserSCT_ClusterContainer> clusterContainer {m_clusterContainerKey};
+  ATH_CHECK(clusterContainer.isValid());
+
+  using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
+  std::shared_ptr<IdentifierMap> identifierMap = m_trackingGeometryTool->getIdentifierMap();
+
+  const int kSize = 1;
+  using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, kSize>;
+  std::array<Acts::BoundIndices, kSize> Indices = {Acts::eBoundLoc0};
+  std::vector<IndexSourceLink> sourceLinks;
+  std::vector<Measurement> measurements;
+  std::map<Index, Identifier> identifierLinkMap;
+  std::vector<const Tracker::FaserSCT_Cluster*> clusters {};
+  for (const Tracker::FaserSCT_ClusterCollection* clusterCollection : *clusterContainer) {
+    for (const Tracker::FaserSCT_Cluster* cluster : *clusterCollection) {
+      Identifier id = cluster->detectorElement()->identify();
+      identifierLinkMap[measurements.size()] = id;
+      Acts::GeometryIdentifier geoId = identifierMap->at(id);
+      IndexSourceLink sourceLink(geoId, measurements.size());
+      // create measurement
+      const auto& par = cluster->localPosition();
+      Eigen::Matrix<double, 1, 1> pos {par.x(),};
+      Eigen::Matrix<double, 1, 1> cov {m_std_cluster * m_std_cluster,};
+      ThisMeasurement meas(sourceLink, Indices, pos, cov);
+      sourceLinks.push_back(sourceLink);
+      measurements.emplace_back(std::move(meas));
+      clusters.push_back(cluster);
+    }
+  }
+
+
+  std::map<int, std::vector<Amg::Vector3D>> station_position_map;
+  for (const Trk::Track* track : *trackCollection) {
+    for (const Trk::TrackStateOnSurface* trackState : *(track->trackStateOnSurfaces())) {
+      auto clusterOnTrack = dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack*> (trackState->measurementOnTrack());
+      if (clusterOnTrack) {
+        Identifier id = clusterOnTrack->identify();
+        int station = m_idHelper->station(id);
+        auto fitParameters = track->trackParameters()->front();
+        Amg::Vector3D fitPosition = fitParameters->position();
+        station_position_map[station].push_back(fitPosition);
+        break;
+      }
+    }
+  }
+
+  Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Zero();
+  cov(Acts::eBoundLoc0, Acts::eBoundLoc0) = m_covLoc0;
+  cov(Acts::eBoundLoc1, Acts::eBoundLoc1) = m_covLoc1;
+  cov(Acts::eBoundPhi, Acts::eBoundPhi) = m_covPhi;
+  cov(Acts::eBoundTheta, Acts::eBoundTheta) = m_covTheta;
+  cov(Acts::eBoundQOverP, Acts::eBoundQOverP) = m_covQOverP;
+  cov(Acts::eBoundTime, Acts::eBoundTime) = m_covTime;
+
+  std::vector<Acts::CurvilinearTrackParameters> initParams {};
+  for (const Amg::Vector3D& pos1 : station_position_map[1]) {
+    for (const Amg::Vector3D& pos2 : station_position_map[2]) {
+      initParams.push_back(get_params(pos1, pos2, cov, m_origin));
+    }
+  }
+
+  m_initialTrackParameters = std::make_shared<std::vector<Acts::CurvilinearTrackParameters>>(initParams);
+  m_sourceLinks = std::make_shared<std::vector<IndexSourceLink>>(sourceLinks);
+  m_idLinks = std::make_shared<IdentifierLink>(identifierLinkMap);
+  m_measurements = std::make_shared<std::vector<Measurement>>(measurements);
+  m_initialSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3 {0, 0, m_origin}, Acts::Vector3{0, 0, 1});
+  m_clusters = std::make_shared<std::vector<const Tracker::FaserSCT_Cluster*>>(clusters);
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ClusterTrackSeedTool::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+
+Acts::CurvilinearTrackParameters ClusterTrackSeedTool::get_params(
+    const Amg::Vector3D& position_st1, const Amg::Vector3D& position_st2, const Acts::BoundSymMatrix& cov, double origin) {
+  Acts::Vector3 dir = position_st2 - position_st1;
+  Acts::Vector3 pos = position_st1 - (position_st1.z() - origin)/dir.z() * dir;
+  Acts::Vector4 pos4 {pos.x(), pos.y(), pos.z(), 0};
+  return Acts::CurvilinearTrackParameters(pos4, dir, 100000000, 1, cov);
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CombinatorialKalmanFilterAlg.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/CombinatorialKalmanFilterAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ebc292de212566ae8e79cc793eabcdc6e679c9fa
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/CombinatorialKalmanFilterAlg.cxx
@@ -0,0 +1,388 @@
+#include "FaserActsKalmanFilter/CombinatorialKalmanFilterAlg.h"
+
+#include "StoreGate/ReadHandle.h"
+#include "StoreGate/ReadCondHandleKey.h"
+#include "TrackerSpacePoint/FaserSCT_SpacePointCollection.h"
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrkPrepRawData/PrepRawData.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+#include "TrkRIO_OnTrack/RIO_OnTrack.h"
+#include "TrkSurfaces/Surface.h"
+#include "Identifier/Identifier.h"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+#include "Acts/EventData/TrackParameters.hpp"
+#include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include "FaserActsKalmanFilter/Measurement.h"
+#include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
+#include "Acts/Surfaces/PerigeeSurface.hpp"
+#include "Acts/MagneticField/MagneticFieldContext.hpp"
+#include "FaserActsKalmanFilter/TrackSelection.h"
+#include "FaserActsKalmanFilter/MyAmbiguitySolver.h"
+#include <algorithm>
+
+using TrajectoriesContainer = std::vector<FaserActsRecMultiTrajectory>;
+std::array<Acts::BoundIndices, 2> indices = {Acts::eBoundLoc0, Acts::eBoundLoc1};
+
+
+CombinatorialKalmanFilterAlg::CombinatorialKalmanFilterAlg(
+    const std::string& name, ISvcLocator* pSvcLocator)
+    : AthAlgorithm(name, pSvcLocator) {}
+
+
+StatusCode CombinatorialKalmanFilterAlg::initialize() {
+  ATH_CHECK(m_fieldCondObjInputKey.initialize());
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+  ATH_CHECK(m_trackSeedTool.retrieve());
+  //  ATH_CHECK(m_trackCollection.initialize());
+  if (m_performanceWriter) {
+    ATH_CHECK(m_performanceWriterTool.retrieve());
+  }
+  if (m_statesWriter) {
+    ATH_CHECK(m_trajectoryStatesWriterTool.retrieve());
+  }
+  if (m_summaryWriter) {
+    ATH_CHECK(m_trajectorySummaryWriterTool.retrieve());
+  }
+  ATH_CHECK(detStore()->retrieve(m_idHelper,"FaserSCT_ID"));
+  m_fit = makeTrackFinderFunction(m_trackingGeometryTool->trackingGeometry(),
+                                  m_resolvePassive, m_resolveMaterial, m_resolveSensitive);
+  // FIXME fix Acts logging level
+  if (m_actsLogging == "VERBOSE") {
+    m_logger = Acts::getDefaultLogger("KalmanFitter", Acts::Logging::VERBOSE);
+  } else if (m_actsLogging == "DEBUG") {
+    m_logger = Acts::getDefaultLogger("KalmanFitter", Acts::Logging::DEBUG);
+  } else {
+    m_logger = Acts::getDefaultLogger("KalmanFitter", Acts::Logging::INFO);
+  }
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode CombinatorialKalmanFilterAlg::execute() {
+
+  const EventContext& ctx = Gaudi::Hive::currentContext();
+
+  ATH_CHECK(m_trackCollection.initialize());
+  SG::WriteHandle<TrackCollection> trackContainer{m_trackCollection,ctx};
+  std::unique_ptr<TrackCollection> outputTracks = std::make_unique<TrackCollection>();
+
+  std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry
+      = m_trackingGeometryTool->trackingGeometry();
+
+  const FaserActsGeometryContext& gctx = m_trackingGeometryTool->getNominalGeometryContext();
+  auto geoctx = gctx.context();
+  Acts::MagneticFieldContext magctx = getMagneticFieldContext(ctx);
+  Acts::CalibrationContext calctx;
+
+  CHECK(m_trackSeedTool->run());
+  std::shared_ptr<const Acts::Surface> initialSurface =
+      m_trackSeedTool->initialSurface();
+  std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> initialParameters =
+      m_trackSeedTool->initialTrackParameters();
+  std::shared_ptr<std::vector<IndexSourceLink>> sourceLinks =
+      m_trackSeedTool->sourceLinks();
+  std::shared_ptr<IdentifierLink> idLinks = m_trackSeedTool->idLinks();
+  std::shared_ptr<std::vector<Measurement>> measurements = m_trackSeedTool->measurements();
+  std::shared_ptr<std::vector<const Tracker::FaserSCT_Cluster*>> clusters = m_trackSeedTool->clusters();
+
+  TrajectoriesContainer trajectories;
+  trajectories.reserve(initialParameters->size());
+
+  Acts::PropagatorPlainOptions pOptions;
+  pOptions.maxSteps = m_maxSteps;
+
+  Acts::GeometryContext geoContext = m_trackingGeometryTool->getNominalGeometryContext().context();
+  Acts::MagneticFieldContext magFieldContext = getMagneticFieldContext(ctx);
+  Acts::CalibrationContext calibContext;
+  Acts::MeasurementSelector::Config measurementSelectorCfg = {
+    {Acts::GeometryIdentifier(), {m_chi2Max, m_nMax}},
+  };
+
+  // Set the CombinatorialKalmanFilter options
+  CombinatorialKalmanFilterAlg::TrackFinderOptions options(
+      geoContext, magFieldContext, calibContext,
+      IndexSourceLinkAccessor(), MeasurementCalibrator(*measurements),
+      Acts::MeasurementSelector(measurementSelectorCfg),
+      Acts::LoggerWrapper{*m_logger}, pOptions, &(*initialSurface));
+
+  // Perform the track finding for all initial parameters
+  ATH_MSG_DEBUG("Invoke track finding with " << initialParameters->size() << " seeds.");
+  IndexSourceLinkContainer tmp;
+  for (const auto& sl : *sourceLinks) {
+    tmp.emplace_hint(tmp.end(), sl);
+  }
+  auto results = (*m_fit)(tmp, *initialParameters, options);
+
+  std::vector<TrackQuality> trackQuality;
+  selectTracks(results, trackQuality);
+  for (const auto& track : trackQuality) {
+    ATH_MSG_VERBOSE("?? " << track.nMeasurements << ", " << track.chi2);
+  }
+
+  TrackFinderResult selectedResults;
+  for (size_t i = 0; i < std::min(trackQuality.size(), (size_t)m_nTrajectories); ++i) {
+    selectedResults.push_back(Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>(trackQuality[i].track));
+  }
+  /*
+  TrackFinderResult minTrackRequirements {};
+  for (auto& result : results) {
+    if (not result.ok()) {
+      continue;
+    }
+    auto& ckfResult = result.value();
+    auto traj = FaserActsRecMultiTrajectory(ckfResult.fittedStates, ckfResult.lastMeasurementIndices, ckfResult.fittedParameters);
+    const auto& mj = traj.multiTrajectory();
+    const auto& trackTips = traj.tips();
+    size_t maxMeasurements = 0;
+    for (const auto& trackTip : trackTips) {
+      auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip);
+      size_t nMeasurements = trajState.nMeasurements;
+      if (nMeasurements > maxMeasurements) {
+        maxMeasurements = nMeasurements;
+      }
+    }
+    if (maxMeasurements >= m_minNumberMeasurements) {
+      minTrackRequirements.push_back(Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>(ckfResult));
+    }
+  }
+
+  TrackFinderResult selectedResults {};
+  if (minTrackRequirements.size() > 2) {
+    std::pair<std::size_t, std::size_t> trackIndices = solveAmbiguity(results);
+    selectedResults.push_back(
+        Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>(results.at(trackIndices.first).value()));
+    selectedResults.push_back(
+        Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>(results.at(trackIndices.second).value()));
+  } else {
+    for (auto& result : minTrackRequirements) {
+      selectedResults.push_back(
+          Acts::Result<Acts::CombinatorialKalmanFilterResult<IndexSourceLink>>(result.value()));
+    }
+  }
+   */
+  computeSharedHits(sourceLinks.get(), selectedResults);
+
+  // Loop over the track finding results for all initial parameters
+  for (std::size_t iseed = 0; iseed < selectedResults.size(); ++iseed) {
+    // The result for this seed
+    auto& result = selectedResults[iseed];
+    if (result.ok()) {
+      // Get the track finding output object
+      const auto& trackFindingOutput = result.value();
+
+      std::unique_ptr<Trk::Track> track = makeTrack(geoctx, result);
+      if (track) {
+        outputTracks->push_back(std::move(track));
+      }
+
+      if (!trackFindingOutput.fittedParameters.empty()) {
+        const std::unordered_map<size_t, Acts::BoundTrackParameters>& params = trackFindingOutput.fittedParameters;
+        for (const auto& surface_params : params) {
+          ATH_MSG_VERBOSE("Fitted parameters for track " << iseed << ", surface " << surface_params.first);
+          ATH_MSG_VERBOSE("  position: " << surface_params.second.position(geoctx).transpose());
+          ATH_MSG_VERBOSE("  momentum: " << surface_params.second.momentum().transpose());
+//          const auto& [currentTip, tipState] = trackFindingOutput.activeTips.back();
+//          ATH_MSG_VERBOSE("  #measurements: " << tipState.nMeasurements);
+        }
+      } else {
+        ATH_MSG_DEBUG("No fitted parameters for track " << iseed);
+      }
+      // Create a Trajectories result struct
+      trajectories.emplace_back(trackFindingOutput.fittedStates,
+                                trackFindingOutput.lastMeasurementIndices,
+                                trackFindingOutput.fittedParameters);
+    } else {
+      ATH_MSG_WARNING("Track finding failed for seed " << iseed << " with error" << result.error());
+      // Track finding failed. Add an empty result so the output container has
+      // the same number of entries as the input.
+      trajectories.push_back(FaserActsRecMultiTrajectory());
+    }
+  }
+
+  if (m_statesWriter) {
+    ATH_CHECK(m_trajectoryStatesWriterTool->write(geoctx, trajectories));
+  }
+  if (m_summaryWriter) {
+    ATH_CHECK(m_trajectorySummaryWriterTool->write(geoctx, trajectories));
+  }
+  if  (m_performanceWriter) {
+    ATH_CHECK(m_performanceWriterTool->write(geoctx, trajectories));
+  }
+  ATH_CHECK(trackContainer.record(std::move(outputTracks)));
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode CombinatorialKalmanFilterAlg::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+
+Acts::MagneticFieldContext CombinatorialKalmanFilterAlg::getMagneticFieldContext(const EventContext& ctx) const {
+  SG::ReadCondHandle<FaserFieldCacheCondObj> readHandle{m_fieldCondObjInputKey, ctx};
+  if (!readHandle.isValid()) {
+    std::stringstream msg;
+    msg << "Failed to retrieve magnetic field condition data " << m_fieldCondObjInputKey.key() << ".";
+    throw std::runtime_error(msg.str());
+  }
+  const FaserFieldCacheCondObj* fieldCondObj{*readHandle};
+  return Acts::MagneticFieldContext(fieldCondObj);
+}
+
+std::unique_ptr<Trk::Track>
+CombinatorialKalmanFilterAlg::makeTrack(Acts::GeometryContext& geoCtx, TrackFitterResult& fitResult) const {
+  using ConstTrackStateProxy =
+  Acts::detail_lt::TrackStateProxy<IndexSourceLink, 6, true>;
+  std::unique_ptr<Trk::Track> newtrack = nullptr;
+  //Get the fit output object
+  const auto& fitOutput = fitResult.value();
+  if (fitOutput.fittedParameters.size() > 0) {
+    DataVector<const Trk::TrackStateOnSurface>* finalTrajectory = new DataVector<const Trk::TrackStateOnSurface>{};
+    std::vector<std::unique_ptr<const Acts::BoundTrackParameters>> actsSmoothedParam;
+    // Loop over all the output state to create track state
+    fitOutput.fittedStates.visitBackwards(fitOutput.lastMeasurementIndices.front(), [&](const ConstTrackStateProxy& state) {
+      auto flag = state.typeFlags();
+      if (state.referenceSurface().associatedDetectorElement() != nullptr) {
+        // We need to determine the type of state
+        std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePattern;
+        const Trk::TrackParameters *parm;
+
+        // State is a hole (no associated measurement), use predicted para meters
+        if (flag[Acts::TrackStateFlag::HoleFlag] == true) {
+          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
+                                                     state.predicted(),
+                                                     state.predictedCovariance());
+          parm = ConvertActsTrackParameterToATLAS(actsParam, geoCtx);
+          // auto boundaryCheck = m_boundaryCheckTool->boundaryCheck(*p arm);
+          typePattern.set(Trk::TrackStateOnSurface::Hole);
+        }
+          // The state was tagged as an outlier, use filtered parameters
+        else if (flag[Acts::TrackStateFlag::OutlierFlag] == true) {
+          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
+                                                     state.filtered(), state.filteredCovariance());
+          parm = ConvertActsTrackParameterToATLAS(actsParam, geoCtx);
+          typePattern.set(Trk::TrackStateOnSurface::Outlier);
+        }
+          // The state is a measurement state, use smoothed parameters
+        else {
+          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
+                                                     state.smoothed(), state.smoothedCovariance());
+          actsSmoothedParam.push_back(std::make_unique<const Acts::BoundTrackParameters>(Acts::BoundTrackParameters(actsParam)));
+          //  const auto& psurface=actsParam.referenceSurface();
+          Acts::Vector2 local(actsParam.parameters()[Acts::eBoundLoc0], actsParam.parameters()[Acts::eBoundLoc1]);
+          //  const Acts::Vector3 dir = Acts::makeDirectionUnitFromPhiTheta(actsParam.parameters()[Acts::eBoundPhi], actsParam.parameters()[Acts::eBoundTheta]);
+          //  auto pos=actsParam.position(tgContext);
+          parm = ConvertActsTrackParameterToATLAS(actsParam, geoCtx);
+          typePattern.set(Trk::TrackStateOnSurface::Measurement);
+        }
+        Tracker::FaserSCT_ClusterOnTrack* measState = nullptr;
+        if (state.hasUncalibrated()) {
+          const Tracker::FaserSCT_Cluster* fitCluster = state.uncalibrated().hit();
+          if (fitCluster->detectorElement() != nullptr) {
+            measState = new Tracker::FaserSCT_ClusterOnTrack{
+                fitCluster,
+                Trk::LocalParameters{
+                    Trk::DefinedParameter{fitCluster->localPosition()[0], Trk::loc1},
+                    Trk::DefinedParameter{fitCluster->localPosition()[1], Trk::loc2}
+                },
+                fitCluster->localCovariance(),
+                m_idHelper->wafer_hash(fitCluster->detectorElement()->identify())
+            };
+          }
+        }
+        double nDoF = state.calibratedSize();
+        const Trk::FitQualityOnSurface *quality = new Trk::FitQualityOnSurface(state.chi2(), nDoF);
+        const Trk::TrackStateOnSurface *perState = new Trk::TrackStateOnSurface(measState, parm, quality, nullptr, typePattern);
+        // If a state was succesfully created add it to the trajectory
+        if (perState) {
+          finalTrajectory->insert(finalTrajectory->begin(), perState);
+        }
+      }
+      return;
+    });
+
+    // Create the track using the states
+    const Trk::TrackInfo newInfo(Trk::TrackInfo::TrackFitter::KalmanFitter, Trk::ParticleHypothesis::muon);
+    // Trk::FitQuality* q = nullptr;
+    // newInfo.setTrackFitter(Trk::TrackInfo::TrackFitter::KalmanFitter     ); //Mark the fitter as KalmanFitter
+    newtrack = std::make_unique<Trk::Track>(newInfo, std::move(*finalTrajectory), nullptr);
+  }
+  return newtrack;
+}
+
+const Trk::TrackParameters*
+CombinatorialKalmanFilterAlg::ConvertActsTrackParameterToATLAS(const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext& gctx) const      {
+  using namespace Acts::UnitLiterals;
+  std::optional<AmgSymMatrix(5)> cov = std::nullopt;
+  if (actsParameter.covariance()){
+    AmgSymMatrix(5) newcov(actsParameter.covariance()->topLeftCorner(5, 5));
+    // Convert the covariance matrix to 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(5)>(newcov);
+  }
+  const Amg::Vector3D& pos=actsParameter.position(gctx);
+  double tphi=actsParameter.get<Acts::eBoundPhi>();
+  double ttheta=actsParameter.get<Acts::eBoundTheta>();
+  double tqOverP=actsParameter.get<Acts::eBoundQOverP>()*1_MeV;
+  double p = std::abs(1. / tqOverP);
+  Amg::Vector3D tmom(p * std::cos(tphi) * std::sin(ttheta), p * std::sin(tphi) * std::sin(ttheta), p * std::cos(ttheta));
+  const Trk::CurvilinearParameters * curv = new Trk::CurvilinearParameters(pos,tmom,tqOverP>0, cov);
+  return curv;
+}
+
+void CombinatorialKalmanFilterAlg::computeSharedHits(std::vector<IndexSourceLink>* sourceLinks, TrackFinderResult& results) const {
+  // Compute shared hits from all the reconstructed tracks
+  // Compute nSharedhits and Update ckf results
+  // hit index -> list of multi traj indexes [traj, meas]
+  static_assert(Acts::SourceLinkConcept<IndexSourceLink>,
+                "Source link does not fulfill SourceLinkConcept");
+
+  std::vector<std::size_t> firstTrackOnTheHit(
+      sourceLinks->size(), std::numeric_limits<std::size_t>::max());
+  std::vector<std::size_t> firstStateOnTheHit(
+      sourceLinks->size(), std::numeric_limits<std::size_t>::max());
+
+  for (unsigned int iresult = 0; iresult < results.size(); iresult++) {
+    if (not results.at(iresult).ok()) {
+      continue;
+    }
+
+    auto& ckfResult = results.at(iresult).value();
+    auto& measIndexes = ckfResult.lastMeasurementIndices;
+
+    for (auto measIndex : measIndexes) {
+      ckfResult.fittedStates.visitBackwards(measIndex, [&](const auto& state) {
+        if (not state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag))
+          return;
+
+        std::size_t hitIndex = state.uncalibrated().index();
+
+        // Check if hit not already used
+        if (firstTrackOnTheHit.at(hitIndex) ==
+            std::numeric_limits<std::size_t>::max()) {
+          firstTrackOnTheHit.at(hitIndex) = iresult;
+          firstStateOnTheHit.at(hitIndex) = state.index();
+          return;
+        }
+
+        // if already used, control if first track state has been marked
+        // as shared
+        int indexFirstTrack = firstTrackOnTheHit.at(hitIndex);
+        int indexFirstState = firstStateOnTheHit.at(hitIndex);
+        if (not results.at(indexFirstTrack).value().fittedStates.getTrackState(indexFirstState).typeFlags().test(Acts::TrackStateFlag::SharedHitFlag))
+          results.at(indexFirstTrack).value().fittedStates.getTrackState(indexFirstState).typeFlags().set(Acts::TrackStateFlag::SharedHitFlag);
+
+        // Decorate this track
+        results.at(iresult).value().fittedStates.getTrackState(state.index()).typeFlags().set(Acts::TrackStateFlag::SharedHitFlag);
+      });
+    }
+  }
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/CombinatorialKalmbanFilterAlg.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/CombinatorialKalmbanFilterAlg.cxx
deleted file mode 100644
index f5c547e61418ca7d5d0e4eb8b1cbdb9651e9316b..0000000000000000000000000000000000000000
--- a/Tracking/Acts/FaserActsKalmanFilter/src/CombinatorialKalmbanFilterAlg.cxx
+++ /dev/null
@@ -1,449 +0,0 @@
-#include "FaserActsKalmanFilter/CombinatorialKalmanFilterAlg.h"
-
-#include "StoreGate/ReadHandle.h"
-#include "StoreGate/ReadCondHandleKey.h"
-#include "TrackerSpacePoint/FaserSCT_SpacePointCollection.h"
-#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
-#include "TrackerIdentifier/FaserSCT_ID.h"
-#include "TrkPrepRawData/PrepRawData.h"
-#include "TrackerPrepRawData/FaserSCT_Cluster.h"
-#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
-#include "TrkRIO_OnTrack/RIO_OnTrack.h"
-#include "TrkSurfaces/Surface.h"
-#include "Identifier/Identifier.h"
-#include "Acts/Geometry/GeometryIdentifier.hpp"
-#include "Acts/EventData/TrackParameters.hpp"
-#include "FaserActsKalmanFilter/IndexSourceLink.h"
-#include "FaserActsKalmanFilter/Measurement.h"
-#include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
-#include "Acts/Surfaces/PerigeeSurface.hpp"
-#include "Acts/MagneticField/MagneticFieldContext.hpp"
-
-using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
-using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, 2>;
-using TrajectoriesContainer = std::vector<FaserActsRecMultiTrajectory>;
-std::array<Acts::BoundIndices, 2> indices = {Acts::eBoundLoc0, Acts::eBoundLoc1};
-
-
-CombinatorialKalmanFilterAlg::CombinatorialKalmanFilterAlg(
-    const std::string& name, ISvcLocator* pSvcLocator)
-    : AthReentrantAlgorithm(name, pSvcLocator) {}
-
-
-StatusCode CombinatorialKalmanFilterAlg::initialize() {
-  ATH_MSG_INFO("CombinatorialKalmanFilterAlg::initialize");
-  m_nevents=0;
-  m_ntracks=0;
-  m_nseeds=0;
-  m_nsp10=0;
-  m_nsp11=0;
-  m_ncom=0;
-  m_nsp1=0;
-  m_nsp2=0;
-  m_nsp3=0;
-
-  ATH_CHECK(m_trackingGeometryTool.retrieve());
-  ATH_CHECK(m_initialParameterTool.retrieve());
-  ATH_CHECK(m_trajectoryWriterTool.retrieve());
-  ATH_CHECK(detStore()->retrieve(m_idHelper,"FaserSCT_ID"));
-
-  ATH_CHECK( m_fieldCondObjInputKey.initialize() );
-
-  ATH_CHECK(m_trackCollection.initialize()); 
-
-  if (m_SpacePointContainerKey.key().empty()) {
-    ATH_MSG_FATAL("empty space point container key");
-    return StatusCode::FAILURE;
-  }
-  ATH_CHECK(m_SpacePointContainerKey.initialize());
-  ATH_CHECK(m_Sct_clcontainerKey.initialize() );
-
-  return StatusCode::SUCCESS;
-}
-
-
-StatusCode CombinatorialKalmanFilterAlg::execute(const EventContext& ctx) const {
-  ATH_MSG_DEBUG("CombinatorialKalmanFilterAlg::execute");
-  m_nevents++;
-
-  SG::ReadHandle<FaserSCT_SpacePointContainer> spcontainer(m_SpacePointContainerKey, ctx);
-  if (!spcontainer.isValid()) {
-    ATH_MSG_FATAL( "Could not find the data object "<< spcontainer.name());
-    return StatusCode::FAILURE;
-  }
-  SG::ReadHandle<Tracker::FaserSCT_ClusterContainer> sct_clcontainer( m_Sct_clcontainerKey, ctx );
-if (!sct_clcontainer.isValid()){
-msg(MSG:: FATAL) << "Could not find the data object "<< sct_clcontainer.name() << " !" << endmsg;
-return StatusCode::RECOVERABLE;
-}
-
-
-   //make TrackCollection                                                  
-     SG::WriteHandle<TrackCollection> trackContainer{m_trackCollection,ctx};
-    std::unique_ptr<TrackCollection> outputTracks = std::make_unique<TrackCollection>();
-
-  const std::shared_ptr<IdentifierMap> identifierMap
-      = m_trackingGeometryTool->getIdentifierMap();
-
-      /*
-  // Create measurement and source link containers
-  IndexSourceLinkContainer sourceLinks;
-  MeasurementContainer measurements;
-  std::vector<Identifier> sp_ids;
-  std::vector<Tracker::FaserSCT_Cluster> sps;
-
-  std::vector<Acts::Vector3> pos1;
-  std::vector<Acts::Vector3> pos2;
-  std::vector<Acts::Vector3> pos3;
-  pos1.clear();pos2.clear();pos3.clear();
-  Tracker::FaserSCT_ClusterContainer::const_iterator coll_it = sct_clcontainer->begin();
-  Tracker::FaserSCT_ClusterContainer::const_iterator coll_itend = sct_clcontainer->end();
-  for (; coll_it != coll_itend; ++coll_it) {
-    const Tracker::FaserSCT_ClusterCollection* spcollection = *coll_it;
-    Tracker::FaserSCT_ClusterCollection::const_iterator sp_it = spcollection->begin();
-    Tracker::FaserSCT_ClusterCollection::const_iterator sp_end = spcollection->end();
-    for (; sp_it != sp_end; ++sp_it) {
-      const Tracker::FaserSCT_Cluster* sp = *sp_it;
-      Identifier id = sp->detectorElement()->identify();
-      //Identifier id = sp->associatedSurface().associatedDetectorElementIdentifier();
-      Acts::GeometryIdentifier geoId = identifierMap->at(id);
-      IndexSourceLink sourceLink(geoId, measurements.size());
-      sourceLinks.emplace_hint(sourceLinks.end(), std::move(sourceLink));
-      ThisMeasurement meas(sourceLink, indices, sp->localPosition(), sp->localCovariance());
-      //ThisMeasurement meas(sourceLink, indices, sp->localParameters(), sp->localCovariance());
-      measurements.emplace_back(std::move(meas));
-
-      if(m_idHelper->station(sp->identify())==1)pos1.push_back(Acts::Vector3(sp->globalPosition().x(),sp->globalPosition().y(),sp->globalPosition().z()));
-      if(m_idHelper->station(sp->identify())==2)pos2.push_back(Acts::Vector3(sp->globalPosition().x(),sp->globalPosition().y(),sp->globalPosition().z()));
-      if(m_idHelper->station(sp->identify())==3)pos3.push_back(Acts::Vector3(sp->globalPosition().x(),sp->globalPosition().y(),sp->globalPosition().z()));
-      sp_ids.push_back(sp->identify());
-      sps.push_back(*sp);
-      }
-      }
-*/
-  // Create measurement and source link containers
-  IndexSourceLinkContainer sourceLinks;
-  MeasurementContainer measurements;
-  std::vector<Identifier> sp_ids;
-  std::vector<Tracker::FaserSCT_SpacePoint> sps;
-
-//  std::vector<Acts::Vector3> pos1;
-//  std::vector<Acts::Vector3> pos2;
-//  std::vector<Acts::Vector3> pos3;
-//  pos1.clear();pos2.clear();pos3.clear();
-  std::vector<int> layer1;
-  layer1.clear();
-  FaserSCT_SpacePointContainer::const_iterator coll_it = spcontainer->begin();
-  FaserSCT_SpacePointContainer::const_iterator coll_itend = spcontainer->end();
-  for (; coll_it != coll_itend; ++coll_it) {
-    const FaserSCT_SpacePointCollection* spcollection = *coll_it;
-    FaserSCT_SpacePointCollection::const_iterator sp_it = spcollection->begin();
-    FaserSCT_SpacePointCollection::const_iterator sp_end = spcollection->end();
-    for (; sp_it != sp_end; ++sp_it) {
-      const Tracker::FaserSCT_SpacePoint* sp = *sp_it;
-      Identifier id = sp->associatedSurface().associatedDetectorElementIdentifier();
-      Acts::GeometryIdentifier geoId = identifierMap->at(id);
-      IndexSourceLink sourceLink(geoId, measurements.size());
-      sourceLinks.emplace_hint(sourceLinks.end(), std::move(sourceLink));
-      ThisMeasurement meas(sourceLink, indices, sp->localParameters(), sp->localCovariance());
-      measurements.emplace_back(std::move(meas));
-
-//      if(m_idHelper->station(sp->clusterList().first->identify())==1) {
-//	      pos1.push_back(Acts::Vector3(sp->globalPosition().x(),sp->globalPosition().y(),sp->globalPosition().z()));
-//	      layer1.push_back(m_idHelper->layer(sp->clusterList().first->identify()));
-//      }
-//      if(m_idHelper->station(sp->clusterList().first->identify())==2)pos2.push_back(Acts::Vector3(sp->globalPosition().x(),sp->globalPosition().y(),sp->globalPosition().z()));
-//      if(m_idHelper->station(sp->clusterList().first->identify())==3)pos3.push_back(Acts::Vector3(sp->globalPosition().x(),sp->globalPosition().y(),sp->globalPosition().z()));
-      sp_ids.push_back(sp->clusterList().first->identify());
-      sps.push_back(*sp);
-      }
-      }
-
-      // Get initial parameters
-      // FIXME: Get initial parameters from clusterFitter or SeedFinder not MC!
-        std::vector<Acts::CurvilinearTrackParameters> initialParameters;
-        auto initialParameter = m_initialParameterTool->getInitialParameters(sp_ids);
-        initialParameters.push_back(initialParameter);
-//      if(pos1.size()<1||pos2.size()<1||pos3.size()<1) return StatusCode::SUCCESS;
-//      std::vector<Acts::CurvilinearTrackParameters> initialParameters;
-//      initialParameters.clear();
-//      Acts::Vector3 pos1a(0,0,0);
-//      Acts::Vector3 pos2a(0,0,0);
-//      Acts::Vector3 pos3a(0,0,0);
-//      for(long unsigned int i1=0;i1<pos1.size();i1++)pos1a+=pos1[i1];
-//      for(long unsigned int i1=0;i1<pos2.size();i1++)pos2a+=pos2[i1];
-//      for(long unsigned int i1=0;i1<pos3.size();i1++)pos3a+=pos3[i1];
-//      pos1a/=pos1.size();
-//      pos2a/=pos2.size();
-//      pos3a/=pos3.size();
-//      m_nsp1+=pos1.size();
-//      m_nsp2+=pos2.size();
-//      m_nsp3+=pos3.size();
-//      for(int i1=0;i1<pos1.size();i1++){
-//      for(int i2=0;i2<pos2.size();i2++){
-//      for(int i3=0;i3<pos3.size();i3++){
-//      auto initialParameter=m_initialParameterTool->getInitialParameters(pos1[i1],pos2[i2],pos3[i3]);
-//      initialParameters.push_back(initialParameter);
-//      }
-//      }
-//      }
-      /*
-      if(pos1.size()>0&&pos2.size()>0&&pos3.size()>0) {
-      auto initialParameter=m_initialParameterTool->getInitialParameters(pos1a,pos2a,pos3a);
-       if(initialParameter.momentum().z()>0){
-	  m_nseeds++;
-      initialParameters.push_back(initialParameter);
-      */
- // for one stations
-//      if(pos1.size()>2) {
-//      std::vector<Acts::Vector3> pos10;
-//      std::vector<Acts::Vector3> pos11;
-//      std::vector<Acts::Vector3> pos12;
-//      pos10.clear();pos11.clear();pos12.clear();
-//	for(std::size_t ipos=0;ipos<pos1.size();ipos++){
-//	  if(layer1[ipos]==0)pos10.push_back(pos1[ipos]);
-//	  if(layer1[ipos]==1)pos11.push_back(pos1[ipos]);
-//	  if(layer1[ipos]==2)pos12.push_back(pos1[ipos]);
-//	}
-//	if(pos10.size()>0&&pos11.size()>0&&pos12.size()>0){
-//      auto initialParameter=m_initialParameterTool->getInitialParameters_1station(pos10,pos11,pos12);
-//      initialParameters.insert(initialParameters.end(),initialParameter.begin(),initialParameter.end());
-      /*
-       //for two stations
-      if(pos1.size()>1&&pos2.size()>0) {
-      Acts::Vector3 pos10a(0,0,0);
-      Acts::Vector3 pos11a(0,0,0);
-      int n10a=0,n11a=0;
-	for(int ipos=0;ipos<pos1.size();ipos++){
-	  if(layer1[ipos]==0){pos10a+=pos1[ipos];n10a++;}
-	  if(layer1[ipos]>0){pos11a+=pos1[ipos];n11a++;}
-	}
-	m_nsp10+=n10a;
-	m_nsp11+=n11a;
-	if(n10a>0&&n11a>0){
-	  m_ncom++;
-	  pos10a/=n10a;
-	  pos11a/=n11a;
-	  Acts::Vector3 dir1=(pos11a-pos10a).normalized();
-      auto initialParameter=m_initialParameterTool->getInitialParameters_2stations(pos1a,pos2a,dir1);
-       if(initialParameter.momentum().z()>0){
-	  m_nseeds++;
-      initialParameters.push_back(initialParameter);
-       }
-       */
-
-  // Prepare the output data with MultiTrajectory
-  TrajectoriesContainer trajectories;
-  trajectories.reserve(initialParameters.size());
-
-  // Construct a perigee surface as the target surface
-  auto pSurface = Acts::Surface::makeShared<Acts::PerigeeSurface>(
-      Acts::Vector3{0., 0., 0.});
-
-  Acts::PropagatorPlainOptions pOptions;
-  pOptions.maxSteps = 10000;
-  /*
-    Acts::DirectNavigator     navigator;
-     std::unique_ptr<ActsExtrapolationDetail::VariantPropagator> varProp;
-      Acts::Vector3 constantFieldVector = Acts::Vector3(0,0,0.55);
-	  auto bField = std::make_shared<Acts::ConstantBField>(constantFieldVector);
-        auto stepper = Acts::EigenStepper<>(std::move(bField));
-	auto propagator = Acts::Propagator<decltype(stepper), Acts::DirectNavigator>(std::move(stepper),
-	std::move(navigator));
-	varProp = std::make_unique<VariantPropagator>(propagator);
-	*/
-
-  Acts::GeometryContext geoContext = m_trackingGeometryTool->getNominalGeometryContext().context();
-  Acts::MagneticFieldContext magFieldContext = getMagneticFieldContext(ctx);
-  Acts::CalibrationContext calibContext;
-  double chi2Max = 15;
-  size_t nMax = 10;
-  Acts::MeasurementSelector::Config measurementSelectorCfg = {
-    {Acts::GeometryIdentifier(), {chi2Max, nMax}},
-  };
-  std::unique_ptr<const Acts::Logger> logger
-      = Acts::getDefaultLogger("CombinatorialKalmanFilter", Acts::Logging::INFO);
-
-  // Set the CombinatorialKalmanFilter options
-  CombinatorialKalmanFilterAlg::TrackFinderOptions options(
-      geoContext, magFieldContext, calibContext,
-      IndexSourceLinkAccessor(), MeasurementCalibrator(measurements),
-      Acts::MeasurementSelector(measurementSelectorCfg),
-      //Acts::LoggerWrapper{*logger}, varProp, &(*pSurface));
-      Acts::LoggerWrapper{*logger}, pOptions, &(*pSurface));
-
-  std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry
-      = m_trackingGeometryTool->trackingGeometry();
-
-  // Get track finder function
-  auto trackFinderFunction = makeTrackFinderFunction(trackingGeometry);
-
-  // Perform the track finding for all initial parameters
-  ATH_MSG_DEBUG("Invoke track finding with " << initialParameters.size()
-                                          << " seeds.");
-  auto results = trackFinderFunction(sourceLinks, initialParameters, options);
-  // Loop over the track finding results for all initial parameters
-  for (std::size_t iseed = 0; iseed < initialParameters.size(); ++iseed) {
-    // The result for this seed
-    auto& result = results[iseed];
-    if (result.ok()) {
-      // Get the track finding output object
-      const auto& trackFindingOutput = result.value();
-      std::unique_ptr<Trk::Track> track = makeTrack(geoContext, result,sps);
-      m_ntracks++;
-      if(track!=nullptr) {
-        outputTracks->push_back(std::move(track));
-      } else {
-        ATH_MSG_DEBUG("No Trk::Track is created" );
-      }
-      // Create a Trajectories result struct
-      trajectories.emplace_back(std::move(trackFindingOutput.fittedStates),
-                                std::move(trackFindingOutput.lastMeasurementIndices),
-                                std::move(trackFindingOutput.fittedParameters));
-    } else {
-      ATH_MSG_WARNING("Track finding failed for seed " << iseed << " with error"
-                                                       << result.error());
-      // Track finding failed. Add an empty result so the output container has
-      // the same number of entries as the input.
-      trajectories.push_back(FaserActsRecMultiTrajectory());
-    }
-  }
-
-  m_trajectoryWriterTool->writeout(trajectories, geoContext,initialParameters);
-//  }
-//      }
-
-  if(outputTracks->size()>0) {
-    ATH_MSG_DEBUG("Found " << outputTracks->size() << " tracks");
-  } else {
-    ATH_MSG_WARNING("No track is found");
-  }
-
-  ATH_CHECK(trackContainer.record(std::move(outputTracks)));
-  return StatusCode::SUCCESS;
-}
-
-
-StatusCode CombinatorialKalmanFilterAlg::finalize() {
-  ATH_MSG_INFO("CombinatorialKalmanFilterAlg::finalize");
-  ATH_MSG_INFO("Summary info");
-  ATH_MSG_INFO("In total, "<<m_nevents<<" events, and "<<m_nseeds<<" seeds, and "<<m_ntracks<<" tracks");
-  ATH_MSG_INFO("In total, "<<m_nsp1<<" , "<<m_nsp2<<" , "<<m_nsp3<<" ,"<<m_nsp10<<" , "<<m_nsp11<<" , "<<m_ncom);
-
-  return StatusCode::SUCCESS;
-}
-
-
-Acts::MagneticFieldContext CombinatorialKalmanFilterAlg::getMagneticFieldContext(const EventContext& ctx) const {
-  SG::ReadCondHandle<FaserFieldCacheCondObj> readHandle{m_fieldCondObjInputKey, ctx};
-  if (!readHandle.isValid()) {
-    std::stringstream msg;
-    msg << "Failed to retrieve magnetic field condition data " << m_fieldCondObjInputKey.key() << ".";
-    throw std::runtime_error(msg.str());
-  }
-  const FaserFieldCacheCondObj* fieldCondObj{*readHandle};
-
-  return Acts::MagneticFieldContext(fieldCondObj);
-}
-
-std::unique_ptr<Trk::Track>
-CombinatorialKalmanFilterAlg::makeTrack(Acts::GeometryContext& tgContext, FitterResult& fitResult, std::vector<Tracker::FaserSCT_SpacePoint> sps) const {
-  std::unique_ptr<Trk::Track> newtrack = nullptr;
-  //Get the fit output object
-  const auto& fitOutput = fitResult.value();
-  if (fitOutput.fittedParameters.size()>0) {
-    DataVector<const Trk::TrackStateOnSurface>* finalTrajectory = new DataVector<const Trk::TrackStateOnSurface>{};
-    std::vector<std::unique_ptr<const Acts::BoundTrackParameters>> actsSmoothedParam;
-    ATH_MSG_DEBUG("makeTrack : trackTip "<<fitOutput.lastMeasurementIndices.size());
-    // Loop over all the output state to create track state
-    fitOutput.fittedStates.visitBackwards(fitOutput.lastMeasurementIndices.front(), [&](const auto &state) {
-      auto flag = state.typeFlags();
-      if (state.referenceSurface().associatedDetectorElement() != nullptr) {
-        //	const auto* actsElement = dynamic_cast<const FaserActsDetectorElement*>(state.referenceSurface().associatedDetectorElement());
-        //	if (actsElement != nullptr ){
-        // We need to determine the type of state
-        std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePattern;
-        const Trk::TrackParameters *parm;
-
-        // State is a hole (no associated measurement), use predicted para meters
-        if (flag[Acts::TrackStateFlag::HoleFlag] == true) {
-          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
-          state.predicted(),
-          state.predictedCovariance());
-          parm = ConvertActsTrackParameterToATLAS(actsParam, tgContext);
-          // auto boundaryCheck = m_boundaryCheckTool->boundaryCheck(*p arm);
-          typePattern.set(Trk::TrackStateOnSurface::Hole);
-        }
-        // The state was tagged as an outlier, use filtered parameters
-        else if (flag[Acts::TrackStateFlag::OutlierFlag] == true) {
-          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
-                                                     state.filtered(), state.filteredCovariance());
-          parm = ConvertActsTrackParameterToATLAS(actsParam, tgContext);
-          typePattern.set(Trk::TrackStateOnSurface::Outlier);
-        }
-        // The state is a measurement state, use smoothed parameters
-        else {
-          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
-                                                     state.smoothed(), state.smoothedCovariance());
-          actsSmoothedParam.push_back(std::make_unique<const Acts::BoundTrackParameters>(Acts::BoundTrackParameters(actsParam)));
-          //  const auto& psurface=actsParam.referenceSurface();
-          Acts::Vector2 local(actsParam.parameters()[Acts::eBoundLoc0], actsParam.parameters()[Acts::eBoundLoc1]);
-          //  const Acts::Vector3 dir = Acts::makeDirectionUnitFromPhiTheta(actsParam.parameters()[Acts::eBoundPhi], actsParam.parameters()[Acts::eBoundTheta]);
-          //  auto pos=actsParam.position(tgContext);
-          parm = ConvertActsTrackParameterToATLAS(actsParam, tgContext);
-          typePattern.set(Trk::TrackStateOnSurface::Measurement);
-        }
-        Tracker::FaserSCT_ClusterOnTrack* measState = nullptr;
-        if (state.hasUncalibrated()) {
-          auto sp= sps.at(state.uncalibrated().index());
-          //const Tracker::FaserSCT_Cluster* fitCluster=&sp;
-          const Tracker::FaserSCT_Cluster* fitCluster=sp.clusterList().first;
-          if(fitCluster !=nullptr) {
-            measState = new Tracker::FaserSCT_ClusterOnTrack{ fitCluster, Trk::LocalParameters { Trk::DefinedParameter { fitCluster->localPosition()[0], Trk::loc1 }, Trk::DefinedParameter { fitCluster->localPosition()[1], Trk::loc2 } }, fitCluster->localCovariance(), m_idHelper->wafer_hash(fitCluster->detectorElement()->identify())};
-          }
-        }
-        double nDoF = state.calibratedSize();
-        const Trk::FitQualityOnSurface *quality = new Trk::FitQualityOnSurface(state.chi2(), nDoF);
-        const Trk::TrackStateOnSurface *perState = new Trk::TrackStateOnSurface(measState, parm, quality, nullptr, typePattern);
-        // If a state was succesfully created add it to the trajectory
-        if (perState) {
-          finalTrajectory->insert(finalTrajectory->begin(), perState);
-        }
-      }
-      return;
-    });
-
-    // Create the track using the states
-    const Trk::TrackInfo newInfo(Trk::TrackInfo::TrackFitter::KalmanFitter, Trk::ParticleHypothesis::muon);
-    // Trk::FitQuality* q = nullptr;
-    // newInfo.setTrackFitter(Trk::TrackInfo::TrackFitter::KalmanFitter     ); //Mark the fitter as KalmanFitter
-    newtrack = std::make_unique<Trk::Track>(newInfo, std::move(*finalTrajectory), nullptr); 
-  }
-  return newtrack;
-}
-
-const Trk::TrackParameters*
-CombinatorialKalmanFilterAlg ::ConvertActsTrackParameterToATLAS(const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext& gctx) const      {
-  using namespace Acts::UnitLiterals;
-  std::optional<AmgSymMatrix(5)> cov = std::nullopt;
-  if (actsParameter.covariance()){
-    AmgSymMatrix(5) newcov(actsParameter.covariance()->topLeftCorner(5, 5));
-    // Convert the covariance matrix to 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(5)>(newcov);
-  }
-  const Amg::Vector3D& pos=actsParameter.position(gctx);
-  double tphi=actsParameter.get<Acts::eBoundPhi>();
-  double ttheta=actsParameter.get<Acts::eBoundTheta>();
-  double tqOverP=actsParameter.get<Acts::eBoundQOverP>()*1_MeV;
-  double p = std::abs(1. / tqOverP);
-  Amg::Vector3D tmom(p * std::cos(tphi) * std::sin(ttheta), p * std::sin(tphi) * std::sin(ttheta), p * std::cos(ttheta));
-  const Trk::CurvilinearParameters * curv = new Trk::CurvilinearParameters(pos,tmom,tqOverP>0, cov);
-  return curv;  
-} 
-
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/EffPlotTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/EffPlotTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..514fe5407c1585e1a0884a60f2dc5d039ebe92ae
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/EffPlotTool.cxx
@@ -0,0 +1,40 @@
+#include "FaserActsKalmanFilter/EffPlotTool.h"
+#include "Acts/Utilities/Helpers.hpp"
+
+void EffPlotTool::book(EffPlotTool::EffPlotCache &effPlotCache) const {
+  PlotHelpers::Binning bPhi = m_varBinning.at("Phi");
+  PlotHelpers::Binning bEta = m_varBinning.at("Eta");
+  PlotHelpers::Binning bPt = m_varBinning.at("Pt");
+  // efficiency vs pT
+  effPlotCache.trackEff_vs_pT = PlotHelpers::bookEff(
+      "trackeff_vs_pT", "Tracking efficiency;Truth pT [GeV/c];Efficiency", bPt);
+  // efficiency vs eta
+  effPlotCache.trackEff_vs_eta = PlotHelpers::bookEff(
+      "trackeff_vs_eta", "Tracking efficiency;Truth #eta;Efficiency", bEta);
+  // efficiency vs phi
+  effPlotCache.trackEff_vs_phi = PlotHelpers::bookEff(
+      "trackeff_vs_phi", "Tracking efficiency;Truth #phi;Efficiency", bPhi);
+}
+
+void EffPlotTool::fill(EffPlotTool::EffPlotCache &effPlotCache,
+                       const HepMC::GenParticle* truthParticle, bool status) const {
+  const auto t_phi = truthParticle->momentum().phi();
+  const auto t_eta = truthParticle->momentum().eta();
+  const auto t_pT = truthParticle->momentum().perp() * m_MeV2GeV;
+
+  PlotHelpers::fillEff(effPlotCache.trackEff_vs_pT, t_pT, status);
+  PlotHelpers::fillEff(effPlotCache.trackEff_vs_eta, t_eta, status);
+  PlotHelpers::fillEff(effPlotCache.trackEff_vs_phi, t_phi, status);
+}
+
+void EffPlotTool::write(const EffPlotTool::EffPlotCache &effPlotCache) const {
+  effPlotCache.trackEff_vs_pT->Write();
+  effPlotCache.trackEff_vs_eta->Write();
+  effPlotCache.trackEff_vs_phi->Write();
+}
+
+void EffPlotTool::clear(EffPlotTool::EffPlotCache &effPlotCache) const {
+  delete effPlotCache.trackEff_vs_pT;
+  delete effPlotCache.trackEff_vs_eta;
+  delete effPlotCache.trackEff_vs_phi;
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/FaserActsKalmanFilterAlg.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/FaserActsKalmanFilterAlg.cxx
index c9427a0f8f9940f6d181e4f4775bcb9a8e2ae527..673524123467db8461bb25da2157bfa00eb6755a 100755
--- a/Tracking/Acts/FaserActsKalmanFilter/src/FaserActsKalmanFilterAlg.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/FaserActsKalmanFilterAlg.cxx
@@ -7,7 +7,7 @@
 // ATHENA
 #include "GaudiKernel/EventContext.h"
 #include "GaudiKernel/ISvcLocator.h"
-#include "GaudiKernel/PhysicalConstants.h"
+//#include "GaudiKernel/PhysicalConstants.h"
 #include "TrackerReadoutGeometry/SCT_DetectorManager.h"
 #include "TrackerReadoutGeometry/SiDetectorDesign.h"
 #include "TrackerReadoutGeometry/SiLocalPosition.h"
@@ -18,14 +18,8 @@
 #include "FaserDetDescr/FaserDetectorID.h"
 
 // ACTS
-#include "Acts/TrackFitting/GainMatrixSmoother.hpp"
-#include "Acts/TrackFitting/GainMatrixUpdater.hpp"
 #include "Acts/Geometry/GeometryIdentifier.hpp"
-#include "Acts/MagneticField/ConstantBField.hpp"
-#include "Acts/MagneticField/InterpolatedBFieldMap.hpp"
-#include "Acts/MagneticField/SharedBField.hpp"
 #include "Acts/MagneticField/MagneticFieldContext.hpp"
-#include "Acts/Propagator/EigenStepper.hpp"
 #include "Acts/Propagator/Navigator.hpp"
 #include "Acts/Propagator/Propagator.hpp"
 #include "Acts/Surfaces/Surface.hpp"
@@ -43,7 +37,6 @@
 #include "Acts/EventData/TrackParameters.hpp"
 #include "Acts/EventData/MultiTrajectoryHelpers.hpp"
 #include "Acts/EventData/Measurement.hpp"
-#include "Acts/Geometry/GeometryIdentifier.hpp"
 
 
 // PACKAGE
@@ -52,6 +45,9 @@
 #include "FaserActsGeometry/FaserActsGeometryContext.h"
 #include "FaserActsGeometry/IFaserActsPropStepRootWriterSvc.h"
 #include "FaserActsGeometry/FaserActsDetectorElement.h"
+#include "FaserActsKalmanFilter/IdentifierLink.h"
+
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
 
 //ROOT
 #include <TTree.h>
@@ -65,7 +61,6 @@
 #include <string>
 #include <fstream>
 #include <cmath>
-#include <random>
 
 using TrajectoryContainer = std::vector<FaserActsRecMultiTrajectory>;
 
@@ -75,476 +70,99 @@ using Acts::VectorHelpers::perp;
 using Acts::VectorHelpers::phi;
 using Acts::VectorHelpers::theta;
 using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, 2>;
-using Updater = Acts::GainMatrixUpdater;
-using Smoother = Acts::GainMatrixSmoother;
-using Stepper = Acts::EigenStepper<FASERMagneticFieldWrapper>;
-using Propagator = Acts::Propagator<Stepper, Acts::DirectNavigator>;
-using Fitter = Acts::KalmanFitter<Propagator, Updater, Smoother>;
-
-namespace ActsExtrapolationDetail {
-  using VariantPropagatorBase = boost::variant<
-    Acts::Propagator<Acts::EigenStepper<>, Acts::DirectNavigator>,
-    Acts::Propagator<Acts::EigenStepper<>, Acts::DirectNavigator>
-  >;
+using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
 
-  class VariantPropagator : public VariantPropagatorBase
-  {
-  public:
-    using VariantPropagatorBase::VariantPropagatorBase;
-  };
+FaserActsKalmanFilterAlg::FaserActsKalmanFilterAlg(const std::string& name, ISvcLocator* pSvcLocator) :
+   AthAlgorithm(name, pSvcLocator)  {}
 
-}
-
-using ActsExtrapolationDetail::VariantPropagator;
-
-FaserActsKalmanFilterAlg::FaserActsKalmanFilterAlg(const std::string& name, ISvcLocator* pSvcLocator)
-    : AthAlgorithm(name, pSvcLocator) , m_thistSvc("THistSvc", name)
-{
-}
-
-// FixMe the Identifier to GeometryIdentfier mapping should not be hardcoded.
-int FaserActsKalmanFilterAlg::getGeometryIdentifierVolume(int station)
-{
-  switch(station)
-  {
-    case 1:
-      return 2;
-    case 2:
-      return 3;
-    case 3:
-      return 4;
-    default:
-      ATH_MSG_ERROR("Received unexpected station. Check detector geometry");
-      return 0;
-  }
-}
-
-int FaserActsKalmanFilterAlg::getGeometryIdentifierLayer(int layer)
-{
-  switch(layer)
-  {
-    case 0:
-      return 2;
-    case 1:
-      return 4;
-    case 2:
-      return 6;
-    default:
-      ATH_MSG_ERROR("Received unexpected layer. Check detector geometry");
-      return 0;
-  }
-}
-
-int FaserActsKalmanFilterAlg::getGeometryIdentifierSensitive(int row, int column)
-{
-  if (row == 0 && column == -1)
-    return 2;
-  else if (row == 0 && column == 1)
-    return 4;
-  else if (row == 1 && column == -1)
-    return 5;
-  else if (row == 1 && column == 1)
-    return 7;
-  else if (row == 2 && column == -1)
-    return 10;
-  else if (row == 2 && column == 1)
-    return 12;
-  else if (row == 3 && column == -1)
-    return 13;
-  else if (row ==3 && column == 1)
-    return 15;
-  else
-  {
-    ATH_MSG_ERROR("Received unexpected row or column. Check detector geometry");
-    return 0;
-  }
-}
-
-Acts::GeometryIdentifier FaserActsKalmanFilterAlg::getGeometryIdentifier(const Identifier id)
-{
-  Acts::GeometryIdentifier geoId = Acts::GeometryIdentifier();
-  geoId.setVolume(getGeometryIdentifierVolume(m_idHelper->station(id)));
-  geoId.setLayer(getGeometryIdentifierLayer(m_idHelper->layer(id)));
-  geoId.setSensitive(getGeometryIdentifierSensitive(m_idHelper->phi_module(id), m_idHelper->eta_module(id)));
-  return geoId;
-}
 
 StatusCode FaserActsKalmanFilterAlg::initialize() {
-
-  ATH_MSG_DEBUG(name() << "::" << __FUNCTION__);
-
-  ATH_MSG_INFO("Initializing ACTS kalman filter");
-
-      ATH_CHECK( m_fieldCondObjInputKey.initialize() );
-
-      ATH_CHECK( m_extrapolationTool.retrieve() );
-
-  if ( m_seed_spcollectionKey.key().empty()){
-    ATH_MSG_FATAL( "SCTs selected and no name set for SCT clusters");
-    return StatusCode::FAILURE;
+  ATH_CHECK(m_fieldCondObjInputKey.initialize());
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+  ATH_CHECK(m_trackFinderTool.retrieve());
+  ATH_CHECK(m_trajectoryWriterTool.retrieve());
+  ATH_CHECK(m_trajectoryStatesWriterTool.retrieve());
+//  ATH_CHECK(m_protoTrackWriterTool.retrieve());
+  ATH_CHECK(m_trackCollection.initialize());
+  ATH_CHECK(detStore()->retrieve(m_idHelper,"FaserSCT_ID"));
+  m_fit = makeTrackFitterFunction(m_trackingGeometryTool->trackingGeometry());
+  if (m_actsLogging == "VERBOSE") {
+    m_logger = Acts::getDefaultLogger("KalmanFitter", Acts::Logging::VERBOSE);
+  } else if (m_actsLogging == "DEBUG") {
+    m_logger = Acts::getDefaultLogger("KalmanFitter", Acts::Logging::DEBUG);
+  } else {
+    m_logger = Acts::getDefaultLogger("KalmanFitter", Acts::Logging::INFO);
   }
-
-      ATH_CHECK( m_seed_spcollectionKey.initialize() );
-
-      ATH_CHECK( m_mcEventKey.initialize() );
-
-      ATH_CHECK( m_sctMap.initialize());
-
-      ATH_CHECK(detStore()->retrieve(m_idHelper,"FaserSCT_ID"));
-
-      ATH_CHECK(detStore()->retrieve(m_detManager, "SCT"));
-
-      ATH_CHECK(m_thistSvc.retrieve());
-
-  m_trackTree = new TTree("tracks", "");
-
-      ATH_CHECK(m_thistSvc->regTree("/KalmanTracks/tracks", m_trackTree));
-
-  if (m_trackTree) {
-    initializeTree();
-  }
-  else {
-    ATH_MSG_ERROR("No tree found!");
-  }
-
-  ATH_MSG_INFO("ACTS kalman filter successfully initialized");
   return StatusCode::SUCCESS;
 }
 
-//StatusCode FaserActsKalmanFilterAlg::execute(const EventContext& ctx) const
-StatusCode FaserActsKalmanFilterAlg::execute()
-{
 
-  ATH_MSG_VERBOSE(name() << "::" << __FUNCTION__);
+//StatusCode FaserActsKalmanFilterAlg::execute(const EventContext& ctx) const {
+StatusCode FaserActsKalmanFilterAlg::execute() {
+  const EventContext& ctx = Gaudi::Hive::currentContext();
 
-  m_eventNr++;
+  //make TrackCollection
+  ATH_CHECK(m_trackCollection.initialize());
+  SG::WriteHandle<TrackCollection> trackContainer{m_trackCollection,ctx};
+  std::unique_ptr<TrackCollection> outputTracks = std::make_unique<TrackCollection>();
 
-  //SG::ReadHandle<SpacePointForSeedCollection> seed_spcollection( m_seed_spcollectionKey, ctx );
-  SG::ReadHandle<SpacePointForSeedCollection> seed_spcollection( m_seed_spcollectionKey );
-  if (!seed_spcollection.isValid()){
-    msg(MSG:: FATAL) << "Could not find the data object "<< seed_spcollection.name() << " !" << endmsg;
-    return StatusCode::RECOVERABLE;
-  }
+  std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry
+      = m_trackingGeometryTool->trackingGeometry();
 
-  const FaserActsGeometryContext& gctx
-      = m_extrapolationTool->trackingGeometryTool()->getNominalGeometryContext();
+  const FaserActsGeometryContext& gctx = m_trackingGeometryTool->getNominalGeometryContext();
   auto geoctx = gctx.context();
-  Acts::MagneticFieldContext magctx = getMagneticFieldContext();
+  Acts::MagneticFieldContext magctx = getMagneticFieldContext(ctx);
   Acts::CalibrationContext calctx;
 
-  std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry
-      = m_extrapolationTool->trackingGeometryTool()->trackingGeometry();
-
-  std::shared_ptr<const Acts::Surface> pSurface;
-
-  // Get SiDetectorElements
-  const TrackerDD::SiDetectorElementCollection* elCollection{m_detManager->getDetectorElementCollection()};
-  if (elCollection == nullptr) {
-    ATH_MSG_FATAL("Null pointer is returned by getDetectorElementCollection()");
-    return StatusCode::FAILURE;
-  }
-
+  CHECK(m_trackFinderTool->run());
+  std::shared_ptr<const Acts::Surface> initialSurface =
+      m_trackFinderTool->initialSurface();
+  std::shared_ptr<std::vector<Acts::CurvilinearTrackParameters>> initialTrackParameters =
+      m_trackFinderTool->initialTrackParameters();
+  std::shared_ptr<std::vector<std::vector<IndexSourceLink>>> sourceLinks =
+      m_trackFinderTool->sourceLinks();
+  std::shared_ptr<std::vector<IdentifierLink>> idLinks = m_trackFinderTool->idLinks();
+  std::shared_ptr<std::vector<std::vector<Measurement>>> measurements =
+      m_trackFinderTool->measurements();
+  std::shared_ptr<std::vector<std::vector<Tracker::FaserSCT_SpacePoint>>> spacePoints =
+      m_trackFinderTool->spacePoints();
+  std::shared_ptr<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>> clusters =
+      m_trackFinderTool->clusters();
 
-  static const TrackerDD::SCT_DetectorManager     *s_sct;
-  if(detStore()->retrieve(s_sct,"SCT").isFailure()) s_sct = 0;
-  int N_1_0=0, N_1_1=0, N_1_2=0, N_2_0=0, N_2_1=0, N_2_2=0;
-  Acts::Vector3 pos1_0(0., 0., 0.);
-  Acts::Vector3 pos1_1(0., 0., 0.);
-  Acts::Vector3 pos1_2(0., 0., 0.);
-  Acts::Vector3 pos2_0(0., 0., 0.);
-  Acts::Vector3 pos2_1(0., 0., 0.);
-  Acts::Vector3 pos2_2(0., 0., 0.);
-  HepMC::FourVector truthmom;
-  HepMC::FourVector pv;
+//  ATH_CHECK(m_protoTrackWriterTool->write(initialTrackParameters, measurements, geoctx));
 
-  // create source links and measurements
-  std::vector<IndexSourceLink> sourceLinks;
-  std::vector<const Acts::Surface*> surfSequence;
-  MeasurementContainer measurements;
+  int n_trackSeeds = initialTrackParameters->size();
 
-  SpacePointForSeedCollection::const_iterator it = seed_spcollection->begin();
-  SpacePointForSeedCollection::const_iterator itend = seed_spcollection->end();
-  for (; it != itend; ++it){
-    const Tracker::FaserSCT_SpacePoint *sp = (&(**it))->SpacePoint();
+  TrajectoryContainer trajectories;
+  trajectories.reserve(1);
 
-    const Identifier id = sp->clusterList().first->identify();
-    const TrackerDD::SiDetectorElement* siSpElement = m_detManager->getDetectorElement(id);
-    auto spElement = static_cast<const FaserActsDetectorElement>(siSpElement);
-    // Acts::GeometryIdentifier geoId = getGeometryIdentifier(id);
-    Acts::GeometryIdentifier geoId;
+  for (int i = 0; i < n_trackSeeds; ++i) {
 
-    const Acts::TrackingVolume* tVolume = trackingGeometry->highestTrackingVolume();
-    if (tVolume->confinedVolumes()) {
-      for (auto volume : tVolume->confinedVolumes()->arrayObjects()) {
-        if (volume->confinedLayers()) {
-          for (const auto& layer : volume->confinedLayers()->arrayObjects()) {
-            if (layer->layerType() == Acts::navigation) continue;
-            for (auto surface : layer->surfaceArray()->surfaces()) {
-              if (surface) {
-                const Acts::DetectorElementBase *detElement = surface->associatedDetectorElement();
-                const auto *faserDetElement = dynamic_cast<const FaserActsDetectorElement*>(detElement);
-                auto* tmp = const_cast<FaserActsDetectorElement*>(faserDetElement);
-                if (*tmp == spElement) {
-                  geoId = surface->geometryId();
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-
-    const Acts::Surface* surfacePtr = trackingGeometry->findSurface(geoId);
-    surfSequence.push_back(surfacePtr);
-    if (not surfacePtr) {
-      ATH_MSG_ERROR("Could not find surface " << geoId);
-      return StatusCode::FAILURE;
-    }
-
-    Index spIdx = measurements.size();
-    IndexSourceLink sourceLink(geoId, spIdx);
-
-
-    auto par = sp->localParameters();
-    auto cov = sp->localCovariance();
-    std::array<Acts::BoundIndices, 2> indices = {Acts::eBoundLoc0, Acts::eBoundLoc1};
-    ThisMeasurement meas(sourceLink, indices, par, cov);
-    sourceLinks.push_back(std::move(sourceLink));
-    measurements.emplace_back(std::move(meas));
-
-
-    // Get the truth position and momentum
-    SG::ReadHandle<McEventCollection> h_mcEvents(m_mcEventKey);
-    SG::ReadHandle<TrackerSimDataCollection> h_collectionMap(m_sctMap);
-    const auto& simdata = h_collectionMap->find(id)->second;
-    const auto& deposits = simdata.getdeposits();
-    for( const auto& depositPair : deposits)
-    {
-      if (depositPair.first->pdg_id() == -13) {
-        pv = depositPair.first->production_vertex()->position();
-        truthmom = depositPair.first->momentum();
-      }
-    }
-
-    // Get the measurements
-    Amg::Vector3D gloPos=sp->globalPosition();
-    int station = m_idHelper->station(id);
-    int plane = m_idHelper->layer(id);
-    if (station==1 && plane==0) {
-      N_1_0++;
-      pos1_0 = Acts::Vector3(gloPos.x(), gloPos.y(), gloPos.z());
-
-      // Construct a plane surface as the target surface
-      const TrackerDD::SiDetectorDesign &design = siSpElement->design();
-      double hlX = design.width()/2. * 1_mm;
-      double hlY = design.length()/2. * 1_mm;
-      auto rectangleBounds = std::make_shared<const Acts::RectangleBounds>(hlX, hlY);
-      Amg::Transform3D g2l = siSpElement->getMaterialGeom()->getDefAbsoluteTransform()
-                             * Amg::CLHEPTransformToEigen(siSpElement->recoToHitTransform());
-      pSurface = Acts::Surface::makeShared<Acts::PlaneSurface>( g2l, rectangleBounds );
-    }
-    if (station==1 && plane==1) {
-      N_1_1++;
-      pos1_1 = Acts::Vector3(gloPos.x(), gloPos.y(), gloPos.z());
-    }
-    if (station==1 && plane==2) {
-      N_1_2++;
-      pos1_2 = Acts::Vector3(gloPos.x(), gloPos.y(), gloPos.z());
-    }
-    if (station==2 && plane==0) {
-      N_2_0++;
-      pos2_0 = Acts::Vector3(gloPos.x(), gloPos.y(), gloPos.z());
-    }
-    if (station==2 && plane==1) {
-      N_2_1++;
-      pos2_1 = Acts::Vector3(gloPos.x(), gloPos.y(), gloPos.z());
-    }
-    if (station==2 && plane==2) {
-      N_2_2++;
-      pos2_2 = Acts::Vector3(gloPos.x(), gloPos.y(), gloPos.z());
-    }
-  }
-
-  for (auto sl : sourceLinks)
-  {
-    std::cout << "??volume=" << sl.geometryId().volume() << std::endl;
-    std::cout << "??layer=" << sl.geometryId().layer() << std::endl;
-    std::cout << "??sensitive=" << sl.geometryId().sensitive() << std::endl;
-  }
-
-  // Calculate the initial track parameters
-  if ( (N_1_0==1) && (N_1_1==1) && (N_1_2==1) && (N_2_0==1) && (N_2_1==1) && (N_2_2==1)) {
-    std::cout<<"!!!!!!!!!!!  pos1_0 = ("<<pos1_0.x()<<", "<<pos1_0.y()<<", "<<pos1_0.z()<<") "<<std::endl;
-    std::cout<<"!!!!!!!!!!!  pos1_1 = ("<<pos1_1.x()<<", "<<pos1_1.y()<<", "<<pos1_1.z()<<") "<<std::endl;
-    std::cout<<"!!!!!!!!!!!  pos1_2 = ("<<pos1_2.x()<<", "<<pos1_2.y()<<", "<<pos1_2.z()<<") "<<std::endl;
-    std::cout<<"!!!!!!!!!!!  pos2_0 = ("<<pos2_0.x()<<", "<<pos2_0.y()<<", "<<pos2_0.z()<<") "<<std::endl;
-    std::cout<<"!!!!!!!!!!!  pos2_1 = ("<<pos2_1.x()<<", "<<pos2_1.y()<<", "<<pos2_1.z()<<") "<<std::endl;
-    std::cout<<"!!!!!!!!!!!  pos2_2 = ("<<pos2_2.x()<<", "<<pos2_2.y()<<", "<<pos2_2.z()<<") "<<std::endl;
-    //@FIXME: change the hard codes in future
-    double charge = 1;
-    double B = 0.55;
-    //const Acts::Vector3 pos = pos1_0;
-    const Acts::Vector3 pos(pos1_0.x(), pos1_0.y(), pos1_0.z()-1);
-    Acts::Vector3 d1 = pos1_2 - pos1_0;
-    Acts::Vector3 d2 = pos2_2 - pos2_0;
-    // the direction of momentum in the first station
-    Acts::Vector3 direct1 = d1.normalized();
-    // the direction of momentum in the second station
-    Acts::Vector3 direct2 = d2.normalized();
-    // the vector pointing from the center of circle to the particle at layer 2 in Y-Z plane
-    double R1_z = charge * direct1.y() / std::sqrt(direct1.y()*direct1.y() + direct1.z()*direct1.z());
-    // double R1_y = -charge * direct1.z() / std::sqrt(direct1.y()*direct1.y() + direct1.z()*direct1.z());
-    // the vector pointing from the center of circle to the particle at layer 3 in Y-Z plane
-    double R2_z = charge * direct2.y() / std::sqrt(direct2.y()*direct2.y() + direct2.z()*direct2.z());
-    // double R2_y = -charge * direct2.z() / std::sqrt(direct2.y()*direct2.y() + direct2.z()*direct2.z());
-    // the norm of radius
-    double R = (pos2_0.z() - pos1_2.z()) / (R2_z - R1_z);
-    // the norm of momentum in Y-Z plane
-    double p_yz = 0.3*B*R / 1000.0;  // R(mm), p(GeV), B(T)
-    double p_z = p_yz * direct1.z() / std::sqrt(direct1.y()*direct1.y() + direct1.z()*direct1.z());
-    double p_y = p_yz * direct1.y() / std::sqrt(direct1.y()*direct1.y() + direct1.z()*direct1.z());
-    double p_x = direct1.x() * p_z / direct1.z();
-    // total momentum at the layer 0
-    const Acts::Vector3 mom(p_x, p_y, p_z);
-    double p = mom.norm();
-    std::cout<<"!!!!!!!!!!!  InitTrack momentum on layer 0: ( "<<mom.x()*1000<<",  "<<mom.y()*1000<<",  "<<mom.z()*1000<<",  "<<p*1000<<")  "<<std::endl;
-    // build the track covariance matrix using the smearing sigmas
-    double sigmaU = 200_um;
-    double sigmaV = 200_um;
-    double sigmaPhi = 1_degree;
-    double sigmaTheta = 1_degree;
-    double sigmaQOverP = 0.01*p / (p*p);
-    double sigmaT0 = 1_ns;
-    Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Zero();
-    cov(Acts::eBoundLoc0, Acts::eBoundLoc0) = sigmaU * sigmaU;
-    cov(Acts::eBoundLoc1, Acts::eBoundLoc1) = sigmaV * sigmaV;
-    cov(Acts::eBoundPhi, Acts::eBoundPhi) = sigmaPhi * sigmaPhi;
-    cov(Acts::eBoundTheta, Acts::eBoundTheta) = sigmaTheta * sigmaTheta;
-    cov(Acts::eBoundQOverP, Acts::eBoundQOverP) = sigmaQOverP * sigmaQOverP;
-    cov(Acts::eBoundTime, Acts::eBoundTime) = sigmaT0 * sigmaT0;
-    double time =0;
-    //Acts::CurvilinearTrackParameters InitTrackParam(std::make_optional(std::move(cov)), pos, mom, charge, time); // calculated initial parameters
-
-    // Smearing truth parameters as initial parameters
-    Acts::Vector3 pPos(pv.x(), pv.y(), pv.z());
-    Acts::Vector3 pMom(truthmom.x()/1000., truthmom.y()/1000., truthmom.z()/1000.);
-    std::random_device rd;
-    std::default_random_engine rng {rd()};
-    std::normal_distribution<> norm; // mu: 0 sigma: 1
-    Acts::Vector3 deltaPos(sigmaU*norm(rng), sigmaU*norm(rng), sigmaU*norm(rng));
-    auto theta = Acts::VectorHelpers::theta(pMom.normalized());
-    auto phi = Acts::VectorHelpers::phi(pMom.normalized());
-    auto angles = Acts::detail::normalizePhiTheta(phi + sigmaPhi*norm(rng), theta + sigmaTheta*norm(rng));
-    Acts::Vector3 dir(std::sin(angles.second) * std::cos(angles.first),
-                      std::sin(angles.second) * std::sin(angles.first),
-                      std::cos(angles.second));
-    const Acts::Vector3 deltaMom = ( pMom.norm()*(1 + 0.01*norm(rng)) ) * dir - pMom;
-    std::cout << "deltaPos: " << deltaPos << std::endl;
-    std::cout << "deltaMom: " << deltaMom << std::endl;
-    const Acts::Vector4 posTime ((pPos+deltaPos).x(), (pPos+deltaPos).y(), (pPos+deltaPos).z(), time);
-    const Acts::Vector3 momentum = pMom+deltaMom;
-    const Acts::Vector3 momentum_dir = momentum.normalized();
-    double momentum_abs = momentum.norm();
-    Acts::CurvilinearTrackParameters InitTrackParam(posTime, momentum_dir, momentum_abs, charge, std::make_optional(std::move(cov)));
-
-    // the surface which the production point is bound to
-    Acts::Vector3 center(0, 0, pPos.z());
-    Acts::Vector3 normal(0, 0, 1);
-    std::shared_ptr<const Acts::Surface> initSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(center, normal);
-    // extrapolate the particle from production point to the first layer
-    const Acts::Vector4 truthPosTime (pPos.x(), pPos.y(), pPos.z(), time);
-//    const Acts::Vector3 truthMomentum_dir = pMom.normalized();
-//    double truthMomentum_abs = pMom.norm();
-
-    BoundVector params = BoundVector::Zero();
-    params[Acts::eBoundLoc0] = pPos.x();
-    params[Acts::eBoundLoc1] = pPos.y();
-    params[Acts::eBoundPhi] = Acts::VectorHelpers::phi(pMom.normalized());
-    params[Acts::eBoundTheta] = Acts::VectorHelpers::theta(pMom.normalized());
-    params[Acts::eBoundQOverP] = charge/p;
-    params[Acts::eBoundTime] = time;
-
-    Acts::BoundTrackParameters startParameters(initSurface, params, charge, std::nullopt);
-    auto truthParam = m_extrapolationTool->propagate(Gaudi::Hive::currentContext(), startParameters, *pSurface);
-    std::cout << "truth pos on 1st layer: " << truthParam->position(geoctx) << std::endl;
-    std::cout << "truth mom on 1st layer: " << truthParam->momentum() << std::endl;
-    std::cout << "truth parameters on 1st layer: " << truthParam->parameters() << std::endl;
-
-
-    // Call the Acts Kalman Filter
-    // Prepare the output data with MultiTrajectory
-    TrajectoryContainer trajectories;
-    trajectories.reserve(1);
-
-    // Construct a perigee surface as the target surface
-    //auto pSurface = Acts::Surface::makeShared<Acts::PerigeeSurface>(
-    //                Acts::Vector3{0., 0., 0.});
-
-    // Set the KalmanFitter options
-    std::unique_ptr<const Acts::Logger> logger = Acts::getDefaultLogger("KalmanFitter", Acts::Logging::VERBOSE);
-    Acts::KalmanFitterOptions<MeasurementCalibrator, Acts::VoidOutlierFinder> kfOptions(
+    Acts::KalmanFitterOptions<MeasurementCalibrator, Acts::VoidOutlierFinder, Acts::VoidReverseFilteringLogic> kfOptions(
         geoctx,
         magctx,
         calctx,
-        MeasurementCalibrator(measurements),
+        MeasurementCalibrator(measurements->at(i)),
         Acts::VoidOutlierFinder(),
-        Acts::LoggerWrapper{*logger},
+        Acts::VoidReverseFilteringLogic(),
+        Acts::LoggerWrapper{*m_logger},
         Acts::PropagatorPlainOptions(),
-        &(*pSurface),
-        true, // scattering
-        true, // energy loss
-        false  // backward filtering
+        &(*initialSurface)
     );
+    kfOptions.multipleScattering = false;
+    kfOptions.energyLoss = false;
 
     ATH_MSG_DEBUG("Invoke fitter");
-
-    // Acts::Navigator     navigator(trackingGeometry);
-    Acts::DirectNavigator     navigator;
-    // navigator.resolvePassive   = false;
-    // navigator.resolveMaterial  = true;
-    // navigator.resolveSensitive = true;
-
-    std::unique_ptr<ActsExtrapolationDetail::VariantPropagator> varProp;
-
-    if (m_fieldMode == "FASER") {
-      ATH_MSG_INFO("Using FASER magnetic field service");
-      ATH_CHECK( m_fieldCondObjInputKey.initialize() );
-      auto bField = std::make_shared<FASERMagneticFieldWrapper>();
-      auto stepper = Acts::EigenStepper<>(std::move(bField));
-      auto propagator = Acts::Propagator<decltype(stepper), Acts::DirectNavigator>(std::move(stepper),
-                                                                                   std::move(navigator));
-      varProp = std::make_unique<VariantPropagator>(propagator);
-    }
-    else if (m_fieldMode == "Constant") {
-      if (m_constantFieldVector.value().size() != 3)
-      {
-        ATH_MSG_ERROR("Incorrect field vector size. Using empty field.");
-        return StatusCode::FAILURE; 
-      }
-
-      Acts::Vector3 constantFieldVector = Acts::Vector3(m_constantFieldVector[0], 
-                                                        m_constantFieldVector[1], 
-                                                        m_constantFieldVector[2]);
-
-      ATH_MSG_INFO("Using constant magnetic field: (Bx, By, Bz) = (" << m_constantFieldVector[0] << ", " 
-                                                                     << m_constantFieldVector[1] << ", " 
-                                                                     << m_constantFieldVector[2] << ")");
-      auto bField = std::make_shared<Acts::ConstantBField>(constantFieldVector);
-      auto stepper = Acts::EigenStepper<>(std::move(bField));
-      auto propagator = Acts::Propagator<decltype(stepper), Acts::DirectNavigator>(std::move(stepper),
-                                                                                   std::move(navigator));
-      varProp = std::make_unique<VariantPropagator>(propagator);
-    }
-
-    auto fit = makeFitterFunction(varProp.get());
-    auto result = fit(sourceLinks, InitTrackParam, kfOptions, surfSequence);
-
-    ATH_MSG_VERBOSE("Size of sourceLinks: " << sourceLinks.size());
+    auto result = (*m_fit)(sourceLinks->at(i), initialTrackParameters->at(i), kfOptions);
 
     int itrack = 0;
     if (result.ok()) {
       // Get the fit output object
       const auto& fitOutput = result.value();
+      std::unique_ptr<Trk::Track> track = makeTrack(geoctx, result, clusters->at(i));
+       if (track) {
+         outputTracks->push_back(std::move(track));
+       }
 
       // The track entry indices container. One element here.
       std::vector<size_t> trackTips;
@@ -568,752 +186,142 @@ StatusCode FaserActsKalmanFilterAlg::execute()
       trajectories.emplace_back(std::move(fitOutput.fittedStates),
                                 std::move(trackTips), std::move(indexedParams));
     } else {
-      ATH_MSG_WARNING("Fit failed for track " << itrack << " with error"
-                                              << result.error());
+      ATH_MSG_WARNING("Fit failed for track " << itrack << " with error" << result.error());
       // Fit failed, but still create a empty truth fit track
       trajectories.push_back(FaserActsRecMultiTrajectory());
     }
 
+  }
 
-    fillFitResult(geoctx, trajectories, *truthParam);
+//  std::vector<Acts::CurvilinearTrackParameters> initialTrackParametersVector {*initialTrackParameters};
+//  m_trajectoryWriterTool->writeout(trajectories, geoctx, initialTrackParametersVector);
+//  ATH_CHECK(m_trajectoryStatesWriterTool->write(trajectories, geoctx, *idLinks));
 
-  }
-  return StatusCode::SUCCESS;
-}
+  ATH_CHECK(trackContainer.record(std::move(outputTracks)));
 
-StatusCode FaserActsKalmanFilterAlg::finalize()
-{
   return StatusCode::SUCCESS;
 }
 
-namespace {
-
-  template <typename Fitter>
-  struct FitterFunctionImpl
-  {
-    Fitter fitter;
-
-    FitterFunctionImpl(Fitter&& f) : fitter(std::move(f)) {}
-
-    FaserActsKalmanFilterAlg::FitterResult
-    operator()(
-        const std::vector<IndexSourceLink>&    sourceLinks,
-        const Acts::CurvilinearTrackParameters&   initialParameters,
-        const Acts::KalmanFitterOptions<MeasurementCalibrator, Acts::VoidOutlierFinder>& options,
-        const std::vector<const Acts::Surface*>& sSequence) const
-    {
-      return fitter.fit(sourceLinks, initialParameters, options, sSequence);
-    };
-  };
+StatusCode FaserActsKalmanFilterAlg::finalize() {
+  return StatusCode::SUCCESS;
 }
 
-FaserActsKalmanFilterAlg::FitterFunction
-FaserActsKalmanFilterAlg::makeFitterFunction(
-    ActsExtrapolationDetail::VariantPropagator* varProp)
-{
-
-  return boost::apply_visitor([&](const auto& propagator) -> FitterFunction {
-    using Updater  = Acts::GainMatrixUpdater;
-    using Smoother = Acts::GainMatrixSmoother;
-    using Fitter = Acts::KalmanFitter<typename std::decay_t<decltype(propagator)>, Updater, Smoother>;
-
-    Fitter     fitter(std::move(propagator));
-    // build the fitter functions. owns the fitter object.
-    return FitterFunctionImpl<Fitter>(std::move(fitter));
-  }, *varProp);
 
-}
-
-//Acts::MagneticFieldContext FaserActsKalmanFilterAlg::getMagneticFieldContext(const EventContext& ctx) const {
-//SG::ReadCondHandle<FaserFieldCacheCondObj> readHandle{m_fieldCondObjInputKey, ctx};
-Acts::MagneticFieldContext FaserActsKalmanFilterAlg::getMagneticFieldContext() const {
-  SG::ReadCondHandle<FaserFieldCacheCondObj> readHandle{m_fieldCondObjInputKey};
+Acts::MagneticFieldContext FaserActsKalmanFilterAlg::getMagneticFieldContext(const EventContext& ctx) const {
+  SG::ReadCondHandle<FaserFieldCacheCondObj> readHandle{m_fieldCondObjInputKey, ctx};
   if (!readHandle.isValid()) {
     std::stringstream msg;
     msg << "Failed to retrieve magnetic field condition data " << m_fieldCondObjInputKey.key() << ".";
     throw std::runtime_error(msg.str());
   }
   const FaserFieldCacheCondObj* fieldCondObj{*readHandle};
-
   return Acts::MagneticFieldContext(fieldCondObj);
 }
 
-void FaserActsKalmanFilterAlg::initializeTree()
-{
-  m_trackTree->Branch("event_nr", &m_eventNr);
-  m_trackTree->Branch("traj_nr", &m_trajNr);
-  m_trackTree->Branch("track_nr", &m_trackNr);
-  m_trackTree->Branch("t_barcode", &m_t_barcode, "t_barcode/l");
-  m_trackTree->Branch("t_charge", &m_t_charge);
-  m_trackTree->Branch("t_eT", &m_t_eT);
-  m_trackTree->Branch("t_eLOC0", &m_t_eLOC0);
-  m_trackTree->Branch("t_eLOC1", &m_t_eLOC1);
-  m_trackTree->Branch("t_x", &m_t_x);
-  m_trackTree->Branch("t_y", &m_t_y);
-  m_trackTree->Branch("t_z", &m_t_z);
-  m_trackTree->Branch("t_px", &m_t_px);
-  m_trackTree->Branch("t_py", &m_t_py);
-  m_trackTree->Branch("t_pz", &m_t_pz);
-  m_trackTree->Branch("t_eTHETA", &m_t_eTHETA);
-  m_trackTree->Branch("t_ePHI", &m_t_ePHI);
-  m_trackTree->Branch("t_eQOP", &m_t_eQOP);
-
-  m_trackTree->Branch("hasFittedParams", &m_hasFittedParams);
-  m_trackTree->Branch("chi2_fit", &m_chi2_fit);
-  m_trackTree->Branch("ndf_fit", &m_ndf_fit);
-  m_trackTree->Branch("eLOC0_fit", &m_eLOC0_fit);
-  m_trackTree->Branch("eLOC1_fit", &m_eLOC1_fit);
-  m_trackTree->Branch("ePHI_fit", &m_ePHI_fit);
-  m_trackTree->Branch("eTHETA_fit", &m_eTHETA_fit);
-  m_trackTree->Branch("eQOP_fit", &m_eQOP_fit);
-  m_trackTree->Branch("eT_fit", &m_eT_fit);
-  m_trackTree->Branch("charge_fit", &m_charge_fit);
-  m_trackTree->Branch("err_eLOC0_fit", &m_err_eLOC0_fit);
-  m_trackTree->Branch("err_eLOC1_fit", &m_err_eLOC1_fit);
-  m_trackTree->Branch("err_ePHI_fit", &m_err_ePHI_fit);
-  m_trackTree->Branch("err_eTHETA_fit", &m_err_eTHETA_fit);
-  m_trackTree->Branch("err_eQOP_fit", &m_err_eQOP_fit);
-  m_trackTree->Branch("err_eT_fit", &m_err_eT_fit);
-  m_trackTree->Branch("g_px_fit", &m_px_fit);
-  m_trackTree->Branch("g_py_fit", &m_py_fit);
-  m_trackTree->Branch("g_pz_fit", &m_pz_fit);
-  m_trackTree->Branch("g_x_fit" , &m_x_fit);
-  m_trackTree->Branch("g_y_fit" , &m_y_fit);
-  m_trackTree->Branch("g_z_fit" , &m_z_fit);
-
-  m_trackTree->Branch("nHoles", &m_nHoles);
-  m_trackTree->Branch("nOutliers", &m_nOutliers);
-  m_trackTree->Branch("nStates", &m_nStates);
-  m_trackTree->Branch("nMeasurements", &m_nMeasurements);
-  m_trackTree->Branch("volume_id", &m_volumeID);
-  m_trackTree->Branch("layer_id", &m_layerID);
-  m_trackTree->Branch("module_id", &m_moduleID);
-  m_trackTree->Branch("l_x_hit", &m_lx_hit);
-  m_trackTree->Branch("l_y_hit", &m_ly_hit);
-  m_trackTree->Branch("g_x_hit", &m_x_hit);
-  m_trackTree->Branch("g_y_hit", &m_y_hit);
-  m_trackTree->Branch("g_z_hit", &m_z_hit);
-  m_trackTree->Branch("res_x_hit", &m_res_x_hit);
-  m_trackTree->Branch("res_y_hit", &m_res_y_hit);
-  m_trackTree->Branch("err_x_hit", &m_err_x_hit);
-  m_trackTree->Branch("err_y_hit", &m_err_y_hit);
-  m_trackTree->Branch("pull_x_hit", &m_pull_x_hit);
-  m_trackTree->Branch("pull_y_hit", &m_pull_y_hit);
-  m_trackTree->Branch("dim_hit", &m_dim_hit);
 
-  m_trackTree->Branch("nPredicted", &m_nPredicted);
-  m_trackTree->Branch("predicted", &m_prt);
-  m_trackTree->Branch("eLOC0_prt", &m_eLOC0_prt);
-  m_trackTree->Branch("eLOC1_prt", &m_eLOC1_prt);
-  m_trackTree->Branch("ePHI_prt", &m_ePHI_prt);
-  m_trackTree->Branch("eTHETA_prt", &m_eTHETA_prt);
-  m_trackTree->Branch("eQOP_prt", &m_eQOP_prt);
-  m_trackTree->Branch("eT_prt", &m_eT_prt);
-  m_trackTree->Branch("res_eLOC0_prt", &m_res_eLOC0_prt);
-  m_trackTree->Branch("res_eLOC1_prt", &m_res_eLOC1_prt);
-  m_trackTree->Branch("err_eLOC0_prt", &m_err_eLOC0_prt);
-  m_trackTree->Branch("err_eLOC1_prt", &m_err_eLOC1_prt);
-  m_trackTree->Branch("err_ePHI_prt", &m_err_ePHI_prt);
-  m_trackTree->Branch("err_eTHETA_prt", &m_err_eTHETA_prt);
-  m_trackTree->Branch("err_eQOP_prt", &m_err_eQOP_prt);
-  m_trackTree->Branch("err_eT_prt", &m_err_eT_prt);
-  m_trackTree->Branch("pull_eLOC0_prt", &m_pull_eLOC0_prt);
-  m_trackTree->Branch("pull_eLOC1_prt", &m_pull_eLOC1_prt);
-  m_trackTree->Branch("g_x_prt", &m_x_prt);
-  m_trackTree->Branch("g_y_prt", &m_y_prt);
-  m_trackTree->Branch("g_z_prt", &m_z_prt);
-  m_trackTree->Branch("px_prt", &m_px_prt);
-  m_trackTree->Branch("py_prt", &m_py_prt);
-  m_trackTree->Branch("pz_prt", &m_pz_prt);
-  m_trackTree->Branch("eta_prt", &m_eta_prt);
-  m_trackTree->Branch("pT_prt", &m_pT_prt);
-
-  m_trackTree->Branch("nFiltered", &m_nFiltered);
-  m_trackTree->Branch("filtered", &m_flt);
-  m_trackTree->Branch("eLOC0_flt", &m_eLOC0_flt);
-  m_trackTree->Branch("eLOC1_flt", &m_eLOC1_flt);
-  m_trackTree->Branch("ePHI_flt", &m_ePHI_flt);
-  m_trackTree->Branch("eTHETA_flt", &m_eTHETA_flt);
-  m_trackTree->Branch("eQOP_flt", &m_eQOP_flt);
-  m_trackTree->Branch("eT_flt", &m_eT_flt);
-  m_trackTree->Branch("res_eLOC0_flt", &m_res_eLOC0_flt);
-  m_trackTree->Branch("res_eLOC1_flt", &m_res_eLOC1_flt);
-  m_trackTree->Branch("err_eLOC0_flt", &m_err_eLOC0_flt);
-  m_trackTree->Branch("err_eLOC1_flt", &m_err_eLOC1_flt);
-  m_trackTree->Branch("err_ePHI_flt", &m_err_ePHI_flt);
-  m_trackTree->Branch("err_eTHETA_flt", &m_err_eTHETA_flt);
-  m_trackTree->Branch("err_eQOP_flt", &m_err_eQOP_flt);
-  m_trackTree->Branch("err_eT_flt", &m_err_eT_flt);
-  m_trackTree->Branch("pull_eLOC0_flt", &m_pull_eLOC0_flt);
-  m_trackTree->Branch("pull_eLOC1_flt", &m_pull_eLOC1_flt);
-  m_trackTree->Branch("g_x_flt", &m_x_flt);
-  m_trackTree->Branch("g_y_flt", &m_y_flt);
-  m_trackTree->Branch("g_z_flt", &m_z_flt);
-  m_trackTree->Branch("px_flt", &m_px_flt);
-  m_trackTree->Branch("py_flt", &m_py_flt);
-  m_trackTree->Branch("pz_flt", &m_pz_flt);
-  m_trackTree->Branch("eta_flt", &m_eta_flt);
-  m_trackTree->Branch("pT_flt", &m_pT_flt);
-  m_trackTree->Branch("chi2", &m_chi2);
-
-  m_trackTree->Branch("nSmoothed", &m_nSmoothed);
-  m_trackTree->Branch("smoothed", &m_smt);
-  m_trackTree->Branch("eLOC0_smt", &m_eLOC0_smt);
-  m_trackTree->Branch("eLOC1_smt", &m_eLOC1_smt);
-  m_trackTree->Branch("ePHI_smt", &m_ePHI_smt);
-  m_trackTree->Branch("eTHETA_smt", &m_eTHETA_smt);
-  m_trackTree->Branch("eQOP_smt", &m_eQOP_smt);
-  m_trackTree->Branch("eT_smt", &m_eT_smt);
-  m_trackTree->Branch("res_eLOC0_smt", &m_res_eLOC0_smt);
-  m_trackTree->Branch("res_eLOC1_smt", &m_res_eLOC1_smt);
-  m_trackTree->Branch("err_eLOC0_smt", &m_err_eLOC0_smt);
-  m_trackTree->Branch("err_eLOC1_smt", &m_err_eLOC1_smt);
-  m_trackTree->Branch("err_ePHI_smt", &m_err_ePHI_smt);
-  m_trackTree->Branch("err_eTHETA_smt", &m_err_eTHETA_smt);
-  m_trackTree->Branch("err_eQOP_smt", &m_err_eQOP_smt);
-  m_trackTree->Branch("err_eT_smt", &m_err_eT_smt);
-  m_trackTree->Branch("pull_eLOC0_smt", &m_pull_eLOC0_smt);
-  m_trackTree->Branch("pull_eLOC1_smt", &m_pull_eLOC1_smt);
-  m_trackTree->Branch("g_x_smt", &m_x_smt);
-  m_trackTree->Branch("g_y_smt", &m_y_smt);
-  m_trackTree->Branch("g_z_smt", &m_z_smt);
-  m_trackTree->Branch("px_smt", &m_px_smt);
-  m_trackTree->Branch("py_smt", &m_py_smt);
-  m_trackTree->Branch("pz_smt", &m_pz_smt);
-  m_trackTree->Branch("eta_smt", &m_eta_smt);
-  m_trackTree->Branch("pT_smt", &m_pT_smt);
+std::unique_ptr<Trk::Track>
+FaserActsKalmanFilterAlg::makeTrack(Acts::GeometryContext& geoCtx, TrackFitterResult& fitResult, std::vector<const Tracker::FaserSCT_Cluster*> clusters) const {
+  using ConstTrackStateProxy =
+     Acts::detail_lt::TrackStateProxy<IndexSourceLink, 6, true>;
+  std::unique_ptr<Trk::Track> newtrack = nullptr;
+  //Get the fit output object
+  const auto& fitOutput = fitResult.value();
+  if (fitOutput.fittedParameters) {
+    DataVector<const Trk::TrackStateOnSurface>* finalTrajectory = new DataVector<const Trk::TrackStateOnSurface>{};
+    std::vector<std::unique_ptr<const Acts::BoundTrackParameters>> actsSmoothedParam;
+    // Loop over all the output state to create track state
+    fitOutput.fittedStates.visitBackwards(fitOutput.lastMeasurementIndex, [&](const ConstTrackStateProxy& state) {
+      auto flag = state.typeFlags();
+      if (state.referenceSurface().associatedDetectorElement() != nullptr) {
+        // We need to determine the type of state
+        std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePattern;
+        const Trk::TrackParameters *parm;
+
+        // State is a hole (no associated measurement), use predicted para meters
+        if (flag[Acts::TrackStateFlag::HoleFlag] == true) {
+          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
+                                                     state.predicted(),
+                                                     state.predictedCovariance());
+          parm = ConvertActsTrackParameterToATLAS(actsParam, geoCtx);
+          // auto boundaryCheck = m_boundaryCheckTool->boundaryCheck(*p arm);
+          typePattern.set(Trk::TrackStateOnSurface::Hole);
+        }
+          // The state was tagged as an outlier, use filtered parameters
+        else if (flag[Acts::TrackStateFlag::OutlierFlag] == true) {
+          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
+                                                     state.filtered(), state.filteredCovariance());
+          parm = ConvertActsTrackParameterToATLAS(actsParam, geoCtx);
+          typePattern.set(Trk::TrackStateOnSurface::Outlier);
+        }
+          // The state is a measurement state, use smoothed parameters
+        else {
+          const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
+                                                     state.smoothed(), state.smoothedCovariance());
+          actsSmoothedParam.push_back(std::make_unique<const Acts::BoundTrackParameters>(Acts::BoundTrackParameters(actsParam)));
+          //  const auto& psurface=actsParam.referenceSurface();
+          Acts::Vector2 local(actsParam.parameters()[Acts::eBoundLoc0], actsParam.parameters()[Acts::eBoundLoc1]);
+          //  const Acts::Vector3 dir = Acts::makeDirectionUnitFromPhiTheta(actsParam.parameters()[Acts::eBoundPhi], actsParam.parameters()[Acts::eBoundTheta]);
+          //  auto pos=actsParam.position(tgContext);
+          parm = ConvertActsTrackParameterToATLAS(actsParam, geoCtx);
+          typePattern.set(Trk::TrackStateOnSurface::Measurement);
+        }
+        Tracker::FaserSCT_ClusterOnTrack* measState = nullptr;
+        if (state.hasUncalibrated()) {
+          const Tracker::FaserSCT_Cluster* fitCluster = clusters.at(state.uncalibrated().index());
+          if (fitCluster->detectorElement() != nullptr) {
+            measState = new Tracker::FaserSCT_ClusterOnTrack{
+                fitCluster,
+                Trk::LocalParameters{
+                    Trk::DefinedParameter{fitCluster->localPosition()[0], Trk::loc1},
+                    Trk::DefinedParameter{fitCluster->localPosition()[1], Trk::loc2}
+                },
+                fitCluster->localCovariance(),
+                m_idHelper->wafer_hash(fitCluster->detectorElement()->identify())
+            };
+          }
+        }
+        double nDoF = state.calibratedSize();
+        const Trk::FitQualityOnSurface *quality = new Trk::FitQualityOnSurface(state.chi2(), nDoF);
+        const Trk::TrackStateOnSurface *perState = new Trk::TrackStateOnSurface(measState, parm, quality, nullptr, typePattern);
+        // If a state was succesfully created add it to the trajectory
+        if (perState) {
+          finalTrajectory->insert(finalTrajectory->begin(), perState);
+        }
+      }
+      return;
+    });
+
+    // Create the track using the states
+    const Trk::TrackInfo newInfo(Trk::TrackInfo::TrackFitter::KalmanFitter, Trk::ParticleHypothesis::muon);
+    // Trk::FitQuality* q = nullptr;
+    // newInfo.setTrackFitter(Trk::TrackInfo::TrackFitter::KalmanFitter     ); //Mark the fitter as KalmanFitter
+    newtrack = std::make_unique<Trk::Track>(newInfo, std::move(*finalTrajectory), nullptr);
+  }
+  return newtrack;
 }
 
-void FaserActsKalmanFilterAlg::fillFitResult(
-    const Acts::GeometryContext& geoctx,
-    const TrajectoryContainer& trajectories,
-    const Acts::BoundTrackParameters& truthParam
-)
-{
-  m_t_eLOC0 = truthParam.parameters()[Acts::eBoundLoc0];
-  m_t_eLOC1 = truthParam.parameters()[Acts::eBoundLoc1];
-  m_t_ePHI = truthParam.parameters()[Acts::eBoundPhi];
-  m_t_eTHETA = truthParam.parameters()[Acts::eBoundTheta];
-  m_t_eQOP = truthParam.parameters()[Acts::eBoundQOverP];
-  m_t_eT = truthParam.parameters()[Acts::eBoundTime];
-  m_t_x = truthParam.position(geoctx)(0);
-  m_t_y = truthParam.position(geoctx)(1);
-  m_t_z = truthParam.position(geoctx)(2);
-  m_t_px = truthParam.momentum()(0);
-  m_t_py = truthParam.momentum()(1);
-  m_t_pz = truthParam.momentum()(2);
-  std::cout<<"truth global position on the first layer = "<<m_t_x<<"  "<<m_t_y<<"  "<<m_t_z<<"  "<<std::endl;
-  std::cout<<"truth momentum on the first layer = "<<m_t_px<<"  "<<m_t_py<<"  "<<m_t_pz<<"  "<<std::endl;
-  std::cout<<"truth local parameters on the first layer = "<<m_t_eLOC0<<"  "<<m_t_eLOC1<<"  "<<m_t_ePHI<<"  "<<m_t_eTHETA<<"  "<<m_t_eQOP<<"  "<<std::endl;
-
-  // Loop over the trajectories
-  int iTraj = 0;
-  for (const auto& traj : trajectories) {
-    m_trajNr = iTraj;
-
-    // The trajectory entry indices and the multiTrajectory
-    const auto& [trackTips, mj] = traj.trajectory();
-    if (trackTips.empty()) {
-      ATH_MSG_WARNING("Empty multiTrajectory.");
-      continue;
+const Trk::TrackParameters*
+FaserActsKalmanFilterAlg ::ConvertActsTrackParameterToATLAS(const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext& gctx) const      {
+  using namespace Acts::UnitLiterals;
+  std::optional<AmgSymMatrix(5)> cov = std::nullopt;
+  if (actsParameter.covariance()){
+    AmgSymMatrix(5) newcov(actsParameter.covariance()->topLeftCorner(5, 5));
+    // Convert the covariance matrix to GeV
+    for(int i=0; i < newcov.rows(); i++){
+      newcov(i, 4) = newcov(i, 4)*1_MeV;
     }
-
-    // Get the entry index for the single trajectory
-    auto& trackTip = trackTips.front();
-    std::cout<<"trackTip = "<<trackTip<<std::endl;
-
-    // Collect the trajectory summary info
-    auto trajState =
-        Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip);
-
-    m_nMeasurements = trajState.nMeasurements;
-    m_nStates = trajState.nStates;
-    m_nOutliers = trajState.nOutliers;
-    m_nHoles = trajState.nHoles;
-    m_chi2_fit = trajState.chi2Sum;
-    m_ndf_fit = trajState.NDF;
-    std::cout << "Track has " << trajState.nMeasurements
-              << " measurements and " << trajState.nHoles
-              << " holes and " << trajState.nOutliers
-              << " outliers and " << trajState.nStates
-              << " states " << std::endl;
-
-    /// If it has track parameters, fill the values
-    if (traj.hasTrackParameters(trackTip))
-    {
-      m_hasFittedParams = true;
-      const auto &boundParam = traj.trackParameters(trackTip);
-      const auto &parameter = boundParam.parameters();
-      const auto &covariance = *boundParam.covariance();
-      m_charge_fit = boundParam.charge();
-      m_eLOC0_fit = parameter[Acts::eBoundLoc0];
-      m_eLOC1_fit = parameter[Acts::eBoundLoc1];
-      m_ePHI_fit = parameter[Acts::eBoundPhi];
-      m_eTHETA_fit = parameter[Acts::eBoundTheta];
-      m_eQOP_fit = parameter[Acts::eBoundQOverP];
-      m_eT_fit = parameter[Acts::eBoundTime];
-      m_err_eLOC0_fit =
-          sqrt(covariance(Acts::eBoundLoc0, Acts::eBoundLoc0));
-      m_err_eLOC1_fit =
-          sqrt(covariance(Acts::eBoundLoc1, Acts::eBoundLoc1));
-      m_err_ePHI_fit = sqrt(covariance(Acts::eBoundPhi, Acts::eBoundPhi));
-      m_err_eTHETA_fit =
-          sqrt(covariance(Acts::eBoundTheta, Acts::eBoundTheta));
-      m_err_eQOP_fit = sqrt(covariance(Acts::eBoundQOverP, Acts::eBoundQOverP));
-      m_err_eT_fit = sqrt(covariance(Acts::eBoundTime, Acts::eBoundTime));
-
-      m_px_fit = boundParam.momentum()(0);
-      m_py_fit = boundParam.momentum()(1);
-      m_pz_fit = boundParam.momentum()(2);
-      m_x_fit  = boundParam.position(geoctx)(0);
-      m_y_fit  = boundParam.position(geoctx)(1);
-      m_z_fit  = boundParam.position(geoctx)(2);
+    for(int i=0; i < newcov.cols(); i++){
+      newcov(4, i) = newcov(4, i)*1_MeV;
     }
-
-    m_nPredicted = 0;
-    m_nFiltered = 0;
-    m_nSmoothed = 0;
-
-    mj.visitBackwards(trackTip, [&](const auto &state) {
-      /// Only fill the track states with non-outlier measurement
-      auto typeFlags = state.typeFlags();
-      if (not typeFlags.test(Acts::TrackStateFlag::MeasurementFlag))
-      {
-        return true;
-      }
-
-      const auto& surface = state.referenceSurface();
-
-      /// Get the geometry ID
-      auto geoID = state.referenceSurface().geometryId();
-      m_volumeID.push_back(geoID.volume());
-      m_layerID.push_back(geoID.layer());
-      m_moduleID.push_back(geoID.sensitive());
-
-      // expand the local measurements into the full bound space
-      Acts::BoundVector meas =
-          state.projector().transpose() * state.calibrated();
-
-      // extract local and global position
-      Acts::Vector2 local(meas[Acts::eBoundLoc0], meas[Acts::eBoundLoc1]);
-      Acts::Vector3 mom(1, 1, 1);
-      Acts::Vector3 global =
-          surface.localToGlobal(geoctx, local, mom);
-
-      // fill the measurement info
-      m_lx_hit.push_back(local[Acts::ePos0]);
-      m_ly_hit.push_back(local[Acts::ePos1]);
-      m_x_hit.push_back(global[Acts::ePos0]);
-      m_y_hit.push_back(global[Acts::ePos1]);
-      m_z_hit.push_back(global[Acts::ePos2]);
-
-      /// Get the predicted parameter for this state
-      bool predicted = false;
-      if (state.hasPredicted())
-      {
-        predicted = true;
-        m_nPredicted++;
-        Acts::BoundTrackParameters parameter(
-            state.referenceSurface().getSharedPtr(),
-            state.predicted(),
-            state.predictedCovariance());
-        auto covariance = state.predictedCovariance();
-
-        /// Local hit residual info
-        auto H = state.effectiveProjector();
-        auto resCov = state.effectiveCalibratedCovariance() +
-                      H * covariance * H.transpose();
-        auto residual = state.effectiveCalibrated() - H * state.predicted();
-
-        /// Predicted residual
-        m_res_eLOC0_prt.push_back(residual(Acts::eBoundLoc0));
-        m_res_eLOC1_prt.push_back(residual(Acts::eBoundLoc1));
-
-        /// Predicted parameter pulls
-        m_pull_eLOC0_prt.push_back(
-            residual(Acts::eBoundLoc0) /
-            sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_pull_eLOC1_prt.push_back(
-            residual(Acts::eBoundLoc1) /
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
-
-
-        /// Predicted parameter
-        m_eLOC0_prt.push_back(parameter.parameters()[Acts::eBoundLoc0]);
-        m_eLOC1_prt.push_back(parameter.parameters()[Acts::eBoundLoc1]);
-        m_ePHI_prt.push_back(parameter.parameters()[Acts::eBoundPhi]);
-        m_eTHETA_prt.push_back(parameter.parameters()[Acts::eBoundTheta]);
-        m_eQOP_prt.push_back(parameter.parameters()[Acts::eBoundQOverP]);
-        m_eT_prt.push_back(parameter.parameters()[Acts::eBoundTime]);
-
-        /// Predicted parameter Uncertainties
-        m_err_eLOC0_prt.push_back(
-            sqrt(covariance(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_err_eLOC1_prt.push_back(
-            sqrt(covariance(Acts::eBoundLoc1, Acts::eBoundLoc1)));
-        m_err_ePHI_prt.push_back(
-            sqrt(covariance(Acts::eBoundPhi, Acts::eBoundPhi)));
-        m_err_eTHETA_prt.push_back(
-            sqrt(covariance(Acts::eBoundTheta, Acts::eBoundTheta)));
-        m_err_eQOP_prt.push_back(
-            sqrt(covariance(Acts::eBoundQOverP, Acts::eBoundQOverP)));
-        m_err_eT_prt.push_back(
-            sqrt(covariance(Acts::eBoundTime, Acts::eBoundTime)));
-
-        m_x_prt.push_back(parameter.position(geoctx).x());
-        m_y_prt.push_back(parameter.position(geoctx).y());
-        m_z_prt.push_back(parameter.position(geoctx).z());
-        m_px_prt.push_back(parameter.momentum().x());
-        m_py_prt.push_back(parameter.momentum().y());
-        m_pz_prt.push_back(parameter.momentum().z());
-        m_pT_prt.push_back(parameter.transverseMomentum());
-        m_eta_prt.push_back(eta(parameter.position(geoctx)));
-      }
-      else
-      {
-        /// Push bad values if no predicted parameter
-        m_eLOC0_prt.push_back(-9999);
-        m_eLOC1_prt.push_back(-9999);
-        m_ePHI_prt.push_back(-9999);
-        m_eTHETA_prt.push_back(-9999);
-        m_eQOP_prt.push_back(-9999);
-        m_eT_prt.push_back(-9999);
-        m_res_eLOC0_prt.push_back(-9999);
-        m_res_eLOC1_prt.push_back(-9999);
-        m_err_eLOC0_prt.push_back(-9999);
-        m_err_eLOC1_prt.push_back(-9999);
-        m_err_ePHI_prt.push_back(-9999);
-        m_err_eTHETA_prt.push_back(-9999);
-        m_err_eQOP_prt.push_back(-9999);
-        m_err_eT_prt.push_back(-9999);
-        m_pull_eLOC0_prt.push_back(-9999);
-        m_pull_eLOC1_prt.push_back(-9999);
-        m_x_prt.push_back(-9999);
-        m_y_prt.push_back(-9999);
-        m_z_prt.push_back(-9999);
-        m_px_prt.push_back(-9999);
-        m_py_prt.push_back(-9999);
-        m_pz_prt.push_back(-9999);
-        m_pT_prt.push_back(-9999);
-        m_eta_prt.push_back(-9999);
-      }
-
-      bool filtered = false;
-      if (state.hasFiltered())
-      {
-        filtered = true;
-        m_nFiltered++;
-        Acts::BoundTrackParameters parameter(
-            state.referenceSurface().getSharedPtr(),
-            state.filtered(),
-            state.filteredCovariance());
-        auto covariance = state.filteredCovariance();
-
-        /// Local hit residual info
-        auto H = state.effectiveProjector();
-        auto resCov = state.effectiveCalibratedCovariance() +
-                      H * covariance * H.transpose();
-        auto residual = state.effectiveCalibrated() - H * state.filtered();
-
-        /// Filtered residual
-        m_res_eLOC0_flt.push_back(residual(Acts::eBoundLoc0));
-        m_res_eLOC1_flt.push_back(residual(Acts::eBoundLoc1));
-
-        /// Filtered parameter pulls
-        m_pull_eLOC0_flt.push_back(
-            residual(Acts::eBoundLoc0) /
-            sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_pull_eLOC1_flt.push_back(
-            residual(Acts::eBoundLoc1) /
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
-
-        /// Filtered parameter
-        m_eLOC0_flt.push_back(parameter.parameters()[Acts::eBoundLoc0]);
-        m_eLOC1_flt.push_back(parameter.parameters()[Acts::eBoundLoc1]);
-        m_ePHI_flt.push_back(parameter.parameters()[Acts::eBoundPhi]);
-        m_eTHETA_flt.push_back(parameter.parameters()[Acts::eBoundTheta]);
-        m_eQOP_flt.push_back(parameter.parameters()[Acts::eBoundQOverP]);
-        m_eT_flt.push_back(parameter.parameters()[Acts::eBoundTime]);
-
-        /// Filtered parameter uncertainties
-        m_err_eLOC0_flt.push_back(
-            sqrt(covariance(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_err_eLOC1_flt.push_back(
-            sqrt(covariance(Acts::eBoundLoc1, Acts::eBoundLoc1)));
-        m_err_ePHI_flt.push_back(
-            sqrt(covariance(Acts::eBoundPhi, Acts::eBoundPhi)));
-        m_err_eTHETA_flt.push_back(
-            sqrt(covariance(Acts::eBoundTheta, Acts::eBoundTheta)));
-        m_err_eQOP_flt.push_back(
-            sqrt(covariance(Acts::eBoundQOverP, Acts::eBoundQOverP)));
-        m_err_eT_flt.push_back(
-            sqrt(covariance(Acts::eBoundTime, Acts::eBoundTime)));
-
-        /// Other filtered parameter info
-        m_x_flt.push_back(parameter.position(geoctx).x());
-        m_y_flt.push_back(parameter.position(geoctx).y());
-        m_z_flt.push_back(parameter.position(geoctx).z());
-        m_px_flt.push_back(parameter.momentum().x());
-        m_py_flt.push_back(parameter.momentum().y());
-        m_pz_flt.push_back(parameter.momentum().z());
-        m_pT_flt.push_back(parameter.transverseMomentum());
-        m_eta_flt.push_back(eta(parameter.position(geoctx)));
-        m_chi2.push_back(state.chi2());
-
-      }
-      else
-      {
-        /// Push bad values if no filtered parameter
-        m_eLOC0_flt.push_back(-9999);
-        m_eLOC1_flt.push_back(-9999);
-        m_ePHI_flt.push_back(-9999);
-        m_eTHETA_flt.push_back(-9999);
-        m_eQOP_flt.push_back(-9999);
-        m_eT_flt.push_back(-9999);
-        m_res_eLOC0_flt.push_back(-9999);
-        m_res_eLOC1_flt.push_back(-9999);
-        m_err_eLOC0_flt.push_back(-9999);
-        m_err_eLOC1_flt.push_back(-9999);
-        m_err_ePHI_flt.push_back(-9999);
-        m_err_eTHETA_flt.push_back(-9999);
-        m_err_eQOP_flt.push_back(-9999);
-        m_err_eT_flt.push_back(-9999);
-        m_pull_eLOC0_flt.push_back(-9999);
-        m_pull_eLOC1_flt.push_back(-9999);
-        m_x_flt.push_back(-9999);
-        m_y_flt.push_back(-9999);
-        m_z_flt.push_back(-9999);
-        m_py_flt.push_back(-9999);
-        m_pz_flt.push_back(-9999);
-        m_pT_flt.push_back(-9999);
-        m_eta_flt.push_back(-9999);
-        m_chi2.push_back(-9999);
-      }
-
-      bool smoothed = false;
-      if (state.hasSmoothed())
-      {
-        smoothed = true;
-        m_nSmoothed++;
-        Acts::BoundTrackParameters parameter(
-            state.referenceSurface().getSharedPtr(),
-            state.smoothed(),
-            state.smoothedCovariance());
-        auto covariance = state.smoothedCovariance();
-
-        /// Local hit residual info
-        auto H = state.effectiveProjector();
-        auto resCov = state.effectiveCalibratedCovariance() +
-                      H * covariance * H.transpose();
-        auto residual = state.effectiveCalibrated() - H * state.smoothed();
-
-        m_res_x_hit.push_back(residual(Acts::eBoundLoc0));
-        m_res_y_hit.push_back(residual(Acts::eBoundLoc1));
-        m_err_x_hit.push_back(
-            sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_err_y_hit.push_back(
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
-        m_pull_x_hit.push_back(
-            residual(Acts::eBoundLoc0) /
-            sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_pull_y_hit.push_back(
-            residual(Acts::eBoundLoc1) /
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
-        m_dim_hit.push_back(state.calibratedSize());
-
-        /// Smoothed residual
-        m_res_eLOC0_smt.push_back(residual(Acts::eBoundLoc0));
-        m_res_eLOC1_smt.push_back(residual(Acts::eBoundLoc1));
-
-        /// Smoothed parameter pulls
-        m_pull_eLOC0_smt.push_back(
-            residual(Acts::eBoundLoc0) /
-            sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_pull_eLOC1_smt.push_back(
-            residual(Acts::eBoundLoc1) /
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
-
-        /// Smoothed parameter
-        m_eLOC0_smt.push_back(parameter.parameters()[Acts::eBoundLoc0]);
-        m_eLOC1_smt.push_back(parameter.parameters()[Acts::eBoundLoc1]);
-        m_ePHI_smt.push_back(parameter.parameters()[Acts::eBoundPhi]);
-        m_eTHETA_smt.push_back(parameter.parameters()[Acts::eBoundTheta]);
-        m_eQOP_smt.push_back(parameter.parameters()[Acts::eBoundQOverP]);
-        m_eT_smt.push_back(parameter.parameters()[Acts::eBoundTime]);
-
-        /// Smoothed parameter uncertainties
-        m_err_eLOC0_smt.push_back(
-            sqrt(covariance(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_err_eLOC1_smt.push_back(
-            sqrt(covariance(Acts::eBoundLoc1, Acts::eBoundLoc1)));
-        m_err_ePHI_smt.push_back(
-            sqrt(covariance(Acts::eBoundPhi, Acts::eBoundPhi)));
-        m_err_eTHETA_smt.push_back(
-            sqrt(covariance(Acts::eBoundTheta, Acts::eBoundTheta)));
-        m_err_eQOP_smt.push_back(
-            sqrt(covariance(Acts::eBoundQOverP, Acts::eBoundQOverP)));
-        m_err_eT_smt.push_back(
-            sqrt(covariance(Acts::eBoundTime, Acts::eBoundTime)));
-
-        m_x_smt.push_back(parameter.position(geoctx).x());
-        m_y_smt.push_back(parameter.position(geoctx).y());
-        m_z_smt.push_back(parameter.position(geoctx).z());
-        m_px_smt.push_back(parameter.momentum().x());
-        m_py_smt.push_back(parameter.momentum().y());
-        m_pz_smt.push_back(parameter.momentum().z());
-        m_pT_smt.push_back(parameter.transverseMomentum());
-        m_eta_smt.push_back(eta(parameter.position(geoctx)));
-      }
-      else
-      {
-        /// Push bad values if no smoothed parameter
-        m_eLOC0_smt.push_back(-9999);
-        m_eLOC1_smt.push_back(-9999);
-        m_ePHI_smt.push_back(-9999);
-        m_eTHETA_smt.push_back(-9999);
-        m_eQOP_smt.push_back(-9999);
-        m_eT_smt.push_back(-9999);
-        m_res_eLOC0_smt.push_back(-9999);
-        m_res_eLOC1_smt.push_back(-9999);
-        m_err_eLOC0_smt.push_back(-9999);
-        m_err_eLOC1_smt.push_back(-9999);
-        m_err_ePHI_smt.push_back(-9999);
-        m_err_eTHETA_smt.push_back(-9999);
-        m_err_eQOP_smt.push_back(-9999);
-        m_err_eT_smt.push_back(-9999);
-        m_pull_eLOC0_smt.push_back(-9999);
-        m_pull_eLOC1_smt.push_back(-9999);
-        m_x_smt.push_back(-9999);
-        m_y_smt.push_back(-9999);
-        m_z_smt.push_back(-9999);
-        m_px_smt.push_back(-9999);
-        m_py_smt.push_back(-9999);
-        m_pz_smt.push_back(-9999);
-        m_pT_smt.push_back(-9999);
-        m_eta_smt.push_back(-9999);
-        m_res_x_hit.push_back(-9999);
-        m_res_y_hit.push_back(-9999);
-        m_err_x_hit.push_back(-9999);
-        m_err_y_hit.push_back(-9999);
-        m_pull_x_hit.push_back(-9999);
-        m_pull_y_hit.push_back(-9999);
-        m_dim_hit.push_back(-9999);
-      }
-
-      /// Save whether or not states had various KF steps
-      m_prt.push_back(predicted);
-      m_flt.push_back(filtered);
-      m_smt.push_back(smoothed);
-
-      return true;
-    }   /// Finish lambda function
-    );  /// Finish multi trajectory visitBackwards call
-
-    iTraj++;
-
-  }  // all trajectories
-
-  m_trackTree->Fill();
-
-  clearTrackVariables();
+    cov =  std::optional<AmgSymMatrix(5)>(newcov);
+  }
+  const Amg::Vector3D& pos=actsParameter.position(gctx);
+  double tphi=actsParameter.get<Acts::eBoundPhi>();
+  double ttheta=actsParameter.get<Acts::eBoundTheta>();
+  double tqOverP=actsParameter.get<Acts::eBoundQOverP>()*1_MeV;
+  double p = std::abs(1. / tqOverP);
+  Amg::Vector3D tmom(p * std::cos(tphi) * std::sin(ttheta), p * std::sin(tphi) * std::sin(ttheta), p * std::cos(ttheta));
+  const Trk::CurvilinearParameters * curv = new Trk::CurvilinearParameters(pos,tmom,tqOverP>0, cov);
+  return curv;
 }
 
-void FaserActsKalmanFilterAlg::clearTrackVariables()
-{
-  m_volumeID.clear();
-  m_layerID.clear();
-  m_moduleID.clear();
-  m_lx_hit.clear();
-  m_ly_hit.clear();
-  m_x_hit.clear();
-  m_y_hit.clear();
-  m_z_hit.clear();
-  m_res_x_hit.clear();
-  m_res_y_hit.clear();
-  m_err_x_hit.clear();
-  m_err_y_hit.clear();
-  m_pull_x_hit.clear();
-  m_pull_y_hit.clear();
-  m_dim_hit.clear();
-
-  m_prt.clear();
-  m_eLOC0_prt.clear();
-  m_eLOC1_prt.clear();
-  m_ePHI_prt.clear();
-  m_eTHETA_prt.clear();
-  m_eQOP_prt.clear();
-  m_eT_prt.clear();
-  m_res_eLOC0_prt.clear();
-  m_res_eLOC1_prt.clear();
-  m_err_eLOC0_prt.clear();
-  m_err_eLOC1_prt.clear();
-  m_err_ePHI_prt.clear();
-  m_err_eTHETA_prt.clear();
-  m_err_eQOP_prt.clear();
-  m_err_eT_prt.clear();
-  m_pull_eLOC0_prt.clear();
-  m_pull_eLOC1_prt.clear();
-  m_x_prt.clear();
-  m_y_prt.clear();
-  m_z_prt.clear();
-  m_px_prt.clear();
-  m_py_prt.clear();
-  m_pz_prt.clear();
-  m_eta_prt.clear();
-  m_pT_prt.clear();
-
-  m_flt.clear();
-  m_eLOC0_flt.clear();
-  m_eLOC1_flt.clear();
-  m_ePHI_flt.clear();
-  m_eTHETA_flt.clear();
-  m_eQOP_flt.clear();
-  m_eT_flt.clear();
-  m_res_eLOC0_flt.clear();
-  m_res_eLOC1_flt.clear();
-  m_err_eLOC0_flt.clear();
-  m_err_eLOC1_flt.clear();
-  m_err_ePHI_flt.clear();
-  m_err_eTHETA_flt.clear();
-  m_err_eQOP_flt.clear();
-  m_err_eT_flt.clear();
-  m_pull_eLOC0_flt.clear();
-  m_pull_eLOC1_flt.clear();
-  m_x_flt.clear();
-  m_y_flt.clear();
-  m_z_flt.clear();
-  m_px_flt.clear();
-  m_py_flt.clear();
-  m_pz_flt.clear();
-  m_eta_flt.clear();
-  m_pT_flt.clear();
-  m_chi2.clear();
-
-  m_smt.clear();
-  m_eLOC0_smt.clear();
-  m_eLOC1_smt.clear();
-  m_ePHI_smt.clear();
-  m_eTHETA_smt.clear();
-  m_eQOP_smt.clear();
-  m_eT_smt.clear();
-  m_res_eLOC0_smt.clear();
-  m_res_eLOC1_smt.clear();
-  m_err_eLOC0_smt.clear();
-  m_err_eLOC1_smt.clear();
-  m_err_ePHI_smt.clear();
-  m_err_eTHETA_smt.clear();
-  m_err_eQOP_smt.clear();
-  m_err_eT_smt.clear();
-  m_pull_eLOC0_smt.clear();
-  m_pull_eLOC1_smt.clear();
-  m_x_smt.clear();
-  m_y_smt.clear();
-  m_z_smt.clear();
-  m_px_smt.clear();
-  m_py_smt.clear();
-  m_pz_smt.clear();
-  m_eta_smt.clear();
-  m_pT_smt.clear();
-
-  return;
-}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/MultiTrackFinderTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/MultiTrackFinderTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..f10448452d2a9161dab5207963a514135475efce
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/MultiTrackFinderTool.cxx
@@ -0,0 +1,120 @@
+#include "FaserActsKalmanFilter/MultiTrackFinderTool.h"
+
+#include "Acts/Definitions/TrackParametrization.hpp"
+#include "Acts/EventData/Measurement.hpp"
+#include "Acts/Geometry/GeometryContext.hpp"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+#include "Acts/Geometry/TrackingGeometry.hpp"
+#include "Acts/Surfaces/Surface.hpp"
+#include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include "Identifier/Identifier.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrkTrack/Track.h"
+#include "TrkTrack/TrackStateOnSurface.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+//#include "TrackerPrepRawData/FaserSCT_ClusterCollection.h"
+#include <algorithm>
+#include <vector>
+
+
+MultiTrackFinderTool::MultiTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent)
+    : base_class(type, name, parent) {}
+
+
+StatusCode MultiTrackFinderTool::initialize() {
+      ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+      ATH_CHECK(detStore()->retrieve(m_detManager, "SCT"));
+      ATH_CHECK(m_trackingGeometryTool.retrieve());
+      ATH_CHECK(m_trackCollection.initialize());
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode MultiTrackFinderTool::run() {
+  SG::ReadHandle<TrackCollection> trackCollection {m_trackCollection};
+      ATH_CHECK(trackCollection.isValid());
+
+  using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
+  std::shared_ptr<IdentifierMap> identifierMap = m_trackingGeometryTool->getIdentifierMap();
+
+  std::map<int, std::vector<Tracklet>> tracklets;
+  for (const Trk::Track* track : *trackCollection) {
+    std::vector<Identifier> ids;
+    std::vector<Acts::GeometryIdentifier> geoIds;
+    std::vector<double> positions;
+    std::vector<const Tracker::FaserSCT_Cluster*> clusters {};
+    auto fitParameters = track->trackParameters()->front();
+    const Amg::Vector3D fitPosition = fitParameters->position();
+    const Amg::Vector3D fitMomentum = fitParameters->momentum();
+    ATH_MSG_DEBUG(fitPosition.x() << ", " << fitPosition.y() << fitPosition.z());
+    for (const Trk::TrackStateOnSurface* state : *(track->trackStateOnSurfaces())) {
+      auto clusterOnTrack = dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack*> ( state->measurementOnTrack());
+      if (clusterOnTrack) {
+        const Tracker::FaserSCT_Cluster* cluster = clusterOnTrack->prepRawData();
+        clusters.push_back(cluster);
+        Identifier id = cluster->detectorElement()->identify();
+        ids.push_back(id);
+        Acts::GeometryIdentifier geoId = identifierMap->at(id);
+        geoIds.push_back(geoId);
+        const auto& par = cluster->localPosition();
+        positions.push_back(par.x());
+      }
+    }
+    auto tracklet = Tracklet(ids, geoIds, positions, fitPosition, clusters);
+    int station = m_idHelper->station(ids.front());
+    tracklets[station].push_back(tracklet);
+  }
+
+  // create all combinations of tracklets
+  std::vector<ProtoTrack> protoTracks {};
+  for (const Tracklet& t1 : tracklets[1]) {
+    for (const Tracklet& t2 : tracklets[2]) {
+      for (const Tracklet& t3 : tracklets[3]) {
+        protoTracks.push_back(ProtoTrack(t1, t2, t3));
+      }
+    }
+  }
+
+  // sort proto tracks by their chi2 value and select best two tracks
+  std::sort(protoTracks.begin(), protoTracks.end(), sort_chi2());
+
+  std::vector<std::vector<IndexSourceLink>> sourceLinkVector;
+  std::vector<std::vector<Measurement>> measurementVector;
+  std::vector<std::map<Index, Identifier>> idLinkVector;
+  std::vector<Acts::CurvilinearTrackParameters> paramVector;
+  std::vector<std::vector<const Tracker::FaserSCT_Cluster*>> clusterVector;
+  int n_protoTracks = std::min((int)protoTracks.size(), 2);
+  for (int i = 0; i < n_protoTracks; ++i) {
+    // FIXME check if protoTrack exists
+    ProtoTrack track = protoTracks[i];
+    auto [measurements, sourceLinks, idLinks, clusters] = track.run();
+    auto params = track.initialTrackParameters(m_covLoc0, m_covLoc1, m_covPhi, m_covTheta, m_covQOverP, m_covTime);
+    sourceLinkVector.push_back(sourceLinks);
+    measurementVector.push_back(measurements);
+    idLinkVector.push_back(idLinks);
+    paramVector.push_back(params);
+    clusterVector.push_back(clusters);
+  }
+
+  m_sourceLinks = std::make_shared<std::vector<std::vector<IndexSourceLink>>>(sourceLinkVector);
+  m_measurements = std::make_shared<std::vector<std::vector<Measurement>>>(measurementVector);
+  m_idLinks = std::make_shared<std::vector<std::map<Index, Identifier>>>(idLinkVector);
+  m_initialTrackParameters = std::make_shared<std::vector<Acts::CurvilinearTrackParameters>>(paramVector);
+  m_clusters = std::make_shared<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>>(clusterVector);
+
+  // create initial surface
+  m_initialSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3 {0, 0, 0}, Acts::Vector3{0, 0, 1});
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode MultiTrackFinderTool::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+
+
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/MyAmbiguitySolver.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/MyAmbiguitySolver.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..998269a83cf6228092dd3b31987696f929820e4e
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/MyAmbiguitySolver.cxx
@@ -0,0 +1,3 @@
+#include "FaserActsKalmanFilter/MyAmbiguitySolver.h"
+
+
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/PerformanceWriterTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/PerformanceWriterTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3f99bea98466b59a4ccaa12c898c1f9660ca27bc
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/PerformanceWriterTool.cxx
@@ -0,0 +1,180 @@
+#include "FaserActsKalmanFilter/PerformanceWriterTool.h"
+#include "FaserActsKalmanFilter/TrackClassification.h"
+#include "Acts/EventData/MultiTrajectory.hpp"
+#include "Acts/EventData/MultiTrajectoryHelpers.hpp"
+#include "TFile.h"
+
+
+PerformanceWriterTool::PerformanceWriterTool(
+    const std::string& type, const std::string& name, const IInterface* parent)
+    : AthAlgTool(type, name, parent) {}
+
+
+StatusCode PerformanceWriterTool::initialize() {
+  ATH_CHECK(m_extrapolationTool.retrieve());
+  ATH_CHECK(m_mcEventCollectionKey.initialize());
+  ATH_CHECK(m_simDataCollectionKey.initialize());
+
+  std::string filePath = m_filePath;
+  m_outputFile = TFile::Open(filePath.c_str(), "RECREATE");
+  if (m_outputFile == nullptr) {
+    ATH_MSG_WARNING("Unable to open output file at " << m_filePath);
+    return StatusCode::RECOVERABLE;
+  }
+
+  // initialize the residual and efficiency plots tool
+  m_resPlotTool.book(m_resPlotCache);
+  m_effPlotTool.book(m_effPlotCache);
+  m_summaryPlotTool.book(m_summaryPlotCache);
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode PerformanceWriterTool::finalize() {
+  // fill residual and pull details into additional hists
+  m_resPlotTool.refinement(m_resPlotCache);
+  if (m_outputFile) {
+    m_outputFile->cd();
+    m_resPlotTool.write(m_resPlotCache);
+    m_effPlotTool.write(m_effPlotCache);
+    m_summaryPlotTool.write(m_summaryPlotCache);
+    ATH_MSG_VERBOSE("Wrote performance plots to '" << m_outputFile->GetPath() << "'");
+  }
+
+  m_resPlotTool.clear(m_resPlotCache);
+  m_effPlotTool.clear(m_effPlotCache);
+  m_summaryPlotTool.clear(m_summaryPlotCache);
+  if (m_outputFile) {
+    m_outputFile->Close();
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode PerformanceWriterTool::write(const Acts::GeometryContext& geoContext, const TrajectoriesContainer& trajectories) {
+  const EventContext& ctx = Gaudi::Hive::currentContext();
+
+  SG::ReadHandle<McEventCollection> mcEvents {m_mcEventCollectionKey, ctx};
+  ATH_CHECK(mcEvents.isValid());
+  if (mcEvents->size() != 1) {
+    ATH_MSG_ERROR("There should be exactly one event in the McEventCollection.");
+    return StatusCode::FAILURE;
+  }
+
+  std::map<int, const HepMC::GenParticle*> particles {};
+  for (const HepMC::GenParticle* particle : mcEvents->front()->particle_range()) {
+    particles[particle->barcode()] = particle;
+  }
+
+  SG::ReadHandle<TrackerSimDataCollection> simData {m_simDataCollectionKey, ctx};
+  ATH_CHECK(simData.isValid());
+
+  // Truth particles with corresponding reconstructed tracks
+  std::vector<int> reconParticleIds;
+  reconParticleIds.reserve(particles.size());
+  // For each particle within a track, how many hits did it contribute
+  std::vector<ParticleHitCount> particleHitCounts;
+
+
+  // Loop over all trajectories
+  for (size_t itraj = 0; itraj < trajectories.size(); ++itraj) {
+    const auto& traj = trajectories[itraj];
+
+    if (traj.empty()) {
+      ATH_MSG_WARNING("Empty trajectories object " << itraj);
+      continue;
+    }
+
+    // The trajectory entry indices and the multiTrajectory
+    const auto& trackTips = traj.tips();
+    const auto& mj = traj.multiTrajectory();
+
+    // Check the size of the trajectory entry indices. For track fitting, there
+    // should be at most one trajectory
+    if (trackTips.size() > 1) {
+      ATH_MSG_ERROR("Track fitting should not result in multiple trajectories.");
+      return StatusCode::FAILURE;
+    }
+    // Get the entry index for the single trajectory
+    auto trackTip = trackTips.front();
+
+    // Select reco track with fitted parameters
+    if (not traj.hasTrackParameters(trackTip)) {
+      ATH_MSG_WARNING("No fitted track parameters.");
+      continue;
+    }
+    const auto& fittedParameters = traj.trackParameters(trackTip);
+
+    // Get the majority truth particle for this trajectory
+    identifyContributingParticles(*simData, traj, trackTip, particleHitCounts);
+    if (particleHitCounts.empty()) {
+      ATH_MSG_WARNING("No truth particle associated with this trajectory.");
+      continue;
+    }
+    // Find the truth particle for the majority barcode
+    const auto ip = particles.find(particleHitCounts.front().particleId);
+    if (ip == particles.end()) {
+      ATH_MSG_WARNING("Majority particle not found in the particles collection.");
+      continue;
+    }
+
+    // Record this majority particle ID of this trajectory
+    reconParticleIds.push_back(ip->first);
+    const HepMC::GenParticle* truthParticle = ip->second;
+    std::unique_ptr<const Acts::BoundTrackParameters> truthParameters
+        = extrapolateToReferenceSurface(ctx, truthParticle);
+    // Fill the residual plots
+    if (truthParameters) {
+      m_resPlotTool.fill(m_resPlotCache, geoContext, std::move(truthParameters), fittedParameters);
+    } else {
+      ATH_MSG_WARNING("Can not extrapolate truth parameters to reference surface.");
+    }
+    // Collect the trajectory summary info
+    auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip);
+    // Fill the trajectory summary info
+    m_summaryPlotTool.fill(m_summaryPlotCache, fittedParameters, trajState.nStates, trajState.nMeasurements,
+                           trajState.nOutliers, trajState.nHoles, trajState.nSharedHits);
+  }
+
+  // Fill the efficiency, defined as the ratio between number of tracks with fitted parameter and total truth tracks
+  // (assumes one truth partilce has one truth track)
+  for (const auto& particle : particles) {
+    bool isReconstructed = false;
+    // Find if the particle has been reconstructed
+    auto it = std::find(reconParticleIds.begin(), reconParticleIds.end(), particle.first);
+    if (it != reconParticleIds.end()) {
+      isReconstructed = true;
+    }
+    m_effPlotTool.fill(m_effPlotCache, particle.second, isReconstructed);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+std::unique_ptr<const Acts::BoundTrackParameters> PerformanceWriterTool::extrapolateToReferenceSurface(
+    const EventContext& ctx, const HepMC::GenParticle* particle) const {
+  const HepMC::FourVector &vertex = particle->production_vertex()->position();
+  const HepMC::FourVector &momentum = particle->momentum();
+
+  // The coordinate system of the Acts::PlaneSurface is defined as
+  // T = Z = normal, U = X x T = -Y, V = T x U = x
+  auto startSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3(0, 0, vertex.z()), Acts::Vector3(0, 0, 1));
+  auto targetSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3(0, 0, 0), Acts::Vector3(0, 0, -1));
+  Acts::BoundVector params = Acts::BoundVector::Zero();
+  params[Acts::eBoundLoc0] = -vertex.y();
+  params[Acts::eBoundLoc1] = vertex.x();
+  params[Acts::eBoundPhi] = momentum.phi();
+  params[Acts::eBoundTheta] = momentum.theta();
+  // FIXME get charge of HepMC::GenParticle, the following does not work, e.g. for pions
+  double charge = particle->pdg_id() > 0 ? -1 : 1;
+  double MeV2GeV = 1e-3;
+  params[Acts::eBoundQOverP] = charge / (momentum.rho() * MeV2GeV);
+  params[Acts::eBoundTime] = vertex.t();
+  Acts::BoundTrackParameters startParameters(std::move(startSurface), params, charge);
+  std::unique_ptr<const Acts::BoundTrackParameters> targetParameters =
+      m_extrapolationTool->propagate(ctx, startParameters, *targetSurface);
+  return targetParameters;
+}
\ No newline at end of file
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/PlotHelpers.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/PlotHelpers.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0d50d843ea075ab3d3799dccdc6e2ba46cd74127
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/PlotHelpers.cxx
@@ -0,0 +1,92 @@
+#include "FaserActsKalmanFilter/PlotHelpers.h"
+#include <cassert>
+
+namespace PlotHelpers {
+
+TH1F* bookHisto(const char* histName, const char* histTitle,
+                const Binning& varBinning) {
+  TH1F* hist = new TH1F(histName, histTitle, varBinning.nBins, varBinning.min,
+                        varBinning.max);
+  hist->GetXaxis()->SetTitle(varBinning.title.c_str());
+  hist->GetYaxis()->SetTitle("Entries");
+  hist->Sumw2();
+  return hist;
+}
+
+TH2F* bookHisto(const char* histName, const char* histTitle,
+                const Binning& varXBinning, const Binning& varYBinning) {
+  TH2F* hist = new TH2F(histName, histTitle, varXBinning.nBins, varXBinning.min,
+                        varXBinning.max, varYBinning.nBins, varYBinning.min,
+                        varYBinning.max);
+  hist->GetXaxis()->SetTitle(varXBinning.title.c_str());
+  hist->GetYaxis()->SetTitle(varYBinning.title.c_str());
+  hist->Sumw2();
+  return hist;
+}
+
+void fillHisto(TH1F* hist, float value, float weight) {
+  assert(hist != nullptr);
+  hist->Fill(value, weight);
+}
+
+void fillHisto(TH2F* hist, float xValue, float yValue, float weight) {
+  assert(hist != nullptr);
+  hist->Fill(xValue, yValue, weight);
+}
+
+void anaHisto(TH1D* inputHist, int j, TH1F* meanHist, TH1F* widthHist) {
+  // evaluate mean and width via the Gauss fit
+  assert(inputHist != nullptr);
+  if (inputHist->GetEntries() > 0) {
+    TFitResultPtr r = inputHist->Fit("gaus", "QS0");
+    if (r.Get() and ((r->Status() % 1000) == 0)) {
+      // fill the mean and width into 'j'th bin of the meanHist and widthHist,
+      // respectively
+      meanHist->SetBinContent(j, r->Parameter(1));
+      meanHist->SetBinError(j, r->ParError(1));
+      widthHist->SetBinContent(j, r->Parameter(2));
+      widthHist->SetBinError(j, r->ParError(2));
+    }
+  }
+}
+
+TEfficiency* bookEff(const char* effName, const char* effTitle, const Binning& varBinning) {
+  TEfficiency* efficiency = new TEfficiency(effName, effTitle, varBinning.nBins,
+                                            varBinning.min, varBinning.max);
+  return efficiency;
+}
+
+TEfficiency* bookEff(const char* effName, const char* effTitle,
+                     const Binning& varXBinning, const Binning& varYBinning) {
+  TEfficiency* efficiency = new TEfficiency(
+      effName, effTitle, varXBinning.nBins, varXBinning.min, varXBinning.max,
+      varYBinning.nBins, varYBinning.min, varYBinning.max);
+  return efficiency;
+}
+
+void fillEff(TEfficiency* efficiency, float value, bool status) {
+  assert(efficiency != nullptr);
+  efficiency->Fill(status, value);
+}
+
+void fillEff(TEfficiency* efficiency, float xValue, float yValue, bool status) {
+  assert(efficiency != nullptr);
+  efficiency->Fill(status, xValue, yValue);
+}
+
+TProfile* bookProf(const char* profName, const char* profTitle,
+                   const Binning& varXBinning, const Binning& varYBinning) {
+  TProfile* prof =
+      new TProfile(profName, profTitle, varXBinning.nBins, varXBinning.min,
+                   varXBinning.max, varYBinning.min, varYBinning.max);
+  prof->GetXaxis()->SetTitle(varXBinning.title.c_str());
+  prof->GetYaxis()->SetTitle(varYBinning.title.c_str());
+  return prof;
+}
+
+void fillProf(TProfile* profile, float xValue, float yValue, float weight) {
+  assert(profile != nullptr);
+  profile->Fill(xValue, yValue, weight);
+}
+
+}  // namespace PlotHelpers
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/ProtoTrackWriterTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/ProtoTrackWriterTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..5ec0dfc1a1d430208b10c14a7c368ac89ac08ff3
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/ProtoTrackWriterTool.cxx
@@ -0,0 +1,108 @@
+#include "FaserActsKalmanFilter/ProtoTrackWriterTool.h"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+#include "Identifier/Identifier.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include <TFile.h>
+#include <TTree.h>
+
+
+ProtoTrackWriterTool::ProtoTrackWriterTool(
+    const std::string& type, const std::string& name, const IInterface* parent)
+    : AthAlgTool(type, name, parent) {}
+
+
+StatusCode ProtoTrackWriterTool::initialize() {
+
+  ATH_CHECK(m_trackFinderTool.retrieve());
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+
+  std::string filePath = m_filePath;
+  m_file = TFile::Open(filePath.c_str(), "RECREATE");
+  if (m_file == nullptr) {
+    ATH_MSG_ERROR("Unable to open output file at " << m_filePath);
+    return StatusCode::FAILURE;
+  }
+  m_file->cd();
+
+  m_params = new TTree("parameters", "parameters");
+  m_params->Branch("run_number", &m_run_number, "run_number/I");
+  m_params->Branch("event_number", &m_event_number, "event_number/I");
+  m_params->Branch("x", &m_x, "x/D");
+  m_params->Branch("y", &m_y, "y/D");
+  m_params->Branch("z", &m_z, "z/D");
+  m_params->Branch("px", &m_px, "px/D");
+  m_params->Branch("py", &m_py, "py/D");
+  m_params->Branch("pz", &m_pz, "pz/D");
+
+  m_meas = new TTree("measurements", "measurements");
+  m_meas->Branch("run_number", &m_run_number, "run_number/I");
+  m_meas->Branch("event_number", &m_event_number, "event_number/I");
+  m_meas->Branch("station", &m_station, "station/I");
+  m_meas->Branch("layer", &m_layer, "layer/I");
+  m_meas->Branch("phi", &m_phi, "phi/I");
+  m_meas->Branch("eta", &m_eta, "eta/I");
+  m_meas->Branch("side", &m_side, "side/I");
+  m_meas->Branch("meas_eLOC0", &m_meas_eLOC0, "meas_eLOC0/D");
+  m_meas->Branch("meas_eLOC1", &m_meas_eLOC1, "meas_eLOC1/D");
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ProtoTrackWriterTool::finalize() {
+  m_file->cd();
+  m_params->Write();
+  m_meas->Write();
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ProtoTrackWriterTool::write(
+    std::shared_ptr<const Acts::CurvilinearTrackParameters> protoTrackParameters,
+    std::shared_ptr<std::vector<Measurement>> measurements,
+    const Acts::GeometryContext& geoctx) const {
+
+  EventContext ctx = Gaudi::Hive::currentContext();
+  m_run_number = ctx.eventID().run_number();
+  m_event_number = ctx.eventID().event_number();
+
+  using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
+  std::shared_ptr<IdentifierMap> identifierMap = m_trackingGeometryTool->getIdentifierMap();
+  // flip key-value pairs of identifier map
+  std::map<Acts::GeometryIdentifier, Identifier> geoIdentifierMap;
+  for (const std::pair<const Identifier, Acts::GeometryIdentifier>& identifierPair : *identifierMap) {
+    geoIdentifierMap[identifierPair.second] = identifierPair.first;
+  }
+
+  Acts::Vector3 position = protoTrackParameters->position(geoctx);
+  Acts::Vector3 direction = protoTrackParameters->momentum();
+  m_x = position.x();
+  m_y = position.y();
+  m_z = position.z();
+  m_px = direction.x();
+  m_py = direction.y();
+  m_pz = direction.z();
+  m_params->Fill();
+
+  for (const Measurement& variantMeasurement : *measurements) {
+    // auto meas = std::get<Acts::Measurement<IndexSourceLink, Acts::BoundIndices, 2>>(variantMeasurement);
+    std::visit([&](const auto& meas) {
+      Acts::GeometryIdentifier geoId = meas.sourceLink().geometryId();
+      Identifier id = geoIdentifierMap.at(geoId);
+      m_station = m_idHelper->station(id);
+      m_layer = m_idHelper->layer(id);
+      m_phi = m_idHelper->phi_module(id);
+      m_eta = m_idHelper->eta_module(id);
+      m_side = m_idHelper->side(id);
+      auto params = meas.parameters();
+      m_meas_eLOC0 = params[Acts::eBoundLoc0];
+      // write out as many variables as there are
+      // m_meas_eLOC1 = params[Acts::eBoundLoc1];
+      m_meas->Fill();
+    }, variantMeasurement);
+  };
+
+  return StatusCode::SUCCESS;
+}
\ No newline at end of file
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/ResPlotTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/ResPlotTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..11c1483a7762679e42ce41542d1a5b165aaf4f0c
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/ResPlotTool.cxx
@@ -0,0 +1,201 @@
+#include "FaserActsKalmanFilter/ResPlotTool.h"
+#include "Acts/Utilities/Helpers.hpp"
+#include "HepMC/GenVertex.h"
+
+void ResPlotTool::book(ResPlotTool::ResPlotCache& resPlotCache) const {
+  PlotHelpers::Binning bEta = m_varBinning.at("Eta");
+  PlotHelpers::Binning bPt = m_varBinning.at("Pt");
+  PlotHelpers::Binning bPull = m_varBinning.at("Pull");
+  std::cout << "DEBUG: Initialize the histograms for residual and pull plots" << std::endl;
+  for (unsigned int parID = 0; parID < Acts::eBoundSize; parID++) {
+    std::string parName = m_paramNames.at(parID);
+    std::string parResidual = "Residual_" + parName;
+    // Binning for residual is parameter dependent
+    PlotHelpers::Binning bResidual = m_varBinning.at(parResidual);
+
+
+    // residual distributions
+    resPlotCache.res[parName] = PlotHelpers::bookHisto(
+        Form("res_%s", parName.c_str()),
+        Form("Residual of %s", parName.c_str()), bResidual);
+    // residual vs eta scatter plots
+    resPlotCache.res_vs_eta[parName] = PlotHelpers::bookHisto(
+        Form("res_%s_vs_eta", parName.c_str()),
+        Form("Residual of %s vs eta", parName.c_str()), bEta, bResidual);
+    // residual mean in each eta bin
+    resPlotCache.resMean_vs_eta[parName] = PlotHelpers::bookHisto(
+        Form("resmean_%s_vs_eta", parName.c_str()),
+        Form("Residual mean of %s", parName.c_str()), bEta);
+    // residual width in each eta bin
+    resPlotCache.resWidth_vs_eta[parName] = PlotHelpers::bookHisto(
+        Form("reswidth_%s_vs_eta", parName.c_str()),
+        Form("Residual width of %s", parName.c_str()), bEta);
+    // residual vs pT scatter plots
+    resPlotCache.res_vs_pT[parName] = PlotHelpers::bookHisto(
+        Form("res_%s_vs_pT", parName.c_str()),
+        Form("Residual of %s vs pT", parName.c_str()), bPt, bResidual);
+    // residual mean in each pT bin
+    resPlotCache.resMean_vs_pT[parName] = PlotHelpers::bookHisto(
+        Form("resmean_%s_vs_pT", parName.c_str()),
+        Form("Residual mean of %s", parName.c_str()), bPt);
+    // residual width in each pT bin
+    resPlotCache.resWidth_vs_pT[parName] = PlotHelpers::bookHisto(
+        Form("reswidth_%s_vs_pT", parName.c_str()),
+        Form("Residual width of %s", parName.c_str()), bPt);
+
+    // pull distritutions
+    resPlotCache.pull[parName] = PlotHelpers::bookHisto(
+        Form("pull_%s", parName.c_str()),
+        Form("Pull of %s", parName.c_str()), bPull);
+    // pull vs eta scatter plots
+    resPlotCache.pull_vs_eta[parName] = PlotHelpers::bookHisto(
+        Form("pull_%s_vs_eta", parName.c_str()),
+        Form("Pull of %s vs eta", parName.c_str()), bEta, bPull);
+    // pull mean in each eta bin
+    resPlotCache.pullMean_vs_eta[parName] = PlotHelpers::bookHisto(
+        Form("pullmean_%s_vs_eta", parName.c_str()),
+        Form("Pull mean of %s", parName.c_str()), bEta);
+    // pull width in each eta bin
+    resPlotCache.pullWidth_vs_eta[parName] = PlotHelpers::bookHisto(
+        Form("pullwidth_%s_vs_eta", parName.c_str()),
+        Form("Pull width of %s", parName.c_str()), bEta);
+    // pull vs pT scatter plots
+    resPlotCache.pull_vs_pT[parName] = PlotHelpers::bookHisto(
+        Form("pull_%s_vs_pT", parName.c_str()),
+        Form("Pull of %s vs pT", parName.c_str()), bPt, bPull);
+    // pull mean in each pT bin
+    resPlotCache.pullMean_vs_pT[parName] = PlotHelpers::bookHisto(
+        Form("pullmean_%s_vs_pT", parName.c_str()),
+        Form("Pull mean of %s", parName.c_str()), bPt);
+    // pull width in each pT bin
+    resPlotCache.pullWidth_vs_pT[parName] = PlotHelpers::bookHisto(
+        Form("pullwidth_%s_vs_pT", parName.c_str()),
+        Form("Pull width of %s", parName.c_str()), bPt);
+    }
+}
+
+void ResPlotTool::write(const ResPlotCache &resPlotCache) const {
+  for (unsigned int parID = 0; parID < Acts::eBoundSize; parID++) {
+    std::string parName = m_paramNames.at(parID);
+    if (resPlotCache.res.count(parName)) {
+      resPlotCache.res.at(parName)->Write();
+      resPlotCache.res_vs_eta.at(parName)->Write();
+      resPlotCache.resMean_vs_eta.at(parName)->Write();
+      resPlotCache.resWidth_vs_eta.at(parName)->Write();
+      resPlotCache.res_vs_pT.at(parName)->Write();
+      resPlotCache.resMean_vs_pT.at(parName)->Write();
+      resPlotCache.resWidth_vs_pT.at(parName)->Write();
+      resPlotCache.pull.at(parName)->Write();
+      resPlotCache.pull_vs_eta.at(parName)->Write();
+      resPlotCache.pullMean_vs_eta.at(parName)->Write();
+      resPlotCache.pullWidth_vs_eta.at(parName)->Write();
+      resPlotCache.pull_vs_pT.at(parName)->Write();
+      resPlotCache.pullMean_vs_pT.at(parName)->Write();
+      resPlotCache.pullWidth_vs_pT.at(parName)->Write();
+    }
+  }
+}
+
+void ResPlotTool::clear(ResPlotCache &resPlotCache) const {
+  for (unsigned int parID = 0; parID < Acts::eBoundSize; parID++) {
+    std::string parName = m_paramNames.at(parID);
+    if (resPlotCache.res.count(parName)) {
+      delete resPlotCache.res.at(parName);
+      delete resPlotCache.res_vs_eta.at(parName);
+      delete resPlotCache.resMean_vs_eta.at(parName);
+      delete resPlotCache.resWidth_vs_eta.at(parName);
+      delete resPlotCache.res_vs_pT.at(parName);
+      delete resPlotCache.resMean_vs_pT.at(parName);
+      delete resPlotCache.resWidth_vs_pT.at(parName);
+      delete resPlotCache.pull.at(parName);
+      delete resPlotCache.pull_vs_eta.at(parName);
+      delete resPlotCache.pullMean_vs_eta.at(parName);
+      delete resPlotCache.pullWidth_vs_eta.at(parName);
+      delete resPlotCache.pull_vs_pT.at(parName);
+      delete resPlotCache.pullMean_vs_pT.at(parName);
+      delete resPlotCache.pullWidth_vs_pT.at(parName);
+    }
+  }
+}
+
+void ResPlotTool::fill(
+    ResPlotCache& resPlotCache, const Acts::GeometryContext& gctx,
+    std::unique_ptr<const Acts::BoundTrackParameters> truthParameters,
+    const Acts::BoundTrackParameters& fittedParameters) const {
+  using ParametersVector = Acts::BoundTrackParameters::ParametersVector;
+  using Acts::VectorHelpers::eta;
+  using Acts::VectorHelpers::perp;
+  // using Acts::VectorHelpers::phi;
+  // using Acts::VectorHelpers::theta;
+
+  // get the fitted parameter (at perigee surface) and its error
+  auto trackParameter = fittedParameters.parameters();
+
+  // get the truth position and momentum
+  ParametersVector truthParameter = truthParameters->parameters();
+
+  // get the truth eta and pT
+  const auto truthEta = eta(truthParameters->momentum().normalized());
+  const auto truthPt = truthParameters->absoluteMomentum() * perp(truthParameters->momentum().normalized());
+
+  // fill the histograms for residual and pull
+  for (unsigned int parID = 0; parID < Acts::eBoundSize; parID++) {
+    std::string parName = m_paramNames.at(parID);
+    float residual = trackParameter[parID] - truthParameter[parID];
+    PlotHelpers::fillHisto(resPlotCache.res.at(parName), residual);
+    PlotHelpers::fillHisto(resPlotCache.res_vs_eta.at(parName), truthEta, residual);
+    PlotHelpers::fillHisto(resPlotCache.res_vs_pT.at(parName), truthPt, residual);
+
+    if (fittedParameters.covariance().has_value()) {
+      auto covariance = *fittedParameters.covariance();
+      if (covariance(parID, parID) > 0) {
+        float pull = residual / sqrt(covariance(parID, parID));
+        PlotHelpers::fillHisto(resPlotCache.pull[parName], pull);
+        PlotHelpers::fillHisto(resPlotCache.pull_vs_eta.at(parName), truthEta, pull);
+        PlotHelpers::fillHisto(resPlotCache.pull_vs_pT.at(parName), truthPt, pull);
+      } else {
+        std::cout << "WARNING: Fitted track parameter :" << parName << " has negative covariance = " << covariance(parID, parID) << std::endl;
+      }
+    } else {
+      std::cout << "WARNING: Fitted track parameter :" << parName << " has no covariance" << std::endl;
+    }
+  }
+}
+
+
+// get the mean and width of residual/pull in each eta/pT bin and fill them into histograms
+void ResPlotTool::refinement(ResPlotTool::ResPlotCache& resPlotCache) const {
+  PlotHelpers::Binning bEta = m_varBinning.at("Eta");
+  PlotHelpers::Binning bPt = m_varBinning.at("Pt");
+  for (unsigned int parID = 0; parID < Acts::eBoundSize; parID++) {
+    std::string parName = m_paramNames.at(parID);
+    // refine the plots vs eta
+    for (int j = 1; j <= bEta.nBins; j++) {
+      TH1D* temp_res = resPlotCache.res_vs_eta.at(parName)->ProjectionY(
+          Form("%s_projy_bin%d", "Residual_vs_eta_Histo", j), j, j);
+      PlotHelpers::anaHisto(temp_res, j,
+                            resPlotCache.resMean_vs_eta.at(parName),
+                            resPlotCache.resWidth_vs_eta.at(parName));
+
+      TH1D* temp_pull = resPlotCache.pull_vs_eta.at(parName)->ProjectionY(
+          Form("%s_projy_bin%d", "Pull_vs_eta_Histo", j), j, j);
+      PlotHelpers::anaHisto(temp_pull, j,
+                            resPlotCache.pullMean_vs_eta.at(parName),
+                            resPlotCache.pullWidth_vs_eta.at(parName));
+    }
+
+    // refine the plots vs pT
+    for (int j = 1; j <= bPt.nBins; j++) {
+      TH1D* temp_res = resPlotCache.res_vs_pT.at(parName)->ProjectionY(
+          Form("%s_projy_bin%d", "Residual_vs_pT_Histo", j), j, j);
+      PlotHelpers::anaHisto(temp_res, j, resPlotCache.resMean_vs_pT.at(parName),
+                            resPlotCache.resWidth_vs_pT.at(parName));
+
+      TH1D* temp_pull = resPlotCache.pull_vs_pT.at(parName)->ProjectionY(
+          Form("%s_projy_bin%d", "Pull_vs_pT_Histo", j), j, j);
+      PlotHelpers::anaHisto(temp_pull, j,
+                            resPlotCache.pullMean_vs_pT.at(parName),
+                            resPlotCache.pullWidth_vs_pT.at(parName));
+    }
+  }
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/RootTrajectoryStatesWriterTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/RootTrajectoryStatesWriterTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e81f46ff6d2d87c96b9d86fba97982bbf01dc4cb
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/RootTrajectoryStatesWriterTool.cxx
@@ -0,0 +1,656 @@
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "FaserActsKalmanFilter/RootTrajectoryStatesWriterTool.h"
+#include "Acts/EventData/MultiTrajectory.hpp"
+#include "Acts/EventData/MultiTrajectoryHelpers.hpp"
+#include "Acts/EventData/detail/TransformationBoundToFree.hpp"
+#include "Acts/Utilities/Helpers.hpp"
+#include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "GeoPrimitives/CLHEPtoEigenConverter.h"
+#include "TrackerReadoutGeometry/SiDetectorElement.h"
+#include "FaserActsKalmanFilter/TrackClassification.h"
+#include <TFile.h>
+#include <TTree.h>
+
+using Acts::VectorHelpers::eta;
+using Acts::VectorHelpers::perp;
+using Acts::VectorHelpers::phi;
+using Acts::VectorHelpers::theta;
+
+RootTrajectoryStatesWriterTool::RootTrajectoryStatesWriterTool(
+    const std::string& type, const std::string& name, const IInterface* parent)
+    : AthAlgTool(type, name, parent) {}
+
+StatusCode RootTrajectoryStatesWriterTool::initialize() {
+  ATH_CHECK(m_mcEventCollectionKey.initialize());
+  ATH_CHECK(m_simDataCollectionKey.initialize());
+  ATH_CHECK(m_faserSiHitKey.initialize());
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+  ATH_CHECK(detStore()->retrieve(m_detMgr, "SCT"));
+
+  std::string filePath = m_filePath;
+  std::string treeName = m_treeName;
+  m_outputFile = TFile::Open(filePath.c_str(), "RECREATE");
+  if (m_outputFile == nullptr) {
+    ATH_MSG_ERROR("Unable to open output file at " << m_filePath);
+    return StatusCode::FAILURE;
+  }
+  m_outputFile->cd();
+  m_outputTree = new TTree(treeName.c_str(), treeName.c_str());
+  if (m_outputTree == nullptr) {
+    ATH_MSG_ERROR("Unable to create TTree");
+    return StatusCode::FAILURE;
+  }
+
+  m_outputTree = new TTree("tree", "tree");
+
+  m_outputTree->Branch("event_nr", &m_eventNr);
+  m_outputTree->Branch("multiTraj_nr", &m_multiTrajNr);
+  m_outputTree->Branch("subTraj_nr", &m_subTrajNr);
+
+  m_outputTree->Branch("t_x", &m_t_x);
+  m_outputTree->Branch("t_y", &m_t_y);
+  m_outputTree->Branch("t_z", &m_t_z);
+  m_outputTree->Branch("t_dx", &m_t_dx);
+  m_outputTree->Branch("t_dy", &m_t_dy);
+  m_outputTree->Branch("t_dz", &m_t_dz);
+  m_outputTree->Branch("t_eLOC0", &m_t_eLOC0);
+  m_outputTree->Branch("t_eLOC1", &m_t_eLOC1);
+  m_outputTree->Branch("t_ePHI", &m_t_ePHI);
+  m_outputTree->Branch("t_eTHETA", &m_t_eTHETA);
+  m_outputTree->Branch("t_eQOP", &m_t_eQOP);
+  m_outputTree->Branch("t_eT", &m_t_eT);
+
+  m_outputTree->Branch("nStates", &m_nStates);
+  m_outputTree->Branch("nMeasurements", &m_nMeasurements);
+  m_outputTree->Branch("volume_id", &m_volumeID);
+  m_outputTree->Branch("layer_id", &m_layerID);
+  m_outputTree->Branch("module_id", &m_moduleID);
+  m_outputTree->Branch("station", &m_station);
+  m_outputTree->Branch("layer", &m_layer);
+  m_outputTree->Branch("phi_module", &m_phi_module);
+  m_outputTree->Branch("eta_module", &m_eta_module);
+  m_outputTree->Branch("side", &m_side);
+  m_outputTree->Branch("pathLength", &m_pathLength);
+  m_outputTree->Branch("l_x_hit", &m_lx_hit);
+  m_outputTree->Branch("l_y_hit", &m_ly_hit);
+  m_outputTree->Branch("g_x_hit", &m_x_hit);
+  m_outputTree->Branch("g_y_hit", &m_y_hit);
+  m_outputTree->Branch("g_z_hit", &m_z_hit);
+  m_outputTree->Branch("res_x_hit", &m_res_x_hit);
+  m_outputTree->Branch("res_y_hit", &m_res_y_hit);
+  m_outputTree->Branch("err_x_hit", &m_err_x_hit);
+  m_outputTree->Branch("err_y_hit", &m_err_y_hit);
+  m_outputTree->Branch("pull_x_hit", &m_pull_x_hit);
+  m_outputTree->Branch("pull_y_hit", &m_pull_y_hit);
+  m_outputTree->Branch("dim_hit", &m_dim_hit);
+
+  m_outputTree->Branch("nPredicted", &m_nParams[0]);
+  m_outputTree->Branch("predicted", &m_hasParams[0]);
+  m_outputTree->Branch("eLOC0_prt", &m_eLOC0[0]);
+  m_outputTree->Branch("eLOC1_prt", &m_eLOC1[0]);
+  m_outputTree->Branch("ePHI_prt", &m_ePHI[0]);
+  m_outputTree->Branch("eTHETA_prt", &m_eTHETA[0]);
+  m_outputTree->Branch("eQOP_prt", &m_eQOP[0]);
+  m_outputTree->Branch("eT_prt", &m_eT[0]);
+  m_outputTree->Branch("res_eLOC0_prt", &m_res_eLOC0[0]);
+  m_outputTree->Branch("res_eLOC1_prt", &m_res_eLOC1[0]);
+  m_outputTree->Branch("res_ePHI_prt", &m_res_ePHI[0]);
+  m_outputTree->Branch("res_eTHETA_prt", &m_res_eTHETA[0]);
+  m_outputTree->Branch("res_eQOP_prt", &m_res_eQOP[0]);
+  m_outputTree->Branch("res_eT_prt", &m_res_eT[0]);
+  m_outputTree->Branch("err_eLOC0_prt", &m_err_eLOC0[0]);
+  m_outputTree->Branch("err_eLOC1_prt", &m_err_eLOC1[0]);
+  m_outputTree->Branch("err_ePHI_prt", &m_err_ePHI[0]);
+  m_outputTree->Branch("err_eTHETA_prt", &m_err_eTHETA[0]);
+  m_outputTree->Branch("err_eQOP_prt", &m_err_eQOP[0]);
+  m_outputTree->Branch("err_eT_prt", &m_err_eT[0]);
+  m_outputTree->Branch("pull_eLOC0_prt", &m_pull_eLOC0[0]);
+  m_outputTree->Branch("pull_eLOC1_prt", &m_pull_eLOC1[0]);
+  m_outputTree->Branch("pull_ePHI_prt", &m_pull_ePHI[0]);
+  m_outputTree->Branch("pull_eTHETA_prt", &m_pull_eTHETA[0]);
+  m_outputTree->Branch("pull_eQOP_prt", &m_pull_eQOP[0]);
+  m_outputTree->Branch("pull_eT_prt", &m_pull_eT[0]);
+  m_outputTree->Branch("g_x_prt", &m_x[0]);
+  m_outputTree->Branch("g_y_prt", &m_y[0]);
+  m_outputTree->Branch("g_z_prt", &m_z[0]);
+  m_outputTree->Branch("px_prt", &m_px[0]);
+  m_outputTree->Branch("py_prt", &m_py[0]);
+  m_outputTree->Branch("pz_prt", &m_pz[0]);
+  m_outputTree->Branch("eta_prt", &m_eta[0]);
+  m_outputTree->Branch("pT_prt", &m_pT[0]);
+
+  m_outputTree->Branch("nFiltered", &m_nParams[1]);
+  m_outputTree->Branch("filtered", &m_hasParams[1]);
+  m_outputTree->Branch("eLOC0_flt", &m_eLOC0[1]);
+  m_outputTree->Branch("eLOC1_flt", &m_eLOC1[1]);
+  m_outputTree->Branch("ePHI_flt", &m_ePHI[1]);
+  m_outputTree->Branch("eTHETA_flt", &m_eTHETA[1]);
+  m_outputTree->Branch("eQOP_flt", &m_eQOP[1]);
+  m_outputTree->Branch("eT_flt", &m_eT[1]);
+  m_outputTree->Branch("res_eLOC0_flt", &m_res_eLOC0[1]);
+  m_outputTree->Branch("res_eLOC1_flt", &m_res_eLOC1[1]);
+  m_outputTree->Branch("res_ePHI_flt", &m_res_ePHI[1]);
+  m_outputTree->Branch("res_eTHETA_flt", &m_res_eTHETA[1]);
+  m_outputTree->Branch("res_eQOP_flt", &m_res_eQOP[1]);
+  m_outputTree->Branch("res_eT_flt", &m_res_eT[1]);
+  m_outputTree->Branch("err_eLOC0_flt", &m_err_eLOC0[1]);
+  m_outputTree->Branch("err_eLOC1_flt", &m_err_eLOC1[1]);
+  m_outputTree->Branch("err_ePHI_flt", &m_err_ePHI[1]);
+  m_outputTree->Branch("err_eTHETA_flt", &m_err_eTHETA[1]);
+  m_outputTree->Branch("err_eQOP_flt", &m_err_eQOP[1]);
+  m_outputTree->Branch("err_eT_flt", &m_err_eT[1]);
+  m_outputTree->Branch("pull_eLOC0_flt", &m_pull_eLOC0[1]);
+  m_outputTree->Branch("pull_eLOC1_flt", &m_pull_eLOC1[1]);
+  m_outputTree->Branch("pull_ePHI_flt", &m_pull_ePHI[1]);
+  m_outputTree->Branch("pull_eTHETA_flt", &m_pull_eTHETA[1]);
+  m_outputTree->Branch("pull_eQOP_flt", &m_pull_eQOP[1]);
+  m_outputTree->Branch("pull_eT_flt", &m_pull_eT[1]);
+  m_outputTree->Branch("g_x_flt", &m_x[1]);
+  m_outputTree->Branch("g_y_flt", &m_y[1]);
+  m_outputTree->Branch("g_z_flt", &m_z[1]);
+  m_outputTree->Branch("px_flt", &m_px[1]);
+  m_outputTree->Branch("py_flt", &m_py[1]);
+  m_outputTree->Branch("pz_flt", &m_pz[1]);
+  m_outputTree->Branch("eta_flt", &m_eta[1]);
+  m_outputTree->Branch("pT_flt", &m_pT[1]);
+
+  m_outputTree->Branch("nSmoothed", &m_nParams[2]);
+  m_outputTree->Branch("smoothed", &m_hasParams[2]);
+  m_outputTree->Branch("eLOC0_smt", &m_eLOC0[2]);
+  m_outputTree->Branch("eLOC1_smt", &m_eLOC1[2]);
+  m_outputTree->Branch("ePHI_smt", &m_ePHI[2]);
+  m_outputTree->Branch("eTHETA_smt", &m_eTHETA[2]);
+  m_outputTree->Branch("eQOP_smt", &m_eQOP[2]);
+  m_outputTree->Branch("eT_smt", &m_eT[2]);
+  m_outputTree->Branch("res_eLOC0_smt", &m_res_eLOC0[2]);
+  m_outputTree->Branch("res_eLOC1_smt", &m_res_eLOC1[2]);
+  m_outputTree->Branch("res_ePHI_smt", &m_res_ePHI[2]);
+  m_outputTree->Branch("res_eTHETA_smt", &m_res_eTHETA[2]);
+  m_outputTree->Branch("res_eQOP_smt", &m_res_eQOP[2]);
+  m_outputTree->Branch("res_eT_smt", &m_res_eT[2]);
+  m_outputTree->Branch("err_eLOC0_smt", &m_err_eLOC0[2]);
+  m_outputTree->Branch("err_eLOC1_smt", &m_err_eLOC1[2]);
+  m_outputTree->Branch("err_ePHI_smt", &m_err_ePHI[2]);
+  m_outputTree->Branch("err_eTHETA_smt", &m_err_eTHETA[2]);
+  m_outputTree->Branch("err_eQOP_smt", &m_err_eQOP[2]);
+  m_outputTree->Branch("err_eT_smt", &m_err_eT[2]);
+  m_outputTree->Branch("pull_eLOC0_smt", &m_pull_eLOC0[2]);
+  m_outputTree->Branch("pull_eLOC1_smt", &m_pull_eLOC1[2]);
+  m_outputTree->Branch("pull_ePHI_smt", &m_pull_ePHI[2]);
+  m_outputTree->Branch("pull_eTHETA_smt", &m_pull_eTHETA[2]);
+  m_outputTree->Branch("pull_eQOP_smt", &m_pull_eQOP[2]);
+  m_outputTree->Branch("pull_eT_smt", &m_pull_eT[2]);
+  m_outputTree->Branch("g_x_smt", &m_x[2]);
+  m_outputTree->Branch("g_y_smt", &m_y[2]);
+  m_outputTree->Branch("g_z_smt", &m_z[2]);
+  m_outputTree->Branch("px_smt", &m_px[2]);
+  m_outputTree->Branch("py_smt", &m_py[2]);
+  m_outputTree->Branch("pz_smt", &m_pz[2]);
+  m_outputTree->Branch("eta_smt", &m_eta[2]);
+  m_outputTree->Branch("pT_smt", &m_pT[2]);
+
+  m_outputTree->Branch("chi2", &m_chi2);
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode RootTrajectoryStatesWriterTool::finalize() {
+  m_outputFile->cd();
+  m_outputTree->Write();
+  m_outputFile->Close();
+  return StatusCode::SUCCESS;
+}
+
+StatusCode RootTrajectoryStatesWriterTool::write(const Acts::GeometryContext& gctx, const TrajectoriesContainer& trajectories) const {
+
+  if (m_outputFile == nullptr)
+    return StatusCode::SUCCESS;
+
+  const EventContext& ctx = Gaudi::Hive::currentContext();
+
+  SG::ReadHandle<McEventCollection> mcEvents {m_mcEventCollectionKey, ctx};
+  ATH_CHECK(mcEvents.isValid());
+  if (mcEvents->size() != 1) {
+    ATH_MSG_ERROR("There should be exactly one event in the McEventCollection.");
+    return StatusCode::FAILURE;
+  }
+  ATH_MSG_VERBOSE("Found " << mcEvents->front()->particles_size() << " particles.");
+  std::map<int, const HepMC::GenParticle*> particles {};
+  for (const HepMC::GenParticle* particle : mcEvents->front()->particle_range()) {
+    particles[particle->barcode()] = particle;
+  }
+
+  SG::ReadHandle<TrackerSimDataCollection> simData {m_simDataCollectionKey, ctx};
+  ATH_CHECK(simData.isValid());
+
+  SG::ReadHandle<FaserSiHitCollection> siHitCollection {m_faserSiHitKey, ctx};
+  ATH_CHECK(siHitCollection.isValid());
+  std::map<std::pair<int, Identifier>, const FaserSiHit*> siHitMap;
+  for (const FaserSiHit& hit : *siHitCollection) {
+    int barcode = hit.trackNumber();
+    Identifier id = m_idHelper->wafer_id(hit.getStation(), hit.getPlane(), hit.getRow(), hit.getModule(), hit.getSensor());
+    siHitMap[std::make_pair(barcode, id)] = &hit;
+  }
+
+  // using HitParticlesMap = IndexMultimap<ActsFatras::Barcode>;
+  // using HitSimHitsMap = IndexMultimap<Index>;
+
+  // Read additional input collections
+  // const auto& particles = ctx.eventStore.get<SimParticleContainer>(m_cfg.inputParticles);
+  // const auto& simHits = ctx.eventStore.get<SimHitContainer>(m_cfg.inputSimHits);
+  // const auto& hitParticlesMap = ctx.eventStore.get<HitParticlesMap>(m_cfg.inputMeasurementParticlesMap);
+  // const auto& hitSimHitsMap = ctx.eventStore.get<HitSimHitsMap>(m_cfg.inputMeasurementSimHitsMap);
+
+  // For each particle within a track, how many hits did it contribute
+  std::vector<ParticleHitCount> particleHitCounts;
+
+  // Get the event number
+  m_eventNr = ctx.eventID().event_number();
+
+  // Loop over the trajectories
+  for (size_t itraj = 0; itraj < trajectories.size(); ++itraj) {
+    const auto& traj = trajectories[itraj];
+
+    if (traj.empty()) {
+      ATH_MSG_WARNING("Empty trajectories object " << itraj);
+      continue;
+    }
+
+    // The trajectory index
+    m_multiTrajNr = itraj;
+
+    // The trajectory entry indices and the multiTrajectory
+    const auto& mj = traj.multiTrajectory();
+    const auto& trackTips = traj.tips();
+
+    // Loop over the entry indices for the subtrajectories
+    for (unsigned int isubtraj = 0; isubtraj < trackTips.size(); ++isubtraj) {
+      // The subtrajectory index
+      m_subTrajNr = isubtraj;
+      // The entry index for this subtrajectory
+      const auto& trackTip = trackTips[isubtraj];
+      // Collect the trajectory summary info
+      auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip);
+      m_nMeasurements = trajState.nMeasurements;
+      m_nStates = trajState.nStates;
+
+      // Get the majority truth particle to this track
+      int barcode;
+      int truthQ = 1.;
+      float truthMomentum = 1;
+      identifyContributingParticles(*simData, traj, trackTip, particleHitCounts);
+      if (not particleHitCounts.empty()) {
+        // Get the barcode of the majority truth particle
+        barcode = particleHitCounts.front().particleId;
+        // Find the truth particle via the barcode
+        auto ip = particles.find(barcode);
+        if (ip != particles.end()) {
+          const auto& particle = ip->second;
+          ATH_MSG_DEBUG("Find the truth particle with barcode = " << barcode);
+          // Get the truth particle charge
+          // FIXME find better way to access charge of simulated particle, this does not work for
+          // pions which have a positive pdg code (211) and positive charge
+          truthQ = particle->pdg_id() > 0 ? -1 : 1;
+          truthMomentum = particle->momentum().rho() * m_MeV2GeV;
+        } else {
+          ATH_MSG_WARNING("Truth particle with barcode = " << barcode << " not found!");
+        }
+      }
+      float truthQOP = truthQ / truthMomentum;
+
+      // Get the trackStates on the trajectory
+      m_nParams = {0, 0, 0};
+      using ConstTrackStateProxy =
+          Acts::detail_lt::TrackStateProxy<IndexSourceLink, 6, true>;
+      mj.visitBackwards(trackTip, [&](const ConstTrackStateProxy& state) {
+        // we only fill the track states with non-outlier measurement
+        auto typeFlags = state.typeFlags();
+        if (not typeFlags.test(Acts::TrackStateFlag::MeasurementFlag)) {
+          return true;
+        }
+
+        const auto& surface = state.referenceSurface();
+
+        // get the truth hits corresponding to this trackState
+        Identifier id = state.uncalibrated().hit()->identify();
+        Identifier waferId = m_idHelper->wafer_id(id);
+        if (siHitMap.count(std::make_pair(barcode, waferId)) == 0) {
+          ATH_MSG_WARNING("no FaserSiHit for hit with id " << id << " from particle " << barcode);
+          return true;
+        }
+        const FaserSiHit* siHit = siHitMap.find(std::make_pair(barcode, waferId))->second;
+        HepGeom::Point3D localStartPos = siHit->localStartPosition();
+        HepGeom::Point3D localEndPos = siHit->localEndPosition();
+        HepGeom::Point3D<double> localPos = 0.5 * (localEndPos + localStartPos);
+        auto truthLocal = Acts::Vector2(localPos.y(), localPos.z());
+        const TrackerDD::SiDetectorElement* element = m_detMgr->getDetectorElement(id);
+        const HepGeom::Point3D<double> globalStartPosition =
+            Amg::EigenTransformToCLHEP(element->transformHit()) * localStartPos;
+        const HepGeom::Point3D<double> globalEndPosition =
+            Amg::EigenTransformToCLHEP(element->transformHit()) * localEndPos;
+        auto globalPosition = 0.5 * (globalStartPosition + globalEndPosition);
+        auto globalDirection = globalEndPosition - globalStartPosition;
+        auto truthUnitDir = Acts::Vector3(globalDirection.x(), globalDirection.y(), globalDirection.z()).normalized();
+        auto truthPos = Acts::Vector3(globalPosition.x() , globalPosition.y(), globalPosition.z());
+        // FIXME get truthQOP for each state
+
+        // fill the truth hit info
+        m_t_x.push_back(truthPos[Acts::ePos0]);
+        m_t_y.push_back(truthPos[Acts::ePos1]);
+        m_t_z.push_back(truthPos[Acts::ePos2]);
+        m_t_dx.push_back(truthUnitDir[Acts::eMom0]);
+        m_t_dy.push_back(truthUnitDir[Acts::eMom1]);
+        m_t_dz.push_back(truthUnitDir[Acts::eMom2]);
+
+        // get the truth track parameter at this track State
+        float truthLOC0 = truthLocal[Acts::ePos0];
+        float truthLOC1 = truthLocal[Acts::ePos1];
+        float truthTIME = siHit->meanTime();
+        float truthPHI = phi(truthUnitDir);
+        float truthTHETA = theta(truthUnitDir);
+
+        // fill the truth track parameter at this track State
+        m_t_eLOC0.push_back(truthLOC0);
+        m_t_eLOC1.push_back(truthLOC1);
+        m_t_ePHI.push_back(truthPHI);
+        m_t_eTHETA.push_back(truthTHETA);
+        m_t_eQOP.push_back(truthQOP);
+        m_t_eT.push_back(truthTIME);
+
+        // get the geometry ID
+        auto geoID = surface.geometryId();
+        m_volumeID.push_back(geoID.volume());
+        m_layerID.push_back(geoID.layer());
+        m_moduleID.push_back(geoID.sensitive());
+
+        // get wafer information
+        m_station.push_back(m_idHelper->station(id));
+        m_layer.push_back(m_idHelper->layer(id));
+        m_phi_module.push_back(m_idHelper->phi_module(id));
+        m_eta_module.push_back(m_idHelper->eta_module(id));
+        m_side.push_back(m_idHelper->side(id));
+
+        // get the path length
+        m_pathLength.push_back(state.pathLength());
+
+        // expand the local measurements into the full bound space
+        Acts::BoundVector meas = state.projector().transpose() * state.calibrated();
+        // extract local and global position
+        Acts::Vector2 local(meas[Acts::eBoundLoc0], meas[Acts::eBoundLoc1]);
+        Acts::Vector3 mom(1, 1, 1);
+        Acts::Vector3 global = surface.localToGlobal(gctx, local, mom);
+
+        // fill the measurement info
+        m_lx_hit.push_back(local[Acts::ePos0]);
+        m_ly_hit.push_back(local[Acts::ePos1]);
+        m_x_hit.push_back(global[Acts::ePos0]);
+        m_y_hit.push_back(global[Acts::ePos1]);
+        m_z_hit.push_back(global[Acts::ePos2]);
+
+        // status of the fitted track parameters
+        std::array<bool, 3> hasParams = {false, false, false};
+        // optional fitted track parameters
+        std::optional<std::pair<Acts::BoundVector, Acts::BoundMatrix>>
+            trackParamsOpt = std::nullopt;
+        // lambda to get the fitted track parameters
+        auto getTrackParams = [&](unsigned int ipar) {
+          if (ipar == 0 && state.hasPredicted()) {
+            hasParams[0] = true;
+            m_nParams[0]++;
+            trackParamsOpt =
+                std::make_pair(state.predicted(), state.predictedCovariance());
+          } else if (ipar == 1 && state.hasFiltered()) {
+            hasParams[1] = true;
+            m_nParams[1]++;
+            trackParamsOpt =
+                std::make_pair(state.filtered(), state.filteredCovariance());
+          } else if (ipar == 2 && state.hasSmoothed()) {
+            hasParams[2] = true;
+            m_nParams[2]++;
+            trackParamsOpt =
+                std::make_pair(state.smoothed(), state.smoothedCovariance());
+          }
+        };
+
+        // fill the fitted track parameters
+        for (unsigned int ipar = 0; ipar < 3; ++ipar) {
+          // get the fitted track parameters
+          getTrackParams(ipar);
+          if (trackParamsOpt) {
+            const auto& [parameters, covariance] = *trackParamsOpt;
+            if (ipar == 0) {
+              //
+              // local hit residual info
+              auto H = state.effectiveProjector();
+              auto resCov = state.effectiveCalibratedCovariance() +
+                            H * covariance * H.transpose();
+              auto res = state.effectiveCalibrated() - H * parameters;
+              m_res_x_hit.push_back(res[Acts::eBoundLoc0]);
+//              m_res_y_hit.push_back(res[Acts::eBoundLoc1]);
+              m_res_y_hit.push_back(-99.);
+              m_err_x_hit.push_back(
+                  sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
+//              m_err_y_hit.push_back(
+//                  sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+              m_err_x_hit.push_back(-99.);
+              m_res_y_hit.push_back(-99.);
+              m_pull_x_hit.push_back(
+                  res[Acts::eBoundLoc0] /
+                  sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
+//              m_pull_y_hit.push_back(
+//                  res[Acts::eBoundLoc1] /
+//                  sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+              m_pull_y_hit.push_back(-99);
+              m_dim_hit.push_back(state.calibratedSize());
+            }
+
+            // track parameters
+            m_eLOC0[ipar].push_back(parameters[Acts::eBoundLoc0]);
+            m_eLOC1[ipar].push_back(parameters[Acts::eBoundLoc1]);
+            m_ePHI[ipar].push_back(parameters[Acts::eBoundPhi]);
+            m_eTHETA[ipar].push_back(parameters[Acts::eBoundTheta]);
+            m_eQOP[ipar].push_back(parameters[Acts::eBoundQOverP]);
+            m_eT[ipar].push_back(parameters[Acts::eBoundTime]);
+
+            // track parameters residual
+            m_res_eLOC0[ipar].push_back(parameters[Acts::eBoundLoc0] -
+                                        truthLOC0);
+            m_res_eLOC1[ipar].push_back(parameters[Acts::eBoundLoc1] -
+                                        truthLOC1);
+            float resPhi = Acts::detail::difference_periodic<float>(
+                parameters[Acts::eBoundPhi], truthPHI,
+                static_cast<float>(2 * M_PI));
+            m_res_ePHI[ipar].push_back(resPhi);
+            m_res_eTHETA[ipar].push_back(parameters[Acts::eBoundTheta] -
+                                         truthTHETA);
+            m_res_eQOP[ipar].push_back(parameters[Acts::eBoundQOverP] -
+                                       truthQOP);
+            m_res_eT[ipar].push_back(parameters[Acts::eBoundTime] - truthTIME);
+
+            // track parameters error
+            m_err_eLOC0[ipar].push_back(
+                sqrt(covariance(Acts::eBoundLoc0, Acts::eBoundLoc0)));
+            m_err_eLOC1[ipar].push_back(
+                sqrt(covariance(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+            m_err_ePHI[ipar].push_back(
+                sqrt(covariance(Acts::eBoundPhi, Acts::eBoundPhi)));
+            m_err_eTHETA[ipar].push_back(
+                sqrt(covariance(Acts::eBoundTheta, Acts::eBoundTheta)));
+            m_err_eQOP[ipar].push_back(
+                sqrt(covariance(Acts::eBoundQOverP, Acts::eBoundQOverP)));
+            m_err_eT[ipar].push_back(
+                sqrt(covariance(Acts::eBoundTime, Acts::eBoundTime)));
+
+            // track parameters pull
+            m_pull_eLOC0[ipar].push_back(
+                (parameters[Acts::eBoundLoc0] - truthLOC0) /
+                sqrt(covariance(Acts::eBoundLoc0, Acts::eBoundLoc0)));
+            m_pull_eLOC1[ipar].push_back(
+                (parameters[Acts::eBoundLoc1] - truthLOC1) /
+                sqrt(covariance(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+            m_pull_ePHI[ipar].push_back(
+                resPhi / sqrt(covariance(Acts::eBoundPhi, Acts::eBoundPhi)));
+            m_pull_eTHETA[ipar].push_back(
+                (parameters[Acts::eBoundTheta] - truthTHETA) /
+                sqrt(covariance(Acts::eBoundTheta, Acts::eBoundTheta)));
+            m_pull_eQOP[ipar].push_back(
+                (parameters[Acts::eBoundQOverP] - truthQOP) /
+                sqrt(covariance(Acts::eBoundQOverP, Acts::eBoundQOverP)));
+            m_pull_eT[ipar].push_back(
+                (parameters[Acts::eBoundTime] - truthTIME) /
+                sqrt(covariance(Acts::eBoundTime, Acts::eBoundTime)));
+
+            // further track parameter info
+            Acts::FreeVector freeParams =
+                Acts::detail::transformBoundToFreeParameters(surface, gctx,
+                                                             parameters);
+            m_x[ipar].push_back(freeParams[Acts::eFreePos0]);
+            m_y[ipar].push_back(freeParams[Acts::eFreePos1]);
+            m_z[ipar].push_back(freeParams[Acts::eFreePos2]);
+            auto p = std::abs(1 / freeParams[Acts::eFreeQOverP]);
+            m_px[ipar].push_back(p * freeParams[Acts::eFreeDir0]);
+            m_py[ipar].push_back(p * freeParams[Acts::eFreeDir1]);
+            m_pz[ipar].push_back(p * freeParams[Acts::eFreeDir2]);
+            m_pT[ipar].push_back(p * std::hypot(freeParams[Acts::eFreeDir0],
+                                                freeParams[Acts::eFreeDir1]));
+            m_eta[ipar].push_back(Acts::VectorHelpers::eta(
+                freeParams.segment<3>(Acts::eFreeDir0)));
+          } else {
+            if (ipar == 0) {
+              // push default values if no track parameters
+              m_res_x_hit.push_back(-99.);
+              m_res_y_hit.push_back(-99.);
+              m_err_x_hit.push_back(-99.);
+              m_err_y_hit.push_back(-99.);
+              m_pull_x_hit.push_back(-99.);
+              m_pull_y_hit.push_back(-99.);
+              m_dim_hit.push_back(-99.);
+            }
+            // push default values if no track parameters
+            m_eLOC0[ipar].push_back(-99.);
+            m_eLOC1[ipar].push_back(-99.);
+            m_ePHI[ipar].push_back(-99.);
+            m_eTHETA[ipar].push_back(-99.);
+            m_eQOP[ipar].push_back(-99.);
+            m_eT[ipar].push_back(-99.);
+            m_res_eLOC0[ipar].push_back(-99.);
+            m_res_eLOC1[ipar].push_back(-99.);
+            m_res_ePHI[ipar].push_back(-99.);
+            m_res_eTHETA[ipar].push_back(-99.);
+            m_res_eQOP[ipar].push_back(-99.);
+            m_res_eT[ipar].push_back(-99.);
+            m_err_eLOC0[ipar].push_back(-99);
+            m_err_eLOC1[ipar].push_back(-99);
+            m_err_ePHI[ipar].push_back(-99);
+            m_err_eTHETA[ipar].push_back(-99);
+            m_err_eQOP[ipar].push_back(-99);
+            m_err_eT[ipar].push_back(-99);
+            m_pull_eLOC0[ipar].push_back(-99.);
+            m_pull_eLOC1[ipar].push_back(-99.);
+            m_pull_ePHI[ipar].push_back(-99.);
+            m_pull_eTHETA[ipar].push_back(-99.);
+            m_pull_eQOP[ipar].push_back(-99.);
+            m_pull_eT[ipar].push_back(-99.);
+            m_x[ipar].push_back(-99.);
+            m_y[ipar].push_back(-99.);
+            m_z[ipar].push_back(-99.);
+            m_px[ipar].push_back(-99.);
+            m_py[ipar].push_back(-99.);
+            m_pz[ipar].push_back(-99.);
+            m_pT[ipar].push_back(-99.);
+            m_eta[ipar].push_back(-99.);
+          }
+          // fill the track parameters status
+          m_hasParams[ipar].push_back(hasParams[ipar]);
+        }
+
+        // fill the chi2
+        m_chi2.push_back(state.chi2());
+
+        return true;
+      });  // all states
+
+      // fill the variables for one track to tree
+      m_outputTree->Fill();
+
+      // now reset
+      m_t_x.clear();
+      m_t_y.clear();
+      m_t_z.clear();
+      m_t_dx.clear();
+      m_t_dy.clear();
+      m_t_dz.clear();
+      m_t_eLOC0.clear();
+      m_t_eLOC1.clear();
+      m_t_ePHI.clear();
+      m_t_eTHETA.clear();
+      m_t_eQOP.clear();
+      m_t_eT.clear();
+
+      m_volumeID.clear();
+      m_layerID.clear();
+      m_moduleID.clear();
+      m_station.clear();
+      m_layer.clear();
+      m_phi_module.clear();
+      m_eta_module.clear();
+      m_side.clear();
+      m_pathLength.clear();
+      m_lx_hit.clear();
+      m_ly_hit.clear();
+      m_x_hit.clear();
+      m_y_hit.clear();
+      m_z_hit.clear();
+      m_res_x_hit.clear();
+      m_res_y_hit.clear();
+      m_err_x_hit.clear();
+      m_err_y_hit.clear();
+      m_pull_x_hit.clear();
+      m_pull_y_hit.clear();
+      m_dim_hit.clear();
+
+      for (unsigned int ipar = 0; ipar < 3; ++ipar) {
+        m_hasParams[ipar].clear();
+        m_eLOC0[ipar].clear();
+        m_eLOC1[ipar].clear();
+        m_ePHI[ipar].clear();
+        m_eTHETA[ipar].clear();
+        m_eQOP[ipar].clear();
+        m_eT[ipar].clear();
+        m_res_eLOC0[ipar].clear();
+        m_res_eLOC1[ipar].clear();
+        m_res_ePHI[ipar].clear();
+        m_res_eTHETA[ipar].clear();
+        m_res_eQOP[ipar].clear();
+        m_res_eT[ipar].clear();
+        m_err_eLOC0[ipar].clear();
+        m_err_eLOC1[ipar].clear();
+        m_err_ePHI[ipar].clear();
+        m_err_eTHETA[ipar].clear();
+        m_err_eQOP[ipar].clear();
+        m_err_eT[ipar].clear();
+        m_pull_eLOC0[ipar].clear();
+        m_pull_eLOC1[ipar].clear();
+        m_pull_ePHI[ipar].clear();
+        m_pull_eTHETA[ipar].clear();
+        m_pull_eQOP[ipar].clear();
+        m_pull_eT[ipar].clear();
+        m_x[ipar].clear();
+        m_y[ipar].clear();
+        m_z[ipar].clear();
+        m_px[ipar].clear();
+        m_py[ipar].clear();
+        m_pz[ipar].clear();
+        m_eta[ipar].clear();
+        m_pT[ipar].clear();
+      }
+
+      m_chi2.clear();
+    }  // all subtrajectories
+  }    // all trajectories
+
+  return StatusCode::SUCCESS;
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/RootTrajectorySummaryWriterTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/RootTrajectorySummaryWriterTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e3251a8098f29661776da8350fff4b26585cdc79
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/RootTrajectorySummaryWriterTool.cxx
@@ -0,0 +1,456 @@
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "FaserActsKalmanFilter/RootTrajectorySummaryWriterTool.h"
+#include "Acts/EventData/MultiTrajectory.hpp"
+#include "Acts/EventData/MultiTrajectoryHelpers.hpp"
+#include "Acts/EventData/detail/TransformationBoundToFree.hpp"
+#include "Acts/Utilities/Helpers.hpp"
+#include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "GeoPrimitives/CLHEPtoEigenConverter.h"
+#include "TrackerReadoutGeometry/SiDetectorElement.h"
+#include "FaserActsKalmanFilter/TrackClassification.h"
+#include "HepMC/GenParticle.h"
+#include "HepMC/GenVertex.h"
+#include <TFile.h>
+#include <TTree.h>
+
+/// NaN values for TTree variables
+constexpr double NaNdouble = std::numeric_limits<double>::quiet_NaN();
+constexpr float NaNfloat = std::numeric_limits<float>::quiet_NaN();
+constexpr float NaNint = std::numeric_limits<int>::quiet_NaN();
+
+using ConstTrackStateProxy = Acts::detail_lt::TrackStateProxy<IndexSourceLink, 6, true>;
+
+using Acts::VectorHelpers::eta;
+using Acts::VectorHelpers::perp;
+using Acts::VectorHelpers::phi;
+using Acts::VectorHelpers::theta;
+
+RootTrajectorySummaryWriterTool::RootTrajectorySummaryWriterTool(
+    const std::string& type, const std::string& name, const IInterface* parent)
+    : AthAlgTool(type, name, parent) {}
+
+
+StatusCode RootTrajectorySummaryWriterTool::initialize() {
+  ATH_CHECK(m_simDataCollectionKey.initialize());
+  ATH_CHECK(m_mcEventCollectionKey.initialize());
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+
+  std::string filePath = m_filePath;
+  std::string treeName = m_treeName;
+  m_outputFile = TFile::Open(filePath.c_str(), "RECREATE");
+  if (m_outputFile == nullptr) {
+    ATH_MSG_WARNING("Unable to open output file at " << m_filePath);
+    return StatusCode::RECOVERABLE;
+  }
+  m_outputFile->cd();
+  m_outputTree = new TTree(treeName.c_str(), treeName.c_str());
+  if (m_outputTree == nullptr) {
+    ATH_MSG_ERROR("Unable to create TTree");
+    return StatusCode::FAILURE;
+  }
+
+  m_outputTree = new TTree("tree", "tree");
+
+  m_outputTree->Branch("event_nr", &m_eventNr);
+  m_outputTree->Branch("multiTraj_nr", &m_multiTrajNr);
+  m_outputTree->Branch("subTraj_nr", &m_subTrajNr);
+
+  m_outputTree->Branch("nStates", &m_nStates);
+  m_outputTree->Branch("nMeasurements", &m_nMeasurements);
+  m_outputTree->Branch("nOutliers", &m_nOutliers);
+  m_outputTree->Branch("nHoles", &m_nHoles);
+  m_outputTree->Branch("nSharedHits", &m_nSharedHits);
+  m_outputTree->Branch("chi2Sum", &m_chi2Sum);
+  m_outputTree->Branch("NDF", &m_NDF);
+  m_outputTree->Branch("measurementChi2", &m_measurementChi2);
+  m_outputTree->Branch("outlierChi2", &m_outlierChi2);
+  m_outputTree->Branch("measurementVolume", &m_measurementVolume);
+  m_outputTree->Branch("measurementLayer", &m_measurementLayer);
+  m_outputTree->Branch("outlierVolume", &m_outlierVolume);
+  m_outputTree->Branch("outlierLayer", &m_outlierLayer);
+
+  m_outputTree->Branch("nMajorityHits", &m_nMajorityHits);
+  m_outputTree->Branch("majorityParticleId", &m_majorityParticleId);
+  m_outputTree->Branch("t_charge", &m_t_charge);
+  m_outputTree->Branch("t_time", &m_t_time);
+  m_outputTree->Branch("t_vx", &m_t_vx);
+  m_outputTree->Branch("t_vy", &m_t_vy);
+  m_outputTree->Branch("t_vz", &m_t_vz);
+  m_outputTree->Branch("t_px", &m_t_px);
+  m_outputTree->Branch("t_py", &m_t_py);
+  m_outputTree->Branch("t_pz", &m_t_pz);
+  m_outputTree->Branch("t_theta", &m_t_theta);
+  m_outputTree->Branch("t_phi", &m_t_phi);
+  m_outputTree->Branch("t_eta", &m_t_eta);
+  m_outputTree->Branch("t_p", &m_t_p);
+  m_outputTree->Branch("t_pT", &m_t_pT);
+
+  m_outputTree->Branch("hasFittedParams", &m_hasFittedParams);
+  m_outputTree->Branch("eLOC0_fit", &m_eLOC0_fit);
+  m_outputTree->Branch("eLOC1_fit", &m_eLOC1_fit);
+  m_outputTree->Branch("ePHI_fit", &m_ePHI_fit);
+  m_outputTree->Branch("eTHETA_fit", &m_eTHETA_fit);
+  m_outputTree->Branch("eQOP_fit", &m_eQOP_fit);
+  m_outputTree->Branch("eT_fit", &m_eT_fit);
+  m_outputTree->Branch("err_eLOC0_fit", &m_err_eLOC0_fit);
+  m_outputTree->Branch("err_eLOC1_fit", &m_err_eLOC1_fit);
+  m_outputTree->Branch("err_ePHI_fit", &m_err_ePHI_fit);
+  m_outputTree->Branch("err_eTHETA_fit", &m_err_eTHETA_fit);
+  m_outputTree->Branch("err_eQOP_fit", &m_err_eQOP_fit);
+  m_outputTree->Branch("err_eT_fit", &m_err_eT_fit);
+  m_outputTree->Branch("res_eLOC0_fit", &m_res_eLOC0_fit);
+  m_outputTree->Branch("res_eLOC1_fit", &m_res_eLOC1_fit);
+  m_outputTree->Branch("res_ePHI_fit", &m_res_ePHI_fit);
+  m_outputTree->Branch("res_eTHETA_fit", &m_res_eTHETA_fit);
+  m_outputTree->Branch("res_eQOP_fit", &m_res_eQOP_fit);
+  m_outputTree->Branch("res_eT_fit", &m_res_eT_fit);
+  m_outputTree->Branch("pull_eLOC0_fit", &m_pull_eLOC0_fit);
+  m_outputTree->Branch("pull_eLOC1_fit", &m_pull_eLOC1_fit);
+  m_outputTree->Branch("pull_ePHI_fit", &m_pull_ePHI_fit);
+  m_outputTree->Branch("pull_eTHETA_fit", &m_pull_eTHETA_fit);
+  m_outputTree->Branch("pull_eQOP_fit", &m_pull_eQOP_fit);
+  m_outputTree->Branch("pull_eT_fit", &m_pull_eT_fit);
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode RootTrajectorySummaryWriterTool::finalize() {
+  m_outputFile->cd();
+  m_outputTree->Write();
+  m_outputFile->Close();
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode RootTrajectorySummaryWriterTool::write(
+    const Acts::GeometryContext& geoContext, const TrajectoriesContainer& trajectories) const {
+  EventContext ctx = Gaudi::Hive::currentContext();
+
+  SG::ReadHandle<TrackerSimDataCollection> simData {m_simDataCollectionKey, ctx};
+  ATH_CHECK(simData.isValid());
+
+  SG::ReadHandle<McEventCollection> mcEvents {m_mcEventCollectionKey, ctx};
+  ATH_CHECK(mcEvents.isValid());
+  if (mcEvents->size() != 1) {
+    ATH_MSG_ERROR("There should be exactly one event in the McEventCollection.");
+    return StatusCode::FAILURE;
+  }
+
+  std::map<int, const HepMC::GenParticle*> particles {};
+  for (const HepMC::GenParticle* particle : mcEvents->front()->particle_range()) {
+    particles[particle->barcode()] = particle;
+  }
+
+  // For each particle within a track, how many hits did it contribute
+  std::vector<ParticleHitCount> particleHitCounts;
+
+  // Get the event number
+  m_eventNr = ctx.eventID().event_number();
+
+  // Loop over the trajectories
+  for (size_t itraj = 0; itraj < trajectories.size(); ++itraj) {
+    const auto& traj = trajectories[itraj];
+
+    if (traj.empty()) {
+      ATH_MSG_WARNING("Empty trajectories object " << itraj);
+      continue;
+    }
+
+    // The trajectory index
+    m_multiTrajNr.push_back(itraj);
+
+    // The trajectory entry indices and the multiTrajectory
+    const auto& mj = traj.multiTrajectory();
+    const auto& trackTips = traj.tips();
+
+    // Loop over the entry indices for the subtrajectories
+    for (unsigned int isubtraj = 0; isubtraj < trackTips.size(); ++isubtraj) {
+      // The subtrajectory index
+      m_subTrajNr.push_back(isubtraj);
+      // The entry index for this subtrajectory
+      const auto& trackTip = trackTips[isubtraj];
+
+      // Collect the trajectory summary info
+      auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState(mj, trackTip);
+      m_nStates.push_back(trajState.nStates);
+      m_nMeasurements.push_back(trajState.nMeasurements);
+      m_nOutliers.push_back(trajState.nOutliers);
+      m_nHoles.push_back(trajState.nHoles);
+      m_nSharedHits.push_back(trajState.nSharedHits);
+      m_chi2Sum.push_back(trajState.chi2Sum);
+      m_NDF.push_back(trajState.NDF);
+      m_measurementChi2.push_back(trajState.measurementChi2);
+      m_outlierChi2.push_back(trajState.measurementChi2);
+      // They are stored as double (as the vector of vector of int is not known to ROOT)
+      m_measurementVolume.emplace_back(trajState.measurementVolume.begin(), trajState.measurementVolume.end());
+      m_measurementLayer.emplace_back(trajState.measurementLayer.begin(), trajState.measurementLayer.end());
+      m_outlierVolume.emplace_back(trajState.outlierVolume.begin(), trajState.outlierVolume.end());
+      m_outlierLayer.emplace_back(trajState.outlierLayer.begin(), trajState.outlierLayer.end());
+
+      // Initialize the truth particle info
+      uint64_t majorityParticleId = NaNint;
+      unsigned int nMajorityHits = NaNint;
+      float t_charge = NaNint;
+      float t_time = NaNfloat;
+      float t_vlx =  NaNfloat;
+      float t_vly = NaNfloat;
+      float t_vx = NaNfloat;
+      float t_vy = NaNfloat;
+      float t_vz = NaNfloat;
+      float t_px = NaNfloat;
+      float t_py = NaNfloat;
+      float t_pz = NaNfloat;
+      float t_theta = NaNfloat;
+      float t_phi = NaNfloat;
+      float t_eta = NaNfloat;
+      float t_p = NaNfloat;
+      float t_pT = NaNfloat;
+
+      // Get the perigee surface
+      Acts::Surface* pSurface = nullptr;
+      if (traj.hasTrackParameters(trackTip)) {
+        const auto& boundParam = traj.trackParameters(trackTip);
+        pSurface = const_cast<Acts::Surface*>(&boundParam.referenceSurface());
+      }
+
+      // Get the majority truth particle to this track
+      ATH_MSG_VERBOSE("get majority truth particle");
+      identifyContributingParticles(*simData, traj, trackTip, particleHitCounts);
+      for (const auto& particle : particleHitCounts) {
+        ATH_MSG_VERBOSE(particle.particleId << ": " << particle.hitCount << " hits");
+      }
+
+      bool foundMajorityParticle = false;
+      // Get the truth particle info
+      if (not particleHitCounts.empty()) {
+        // Get the barcode of the majority truth particle
+        majorityParticleId = particleHitCounts.front().particleId;
+        nMajorityHits = particleHitCounts.front().hitCount;
+
+        // Find the truth particle via the barcode
+        auto ip = particles.find(majorityParticleId);
+        if (ip != particles.end()) {
+          foundMajorityParticle = true;
+
+          const HepMC::GenParticle* particle = ip->second;
+          ATH_MSG_DEBUG("Find the truth particle with barcode = " << majorityParticleId);
+
+          // extrapolate parameters from vertex to reference surface at origin.
+          std::unique_ptr<const Acts::BoundTrackParameters> truthParameters
+              = extrapolateToReferenceSurface(ctx, particle);
+          if (!truthParameters) {
+            continue;
+          }
+          // Get the truth particle info at vertex
+          const HepMC::GenVertex* vertex = particle->production_vertex();
+          t_p = truthParameters->momentum().mag();
+          t_charge = truthParameters->charge();
+          t_time = truthParameters->time();
+          t_vx = truthParameters->position(geoContext).x();
+          t_vy = truthParameters->position(geoContext).y();
+          t_vz = truthParameters->position(geoContext).z();
+          t_px = truthParameters->momentum().x();
+          t_py = truthParameters->momentum().y();
+          t_pz = truthParameters->momentum().z();
+          t_theta = theta(truthParameters->momentum().normalized());
+          t_phi = phi(truthParameters->momentum().normalized());
+          t_eta = eta(truthParameters->momentum().normalized());
+          t_pT = t_p * perp(truthParameters->momentum().normalized());
+
+          auto unitDirection = Acts::Vector3(t_px, t_py, t_pz).normalized();
+          auto positon = Acts::Vector3 (t_vx, t_vy, t_vz);
+
+          if (pSurface) {
+            // get the truth perigee parameter
+            auto lpResult = pSurface->globalToLocal(geoContext, positon, unitDirection);
+            if (lpResult.ok()) {
+              t_vlx = lpResult.value()[Acts::BoundIndices::eBoundLoc0];
+              t_vly = lpResult.value()[Acts::BoundIndices::eBoundLoc1];
+            } else {
+              ATH_MSG_ERROR("Global to local transformation did not succeed.");
+            }
+          }
+        } else {
+          ATH_MSG_WARNING("Truth particle with barcode = " << majorityParticleId << " not found in the input collection!");
+        }
+      }
+      if (not foundMajorityParticle) {
+        ATH_MSG_WARNING("Truth particle for mj " << itraj << " subtraj " << isubtraj << " not found!");
+      }
+
+      // Push the corresponding truth particle info for the track.
+      // Always push back even if majority particle not found
+      m_majorityParticleId.push_back(majorityParticleId);
+      m_nMajorityHits.push_back(nMajorityHits);
+      m_t_charge.push_back(t_charge);
+      m_t_time.push_back(t_time);
+      m_t_vx.push_back(t_vx);
+      m_t_vy.push_back(t_vy);
+      m_t_vz.push_back(t_vz);
+      m_t_px.push_back(t_px);
+      m_t_py.push_back(t_py);
+      m_t_pz.push_back(t_pz);
+      m_t_theta.push_back(t_theta);
+      m_t_phi.push_back(t_phi);
+      m_t_eta.push_back(t_eta);
+      m_t_p.push_back(t_p);
+      m_t_pT.push_back(t_pT);
+
+      // Initialize the fitted track parameters info
+      std::array<float, Acts::eBoundSize> param = {NaNfloat, NaNfloat, NaNfloat, NaNfloat, NaNfloat, NaNfloat};
+      std::array<float, Acts::eBoundSize> res = {NaNfloat, NaNfloat, NaNfloat, NaNfloat, NaNfloat, NaNfloat};
+      std::array<float, Acts::eBoundSize> error = {NaNfloat, NaNfloat, NaNfloat, NaNfloat, NaNfloat, NaNfloat};
+      std::array<float, Acts::eBoundSize> pull = {NaNfloat, NaNfloat, NaNfloat, NaNfloat, NaNfloat, NaNfloat};
+      bool hasFittedParams = false;
+      if (traj.hasTrackParameters(trackTip)) {
+        hasFittedParams = true;
+        const auto& boundParam = traj.trackParameters(trackTip);
+        const auto& parameter = boundParam.parameters();
+        for (unsigned int i = 0; i < Acts::eBoundSize; ++i) {
+          param[i] = parameter[i];
+        }
+
+        res = {param[Acts::eBoundLoc0] - t_vlx,
+               param[Acts::eBoundLoc1] - t_vly,
+               param[Acts::eBoundPhi] - t_phi,
+               param[Acts::eBoundTheta] - t_theta,
+               param[Acts::eBoundQOverP] - t_charge / t_p,
+               param[Acts::eBoundTime] - t_time};
+
+        if (boundParam.covariance().has_value()) {
+          const auto& covariance = *boundParam.covariance();
+          for (unsigned int i = 0; i < Acts::eBoundSize; ++i) {
+            error[i] = std::sqrt(covariance(i, i));
+            pull[i] = res[i] / error[i];
+          }
+        }
+      }
+
+      // Push the fitted track parameters.
+      // Always push back even if no fitted track parameters
+      m_eLOC0_fit.push_back(param[Acts::eBoundLoc0]);
+      m_eLOC1_fit.push_back(param[Acts::eBoundLoc1]);
+      m_ePHI_fit.push_back(param[Acts::eBoundPhi]);
+      m_eTHETA_fit.push_back(param[Acts::eBoundTheta]);
+      m_eQOP_fit.push_back(param[Acts::eBoundQOverP]);
+      m_eT_fit.push_back(param[Acts::eBoundTime]);
+
+      m_res_eLOC0_fit.push_back(res[Acts::eBoundLoc0]);
+      m_res_eLOC1_fit.push_back(res[Acts::eBoundLoc1]);
+      m_res_ePHI_fit.push_back(res[Acts::eBoundPhi]);
+      m_res_eTHETA_fit.push_back(res[Acts::eBoundTheta]);
+      m_res_eQOP_fit.push_back(res[Acts::eBoundQOverP]);
+      m_res_eT_fit.push_back(res[Acts::eBoundTime]);
+
+      m_err_eLOC0_fit.push_back(error[Acts::eBoundLoc0]);
+      m_err_eLOC1_fit.push_back(error[Acts::eBoundLoc1]);
+      m_err_ePHI_fit.push_back(error[Acts::eBoundPhi]);
+      m_err_eTHETA_fit.push_back(error[Acts::eBoundTheta]);
+      m_err_eQOP_fit.push_back(error[Acts::eBoundQOverP]);
+      m_err_eT_fit.push_back(error[Acts::eBoundTime]);
+
+      m_pull_eLOC0_fit.push_back(pull[Acts::eBoundLoc0]);
+      m_pull_eLOC1_fit.push_back(pull[Acts::eBoundLoc1]);
+      m_pull_ePHI_fit.push_back(pull[Acts::eBoundPhi]);
+      m_pull_eTHETA_fit.push_back(pull[Acts::eBoundTheta]);
+      m_pull_eQOP_fit.push_back(pull[Acts::eBoundQOverP]);
+      m_pull_eT_fit.push_back(pull[Acts::eBoundTime]);
+
+      m_hasFittedParams.push_back(hasFittedParams);
+    }  // all subtrajectories
+  }    // all trajectories
+
+
+  // fill the variables
+  m_outputTree->Fill();
+
+  m_multiTrajNr.clear();
+  m_subTrajNr.clear();
+  m_nStates.clear();
+  m_nMeasurements.clear();
+  m_nOutliers.clear();
+  m_nHoles.clear();
+  m_nSharedHits.clear();
+  m_chi2Sum.clear();
+  m_NDF.clear();
+  m_measurementChi2.clear();
+  m_outlierChi2.clear();
+  m_measurementVolume.clear();
+  m_measurementLayer.clear();
+  m_outlierVolume.clear();
+  m_outlierLayer.clear();
+
+  m_nMajorityHits.clear();
+  m_majorityParticleId.clear();
+  m_t_charge.clear();
+  m_t_time.clear();
+  m_t_vx.clear();
+  m_t_vy.clear();
+  m_t_vz.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_hasFittedParams.clear();
+  m_eLOC0_fit.clear();
+  m_eLOC1_fit.clear();
+  m_ePHI_fit.clear();
+  m_eTHETA_fit.clear();
+  m_eQOP_fit.clear();
+  m_eT_fit.clear();
+  m_err_eLOC0_fit.clear();
+  m_err_eLOC1_fit.clear();
+  m_err_ePHI_fit.clear();
+  m_err_eTHETA_fit.clear();
+  m_err_eQOP_fit.clear();
+  m_err_eT_fit.clear();
+  m_res_eLOC0_fit.clear();
+  m_res_eLOC1_fit.clear();
+  m_res_ePHI_fit.clear();
+  m_res_eTHETA_fit.clear();
+  m_res_eQOP_fit.clear();
+  m_res_eT_fit.clear();
+  m_pull_eLOC0_fit.clear();
+  m_pull_eLOC1_fit.clear();
+  m_pull_ePHI_fit.clear();
+  m_pull_eTHETA_fit.clear();
+  m_pull_eQOP_fit.clear();
+  m_pull_eT_fit.clear();
+
+  return StatusCode::SUCCESS;
+}
+
+
+std::unique_ptr<const Acts::BoundTrackParameters> RootTrajectorySummaryWriterTool::extrapolateToReferenceSurface(
+    const EventContext& ctx, const HepMC::GenParticle* particle) const {
+  const HepMC::FourVector &vertex = particle->production_vertex()->position();
+  const HepMC::FourVector &momentum = particle->momentum();
+
+  // The coordinate system of the Acts::PlaneSurface is defined as
+  // T = Z = normal, U = X x T = -Y, V = T x U = x
+  auto startSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3(0, 0, vertex.z()), Acts::Vector3(0, 0, 1));
+  auto targetSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3(0, 0, 0), Acts::Vector3(0, 0, -1));
+  Acts::BoundVector params = Acts::BoundVector::Zero();
+  params[Acts::eBoundLoc0] = -vertex.y();
+  params[Acts::eBoundLoc1] = vertex.x();
+  params[Acts::eBoundPhi] = momentum.phi();
+  params[Acts::eBoundTheta] = momentum.theta();
+  // FIXME get charge of HepMC::GenParticle, the following does not work, e.g. for pions
+  double charge = particle->pdg_id() > 0 ? -1 : 1;
+  double MeV2GeV = 1e-3;
+  params[Acts::eBoundQOverP] = charge / (momentum.rho() * MeV2GeV);
+  params[Acts::eBoundTime] = vertex.t();
+  Acts::BoundTrackParameters startParameters(std::move(startSurface), params, charge);
+  std::unique_ptr<const Acts::BoundTrackParameters> targetParameters =
+      m_extrapolationTool->propagate(ctx, startParameters, *targetSurface);
+  return targetParameters;
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/SegmentFitClusterTrackFinderTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/SegmentFitClusterTrackFinderTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b12215257197a931266e251620f2c63ecef7b10a
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/SegmentFitClusterTrackFinderTool.cxx
@@ -0,0 +1,151 @@
+#include "FaserActsKalmanFilter/SegmentFitClusterTrackFinderTool.h"
+
+#include "Acts/Definitions/TrackParametrization.hpp"
+#include "Acts/EventData/Measurement.hpp"
+#include "Acts/Geometry/GeometryContext.hpp"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+#include "Acts/Geometry/TrackingGeometry.hpp"
+#include "Acts/Surfaces/Surface.hpp"
+#include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include "Identifier/Identifier.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrkTrack/Track.h"
+#include "TrkTrack/TrackStateOnSurface.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+//#include "TrackerPrepRawData/FaserSCT_ClusterCollection.h"
+#include <random>
+
+
+SegmentFitClusterTrackFinderTool::SegmentFitClusterTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent)
+    : base_class(type, name, parent) {}
+
+
+StatusCode SegmentFitClusterTrackFinderTool::initialize() {
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+  ATH_CHECK(detStore()->retrieve(m_detManager, "SCT"));
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+  ATH_CHECK(m_trackCollection.initialize());
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode SegmentFitClusterTrackFinderTool::run() {
+  SG::ReadHandle<TrackCollection> trackCollection {m_trackCollection};
+  ATH_CHECK(trackCollection.isValid());
+
+  using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
+  std::shared_ptr<IdentifierMap> identifierMap = m_trackingGeometryTool->getIdentifierMap();
+
+  // create source links and measurements
+  const int kSize = 1;
+  using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, kSize>;
+  std::array<Acts::BoundIndices, kSize> Indices = {Acts::eBoundLoc0};
+  std::vector<IndexSourceLink> sourceLinks;
+  std::vector<Measurement> measurements;
+  std::map<int, Amg::Vector3D> positions;
+  std::map<int, Amg::Vector3D> directions;
+  std::map<Index, Identifier> identifierLinkMap;
+  std::vector<const Tracker::FaserSCT_Cluster*> clusters {};
+  // TODO only take best track (most hits, smallest chi2)
+  // for now I assume that there is only one track in each station
+  for (const Trk::Track* track : *trackCollection) {
+    auto fitParameters = track->trackParameters()->front();
+    const Amg::Vector3D fitPosition = fitParameters->position();
+    const Amg::Vector3D fitMomentum = fitParameters->momentum();
+    Identifier id;
+    for (const Trk::TrackStateOnSurface* state : *(track->trackStateOnSurfaces())) {
+      auto clusterOnTrack = dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack*> (
+          state->measurementOnTrack());
+      if (clusterOnTrack) {
+        const Tracker::FaserSCT_Cluster* cluster = clusterOnTrack->prepRawData();
+        id = cluster->detectorElement()->identify();
+        // create source link
+        Acts::GeometryIdentifier geoId = identifierMap->at(id);
+        ATH_MSG_DEBUG(geoId);
+        ATH_MSG_DEBUG(m_idHelper->station(id) << "/" <<
+          m_idHelper->layer(id) << "/" <<
+          m_idHelper->phi_module(id) << "/" <<
+          m_idHelper->eta_module(id) << "/" <<
+          m_idHelper->side(id)
+        );
+        identifierLinkMap[measurements.size()] = id;
+        IndexSourceLink sourceLink(geoId, measurements.size());
+        // create measurement
+        const auto& par = cluster->localPosition();
+        const auto& cov = cluster->localCovariance();
+        Eigen::Matrix<double, 1, 1> myCov {m_sigmaCluster * m_sigmaCluster,};
+        ThisMeasurement meas(sourceLink, Indices, par.head(1), myCov);
+//        ThisMeasurement meas(sourceLink, Indices, par.head(1), cov.block<1,1>(0,0));
+        sourceLinks.push_back(sourceLink);
+        measurements.emplace_back(std::move(meas));
+        clusters.push_back(cluster);
+      }
+    }
+    int station = m_idHelper->station(id);
+    positions[station] = fitPosition;
+    directions[station] = fitMomentum;
+  }
+  std::vector<std::vector<IndexSourceLink>> sourceLinkVector {sourceLinks};
+  m_sourceLinks = std::make_shared<std::vector<std::vector<IndexSourceLink>>>(sourceLinkVector);
+  std::vector<std::vector<Measurement>> measurementVector {measurements};
+  m_measurements = std::make_shared<std::vector<std::vector<Measurement>>>(measurementVector);
+  std::vector<std::map<Index, Identifier>> idLinkVector {identifierLinkMap};
+  m_idLinks = std::make_shared<std::vector<std::map<Index, Identifier>>>(idLinkVector);
+  std::vector<std::vector<const Tracker::FaserSCT_Cluster*>> clusterVector {clusters};
+  m_clusters = std::make_shared<std::vector<std::vector<const Tracker::FaserSCT_Cluster*>>>(clusterVector);
+
+  // TODO how should we handle events without a track in each station?
+  if (positions.size() != 3) {
+    return StatusCode::RECOVERABLE;
+  }
+
+  // create initial surface
+  m_initialSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3 {0, 0, 0}, Acts::Vector3{0, 0, 1});
+
+  // create initial parameters
+  Acts::Vector3 pos = positions[1] - positions[1].z()/directions[1].z() * directions[1];
+  Acts::Vector4 pos4 {pos.x(), pos.y(), pos.z(), 0};
+  Acts::Vector3 dir = positions[2] - positions[1];
+  auto [abs_momentum, charge] = momentum(positions);
+//  Acts::Vector3 pos = positions[1] - positions[1].z()/directions[1].z() * directions[1];
+//  Acts::Vector4 pos4 {pos.x(), pos.y(), pos.z(), 0};
+//  Acts::Vector3 dir = {directions[1].x(), directions[1].y(), directions[1].z()};
+//  double abs_momentum = 1000;
+//  double charge = 1;
+
+  Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Zero();
+  cov(Acts::eBoundLoc0, Acts::eBoundLoc0) = m_covLoc0;
+  cov(Acts::eBoundLoc1, Acts::eBoundLoc1) = m_covLoc1;
+  cov(Acts::eBoundPhi, Acts::eBoundPhi) = m_covPhi;
+  cov(Acts::eBoundTheta, Acts::eBoundTheta) = m_covTheta;
+  cov(Acts::eBoundQOverP, Acts::eBoundQOverP) = m_covQOverP;
+  cov(Acts::eBoundTime, Acts::eBoundTime) = m_covTime;
+
+  auto initialParameters = Acts::CurvilinearTrackParameters(
+      pos4, dir, abs_momentum, charge, cov);
+  std::vector<Acts::CurvilinearTrackParameters> paramVector {initialParameters};
+  m_initialTrackParameters = std::make_shared<std::vector<Acts::CurvilinearTrackParameters>>(paramVector);
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode SegmentFitClusterTrackFinderTool::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+
+std::pair<double, double> SegmentFitClusterTrackFinderTool::momentum(const std::map<int, Amg::Vector3D>& pos, double B) {
+  Acts::Vector3 vec_l = pos.at(3) - pos.at(1);
+  double abs_l = std::sqrt(vec_l.y() * vec_l.y() + vec_l.z() * vec_l.z());
+  double t = (pos.at(2).z() - pos.at(1).z()) / (pos.at(3).z() - pos.at(1).z());
+  Acts::Vector3 vec_m = pos.at(1) + t * vec_l;
+  Acts::Vector3 vec_s = pos.at(2) - vec_m;
+  double abs_s = std::sqrt(vec_s.y() * vec_s.y() + vec_s.z() * vec_s.z());
+  double p_yz = 0.3 * abs_l * abs_l * B / (8 * abs_s * 1000);
+  double charge = vec_s.y() < 0 ? 1 : -1;
+  return std::make_pair(p_yz, charge);
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/SegmentFitTrackFinderTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/SegmentFitTrackFinderTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0aa77c3e8b3f18a75b55aafb730e216e388c10f5
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/SegmentFitTrackFinderTool.cxx
@@ -0,0 +1,152 @@
+#include "FaserActsKalmanFilter/SegmentFitTrackFinderTool.h"
+
+#include "Acts/Definitions/TrackParametrization.hpp"
+#include "Acts/EventData/Measurement.hpp"
+#include "Acts/Geometry/GeometryContext.hpp"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+#include "Acts/Geometry/TrackingGeometry.hpp"
+#include "Acts/Surfaces/Surface.hpp"
+#include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include "Identifier/Identifier.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrkTrack/Track.h"
+#include "TrkTrack/TrackStateOnSurface.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "TrackerPrepRawData/FaserSCT_ClusterCollection.h"
+#include <random>
+
+
+SegmentFitTrackFinderTool::SegmentFitTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent)
+    : base_class(type, name, parent) {}
+
+
+StatusCode SegmentFitTrackFinderTool::initialize() {
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+  ATH_CHECK(detStore()->retrieve(m_detManager, "SCT"));
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+  ATH_CHECK(m_trackCollection.initialize());
+  ATH_CHECK(m_spacePointContainerKey.initialize());
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode SegmentFitTrackFinderTool::run() {
+  SG::ReadHandle<TrackCollection> trackCollection {m_trackCollection};
+  ATH_CHECK(trackCollection.isValid());
+
+  SG::ReadHandle<FaserSCT_SpacePointContainer> spacePointContainer {m_spacePointContainerKey};
+  ATH_CHECK(spacePointContainer.isValid());
+
+  using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
+  std::shared_ptr<IdentifierMap> identifierMap = m_trackingGeometryTool->getIdentifierMap();
+
+  // create vector of cluster identifiers which have been used by any track fit
+  std::vector<Identifier> trackIds;
+  std::map<int, Amg::Vector3D> positions;
+  std::map<int, Amg::Vector3D> directions;
+  // TODO only take best track (most hits, smallest chi2)
+  // for now I assume that there is only one track in each station
+  for (const Trk::Track* track : *trackCollection) {
+    auto fitParameters = track->trackParameters()->front();
+    const Amg::Vector3D fitPosition = fitParameters->position();
+    const Amg::Vector3D fitMomentum = fitParameters->momentum();
+    Identifier id;
+    for (const Trk::TrackStateOnSurface* state : *(track->trackStateOnSurfaces())) {
+      auto clusterOnTrack = dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack*> (
+          state->measurementOnTrack());
+      if (clusterOnTrack) {
+        id = clusterOnTrack->identify();
+        trackIds.push_back(id);
+      }
+    }
+    int station = m_idHelper->station(id);
+    positions[station] = fitPosition;
+    directions[station] = fitMomentum;
+  }
+
+  // create source links and measurements
+  const int kSize = 2;
+  using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, kSize>;
+  std::array<Acts::BoundIndices, kSize> Indices = {Acts::eBoundLoc0, Acts::eBoundLoc1};
+  std::vector<IndexSourceLink> sourceLinks;
+  std::vector<Measurement> measurements;
+  std::vector<Tracker::FaserSCT_SpacePoint> spacePoints {};
+  for (const FaserSCT_SpacePointCollection* spacePointCollection : *spacePointContainer) {
+    for (const Tracker::FaserSCT_SpacePoint *spacePoint: *spacePointCollection) {
+      Identifier id1 = spacePoint->cluster1()->identify();
+      Identifier id2 = spacePoint->cluster2()->identify();
+      // check if both clusters have been used to reconstruct track
+      if (std::find(trackIds.begin(), trackIds.end(), id1) != trackIds.end() &&
+          std::find(trackIds.begin(), trackIds.end(), id2) != trackIds.end()) {
+        spacePoints.push_back(*spacePoint);
+        Identifier id = spacePoint->associatedSurface().associatedDetectorElementIdentifier();
+        // create source link
+        Acts::GeometryIdentifier geoId = identifierMap->at(id);
+        IndexSourceLink sourceLink(geoId, measurements.size());
+        // create measurement
+        const auto& par = spacePoint->localParameters();
+        const auto& cov = spacePoint->localCovariance();
+        ThisMeasurement meas(sourceLink, Indices, par, cov);
+        sourceLinks.push_back(sourceLink);
+        measurements.emplace_back(std::move(meas));
+      }
+    }
+  }
+  m_sourceLinks = std::make_shared<std::vector<IndexSourceLink>>(sourceLinks);
+  m_measurements = std::make_shared<std::vector<Measurement>>(measurements);
+  m_spacePoints = std::make_shared<std::vector<Tracker::FaserSCT_SpacePoint>>(spacePoints);
+
+  // TODO how should we handle events without a track in each station?
+  if (positions.size() != 3) {
+    return StatusCode::RECOVERABLE;
+  }
+
+  // create initial surface
+  m_initialSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3 {0, 0, 0}, Acts::Vector3{0, 0, 1});
+
+  // create initial parameters
+  Acts::Vector3 pos = positions[1] - positions[1].z()/directions[1].z() * directions[1];
+  Acts::Vector4 pos4 {pos.x(), pos.y(), pos.z(), 0};
+  Acts::Vector3 dir = positions[2] - positions[1];
+  Acts::Vector3 tmp {directions[1].x(), directions[1].y(), directions[1].z()};
+  ATH_MSG_DEBUG(dir);
+  ATH_MSG_DEBUG(tmp);
+  double abs_momentum = momentum(positions);
+  // TODO get charge from momentum calculation
+  double charge = 1;
+
+  Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Zero();
+  cov(Acts::eBoundLoc0, Acts::eBoundLoc0) = m_covLoc0;
+  cov(Acts::eBoundLoc1, Acts::eBoundLoc1) = m_covLoc1;
+  cov(Acts::eBoundPhi, Acts::eBoundPhi) = m_covPhi;
+  cov(Acts::eBoundTheta, Acts::eBoundTheta) = m_covTheta;
+  cov(Acts::eBoundQOverP, Acts::eBoundQOverP) = m_covQOverP;
+  cov(Acts::eBoundTime, Acts::eBoundTime) = m_covTime;
+
+  auto initialParameters = Acts::CurvilinearTrackParameters(
+      pos4, dir, abs_momentum, charge, cov);
+  m_initialTrackParameters = std::make_shared<const Acts::CurvilinearTrackParameters>(initialParameters);
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode SegmentFitTrackFinderTool::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+
+double SegmentFitTrackFinderTool::momentum(const std::map<int, Amg::Vector3D>& pos, double B) {
+  Acts::Vector3 vec_l = pos.at(3) - pos.at(1);
+  double abs_l = std::sqrt(vec_l.y() * vec_l.y() + vec_l.z() * vec_l.z());
+  double t = (pos.at(2).z() - pos.at(1).z()) / (pos.at(3).z() - pos.at(1).z());
+  Acts::Vector3 vec_m = pos.at(1) + t * vec_l;
+  Acts::Vector3 vec_s = pos.at(2) - vec_m;
+  double abs_s = std::sqrt(vec_s.y() * vec_s.y() + vec_s.z() * vec_s.z());
+  double p_yz = 0.3 * abs_l * abs_l * B / (8 * abs_s * 1000);
+  return p_yz;
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/SummaryPlotTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/SummaryPlotTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..f216042a6bbf915f169c41fe9ea0cc9b206f2c73
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/SummaryPlotTool.cxx
@@ -0,0 +1,86 @@
+#include "FaserActsKalmanFilter/SummaryPlotTool.h"
+#include <iostream>
+
+void SummaryPlotTool::book(SummaryPlotTool::SummaryPlotCache &trackSummaryPlotCache) const {
+  PlotHelpers::Binning bEta = m_varBinning.at("Eta");
+  PlotHelpers::Binning bPt = m_varBinning.at("Pt");
+  PlotHelpers::Binning bNum = m_varBinning.at("Num");
+  // number of track states versus eta
+  trackSummaryPlotCache.nStates_vs_eta = PlotHelpers::bookProf(
+      "nStates_vs_eta", "Number of total states vs. #eta", bEta, bNum);
+  // number of measurements versus eta
+  trackSummaryPlotCache.nMeasurements_vs_eta = PlotHelpers::bookProf(
+      "nMeasurements_vs_eta", "Number of measurements vs. #eta", bEta, bNum);
+  // number of holes versus eta
+  trackSummaryPlotCache.nHoles_vs_eta = PlotHelpers::bookProf(
+      "nHoles_vs_eta", "Number of holes vs. #eta", bEta, bNum);
+  // number of outliers versus eta
+  trackSummaryPlotCache.nOutliers_vs_eta = PlotHelpers::bookProf(
+      "nOutliers_vs_eta", "Number of outliers vs. #eta", bEta, bNum);
+  // number of Shared Hits versus eta
+  trackSummaryPlotCache.nSharedHits_vs_eta = PlotHelpers::bookProf(
+      "nSharedHits_vs_eta", "Number of Shared Hits vs. #eta", bEta, bNum);
+  // number of track states versus pt
+  trackSummaryPlotCache.nStates_vs_pt = PlotHelpers::bookProf(
+      "nStates_vs_pT", "Number of total states vs. pT", bPt, bNum);
+  // number of measurements versus pt
+  trackSummaryPlotCache.nMeasurements_vs_pt = PlotHelpers::bookProf(
+      "nMeasurements_vs_pT", "Number of measurements vs. pT", bPt, bNum);
+  // number of holes versus pt
+  trackSummaryPlotCache.nHoles_vs_pt = PlotHelpers::bookProf(
+      "nHoles_vs_pT", "Number of holes vs. pT", bPt, bNum);
+  // number of outliers versus pt
+  trackSummaryPlotCache.nOutliers_vs_pt = PlotHelpers::bookProf(
+      "nOutliers_vs_pT", "Number of outliers vs. pT", bPt, bNum);
+  // number of Shared Hits versus pt
+  trackSummaryPlotCache.nSharedHits_vs_pt = PlotHelpers::bookProf(
+      "nSharedHits_vs_pT", "Number of Shared Hits vs. pT", bPt, bNum);
+}
+
+void SummaryPlotTool::fill(SummaryPlotTool::SummaryPlotCache &trackSummaryPlotCache,
+                           const Acts::BoundTrackParameters &fittedParameters, size_t nStates, size_t nMeasurements,
+                           size_t nOutliers, size_t nHoles, size_t nSharedHits) const {
+  using Acts::VectorHelpers::eta;
+  using Acts::VectorHelpers::perp;
+  const auto& momentum = fittedParameters.momentum();
+  const double fit_eta = eta(momentum);
+  const double fit_pT = perp(momentum);
+
+  PlotHelpers::fillProf(trackSummaryPlotCache.nStates_vs_eta, fit_eta, nStates);
+  PlotHelpers::fillProf(trackSummaryPlotCache.nMeasurements_vs_eta, fit_eta, nMeasurements);
+  PlotHelpers::fillProf(trackSummaryPlotCache.nOutliers_vs_eta, fit_eta, nOutliers);
+  PlotHelpers::fillProf(trackSummaryPlotCache.nHoles_vs_eta, fit_eta, nHoles);
+  PlotHelpers::fillProf(trackSummaryPlotCache.nSharedHits_vs_eta, fit_eta, nSharedHits);
+
+  PlotHelpers::fillProf(trackSummaryPlotCache.nStates_vs_pt, fit_pT, nStates);
+  PlotHelpers::fillProf(trackSummaryPlotCache.nMeasurements_vs_pt, fit_pT, nMeasurements);
+  PlotHelpers::fillProf(trackSummaryPlotCache.nOutliers_vs_pt, fit_pT, nOutliers);
+  PlotHelpers::fillProf(trackSummaryPlotCache.nHoles_vs_pt, fit_pT, nHoles);
+  PlotHelpers::fillProf(trackSummaryPlotCache.nSharedHits_vs_pt, fit_pT, nSharedHits);
+}
+
+void SummaryPlotTool::write(const SummaryPlotTool::SummaryPlotCache &trackSummaryPlotCache) const {
+  trackSummaryPlotCache.nStates_vs_eta->Write();
+  trackSummaryPlotCache.nMeasurements_vs_eta->Write();
+  trackSummaryPlotCache.nOutliers_vs_eta->Write();
+  trackSummaryPlotCache.nHoles_vs_eta->Write();
+  trackSummaryPlotCache.nSharedHits_vs_eta->Write();
+  trackSummaryPlotCache.nStates_vs_pt->Write();
+  trackSummaryPlotCache.nMeasurements_vs_pt->Write();
+  trackSummaryPlotCache.nOutliers_vs_pt->Write();
+  trackSummaryPlotCache.nHoles_vs_pt->Write();
+  trackSummaryPlotCache.nSharedHits_vs_pt->Write();
+}
+
+void SummaryPlotTool::clear(SummaryPlotTool::SummaryPlotCache &trackSummaryPlotCache) const {
+  delete trackSummaryPlotCache.nStates_vs_eta;
+  delete trackSummaryPlotCache.nMeasurements_vs_eta;
+  delete trackSummaryPlotCache.nOutliers_vs_eta;
+  delete trackSummaryPlotCache.nHoles_vs_eta;
+  delete trackSummaryPlotCache.nSharedHits_vs_eta;
+  delete trackSummaryPlotCache.nStates_vs_pt;
+  delete trackSummaryPlotCache.nMeasurements_vs_pt;
+  delete trackSummaryPlotCache.nOutliers_vs_pt;
+  delete trackSummaryPlotCache.nHoles_vs_pt;
+  delete trackSummaryPlotCache.nSharedHits_vs_pt;
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/ThreeStationTrackSeedTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/ThreeStationTrackSeedTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b9e53c66f50eb6f4974ca04fdbd7fa028f2954e3
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/ThreeStationTrackSeedTool.cxx
@@ -0,0 +1,138 @@
+#include "FaserActsKalmanFilter/ThreeStationTrackSeedTool.h"
+#include "TrackerRIO_OnTrack/FaserSCT_ClusterOnTrack.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrackerPrepRawData/FaserSCT_ClusterCollection.h"
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "Identifier/Identifier.h"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+
+
+ThreeStationTrackSeedTool::ThreeStationTrackSeedTool(
+    const std::string& type, const std::string& name, const IInterface* parent)
+    : base_class(type, name, parent) {}
+
+
+StatusCode ThreeStationTrackSeedTool::initialize() {
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+  ATH_CHECK(detStore()->retrieve(m_detManager, "SCT"));
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+  ATH_CHECK(m_trackCollection.initialize());
+  ATH_CHECK(m_clusterContainerKey.initialize());
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ThreeStationTrackSeedTool::run() {
+  // create track seeds for multiple tracks
+  SG::ReadHandle<TrackCollection> trackCollection {m_trackCollection};
+  ATH_CHECK(trackCollection.isValid());
+
+  SG::ReadHandle<Tracker::FaserSCT_ClusterContainer> clusterContainer {m_clusterContainerKey};
+  ATH_CHECK(clusterContainer.isValid());
+
+  using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
+  std::shared_ptr<IdentifierMap> identifierMap = m_trackingGeometryTool->getIdentifierMap();
+
+  const int kSize = 1;
+  using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, kSize>;
+  std::array<Acts::BoundIndices, kSize> Indices = {Acts::eBoundLoc0};
+  std::vector<IndexSourceLink> sourceLinks;
+  std::vector<Measurement> measurements;
+  std::map<Index, Identifier> identifierLinkMap;
+  std::vector<const Tracker::FaserSCT_Cluster*> clusters {};
+  for (const Tracker::FaserSCT_ClusterCollection* clusterCollection : *clusterContainer) {
+    for (const Tracker::FaserSCT_Cluster* cluster : *clusterCollection) {
+      Identifier id = cluster->detectorElement()->identify();
+      identifierLinkMap[measurements.size()] = id;
+      if (identifierMap->count(id) != 0) {
+        Acts::GeometryIdentifier geoId = identifierMap->at(id);
+        IndexSourceLink sourceLink(geoId, measurements.size(), cluster);
+        // create measurement
+        const auto& par = cluster->localPosition();
+        Eigen::Matrix<double, 1, 1> pos {par.x(),};
+        Eigen::Matrix<double, 1, 1> cov {m_std_cluster * m_std_cluster,};
+        ThisMeasurement meas(sourceLink, Indices, pos, cov);
+        sourceLinks.push_back(sourceLink);
+        measurements.emplace_back(std::move(meas));
+        clusters.push_back(cluster);
+      }
+    }
+  }
+
+
+  std::map<int, std::vector<Amg::Vector3D>> station_position_map;
+  for (const Trk::Track* track : *trackCollection) {
+    auto momentum = track->trackParameters()->front()->momentum();
+    ATH_MSG_DEBUG("track momentum: " << momentum.x() << ", " << momentum.y() << ", " << momentum.z());
+    for (const Trk::TrackStateOnSurface* trackState : *(track->trackStateOnSurfaces())) {
+      auto clusterOnTrack = dynamic_cast<const Tracker::FaserSCT_ClusterOnTrack*> (trackState->measurementOnTrack());
+      if (clusterOnTrack) {
+        Identifier id = clusterOnTrack->identify();
+        int station = m_idHelper->station(id);
+        auto fitParameters = track->trackParameters()->front();
+        Amg::Vector3D fitPosition = fitParameters->position();
+        ATH_MSG_DEBUG("cluster position: " << fitPosition.x() << ", " << fitPosition.y() << ", " << fitPosition.z());
+        station_position_map[station].push_back(fitPosition);
+        break;
+      }
+    }
+  }
+
+  Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Zero();
+  cov(Acts::eBoundLoc0, Acts::eBoundLoc0) = m_covLoc0;
+  cov(Acts::eBoundLoc1, Acts::eBoundLoc1) = m_covLoc1;
+  cov(Acts::eBoundPhi, Acts::eBoundPhi) = m_covPhi;
+  cov(Acts::eBoundTheta, Acts::eBoundTheta) = m_covTheta;
+  cov(Acts::eBoundQOverP, Acts::eBoundQOverP) = m_covQOverP;
+  cov(Acts::eBoundTime, Acts::eBoundTime) = m_covTime;
+
+  std::vector<Acts::CurvilinearTrackParameters> initParams {};
+  for (const Amg::Vector3D& pos1 : station_position_map[1]) {
+    for (const Amg::Vector3D& pos2 : station_position_map[2]) {
+      for (const Amg::Vector3D& pos3 : station_position_map[3]) {
+        initParams.push_back(get_params(pos1, pos2, pos3, cov, m_origin));
+        auto seed = initParams.back();
+        auto seed_momentum = seed.momentum();
+      }
+    }
+  }
+
+  m_initialTrackParameters = std::make_shared<std::vector<Acts::CurvilinearTrackParameters>>(initParams);
+  m_sourceLinks = std::make_shared<std::vector<IndexSourceLink>>(sourceLinks);
+  m_idLinks = std::make_shared<IdentifierLink>(identifierLinkMap);
+  m_measurements = std::make_shared<std::vector<Measurement>>(measurements);
+  m_initialSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(
+      Acts::Vector3 {0, 0, m_origin}, Acts::Vector3{0, 0, -1});
+  m_clusters = std::make_shared<std::vector<const Tracker::FaserSCT_Cluster*>>(clusters);
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ThreeStationTrackSeedTool::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+
+Acts::CurvilinearTrackParameters ThreeStationTrackSeedTool::get_params(
+    const Amg::Vector3D& position_st1, const Amg::Vector3D& position_st2, const Amg::Vector3D& position_st3, const Acts::BoundSymMatrix& cov, double origin) {
+  Acts::Vector3 dir = position_st2 - position_st1;
+  Acts::Vector3 pos = position_st1 - (position_st1.z() - origin)/dir.z() * dir;
+  Acts::Vector4 pos4 {pos.x(), pos.y(), pos.z(), 0};
+  auto [abs_momenutm, charge] = momentum({{1, position_st1}, {2, position_st2}, {3, position_st3}});
+  return Acts::CurvilinearTrackParameters(pos4, dir, abs_momenutm, charge, cov);
+}
+std::pair<double, double> ThreeStationTrackSeedTool::momentum(const std::map<int, Amg::Vector3D>& pos, double B) {
+  Acts::Vector3 vec_l = pos.at(3) - pos.at(1);
+  double abs_l = std::sqrt(vec_l.y() * vec_l.y() + vec_l.z() * vec_l.z());
+  double t = (pos.at(2).z() - pos.at(1).z()) / (pos.at(3).z() - pos.at(1).z());
+  Acts::Vector3 vec_m = pos.at(1) + t * vec_l;
+  Acts::Vector3 vec_s = pos.at(2) - vec_m;
+  double abs_s = std::sqrt(vec_s.y() * vec_s.y() + vec_s.z() * vec_s.z());
+  double p_yz = 0.3 * abs_l * abs_l * B / (8 * abs_s * 1000);
+  double charge = vec_s.y() < 0 ? 1 : -1;
+  return std::make_pair(p_yz, charge);
+}
+
+
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TrackClassification.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TrackClassification.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3feae9515e6007f91ca0696e03c8151ebdb456ea
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TrackClassification.cxx
@@ -0,0 +1,66 @@
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "FaserActsKalmanFilter/TrackClassification.h"
+
+namespace {
+
+/// Increase the hit count for the given particle id by one.
+inline void increaseHitCount(std::vector<ParticleHitCount> &particleHitCounts, int particleId) {
+  // linear search since there is no ordering
+  auto it = std::find_if(particleHitCounts.begin(), particleHitCounts.end(),
+                         [=](const ParticleHitCount &phc) {
+                           return (phc.particleId == particleId);
+                         });
+  // either increase count if we saw the particle before or add it
+  if (it != particleHitCounts.end()) {
+    it->hitCount += 1u;
+  } else {
+    particleHitCounts.push_back({particleId, 1u});
+  }
+}
+
+/// Sort hit counts by decreasing values, i.e. majority particle comes first.
+inline void sortHitCount(std::vector<ParticleHitCount> &particleHitCounts) {
+  std::sort(particleHitCounts.begin(), particleHitCounts.end(),
+            [](const ParticleHitCount &lhs, const ParticleHitCount &rhs) {
+              return (lhs.hitCount > rhs.hitCount);
+            });
+}
+
+}  // namespace
+
+
+/// Identify all particles that contribute to a trajectory.
+void identifyContributingParticles(
+    const TrackerSimDataCollection& simDataCollection,
+    const FaserActsRecMultiTrajectory& trajectories, size_t tip,
+    std::vector<ParticleHitCount>& particleHitCounts) {
+  particleHitCounts.clear();
+
+  if (not trajectories.hasTrajectory(tip)) {
+    return;
+  }
+
+  trajectories.multiTrajectory().visitBackwards(tip, [&](const auto& state) {
+    // no truth info with non-measurement state
+    if (not state.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) {
+      return true;
+    }
+    // register all particles that generated this hit
+    Identifier id = state.uncalibrated().hit()->identify();
+    if (simDataCollection.count(id) == 0) {
+      return true;
+    }
+    const auto& deposits = simDataCollection.find(id)->second.getdeposits();
+    int barcode = 0;
+    float highestDep = 0;
+    for (const TrackerSimData::Deposit &deposit : deposits) {
+      if (deposit.second > highestDep) {
+        highestDep = deposit.second;
+        barcode = deposit.first->barcode();
+      }
+    }
+    increaseHitCount(particleHitCounts, barcode);
+    return true;
+  });
+  sortHitCount(particleHitCounts);
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TrackFindingAlgorithmFunction.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TrackFindingAlgorithmFunction.cxx
index 954906a0f4263c6e17f17143b23d4639c5d1107a..44a86cbf06d0b10ae6aeca00bdd8bfd4e261d680 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/TrackFindingAlgorithmFunction.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TrackFindingAlgorithmFunction.cxx
@@ -6,94 +6,51 @@
 #include "Acts/Propagator/Propagator.hpp"
 #include "Acts/TrackFitting/GainMatrixSmoother.hpp"
 #include "Acts/TrackFitting/GainMatrixUpdater.hpp"
-#include "Acts/MagneticField/ConstantBField.hpp"
-#include "Acts/MagneticField/InterpolatedBFieldMap.hpp"
-#include "Acts/MagneticField/SharedBField.hpp"
 
-#include <boost/variant/variant.hpp>
-#include <boost/variant/apply_visitor.hpp>
-#include <boost/variant/static_visitor.hpp>
 
+namespace {
 
 using Updater = Acts::GainMatrixUpdater;
 using Smoother = Acts::GainMatrixSmoother;
-using Stepper = Acts::EigenStepper<>;
-using Propagator = Acts::Propagator<Stepper, Acts::Navigator>;
-
-namespace ActsExtrapolationDetail {
-  using VariantPropagatorBase = boost::variant<
-      Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator>,
-      Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator>
-  >;
-
-  class VariantPropagator : public VariantPropagatorBase
-  {
-  public:
-    using VariantPropagatorBase::VariantPropagatorBase;
-  };
 
-}
-
-using ActsExtrapolationDetail::VariantPropagator;
+using Stepper = Acts::EigenStepper<>;
+using Navigator = Acts::Navigator;
+using Propagator = Acts::Propagator<Stepper, Navigator>;
+using CKF = Acts::CombinatorialKalmanFilter<Propagator, Updater, Smoother>;
 
+using TrackParametersContainer = std::vector<CombinatorialKalmanFilterAlg::TrackParameters>;
 
-template <typename TrackFinder>
-struct TrackFinderFunctionImpl {
-  TrackFinder trackFinder;
+struct TrackFinderFunctionImpl
+    : public CombinatorialKalmanFilterAlg::TrackFinderFunction {
+  CKF trackFinder;
 
-  TrackFinderFunctionImpl(TrackFinder&& f) : trackFinder(std::move(f)) {}
+  TrackFinderFunctionImpl(CKF&& f) : trackFinder(std::move(f)) {}
 
   CombinatorialKalmanFilterAlg::TrackFinderResult operator()(
-      const IndexSourceLinkContainer& sourceLinks,
-      const std::vector<Acts::CurvilinearTrackParameters>& initialParameters,
-      const CombinatorialKalmanFilterAlg::TrackFinderOptions& options) const
-  {
-    return trackFinder.findTracks(sourceLinks, initialParameters, options);
-  }
+      const IndexSourceLinkContainer& sourcelinks,
+      const TrackParametersContainer& initialParameters,
+      const CombinatorialKalmanFilterAlg::TrackFinderOptions& options)
+  const override {
+    return trackFinder.findTracks(sourcelinks, initialParameters, options);
+  };
 };
 
+}  // namespace
 
-CombinatorialKalmanFilterAlg::TrackFinderFunction 
+std::shared_ptr<CombinatorialKalmanFilterAlg::TrackFinderFunction>
 CombinatorialKalmanFilterAlg::makeTrackFinderFunction(
-    std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry)
-{
-  const std::string fieldMode = "FASER";
-  const std::vector<double> constantFieldVector = {0., 0., 0.55};
-
-  Acts::Navigator::Config cfg{trackingGeometry};
-  cfg.resolvePassive   = false;
-  cfg.resolveMaterial  = true;
-  cfg.resolveSensitive = true;
-  Acts::Navigator navigator( cfg );
-
-  std::unique_ptr<ActsExtrapolationDetail::VariantPropagator> varProp;
-
-  if (fieldMode == "FASER") {
-    auto bField = std::make_shared<FASERMagneticFieldWrapper>();
-    auto stepper = Acts::EigenStepper<>(std::move(bField));
-    auto propagator = Acts::Propagator<decltype(stepper), Acts::Navigator>(std::move(stepper),
-                                                                      std::move(navigator));
-    varProp = std::make_unique<VariantPropagator>(propagator);
-  }
-  else if (fieldMode == "Constant") {
-    Acts::Vector3 constantFieldVector = Acts::Vector3(constantFieldVector[0], 
-                                                      constantFieldVector[1], 
-                                                      constantFieldVector[2]);
-
-    auto bField = std::make_shared<Acts::ConstantBField>(constantFieldVector);
-    auto stepper = Acts::EigenStepper<>(std::move(bField));
-    auto propagator = Acts::Propagator<decltype(stepper), Acts::Navigator>(std::move(stepper),
-                                                                      std::move(navigator));
-    varProp = std::make_unique<VariantPropagator>(propagator);
-  }
-
-  return boost::apply_visitor([&](const auto& propagator) -> TrackFinderFunction {
-    using Updater  = Acts::GainMatrixUpdater;
-    using Smoother = Acts::GainMatrixSmoother;
-    using CKF = Acts::CombinatorialKalmanFilter<typename std::decay_t<decltype(propagator)>, Updater, Smoother>;
-
-    CKF trackFinder(std::move(propagator));
-
-    return TrackFinderFunctionImpl<CKF>(std::move(trackFinder));
-  }, *varProp);
-}
\ No newline at end of file
+    std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry,
+    bool resolvePassive, bool resolveMaterial, bool resolveSensitive) {
+  auto magneticField = std::make_shared<FASERMagneticFieldWrapper>();
+  Stepper stepper(std::move(magneticField));
+  Navigator::Config cfg{trackingGeometry};
+  cfg.resolvePassive = resolvePassive;
+  cfg.resolveMaterial = resolveMaterial;
+  cfg.resolveSensitive = resolveSensitive;
+  Navigator navigator(cfg);
+  Propagator propagator(std::move(stepper), std::move(navigator));
+  CKF trackFinder(std::move(propagator));
+
+  // build the track finder functions. owns the track finder object.
+  return std::make_shared<TrackFinderFunctionImpl>(std::move(trackFinder));
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TrackFittingFunction.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TrackFittingFunction.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..0ece62669f80e7863a75b191a9fc7a51d759ca6c
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TrackFittingFunction.cxx
@@ -0,0 +1,118 @@
+#include "FaserActsKalmanFilter/FaserActsKalmanFilterAlg.h"
+#include "FaserActsGeometry/FASERMagneticFieldWrapper.h"
+
+#include "Acts/Propagator/EigenStepper.hpp"
+#include "Acts/Propagator/Navigator.hpp"
+#include "Acts/Propagator/Propagator.hpp"
+#include "Acts/TrackFitting/GainMatrixSmoother.hpp"
+#include "Acts/TrackFitting/GainMatrixUpdater.hpp"
+
+
+namespace {
+
+using Updater = Acts::GainMatrixUpdater;
+using Smoother = Acts::GainMatrixSmoother;
+using Stepper = Acts::EigenStepper<>;
+using Propagator = Acts::Propagator<Stepper, Acts::Navigator>;
+using Fitter = Acts::KalmanFitter<Propagator, Updater, Smoother>;
+
+struct TrackFitterFunctionImpl
+    : public FaserActsKalmanFilterAlg::TrackFitterFunction {
+  Fitter trackFitter;
+
+  TrackFitterFunctionImpl(Fitter &&f) : trackFitter(std::move(f)) {}
+
+  FaserActsKalmanFilterAlg::TrackFitterResult operator()(
+      const std::vector<IndexSourceLink> &sourceLinks,
+      const FaserActsKalmanFilterAlg::TrackParameters &initialParameters,
+      const FaserActsKalmanFilterAlg::TrackFitterOptions &options)
+  const override {
+    return trackFitter.fit(sourceLinks, initialParameters, options);
+  };
+};
+
+}  // namespace
+
+
+std::shared_ptr<FaserActsKalmanFilterAlg::TrackFitterFunction>
+FaserActsKalmanFilterAlg::makeTrackFitterFunction(
+    std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry) {
+  auto magneticField = std::make_shared<FASERMagneticFieldWrapper>();
+  auto stepper = Stepper(std::move(magneticField));
+  Acts::Navigator::Config cfg{trackingGeometry};
+  cfg.resolvePassive = false;
+  cfg.resolveMaterial = true;
+  cfg.resolveSensitive = true;
+  Acts::Navigator navigator(cfg);
+  Propagator propagator(std::move(stepper), std::move(navigator));
+  Fitter trackFitter(std::move(propagator));
+  return std::make_shared<TrackFitterFunctionImpl>(std::move(trackFitter));
+}
+
+/*
+
+namespace ActsExtrapolationDetail {
+using VariantPropagatorBase = boost::variant<
+  Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator>,
+  Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator>>;
+
+class VariantPropagator : public VariantPropagatorBase {
+public:
+  using VariantPropagatorBase::VariantPropagatorBase;
+};
+}  // namespace ActsExtrapolationDetail
+
+
+namespace {
+template <typename Fitter>
+struct FitterFunctionImpl {
+  Fitter fitter;
+  FitterFunctionImpl(Fitter&& f) : fitter(std::move(f)) {}
+  FaserActsKalmanFilterAlg::TrackFitterResult operator()(
+    const std::vector<IndexSourceLink>& sourceLinks,
+    const Acts::CurvilinearTrackParameters& initialParameters,
+    const Acts::KalmanFitterOptions<MeasurementCalibrator, Acts::VoidOutlierFinder, Acts::VoidReverseFilteringLogic>& options) const {
+      return fitter.fit(sourceLinks, initialParameters, options);
+    };
+};
+}  // namespace
+
+std::shared_ptr<FaserActsKalmanFilterAlg::TrackFitterFunction>
+FaserActsKalmanFilterAlg::makeTrackFitterFunction(std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry) {
+
+  const std::string fieldMode = "FASER";
+  const std::vector<double> constantFieldVector = {0., 0., 0.55};
+
+  Acts::Navigator::Config cfg{trackingGeometry};
+  cfg.resolvePassive   = false;
+  cfg.resolveMaterial  = true;
+  cfg.resolveSensitive = true;
+  Acts::Navigator navigator( cfg );
+
+  std::unique_ptr<ActsExtrapolationDetail::VariantPropagator> varProp;
+
+  if (fieldMode == "FASER") {
+    auto bField = std::make_shared<FASERMagneticFieldWrapper>();
+    auto stepper = Acts::EigenStepper<>(std::move(bField));
+    auto propagator = Acts::Propagator<decltype(stepper),
+        Acts::Navigator>(std::move(stepper), std::move(navigator));
+    varProp = std::make_unique<ActsExtrapolationDetail::VariantPropagator>(propagator);
+  } else if (fieldMode == "Constant") {
+    Acts::Vector3 constantFieldVector = Acts::Vector3(
+      constantFieldVector[0], constantFieldVector[1], constantFieldVector[2]);
+    auto bField = std::make_shared<Acts::ConstantBField>(constantFieldVector);
+    auto stepper = Acts::EigenStepper<>(std::move(bField));
+    auto propagator = Acts::Propagator<decltype(stepper),
+        Acts::Navigator>(std::move(stepper), std::move(navigator));
+    varProp = std::make_unique<ActsExtrapolationDetail::VariantPropagator>(propagator);
+  }
+
+  return boost::apply_visitor([&](const auto& propagator) -> TrackFitterFunction {
+    using Updater  = Acts::GainMatrixUpdater;
+    using Smoother = Acts::GainMatrixSmoother;
+    using Fitter = Acts::KalmanFitter<typename std::decay_t<decltype(propagator)>, Updater, Smoother>;
+    Fitter fitter(std::move(propagator));
+    return FitterFunctionImpl<Fitter>(std::move(fitter));
+  }, *varProp);
+}
+ */
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TrackSelection.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TrackSelection.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..fdc77b13a95da4e89a7ff0c869f24e781d4ed7cc
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TrackSelection.cxx
@@ -0,0 +1,41 @@
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
+#include "FaserActsKalmanFilter/TrackSelection.h"
+
+namespace {
+
+inline void sortTracks(std::vector<TrackQuality>& tracks) {
+  std::sort(tracks.begin(), tracks.end(),
+            [](const TrackQuality& lhs, const TrackQuality& rhs) {
+    if (lhs.nMeasurements != rhs.nMeasurements) {
+      return lhs.nMeasurements > rhs.nMeasurements;
+    }
+    return lhs.chi2 < rhs.chi2;
+  });
+}
+
+}  // namespace
+
+
+void selectTracks(TrackFinderResult& results, std::vector<TrackQuality>& trackQuality) {
+  for (auto& result : results) {
+    if (not result.ok()) {
+      continue;
+    }
+    auto& ckfResult = result.value();
+    auto traj = FaserActsRecMultiTrajectory(ckfResult.fittedStates, ckfResult.lastMeasurementIndices, ckfResult.fittedParameters);
+    const auto& mj = traj.multiTrajectory();
+    const auto& trackTips = traj.tips();
+    auto it = std::max_element(trackTips.begin(), trackTips.end(),
+                               [&](const size_t& lhs, const size_t& rhs) {
+                                 auto trajState_lhs = Acts::MultiTrajectoryHelpers::trajectoryState(mj, lhs);
+                                 auto trajState_rhs = Acts::MultiTrajectoryHelpers::trajectoryState(mj, rhs);
+                                 if (trajState_lhs.nMeasurements != trajState_rhs.nMeasurements) {
+                                   return trajState_lhs.nMeasurements > trajState_rhs.nMeasurements;
+                                 }
+                                 return trajState_lhs.chi2Sum < trajState_rhs.chi2Sum;
+                               });
+    auto trajState = Acts::MultiTrajectoryHelpers::trajectoryState(mj, *it);
+    trackQuality.push_back({ckfResult, trajState.nMeasurements, trajState.chi2Sum});
+  }
+  sortTracks(trackQuality);
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TrajectoryWriterTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TrajectoryWriterTool.cxx
index aca7bd57a3df15bfbecffc5e6afb996daffb7020..55dfda762765da67bea84b219d5279ce0bf1a197 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/TrajectoryWriterTool.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TrajectoryWriterTool.cxx
@@ -1,3 +1,4 @@
+#include "TrackerPrepRawData/FaserSCT_Cluster.h"
 #include "FaserActsKalmanFilter/TrajectoryWriterTool.h"
 #include "FaserActsKalmanFilter/FaserActsRecMultiTrajectory.h"
 #include "Acts/EventData/MultiTrajectoryHelpers.hpp"
@@ -80,6 +81,15 @@ void TrajectoryWriterTool::initializeTree() {
   m_tree->Branch("g_x_fit" , &m_x_fit);
   m_tree->Branch("g_y_fit" , &m_y_fit);
   m_tree->Branch("g_z_fit" , &m_z_fit);
+  m_tree->Branch("lx_hit" , &m_lx_hit);
+  m_tree->Branch("ly_hit" , &m_ly_hit);
+  m_tree->Branch("x_hit" , &m_x_hit);
+  m_tree->Branch("y_hit" , &m_y_hit);
+  m_tree->Branch("z_hit" , &m_z_hit);
+  m_tree->Branch("meas_eLOC0" , &m_meas_eLOC0);
+  m_tree->Branch("meas_eLOC1" , &m_meas_eLOC1);
+  m_tree->Branch("meas_cov_eLOC0" , &m_meas_cov_eLOC0);
+  m_tree->Branch("meas_cov_eLOC1" , &m_meas_cov_eLOC1);
 
   m_tree->Branch("nPredicted", &m_nPredicted);
   m_tree->Branch("predicted", &m_prt);
@@ -228,7 +238,10 @@ void TrajectoryWriterTool::writeout(TrajectoriesContainer trajectories,
         m_eta_ini.push_back(eta(initialparameters[iTraj].position(geoContext)));
 
     // The trajectory entry indices and the multiTrajectory
-    const auto& [trackTips, mj] = traj.trajectory();
+    // const auto& [trackTips, mj] = traj.multiTrajectory();
+    const auto& mj = traj.multiTrajectory();
+    const auto& trackTips = traj.tips();
+
     if (trackTips.empty()) {
       ATH_MSG_WARNING("Empty multiTrajectory.");
       m_nMeasurements = -1;
@@ -308,11 +321,16 @@ void TrajectoryWriterTool::writeout(TrajectoriesContainer trajectories,
 
       // extract local and global position
       Acts::Vector2 local(meas[Acts::eBoundLoc0], meas[Acts::eBoundLoc1]);
-const Acts::Vector3 dir = Acts::makeDirectionUnitFromPhiTheta(meas[Acts::eBoundPhi], meas[Acts::eBoundTheta]);
+      const Acts::Vector3 dir = Acts::makeDirectionUnitFromPhiTheta(meas[Acts::eBoundPhi], meas[Acts::eBoundTheta]);
       Acts::Vector3 mom(1, 1, 1);
       Acts::Vector3 global =
           surface.localToGlobal(geoContext, local, dir);
 
+      m_meas_eLOC0.push_back(state.calibrated()[Acts::eBoundLoc0]);
+      m_meas_eLOC1.push_back(state.calibrated()[Acts::eBoundLoc1]);
+      m_meas_cov_eLOC0.push_back(state.calibratedCovariance()(Acts::eBoundLoc0, Acts::eBoundLoc0));
+      m_meas_cov_eLOC1.push_back(state.calibratedCovariance()(Acts::eBoundLoc1, Acts::eBoundLoc1));
+
       // fill the measurement info
       m_lx_hit.push_back(local[Acts::ePos0]);
       m_ly_hit.push_back(local[Acts::ePos1]);
@@ -340,15 +358,15 @@ const Acts::Vector3 dir = Acts::makeDirectionUnitFromPhiTheta(meas[Acts::eBoundP
 
         /// Predicted residual
         m_res_eLOC0_prt.push_back(residual(Acts::eBoundLoc0));
-        m_res_eLOC1_prt.push_back(residual(Acts::eBoundLoc1));
+//        m_res_eLOC1_prt.push_back(residual(Acts::eBoundLoc1));
 
         /// Predicted parameter pulls
         m_pull_eLOC0_prt.push_back(
             residual(Acts::eBoundLoc0) /
             sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_pull_eLOC1_prt.push_back(
-            residual(Acts::eBoundLoc1) /
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+//        m_pull_eLOC1_prt.push_back(
+//            residual(Acts::eBoundLoc1) /
+//            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
 
         /// Predicted parameter
         m_eLOC0_prt.push_back(parameter.parameters()[Acts::eBoundLoc0]);
@@ -429,15 +447,15 @@ const Acts::Vector3 dir = Acts::makeDirectionUnitFromPhiTheta(meas[Acts::eBoundP
 
         /// Filtered residual
         m_res_eLOC0_flt.push_back(residual(Acts::eBoundLoc0));
-        m_res_eLOC1_flt.push_back(residual(Acts::eBoundLoc1));
+//        m_res_eLOC1_flt.push_back(residual(Acts::eBoundLoc1));
 
         /// Filtered parameter pulls
         m_pull_eLOC0_flt.push_back(
             residual(Acts::eBoundLoc0) /
             sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_pull_eLOC1_flt.push_back(
-            residual(Acts::eBoundLoc1) /
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+//        m_pull_eLOC1_flt.push_back(
+//            residual(Acts::eBoundLoc1) /
+//            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
 
         /// Filtered parameter
         m_eLOC0_flt.push_back(parameter.parameters()[Acts::eBoundLoc0]);
@@ -520,30 +538,30 @@ const Acts::Vector3 dir = Acts::makeDirectionUnitFromPhiTheta(meas[Acts::eBoundP
         auto residual = state.effectiveCalibrated() - H * state.smoothed();
 
         m_res_x_hit.push_back(residual(Acts::eBoundLoc0));
-        m_res_y_hit.push_back(residual(Acts::eBoundLoc1));
+//        m_res_y_hit.push_back(residual(Acts::eBoundLoc1));
         m_err_x_hit.push_back(
             sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_err_y_hit.push_back(
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+//        m_err_y_hit.push_back(
+//            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
         m_pull_x_hit.push_back(
             residual(Acts::eBoundLoc0) /
             sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_pull_y_hit.push_back(
-            residual(Acts::eBoundLoc1) /
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+//        m_pull_y_hit.push_back(
+//            residual(Acts::eBoundLoc1) /
+//            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
         m_dim_hit.push_back(state.calibratedSize());
 
         /// Smoothed residual
         m_res_eLOC0_smt.push_back(residual(Acts::eBoundLoc0));
-        m_res_eLOC1_smt.push_back(residual(Acts::eBoundLoc1));
+//        m_res_eLOC1_smt.push_back(residual(Acts::eBoundLoc1));
 
         /// Smoothed parameter pulls
         m_pull_eLOC0_smt.push_back(
             residual(Acts::eBoundLoc0) /
             sqrt(resCov(Acts::eBoundLoc0, Acts::eBoundLoc0)));
-        m_pull_eLOC1_smt.push_back(
-            residual(Acts::eBoundLoc1) /
-            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
+//        m_pull_eLOC1_smt.push_back(
+//            residual(Acts::eBoundLoc1) /
+//            sqrt(resCov(Acts::eBoundLoc1, Acts::eBoundLoc1)));
 
         /// Smoothed parameter
         m_eLOC0_smt.push_back(parameter.parameters()[Acts::eBoundLoc0]);
@@ -638,6 +656,10 @@ void TrajectoryWriterTool::clearVariables() const {
   m_x_hit.clear();
   m_y_hit.clear();
   m_z_hit.clear();
+  m_meas_eLOC0.clear();
+  m_meas_eLOC1.clear();
+  m_meas_cov_eLOC0.clear();
+  m_meas_cov_eLOC1.clear();
   m_res_x_hit.clear();
   m_res_y_hit.clear();
   m_err_x_hit.clear();
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TruthBasedInitialParameterTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TruthBasedInitialParameterTool.cxx
index 85b1036e415fb3f28f9f6b8ee37679afb60304bd..f5b850d8a05877b9d7d48db65faadbff7cfac519 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/src/TruthBasedInitialParameterTool.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TruthBasedInitialParameterTool.cxx
@@ -51,8 +51,6 @@ Acts::CurvilinearTrackParameters TruthBasedInitialParameterTool::getInitialParam
     }
   }
 
-  std::cout << "?? px = " << momentum.x() << " py = " << momentum.y() << " pz = " << momentum.z() << std::endl;
-
   Acts::Vector3 truthVertex = {vertex.x(), vertex.y(), vertex.z()}; // in mm
   Acts::Vector3 truthMomentum = {momentum.x() / 1000, momentum.y() / 1000, momentum.z() / 1000}; // in GeV
   m_simWriterTool->writeout(truthVertex, truthMomentum);
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TruthSeededTrackFinderTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TruthSeededTrackFinderTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4a574ee970e5dc9ccc67dcdd94143b2df20d1114
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TruthSeededTrackFinderTool.cxx
@@ -0,0 +1,252 @@
+#include "FaserActsKalmanFilter/TruthSeededTrackFinderTool.h"
+
+#include "Acts/Definitions/TrackParametrization.hpp"
+#include "Acts/EventData/Measurement.hpp"
+#include "Acts/Geometry/GeometryContext.hpp"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+#include "Acts/Geometry/TrackingGeometry.hpp"
+#include "Acts/Surfaces/PlaneSurface.hpp"
+#include "Acts/Surfaces/RectangleBounds.hpp"
+#include "Acts/Surfaces/Surface.hpp"
+#include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include "GeoPrimitives/CLHEPtoEigenConverter.h"
+#include "Identifier/Identifier.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrackerReadoutGeometry/SiDetectorElement.h"
+#include "TrackerSpacePoint/FaserSCT_SpacePoint.h"
+#include "TrackerSpacePoint/SpacePointForSeed.h"
+#include <array>
+#include <random>
+
+
+TruthSeededTrackFinderTool::TruthSeededTrackFinderTool(
+    const std::string& type, const std::string& name, const IInterface* parent) :
+    base_class(type, name, parent) {}
+
+
+StatusCode TruthSeededTrackFinderTool::initialize() {
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+  ATH_CHECK(detStore()->retrieve(m_detManager, "SCT"));
+  ATH_CHECK(m_spacePointCollectionKey.initialize());
+  ATH_CHECK(m_mcEventCollectionKey.initialize());
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TruthSeededTrackFinderTool::run() {
+//  SG::ReadHandle<FaserSiHitCollection> siHitCollection {m_siHitCollectionKey};
+//      ATH_CHECK(siHitCollection.isValid());
+//  ATH_MSG_DEBUG("Read FaserSiHitCollection with " << siHitCollection->size() << " hits");
+
+  using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
+  std::shared_ptr<IdentifierMap> identifierMap = m_trackingGeometryTool->getIdentifierMap();
+
+  SG::ReadHandle<SpacePointForSeedCollection> spacePointCollection {m_spacePointCollectionKey};
+  ATH_CHECK(spacePointCollection.isValid());
+
+  Acts::GeometryContext geoctx = m_trackingGeometryTool->getNominalGeometryContext().context();
+
+  std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry
+      = m_trackingGeometryTool->trackingGeometry();
+
+  const int kSize = 2;
+  using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, kSize>;
+  std::array<Acts::BoundIndices, kSize> myIndices = {Acts::eBoundLoc0, Acts::eBoundLoc1};
+
+  std::map<int, Acts::Vector3> spacePoints;
+
+  std::vector<IndexSourceLink> sourcelinks;
+  std::vector<Measurement> measurements;
+
+  for (const SpacePointForSeed* sp : *spacePointCollection) {
+    const Tracker::FaserSCT_SpacePoint* spacePoint = sp->SpacePoint();
+    Identifier id = spacePoint->associatedSurface().associatedDetectorElementIdentifier();
+    Acts::GeometryIdentifier geoId = identifierMap->at(id);
+    // const Acts::Surface *surface = m_trackingGeometryTool->trackingGeometry()->findSurface(geoId);
+
+    auto par = spacePoint->localParameters();
+    ATH_MSG_DEBUG("par " << par);
+    auto cov = spacePoint->localCovariance();
+    ATH_MSG_DEBUG("cov " << cov);
+    ATH_MSG_DEBUG(cov(0, 0) << ", " << cov(0, 1) << ", " << cov(1, 0) << ", " << cov(1, 1));
+    Acts::ActsSymMatrix<2> myCov = Acts::ActsSymMatrix<2>::Zero();
+    myCov(0, 0) = m_covMeas00;
+    myCov(1, 1) = m_covMeas11;
+    myCov(0, 1) = m_covMeas01;
+    myCov(1, 0) = m_covMeas10;
+
+    IndexSourceLink sourceLink(geoId, measurements.size());
+    ThisMeasurement meas(sourceLink, myIndices, par, myCov);
+    sourcelinks.push_back(sourceLink);
+    measurements.emplace_back(std::move(meas));
+
+    Amg::Vector3D globalPosition = spacePoint->globalPosition();
+    int station = m_idHelper->station(id);
+    int layer = m_idHelper->layer(id);
+    // TODO check if map already contains a space point for this layer
+    spacePoints[3*(station-1) + layer] = Acts::Vector3(globalPosition.x(), globalPosition.y(), globalPosition.z());
+
+    if (station == 1 && layer == 0) {
+      const TrackerDD::SiDetectorElement *element = m_detManager->getDetectorElement(id);
+      // Construct a plane surface as the target surface
+      const TrackerDD::SiDetectorDesign &design = element->design();
+      double hlX = design.width()/2. * 1_mm;
+      double hlY = design.length()/2. * 1_mm;
+      auto rectangleBounds = std::make_shared<const Acts::RectangleBounds>(hlX, hlY);
+      Amg::Transform3D g2l = element->getMaterialGeom()->getDefAbsoluteTransform()
+                             * Amg::CLHEPTransformToEigen(element->recoToHitTransform());
+      m_initialSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(g2l, rectangleBounds);
+    }
+  }
+
+  // check if there is atleast one space point in each station
+
+  if (map2vector(spacePoints, 1).empty() || map2vector(spacePoints, 2).empty() || map2vector(spacePoints, 0).empty()) {
+    return StatusCode::RECOVERABLE;
+  }
+
+  Acts::Vector4 smearedPosition4;
+  Acts::Vector3 smearedDirection;
+  double smearedAbsoluteMomentum;
+  double charge;
+  if (m_useTrueInitialParameters) {
+    // get initial parameters from generated particle
+    SG::ReadHandle<McEventCollection> mcEventCollection {m_mcEventCollectionKey};
+    ATH_CHECK(mcEventCollection.isValid());
+    for (const HepMC::GenEvent* event : *mcEventCollection) {
+      for (const HepMC::GenParticle* particle : event->particle_range()) {
+        const HepMC::FourVector& momentum = particle->momentum();
+        double abs_momentum = momentum.rho() * Acts::UnitConstants::MeV;
+        Acts::Vector3 direction = Acts::Vector3(momentum.x(), momentum.y(), momentum.z());
+        const HepMC::FourVector vertex = particle->production_vertex()->position();
+        charge = particle->pdg_id() > 0 ? -1 : 1;
+
+        std::random_device rd;
+        std::default_random_engine rng {rd()};
+        std::normal_distribution<> norm;
+
+        auto theta = Acts::VectorHelpers::theta(direction);
+        auto phi = Acts::VectorHelpers::phi(direction);
+        auto angles = Acts::detail::normalizePhiTheta(
+            phi + m_sigmaPhi * norm(rng),
+            theta + m_sigmaTheta * norm(rng));
+        smearedDirection = Acts::Vector3(
+            std::sin(angles.second) * std::cos(angles.first),
+            std::sin(angles.second) * std::sin(angles.first),
+            std::cos(angles.second));
+        smearedAbsoluteMomentum = abs_momentum * (1 + m_sigmaP * norm(rng));
+
+        smearedPosition4 = Acts::Vector4(vertex.x() + m_sigmaLoc0 * norm(rng), vertex.y() + m_sigmaLoc1 * norm(rng), vertex.z(), 0);
+      }
+    }
+  } else {
+    // get initial parameters from track seed
+    auto [p, q] = momentum2(spacePoints);
+    double abs_momentum = p;
+    charge = q;
+    Acts::Vector3 initPos {0, 0, 0};
+    if (spacePoints.count(0)) {
+      initPos = spacePoints.at(0);
+    } else {
+      ATH_MSG_ERROR("Could not find space point on first layer");
+    }
+    smearedPosition4 = Acts::Vector4(initPos.x(), initPos.y(), initPos.z(), 0);
+
+    auto [pos1, dir1] = linear_fit(map2vector(spacePoints, 1));
+    auto [pos2, dir2] = linear_fit(map2vector(spacePoints, 2));
+    smearedDirection = pos2 - pos1;
+//    smearedDirection = dir;
+    smearedAbsoluteMomentum = p;
+  }
+
+  Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Zero();
+  cov(Acts::eBoundLoc0, Acts::eBoundLoc0) = m_covLoc0;
+  cov(Acts::eBoundLoc1, Acts::eBoundLoc1) = m_covLoc1;
+  cov(Acts::eBoundPhi, Acts::eBoundPhi) = m_covPhi;
+  cov(Acts::eBoundTheta, Acts::eBoundTheta) = m_covTheta;
+  cov(Acts::eBoundQOverP, Acts::eBoundQOverP) = m_covQOverP;
+  cov(Acts::eBoundTime, Acts::eBoundTime) = m_covTime;
+
+  // auto initialParameters = Acts::BoundTrackParameters(surface->getSharedPtr(), params, charge, cov);
+  auto initialParameters = Acts::CurvilinearTrackParameters(
+      smearedPosition4, smearedDirection, smearedAbsoluteMomentum, charge, cov);
+
+  // write out
+  double initialCharge = initialParameters.charge();
+  double initialAbsoluteMomentum = initialParameters.absoluteMomentum();
+  Acts::Vector3 initialPosition = initialParameters.position(geoctx);
+  Acts::Vector3 initialMomentum = initialParameters.momentum();
+  ATH_MSG_DEBUG("initial charge: " << initialCharge);
+  ATH_MSG_DEBUG("initial absolute momentum: " << initialAbsoluteMomentum);
+  ATH_MSG_DEBUG("initial position: x=" << initialPosition.x() << ", y=" << initialPosition.y() << ", z=" << initialPosition.z());
+  ATH_MSG_DEBUG("initial momentum: x=" << initialMomentum.x() << ", y=" << initialMomentum.y() << ", z=" << initialMomentum.z());
+  m_initialTrackParameters = std::make_shared<const Acts::CurvilinearTrackParameters>(initialParameters);
+
+  m_sourceLinks = std::make_shared<std::vector<IndexSourceLink>>(sourcelinks);
+  m_measurements = std::make_shared<std::vector<Measurement>>(measurements);
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TruthSeededTrackFinderTool::finalize() {
+  return StatusCode::SUCCESS;
+}
+
+Acts::Vector3 TruthSeededTrackFinderTool::average(const std::vector<Acts::Vector3>& spacePoints) {
+  Acts::Vector3 ret {0, 0, 0};
+  if (!spacePoints.empty()) {
+    for (const Acts::Vector3& spacePoint : spacePoints) {
+      ret += spacePoint;
+    }
+    ret /= spacePoints.size();
+  }
+  return ret;
+}
+
+std::pair<Acts::Vector3, Acts::Vector3>
+TruthSeededTrackFinderTool::linear_fit(const std::vector<Acts::Vector3>& hits) {
+  size_t n_hits = hits.size();
+  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> centers(n_hits, 3);
+  for (size_t i = 0; i < n_hits; ++i) centers.row(i) = hits[i];
+
+  Acts::Vector3 origin = centers.colwise().mean();
+  Eigen::MatrixXd centered = centers.rowwise() - origin.transpose();
+  Eigen::MatrixXd cov = centered.adjoint() * centered;
+  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eig(cov);
+  Acts::Vector3 axis = eig.eigenvectors().col(2).normalized();
+
+  return std::make_pair(origin, axis);
+}
+
+
+std::vector<Acts::Vector3>
+TruthSeededTrackFinderTool::map2vector(const std::map<int, Acts::Vector3>& map, int station) {
+  std::vector<Acts::Vector3> vec;
+  for (int layer = station * 3; layer < station * 3 + 3; ++layer) {
+    if (map.count(layer)) {
+      vec.push_back(map.at(layer));
+    }
+  }
+  return vec;
+}
+
+
+std::pair<double, double> TruthSeededTrackFinderTool::momentum2(const std::map<int, Acts::Vector3>& hits, double B) {
+  Acts::Vector3 pos1 = average(map2vector(hits, 0));
+  Acts::Vector3 pos2 = average(map2vector(hits, 1));
+  Acts::Vector3 pos3 = average(map2vector(hits, 2));
+
+  Acts::Vector3 vec_l = pos3 - pos1;
+  double abs_l = std::sqrt(vec_l.y() * vec_l.y() + vec_l.z() * vec_l.z());
+  double t = (pos2.z() - pos1.z()) / (pos3.z() - pos1.z());
+  Acts::Vector3 vec_m = pos1 + t * vec_l;
+  Acts::Vector3 vec_s = pos2 - vec_m;
+  double abs_s = std::sqrt(vec_s.y() * vec_s.y() + vec_s.z() * vec_s.z());
+  double p_yz = 0.3 * abs_l * abs_l * B / (8 * abs_s * 1000); // in GeV
+  // double charge = vec_s.y() < 0 ? 1 : -1;
+  double charge = 1;
+
+  return std::make_pair(p_yz, charge);
+}
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/TruthTrackFinderTool.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/TruthTrackFinderTool.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..16882021e4babed59f3a52030a565411290eb9ac
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/TruthTrackFinderTool.cxx
@@ -0,0 +1,191 @@
+#include "FaserActsKalmanFilter/TruthTrackFinderTool.h"
+
+#include "Acts/Definitions/TrackParametrization.hpp"
+#include "Acts/EventData/Measurement.hpp"
+#include "Acts/Geometry/GeometryContext.hpp"
+#include "Acts/Geometry/GeometryIdentifier.hpp"
+#include "Acts/Geometry/TrackingGeometry.hpp"
+#include "Acts/Surfaces/Surface.hpp"
+#include "FaserActsKalmanFilter/IndexSourceLink.h"
+#include "GeoPrimitives/CLHEPtoEigenConverter.h"
+#include "Identifier/Identifier.h"
+#include "TrackerIdentifier/FaserSCT_ID.h"
+#include "TrackerReadoutGeometry/SCT_DetectorManager.h"
+#include "TrackerReadoutGeometry/SiDetectorElement.h"
+#include <array>
+#include <random>
+
+
+TruthTrackFinderTool::TruthTrackFinderTool(const std::string& type, const std::string& name, const IInterface* parent)
+    : base_class(type, name, parent) {}
+
+
+StatusCode TruthTrackFinderTool::initialize() {
+  ATH_CHECK(detStore()->retrieve(m_idHelper, "FaserSCT_ID"));
+  ATH_CHECK(detStore()->retrieve(m_detManager, "SCT"));
+  ATH_CHECK(m_siHitCollectionKey.initialize());
+  ATH_CHECK(m_trackingGeometryTool.retrieve());
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TruthTrackFinderTool::run() {
+  SG::ReadHandle<FaserSiHitCollection> siHitCollection {m_siHitCollectionKey};
+  ATH_CHECK(siHitCollection.isValid());
+  ATH_MSG_DEBUG("Read FaserSiHitCollection with " << siHitCollection->size() << " hits");
+
+  std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry
+      = m_trackingGeometryTool->trackingGeometry();
+
+  using IdentifierMap = std::map<Identifier, Acts::GeometryIdentifier>;
+  std::shared_ptr<IdentifierMap> identifierMap = m_trackingGeometryTool->getIdentifierMap();
+  Acts::GeometryContext gctx = m_trackingGeometryTool->getNominalGeometryContext().context();
+  const int kSize = 2;
+  using ParametersVector = Acts::ActsVector<kSize>;
+  using CovarianceMatrix = Acts::ActsSymMatrix<kSize>;
+  using ThisMeasurement = Acts::Measurement<IndexSourceLink, Acts::BoundIndices, kSize>;
+  std::array<Acts::BoundIndices, kSize> myIndices = {Acts::eBoundLoc0, Acts::eBoundLoc1};
+
+  std::vector<IndexSourceLink> sourcelinks;
+  std::vector<Measurement> measurements;
+
+  std::vector<Acts::GeometryIdentifier> geoIds = {};
+  int station, layer, phi, eta, side;
+  for (const FaserSiHit &hit: *siHitCollection) {
+    if (!hit.particleLink() or std::abs(hit.particleLink()->pdg_id()) != 13) {
+      continue;
+    }
+
+    station = hit.getStation();
+    layer = hit.getPlane();
+    phi = hit.getRow();
+    eta = hit.getModule();
+    side = hit.getSensor();
+
+    if (side == 0) {
+      continue;
+    }
+
+    ATH_MSG_DEBUG(station << "/" << layer << "/" << phi << "/" << eta << "/" << side);
+
+    Identifier id = m_idHelper->wafer_id(station, layer, phi, eta, side);
+    // TODO check if there can be multiple simulated muon hits in the same wafer
+    // currently I take the first hit in each wafer
+    Acts::GeometryIdentifier geoId = identifierMap->at(id);
+    if (std::find(geoIds.begin(), geoIds.end(), geoId) != geoIds.end()) {
+      continue;
+    }
+    geoIds.push_back(geoId);
+    ATH_MSG_DEBUG(geoIds);
+
+    auto momentum = hit.particleLink()->momentum();
+    double abs_momentum = momentum.rho() * Acts::UnitConstants::MeV;
+    double QOverP = 1 / abs_momentum;
+    auto direction = Acts::Vector3(momentum.x(), momentum.y(), momentum.z()).normalized();
+
+    const TrackerDD::SiDetectorElement *element = m_detManager->getDetectorElement(id);
+
+    // create source links and measurements
+    const Acts::Surface *surface = m_trackingGeometryTool->trackingGeometry()->findSurface(geoId);
+    if (surface == nullptr) {
+      ATH_MSG_FATAL("Could not find surface");
+      continue;
+    }
+
+    const HepGeom::Point3D<double> localStartPos = hit.localStartPosition();
+    if (element) {
+      const HepGeom::Point3D<double> globalStartPos =
+          Amg::EigenTransformToCLHEP(element->transformHit()) * HepGeom::Point3D<double>(localStartPos);
+      auto center = surface->center(gctx);
+      Acts::Vector3 position = {globalStartPos.x(), globalStartPos.y(), center.z()};
+      Acts::Result<Acts::BoundVector> boundParamsRes
+          = Acts::detail::transformFreeToBoundParameters(position, hit.meanTime(), direction, QOverP, *surface, gctx);
+      if (!boundParamsRes.ok()) {
+        ATH_MSG_FATAL("Could not construct bound parameters");
+        return StatusCode::FAILURE;
+      }
+      const auto &boundParams = boundParamsRes.value();
+      ATH_MSG_DEBUG(boundParams[0] << ", " << boundParams[1]);
+
+      std::random_device rd;
+      std::default_random_engine rng {rd()};
+      std::normal_distribution<> norm;
+
+      ParametersVector smearedBoundParams = {
+          boundParams[0] + norm(rng) * m_sigma0, boundParams[1] + norm(rng) * m_sigma1};
+
+      ParametersVector par = ParametersVector::Zero();
+      par[0] = smearedBoundParams[0];
+      par[1] = smearedBoundParams[1];
+
+      ATH_MSG_DEBUG("bound parameters: par0=" << boundParams[0] << ", par1=" << boundParams[1]);
+      ATH_MSG_DEBUG("smeared parameters: par0=" << smearedBoundParams[0] << ", par1=" << smearedBoundParams[1]);
+
+      CovarianceMatrix cov = CovarianceMatrix::Zero();
+      cov(0, 0) = std::max(m_covMeasLoc0.value(), m_sigma0 * m_sigma0);
+      cov(1, 1) = std::max(m_covMeasLoc1.value(), m_sigma1 * m_sigma1);
+
+      IndexSourceLink sourceLink(geoId, measurements.size());
+      sourcelinks.emplace_back(sourceLink);
+      ThisMeasurement meas(sourceLink, myIndices, par, cov);
+      measurements.emplace_back(std::move(meas));
+
+      // create initial parameters from hit in first layer
+      if ((station == m_first_station) && (layer == m_first_layer) && (side == m_first_side)) {
+
+        // smear initial direction
+         auto theta = Acts::VectorHelpers::theta(direction);
+         auto phi = Acts::VectorHelpers::phi(direction);
+         auto angles = Acts::detail::normalizePhiTheta(
+             phi + m_sigmaPhi * M_PI/180. * norm(rng),
+             theta + m_sigmaTheta * M_PI/180. * norm(rng));
+         Acts::Vector3 smearedDirection(
+             std::sin(angles.second) * std::cos(angles.first),
+             std::sin(angles.second) * std::sin(angles.first),
+             std::cos(angles.second));
+         double smearedAbsoluteMomentum = abs_momentum * (1 + m_sigmaP * norm(rng));
+
+        double z_init = 0;
+        m_initialSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(Acts::Vector3 {0, 0, z_init}, Acts::Vector3{0, 0, 1});
+
+        // extrapolate position on first layer to initial position
+        // TODO use first layer as initial layer instead?
+        Acts::Vector3 initPosition = position + (z_init - position.z()) / direction.z() * direction;
+        Acts::Vector3 smearedPosition {initPosition.x() + m_sigmaLoc0 * norm(rng), initPosition.y() + m_sigmaLoc1 * norm(rng), initPosition.z()};
+        Acts::Vector4 smearedPosition4 {smearedPosition.x(), smearedPosition.y(), smearedPosition.z(), 0};
+        double charge = hit.particleLink()->pdg_id() > 0 ? -1 : 1;
+
+        Acts::BoundSymMatrix cov = Acts::BoundSymMatrix::Zero();
+        cov(Acts::eBoundLoc0, Acts::eBoundLoc0) = m_covLoc0;
+        cov(Acts::eBoundLoc1, Acts::eBoundLoc1) = m_covLoc1;
+        cov(Acts::eBoundPhi, Acts::eBoundPhi) = m_covPhi;
+        cov(Acts::eBoundTheta, Acts::eBoundTheta) = m_covTheta;
+        cov(Acts::eBoundQOverP, Acts::eBoundQOverP) = m_covQOverP;
+        cov(Acts::eBoundTime, Acts::eBoundTime) = m_covTime;
+
+        // auto initialParameters = Acts::BoundTrackParameters(surface->getSharedPtr(), params, charge, cov);
+        auto initialParameters = Acts::CurvilinearTrackParameters(smearedPosition4, smearedDirection, smearedAbsoluteMomentum, charge, cov);
+
+        // write out
+        double initialCharge = initialParameters.charge();
+        double initialAbsoluteMomentum = initialParameters.absoluteMomentum();
+        Acts::Vector3 initialPosition = initialParameters.position(gctx);
+        Acts::Vector3 initialMomentum = initialParameters.momentum();
+        ATH_MSG_DEBUG("initial charge: " << initialCharge);
+        ATH_MSG_DEBUG("initial absolute momentum: " << initialAbsoluteMomentum);
+        ATH_MSG_DEBUG("initial position: x=" << initialPosition.x() << ", y=" << initialPosition.y() << ", z=" << initialPosition.z());
+        ATH_MSG_DEBUG("initial momentum: x=" << initialMomentum.x() << ", y=" << initialMomentum.y() << ", z=" << initialMomentum.z());
+        m_initialTrackParameters = std::make_shared<const Acts::CurvilinearTrackParameters>(initialParameters);
+      }
+    }
+  }
+
+  m_sourceLinks = std::make_shared<std::vector<IndexSourceLink>>(sourcelinks);
+  m_measurements = std::make_shared<std::vector<Measurement>>(measurements);
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode TruthTrackFinderTool::finalize() {
+  return StatusCode::SUCCESS;
+}
\ No newline at end of file
diff --git a/Tracking/Acts/FaserActsKalmanFilter/src/components/FaserActsKalmanFilter_entries.cxx b/Tracking/Acts/FaserActsKalmanFilter/src/components/FaserActsKalmanFilter_entries.cxx
index 0b86d895fdd266b4df4bb9ba6ecb3b928581c9db..0a79b99832495297ac3a3ebd419a1134c8f3a9e3 100755
--- a/Tracking/Acts/FaserActsKalmanFilter/src/components/FaserActsKalmanFilter_entries.cxx
+++ b/Tracking/Acts/FaserActsKalmanFilter/src/components/FaserActsKalmanFilter_entries.cxx
@@ -2,19 +2,41 @@
   Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
 */
 
-//#include "FaserActsKalmanFilter/FaserActsKalmanFilterAlg.h"
+#include "FaserActsKalmanFilter/FaserActsKalmanFilterAlg.h"
 #include "FaserActsKalmanFilter/CombinatorialKalmanFilterAlg.h"
-#include "FaserActsKalmanFilter/TruthBasedInitialParameterTool.h"
-#include "FaserActsKalmanFilter/SPSeedBasedInitialParameterTool.h"
-#include "FaserActsKalmanFilter/SPSimpleInitialParameterTool.h"
-#include "FaserActsKalmanFilter/TrajectoryWriterTool.h"
-#include "FaserActsKalmanFilter/SimWriterTool.h"
+//#include "FaserActsKalmanFilter/MultiTrackFinderTool.h"
+//#include "FaserActsKalmanFilter/TruthBasedInitialParameterTool.h"
+//#include "FaserActsKalmanFilter/TruthTrackFinderTool.h"
+//#include "FaserActsKalmanFilter/SPSeedBasedInitialParameterTool.h"
+//#include "FaserActsKalmanFilter/SPSimpleInitialParameterTool.h"
+//#include "FaserActsKalmanFilter/TrajectoryWriterTool.h"
+//#include "FaserActsKalmanFilter/SimWriterTool.h"
+//#include "FaserActsKalmanFilter/TruthSeededTrackFinderTool.h"
+//#include "FaserActsKalmanFilter/ProtoTrackWriterTool.h"
+#include "FaserActsKalmanFilter/RootTrajectoryStatesWriterTool.h"
+#include "FaserActsKalmanFilter/RootTrajectorySummaryWriterTool.h"
+//#include "FaserActsKalmanFilter/SegmentFitClusterTrackFinderTool.h"
+//#include "FaserActsKalmanFilter/SegmentFitTrackFinderTool.h"
+//#include "FaserActsKalmanFilter/ClusterTrackSeedTool.h"
+#include "FaserActsKalmanFilter/ThreeStationTrackSeedTool.h"
+#include "FaserActsKalmanFilter/PerformanceWriterTool.h"
 
 
-//DECLARE_COMPONENT(FaserActsKalmanFilterAlg)
+DECLARE_COMPONENT(FaserActsKalmanFilterAlg)
 DECLARE_COMPONENT(CombinatorialKalmanFilterAlg)
-DECLARE_COMPONENT(TruthBasedInitialParameterTool)
-DECLARE_COMPONENT(SPSeedBasedInitialParameterTool)
-DECLARE_COMPONENT(SPSimpleInitialParameterTool)
-DECLARE_COMPONENT(TrajectoryWriterTool)
-DECLARE_COMPONENT(SimWriterTool)
+//DECLARE_COMPONENT(TruthBasedInitialParameterTool)
+//DECLARE_COMPONENT(SPSeedBasedInitialParameterTool)
+//DECLARE_COMPONENT(SPSimpleInitialParameterTool)
+//DECLARE_COMPONENT(TrajectoryWriterTool)
+//DECLARE_COMPONENT(TruthTrackFinderTool)
+//DECLARE_COMPONENT(SimWriterTool)
+//DECLARE_COMPONENT(TruthSeededTrackFinderTool)
+//DECLARE_COMPONENT(ProtoTrackWriterTool)
+//DECLARE_COMPONENT(SegmentFitClusterTrackFinderTool)
+//DECLARE_COMPONENT(SegmentFitTrackFinderTool)
+DECLARE_COMPONENT(RootTrajectoryStatesWriterTool)
+DECLARE_COMPONENT(RootTrajectorySummaryWriterTool)
+//DECLARE_COMPONENT(MultiTrackFinderTool)
+//DECLARE_COMPONENT(ClusterTrackSeedTool)
+DECLARE_COMPONENT(ThreeStationTrackSeedTool)
+DECLARE_COMPONENT(PerformanceWriterTool)
diff --git a/Tracking/Acts/FaserActsKalmanFilter/test/CombinatorialKalmanFilterAlg.py b/Tracking/Acts/FaserActsKalmanFilter/test/CombinatorialKalmanFilterAlg.py
index eef29095e39bc064e2dde11149be503b0d0d79ea..887c03812bce040d5cdf43f0559a499cbbe02b89 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/test/CombinatorialKalmanFilterAlg.py
+++ b/Tracking/Acts/FaserActsKalmanFilter/test/CombinatorialKalmanFilterAlg.py
@@ -9,20 +9,19 @@ from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
 from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
 # from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg
 from TrackerPrepRawDataFormation.TrackerPrepRawDataFormationConfig import FaserSCT_ClusterizationCfg
-from TrackerSpacePointFormation.TrackerSpacePointFormationConfig import TrackerSpacePointFinderCfg
-from TrackerSeedFinder.TrackerSeedFinderConfig import TrackerSeedFinderCfg
+from TrackerSegmentFit.TrackerSegmentFitConfig import SegmentFitAlgCfg
 from FaserActsKalmanFilter.CombinatorialKalmanFilterConfig import CombinatorialKalmanFilterCfg
 
 log.setLevel(DEBUG)
 Configurable.configurableRun3Behavior = True
 
 ConfigFlags.Input.Files = ['my.RDO.pool.root']
-ConfigFlags.Output.ESDFileName = "myCKF.ESD.pool.root"
-ConfigFlags.Output.AODFileName = "myCKF.AOD.pool.root"
+ConfigFlags.Output.ESDFileName = "CKF.ESD.pool.root"
 ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-01"
-# ConfigFlags.GeoModel.FaserVersion = "FASER-01"
+ConfigFlags.GeoModel.FaserVersion = "FASER-01"
 ConfigFlags.GeoModel.Align.Dynamic = False
 ConfigFlags.Beam.NumberOfCollisions = 0.
+# ConfigFlags.TrackingGeometry.MaterialSource = "Input"
 ConfigFlags.lock()
 
 acc = MainServicesCfg(ConfigFlags)
@@ -30,9 +29,9 @@ acc.merge(PoolReadCfg(ConfigFlags))
 # acc.merge(PoolWriteCfg(ConfigFlags))
 
 acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags))
-acc.merge(TrackerSpacePointFinderCfg(ConfigFlags))
-acc.merge(TrackerSeedFinderCfg(ConfigFlags))
+acc.merge(SegmentFitAlgCfg(ConfigFlags))
 acc.merge(CombinatorialKalmanFilterCfg(ConfigFlags))
+# acc.getEventAlgo("CombinatorialKalmanFilterAlg").OutputLevel = VERBOSE
 
 # logging.getLogger('forcomps').setLevel(INFO)
 # acc.foreach_component("*").OutputLevel = INFO
@@ -42,6 +41,5 @@ acc.merge(CombinatorialKalmanFilterCfg(ConfigFlags))
 # acc.printConfig(withDetails=True)
 # ConfigFlags.dump()
 
-sc = acc.run(maxEvents=1000)
-
+sc = acc.run(maxEvents=-1)
 sys.exit(not sc.isSuccess())
diff --git a/Tracking/Acts/FaserActsKalmanFilter/test/FaserActsKalmanFilterAlg.py b/Tracking/Acts/FaserActsKalmanFilter/test/FaserActsKalmanFilterAlg.py
index 06975170f670f0fe8dec65b9a2d608aea6fc4c36..b2efe5323cf53e0220925994ab7b2d544462f81e 100644
--- a/Tracking/Acts/FaserActsKalmanFilter/test/FaserActsKalmanFilterAlg.py
+++ b/Tracking/Acts/FaserActsKalmanFilter/test/FaserActsKalmanFilterAlg.py
@@ -11,6 +11,7 @@ from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
 from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg
 from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
 from TrackerPrepRawDataFormation.TrackerPrepRawDataFormationConfig import FaserSCT_ClusterizationCfg
+from TrackerSegmentFit.TrackerSegmentFitConfig import SegmentFitAlgCfg
 from TrackerSpacePointFormation.TrackerSpacePointFormationConfig import TrackerSpacePointFinderCfg
 from TruthSeededTrackFinder.TruthSeededTrackFinderConfig import TruthSeededTrackFinderCfg
 from FaserActsKalmanFilter.FaserActsKalmanFilterConfig import FaserActsKalmanFilterCfg
@@ -20,9 +21,10 @@ log.setLevel(DEBUG)
 Configurable.configurableRun3Behavior = True
 
 # Configure
-ConfigFlags.Input.Files = ['my.RDO.pool.root']
-ConfigFlags.Output.ESDFileName = "tmp.root"
-ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-01"             # Always needed; must match FaserVersion
+ConfigFlags.Input.Files = ['../my.RDO.pool.root']
+ConfigFlags.Output.ESDFileName = "FaserActsKalmanFilter.ESD.root"
+ConfigFlags.Output.AODFileName = "FaserActsKalmanFilter.AOD.pool.root"
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-01"
 ConfigFlags.GeoModel.Align.Dynamic = False
 ConfigFlags.Beam.NumberOfCollisions = 0.
 #ConfigFlags.Concurrency.NumThreads = 1
@@ -34,20 +36,20 @@ acc.merge(PoolReadCfg(ConfigFlags))
 
 # Inner Detector
 acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags))
-acc.merge(TrackerSpacePointFinderCfg(ConfigFlags))
-acc.merge(TruthSeededTrackFinderCfg(ConfigFlags))
+acc.merge(SegmentFitAlgCfg(ConfigFlags))
+# acc.merge(TrackerSpacePointFinderCfg(ConfigFlags))
+# acc.merge(TruthSeededTrackFinderCfg(ConfigFlags))
 acc.merge(FaserActsKalmanFilterCfg(ConfigFlags))
+acc.getEventAlgo("FaserActsKalmanFilterAlg").OutputLevel = DEBUG
 
-logging.getLogger('forcomps').setLevel(VERBOSE)
-acc.foreach_component("*").OutputLevel = VERBOSE
-acc.foreach_component("*ClassID*").OutputLevel = INFO
-acc.getService("StoreGateSvc").Dump = True
-acc.getService("ConditionStore").Dump = True
-acc.printConfig(withDetails=True)
-ConfigFlags.dump()
+# logging.getLogger('forcomps').setLevel(VERBOSE)
+# acc.foreach_component("*").OutputLevel = VERBOSE
+# acc.foreach_component("*ClassID*").OutputLevel = INFO
+# acc.getService("StoreGateSvc").Dump = True
+# acc.getService("ConditionStore").Dump = True
+# acc.printConfig(withDetails=True)
+# ConfigFlags.dump()
 
 # Execute and finish
-sc = acc.run(maxEvents=-1)
-
-# Success should be 0
+sc = acc.run(maxEvents=1000)
 sys.exit(not sc.isSuccess())
diff --git a/Tracking/Acts/FaserActsKalmanFilter/test/TI12KalmanFilter.py b/Tracking/Acts/FaserActsKalmanFilter/test/TI12KalmanFilter.py
new file mode 100644
index 0000000000000000000000000000000000000000..11b7e4217bed8400426f4257ef5cf43f5b7adbfd
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/test/TI12KalmanFilter.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+import sys
+from AthenaCommon.Logging import log, logging
+from AthenaCommon.Constants import DEBUG, VERBOSE, INFO
+from AthenaCommon.Configurable import Configurable
+from CalypsoConfiguration.AllConfigFlags import ConfigFlags
+from AthenaConfiguration.TestDefaults import defaultTestFiles
+from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
+from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
+from AthenaPoolCnvSvc.PoolWriteConfig import PoolWriteCfg
+from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
+from FaserByteStreamCnvSvc.FaserByteStreamCnvSvcConfig import FaserByteStreamCnvSvcCfg
+from TrackerPrepRawDataFormation.TrackerPrepRawDataFormationConfig import FaserSCT_ClusterizationCfg
+from TrackerSegmentFit.TrackerSegmentFitConfig import SegmentFitAlgCfg
+from TrackerSpacePointFormation.TrackerSpacePointFormationConfig import TrackerSpacePointFinderCfg
+from FaserActsKalmanFilter.FaserActsKalmanFilterConfig import FaserActsKalmanFilterCfg
+
+
+log.setLevel(DEBUG)
+Configurable.configurableRun3Behavior = True
+
+# Configure
+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.DatabaseInstance = "OFLP200"
+ConfigFlags.Input.ProjectName = "data21"
+ConfigFlags.Input.isMC = False
+ConfigFlags.GeoModel.FaserVersion = "FASER-02"
+ConfigFlags.Common.isOnline = False
+ConfigFlags.GeoModel.Align.Dynamic = False
+ConfigFlags.Beam.NumberOfCollisions = 0.
+ConfigFlags.Detector.GeometryFaserSCT = True
+ConfigFlags.lock()
+
+# Core components
+acc = MainServicesCfg(ConfigFlags)
+acc.merge(FaserByteStreamCnvSvcCfg(ConfigFlags))
+acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags, name="LevelClustering", DataObjectName="SCT_LEVELMODE_RDOs", ClusterToolTimingPattern="X1X"))
+acc.merge(SegmentFitAlgCfg(ConfigFlags, name=f"LevelFit", MaxClusters=44))
+# acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags))
+# acc.merge(SegmentFitAlgCfg(ConfigFlags))
+# acc.merge(TrackerSpacePointFinderCfg(ConfigFlags))
+acc.merge(FaserActsKalmanFilterCfg(ConfigFlags))
+acc.getEventAlgo("FaserActsKalmanFilterAlg").OutputLevel = DEBUG
+
+replicaSvc = acc.getService("DBReplicaSvc")
+replicaSvc.COOLSQLiteVetoPattern = ""
+replicaSvc.UseCOOLSQLite = True
+replicaSvc.UseCOOLFrontier = False
+replicaSvc.UseGeomSQLite = True
+
+
+# logging.getLogger('forcomps').setLevel(VERBOSE)
+# acc.foreach_component("*").OutputLevel = VERBOSE
+# acc.foreach_component("*ClassID*").OutputLevel = INFO
+# acc.getService("StoreGateSvc").Dump = True
+# acc.getService("ConditionStore").Dump = True
+# acc.printConfig(withDetails=True)
+# ConfigFlags.dump()
+
+# Execute and finish
+sc = acc.run(maxEvents=-1)
+sys.exit(not sc.isSuccess())
diff --git a/Tracking/Acts/FaserActsKalmanFilter/test/TI12_CKF.py b/Tracking/Acts/FaserActsKalmanFilter/test/TI12_CKF.py
new file mode 100644
index 0000000000000000000000000000000000000000..98acb56f00ace95d54277726768250e9543b06ab
--- /dev/null
+++ b/Tracking/Acts/FaserActsKalmanFilter/test/TI12_CKF.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+
+import sys
+from AthenaCommon.Logging import log, logging
+from AthenaCommon.Constants import DEBUG, VERBOSE, INFO
+from AthenaCommon.Configurable import Configurable
+from CalypsoConfiguration.AllConfigFlags import ConfigFlags
+from CalypsoConfiguration.MainServicesConfig import MainServicesCfg
+from FaserByteStreamCnvSvc.FaserByteStreamCnvSvcConfig import FaserByteStreamCnvSvcCfg
+from TrackerPrepRawDataFormation.TrackerPrepRawDataFormationConfig import FaserSCT_ClusterizationCfg
+from TrackerSegmentFit.TrackerSegmentFitConfig import SegmentFitAlgCfg
+from FaserActsKalmanFilter.CombinatorialKalmanFilterConfig import CombinatorialKalmanFilterCfg
+
+log.setLevel(DEBUG)
+Configurable.configurableRun3Behavior = True
+
+ConfigFlags.Input.Files = ['/home/tboeckh/tmp/Faser-Physics-006470-00093.raw_middleStation.SPs']
+ConfigFlags.Output.ESDFileName = "CKF.ESD.pool.root"
+ConfigFlags.IOVDb.GlobalTag = "OFLCOND-FASER-02"
+ConfigFlags.GeoModel.FaserVersion = "FASER-02"
+ConfigFlags.IOVDb.DatabaseInstance = "OFLP200"
+ConfigFlags.Input.ProjectName = "data21"
+ConfigFlags.Input.isMC = False
+ConfigFlags.Common.isOnline = False
+ConfigFlags.GeoModel.Align.Dynamic = False
+ConfigFlags.Beam.NumberOfCollisions = 0.
+ConfigFlags.Detector.GeometryFaserSCT = True
+ConfigFlags.lock()
+
+acc = MainServicesCfg(ConfigFlags)
+acc.merge(FaserByteStreamCnvSvcCfg(ConfigFlags))
+acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags, name="LevelClustering", DataObjectName="SCT_LEVELMODE_RDOs", ClusterToolTimingPattern="X1X"))
+acc.merge(SegmentFitAlgCfg(ConfigFlags, name=f"LevelFit", MaxClusters=44))
+# acc.merge(FaserSCT_ClusterizationCfg(ConfigFlags, DataObjectName="SCT_RDOs", ClusterToolTimingPattern="XXX"))
+# acc.merge(SegmentFitAlgCfg(ConfigFlags))
+acc.merge(CombinatorialKalmanFilterCfg(ConfigFlags))
+acc.getEventAlgo("CombinatorialKalmanFilterAlg").OutputLevel = VERBOSE
+
+replicaSvc = acc.getService("DBReplicaSvc")
+replicaSvc.COOLSQLiteVetoPattern = ""
+replicaSvc.UseCOOLSQLite = True
+replicaSvc.UseCOOLFrontier = False
+replicaSvc.UseGeomSQLite = True
+
+logging.getLogger('forcomps').setLevel(VERBOSE)
+acc.foreach_component("*").OutputLevel = VERBOSE
+acc.foreach_component("*ClassID*").OutputLevel = INFO
+acc.getCondAlgo("FaserSCT_AlignCondAlg").OutputLevel = VERBOSE
+acc.getCondAlgo("FaserSCT_DetectorElementCondAlg").OutputLevel = VERBOSE
+acc.getService("StoreGateSvc").Dump = True
+acc.getService("ConditionStore").Dump = True
+acc.printConfig(withDetails=True)
+ConfigFlags.dump()
+
+sc = acc.run(maxEvents=-1)
+sys.exit(not sc.isSuccess())