diff --git a/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/SiSPSeededTrackFinderData/SiSpacePointForSeedITK.h b/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/SiSPSeededTrackFinderData/SiSpacePointForSeedITK.h
index be68fd5df794100406e5d534f8c0be1510cd6ad4..28d3ec7907d4a0e2f26ba7961516b28b18be237c 100644
--- a/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/SiSPSeededTrackFinderData/SiSpacePointForSeedITK.h
+++ b/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/SiSPSeededTrackFinderData/SiSpacePointForSeedITK.h
@@ -46,6 +46,11 @@ namespace InDet {
     void set(const Trk::SpacePoint*const&,const float*,const float*);
     void setQuality(float);
     void setParam(const float&);
+    void setDR(const float&);
+    void setDZDR(const float&);
+    void setEta(const float&);
+    void setScorePenalty(const float&);
+    void setPt(const float&);
 
     const Trk::SpacePoint* spacepoint{}              ; 
     const float&          x() const {return m_x;}
@@ -57,6 +62,11 @@ namespace InDet {
     const float&       covz() const {return m_covz;}
     const float&      param() const {return m_param;}
     const float&    quality() const {return m_q ;}
+    const float&       dzdr() const {return m_dzdr;}
+    const float&        eta() const {return m_eta;}
+    const float&         pt() const {return m_pt;}
+    const float&      scorePenalty() const {return m_scorePenalty;} /// penalty term in the seed score
+    const float&         dR() const {return m_dR;} /// distance between top and central SP
     const Trk::Surface* sur() const {return m_su;}
     const Trk::Surface* sun() const {return m_sn;}
 
@@ -72,6 +82,12 @@ namespace InDet {
     float m_covz{}; //
     float m_param{};
     float m_q{}   ;
+    float m_scorePenalty{}; /// penalty term in the seed score
+    float m_dR{};
+    float m_eta{} ;
+    float m_pt{}  ;
+    float m_dzdr{};
+
 
     float m_b0[3]{};
     float m_b1[3]{};
@@ -81,6 +97,7 @@ namespace InDet {
     const Trk::Surface* m_su{};
     const Trk::Surface* m_sn{};
   };
+  
 
 } // end of name space
 
diff --git a/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/SiSPSeededTrackFinderData/SiSpacePointsSeedMakerEventData.h b/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/SiSPSeededTrackFinderData/SiSpacePointsSeedMakerEventData.h
index b036fdd90f65ab2f87c93d3173b4c8fc327050ef..80531162ea40255bd1e00df46e9734b21e6ee0c9 100644
--- a/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/SiSPSeededTrackFinderData/SiSpacePointsSeedMakerEventData.h
+++ b/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/SiSPSeededTrackFinderData/SiSpacePointsSeedMakerEventData.h
@@ -23,6 +23,12 @@
 
 namespace InDet {
 
+  class FloatInt {
+  public:
+    float Fl;
+    int   In;
+  };
+
  /**
   @class InDet::SiSpacePointsSeedMakerEventData
   
@@ -95,7 +101,9 @@ namespace InDet {
     float zmaxB{0.};
     float ftrig{0.};
     float ftrigW{0.};
-    float maxScore{0.};    
+    float maxScore{0.};   
+    float RTmin{0.};
+    float RTmax{0.}; 
 
     /**
      * @name Beam geometry
@@ -136,6 +144,7 @@ namespace InDet {
     std::vector<float> X;
     std::vector<float> Y;
     std::vector<float> Er;    ///< error component on 1/tan(theta)==dz/dr from the position errors on the space-points
+    std::vector<FloatInt> Tn;
     //@}
 
     InDet::SiSpacePointsSeed seedOutput;
@@ -201,6 +210,7 @@ namespace InDet {
         SP_ITK.resize(newSize, nullptr);
         X.resize(newSize, 0.);
         Y.resize(newSize, 0.);
+        Tn.resize(newSize);
       } else {
         SP.resize(newSize, nullptr);
       }
diff --git a/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/src/SiSpacePointForSeedITK.cxx b/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/src/SiSpacePointForSeedITK.cxx
index b244b0bad3e8e8a99a86126eea70c55b58a4f8e0..f3501a90bada0861b1655553de74664a4bfaddd0 100644
--- a/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/src/SiSpacePointForSeedITK.cxx
+++ b/InnerDetector/InDetRecEvent/SiSPSeededTrackFinderData/src/SiSpacePointForSeedITK.cxx
@@ -28,6 +28,11 @@ namespace InDet {
     m_q     = 0.;
     m_su    = nullptr ;
     m_sn    = nullptr ;
+    m_scorePenalty = 0.; 
+    m_dR = 0.;
+    m_eta = 0.;
+    m_pt = 0.;
+    m_dzdr = 0;
     for(int i=0; i!=3; ++i) {m_b0[i]=0.; m_b1[i]=0.; m_dr[i]=0.; m_r0[i]=0.;}
   }
 
@@ -35,16 +40,21 @@ namespace InDet {
   (const SiSpacePointForSeedITK& sp) 
   {
     if(&sp!=this) {
-      spacepoint  = sp.spacepoint;
-      m_x         = sp.m_x       ;
-      m_y         = sp.m_y       ;
-      m_z         = sp.m_z       ;
-      m_r         = sp.m_r       ;
-      m_covr      = sp.m_covr    ;
-      m_covz      = sp.m_covz    ;
-      m_q         = sp.m_q       ;
-      m_su        = sp.m_su      ;
-      m_sn        = sp.m_sn      ;        
+      spacepoint     = sp.spacepoint        ;
+      m_x            = sp.m_x               ;
+      m_y            = sp.m_y               ;
+      m_z            = sp.m_z               ;
+      m_r            = sp.m_r               ;
+      m_covr         = sp.m_covr            ;
+      m_covz         = sp.m_covz            ;
+      m_q            = sp.m_q               ;
+      m_su           = sp.m_su              ;
+      m_sn           = sp.m_sn              ; 
+      m_scorePenalty = sp.m_scorePenalty    ; 
+      m_dR           = sp.m_dR              ;
+      m_eta          = sp.m_eta             ;
+      m_pt           = sp.m_pt              ;
+      m_dzdr         = sp.m_dzdr            ;       
       for(int i=0; i!=3; ++i) m_b0[i]=sp.m_b0[i];
       for(int i=0; i!=3; ++i) m_b1[i]=sp.m_b1[i];
       for(int i=0; i!=3; ++i) m_dr[i]=sp.m_dr[i];
@@ -123,6 +133,33 @@ namespace InDet {
     m_su = &sp->clusterList().first->detectorElement()->surface();
   } 
 
+
+  void SiSpacePointForSeedITK::setDR(const float& dr)
+  {
+    m_dR = dr;
+  }
+
+  void SiSpacePointForSeedITK::setEta(const float& eta)
+  {
+    m_eta = eta;
+  }
+   
+  void SiSpacePointForSeedITK::setDZDR(const float& dzdr)
+  {
+    m_dzdr = dzdr;
+  }
+ 
+  void SiSpacePointForSeedITK::setPt(const float& pt)
+  {
+    m_pt = pt;
+  }
+
+  void SiSpacePointForSeedITK::setScorePenalty(const float& score)
+  {
+    m_scorePenalty = score;
+  }
+
+
   /////////////////////////////////////////////////////////////////////////////////
   // Set with error correction 
   // sc[0] - barrel pixels error correction
diff --git a/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/CMakeLists.txt b/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/CMakeLists.txt
index e901b6a054ae2bcdc27249279ccfdcf6b16cae9f..e40f9bda22afe65df014bf52731179a40ea55192 100644
--- a/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/CMakeLists.txt
+++ b/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/CMakeLists.txt
@@ -1,5 +1,7 @@
 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
 
+# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+
 # Declare the package name:
 atlas_subdir( SiSpacePointsSeedTool_xk )
 
diff --git a/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/SiSpacePointsSeedTool_xk/SiSpacePointsSeedMaker_ITK.h b/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/SiSpacePointsSeedTool_xk/SiSpacePointsSeedMaker_ITK.h
index eaba5f80fa290fb4fac6ae2110d24904ce37f5b9..ddd97e1ed0bf4aeeaa2b5fe51db52ad53c7b989c 100644
--- a/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/SiSpacePointsSeedTool_xk/SiSpacePointsSeedMaker_ITK.h
+++ b/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/SiSpacePointsSeedTool_xk/SiSpacePointsSeedMaker_ITK.h
@@ -23,6 +23,14 @@
 #include "TrkSpacePoint/SpacePointOverlapCollection.h"
 #include "TrkEventUtils/PRDtoTrackMap.h"
 
+//for validation
+#include "GaudiKernel/ITHistSvc.h"
+#include "TFile.h"
+#include "TTree.h"
+
+
+
+
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // MagField cache
 #include "MagFieldConditions/AtlasFieldCacheCondObj.h"
@@ -38,6 +46,19 @@ class MsgStream;
 
 namespace InDet {
 
+  ///////////////////////////////////////////////////////////////////
+  // Object function for ordering space point in R coordinate order
+  ///////////////////////////////////////////////////////////////////
+  
+  class SiSpacePointsITKComparison_R {
+ 
+  public: 
+    bool operator () (InDet::SiSpacePointForSeedITK* s1,InDet::SiSpacePointForSeedITK* s2) {
+      return((*s1).radius() < (*s2).radius());
+    }
+  };
+
+
   using EventData = SiSpacePointsSeedMakerEventData;
 
   /**
@@ -112,7 +133,6 @@ namespace InDet {
     //@}
 
     virtual void writeNtuple(const SiSpacePointsSeed* seed, const Trk::Track* track, int seedType, long eventNumber) const override;
-
     virtual bool getWriteNtupleBoolProperty() const override;
 
     ///////////////////////////////////////////////////////////////////
@@ -124,14 +144,18 @@ namespace InDet {
 
   private:
     /// enum for array sizes
-    enum Size {SizeRF=53,
-               SizeZ=11,
-               SizeRFZ=SizeRF*SizeZ,
-               SizeI=9,
-               SizeRFV=100,
-               SizeZV=3,
-               SizeRFZV=SizeRFV*SizeZV,
-               SizeIV=6};
+    /// Note that this stores the maximum capacities, the actual binnings 
+    /// do not always use the full size. See data members below for the 
+    /// actual binning paramaters, which are determined in buildFramework. 
+    enum Size {arraySizePhi=200,     ///< capacity of the 1D phi arrays 
+                arraySizeZ=11,       ///< capacity of the 1D z arrays
+                arraySizePhiZ=arraySizePhi*arraySizeZ,   ///< capacity for the 2D phi-z arrays 
+                arraySizeNeighbourBins=9,  ///< array size to store neighbouring phi-z-regions in the seed finding
+                arraySizePhiV=100,         ///< array size in phi for vertexing 
+                arraySizeZV=3,             ///< array size in z for vertexing
+                arraySizePhiZV=arraySizePhiV*arraySizeZV,      ///< array size in phi-Z 2D for the vertexing
+                arraySizeNeighbourBinsVertex=6};       ///< array size to store neighbouring phi-z regions for the vertexing
+
 
     ///////////////////////////////////////////////////////////////////
     // Private data and methods
@@ -166,13 +190,35 @@ namespace InDet {
     FloatProperty m_zmin{this, "minZ", -250.};
     FloatProperty m_zmax{this , "maxZ", +250.};
     FloatProperty m_r_rmin{this, "radMin", 0.};
-    FloatProperty m_r_rstep{this, "radStep", 2.};
+    FloatProperty m_binSizeR{this, "radStep", 2.};
     FloatProperty m_dzver{this, "maxdZver", 5.};
     FloatProperty m_dzdrver{this, "maxdZdRver", 0.02};
-    FloatProperty m_diver{this, "maxdImpact", 10.};
-    FloatProperty m_diversss{this, "maxdImpactSSS", 20.};
+    FloatProperty m_maxdImpact{this, "maxdImpact", 10.};
+    FloatProperty m_maxdImpactSSS{this, "maxdImpactSSS", 20.};
     FloatProperty m_divermax{this, "maxdImpactForDecays", 20.};
     FloatProperty m_dzmaxPPP{this, "dZmaxForPPPSeeds", 600.};
+
+    FloatProperty m_maxScore{this, "maximumAcceptedSeedScore", 100.};
+    IntegerProperty m_maxOneSizeSSS{this, "maxSeedsForSpacePointStrips", 5};
+    IntegerProperty m_maxOneSizePPP{this, "maxSeedsForSpacePointPixels", 5};
+    BooleanProperty m_alwaysKeepConfirmedPixelSeeds{this, "alwaysKeepConfirmedPixelSeeds", false};
+    BooleanProperty m_alwaysKeepConfirmedStripSeeds{this, "alwaysKeepConfirmedStripSeeds", false};
+    BooleanProperty m_fastTracking{this, "useFastTracking", false};
+    FloatProperty m_seedScoreBonusPPP{this, "seedScoreBonusPPP", -200.};
+    FloatProperty m_seedScoreBonusSSS{this, "seedScoreBonusSSS", -400.};
+    BooleanProperty m_optimisePhiBinning{this, "optimisePhiBinning", true};
+    FloatProperty m_rminSSS{this, "radMinSSS", 400.};
+    FloatProperty m_rmaxSSS{this, "radMaxSSS", 1000.};
+    BooleanProperty m_isLRT{this, "isLRT", false};
+    FloatProperty m_drminPPP{this, "mindRadiusPPP", 6.};
+    FloatProperty m_drmaxPPP{this, "maxdRadiusPPP", 140.};
+    FloatProperty m_drminSSS{this, "mindRadiusSSS", 20.};
+    FloatProperty m_drmaxSSS{this, "maxdRadiusSSS", 3000.};
+    FloatProperty m_dImpactCutSlopeUnconfirmedSSS{this, "dImpactCutSlopeUnconfirmedSSS", 1.0};
+    FloatProperty m_dImpactCutSlopeUnconfirmedPPP{this, "dImpactCutSlopeUnconfirmedPPP", 0.};
+    FloatProperty m_seedScoreBonusConfirmationSeed{this, "seedScoreBonusConfirmationSeed", -200.};
+    BooleanProperty m_useSeedConfirmation{this, "useSeedConfirmation", false};
+    FloatProperty m_rminPPPFast{this, "m_rminPPPFast", 70.};
     //@}
 
     /// @name Properties, which will be updated in initialize
@@ -211,15 +257,15 @@ namespace InDet {
     //@{
     bool m_initialized{false};
     int m_outputlevel{0};
-    int m_r_size{0};
+    
     int m_fNmax{0};
     int m_fvNmax{0};
-    int m_rfz_b[SizeRFZ]{};
-    int m_rfz_t[SizeRFZ]{};
-    int m_rfz_ib[SizeRFZ][SizeI]{};
-    int m_rfz_it[SizeRFZ][SizeI]{};
-    int m_rfzv_n[SizeRFZV]{};
-    int m_rfzv_i[SizeRFZV][SizeIV]{};
+    int m_rfz_b[arraySizePhiZ]{};
+    int m_rfz_t[arraySizePhiZ]{};
+    int m_rfz_ib[arraySizePhiZ][arraySizeNeighbourBins]{};
+    int m_rfz_it[arraySizePhiZ][arraySizeNeighbourBins]{};
+    int m_rfzv_n[arraySizePhiZV]{};
+    int m_rfzv_i[arraySizePhiZV][arraySizeNeighbourBinsVertex]{};
     float m_dzdrmin0{0.};
     float m_dzdrmax0{0.};
     float m_ipt{0.};
@@ -227,8 +273,83 @@ namespace InDet {
     float m_COF{0.};
     float m_sF{0.};
     float m_sFv{0.};
+    float m_dzMaxFast   {200.};
+    float m_R2MaxFast   {2500.};        
+    float m_zmaxPPP     {2700.};
+    float m_rmaxPPP     {140.};   
+    float m_zmaxSSS     {2700.};
+    float m_dzmaxSSS    {900.};    
+    float m_drminSeedConf{5.};
+    //@}
+
+     /// @name Properties of writeNTuple for validation purpose
+    //@{
+    Gaudi::Property<bool> m_writeNtuple {this, "WriteNtuple", false, "Flag to write Validation Ntuples"};
+    ///Flag to write validation ntuples. Turned off by default
+    ITHistSvc* m_thistSvc;
+    TTree* m_outputTree;
+    mutable std::mutex m_mutex;
+    mutable std::string          m_treeName               ATLAS_THREAD_SAFE;
+    mutable TString              m_treeFolder             ATLAS_THREAD_SAFE;
+    mutable float                  m_d0                   ATLAS_THREAD_SAFE = 0;
+    mutable float                  m_z0                   ATLAS_THREAD_SAFE = 0;
+    mutable float                  m_pt                   ATLAS_THREAD_SAFE = 0;
+    mutable float                  m_eta                  ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_x1                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_x2                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_x3                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_y1                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_y2                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_y3                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_z1                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_z2                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_z3                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_r1                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_r2                   ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_r3                   ATLAS_THREAD_SAFE = 0;
+    mutable float                  m_quality              ATLAS_THREAD_SAFE = 0;
+    mutable int                    m_type                 ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_dzdr_t               ATLAS_THREAD_SAFE = 0;
+    mutable double                 m_dzdr_b               ATLAS_THREAD_SAFE = 0;
+    mutable bool                   m_givesTrack           ATLAS_THREAD_SAFE = 0;
+    mutable float                  m_trackPt              ATLAS_THREAD_SAFE = 0;
+    mutable float                  m_trackEta             ATLAS_THREAD_SAFE = 0;
+    mutable long                   m_eventNumber          ATLAS_THREAD_SAFE = 0;
     //@}
 
+
+    /// @name Binning parameters 
+    int m_nBinsR{0};              ///<  number of bins in the radial coordinate 
+    int m_maxPhiBinPPP{0};           ///<  number of bins in phi 
+    float m_inverseBinSizePhiPPP{0};   ///<  cache the inverse bin size in phi which we use - needed to evaluate phi bin locations
+    int m_maxPhiBinSSS{0};
+    float m_inverseBinSizePhiSSS{0};
+
+    
+    /** Seed score thresholds defined based on the modifiers defined 
+      * as configurables above.
+      * These allow to categorise the seeds based on their quality score.
+      * The modifiers above are much larger than the range of the raw 
+      * unmodified scores would be, resulting in a grouping of the scores
+      * based on the modifiers applied. The thresholds are just below 
+      * the value reachable without the additional modifier
+    **/   
+    float m_seedScoreThresholdPPPConfirmationSeed{0.};    ///< max (score is assigned negative sign) score for PPP seeds with confirmation seed requirement. 
+    float m_seedScoreThresholdSSSConfirmationSeed{0.};    ///< max (score is assigned negative sign) score for SSS seeds with confirmation seed requirement. 
+
+
+    /// arrays associating bins to each other for SP formation
+    std::array<int,arraySizePhiZ> m_nNeighbourCellsBottomPPP;  ///< number of neighbouring phi-z bins to consider when looking for "bottom SP" candidates for each phi-z bin
+    std::array<int,arraySizePhiZ> m_nNeighbourCellsTopPPP;  ///< number of neighbouring phi-z bins to consider when looking for "top SP" candidates for each phi-z bin
+    std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ> m_neighbourCellsBottomPPP; ///< mapping of neighbour cells in the 2D phi-z binning to consider  for the "bottom SP" search for central SPs in each phi-z bin. Number of valid entries stored in m_nNeighboursPhiZbottom
+    std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ> m_neighbourCellsTopPPP; ///< mapping of neighbour cells in the 2D phi-z binning to consider  for the "top SP" search for central SPs in each phi-z bin. Number of valid entries stored in m_nNeighboursPhiZtop
+
+    std::array<int,arraySizePhiZ> m_nNeighbourCellsBottomSSS;
+    std::array<int,arraySizePhiZ> m_nNeighbourCellsTopSSS;
+    std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ> m_neighbourCellsBottomSSS;
+    std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ> m_neighbourCellsTopSSS;
+
+
     ///////////////////////////////////////////////////////////////////
     // Private methods
     ///////////////////////////////////////////////////////////////////
@@ -243,10 +364,44 @@ namespace InDet {
     MsgStream& dumpEvent(EventData& data, MsgStream& out) const;
 
     void buildFrameWork();
+
+    void buildConnectionMaps(std::array<int, arraySizePhiZ>& nNeighbourCellsBottom,
+			       std::array<int, arraySizePhiZ>& nNeighbourCellsTop,
+			       std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ>& neighbourCellsBottom,
+			       std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ>& neighbourCellsTop,
+			       int maxPhiBin, bool isSSS);
+
     void buildBeamFrameWork(EventData& data) const;
 
-    SiSpacePointForSeedITK* newSpacePoint
-    (EventData& data, const Trk::SpacePoint*const&) const;
+    /** Determine the expected azimuthal trajectory displacement
+      *  in phi in presence of the magnetic field for a particle 
+      *  with momentum pTmin and impact parameter maxd0, 
+      *  moving from a radial coordinate Rmin outward to Rmax.
+      *  This method is used to determine the optimal binning 
+      *  of the phi-z regions we consider in the seed making,
+      *  to ensure we contain the hits from our softest tracks
+      *  in a set of consecutive bins.
+      *  @param[in] pTmin: minimum pt cut applied in MeV
+      *  @param[in] maxD0: maximum d0 allowed 
+      *  @param[in] Rmin: starting radius for trajectory displacement
+      *  @param[in] Rmax: end radius for trajectory displacement
+    **/
+    float azimuthalStep(const float pTmin,const float maxd0,const float Rmin,const float Rmax) const;
+
+
+    /** Create a SiSpacePointForSeed from the space point. 
+      * This will also add the point to the data object's
+      * l_spforseed list and update its i_spforseed iterator 
+      * to point to the entry after the new SP 
+      * for further additions.
+      * Returns a nullptr if the SP fails the eta cut, 
+      * should we apply one 
+      * @param[in,out] data: Provides beam spot location, receives updates to the l_spforseed and i_spforseed members 
+      * @param[in] sp: Input space point. 
+    **/
+    SiSpacePointForSeedITK* newSpacePoint(EventData& data, const Trk::SpacePoint*const& sp) const;
+    SiSpacePointForSeedITK* newSpacePoint(EventData& data, const Trk::SpacePoint*const& sp, float* r, bool usePixSctInform=false) const;
+
     void newSeed
     (EventData& data,
      SiSpacePointForSeedITK*&,SiSpacePointForSeedITK*&,float) const;
@@ -262,31 +417,95 @@ namespace InDet {
 
     void fillSeeds(EventData& data) const;
     void fillLists(EventData& data) const;
+    void fillListsFast(const EventContext& ctx, EventData& data) const;
+    void pixInform(const Trk::SpacePoint* sp, float* r) const;
+    void sctInform(EventData& data,const Trk::SpacePoint* sp, float* r) const;
     void erase(EventData& data) const;
     void production2Sp(EventData& data) const;
     void production3Sp(EventData& data) const;
-    void production3SpSSS
-    (EventData& data,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     int,int,int&) const;
-    void production3SpPPP
-    (EventData& data,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     int,int,int&) const;
-    void production3SpTrigger
-    (EventData& data,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     std::list<InDet::SiSpacePointForSeedITK*>::iterator*,
-     int,int,int&) const;
+
+    /** \brief: Seed production from space points. 
+       * 
+       * This method will try to find 3-SP combinations within a 
+       * local phi-z region in the detector. 
+       * 
+       * The central SP of the seed will be taken from this region
+       * (technically via the first entry of the bottom candidate array, 
+       * which always points to the phi-z bin of interest itself). 
+       * 
+       * The top SP is allowed to come from the same or one of several close-by
+       * phi-Z bins, as is the bottom SP. 
+       * 
+       * All SP collections are expected to be internally sorted in the radial coordinate.
+       * 
+       * @param[in,out] data: Event data
+       * @param[in,out] iter_bottomCands: collection of iterators over SP collections for up to 9 phi-z cells to consider for the bottom space-point search 
+       * @param[in,out] iter_endBottomCands: collection of end-iterators over the 
+       * SP collections  for up to 9 phi-z cells to consider for the bottom space-point search 
+       * @param[in,out] iter_topCands: collection of iterators over SP collections for up to 9 phi-z cells to consider for the top space-point search 
+       * @param[in,out] iter_endTopCands: collection of end-iterators over the 
+       * SP collections  for up to 9 phi-z cells to consider for the top space-point search 
+       * @param[in] numberBottomCells: Number of bottom cells to consider. Determines how many entries in iter_(end)bottomCands are expected to be valid. 
+       * @param[in] numberTopCells: Number of top cells to consider.Determines how many entries in iter_(end)topCands are expected to be valid. 
+       * @param[out] nseed: Number of seeds found 
+       **/ 
+      void production3SpSSS
+      (EventData& data,
+      std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & iter_bottomCands,
+      std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & iter_endBottomCands,
+      std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & iter_topCands,
+      std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & iter_endTopCands,
+      const int numberBottomCells, const int numberTopCells, int& nseed) const;
+
+      void production3SpPPP
+      (EventData& data,
+      std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & iter_bottomCands,
+      std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & iter_endBottomCands,
+      std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & iter_topCands,
+      std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & iter_endTopCands,
+      const int numberBottomCells, const int numberTopCells, int& nseed) const;
+
+      /// as above, but for the trigger 
+      void production3SpTrigger
+      (EventData& /*data*/,
+       std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & /*rb*/,
+       std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & /*rbe*/,
+       std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & /*rt*/,
+       std::array<std::list<InDet::SiSpacePointForSeedITK*>::iterator, arraySizeNeighbourBins> & /*rte*/,
+       const int /*numberBottomCells*/, const int /*numberTopCells*/, int& /*nseed*/) const;
+
+    /** This creates all possible seeds with the passed central and bottom SP, using all top SP 
+       * candidates which are stored in the data.CmSp member.  Seeds are scored by a quality score 
+       * seeded by abs(d0), and modified if there is a second-seed confirmation or in case of PPP/SSS 
+       * topologies. Then, they are written out via the newOneSeed method. 
+       * @param[in] SPb Bottom Space point for the seed creation
+       * @param[in] SP0 Central Space point for the seed creation
+       * @param[in] Zob z0 estimate 
+    **/ 
+    void newOneSeedWithCurvaturesComparisonSSS
+      (EventData& data, SiSpacePointForSeedITK*& SPb, SiSpacePointForSeedITK*& SP0, float Zob) const;
+    void newOneSeedWithCurvaturesComparisonPPP
+      (EventData& data, SiSpacePointForSeedITK*& SPb, SiSpacePointForSeedITK*& SP0, float Zob) const;
+    void newOneSeedWithCurvaturesComparisonSeedConfirmation
+      (EventData& data, SiSpacePointForSeedITK*& SPb, SiSpacePointForSeedITK*& SP0, float Zob) const;
+
  
+     /** Helper method to determine if a seed 
+       * is 'confirmed' - this means that a second
+       * seed exists with compatible curvature, 
+       * the same bottom and central SP, but a 
+       * different third SP. This information 
+       * is stored in a modification of the 
+       * seed quality, which we check here. 
+       * @param[in] bottomSP: bottom space point
+       * @param[in] topSP: top space point 
+       * @param[in] quality: seed quality
+       * @return true if the seed is confirmed, false otherwise 
+    **/ 
+    bool isConfirmedSeed(const InDet::SiSpacePointForSeedITK* bottomSP, const InDet::SiSpacePointForSeedITK* topSP, float quality) const;
+
+
+    void sort(std::vector<FloatInt>& s, int start, int size) const;
     bool newVertices(EventData& data, const std::list<Trk::Vertex>&) const;
     void findNext(EventData& data) const;
     bool isZCompatible(EventData& data, float&,float&,float&) const;
@@ -329,7 +548,20 @@ namespace InDet {
 
     return false;
   }
-}
 
+///////////////////////////////////////////////////////////////////
+  // The procedure sorts the elements into ascending order.
+  ///////////////////////////////////////////////////////////////////
+  
+  inline
+  void SiSpacePointsSeedMaker_ITK::sort(std::vector<FloatInt>& s, int start, int size) const
+  {
+    //QuickSort for fast tracking currently buggy
+    //TBC if really faster than std::sort
+    //Using std::sort in all cases for now
+    std::sort(s.begin()+start,s.begin()+start+size,[](const FloatInt a,const FloatInt b)->bool {return a.Fl < b.Fl;});
+  }
+
+}
 
 #endif // SiSpacePointsSeedMaker_ITK_H
diff --git a/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/src/SiSpacePointsSeedMaker_ITK.cxx b/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/src/SiSpacePointsSeedMaker_ITK.cxx
index 5bb089e57196c1681ba098d1601f32a4906c0602..8e4f02d77b81e0d629d0074125c7a2c12ebebf67 100644
--- a/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/src/SiSpacePointsSeedMaker_ITK.cxx
+++ b/InnerDetector/InDetRecTools/SiSpacePointsSeedTool_xk/src/SiSpacePointsSeedMaker_ITK.cxx
@@ -1,6 +1,6 @@
 /*
-  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
-*/
+    Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
+  */
 
 ///////////////////////////////////////////////////////////////////
 //   Implementation file for class SiSpacePointsSeedMaker_ITK
@@ -16,19 +16,33 @@
 
 #include "InDetPrepRawData/SiCluster.h"
 
+
+//for validation
+#include "TrkTrack/Track.h"
+#include "TrkParameters/TrackParameters.h"
+
 #include <cmath>
 
 #include <iomanip>
 #include <ostream>
 
+
 ///////////////////////////////////////////////////////////////////
 // Constructor
 ///////////////////////////////////////////////////////////////////
 
-InDet::SiSpacePointsSeedMaker_ITK::SiSpacePointsSeedMaker_ITK
-(const std::string& t, const std::string& n, const IInterface* p)
-  : base_class(t, n, p)
+InDet::SiSpacePointsSeedMaker_ITK::SiSpacePointsSeedMaker_ITK(const std::string &t, const std::string &n, const IInterface *p)
+    : base_class(t, n, p),
+     m_thistSvc(nullptr),
+    m_outputTree(nullptr),
+    m_treeName(""),
+    m_treeFolder("/valNtuples/")
 {
+  if (m_maxOneSize > 0)
+  {
+    m_maxOneSizeSSS = m_maxOneSize;
+    m_maxOneSizePPP = m_maxOneSize;
+  }
 }
 
 ///////////////////////////////////////////////////////////////////
@@ -47,29 +61,68 @@ StatusCode InDet::SiSpacePointsSeedMaker_ITK::initialize()
   //
   ATH_CHECK(m_beamSpotKey.initialize());
 
-  ATH_CHECK( m_fieldCondObjInputKey.initialize());
+  ATH_CHECK(m_fieldCondObjInputKey.initialize());
 
   // PRD-to-track association (optional)
-  ATH_CHECK( m_prdToTrackMap.initialize( !m_prdToTrackMap.key().empty()));
+  ATH_CHECK(m_prdToTrackMap.initialize(!m_prdToTrackMap.key().empty()));
 
-  if (m_r_rmax < 1100.) m_r_rmax = 1100.;
-  
   // Build framework
   //
   buildFrameWork();
 
   // Get output print level
   //
-  m_outputlevel = msg().level()-MSG::DEBUG;
-  if (m_outputlevel<=0) {
+  m_outputlevel = msg().level() - MSG::DEBUG;
+
+   if (msgLvl(MSG::DEBUG)) { 
     EventData data;
     initializeEventData(data);
     data.nprint=0;
-    dump(data, msg(MSG::DEBUG));
+    dump(data, msg(MSG::DEBUG)); 
+  }
+
+  m_umax = 100. - std::abs(m_umax) * 300.;
+
+  if (m_writeNtuple) {
+
+    ATH_CHECK( service("THistSvc",m_thistSvc)  );
+
+    m_treeName = (std::string("SeedTree_")+name());
+    std::replace( m_treeName.begin(), m_treeName.end(), '.', '_' );
+
+    m_outputTree = new TTree( m_treeName.c_str() , "SeedMakerValTool");
+
+    m_outputTree->Branch("eventNumber",    &m_eventNumber);
+    m_outputTree->Branch("d0",             &m_d0);
+    m_outputTree->Branch("z0",             &m_z0);
+    m_outputTree->Branch("pt",             &m_pt);
+    m_outputTree->Branch("eta",            &m_eta);
+    m_outputTree->Branch("x1",             &m_x1);
+    m_outputTree->Branch("x2",             &m_x2);
+    m_outputTree->Branch("x3",             &m_x3);
+    m_outputTree->Branch("y1",             &m_y1);
+    m_outputTree->Branch("y2",             &m_y2);
+    m_outputTree->Branch("y3",             &m_y3);
+    m_outputTree->Branch("z1",             &m_z1);
+    m_outputTree->Branch("z2",             &m_z2);
+    m_outputTree->Branch("z3",             &m_z3);
+    m_outputTree->Branch("r1",             &m_r1);
+    m_outputTree->Branch("r2",             &m_r2);
+    m_outputTree->Branch("r3",             &m_r3);
+    m_outputTree->Branch("quality",        &m_quality);
+    m_outputTree->Branch("seedType",       &m_type);
+    m_outputTree->Branch("givesTrack",     &m_givesTrack);
+    m_outputTree->Branch("dzdr_b",         &m_dzdr_b);
+    m_outputTree->Branch("dzdr_t",         &m_dzdr_t);
+    m_outputTree->Branch("track_pt",       &m_trackPt);
+    m_outputTree->Branch("track_eta",      &m_trackEta);
+
+    TString fullTreeName = m_treeFolder + m_treeName;
+
+    ATH_CHECK(  m_thistSvc->regTree( fullTreeName.Data(), m_outputTree )  );
+
   }
-  m_umax = 100.-std::abs(m_umax)*300.;
 
-  m_initialized = true;
 
   return sc;
 }
@@ -84,157 +137,287 @@ StatusCode InDet::SiSpacePointsSeedMaker_ITK::finalize()
 }
 
 ///////////////////////////////////////////////////////////////////
-// Initialize tool for new event 
+// Initialize tool for new event
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::newEvent(const EventContext& ctx, EventData& data, int iteration) const
+void InDet::SiSpacePointsSeedMaker_ITK::newEvent(const EventContext &ctx, EventData &data, int iteration) const
 {
-  if (not data.initialized) initializeEventData(data);
 
-  data.iteration0 = iteration;
+  /// if not done so, book the arrays etc inside the event data object
+  if (not data.initialized)
+    initializeEventData(data);
+
   data.trigger = false;
-  if (!m_pixel && !m_sct) return;
+  if (!m_pixel && !m_sct)
+    return;
 
-  iteration <=0 ? data.iteration = 0 : data.iteration = iteration;
+  /// pass the iteration info into our data object
+  data.iteration = iteration;
+  if (iteration <= 0)
+    data.iteration = 0;
+  /// Erase any existing entries in the data object
   erase(data);
+
   data.dzdrmin = m_dzdrmin0;
   data.dzdrmax = m_dzdrmax0;
+  data.maxScore = m_maxScore; ///< max score, where low scores are "better".
+
+  /// in the first iteration, initialise the beam framework - beam spot position and direction
+  if (data.iteration == 0)
+  {
 
-  if (!data.iteration) {
     buildBeamFrameWork(data);
 
-    double f[3], gP[3] ={10.,10.,0.};
+    /// Read the field information
+    double magField[3]{0, 0, 0};
+    double globalPos[3] = {10., 10., 0.};
 
-    MagField::AtlasFieldCache    fieldCache;
+    MagField::AtlasFieldCache fieldCache;
 
-    // Get field cache object
     SG::ReadCondHandle<AtlasFieldCacheCondObj> readHandle{m_fieldCondObjInputKey, ctx};
-    const AtlasFieldCacheCondObj* fieldCondObj{*readHandle};
-    if (fieldCondObj == nullptr) {
+    const AtlasFieldCacheCondObj *fieldCondObj{*readHandle};
+    if (fieldCondObj == nullptr)
+    {
       ATH_MSG_ERROR("SiSpacePointsSeedMaker_ITK: Failed to retrieve AtlasFieldCacheCondObj with key " << m_fieldCondObjInputKey.key());
       return;
     }
-    fieldCondObj->getInitializedCache (fieldCache);
-
-    if (fieldCache.solenoidOn()) {
-      fieldCache.getFieldZR(gP, f);
-
-      data.K = 2./(300.*f[2]);
-    } else {
-      data.K = 2./(300.* 5. );
+    fieldCondObj->getInitializedCache(fieldCache);
+
+    if (fieldCache.solenoidOn())
+    {
+      /// retrieve field
+      fieldCache.getFieldZR(globalPos, magField);
+      /** 
+        * Knowing the field (note that the field cache returns the field in units of kiloTesla!) 
+        * allows to set the circle-radius to pT conversion factor.
+        * 
+        * See for example ATLAS-CONF-2010-072 
+        * R[mm] =pT[GeV] / (3·10−4×B[T]) =  pT[MeV] / (300 *Bz[kT])
+        * 
+        * We actually estimate the circle diameter, 2R, in the seeding. 
+        * So what we want is: 2R = pT[MeV] x 2  / (300 x Bz) = K x pT[MeV]. 
+        **/
+      data.K = 2. / (300. * magField[2]);
     }
-
-    data.ipt2K = m_ipt2/(data.K*data.K);
-    data.ipt2C = m_ipt2*m_COF;
-    data.COFK  = m_COF*(data.K*data.K);
+    else
+    {
+      data.K = 2. / (300. * 5.);
+    }
+    /** helper variables allowing us to directly apply our pt cut on the variables
+      * available at seed level. 
+      * ipt2K is 1 / (K * 0.9 * pt cut)² 
+      **/
+    data.ipt2K = m_ipt2 / (data.K * data.K);
+    /// related to the mysterious magic number, m_COF{134*.05*9}
+    data.ipt2C = m_ipt2 * m_COF;
+    data.COFK = m_COF * (data.K * data.K);
+
+    /// set the spacepoint iterator to the beginning of the space-point list
     data.i_spforseed_ITK = data.l_spforseed_ITK.begin();
-  } else {
-    data.r_first = 0;
+    // Set the seed multiplicity strategy of the event data to the one configured
+    // by the user for strip seeds
+    data.maxSeedsPerSP = m_maxOneSizeSSS;
+    data.keepAllConfirmedSeeds = m_alwaysKeepConfirmedStripSeeds;
+  } ///< end if-statement for iteration 0
+  else
+  {                   /// for the second iteration (PPP pass), don't redo the full init required the first time
+    data.r_first = 0; ///< reset the first radial bin
+    // Set the seed multiplicity strategy of the event data to the one configured
+    // by the user for pixel seeds
+    data.maxSeedsPerSP = m_maxOneSizePPP;
+    data.keepAllConfirmedSeeds = m_alwaysKeepConfirmedPixelSeeds;
+
+    /// call fillLists to repopulate the candidate space points and exit
     fillLists(data);
     return;
   }
 
+  if (m_fastTracking)
+  {
+    fillListsFast(ctx, data);
+    return;
+  }
+
+  /// the following will only happen in the first iteration
   data.checketa = data.dzdrmin > 1.;
 
-  float irstep = 1.f/m_r_rstep;
-  int   irmax  = m_r_size-1;
-  for (int i=0; i<data.nr; ++i) {
+  /// build the r-binning.
+  float oneOverBinSizeR = 1. / m_binSizeR;
+  int maxBinR = m_nBinsR - 1;
+
+  /** This cleans up remaining entries in the data object. 
+    * In standard execution, we only run this in the first 
+    * iterations on a newly created data object, 
+    * in which case this loop will not ever be entered. 
+    * Leaving it in place for nonstandard use cases. 
+    **/
+  for (int i = 0; i < data.nr; ++i)
+  {
     int n = data.r_index[i];
     data.r_map[n] = 0;
     data.r_Sorted_ITK[n].clear();
   }
   data.ns = data.nr = 0;
 
-  SG::ReadHandle<Trk::PRDtoTrackMap>  prd_to_track_map;
+  SG::ReadHandle<Trk::PRDtoTrackMap> prd_to_track_map;
   const Trk::PRDtoTrackMap *prd_to_track_map_cptr = nullptr;
-  if (!m_prdToTrackMap.key().empty()) {
-    prd_to_track_map=SG::ReadHandle<Trk::PRDtoTrackMap>(m_prdToTrackMap, ctx);
-    if (!prd_to_track_map.isValid()) {
+  if (!m_prdToTrackMap.key().empty())
+  {
+    prd_to_track_map = SG::ReadHandle<Trk::PRDtoTrackMap>(m_prdToTrackMap, ctx);
+    if (!prd_to_track_map.isValid())
+    {
       ATH_MSG_ERROR("Failed to read PRD to track association map: " << m_prdToTrackMap.key());
     }
     prd_to_track_map_cptr = prd_to_track_map.cptr();
   }
 
-  // Get pixels space points containers from store gate 
-  //
+  /////////////////////////////
+  /// Now, we will populate the space point list in the event data object once.
+  /////////////////////////////
+
+  /// reset the first r index
   data.r_first = 0;
-  if (m_pixel) {
 
+  /// Get pixels space points containers from store gate
+  if (m_pixel)
+  {
     SG::ReadHandle<SpacePointContainer> spacepointsPixel{m_spacepointsPixel, ctx};
-    if (spacepointsPixel.isValid()) {
-
-      for (const SpacePointCollection* spc: *spacepointsPixel) {
-        for (const Trk::SpacePoint* sp: *spc) {
-
-	  if ((prd_to_track_map_cptr && isUsed(sp,*prd_to_track_map_cptr)) || sp->r() > m_r_rmax || sp->r() < m_r_rmin ) continue;
-
-	  InDet::SiSpacePointForSeedITK* sps = newSpacePoint(data, sp);
-          if (!sps) continue;
-
-	  int ir = static_cast<int>(sps->radius()*irstep);
-          if (ir>irmax) ir = irmax;
-	  data.r_Sorted_ITK[ir].push_back(sps);
-          ++data.r_map[ir];
-	  if (data.r_map[ir]==1) data.r_index[data.nr++] = ir;
-	  if (ir > data.r_first) data.r_first = ir;
-	  ++data.ns;
-	}
-      }
-    }
-    ++data.r_first;
-  }
-
-  // Get sct space points containers from store gate
-  //
-  if (m_sct) {
+    if (spacepointsPixel.isValid())
+    {
+      /// loop over the pixel space points
+      for (const SpacePointCollection *spc : *spacepointsPixel)
+      {
+        for (const Trk::SpacePoint *sp : *spc)
+        {
+
+          /// if we use the PRD to track map and this SP has already been used in a track, bail out
+          /// also skip any SP outside the r binning
+          if ((prd_to_track_map_cptr && isUsed(sp, *prd_to_track_map_cptr)) || sp->r() > m_r_rmax || sp->r() < m_r_rmin)
+            continue;
+
+          /// Remove DBM space points
+          ///
+          const InDetDD::SiDetectorElement *de =
+              static_cast<const InDetDD::SiDetectorElement *>(sp->clusterList().first->detectorElement());
+          if (!de || de->isDBM())
+            continue;
+
+          /** create a SiSpacePointForSeed from the space point. 
+            * This will also add the point to the l_spforseed list and update 
+            * the i_spforseed iterator
+            **/
+          InDet::SiSpacePointForSeedITK *sps = newSpacePoint(data, sp);
+          /// this can occur if we fail the eta cut
+          if (!sps)
+            continue;
+
+          /// determine the r-bin of this SP.
+          /// done by dividing the radius by the bin size.
+          int radiusBin = static_cast<int>(sps->radius() * oneOverBinSizeR);
+          /// catch outliers
+          if (radiusBin > maxBinR)
+            radiusBin = maxBinR;
+
+          /// now add the SP to the r-binned vector
+          data.r_Sorted_ITK[radiusBin].push_back(sps);
+          /// increment the counter for this bin
+          ++data.r_map[radiusBin];
+          /// if this is the first time we see this bin in use, we update the index map for this bin
+          /// to the radius bin index
+          if (data.r_map[radiusBin] == 1)
+            data.r_index[data.nr++] = radiusBin;
+          /// if this is the highest bin we saw so far, update the r_first member of the data object to this bin
+          if (radiusBin > data.r_first)
+            data.r_first = radiusBin;
+          /// update the space point counter
+          ++data.ns;
+        }           ///< end loop over space points in collection
+      }             ///< end loop over pixel SP collections
+    }               ///< end if-statement on valid pixel SP container
+    ++data.r_first; ///< increment r_first past the last occupied bin we saw
+  }                 ///< end pixel case
+
+  /// Get sct space points containers from store gate
+  if (m_sct)
+  {
 
     SG::ReadHandle<SpacePointContainer> spacepointsSCT{m_spacepointsSCT, ctx};
-    if (spacepointsSCT.isValid()) {
-
-      for (const SpacePointCollection* spc: *spacepointsSCT) {
-        for (const Trk::SpacePoint* sp: *spc) {
-
-	  if ((prd_to_track_map_cptr && isUsed(sp,*prd_to_track_map_cptr)) || sp->r() > m_r_rmax || sp->r() < m_r_rmin ) continue;
-
-	  InDet::SiSpacePointForSeedITK* sps = newSpacePoint(data, sp);
-          if (!sps) continue;
-
-	  int ir = static_cast<int>(sps->radius()*irstep);
-          if (ir>irmax) ir = irmax;
-	  data.r_Sorted_ITK[ir].push_back(sps);
-          ++data.r_map[ir];
-	  if (data.r_map[ir]==1) data.r_index[data.nr++] = ir;
-	  ++data.ns;
-	}
+    if (spacepointsSCT.isValid())
+    {
+
+      for (const SpacePointCollection *spc : *spacepointsSCT)
+      {
+        for (const Trk::SpacePoint *sp : *spc)
+        {
+          /// as for the pixel, veto already used SP if we are using the PRD to track map in later passes of track finding.
+          /// Also, veto SP outside the maximum and minimum radii
+          if ((prd_to_track_map_cptr && isUsed(sp, *prd_to_track_map_cptr)) || sp->r() > m_r_rmax || sp->r() < m_r_rmin)
+            continue;
+          /// create a space point and write it into the data object's list of points
+          InDet::SiSpacePointForSeedITK *sps = newSpacePoint(data, sp);
+          if (!sps)
+            continue;
+
+          /// as for PIX, determine the radial bin.
+          /// Note that for the SCT we do not update data.r_first.
+          int radiusBin = static_cast<int>(sps->radius() * oneOverBinSizeR);
+          if (radiusBin > maxBinR)
+            radiusBin = maxBinR;
+          /// again store the SP in the r-binned vectors
+          data.r_Sorted_ITK[radiusBin].push_back(sps);
+          /// update the count of SP in the given bin
+          ++data.r_map[radiusBin];
+          /// update the r_index map and data.nr if needed
+          if (data.r_map[radiusBin] == 1)
+            data.r_index[data.nr++] = radiusBin;
+          /// and increment the SP count too.
+          ++data.ns;
+        }
       }
     }
 
-    // Get sct overlap space points containers from store gate 
-    //
-    if (m_useOverlap) {
+    /// Get sct overlap space points containers from store gate
+    if (m_useOverlap && !data.checketa)
+    {
 
       SG::ReadHandle<SpacePointOverlapCollection> spacepointsOverlap{m_spacepointsOverlap, ctx};
-      if (spacepointsOverlap.isValid()) {
-	
-        for (const Trk::SpacePoint* sp: *spacepointsOverlap) {
-
-	  if ((prd_to_track_map_cptr && isUsed(sp,*prd_to_track_map_cptr)) || sp->r() > m_r_rmax || sp->r() < m_r_rmin) continue;
-
-	  InDet::SiSpacePointForSeedITK* sps = newSpacePoint(data, sp);
-          if (!sps) continue;
-
-	  int ir = static_cast<int>(sps->radius()*irstep);
-          if (ir>irmax) ir = irmax;
-	  data.r_Sorted_ITK[ir].push_back(sps);
-          ++data.r_map[ir];
-	  if (data.r_map[ir]==1) data.r_index[data.nr++] = ir;
-	  ++data.ns;
-	}
+      if (spacepointsOverlap.isValid())
+      {
+        for (const Trk::SpacePoint *sp : *spacepointsOverlap)
+        {
+          /// usual rejection of SP used in previous track finding passes if we run with the PRT to track map + check of the max and min radii
+          if ((prd_to_track_map_cptr && isUsed(sp, *prd_to_track_map_cptr)) || sp->r() > m_r_rmax || sp->r() < m_r_rmin)
+            continue;
+
+          /// SP creation, entry into list of the data object
+          InDet::SiSpacePointForSeedITK *sps = newSpacePoint(data, sp);
+          if (!sps)
+            continue;
+
+          /// radial bin determination
+          int radiusBin = static_cast<int>(sps->radius() * oneOverBinSizeR);
+          if (radiusBin > maxBinR)
+            radiusBin = maxBinR;
+          /// insert into the "histogram" vector
+          data.r_Sorted_ITK[radiusBin].push_back(sps);
+          /// update the counter for each bin content
+          ++data.r_map[radiusBin];
+          /// update the bin index list and occupied bin counter
+          if (data.r_map[radiusBin] == 1)
+            data.r_index[data.nr++] = radiusBin;
+          /// and the total SP count too.
+          ++data.ns;
+        }
       }
     }
   }
 
-  if (iteration < 0) data.r_first = 0;
+  /// negative iterations are not used in the current ITk reco
+  if (iteration < 0)
+    data.r_first = 0;
+
+  /// populates the phi-z sorted histograms using the spacepoint lists and r-binning.
+  /// after this call, we have a 3D binning
   fillLists(data);
 }
 
@@ -242,112 +425,151 @@ void InDet::SiSpacePointsSeedMaker_ITK::newEvent(const EventContext& ctx, EventD
 // Initialize tool for new region
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::newRegion
-(const EventContext& ctx, EventData& data,
- const std::vector<IdentifierHash>& vPixel, const std::vector<IdentifierHash>& vSCT) const
+void InDet::SiSpacePointsSeedMaker_ITK::newRegion(const EventContext &ctx, EventData &data,
+                                                  const std::vector<IdentifierHash> &vPixel, const std::vector<IdentifierHash> &vSCT) const
 {
-  if (not data.initialized) initializeEventData(data);
+  if (not data.initialized)
+    initializeEventData(data);
 
   data.iteration = 0;
   data.trigger = false;
   erase(data);
-  if (!m_pixel && !m_sct) return;
+  if (!m_pixel && !m_sct)
+    return;
 
   data.dzdrmin = m_dzdrmin0;
   data.dzdrmax = m_dzdrmax0;
+  data.maxScore = m_maxScore;
 
   buildBeamFrameWork(data);
 
-  double f[3], gP[3] ={10.,10.,0.};
+  double magField[3]{0, 0, 0};
+  double globalPos[3] = {10., 10., 0.};
 
-  MagField::AtlasFieldCache    fieldCache;
+  MagField::AtlasFieldCache fieldCache;
 
   // Get field cache object
   SG::ReadCondHandle<AtlasFieldCacheCondObj> readHandle{m_fieldCondObjInputKey, ctx};
-  const AtlasFieldCacheCondObj* fieldCondObj{*readHandle};
-  if (fieldCondObj == nullptr) {
+  const AtlasFieldCacheCondObj *fieldCondObj{*readHandle};
+  if (fieldCondObj == nullptr)
+  {
     ATH_MSG_ERROR("SiSpacePointsSeedMaker_ITK: Failed to retrieve AtlasFieldCacheCondObj with key " << m_fieldCondObjInputKey.key());
     return;
   }
-  fieldCondObj->getInitializedCache (fieldCache);
+  fieldCondObj->getInitializedCache(fieldCache);
 
-  if (fieldCache.solenoidOn()) {
-    fieldCache.getFieldZR(gP, f);
+  if (fieldCache.solenoidOn())
+  {
+    fieldCache.getFieldZR(globalPos, magField);
 
-    data.K = 2./(300.*f[2]);
-  } else {
-    data.K = 2./(300.* 5. );
+    data.K = 2. / (300. * magField[2]);
+  }
+  else
+  {
+    data.K = 2. / (300. * 5.);
   }
 
-  data.ipt2K = m_ipt2/(data.K*data.K);
-  data.ipt2C = m_ipt2*m_COF;
-  data.COFK = m_COF*(data.K*data.K);
+  data.ipt2K = m_ipt2 / (data.K * data.K);
+  data.ipt2C = m_ipt2 * m_COF;
+  data.COFK = m_COF * (data.K * data.K);
 
   data.i_spforseed_ITK = data.l_spforseed_ITK.begin();
 
-  float irstep = 1.f/m_r_rstep;
-  int   irmax  = m_r_size-1;
+  float oneOverBinSizeR = 1. / m_binSizeR; //was float irstep = 1.f/m_binSizeR;
+  int maxBinR = m_nBinsR - 1;              //was int   irmax  = m_nBinsR-1;
 
   data.r_first = 0;
   data.checketa = false;
 
-  for (int i=0; i<data.nr; ++i) {
+  for (int i = 0; i < data.nr; ++i)
+  {
     int n = data.r_index[i];
     data.r_map[n] = 0;
     data.r_Sorted_ITK[n].clear();
   }
   data.ns = data.nr = 0;
 
-  // Get pixels space points containers from store gate 
+  SG::ReadHandle<Trk::PRDtoTrackMap> prd_to_track_map;
+  const Trk::PRDtoTrackMap *prd_to_track_map_cptr = nullptr;
+  if (!m_prdToTrackMap.key().empty())
+  {
+    prd_to_track_map = SG::ReadHandle<Trk::PRDtoTrackMap>(m_prdToTrackMap, ctx);
+    if (!prd_to_track_map.isValid())
+    {
+      ATH_MSG_ERROR("Failed to read PRD to track association map: " << m_prdToTrackMap.key());
+    }
+    prd_to_track_map_cptr = prd_to_track_map.cptr();
+  }
+
+  // Get pixels space points containers from store gate
   //
-  if (m_pixel && !vPixel.empty()) {
+  if (m_pixel && !vPixel.empty())
+  {
 
     SG::ReadHandle<SpacePointContainer> spacepointsPixel{m_spacepointsPixel, ctx};
-    if (spacepointsPixel.isValid()) {
+    if (spacepointsPixel.isValid())
+    {
 
+      SpacePointContainer::const_iterator spce = spacepointsPixel->end();
       // Loop through all trigger collections
       //
-      for (const IdentifierHash& l: vPixel) {
-	const auto *w = spacepointsPixel->indexFindPtr(l);
-	if (w==nullptr) continue;
-        for (const Trk::SpacePoint* sp: *w) {
-	  float r = sp->r();
-          if (r > m_r_rmax || r < m_r_rmin) continue;
-	  InDet::SiSpacePointForSeedITK* sps = newSpacePoint(data, sp);
-	  int ir = static_cast<int>(sps->radius()*irstep);
-          if (ir>irmax) ir = irmax;
-	  data.r_Sorted_ITK[ir].push_back(sps);
+      for (const IdentifierHash &l : vPixel)
+      {
+        auto w = spacepointsPixel->indexFind(l);
+        if (w == spce)
+          continue;
+        for (const Trk::SpacePoint *sp : **w)
+        {
+          float r = sp->r();
+          if ((prd_to_track_map_cptr && isUsed(sp, *prd_to_track_map_cptr)) || r > m_r_rmax || r < m_r_rmin)
+            continue;
+          InDet::SiSpacePointForSeedITK *sps = newSpacePoint(data, sp);
+          int ir = static_cast<int>(sps->radius() * oneOverBinSizeR);
+          if (ir > maxBinR)
+            ir = maxBinR;
+          data.r_Sorted_ITK[ir].push_back(sps);
           ++data.r_map[ir];
-	  if (data.r_map[ir]==1) data.r_index[data.nr++] = ir;
-	  ++data.ns;
-	}
+          if (data.r_map[ir] == 1)
+            data.r_index[data.nr++] = ir;
+          ++data.ns;
+        }
       }
     }
   }
 
-  // Get sct space points containers from store gate 
+  // Get sct space points containers from store gate
   //
-  if (m_sct && !vSCT.empty()) {
+  if (m_sct && !vSCT.empty())
+  {
 
     SG::ReadHandle<SpacePointContainer> spacepointsSCT{m_spacepointsSCT, ctx};
-    if (spacepointsSCT.isValid()) {
+    if (spacepointsSCT.isValid())
+    {
+
+      SpacePointContainer::const_iterator spce = spacepointsSCT->end();
 
       // Loop through all trigger collections
       //
-      for (const IdentifierHash& l: vSCT) {
-	const auto *w = spacepointsSCT->indexFindPtr(l);
-	if (w==nullptr) continue;
-        for (const Trk::SpacePoint* sp: *w) {
-	  float r = sp->r();
-          if (r > m_r_rmax || r < m_r_rmin) continue;
-	  InDet::SiSpacePointForSeedITK* sps = newSpacePoint(data, sp);
-	  int ir = static_cast<int>(sps->radius()*irstep);
-          if (ir>irmax) ir = irmax;
-	  data.r_Sorted_ITK[ir].push_back(sps);
+      for (const IdentifierHash &l : vSCT)
+      {
+        auto w = spacepointsSCT->indexFind(l);
+        if (w == spce)
+          continue;
+        for (const Trk::SpacePoint *sp : **w)
+        {
+          float r = sp->r();
+          if ((prd_to_track_map_cptr && isUsed(sp, *prd_to_track_map_cptr)) || r > m_r_rmax || r < m_r_rmin)
+            continue;
+          InDet::SiSpacePointForSeedITK *sps = newSpacePoint(data, sp);
+          int ir = static_cast<int>(sps->radius() * oneOverBinSizeR);
+          if (ir > maxBinR)
+            ir = maxBinR;
+          data.r_Sorted_ITK[ir].push_back(sps);
           ++data.r_map[ir];
-	  if (data.r_map[ir]==1) data.r_index[data.nr++] = ir;
-	  ++data.ns;
-	}
+          if (data.r_map[ir] == 1)
+            data.r_index[data.nr++] = ir;
+          ++data.ns;
+        }
       }
     }
   }
@@ -358,62 +580,70 @@ void InDet::SiSpacePointsSeedMaker_ITK::newRegion
 // Initialize tool for new region
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::newRegion
-(const EventContext& ctx, EventData& data,
- const std::vector<IdentifierHash>& vPixel, const std::vector<IdentifierHash>& vSCT, const IRoiDescriptor& IRD) const
+void InDet::SiSpacePointsSeedMaker_ITK::newRegion(const EventContext &ctx, EventData &data,
+                                                  const std::vector<IdentifierHash> &vPixel, const std::vector<IdentifierHash> &vSCT, const IRoiDescriptor &IRD) const
 {
-  if (not data.initialized) initializeEventData(data);
+  constexpr float twoPi = 2. * M_PI;
+
+  if (not data.initialized)
+    initializeEventData(data);
 
   newRegion(ctx, data, vPixel, vSCT);
   data.trigger = true;
 
-  double dzdrmin = 1.f/std::tan(2.f*std::atan(std::exp(-IRD.etaMinus())));
-  double dzdrmax = 1.f/std::tan(2.f*std::atan(std::exp(-IRD.etaPlus ())));
- 
-  data.zminB        = IRD.zedMinus()-data.zbeam[0]; // min bottom Z
-  data.zmaxB        = IRD.zedPlus ()-data.zbeam[0]; // max bottom Z
-  data.zminU        = data.zminB+550.*dzdrmin;
-  data.zmaxU        = data.zmaxB+550.*dzdrmax;
-  double fmax    = IRD.phiPlus ();
-  double fmin    = IRD.phiMinus();
-  if (fmin > fmax) fmin-=(2.*M_PI);
-  data.ftrig        = (fmin+fmax)*.5;
-  data.ftrigW       = (fmax-fmin)*.5;
+  double dzdrmin = 1.f / std::tan(2.f * std::atan(std::exp(-IRD.etaMinus())));
+  double dzdrmax = 1.f / std::tan(2.f * std::atan(std::exp(-IRD.etaPlus())));
+
+  data.zminB = IRD.zedMinus() - data.zbeam[0]; // min bottom Z
+  data.zmaxB = IRD.zedPlus() - data.zbeam[0];  // max bottom Z
+  data.zminU = data.zminB + 550. * dzdrmin;
+  data.zmaxU = data.zmaxB + 550. * dzdrmax;
+  double fmax = IRD.phiPlus();
+  double fmin = IRD.phiMinus();
+  if (fmin > fmax)
+    fmin -= twoPi;
+  data.ftrig = (fmin + fmax) * .5;
+  data.ftrigW = (fmax - fmin) * .5;
 }
 
+
 ///////////////////////////////////////////////////////////////////
 // Methods to initilize different strategies of seeds production
 // with two space points with or without vertex constraint
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::find2Sp(EventData& data, const std::list<Trk::Vertex>& lv) const
+void InDet::SiSpacePointsSeedMaker_ITK::find2Sp(EventData &data, const std::list<Trk::Vertex> &lv) const
 {
-  if (not data.initialized) initializeEventData(data);
+  if (not data.initialized)
+    initializeEventData(data);
 
   data.zminU = m_zmin;
   data.zmaxU = m_zmax;
 
   int mode = 0;
-  if (lv.begin()!=lv.end()) mode = 1;
+  if (lv.begin() != lv.end())
+    mode = 1;
   bool newv = newVertices(data, lv);
-  
-  if (newv || !data.state || data.nspoint!=2 || data.mode!=mode || data.nlist) {
+
+  if (newv || !data.state || data.nspoint != 2 || data.mode != mode || data.nlist)
+  {
 
     data.i_seede_ITK = data.l_seeds_ITK.begin();
-    data.state   = 1;
+    data.state = 1;
     data.nspoint = 2;
-    data.nlist   = 0;
-    data.mode    = mode;
+    data.nlist = 0;
+    data.mode = mode;
     data.endlist = true;
-    data.fvNmin  = 0;
-    data.fNmin   = 0;
-    data.zMin    = 0;
+    data.fvNmin = 0;
+    data.fNmin = 0;
+    data.zMin = 0;
     production2Sp(data);
   }
   data.i_seed_ITK = data.l_seeds_ITK.begin();
-  
-  if (m_outputlevel<=0) {
-    data.nprint=1;
+
+  if (m_outputlevel <= 0)
+  {
+    data.nprint = 1;
     dump(data, msg(MSG::DEBUG));
   }
 }
@@ -423,34 +653,45 @@ void InDet::SiSpacePointsSeedMaker_ITK::find2Sp(EventData& data, const std::list
 // with three space points with or without vertex constraint
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::find3Sp(const EventContext&, EventData& data, const std::list<Trk::Vertex>& lv) const
+void InDet::SiSpacePointsSeedMaker_ITK::find3Sp(const EventContext &, EventData &data, const std::list<Trk::Vertex> &lv) const
 {
-  if (not data.initialized) initializeEventData(data);
+  if (not data.initialized)
+    initializeEventData(data);
 
+  /// reset the Z interval stored in the data object
   data.zminU = m_zmin;
   data.zmaxU = m_zmax;
-
+  /// mode 2 if we have no vertices in the list, otherwise mode 3
   int mode = 2;
-  if (lv.begin()!=lv.end()) mode = 3;
+  if (lv.begin() != lv.end())
+    mode = 3;
+  /** copy the vertices into the data object, if we have any.
+    *   Note that by construction, newv will ALWAYS be false, also if we 
+    *   pass vertices. 
+    **/
   bool newv = newVertices(data, lv);
-
-  if (newv || !data.state || data.nspoint!=3 || data.mode!=mode || data.nlist) {
+  /// update the data object's config
+  if (newv || !data.state || data.nspoint != 3 || data.mode != mode || data.nlist)
+  {
     data.i_seede_ITK = data.l_seeds_ITK.begin();
-    data.state   = 1;
+    data.state = 1;
     data.nspoint = 3;
-    data.nlist   = 0;
-    data.mode    = mode;
+    data.nlist = 0;
+    data.mode = mode;
     data.endlist = true;
-    data.fvNmin  = 0;
-    data.fNmin   = 0;
-    data.zMin    = 0;
-    production3Sp(data);
+    data.fvNmin = 0;
+    data.fNmin = 0;
+    data.zMin = 0;
+    production3Sp(data); ///< This performs the actual seed finding
   }
+
+  /// reset the i_seed_ITK iterator - this is used to return the seeds to the
+  /// consumer when they call next()
   data.i_seed_ITK = data.l_seeds_ITK.begin();
-  data.seed_ITK = data.seeds_ITK.begin();
 
-  if (m_outputlevel<=0) {
-    data.nprint=1;
+  if (msgLvl(MSG::DEBUG))
+  {
+    data.nprint = 1;
     dump(data, msg(MSG::DEBUG));
   }
 }
@@ -460,36 +701,51 @@ void InDet::SiSpacePointsSeedMaker_ITK::find3Sp(const EventContext&, EventData&
 // with three space points with or without vertex constraint
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::find3Sp(const EventContext&, EventData& data, const std::list<Trk::Vertex>& lv, const double* ZVertex) const
+void InDet::SiSpacePointsSeedMaker_ITK::find3Sp(const EventContext &, EventData &data, const std::list<Trk::Vertex> &lv, const double *ZVertex) const
 {
-  if (not data.initialized) initializeEventData(data);
 
+  if (not data.initialized)
+    initializeEventData(data);
+
+  /// Update the data object's Z interval based on the interval passed as arg to this
+  /// function.
   data.zminU = ZVertex[0];
-  if (data.zminU < m_zmin) data.zminU = m_zmin;
+  if (data.zminU < m_zmin)
+    data.zminU = m_zmin; ///< don't go beyond user-specified intervals
   data.zmaxU = ZVertex[1];
-  if (data.zmaxU > m_zmax) data.zmaxU = m_zmax;
+  if (data.zmaxU > m_zmax)
+    data.zmaxU = m_zmax; ///< don't go beyond user-specified intervals
 
+  /// mode 2 when working with the Z constraint
   int mode = 2;
-  if (lv.begin()!=lv.end()) mode = 3;
+  if (lv.begin() != lv.end())
+    mode = 3;
+  /** copy the vertices into the data object, if we have any.
+    *   Note that by construction, newv will ALWAYS be false, also if we 
+    *   pass vertices. 
+    **/
   bool newv = newVertices(data, lv);
-
-  if (newv || !data.state || data.nspoint!=3 || data.mode!=mode || data.nlist) {
+  /// update the data object's config
+  if (newv || !data.state || data.nspoint != 3 || data.mode != mode || data.nlist)
+  {
     data.i_seede_ITK = data.l_seeds_ITK.begin();
-    data.state   = 1;
+    data.state = 1;
     data.nspoint = 3;
-    data.nlist   = 0;
-    data.mode    = mode;
+    data.nlist = 0;
+    data.mode = mode;
     data.endlist = true;
-    data.fvNmin  = 0;
-    data.fNmin   = 0;
-    data.zMin    = 0;
-    production3Sp(data);
+    data.fvNmin = 0;
+    data.fNmin = 0;
+    data.zMin = 0;
+    production3Sp(data); ///< This performs the actual seed finding
   }
+  /// reset the i_seed_ITK iterator - this is used to return the seeds to the
+  /// consumer when they call next()
   data.i_seed_ITK = data.l_seeds_ITK.begin();
-  data.seed_ITK = data.seeds_ITK.begin();
 
-  if (m_outputlevel<=0) {
-    data.nprint=1;
+  if (msgLvl(MSG::DEBUG))
+  {
+    data.nprint = 1;
     dump(data, msg(MSG::DEBUG));
   }
 }
@@ -500,35 +756,38 @@ void InDet::SiSpacePointsSeedMaker_ITK::find3Sp(const EventContext&, EventData&
 // Variable means (2,3,4,....) any number space points
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::findVSp(const EventContext&, EventData& data, const std::list<Trk::Vertex>& lv) const
+void InDet::SiSpacePointsSeedMaker_ITK::findVSp(const EventContext &, EventData &data, const std::list<Trk::Vertex> &lv) const
 {
-  if (not data.initialized) initializeEventData(data);
+
+  if (not data.initialized)
+    initializeEventData(data);
 
   data.zminU = m_zmin;
   data.zmaxU = m_zmax;
 
   int mode = 5;
-  if (lv.begin()!=lv.end()) mode = 6;
+  if (lv.begin() != lv.end())
+    mode = 6;
   bool newv = newVertices(data, lv);
-  
-  if (newv || !data.state || data.nspoint!=4 || data.mode!=mode || data.nlist) {
 
+  if (newv || !data.state || data.nspoint != 4 || data.mode != mode || data.nlist)
+  {
     data.i_seede_ITK = data.l_seeds_ITK.begin();
-    data.state   = 1;
+    data.state = 1;
     data.nspoint = 4;
-    data.nlist   = 0;
-    data.mode    = mode;
+    data.nlist = 0;
+    data.mode = mode;
     data.endlist = true;
-    data.fvNmin  = 0;
-    data.fNmin   = 0;
-    data.zMin    = 0;
+    data.fvNmin = 0;
+    data.fNmin = 0;
+    data.zMin = 0;
     production3Sp(data);
   }
   data.i_seed_ITK = data.l_seeds_ITK.begin();
-  data.seed_ITK = data.seeds_ITK.begin();
 
-  if (m_outputlevel<=0) {
-    data.nprint=1;
+  if (msgLvl(MSG::DEBUG))
+  {
+    data.nprint = 1;
     dump(data, msg(MSG::DEBUG));
   }
 }
@@ -537,11 +796,13 @@ void InDet::SiSpacePointsSeedMaker_ITK::findVSp(const EventContext&, EventData&
 // Dumps relevant information into the MsgStream
 ///////////////////////////////////////////////////////////////////
 
-MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dump(EventData& data, MsgStream& out) const
+MsgStream &InDet::SiSpacePointsSeedMaker_ITK::dump(EventData &data, MsgStream &out) const
 {
-  if (not data.initialized) initializeEventData(data);
+  if (not data.initialized)
+    initializeEventData(data);
 
-  if (data.nprint) return dumpEvent(data, out);
+  if (data.nprint)
+    return dumpEvent(data, out);
   return dumpConditions(data, out);
 }
 
@@ -549,17 +810,24 @@ MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dump(EventData& data, MsgStream& o
 // Dumps conditions information into the MsgStream
 ///////////////////////////////////////////////////////////////////
 
-MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dumpConditions(EventData& data, MsgStream& out) const
+MsgStream &InDet::SiSpacePointsSeedMaker_ITK::dumpConditions(EventData &data, MsgStream &out) const
 {
   int n = 42-m_spacepointsPixel.key().size();
-  std::string s2; for (int i=0; i<n; ++i) s2.append(" "); s2.append("|");
+  std::string s2;
+  for (int i=0; i<n; ++i) s2.append(" ");
+  s2.append("|");
   n     = 42-m_spacepointsSCT.key().size();
-  std::string s3; for (int i=0; i<n; ++i) s3.append(" "); s3.append("|");
+  std::string s3;
+  for (int i=0; i<n; ++i) s3.append(" ");
+  s3.append("|");
   n     = 42-m_spacepointsOverlap.key().size();
-  std::string s4; for (int i=0; i<n; ++i) s4.append(" "); s4.append("|");
+  std::string s4;
+  for (int i=0; i<n; ++i) s4.append(" ");
+  s4.append("|");
   n     = 42-m_beamSpotKey.key().size();
-  std::string s5; for (int i=0; i<n; ++i) s5.append(" "); s5.append("|");
-
+  std::string s5;
+  for (int i=0; i<n; ++i) s5.append(" ");
+  s5.append("|");
 
   out<<"|---------------------------------------------------------------------|"
      <<endmsg;
@@ -586,17 +854,11 @@ MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dumpConditions(EventData& data, Ms
   out<<"| pTmin  (mev)            | "
      <<std::setw(12)<<std::setprecision(5)<<m_ptmin
      <<"                              |"<<endmsg;
-  out<<"| |rapidity|          <=  | " 
-     <<std::setw(12)<<std::setprecision(5)<<m_rapcut
-     <<"                              |"<<endmsg;
   out<<"| max radius SP           | "
      <<std::setw(12)<<std::setprecision(5)<<m_r_rmax 
      <<"                              |"<<endmsg;
-  out<<"| min radius SP           | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r_rmin 
-     <<"                              |"<<endmsg;
   out<<"| radius step             | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r_rstep
+     <<std::setw(12)<<std::setprecision(5)<<m_binSizeR
      <<"                              |"<<endmsg;
   out<<"| min Z-vertex position   | "
      <<std::setw(12)<<std::setprecision(5)<<m_zmin
@@ -604,42 +866,18 @@ MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dumpConditions(EventData& data, Ms
   out<<"| max Z-vertex position   | "
      <<std::setw(12)<<std::setprecision(5)<<m_zmax
      <<"                              |"<<endmsg;
-  out<<"| min radius first  SP(3) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r1min
-     <<"                              |"<<endmsg;
-  out<<"| min radius second SP(3) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r2min
-     <<"                              |"<<endmsg;
-  out<<"| min radius last   SP(3) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r3min
-     <<"                              |"<<endmsg;
-  out<<"| max radius first  SP(3) | "
-     <<std::setw(12)<<std::setprecision(4)<<m_r1max
-     <<"                              |"<<endmsg;
-  out<<"| max radius second SP(3) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r2max
-     <<"                              |"<<endmsg;
-  out<<"| max radius last   SP(3) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r3max
-     <<"                              |"<<endmsg;
-  out<<"| min radius first  SP(2) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r1minv
-     <<"                              |"<<endmsg;
-  out<<"| min radius second SP(2) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r2minv
-     <<"                              |"<<endmsg;
-  out<<"| max radius first  SP(2) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r1maxv
-     <<"                              |"<<endmsg;
-  out<<"| max radius second SP(2) | "
-     <<std::setw(12)<<std::setprecision(5)<<m_r2maxv
-     <<"                              |"<<endmsg;
-  out<<"| min space points dR     | "
-     <<std::setw(12)<<std::setprecision(5)<<m_drmin
-     <<"                              |"<<endmsg;
-  out<<"| max space points dR     | "
-     <<std::setw(12)<<std::setprecision(5)<<m_drmax
-     <<"                              |"<<endmsg;
+  out<<"| min space points dR SSS | "
+     <<std::setw(12)<<std::setprecision(5)<<m_drminSSS
+     <<"                              |"<<std::endl;
+  out<<"| max space points dR SSS | "
+     <<std::setw(12)<<std::setprecision(5)<<m_drmaxSSS
+     <<"                              |"<<std::endl;
+  out<<"| min space points dR PPP | "
+     <<std::setw(12)<<std::setprecision(5)<<m_drminPPP
+     <<"                              |"<<std::endl;
+  out<<"| max space points dR PPP | "
+     <<std::setw(12)<<std::setprecision(5)<<m_drmaxPPP
+     <<"                              |"<<std::endl;
   out<<"| max dZ    impact        | "
      <<std::setw(12)<<std::setprecision(5)<<m_dzver 
      <<"                              |"<<endmsg;
@@ -647,13 +885,10 @@ MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dumpConditions(EventData& data, Ms
      <<std::setw(12)<<std::setprecision(5)<<m_dzdrver 
      <<"                              |"<<endmsg;
   out<<"| max       impact        | "
-     <<std::setw(12)<<std::setprecision(5)<<m_diver
-     <<"                              |"<<endmsg;
-  out<<"| max       impact pps    | "
-     <<std::setw(12)<<std::setprecision(5)<<m_diverpps
+     <<std::setw(12)<<std::setprecision(5)<<m_maxdImpact
      <<"                              |"<<endmsg;
   out<<"| max       impact sss    | "
-     <<std::setw(12)<<std::setprecision(5)<<m_diversss
+     <<std::setw(12)<<std::setprecision(5)<<m_maxdImpactSSS
      <<"                              |"<<endmsg;
   out<<"|---------------------------------------------------------------------|"
      <<endmsg;
@@ -684,23 +919,25 @@ MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dumpConditions(EventData& data, Ms
   out<<"|---------------------------------------------------------------------|"
      <<endmsg;
   return out;
+
+
 }
 
 ///////////////////////////////////////////////////////////////////
 // Dumps event information into the MsgStream
 ///////////////////////////////////////////////////////////////////
 
-MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dumpEvent(EventData& data, MsgStream& out) const
+MsgStream &InDet::SiSpacePointsSeedMaker_ITK::dumpEvent(EventData &data, MsgStream &out) const
 {
   out<<"|---------------------------------------------------------------------|"
      <<endmsg;
-  out<<"| data.ns                    | "
+  out<<"| ns                    | "
      <<std::setw(12)<<data.ns
      <<"                              |"<<endmsg;
-  out<<"| data.nsaz                  | "
+  out<<"| nsaz                  | "
      <<std::setw(12)<<data.nsaz
      <<"                              |"<<endmsg;
-  out<<"| data.nsazv                 | "
+  out<<"| nsazv                 | "
      <<std::setw(12)<<data.nsazv
      <<"                              |"<<endmsg;
   out<<"| seeds                   | "
@@ -709,51 +946,65 @@ MsgStream& InDet::SiSpacePointsSeedMaker_ITK::dumpEvent(EventData& data, MsgStre
   out<<"|---------------------------------------------------------------------|"
      <<endmsg;
   return out;
+
 }
 
 ///////////////////////////////////////////////////////////////////
 // Find next set space points
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::findNext(EventData& data) const
+void InDet::SiSpacePointsSeedMaker_ITK::findNext(EventData &data) const
 {
-  if (data.endlist) return;
+  if (data.endlist)
+    return;
 
   data.i_seede_ITK = data.l_seeds_ITK.begin();
 
-  if      (data.mode==0 || data.mode==1) production2Sp(data);
-  else if (data.mode==2 || data.mode==3) production3Sp(data);
-  else if (data.mode==5 || data.mode==6) production3Sp(data);
+  if (data.mode == 2 || data.mode == 3 || data.mode == 5 || data.mode == 6)
+    production3Sp(data);
 
   data.i_seed_ITK = data.l_seeds_ITK.begin();
-  data.seed_ITK = data.seeds_ITK.begin();
   ++data.nlist;
-}                       
+}
 
 ///////////////////////////////////////////////////////////////////
 // New and old list vertices comparison
 ///////////////////////////////////////////////////////////////////
 
-bool InDet::SiSpacePointsSeedMaker_ITK::newVertices(EventData& data, const std::list<Trk::Vertex>& lV) const
+bool InDet::SiSpacePointsSeedMaker_ITK::newVertices(EventData &data, const std::list<Trk::Vertex> &lV) const
 {
+
   unsigned int s1 = data.l_vertex.size();
   unsigned int s2 = lV.size();
 
+  /// reset the isvertex flag
   data.isvertex = false;
-  if (s1==0 && s2==0) return false;
+  /// if we had no vertices before and have none now,
+  /// we can exit right away
+  if (s1 == 0 && s2 == 0)
+    return false;
 
+  /// clean up the vertex list
   data.l_vertex.clear();
-  if (s2 == 0) return false;
+  /// if we have no vertices now, we can exit
+  if (s2 == 0)
+    return false;
 
+  /// otherwise, update the data with the new vertices
   data.isvertex = true;
-  for (const Trk::Vertex& v: lV) {
+  for (const Trk::Vertex &v : lV)
+  {
     data.l_vertex.insert(static_cast<float>(v.position().z()));
   }
 
-  data.zminU = (*data.l_vertex. begin())-20.;
-  if ( data.zminU < m_zmin) data.zminU = m_zmin;
-  data.zmaxU = (*data.l_vertex.rbegin())+20.;
-  if ( data.zmaxU > m_zmax) data.zmaxU = m_zmax;
+  /// and also update the z interval, adding 20mm before/after the first/last vertex in z
+  /// make sure not to extend the interval beyond the user-configured z interval.
+  data.zminU = (*data.l_vertex.begin()) - 20.;
+  if (data.zminU < m_zmin)
+    data.zminU = m_zmin;
+  data.zmaxU = (*data.l_vertex.rbegin()) + 20.;
+  if (data.zmaxU > m_zmax)
+    data.zmaxU = m_zmax;
 
   return false;
 }
@@ -762,330 +1013,734 @@ bool InDet::SiSpacePointsSeedMaker_ITK::newVertices(EventData& data, const std::
 // Initiate frame work for seed generator
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::buildFrameWork() 
+void InDet::SiSpacePointsSeedMaker_ITK::buildFrameWork()
 {
   m_ptmin = std::abs(m_ptmin);
-  
-  if (m_ptmin < 100.) m_ptmin = 100.;
-
-  if (m_diversss < m_diver   ) m_diversss = m_diver;
-  if (m_divermax < m_diversss) m_divermax = m_diversss;
-
-  if (std::abs(m_etamin) < .1) m_etamin = -m_etamax;
-  m_dzdrmax0 = 1.f/std::tan(2.f*std::atan(exp(-m_etamax)));
-  m_dzdrmin0 = 1.f/std::tan(2.f*std::atan(exp(-m_etamin)));
-  
-  m_COF = 134*.05*9.;
-  m_ipt = 1.f/std::abs(.9f*m_ptmin);
-  m_ipt2 = m_ipt*m_ipt;
-
-  // Build radius sorted containers
-  //
-  m_r_size = static_cast<int>((m_r_rmax+.1)/m_r_rstep);
-
-  // Build radius-azimuthal sorted containers
-  //
-  constexpr float pi2 = 2.*M_PI;
-  const int   NFmax = SizeRF;
-  const float sFmax = static_cast<float>(NFmax)/pi2;
-  const float sFmin = 100./60.;
 
-  float ptm = 400.;
-  if (m_ptmin < ptm) ptm = m_ptmin;
+  if (m_ptmin < 100.)
+    m_ptmin = 100.;
+
+  /// ensure consistency in the transverse IP cuts
+  if (m_maxdImpactSSS < m_maxdImpact)
+    m_maxdImpactSSS = m_maxdImpact;
+
+  /// symmetrise eta cut if eta min not explicitly set
+  if (std::abs(m_etamin) < .1)
+    m_etamin = -m_etamax;
+  /// set dz/dr cut values based on eta cuts
+  m_dzdrmax0 = 1. / std::tan(2. * std::atan(std::exp(-m_etamax)));
+  m_dzdrmin0 = 1. / std::tan(2. * std::atan(std::exp(-m_etamin)));
+
+  /// cache inverse pt cuts
+  m_ipt = 1. / std::abs(m_ptmin);
+  m_ipt2 = m_ipt * m_ipt;
+
+  /// set up the score thresholds based on the user-supplied properties
+  /// The score of any seeds will always be >= the bonus applied,
+  /// since the starting value is |d0|.
+  /// Hence, by subtracting one, we get all seeds which have
+  /// received an additional bonus in addition to the PPP/SSS one,
+  /// which is the confirmation one by construction.
+  m_seedScoreThresholdPPPConfirmationSeed = m_seedScoreBonusPPP - 1.;
+  m_seedScoreThresholdSSSConfirmationSeed = m_seedScoreBonusSSS - 1.;
+
+  /// Build radius sorted containers
+  m_nBinsR = static_cast<int>((m_r_rmax + .1) / m_binSizeR);
+
+  /** 
+     * Now we construct the radius-azimuthal sorted containers.
+     * Binning is determined semi-dynamically based on the tool config 
+    **/
+
+  /// determine the phi binning
+  constexpr float twoPi = 2. * M_PI;
+
+  /// The max Nb. of bins possible is given by the binning enum
+  const int nPhiBinsMax = arraySizePhi;
+  const float inverseSizePhiMax = static_cast<float>(nPhiBinsMax) / twoPi; ///< for run-3: 200 : 6.28 ~ 31.8, bin size 0.0314
+  constexpr float inverseSizePhiMin = 10. / twoPi;
+
+  /// derive the optimum bin size in phi for our needs.
+  /// We do this by checking the size needed for each of the passes we run,
+  /// and then pick the larger one (more conservative).
+  /// This is slightly less optimal than using separate settings per pass, but avoids
+  /// having to book and store in memory two distinct R-phi-Z maps.
+
+  if (m_optimisePhiBinning)
+  {
+    /// case 1: PPP seeds, if we use them
+    const float radiusPixelStart = m_fastTracking ? 50. : 40.; /// approximate lowest R location of pixel hits (driven by barrel)
+    const float radiusPixelEnd = m_fastTracking ? 250. : 320.; /// approximate largest R location of pixel hits (driven by endcap)
+    const float binSizePhi_PPP = m_pixel ? azimuthalStep(m_ptmin, m_maxdImpact, radiusPixelStart, radiusPixelEnd) / 3. : 0.;
+    /// case 2: SSS seeds, if we use them
+    const float binSizePhi_SSS = m_sct ? azimuthalStep(m_ptmin, m_maxdImpactSSS, m_rminSSS, m_rmaxSSS) / 3. : 0.;
+    m_inverseBinSizePhiPPP = 1. / binSizePhi_PPP;
+    m_inverseBinSizePhiSSS = 1. / binSizePhi_SSS;
+  }
+  else
+  {
+    /// this is the default phi binning as operated in release 21 - optimised for
+    /// a trajectory with 400 MeV, from the origin, and Rmin = 0 / Rmax = 600mm   float ptm = 400.;
+    float ptm = 400.;
+    /// if we cut below 400 MeV, adapt the ptm
+    if (m_ptmin < ptm)
+      ptm = m_ptmin;
+    m_inverseBinSizePhiPPP = m_inverseBinSizePhiSSS = ptm / 60.;
+  }
 
-  m_sF = ptm /60.;
-  if (m_sF > sFmax) m_sF = sFmax;
-  else if (m_sF < sFmin) m_sF = sFmin;
-  m_fNmax = static_cast<int>(pi2*m_sF);
-  if (m_fNmax >=NFmax) m_fNmax = NFmax-1;
+  /// truncate the bin size to fall within our thresholds
+  if (m_inverseBinSizePhiPPP > inverseSizePhiMax)
+    m_inverseBinSizePhiPPP = inverseSizePhiMax;
+  else if (m_inverseBinSizePhiPPP < inverseSizePhiMin)
+    m_inverseBinSizePhiPPP = inverseSizePhiMin;
+  if (m_inverseBinSizePhiSSS > inverseSizePhiMax)
+    m_inverseBinSizePhiSSS = inverseSizePhiMax;
+  else if (m_inverseBinSizePhiSSS < inverseSizePhiMin)
+    m_inverseBinSizePhiSSS = inverseSizePhiMin;
+
+  /// now we can determine the number of bins by dividing the interval by the bin size
+  m_maxPhiBinPPP = static_cast<int>(twoPi * m_inverseBinSizePhiPPP);
+  /// additional protection against too many bins. Should not happen given constraints above
+  if (m_maxPhiBinPPP >= nPhiBinsMax)
+    m_maxPhiBinPPP = nPhiBinsMax - 1;
+  m_maxPhiBinSSS = static_cast<int>(twoPi * m_inverseBinSizePhiSSS);
+  if (m_maxPhiBinSSS >= nPhiBinsMax)
+    m_maxPhiBinSSS = nPhiBinsMax - 1;
+  /// recompute inverse bin size, taking into account rounding + truncation
+  m_inverseBinSizePhiPPP = m_maxPhiBinPPP / twoPi;
+  m_inverseBinSizePhiSSS = m_maxPhiBinSSS / twoPi;
+
+  buildConnectionMaps(m_nNeighbourCellsBottomPPP, m_nNeighbourCellsTopPPP,
+                      m_neighbourCellsBottomPPP, m_neighbourCellsTopPPP,
+                      m_maxPhiBinPPP, false);
+
+  buildConnectionMaps(m_nNeighbourCellsBottomSSS, m_nNeighbourCellsTopSSS,
+                      m_neighbourCellsBottomSSS, m_neighbourCellsTopSSS,
+                      m_maxPhiBinSSS, true);
+}
 
-  // Build radius-azimuthal-Z sorted containers for Z-vertices
-  //
-  const int   NFtmax = SizeRFV;
-  const float sFvmax = static_cast<float>(NFtmax)/pi2;
-  m_sFv = m_ptmin/120.;
-  if (m_sFv>sFvmax)  m_sFv = sFvmax; 
-  m_fvNmax = static_cast<int>(pi2*m_sFv);
-  if (m_fvNmax>=NFtmax) m_fvNmax = NFtmax-1;
-
-  // Build maps for radius-azimuthal-Z sorted collections 
-  //
-  for (int f=0; f<=m_fNmax; ++f) {
+void InDet::SiSpacePointsSeedMaker_ITK::buildConnectionMaps(std::array<int, arraySizePhiZ> &nNeighbourCellsBottom,
+                                                            std::array<int, arraySizePhiZ> &nNeighbourCellsTop,
+                                                            std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ> &neighbourCellsBottom,
+                                                            std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ> &neighbourCellsTop,
+                                                            int maxPhiBin, bool isSSS)
+{
 
-    int fb = f-1; if (fb<0      ) fb=m_fNmax;
-    int ft = f+1; if (ft>m_fNmax) ft=0;
-    
-    // For each azimuthal region loop through all Z regions
-    //
-    for (int z=0; z<SizeZ; ++z) {
- 
-      int a        = f *SizeZ+z;
-      int b        = fb*SizeZ+z;
-      int c        = ft*SizeZ+z;
-      m_rfz_b [a]    = 3; m_rfz_t [a]    = 3;
-      m_rfz_ib[a][0] = a; m_rfz_it[a][0] = a;
-      m_rfz_ib[a][1] = b; m_rfz_it[a][1] = b;
-      m_rfz_ib[a][2] = c; m_rfz_it[a][2] = c;
-      if (z==5) {
-
-	m_rfz_t [a]    = 9;
-	m_rfz_it[a][3] = a+1;
-	m_rfz_it[a][4] = b+1;
-	m_rfz_it[a][5] = c+1;
-	m_rfz_it[a][6] = a-1;
-	m_rfz_it[a][7] = b-1;
-	m_rfz_it[a][8] = c-1;
-      } else if (z> 5) {
-
-	m_rfz_b [a]    = 6;
-	m_rfz_ib[a][3] = a-1;
-	m_rfz_ib[a][4] = b-1;
-	m_rfz_ib[a][5] = c-1;
-
-	if (z<10) {
-
-	  m_rfz_t [a]    = 6;
-	  m_rfz_it[a][3] = a+1;
-	  m_rfz_it[a][4] = b+1;
-	  m_rfz_it[a][5] = c+1;
-	}
-      } else {
-
-	m_rfz_b [a]    = 6;
-	m_rfz_ib[a][3] = a+1;
-	m_rfz_ib[a][4] = b+1;
-	m_rfz_ib[a][5] = c+1;
-
-	if (z>0) {
-
-	  m_rfz_t [a]    = 6;
-	  m_rfz_it[a][3] = a-1;
-	  m_rfz_it[a][4] = b-1;
-	  m_rfz_it[a][5] = c-1;
-	}
+  /// Build maps for radius-azimuthal-Z sorted collections.
+  /// Here, we associate which bins are 'connected' to a given phi-Z bin
+  /// for the seeding
+
+  for (int phiBin = 0; phiBin <= maxPhiBin; ++phiBin)
+  {
+
+    int phiBelow = phiBin - 1;
+    if (phiBelow < 0)
+      phiBelow = maxPhiBin; ///< loop around at edges of range
+
+    int phiAbove = phiBin + 1;
+    if (phiAbove > maxPhiBin)
+      phiAbove = 0; ///< loop around at edges of range
+
+    /// For each azimuthal region loop through all Z regions
+    for (int z = 0; z < arraySizeZ; ++z)
+    {
+
+      /// we always include the two neighbouring phi bins for the top / bottom
+      /// space point search
+
+      int twoDbinSamePhi = phiBin * arraySizeZ + z;
+      int twoDbinLowerPhi = phiBelow * arraySizeZ + z;
+      int twoDbinHigherPhi = phiAbove * arraySizeZ + z;
+
+      nNeighbourCellsBottom[twoDbinSamePhi] = 3;
+      nNeighbourCellsTop[twoDbinSamePhi] = 3;
+
+      neighbourCellsBottom[twoDbinSamePhi][0] = twoDbinSamePhi;
+      neighbourCellsTop[twoDbinSamePhi][0] = twoDbinSamePhi;
+
+      neighbourCellsBottom[twoDbinSamePhi][1] = twoDbinLowerPhi;
+      neighbourCellsTop[twoDbinSamePhi][1] = twoDbinLowerPhi;
+
+      neighbourCellsBottom[twoDbinSamePhi][2] = twoDbinHigherPhi;
+      neighbourCellsTop[twoDbinSamePhi][2] = twoDbinHigherPhi;
+
+      /** in addition, we usually add at least one neighbouring slice 
+        * in Z. This depends on where we are in the detector. 
+        * Guide for the following: 
+        * z == 5: central z region, |z|<250mm
+        * 0  1  2  3  4    5    6  7  8  9  10  z bin index
+        * --------------------------------------> Z[mm]
+        *  Z=-2500       IP,Z=0            Z=+2500
+        **/
+      if (z == 5)
+      {
+        nNeighbourCellsTop[twoDbinSamePhi] = 9;
+        // in the central z region, we include the two neighbouring
+        // z slices for the top neighbour search
+
+        neighbourCellsTop[twoDbinSamePhi][3] = twoDbinSamePhi + 1;
+        neighbourCellsTop[twoDbinSamePhi][4] = twoDbinLowerPhi + 1;
+        neighbourCellsTop[twoDbinSamePhi][5] = twoDbinHigherPhi + 1;
+        neighbourCellsTop[twoDbinSamePhi][6] = twoDbinSamePhi - 1;
+        neighbourCellsTop[twoDbinSamePhi][7] = twoDbinLowerPhi - 1;
+        neighbourCellsTop[twoDbinSamePhi][8] = twoDbinHigherPhi - 1;
       }
-
-      if (z==3) {
-	m_rfz_b[a]      = 9;
-	m_rfz_ib[a][6] = a+2;
-	m_rfz_ib[a][7] = b+2;
-	m_rfz_ib[a][8] = c+2;
-      } else if (z==7) {
-	m_rfz_b[a]      = 9;
-	m_rfz_ib[a][6] = a-2;
-	m_rfz_ib[a][7] = b-2;
-	m_rfz_ib[a][8] = c-2;
+      // z > 5: positive z values, |z| > 250mm
+      else if (z > 5)
+      {
+        // for the bottom SP search in positive non-central z, we include the
+        // neighbouring Z region on the left (towards the IP) in the bottom
+        // neighbour search
+        nNeighbourCellsBottom[twoDbinSamePhi] = 6;
+        neighbourCellsBottom[twoDbinSamePhi][3] = twoDbinSamePhi - 1;
+        neighbourCellsBottom[twoDbinSamePhi][4] = twoDbinLowerPhi - 1;
+        neighbourCellsBottom[twoDbinSamePhi][5] = twoDbinHigherPhi - 1;
+
+        if (z < 10)
+        {
+          /** for the top SP search in positive z, 
+          * if we are not in the outermost z region, 
+          * we include the neighbouring Z region on the right (away from the IP). 
+          * In z = 10, the most forward, we do not have such a 'right side' neighbour we can add
+          **/
+          nNeighbourCellsTop[twoDbinSamePhi] = 6;
+          neighbourCellsTop[twoDbinSamePhi][3] = twoDbinSamePhi + 1;
+          neighbourCellsTop[twoDbinSamePhi][4] = twoDbinLowerPhi + 1;
+          neighbourCellsTop[twoDbinSamePhi][5] = twoDbinHigherPhi + 1;
+        }
+      }
+      // z < 5: negative z values, |z| > 250mm
+      else
+      {
+        /** for the bottom SP in negative non-central z, we include 
+          * the neighbouring z region on the right (towards the IP) in the 
+          * bottom neighbour search 
+          **/
+        nNeighbourCellsBottom[twoDbinSamePhi] = 6;
+        neighbourCellsBottom[twoDbinSamePhi][3] = twoDbinSamePhi + 1;
+        neighbourCellsBottom[twoDbinSamePhi][4] = twoDbinLowerPhi + 1;
+        neighbourCellsBottom[twoDbinSamePhi][5] = twoDbinHigherPhi + 1;
+
+        if (z > 0)
+        {
+          // if there is a z region on the left (away from the IP), we include it in the top
+          // neighbour search
+          nNeighbourCellsTop[twoDbinSamePhi] = 6;
+          neighbourCellsTop[twoDbinSamePhi][3] = twoDbinSamePhi - 1;
+          neighbourCellsTop[twoDbinSamePhi][4] = twoDbinLowerPhi - 1;
+          neighbourCellsTop[twoDbinSamePhi][5] = twoDbinHigherPhi - 1;
+        }
       }
-    }
-  }
-
-  // Build maps for radius-azimuthal-Z sorted collections for Z
-  //
-  for (int f=0; f<=m_fvNmax; ++f) {
 
-    int fb = f-1;
-    if (fb<0) fb=m_fvNmax;
-    int ft = f+1;
-    if (ft>m_fvNmax) ft=0;
-    
-    // For each azimuthal region loop through central Z regions
-    //
-    for (int z=0; z<SizeZV; ++z) {
-      
-      int a  = f *SizeZV+z;
-      int b  = fb*SizeZV+z;
-      int c  = ft*SizeZV+z;
-      m_rfzv_n[a]    = 3;
-      m_rfzv_i[a][0] = a;
-      m_rfzv_i[a][1] = b;
-      m_rfzv_i[a][2] = c;
-      if (z>1) {
-	m_rfzv_n[a]    = 6;
-	m_rfzv_i[a][3] = a-1;
-	m_rfzv_i[a][4] = b-1;
-	m_rfzv_i[a][5] = c-1;
-      } else if (z<1) {
-	m_rfzv_n[a]    = 6;
-	m_rfzv_i[a][3] = a+1;
-	m_rfzv_i[a][4] = b+1;
-	m_rfzv_i[a][5] = c+1;
+      /** 
+        * z bins 3 / 7: 450mm < |z| < 925mm.: 
+        * also include the central z region in the bottom SP search.  
+        * likely for PPP seeds with hits in pixel barrel + endcaps
+        **/
+      /// Only used for strip seeds
+      if (isSSS)
+      {
+        if (z == 3)
+        {
+          nNeighbourCellsBottom[twoDbinSamePhi] = 9;
+          neighbourCellsBottom[twoDbinSamePhi][6] = twoDbinSamePhi + 2;
+          neighbourCellsBottom[twoDbinSamePhi][7] = twoDbinLowerPhi + 2;
+          neighbourCellsBottom[twoDbinSamePhi][8] = twoDbinHigherPhi + 2;
+        }
+        else if (z == 7)
+        {
+          nNeighbourCellsBottom[twoDbinSamePhi] = 9;
+          neighbourCellsBottom[twoDbinSamePhi][6] = twoDbinSamePhi - 2;
+          neighbourCellsBottom[twoDbinSamePhi][7] = twoDbinLowerPhi - 2;
+          neighbourCellsBottom[twoDbinSamePhi][8] = twoDbinHigherPhi - 2;
+        }
       }
     }
   }
 }
 
+float InDet::SiSpacePointsSeedMaker_ITK::azimuthalStep(const float pTmin, const float maxd0, const float Rmin, const float Rmax) const
+{
+  /// here we approximate the largest curvature
+  /// that can be expected for the seeds we build
+  /// using R[mm] ~ pT[MeV] / (0.3 * B[T]), with B == 2T
+  float Rm = pTmin / .6;
+
+  /// for LRT, the maximum allowed d0 may be larger than
+  /// the radius at which the innermost hit is possible.
+  /// In that case, our "worst case" trajectory
+  /// generating the largest phi displacement
+  ///  will be the one with a hit at Rmin and
+  /// d0 == Rmin.
+  float worstCaseD0 = maxd0;
+  if (maxd0 > Rmin)
+    worstCaseD0 = Rmin;
+
+  float sI = std::abs(std::asin(worstCaseD0 / Rmin) - std::asin(worstCaseD0 / Rmax));
+  float sF = std::abs(std::asin(std::min(1., Rmax / (2. * Rm))) -
+                      std::asin(std::min(1., Rmin / (2. * Rm))));
+  return sI + sF;
+}
+
 ///////////////////////////////////////////////////////////////////
 // Initiate beam frame work for seed generator
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::buildBeamFrameWork(EventData& data) const
-{ 
-  SG::ReadCondHandle<InDet::BeamSpotData> beamSpotHandle { m_beamSpotKey };
+void InDet::SiSpacePointsSeedMaker_ITK::buildBeamFrameWork(EventData &data) const
+{
+  SG::ReadCondHandle<InDet::BeamSpotData> beamSpotHandle{m_beamSpotKey};
 
   const Amg::Vector3D &cb = beamSpotHandle->beamPos();
   double tx = std::tan(beamSpotHandle->beamTilt(0));
   double ty = std::tan(beamSpotHandle->beamTilt(1));
 
-  double ph   = atan2(ty,tx);
-  double th   = acos(1./sqrt(1.+tx*tx+ty*ty));
-  double sint = sin(th);
-  double cost = cos(th);
-  double sinp = sin(ph);
-  double cosp = cos(ph);
-  
-  data.xbeam[0] =  static_cast<float>(cb.x());
-  data.xbeam[1] =  static_cast<float>(cost*cosp*cosp+sinp*sinp);
-  data.xbeam[2] =  static_cast<float>(cost*sinp*cosp-sinp*cosp);
-  data.xbeam[3] = -static_cast<float>(sint*cosp               );
-  
-  data.ybeam[0] =  static_cast<float>(cb.y());
-  data.ybeam[1] =  static_cast<float>(cost*cosp*sinp-sinp*cosp);
-  data.ybeam[2] =  static_cast<float>(cost*sinp*sinp+cosp*cosp);
-  data.ybeam[3] = -static_cast<float>(sint*sinp               );
-  
-  data.zbeam[0] =  static_cast<float>(cb.z());
-  data.zbeam[1] =  static_cast<float>(sint*cosp);
-  data.zbeam[2] =  static_cast<float>(sint*sinp);
-  data.zbeam[3] =  static_cast<float>(cost);
+  double phi = std::atan2(ty, tx);
+  double theta = std::acos(1. / std::sqrt(1. + tx * tx + ty * ty));
+  double sinTheta = std::sin(theta);
+  double cosTheta = std::cos(theta);
+  double sinPhi = std::sin(phi);
+  double cosPhi = std::cos(phi);
+
+  data.xbeam[0] = static_cast<float>(cb.x());
+  data.xbeam[1] = static_cast<float>(cosTheta * cosPhi * cosPhi + sinPhi * sinPhi);
+  data.xbeam[2] = static_cast<float>(cosTheta * sinPhi * cosPhi - sinPhi * cosPhi);
+  data.xbeam[3] = -static_cast<float>(sinTheta * cosPhi);
+
+  data.ybeam[0] = static_cast<float>(cb.y());
+  data.ybeam[1] = static_cast<float>(cosTheta * cosPhi * sinPhi - sinPhi * cosPhi);
+  data.ybeam[2] = static_cast<float>(cosTheta * sinPhi * sinPhi + cosPhi * cosPhi);
+  data.ybeam[3] = -static_cast<float>(sinTheta * sinPhi);
+
+  data.zbeam[0] = static_cast<float>(cb.z());
+  data.zbeam[1] = static_cast<float>(sinTheta * cosPhi);
+  data.zbeam[2] = static_cast<float>(sinTheta * sinPhi);
+  data.zbeam[3] = static_cast<float>(cosTheta);
 }
 
 ///////////////////////////////////////////////////////////////////
 // Initiate beam frame work for seed generator
 ///////////////////////////////////////////////////////////////////
-void  InDet::SiSpacePointsSeedMaker_ITK::convertToBeamFrameWork
-(EventData& data, const Trk::SpacePoint*const& sp,float* r) const
+void InDet::SiSpacePointsSeedMaker_ITK::convertToBeamFrameWork(EventData &data, const Trk::SpacePoint *const &sp, float *r) const
 {
-  r[0] = static_cast<float>(sp->globalPosition().x())-data.xbeam[0];
-  r[1] = static_cast<float>(sp->globalPosition().y())-data.ybeam[0];
-  r[2] = static_cast<float>(sp->globalPosition().z())-data.zbeam[0];
+  r[0] = static_cast<float>(sp->globalPosition().x()) - data.xbeam[0];
+  r[1] = static_cast<float>(sp->globalPosition().y()) - data.ybeam[0];
+  r[2] = static_cast<float>(sp->globalPosition().z()) - data.zbeam[0];
 
-  if (!sp->clusterList().second) return;
+  //not in 21.9 ITk New implementation
+  /*
+    if (!sp->clusterList().second) return;
 
-  // Only for SCT space points
-  //
-  const InDet::SiCluster* c0 = static_cast<const InDet::SiCluster*>(sp->clusterList().first );
-  const InDet::SiCluster* c1 = static_cast<const InDet::SiCluster*>(sp->clusterList().second);
-  
-  Amg::Vector2D lc0 = c0->localPosition();
-  Amg::Vector2D lc1 = c1->localPosition();
-  
-  std::pair<Amg::Vector3D, Amg::Vector3D > e0 =
-    (c0->detectorElement()->endsOfStrip(InDetDD::SiLocalPosition(lc0.y(),lc0.x(),0.)));
-  std::pair<Amg::Vector3D, Amg::Vector3D > e1 =
-    (c1->detectorElement()->endsOfStrip(InDetDD::SiLocalPosition(lc1.y(),lc1.x(),0.)));
+    // Only for SCT space points
+    //
+    const InDet::SiCluster* c0 = static_cast<const InDet::SiCluster*>(sp->clusterList().first );
+    const InDet::SiCluster* c1 = static_cast<const InDet::SiCluster*>(sp->clusterList().second);
+    
+    Amg::Vector2D lc0 = c0->localPosition();
+    Amg::Vector2D lc1 = c1->localPosition();
+    
+    std::pair<Amg::Vector3D, Amg::Vector3D > e0 =
+      (c0->detectorElement()->endsOfStrip(InDetDD::SiLocalPosition(lc0.y(),lc0.x(),0.)));
+    std::pair<Amg::Vector3D, Amg::Vector3D > e1 =
+      (c1->detectorElement()->endsOfStrip(InDetDD::SiLocalPosition(lc1.y(),lc1.x(),0.)));
+
+    Amg::Vector3D b0 (e0.second-e0.first);
+    Amg::Vector3D b1 (e1.second-e1.first);
+    Amg::Vector3D d02(e0.first -e1.first);
+
+    // b0
+    r[ 3] = static_cast<float>(b0[0]);
+    r[ 4] = static_cast<float>(b0[1]);
+    r[ 5] = static_cast<float>(b0[2]);
+    
+    // b1
+    r[ 6] = static_cast<float>(b1[0]);
+    r[ 7] = static_cast<float>(b1[1]);
+    r[ 8] = static_cast<float>(b1[2]);
+
+    // r0-r2
+    r[ 9] = static_cast<float>(d02[0]);
+    r[10] = static_cast<float>(d02[1]);
+    r[11] = static_cast<float>(d02[2]);
+
+    // r0
+    r[12] = static_cast<float>(e0.first[0])-data.xbeam[0];
+    r[13] = static_cast<float>(e0.first[1])-data.ybeam[0];
+    r[14] = static_cast<float>(e0.first[2])-data.zbeam[0];
+    */
+}
 
-  Amg::Vector3D b0 (e0.second-e0.first);
-  Amg::Vector3D b1 (e1.second-e1.first);
-  Amg::Vector3D d02(e0.first -e1.first);
+///////////////////////////////////////////////////////////////////
+// Initiate space points seed maker
+///////////////////////////////////////////////////////////////////
 
-  // b0
-  r[ 3] = static_cast<float>(b0[0]);
-  r[ 4] = static_cast<float>(b0[1]);
-  r[ 5] = static_cast<float>(b0[2]);
-  
-  // b1
-  r[ 6] = static_cast<float>(b1[0]);
-  r[ 7] = static_cast<float>(b1[1]);
-  r[ 8] = static_cast<float>(b1[2]);
+void InDet::SiSpacePointsSeedMaker_ITK::fillLists(EventData &data) const
+{
+  constexpr float twoPi = 2. * M_PI;
+
+  int firstRadialBin = 0;
+  int lastRadialBin = 0;
+  bool endcap = false;
+
+  /** 
+    * The following is done separately for each iteration. 
+    * We sort the hits in our radially sorted lists into the 
+    * z-phi binning, keeping only those we want to consider to reduce 
+    * combinatorics
+    *
+    * Note that the use of r_first to start the loop is what ensures that 
+    * the first iteration is a pure SSS pass. 
+    * In newEvent, r_first is set to the bin after the last 
+    * radial bin containing Pixel space points. 
+    * For the second iteration, we reset it to zero and thus start in the pixels. 
+    **/
+
+  const std::map<float, int> ztoBin{
+      {-2500., 0},
+      {-1400., 1},
+      {-925., 2},
+      {-450., 3},
+      {-250, 4},
+      {250, 5},
+      {450, 6},
+      {925, 7},
+      {1400, 8},
+      {2500, 9},
+      {100000, 10}, ///< if we encounter Si hits at z > +100m, we are probably not in ATLAS anymore...
+  };
+
+  int nPhiBins = data.iteration ? m_maxPhiBinPPP : m_maxPhiBinSSS;
+  float inverseBinSizePhi = data.iteration ? m_inverseBinSizePhiPPP : m_inverseBinSizePhiSSS;
+
+  for (int radialBin = data.r_first; radialBin < m_nBinsR; ++radialBin)
+  {
+
+    /// skip empty radial bins
+    if (!data.r_map[radialBin])
+      continue;
+
+    // Stop when we reach strip SP in PPP iteration #1
+    std::list<InDet::SiSpacePointForSeedITK *>::iterator SP_first = data.r_Sorted_ITK[radialBin].begin();
+    if (data.iteration && (*SP_first)->spacepoint->clusterList().second)
+      break;
+
+    /// remember the first non-empty bin we encounter
+    if (firstRadialBin == 0)
+      firstRadialBin = radialBin;
+    lastRadialBin = radialBin;
+
+    // loop over the space points in the r-bin and sort them into the 2d phi-z binning
+    for (InDet::SiSpacePointForSeedITK *SP : data.r_Sorted_ITK[radialBin])
+    {
+
+      /// Azimuthal angle sort
+      /// find the bin by dividing phi in 0...2pi by the bin size
+      float Phi = SP->phi();
+      if (Phi < 0.)
+        Phi += twoPi; // phi is defined in [0..2pi] for the binning
+      int phiBin = static_cast<int>(Phi * inverseBinSizePhi);
+      /// handle overflows
+      if (phiBin < 0)
+      {
+        phiBin = nPhiBins;
+      }
+      else if (phiBin > nPhiBins)
+      {
+        phiBin = 0;
+      }
 
-  // r0-r2
-  r[ 9] = static_cast<float>(d02[0]);
-  r[10] = static_cast<float>(d02[1]);
-  r[11] = static_cast<float>(d02[2]);
+      float Z = SP->z();
+      endcap = (std::abs(Z) > 1490);
+      /** z-coordinate sort. 
+        * Here, we have a variable bin size. 
+        * Use a map to replace 5 levels of nested ternaries 
+        * for a somewhat more readable notation while retaining
+        * a logN lookup speed 
+        **/
+      int zBin{0};
+      auto bound = ztoBin.lower_bound(Z);
+      /// some protection in case things go REALLY wrong
+      if (bound == ztoBin.end())
+      {
+        --bound; ///< z beyond the max: return last bin
+      }
+      zBin = bound->second;
 
-  // r0
-  r[12] = static_cast<float>(e0.first[0])-data.xbeam[0];
-  r[13] = static_cast<float>(e0.first[1])-data.ybeam[0];
-  r[14] = static_cast<float>(e0.first[2])-data.zbeam[0];
+      /// 2D bin index - computed from the 1D using standard 2D array bin arithmetics
+      int twoDbin = phiBin * arraySizeZ + zBin;
+      /// increment total counter of space points.
+      /// This is not reset between iterations.
+      ++data.nsaz;
+      // push our space point into the 2D binned array
+      data.rfz_Sorted_ITK[twoDbin].push_back(SP);
+      /// the conditional seems to always be true. The rfz_index vector stores
+      /// the 2D bin for each SP in the radius-sorted map. This way,
+      /// we obtain effectively a *3D binning* in r(via the r-sorted vector), phi and z (via the 2D index)
+      if (!data.rfz_map[twoDbin]++)
+        data.rfz_index[data.nrfz++] = twoDbin;
+    }
+  }
+
+  data.state = 0;
+    if(data.iteration){ // PPP
+      data.RTmin = m_binSizeR*firstRadialBin+10. ;
+      data.RTmax = m_binSizeR*lastRadialBin-10.;
+    }else{ //SSS
+      if ( endcap and m_isLRT) {
+        data.RTmin = m_binSizeR*firstRadialBin+10. ;
+        data.RTmax = m_binSizeR*lastRadialBin-10.;
+      }
+      else{
+        data.RTmin = m_binSizeR*firstRadialBin+30. ;
+        data.RTmax = m_binSizeR*lastRadialBin-150.;
+      }
+    }
+    
 }
-   
+
 ///////////////////////////////////////////////////////////////////
-// Initiate space points seed maker
+/// Initiate space points seed maker for fast tracking
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::fillLists(EventData& data) const
+void InDet::SiSpacePointsSeedMaker_ITK::fillListsFast(const EventContext &ctx, EventData &data) const
 {
-  constexpr float pi2 = 2.*M_PI;
-  std::list<InDet::SiSpacePointForSeedITK*>::iterator r, re;
+  constexpr float twoPi = 2. * M_PI;
+
+  /// Erase any existing entries in the data object
+  erase(data);
+
+  SG::ReadHandle<Trk::PRDtoTrackMap> prd_to_track_map;
+  const Trk::PRDtoTrackMap *prd_to_track_map_cptr = nullptr;
+  if (!m_prdToTrackMap.key().empty())
+  {
+    prd_to_track_map = SG::ReadHandle<Trk::PRDtoTrackMap>(m_prdToTrackMap, ctx);
+    if (!prd_to_track_map.isValid())
+    {
+      ATH_MSG_ERROR("Failed to read PRD to track association map: " << m_prdToTrackMap.key());
+    }
+    prd_to_track_map_cptr = prd_to_track_map.cptr();
+  }
 
-  int  ir0 =0;
-  
-  for (int i=data.r_first; i!=m_r_size; ++i) {
+  /** 
+    * The following is done separately for each iteration. 
+    * We sort the hits in our radially sorted lists into the 
+    * z-phi binning, keeping only those we want to consider to reduce 
+    * combinatorics
+    *
+    * Note that the use of r_first to start the loop is what ensures that 
+    * the first iteration is a pure SSS pass. 
+    * In newEvent, r_first is set to the bin after the last 
+    * radial bin containing Pixel space points. 
+    * For the second iteration, we reset it to zero and thus start in the pixels. 
+    **/
+
+  const std::map<float, int> ztoBin{
+      {-2500., 0},
+      {-1400., 1},
+      {-925., 2},
+      {-450., 3},
+      {-250, 4},
+      {250, 5},
+      {450, 6},
+      {925, 7},
+      {1400, 8},
+      {2500, 9},
+      {100000, 10}, ///< if we encounter Si hits at z > +100m, we are probably not in ATLAS anymore...
+  };
+
+  SG::ReadHandle<SpacePointContainer> spacepoints;
+  if (m_sct)
+  {
+    SG::ReadHandle<SpacePointContainer> spacepointsSCT{m_spacepointsSCT, ctx};
+    spacepoints = spacepointsSCT;
+  }
+  else if (m_pixel)
+  {
+    SG::ReadHandle<SpacePointContainer> spacepointsPixel{m_spacepointsPixel, ctx};
+    spacepoints = spacepointsPixel;
+  }
 
-    if (!data.r_map[i]) continue;
-    r = data.r_Sorted_ITK[i].begin();
-    re = data.r_Sorted_ITK[i].end();
-    if (!ir0) ir0 = i;
+  int nPhiBins = m_pixel ? m_maxPhiBinPPP : m_maxPhiBinSSS;
+  float inverseBinSizePhi = m_pixel ? m_inverseBinSizePhiPPP : m_inverseBinSizePhiSSS;
 
-    if (data.iteration && (*r)->spacepoint->clusterList().second) break;
+  if (spacepoints.isValid())
+  {
+    /// loop over the space points
+    for (const SpacePointCollection *spc : *spacepoints)
+    {
 
-    for (; r!=re; ++r) {
-      
-      // Azimuthal angle sort
-      //
-      float F = (*r)->phi();
-      if (F<0.) F+=pi2;
+      SpacePointCollection::const_iterator spFirst = spc->begin();
+      float r[15];
+      if (m_pixel)
+        pixInform(*spFirst, r);
+      else if (m_sct)
+        sctInform(data, *spFirst, r);
 
-      int   f = static_cast<int>(F*m_sF);
-      if (f < 0) f = m_fNmax;
-      else if (f > m_fNmax) f = 0;
+      for (const Trk::SpacePoint *sp : *spc)
+      {
 
-      int z;
-      float Z = (*r)->z();
+        if (prd_to_track_map_cptr && isUsed(sp, *prd_to_track_map_cptr))
+          continue;
 
-      // Azimuthal angle and Z-coordinate sort
-      //
-      if (Z>0.) {
-	Z< 250.?z=5:Z< 450.?z=6:Z< 925.?z=7:Z< 1400.?z=8:Z< 2500.?z=9:z=10;
-      } else {
-	Z>-250.?z=5:Z>-450.?z=4:Z>-925.?z=3:Z>-1400.?z=2:Z>-2500.?z=1:z= 0;
-      }
+        /** create a SiSpacePointForSeedITK from the space point.
+     * This will also add the point to the l_spforseed list and update
+     * the i_spforseed iterator
+     **/
+        InDet::SiSpacePointForSeedITK *sps = newSpacePoint(data, sp, r);
+        if (!sps)
+          continue;
 
-      int n = f*SizeZ+z;
-      ++data.nsaz;
-      data.rfz_Sorted_ITK[n].push_back(*r);
-      if (!data.rfz_map[n]++) data.rfz_index[data.nrfz++] = n;
-      
-      if (!data.iteration && (*r)->spacepoint->clusterList().second == 0 && z>=3 && z<=7) { 
-	z<=4 ? z=0 : z>=6 ? z=2 : z=1;
-
-	// Azimuthal angle and Z-coordinate sort for fast vertex search
-	//
-	f = static_cast<int>(F*m_sFv);
-        if (f < 0) f += m_fvNmax;
-        else if (f> m_fvNmax) f -= m_fvNmax;
-
-        n = f*3+z;
-        ++data.nsazv;
-	data.rfzv_Sorted_ITK[n].push_back(*r);
-        if (!data.rfzv_map[n]++) data.rfzv_index[data.nrfzv++] = n;
+        /// Azimuthal angle sort
+        /// find the bin by dividing phi in 0...2pi by the bin size
+        float Phi = sps->phi();
+        if (Phi < 0.)
+          Phi += twoPi; // phi is defined in [0..2pi] for the binning
+        int phiBin = static_cast<int>(Phi * inverseBinSizePhi);
+        /// handle overflows
+        if (phiBin < 0)
+        {
+          phiBin = nPhiBins;
+        }
+        else if (phiBin > nPhiBins)
+        {
+          phiBin = 0;
+        }
+
+        float Z = sps->z();
+        /** z-coordinate sort.
+     * Here, we have a variable bin size.
+     * Use a map to replace 5 levels of nested ternaries
+     * for a somewhat more readable notation while retaining
+     * a logN lookup speed
+     **/
+        int zBin{0};
+        auto bound = ztoBin.lower_bound(Z);
+        /// some protection in case things go REALLY wrong
+        if (bound == ztoBin.end())
+        {
+          --bound; ///< z beyond the max: return last bin
+        }
+        zBin = bound->second;
+
+        /// 2D bin index - computed from the 1D using standard 2D array bin arithmetics
+        int twoDbin = phiBin * arraySizeZ + zBin;
+        /// increment total counter of space points.
+        /// This is not reset between iterations.
+        ++data.nsaz;
+        // push our space point into the 2D binned array
+        data.rfz_Sorted_ITK[twoDbin].push_back(sps);
+        /// the conditional seems to always be true. The rfz_index vector stores
+        /// the 2D bin for each SP in the radius-sorted map. This way,
+        /// we obtain effectively a *3D binning* in r(via the r-sorted vector), phi and z (via the 2D index)
+        if (!data.rfz_map[twoDbin]++)
+          data.rfz_index[data.nrfz++] = twoDbin;
       }
     }
   }
-  data.state = 0;
+
+  // Loop through all RZ collections and sort them in radius order
+  //
+  for (int twoDbin(0); twoDbin != arraySizePhiZ; ++twoDbin)
+  {
+    if (data.rfz_Sorted_ITK[twoDbin].size() > 1)
+    {
+      data.rfz_Sorted_ITK[twoDbin].sort(InDet::SiSpacePointsITKComparison_R());
+    }
+  }
+
+    if(m_pixel){
+      data.RTmin = m_rminPPPFast ;
+      //m_RTmax set per zBin in production3Sp
+    }
+    else if(m_sct){
+      data.RTmin = m_rminSSS ;
+      data.RTmax = m_rmaxSSS ;
+    }
 }
-   
+
+///////////////////////////////////////////////////////////////////
+// Pixels information
+///////////////////////////////////////////////////////////////////
+
+void InDet::SiSpacePointsSeedMaker_ITK::pixInform(const Trk::SpacePoint *sp, float *r) const
+{
+  const InDet::SiCluster *cl = static_cast<const InDet::SiCluster *>(sp->clusterList().first);
+  const InDetDD::SiDetectorElement *de = cl->detectorElement();
+  const Amg::Transform3D &Tp = de->surface().transform();
+  r[3] = float(Tp(0, 2));
+  r[4] = float(Tp(1, 2));
+  r[5] = float(Tp(2, 2));
+}
+
+///////////////////////////////////////////////////////////////////
+// SCT information
+///////////////////////////////////////////////////////////////////
+
+void InDet::SiSpacePointsSeedMaker_ITK::sctInform(EventData &data, const Trk::SpacePoint *sp, float *r) const
+{
+  const InDet::SiCluster *c0 = static_cast<const InDet::SiCluster *>(sp->clusterList().first);
+  const InDet::SiCluster *c1 = static_cast<const InDet::SiCluster *>(sp->clusterList().second);
+  const InDetDD::SiDetectorElement *d0 = c0->detectorElement();
+  const InDetDD::SiDetectorElement *d1 = c1->detectorElement();
+
+  Amg::Vector2D lc0 = c0->localPosition();
+  Amg::Vector2D lc1 = c1->localPosition();
+
+  std::pair<Amg::Vector3D, Amg::Vector3D> e0 =
+      (d0->endsOfStrip(InDetDD::SiLocalPosition(lc0.y(), lc0.x(), 0.)));
+  std::pair<Amg::Vector3D, Amg::Vector3D> e1 =
+      (d1->endsOfStrip(InDetDD::SiLocalPosition(lc1.y(), lc1.x(), 0.)));
+
+  Amg::Vector3D s0(.5 * (e0.first + e0.second));
+  Amg::Vector3D s1(.5 * (e1.first + e1.second));
+
+  Amg::Vector3D b0(.5 * (e0.second - e0.first));
+  Amg::Vector3D b1(.5 * (e1.second - e1.first));
+  Amg::Vector3D d02(s0 - s1);
+
+  // b0
+  r[3] = float(b0[0]);
+  r[4] = float(b0[1]);
+  r[5] = float(b0[2]);
+
+  // b1
+  r[6] = float(b1[0]);
+  r[7] = float(b1[1]);
+  r[8] = float(b1[2]);
+
+  // r0-r2
+  r[9] = float(d02[0]);
+  r[10] = float(d02[1]);
+  r[11] = float(d02[2]);
+
+  // r0
+  r[12] = float(s0[0]) - data.xbeam[0];
+  r[13] = float(s0[1]) - data.ybeam[0];
+  r[14] = float(s0[2]) - data.zbeam[0];
+}
+
 ///////////////////////////////////////////////////////////////////
 // Erase space point information
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::erase(EventData& data) const
+void InDet::SiSpacePointsSeedMaker_ITK::erase(EventData &data) const
 {
-  for (int i=0; i<data.nrfz; ++i) {
+  for (int i = 0; i < data.nrfz; ++i)
+  {
     int n = data.rfz_index[i];
     data.rfz_map[n] = 0;
     data.rfz_Sorted_ITK[n].clear();
   }
-  
-  for (int i=0; i<data.nrfzv; ++i) {
+
+  for (int i = 0; i < data.nrfzv; ++i)
+  {
     int n = data.rfzv_index[i];
     data.rfzv_map[n] = 0;
     data.rfzv_Sorted_ITK[n].clear();
   }
   data.state = 0;
-  data.nsaz  = 0;
+  data.nsaz = 0;
   data.nsazv = 0;
-  data.nrfz  = 0;
+  data.nrfz = 0;
   data.nrfzv = 0;
 }
 
@@ -1093,95 +1748,119 @@ void InDet::SiSpacePointsSeedMaker_ITK::erase(EventData& data) const
 // 2 space points seeds production
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::production2Sp(EventData& data) const
+void InDet::SiSpacePointsSeedMaker_ITK::production2Sp(EventData &data) const
 {
-  if (data.nsazv<2) return;
+  if (data.nsazv < 2)
+    return;
 
-  std::list<InDet::SiSpacePointForSeedITK*>::iterator r0,r0e,r,re;
+  std::list<InDet::SiSpacePointForSeedITK *>::iterator r0, r0e, r, re;
   int nseed = 0;
 
   // Loop thorugh all azimuthal regions
   //
-  for (int f=data.fvNmin; f<=m_fvNmax; ++f) {
+  for (int f = data.fvNmin; f <= m_fvNmax; ++f)
+  {
 
     // For each azimuthal region loop through Z regions
     //
     int z = 0;
-    if (!data.endlist) z = data.zMin;
-    for (; z<SizeZV; ++z) {
-      
-      int a = f*SizeZV+z;
-      if (!data.rfzv_map[a]) continue;
-      r0  = data.rfzv_Sorted_ITK[a].begin();
-      r0e = data.rfzv_Sorted_ITK[a].end  ();
-
-      if (!data.endlist) {
+    if (!data.endlist)
+      z = data.zMin;
+    for (; z < arraySizeZV; ++z)
+    {
+
+      int a = f * arraySizeZV + z;
+      if (!data.rfzv_map[a])
+        continue;
+      r0 = data.rfzv_Sorted_ITK[a].begin();
+      r0e = data.rfzv_Sorted_ITK[a].end();
+
+      if (!data.endlist)
+      {
         r0 = data.rMin_ITK;
         data.endlist = true;
       }
 
       // Loop through trigger space points
       //
-      for (; r0!=r0e; ++r0) {
-	float X  = (*r0)->x();
-	float Y  = (*r0)->y();
-	float R  = (*r0)->radius();
-	if (R<m_r2minv) continue;
-        if (R>m_r2maxv) break;
-	float Z  = (*r0)->z();
-	float ax = X/R;
-	float ay = Y/R;
-
-	// Bottom links production
-	//
-	int NB = m_rfzv_n[a];
-	for (int i=0; i<NB; ++i) {	  
-	  int an = m_rfzv_i[a][i];
-	  if (!data.rfzv_map[an]) continue;
-
-	  r  =  data.rfzv_Sorted_ITK[an].begin();
-	  re =  data.rfzv_Sorted_ITK[an].end  ();
-	  
-	  for (; r!=re; ++r) {
-	    float Rb =(*r)->radius();
-	    if (Rb<m_r1minv) continue;
-            if (Rb>m_r1maxv) break;
-	    float dR = R-Rb;
-	    if (dR<m_drminv) break;
-            if (dR>m_drmax) continue;
-	    float dZ = Z-(*r)->z();
-	    float Tz = dZ/dR;
-            if (Tz<data.dzdrmin || Tz>data.dzdrmax) continue;
-	    float Zo = Z-R*Tz;
-
-	    // Comparison with vertices Z coordinates
-	    //
-	    if (!isZCompatible(data, Zo, Rb, Tz)) continue;
-
-	    // Momentum cut
-	    //
-	    float dx =(*r)->x()-X;
-	    float dy =(*r)->y()-Y;
-	    float x  = dx*ax+dy*ay;
-	    float y  =-dx*ay+dy*ax;
-	    float xy = x*x+y*y; if (xy == 0.) continue;
-	    float r2 = 1.f/xy;
-	    float Ut = x*r2;
-	    float Vt = y*r2;
-	    float UR = Ut*R+1.f; if (UR == 0.) continue;
-	    float A  = Vt*R/UR;
-	    float B  = Vt-A*Ut;
-	    if (std::abs(B*data.K) > m_ipt*sqrt(1.f+A*A)) continue;
+      for (; r0 != r0e; ++r0)
+      {
+        float X = (*r0)->x();
+        float Y = (*r0)->y();
+        float R = (*r0)->radius();
+        if (R < m_r2minv)
+          continue;
+        if (R > m_r2maxv)
+          break;
+        float Z = (*r0)->z();
+        float ax = X / R;
+        float ay = Y / R;
+
+        // Bottom links production
+        //
+        int NB = m_rfzv_n[a];
+        for (int i = 0; i < NB; ++i)
+        {
+          int an = m_rfzv_i[a][i];
+          if (!data.rfzv_map[an])
+            continue;
+
+          r = data.rfzv_Sorted_ITK[an].begin();
+          re = data.rfzv_Sorted_ITK[an].end();
+
+          for (; r != re; ++r)
+          {
+            float Rb = (*r)->radius();
+            if (Rb < m_r1minv)
+              continue;
+            if (Rb > m_r1maxv)
+              break;
+            float dR = R - Rb;
+            if (dR < m_drminv)
+              break;
+            if (dR > m_drmax)
+              continue;
+            float dZ = Z - (*r)->z();
+            float Tz = dZ / dR;
+            if (Tz < data.dzdrmin || Tz > data.dzdrmax)
+              continue;
+            float Zo = Z - R * Tz;
+
+            // Comparison with vertices Z coordinates
+            //
+            if (!isZCompatible(data, Zo, Rb, Tz))
+              continue;
+
+            // Momentum cut
+            //
+            float dx = (*r)->x() - X;
+            float dy = (*r)->y() - Y;
+            float x = dx * ax + dy * ay;
+            float y = -dx * ay + dy * ax;
+            float xy = x * x + y * y;
+            if (xy == 0.)
+              continue;
+            float r2 = 1.f / xy;
+            float Ut = x * r2;
+            float Vt = y * r2;
+            float UR = Ut * R + 1.f;
+            if (UR == 0.)
+              continue;
+            float A = Vt * R / UR;
+            float B = Vt - A * Ut;
+            if (std::abs(B * data.K) > m_ipt * sqrt(1.f + A * A))
+              continue;
             ++nseed;
-	    newSeed(data, (*r), (*r0), Zo);
-	  }
-	}
-	if (nseed < m_maxsize) continue;
-	data.endlist=false;
+            newSeed(data, (*r), (*r0), Zo);
+          }
+        }
+        if (nseed < m_maxsize)
+          continue;
+        data.endlist = false;
         data.rMin_ITK = (++r0);
-        data.fvNmin=f;
-        data.zMin=z;
-	return;
+        data.fvNmin = f;
+        data.zMin = z;
+        return;
       }
     }
   }
@@ -1189,686 +1868,1321 @@ void InDet::SiSpacePointsSeedMaker_ITK::production2Sp(EventData& data) const
 }
 
 ///////////////////////////////////////////////////////////////////
-// Production 3 space points seeds 
+// Production 3 space points seeds
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::production3Sp(EventData& data) const
-{ 
-  if (data.nsaz<3) return;
-  data.seeds_ITK.clear();
+void InDet::SiSpacePointsSeedMaker_ITK::production3Sp(EventData &data) const
+{
+  /// we need at least 3 SP in our phi-z binning, otherwise can't build 3-SP seeds
+  if (data.nsaz < 3)
+    return;
+
+  /**
+     *  This method will run a separate seed formation round
+     *  for each phi-Z region, taking the central SP from there
+     *  and allowing the top/bottom SP to come from either the same
+     *  or certain neighbouring bins. 
+     *  
+     *  The search in each region is performed in the overload of this 
+     *  method with the extended signature below. 
+     *  Here, we implement the loop over the 2D regions
+     **/
+
+  /** 
+    * Order how we walk across z. 
+    * 0-4 are negative z, 5 is central z, 6-10 are positive z.
+    * 0  1  2  3  4    5    6  7  8  9  10  z bin index
+    * --------------------------------------> Z[mm]
+    *  Z=-2500       IP,Z=0            Z=+2500
+    **/
+  const std::array<int, arraySizeZ> zBinIndex_SSS{5, 6, 4, 7, 3, 8, 2, 9, 1, 10, 0};
+  const std::array<int, arraySizeZ> zBinIndex_PPP_fast{0, 10, 1, 9, 2, 8, 5, 3, 7, 4, 6};
+  const std::array<int, arraySizeZ> zBinIndex_PPP_long{0, 1, 2, 3, 10, 9, 8, 7, 5, 4, 6};
+  const auto zBinIndex_PPP = m_fastTracking ? zBinIndex_PPP_fast : zBinIndex_PPP_long;
+  // Fast tracking runs a single iteration, either pixel or strip
+  // Default tracking runs a 0-th iteration for strip then a 1-st for pixel
+  bool isPixel = (m_fastTracking && m_pixel) || data.iteration == 1;
+  const auto zBinIndex = isPixel ? zBinIndex_PPP : zBinIndex_SSS;
+
+  const float RTmax[11] = {80., 100., 150., 200., 250., 250., 250., 200., 150., 100., 80.};
+
+  /// prepare arrays to store the iterators over the SP containers for all
+  /// neighbouring cells we wish to consider in the seed formation
+  std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> iter_topCands;
+  std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> iter_endTopCands;
+  std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> iter_bottomCands;
+  std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> iter_endBottomCands;
+
+  int nPhiBins;
+  std::array<int, arraySizePhiZ> nNeighbourCellsBottom;
+  std::array<int, arraySizePhiZ> nNeighbourCellsTop;
+  std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ> neighbourCellsBottom;
+  std::array<std::array<int, arraySizeNeighbourBins>, arraySizePhiZ> neighbourCellsTop;
+
+  if (isPixel)
+  {
+    nPhiBins = m_maxPhiBinPPP;
+    nNeighbourCellsBottom = m_nNeighbourCellsBottomPPP;
+    nNeighbourCellsTop = m_nNeighbourCellsTopPPP;
+    neighbourCellsBottom = m_neighbourCellsBottomPPP;
+    neighbourCellsTop = m_neighbourCellsTopPPP;
+  }
+  else
+  {
+    nPhiBins = m_maxPhiBinSSS;
+    nNeighbourCellsBottom = m_nNeighbourCellsBottomSSS;
+    nNeighbourCellsTop = m_nNeighbourCellsTopSSS;
+    neighbourCellsBottom = m_neighbourCellsBottomSSS;
+    neighbourCellsTop = m_neighbourCellsTopSSS;
+  }
 
-  const int   ZI[SizeZ]= {5,6,7,8,9,10,4,3,2,1,0};
-  std::list<InDet::SiSpacePointForSeedITK*>::iterator rt[9],rte[9],rb[9],rbe[9];
+  /// counter for the found
   int nseed = 0;
+  /// prevent another pass from being run when we run out of Seeds
+  data.endlist = true;
 
-  // Loop thorugh all azimuthal regions
-  //
-  for (int f=data.fNmin; f<=m_fNmax; ++f) {
-    
-    // For each azimuthal region loop through all Z regions
-    //
-    int z = 0;
-    if (!data.endlist) z = data.zMin;
-
-    for (; z<SizeZ; ++z) {
-
-      int a  = f *SizeZ+ZI[z];
-      if (!data.rfz_map[a]) continue;
-      int NB = 0, NT = 0;
-      for (int i=0; i<m_rfz_b[a]; ++i) {
-	int an =  m_rfz_ib[a][i];
-	if (!data.rfz_map[an]) continue;
-	rb [NB] = data.rfz_Sorted_ITK[an].begin();
-        rbe[NB++] = data.rfz_Sorted_ITK[an].end();
-      } 
-      for (int i=0; i<m_rfz_t[a]; ++i) {
-	int an = m_rfz_it[a][i];
-	if (!data.rfz_map[an]) continue;
-	rt [NT] = data.rfz_Sorted_ITK[an].begin();
-        rte[NT++] = data.rfz_Sorted_ITK[an].end();
-      } 
-
-      if (data.iteration == 0  && data.iteration0 ==0) production3SpSSS(data, rb, rbe, rt, rte, NB, NT, nseed);
-      else                                       production3SpPPP(data, rb, rbe, rt, rte, NB, NT, nseed);
-
-      if (!data.endlist) {
-        data.fNmin = f;
-        data.zMin = z;
-        return;
+  /// Loop through all azimuthal regions
+  for (int phiBin = data.fNmin; phiBin <= nPhiBins; ++phiBin)
+  {
+
+    /// For each azimuthal region loop through all Z regions
+    int z = (m_fastTracking && m_pixel) ? 2 : 0;
+    /// If we had to abort a previous run, continue where we left off
+    if (!data.endlist)
+      z = data.zMin;
+
+    /// note that this loop follows the order within 'zBinIndex',
+    /// not the ascending order of z regions. We start in the centre,
+    /// not at -2500 mm, and then move outward.
+    for (; z < arraySizeZ; ++z)
+    {
+
+      if (m_fastTracking && m_pixel)
+      {
+        data.RTmax = RTmax[ zBinIndex[z] ];
       }
-    }
-  }
-  data.endlist = true;
-}
 
-///////////////////////////////////////////////////////////////////
-// Production 3 pixel space points seeds for full scan
-///////////////////////////////////////////////////////////////////
+      int phiZbin = phiBin * arraySizeZ + zBinIndex[z];
 
-void InDet::SiSpacePointsSeedMaker_ITK::production3SpPPP
-(EventData& data,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rb ,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rbe,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rt ,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rte,
- int NB, int NT, int& nseed) const
-{
-  std::list<InDet::SiSpacePointForSeedITK*>::iterator r0=rb[0], r;
-  if (!data.endlist) {
-    r0 = data.rMin_ITK;
-    data.endlist = true;
+      /// can skip the rest if this particular 2D bin is empty
+      if (!data.rfz_map[phiZbin])
+        continue;
+
+      /// count how many non-emtpy cells should be searched for the
+      /// top and bottom neighbour
+      int numberBottomCells = 0;
+      int numberTopCells = 0;
+
+      /// walk through the cells in phi-z we wish to consider for the bottom SP search.
+      /// Typically, this will be 3 adjacent phi bins (including the one of the central SP)
+      /// and possibly neighbours in z on side towards the IP or on both sides,
+      /// depdending on the z region we are in
+      for (int neighbourCellNumber = 0; neighbourCellNumber < nNeighbourCellsBottom[phiZbin]; ++neighbourCellNumber)
+      {
+
+        int theNeighbourCell = neighbourCellsBottom[phiZbin][neighbourCellNumber];
+        /// only do something if this cell is populated
+        if (!data.rfz_map[theNeighbourCell])
+          continue;
+        /// plug the begin and end iterators to the SP in the cell into our array
+        iter_bottomCands[numberBottomCells] = data.rfz_Sorted_ITK[theNeighbourCell].begin();
+        iter_endBottomCands[numberBottomCells++] = data.rfz_Sorted_ITK[theNeighbourCell].end();
+      }
+
+      /// walk through the cells in phi-z we wish to consider for the top SP search.
+      /// Typically, this will be 3 adjacent phi bins (including the one of the central SP)
+      /// and possibly neighbours in z on the side opposed to the IP or on both sides,
+      /// depdending on the z region we are in
+      for (int neighbourCellNumber = 0; neighbourCellNumber < nNeighbourCellsTop[phiZbin]; ++neighbourCellNumber)
+      {
+
+        int theNeighbourCell = neighbourCellsTop[phiZbin][neighbourCellNumber];
+        /// only do something if this cell is populated
+        if (!data.rfz_map[theNeighbourCell])
+          continue;
+        /// plug the begin and end iterators to the SP in the cell into our array
+        iter_topCands[numberTopCells] = data.rfz_Sorted_ITK[theNeighbourCell].begin();
+        iter_endTopCands[numberTopCells++] = data.rfz_Sorted_ITK[theNeighbourCell].end();
+      }
+
+      /// now run the seed search for the current phi-z bin.
+      if (!data.trigger)
+      {
+        if (isPixel)
+          production3SpPPP(data, iter_bottomCands, iter_endBottomCands, iter_topCands, iter_endTopCands, numberBottomCells, numberTopCells, nseed);
+        else
+          production3SpSSS(data, iter_bottomCands, iter_endBottomCands, iter_topCands, iter_endTopCands, numberBottomCells, numberTopCells, nseed);
+      }
+      else
+        production3SpTrigger(data, iter_bottomCands, iter_endBottomCands, iter_topCands, iter_endTopCands, numberBottomCells, numberTopCells, nseed);
+    }
+
+    /** If we exceed the seed capacity, we stop here. 
+       * Save where we were in z and phi, and set endlist to false. 
+       * This will trigger another run of production3Sp when 
+       * The client calls next() after processing all vertices seen 
+       * so far (freeing up capacity). 
+       **/
+    if (nseed >= m_maxsize)
+    {
+      data.endlist = false;
+      data.fNmin = phiBin + 1;
+      return;
+    }
   }
+  /// Processed all seeds there are without aborting - no re-run needed!
+  data.endlist = true;
+}
 
-  float ipt2K = data.ipt2K;
-  float ipt2C = data.ipt2C;
-  float COFK  = data.COFK;
-  float imaxp = m_diver;
-  float imaxs = m_divermax;
+///////////////////////////////////////////////////////////////////
+// Production 3 pixel space points seeds for full scan
+///////////////////////////////////////////////////////////////////
+
+void InDet::SiSpacePointsSeedMaker_ITK::production3SpPPP(EventData &data,
+                                                         std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &iter_bottomCands,
+                                                         std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &iter_endBottomCands,
+                                                         std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &iter_topCands,
+                                                         std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &iter_endTopCands,
+                                                         const int numberBottomCells, const int numberTopCells, int &nseed) const
+{
+  /** 
+     * This method implements the seed search for a single phi-Z region of the detector. 
+     * The central SP is taken from the region, while the top and bottom SP are allowed 
+     * to come from either the same or a range of neighbouring cells. 
+     **/
+
+  /// iterator across the candidates for the central space point.
+  std::list<InDet::SiSpacePointForSeedITK *>::iterator iter_centralSP = iter_bottomCands[0];
+  std::list<InDet::SiSpacePointForSeedITK *>::iterator iter_otherSP; ///< will be used for iterating over top/bottom SP
+
+  /** 
+     * Next, we work out where we are within the ATLAS geometry.
+     * This will help us identify which space-points we need to 
+     * consider as potential central points of a seed. 
+     **/
+
+  /// find the first central SP candidate above the minimum radius.
+  for (; iter_centralSP != iter_endBottomCands[0]; ++iter_centralSP)
+  {
+    if((*iter_centralSP)->radius() > data.RTmin) break;
+  }
 
+  /// for the top candidates in the central phi-Z bin, we do not need to start at a smaller
+  /// radius than the lowest-r valid central SP candidate
+  iter_topCands[0] = iter_centralSP;
+  ++iter_topCands[0];
+
+  /// prepare cut values
+  const float &ipt2K = data.ipt2K;
+  const float &ipt2C = data.ipt2C;
+  const float &COFK = data.COFK;
+  const float &maxd0cut = m_maxdImpact;
+  const float &zmax = data.zmaxU;
+  const float &dzdrmax = data.dzdrmax;
   data.CmSp_ITK.clear();
 
-  // Loop through all trigger space points
-  //
-  for (; r0!=rbe[0]; ++r0) {
+  /// keep track of the SP storace capacity.
+  /// Extend it needed (should rarely be the case)
+  size_t SPcapacity = data.SP_ITK.size();
 
-    data.nOneSeeds = 0;
-    data.mapOneSeeds_ITK.clear();
+  /// Loop through all central space point candidates
+  for (; iter_centralSP != iter_endBottomCands[0]; ++iter_centralSP)
+  {
 
-    float R = (*r0)->radius();
+    const float &R = (*iter_centralSP)->radius();
+   
+    if(R > data.RTmax) break; ///< stop if we have moved outside our radial region of interest.
+
+    /// global coordinates of the central SP
+    const float &X = (*iter_centralSP)->x();
+    const float &Y = (*iter_centralSP)->y();
+    const float &Z = (*iter_centralSP)->z();
+
+    /// for the central SP, we veto locations on the last disk -
+    /// there would be no "outer" hits to complete a seed.
+    double absZ = std::abs(Z);
+    if (!m_fastTracking && absZ > m_zmaxPPP)
+      continue;
+
+    float covr0 = (*iter_centralSP)->covr();
+    float covz0 = (*iter_centralSP)->covz();
+    float ax = X / R;
+    float ay = Y / R;
+    float VR = maxd0cut / (R * R);
+    size_t Ntm = 2;
+    if (R > m_rmaxPPP)
+      Ntm = 1;
+
+    /// initialise a counter for found bottom links
+    /// This also serves as an index in the data.SP vector
+    size_t Nt = 0;
+
+    /// Top links production
+    /// Loop over all the cells where we expect to find such SP
+    for (int cell = 0; cell < numberTopCells; ++cell)
+    {
+
+      if (iter_otherSP == iter_endTopCands[cell])
+        continue;
+
+      /// loop over each SP in each cell
+      for (iter_otherSP = iter_topCands[cell]; iter_otherSP != iter_endTopCands[cell]; ++iter_otherSP)
+      {
+
+        /// evaluate the radial distance,
+        float Rt = (*iter_otherSP)->radius();
+        float dR = Rt - R;
+        /// and continue if we are too close
+        if (dR < m_drminPPP)
+        {
+          iter_topCands[cell] = iter_otherSP;
+          continue;
+        }
 
-    const Trk::Surface* sur0 = (*r0)->sur();
-    const Trk::Surface* surn = (*r0)->sun();
-    float               X    = (*r0)->x();
-    float               Y    = (*r0)->y();
-    float               Z    = (*r0)->z();
-    int                 Nb   = 0;
+        /// if we are too far, the next ones will be even farther, so abort
+        if (!m_fastTracking && dR > m_drmaxPPP)
+          break;
 
-    // Bottom links production
-    //
-    for (int i=0; i<NB; ++i) {
-      for (r=rb[i]; r!=rbe[i]; ++r) {	
-	float Rb =(*r)->radius();
-	float dR = R-Rb;
+        const float dz = (*iter_otherSP)->z() - Z;
+        const float dZdR = dz / dR;
+        /// Comparison with vertices Z coordinates
+        /// straight line extrapolation to r=0
+        const float z0 = Z - R * dZdR;
+        if (std::abs(z0) > zmax)
+          continue;
+
+        float dx = (*iter_otherSP)->x() - X;
+        float dy = (*iter_otherSP)->y() - Y;
+        float x = dx * ax + dy * ay;
+        float y = dy * ax - dx * ay;
+        float dxy = x * x + y * y;
+        float r2 = 1. / dxy;
+        float u = x * r2;
+        float v = y * r2;
+
+        if (std::abs(R * y) > maxd0cut * x)
+        {
+          float V0;
+          y < 0. ? V0 = VR : V0 = -VR;
+          float A = (v - V0) / (u + 1. / R);
+          float B = V0 + A / R;
+          if ((B * B) > (ipt2K * (1. + A * A)))
+            continue;
+        }
 
-	if (dR > m_drmax) {
-          rb[i]=r;
+        const float dr = std::sqrt(r2);
+        const float tz = dz * dr;
+        /// this is effectively a segment-level eta cut - exclude too shallow seed segments
+        if (std::abs(tz) > dzdrmax)
           continue;
+
+        /// add SP to the list
+        data.SP_ITK[Nt] = (*iter_otherSP);
+        data.R[Nt] = dr;                                                                                        ///< inverse distance to central SP
+        data.U[Nt] = u;                                                                                         ///< transformed U coordinate
+        data.V[Nt] = v;                                                                                         ///< transformed V coordinate
+        data.Er[Nt] = ((covz0 + (*iter_otherSP)->covz()) + (tz * tz) * (covr0 + (*iter_otherSP)->covr())) * r2; ///<squared Error on 1/tan theta coming from the space-point position errors
+        data.SP_ITK[Nt]->setDR(std::sqrt(dxy + dz * dz));
+        data.SP_ITK[Nt]->setDZDR(dZdR);
+        data.Tn[Nt].Fl = tz;
+        data.Tn[Nt].In = Nt;
+
+        /// if we are exceeding the SP capacity of our data object,
+        /// make it resize its vectors. Will add 50 slots by default,
+        /// so rarely should happen more than once per event.
+        if (++Nt == SPcapacity)
+        {
+          data.resizeSPCont();
+          SPcapacity = data.SP_ITK.size();
         }
-	if (dR < m_drmin) break;
-	if ((*r)->sur()==sur0 || (surn && surn==(*r)->sun())) continue;
-
-	float Tz = (Z-(*r)->z())/dR, aTz =std::abs(Tz);
-
-	if (aTz < data.dzdrmin || aTz > data.dzdrmax) continue;
-	
-	// Comparison with vertices Z coordinates
-	//
-	float Zo = Z-R*Tz;
-        if (!isZCompatible(data, Zo, Rb, Tz)) continue;
-	data.SP_ITK[Nb] = (*r);
-        if (++Nb==m_maxsizeSP) goto breakb;
-      }
-    }
-  breakb:
-    if (!Nb || Nb==m_maxsizeSP) continue;
-    int Nt = Nb;
-    
-    // Top   links production
-    //
-    for (int i=0; i<NT; ++i) {
-      for (r=rt[i]; r!=rte[i]; ++r) {
-	float Rt =(*r)->radius();
-	float dR = Rt-R;
-	
-	if (dR<m_drmin) {
-          rt[i]=r;
+      } ///< end of loop over SP within top candidate cell
+    }   ///< end of loop over top candidate cells
+
+    if (Nt < Ntm)
+      continue;
+
+    /// now continue with the bottom SP search.
+    /// Make the counter start at Nt, as this serves as a running
+    /// index in the SP list for this seed.
+    size_t Nb = Nt;
+
+    /// Bottom links production
+    /// Loop over all the cells where we expect to find such SP
+    for (int cell = 0; cell < numberBottomCells; ++cell)
+    {
+      /// in each cell, loop over the space points
+      for (iter_otherSP = iter_bottomCands[cell]; iter_otherSP != iter_endBottomCands[cell]; ++iter_otherSP)
+      {
+
+        /// evaluate the radial distance between the central and bottom SP
+        const float &Rb = (*iter_otherSP)->radius();
+        float dR = R - Rb;
+
+        /// if the bottom SP is too far, remember this for future iterations and
+        /// don't bother starting from the beginning again
+        if (dR > m_drmaxPPP)
+        {
+          iter_bottomCands[cell] = iter_otherSP;
           continue;
         }
-	if (dR>m_drmax) break;
+        /// if the points are too close in r, abort (future ones will be even closer).
+        if (dR < m_drminPPP)
+          break;
 
-	if ( (*r)->sur()==sur0 || (surn && surn==(*r)->sun())) continue;
+        const float dz = Z - (*iter_otherSP)->z();
+        const float dZdR = dz / dR;
+        /// Comparison with vertices Z coordinates
+        /// straight line extrapolation to r=0
+        const float z0 = Z - R * dZdR;
+        if (std::abs(z0) > zmax)
+          continue;
 
-	float Tz = ((*r)->z()-Z)/dR, aTz =std::abs(Tz);
+        float dx = (*iter_otherSP)->x() - X;
+        float dy = (*iter_otherSP)->y() - Y;
+        float x = dx * ax + dy * ay;
+        float y = dy * ax - dx * ay;
+        float dxy = x * x + y * y;
+        float r2 = 1. / dxy;
+        float u = x * r2;
+        float v = y * r2;
+
+        if (std::abs(R * y) > -maxd0cut * x)
+        {
+          float V0;
+          y > 0. ? V0 = VR : V0 = -VR;
+          float A = (v - V0) / (u + 1. / R);
+          float B = V0 + A / R;
+          if ((B * B) > (ipt2K * (1. + A * A)))
+            continue;
+        }
 
-	if (aTz < data.dzdrmin || aTz > data.dzdrmax) continue;
+        const float dr = std::sqrt(r2);
+        const float tz = dz * dr;
+        /// this is effectively a segment-level eta cut - exclude too shallow seed segments
+        if (std::abs(tz) > dzdrmax)
+          continue;
+        if (m_fastTracking && (*iter_otherSP)->radius() < 50. && std::abs(tz) > 1.5)
+          continue;
 
-	// Comparison with vertices Z coordinates
-	//
-	float Zo = Z-R*Tz;
-        if (!isZCompatible(data, Zo, R, Tz)) continue;
-  	data.SP_ITK[Nt] = (*r);
-        if (++Nt==m_maxsizeSP) goto breakt;
-      }
-    }
-    
-  breakt:
-    if (!(Nt-Nb)) continue;
-    float covr0 = (*r0)->covr ();
-    float covz0 = (*r0)->covz ();
-    float ax    = X/R;
-    float ay    = Y/R;
-
-    for (int i=0; i<Nt; ++i) {
-      InDet::SiSpacePointForSeedITK* sp = data.SP_ITK[i];
-
-      float dx  = sp->x()-X;
-      float dy  = sp->y()-Y;
-      float dz  = sp->z()-Z;
-      float x   = dx*ax+dy*ay;
-      float y   = dy*ax-dx*ay;
-      float r2  = 1.f/(x*x+y*y);
-      float dr  = std::sqrt(r2);
-      float tz  = dz*dr;
-      if (i < Nb) tz = -tz;
-
-      data.Tz[i]   = tz;
-      data.Zo[i]   = Z-R*tz;
-      data.R [i]   = dr;
-      data.U [i]   = x*r2;
-      data.V [i]   = y*r2;
-      data.Er[i]   = ((covz0+sp->covz())+(tz*tz)*(covr0+sp->covr()))*r2;
-    }
-    covr0 *= .5;
-    covz0 *= 2.;
-   
-    // Three space points comparison
-    //
-    for (int b=0; b<Nb; ++b) {    
-      float  Zob  = data.Zo[b];
-      float  Tzb  = data.Tz[b];
-      float  Rb2r = data.R [b]*covr0;
-      float  Rb2z = data.R [b]*covz0;
-      float  Erb  = data.Er[b];
-      float  Vb   = data.V [b];
-      float  Ub   = data.U [b];
-      float  Tzb2 = (1.f+Tzb*Tzb);
-      float sTzb2 = std::sqrt(Tzb2);
-      float  CSA  = Tzb2*COFK;
-      float ICSA  = Tzb2*ipt2C;
-      float imax  = imaxp;
-      if (data.SP_ITK[b]->spacepoint->clusterList().second) imax = imaxs;
-  
-      for (int t=Nb; t<Nt; ++t) {
-	float dT  = ((Tzb-data.Tz[t])*(Tzb-data.Tz[t])-data.R[t]*Rb2z-(Erb+data.Er[t]))-(data.R[t]*Rb2r)*((Tzb+data.Tz[t])*(Tzb+data.Tz[t]));
-	if (dT > ICSA) continue;
-
-	float dU  = data.U[t]-Ub;
-        if (dU == 0.) continue;
-	float A   = (data.V[t]-Vb)/dU;
-	float S2  = 1.f+A*A;
-	float B   = Vb-A*Ub;
-	float B2  = B*B;
-	if (B2  > ipt2K*S2 || dT*S2 > B2*CSA) continue;
-
-	float Im  = std::abs((A-B*R)*R);
-
-	if (Im <= imax) {
-	  float dr = data.R[b];
-          if (data.R[t] < data.R[b]) dr = data.R[t];
-          Im+=std::abs((Tzb-data.Tz[t])/(dr*sTzb2));
-	  data.CmSp_ITK.emplace_back(std::make_pair(B/std::sqrt(S2), data.SP_ITK[t]));
-          data.SP_ITK[t]->setParam(Im);
-	}
-      }
-      if (!data.CmSp_ITK.empty()) {
-        newOneSeedWithCurvaturesComparison(data, data.SP_ITK[b], (*r0), Zob);
+        /// add SP to the list
+        data.SP_ITK[Nb] = (*iter_otherSP);
+        data.R[Nb] = dr;                                                                                        ///< inverse distance to central SP
+        data.U[Nb] = u;                                                                                         ///< transformed U coordinate
+        data.V[Nb] = v;                                                                                         ///< transformed V coordinate
+        data.Er[Nb] = ((covz0 + (*iter_otherSP)->covz()) + (tz * tz) * (covr0 + (*iter_otherSP)->covr())) * r2; ///<squared Error on 1/tan theta coming from the space-point position errors
+        data.SP_ITK[Nb]->setDR(std::sqrt(dxy + dz * dz));
+        data.SP_ITK[Nb]->setDZDR(dZdR);
+        data.Tn[Nb].Fl = tz;
+        data.Tn[Nb].In = Nb;
+
+        /// if we are exceeding the SP capacity of our data object,
+        /// make it resize its vectors. Will add 50 slots by default,
+        /// so rarely should happen more than once per event.
+        if (++Nb == SPcapacity)
+        {
+          data.resizeSPCont();
+          SPcapacity = data.SP_ITK.size();
+        }
+
+      } ///< end of loop over SP in bottom candidate cell
+    }   ///< end of loop over bottom candidate cells
+
+    /// if we found no bottom candidates (remember, Nb starts counting at Nt), abort
+    if (!(Nb - Nt))
+      continue;
+
+    sort(data.Tn,0,Nt);
+    sort(data.Tn,Nt,Nb-Nt);
+
+    data.nOneSeeds = 0;
+    data.mapOneSeeds_ITK.clear();
+
+    /// Three space points comparison
+    /// first, loop over the bottom point candidates
+    size_t it0 = 0;
+    for (size_t ib = Nt; ib < Nb; ++ib)
+    {
+
+      if (it0 == Nt)
+        break;
+
+      /// retrieve the geometrical paranmeters w.r.t the central SP for this candidate
+      float Tzb = data.Tn[ib].Fl;       ///< 1/tanTheta estimate from central+bottom SP
+      int b = data.Tn[ib].In;       /// bottom seed index after sorting
+
+      float Rb2r = data.R[b] * covr0;
+      float Rb2z = data.R[b] * covz0;
+      float Erb = data.Er[b];        ///< this is the uncertainty in 1/tanTheta on the bottom segment resulting from the position errors in the 2 SP
+      float Vb = data.V[b];          ///< v-coordinate of bottom SP
+      float Ub = data.U[b];          ///< u-coordinate of bottom SP
+      float Tzb2 = (1. + Tzb * Tzb); ///< 1+1/tan²theta - converts transverse to total squared pt
+      float sTzb2 = std::sqrt(Tzb2); ///< sqrt (1+1/tan²theta) - used to convert pt to |p|
+
+      float sigmaSquaredScatteringPtDependent = Tzb2 * COFK; ///< this, when divided by the 2R², yields an approximated multiple scattering term assuming the measured pt.
+      float sigmaSquaredScatteringMinPt = Tzb2 * ipt2C;      ///< this is an approximate worst case multiple scattering term assuming the lowest
+                                                             ///  pt we allow and the estimated theta angle
+      /// max IP
+      float d0max = maxd0cut;
+
+      size_t Nc = 1;
+      if (data.SP_ITK[b]->radius() > m_rmaxPPP)
+        Nc = 0;
+      if (data.nOneSeeds)
+        ++Nc;
+
+      /// inner loop over the top point candidates
+      for (size_t it = it0; it < Nt; ++it)
+      {
+
+        int t = data.Tn[it].In; // index of top seed after sorting
+        float Tzt = data.Tn[it].Fl;
+
+        /// Apply a cut on the compatibility between the r-z slope of the two seed segments.
+        /// This is done by comparing the squared difference between slopes, and comparing
+        /// to the squared uncertainty in this difference - we keep a seed if the difference
+        /// is compatible within the assumed uncertainties.
+
+        /// average value of 1/tan(theta), approximate the slope at the location of the central space point
+        float meanOneOverTanThetaSquare = Tzb * Tzt; // SSS uses arithmetic average, PPP geometric average
+
+        /// squared error on the difference in tan(theta) due to space point position errors.
+        float sigmaSquaredSpacePointErrors = Erb + data.Er[t]                                    /// pre-computed individual squared errors on 1/tan(theta) for the two segments
+                                             + 2 * Rb2z * data.R[t]                              /// mixed term with z-uncertainty on central SP
+                                             + 2 * Rb2r * data.R[t] * meanOneOverTanThetaSquare; // mixed term with r-uncertainy on central SP
+
+        /// start out by subtracting from the squared difference in 1/tanTheta the space-point-related squared error
+        float remainingSquaredDelta = (Tzb - Tzt) * (Tzb - Tzt) - sigmaSquaredSpacePointErrors;
+
+        /// First, we test using a generous scattering term calculated assuming the minimum pt we expect
+        /// to reconstruct.
+        if (remainingSquaredDelta - sigmaSquaredScatteringMinPt > 0)
+        {
+          if (Tzb - Tzt < 0.)
+            break;
+          it0 = it + 1;
+          continue;
+        }
+
+        /**
+          * The following exploits the transformation u:=x/(x²+y²); v:=y/(x²+y²); 
+          * This is applied on the x,y coordinates in the frame described above, where the 
+          * origin is put in the central SP and the x axis defined to point directly away from the IP.
+          * 
+          * In this transformed u,v frame, what would be our circle in x-y space takes the form of  
+          * a linear function V = (-x0/y0) x U + 1/(2y0) =: A x U + B/2.
+          * Here, x0 and y0 describe the center point of the circle in the x-y frame. 
+          * As the origin of the x-y frame (the middle space point of our seed) is on the circle, 
+          * we have x0²+y0²=R² with circle radius R. 
+          * 
+          * For our seed, we can experimentally obtain A as the slope of the linear function, 
+          * delta V / delta U, 
+          * estimated using the delta U and delta V between the top and bottom space point. 
+          * 
+          * B is then obtained by inserting the obtained A into the 
+          * linear equation for the bottom SP, A x U + B/2 = V --> B = 2(V - A x U0 
+          * 
+          * With x0²+y0²=R², and x0=-A/B and y0=1/B, the radius of the circle is 
+          * then obtained as R²=(1+A²)/B². 
+          **/
+
+        float dU = data.U[t] - Ub;
+        if (dU == 0.)
+          continue;
+        float A = (data.V[t] - Vb) / dU;
+        float onePlusAsquare = 1. + A * A;
+        float B = Vb - A * Ub;
+        float BSquare = B * B;
+
+        /** With this radius (and pT) estimate, we can apply our pt cut.
+           * Reminder, ipt2K is 1 / (K x 0.9 x pt-cut)², where K translates pt into 2R. 
+           * So here we can apply the pt cut directly on the (2R)² estimate without
+           * the extra overhead of conversion / division.
+           * The second check is a refinement of the above Tz compatibility cut,
+           * replacing the sigmaSquaredScatteringMinPt scattering contribution which assumes the lowest pt 
+           * by one based on the actual estimated pt. 
+           * 
+           * The second term in this if-statement applies a second version of the 
+           * 1/tan(theta) compatibility, this time using a scattering term scaled by the actual measured
+           * pt. This refines the cut applied above, following the same logic ("delta² - sigma² ?<=0")
+           **/
+        if (BSquare > ipt2K * onePlusAsquare)
+          continue;
+        if (remainingSquaredDelta * onePlusAsquare > BSquare * sigmaSquaredScatteringPtDependent)
+        {
+          if (Tzb - Tzt < 0.)
+            break;
+          it0 = it + 1;
+          continue;
+        }
+
+        /** This is an estimate of the transverse impact parameter.
+          * The reasoning is that, in the x-y frame with the central SP as origin and 
+          * the x axis pointing away from the IP, we have for the distance between
+          * the IP and the middle of the circle: 
+          * (x0 - r_central)²+y0² = (R + d0)², 
+          * with R being the circle radius and r_central 
+          * the radial location of the central SP, placing the IP at IP at (-r_central, 0). 
+          * 
+          * First simplify using R² =x0²+y0², then apply the approximation d0²/R² ~ 0. 
+          * 
+          * Finally, consider that locally close to the central SP, the circle is parallel to the x axis, 
+          * so A = 0 --> expand (2R)²=(1+A²)/B² around this point to obtain 
+          * d0 = r_central x (r_central x B - A). 
+          * Note that below, the variable R is the radial coordinate fo the central SP, 
+          * corresponding to r_central in the notation above. 
+          **/
+        float d0 = std::abs((A - B * R) * R);
+
+        /// apply d0 cut to seed
+        if (d0 <= d0max)
+        {
+          /// evaluate distance the two closest-by SP in this seed candidate
+          float dr = data.R[b];
+          if (data.R[t] < data.R[b])
+            dr = data.R[t];
+          /// obtain a quality score - start from the d0 estimate, and add
+          /// a penalty term corresponding to how far the seed segments
+          /// deviate from a straight line in r-z
+          data.SP_ITK[t]->setScorePenalty(std::abs((Tzb - Tzt) / (dr * sTzb2)));
+          data.SP_ITK[t]->setParam(d0);
+
+          float meanOneOverTanTheta = std::sqrt(meanOneOverTanThetaSquare);
+          if (meanOneOverTanTheta < 1e-8)
+            meanOneOverTanTheta = 1e-8;
+          if (BSquare < 1e-8)
+            BSquare = 1e-8;
+          float theta = std::atan(1. / std::sqrt(meanOneOverTanThetaSquare));
+          data.SP_ITK[t]->setEta(-std::log(std::tan(0.5 * theta)));
+          data.SP_ITK[t]->setPt(std::sqrt(onePlusAsquare / BSquare) / (1000 * data.K));
+
+          /// record one possible seed candidate, sort by the curvature
+          data.CmSp_ITK.emplace_back(std::make_pair(B / std::sqrt(onePlusAsquare), data.SP_ITK[t]));
+          /// store the transverse IP, will later be used as a quality estimator
+          if (data.CmSp_ITK.size() == 500)
+            break;
+        }
+
+      } ///< end loop over top space point candidates
+      /// now apply further cleaning on the seed candidates for this central+bottom pair.
+      if (data.CmSp_ITK.size() > Nc)
+      {
+        newOneSeedWithCurvaturesComparisonPPP(data, data.SP_ITK[b], (*iter_centralSP), Z - R * Tzb);
       }
-    }
+      data.CmSp_ITK.clear(); /// cleared in newOneSeedWithCurvaturesComparisonPPP but need to also be cleared in case previous conditional statement isn't fulfilled
+    }                        ///< end loop over bottom space points
+    ///record seeds found in this run
     fillSeeds(data);
     nseed += data.fillOneSeeds;
-    if (nseed>=m_maxsize) {
-      data.endlist=false;
-      ++r0;
-      data.rMin_ITK = r0;
-      return;
-    }
-  }
+
+  } ///< end loop over central SP
 }
 
 ///////////////////////////////////////////////////////////////////
-// Production 3 SCT space points seeds for full scan
+/// Production 3 space points seeds for full scan
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::production3SpSSS
-(EventData& data,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rb ,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rbe,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rt ,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rte,
- int NB, int NT, int& nseed) const
+void InDet::SiSpacePointsSeedMaker_ITK::production3SpSSS(EventData &data,
+                                                         std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &iter_bottomCands,
+                                                         std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &iter_endBottomCands,
+                                                         std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &iter_topCands,
+                                                         std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &iter_endTopCands,
+                                                         const int numberBottomCells, const int numberTopCells, int &nseed) const
 {
-  std::list<InDet::SiSpacePointForSeedITK*>::iterator r0=rb[0], r;
-  if (!data.endlist) {
-    r0 = data.rMin_ITK;
-    data.endlist = true;
-  }
 
-  float ipt2K = data.ipt2K;
-  float ipt2C = data.ipt2C;
-  float COFK  = data.COFK;
-  float imaxs = m_divermax;
+  /** 
+     * This method implements the seed search for a single phi-Z region of the detector. 
+     * The central SP is taken from the region, while the top and bottom SP are allowed 
+     * to come from either the same or a range of neighbouring cells. 
+     **/
+
+  /// iterator across the candidates for the central space point.
+  std::list<InDet::SiSpacePointForSeedITK *>::iterator iter_centralSP = iter_bottomCands[0];
+  std::list<InDet::SiSpacePointForSeedITK *>::iterator iter_otherSP; ///< will be used for iterating over top/bottom SP
+
+  /** 
+     * Next, we work out where we are within the ATLAS geometry.
+     * This will help us identify which space-points we need to 
+     * consider as potential central points of a seed. 
+     **/
+
+  /// find the first central SP candidate above the minimum radius.
+  for (; iter_centralSP != iter_endBottomCands[0]; ++iter_centralSP)
+  {
+    if((*iter_centralSP)->radius() > data.RTmin) break;
+  }
 
+  /// for the top candidates in the central phi-Z bin, we do not need to start at a smaller
+  /// radius than the lowest-r valid central SP candidate
+  iter_topCands[0] = iter_centralSP;
+  ++iter_topCands[0];
+
+  /// prepare cut values
+  const float &ipt2K = data.ipt2K;
+  const float &ipt2C = data.ipt2C;
+  const float &COFK = data.COFK;
+  const float &maxd0cut = m_maxdImpactSSS;
+  const float &zmax = data.zmaxU;
   data.CmSp_ITK.clear();
 
-  // Loop through all trigger space points
-  //
-  for (; r0!=rbe[0]; ++r0) {
-    data.nOneSeeds = 0;
-    data.mapOneSeeds_ITK.clear();
+  /// keep track of the SP storace capacity.
+  /// Extend it needed (should rarely be the case)
+  size_t SPcapacity = data.SP_ITK.size();
 
-    float R = (*r0)->radius();
+  /// Loop through all central space point candidates
+  for (; iter_centralSP != iter_endBottomCands[0]; ++iter_centralSP)
+  {
+
+    const float &R = (*iter_centralSP)->radius();
+    
+    if(R > data.RTmax) break; ///< stop if we have moved outside our radial region of interest.
+
+    /// global coordinates of the central SP
+    const float &X = (*iter_centralSP)->x();
+    const float &Y = (*iter_centralSP)->y();
+    const float &Z = (*iter_centralSP)->z();
+
+    /// for the central SP, we veto locations on the last disk -
+    /// there would be no "outer" hits to complete a seed.
+    double absZ = std::abs(Z);
+    /// veto the last strip disk
+    if (absZ > m_zmaxSSS)
+      continue;
+
+    /// initialise a counter for found bottom links
+    /// This also serves as an index in the data.SP vector
+    size_t Nt = 0;
+
+    /// Top links production
+    /// Loop over all the cells where we expect to find such SP
+    for (int cell = 0; cell < numberTopCells; ++cell)
+    {
+
+      if (iter_otherSP == iter_endTopCands[cell])
+        continue;
+
+      for (iter_otherSP = iter_topCands[cell]; iter_otherSP != iter_endTopCands[cell]; ++iter_otherSP)
+      {
+        /// evaluate the radial distance,
+        float Rt = (*iter_otherSP)->radius();
+        float dR = Rt - R;
+        if (dR >= m_drminSSS)
+          break;
+      }
+      iter_topCands[cell] = iter_otherSP;
 
-    const Trk::Surface* sur0 = (*r0)->sur();
-    const Trk::Surface* surn = (*r0)->sun();
-    float               X    = (*r0)->x();
-    float               Y    = (*r0)->y();
-    float               Z    = (*r0)->z();
-    int                 Nb   = 0;
+      /// loop over each SP in each cell
+      for (iter_otherSP = iter_topCands[cell]; iter_otherSP != iter_endTopCands[cell]; ++iter_otherSP)
+      {
 
-    // Bottom links production
-    //
-    for (int i=0; i<NB; ++i) {
-      for (r=rb[i]; r!=rbe[i]; ++r) {
-	float Rb =(*r)->radius();
-	float dR = R-Rb;
-	if (dR > m_drmax) {
-          rb[i]=r;
+        /// evaluate the radial distance,
+        float Rt = (*iter_otherSP)->radius();
+        float dR = Rt - R;
+        /// if we are to far, the next ones will be even farther, so abort
+        if (dR > m_drmaxSSS)
+          break;
+
+        const float dz = (*iter_otherSP)->z() - Z;
+        const float dZdR = dz / dR;
+
+        /// Comparison with vertices Z coordinates
+        /// straight line extrapolation to r=0
+        const float z0 = Z - R * dZdR;
+        if (std::abs(dz) > m_dzmaxSSS || std::abs(z0) > zmax)
           continue;
+
+        /// add SP to the list
+        data.SP_ITK[Nt] = (*iter_otherSP);
+        data.SP_ITK[Nt]->setDZDR(dZdR);
+        /// if we are exceeding the SP capacity of our data object,
+        /// make it resize its vectors. Will add 50 slots by default,
+        /// so rarely should happen more than once per event.
+        if (++Nt == SPcapacity)
+        {
+          data.resizeSPCont();
+          SPcapacity = data.SP_ITK.size();
         }
-	if (dR < m_drmin) break;
-	if ((*r)->sur()==sur0 || (surn && surn==(*r)->sun())) continue;
-	float Tz = (Z-(*r)->z())/dR;
-        float aTz =std::abs(Tz);
-	if (aTz < data.dzdrmin || aTz > data.dzdrmax) continue;
-
-	// Comparison with vertices Z coordinates
-	//
-	float Zo = Z-R*Tz;
-        if (!isZCompatible(data, Zo, Rb, Tz)) continue;
-	data.SP_ITK[Nb] = (*r);
-        if (++Nb==m_maxsizeSP) goto breakb;
-      }
-    }
-  breakb:
-    if (!Nb || Nb==m_maxsizeSP) continue;
-    int Nt = Nb;
-    
-    // Top   links production
-    //
-    for (int i=0; i<NT; ++i) {
-      for (r=rt[i]; r!=rte[i]; ++r) {
-	float Rt =(*r)->radius();
-	float dR = Rt-R;
-	
-	if (dR<m_drmin) {
-          rt[i]=r; 
+      } ///< end of loop over SP within top candidate cell
+    }   ///< end of loop over top candidate cells
+
+    /// if we did not find ANY top SP, or if we exceed the storage capacity, we abort this seed candidate.
+    if (!Nt)
+      continue;
+
+    /// now continue with the bottom SP search.
+    /// Make the counter start at Nt, as this serves as a running
+    /// index in the SP list for this seed.
+    size_t Nb = Nt;
+
+    /// Bottom links production
+    /// Loop over all the cells where we expect to find such SP
+    for (int cell = 0; cell < numberBottomCells; ++cell)
+    {
+      /// in each cell, loop over the space points
+      for (iter_otherSP = iter_bottomCands[cell]; iter_otherSP != iter_endBottomCands[cell]; ++iter_otherSP)
+      {
+
+        /// evaluate the radial distance between the central and bottom SP
+        const float &Rb = (*iter_otherSP)->radius();
+        float dR = R - Rb;
+
+        /// if the bottom SP is too far, remember this for future iterations and
+        /// don't bother starting from the beginning again
+        if (dR > m_drmaxSSS)
+        {
+          iter_bottomCands[cell] = iter_otherSP;
           continue;
         }
-	if (dR>m_drmax) break;
-
-	if ((*r)->sur()==sur0 || (surn && surn==(*r)->sun())) continue;
-	float Tz = ((*r)->z()-Z)/dR;
-        float aTz = std::abs(Tz);
-	if (aTz < data.dzdrmin || aTz > data.dzdrmax) continue;
-
-	// Comparison with vertices Z coordinates
-	//
-	float Zo = Z-R*Tz;
-        if (!isZCompatible(data, Zo, R, Tz)) continue;
-  	data.SP_ITK[Nt] = (*r);
-        if (++Nt==m_maxsizeSP) goto breakt;
-      }
-    }
-    
-  breakt:
-    if (!(Nt-Nb)) continue;
-    float covr0 = (*r0)->covr();
-    float covz0 = (*r0)->covz();
-    float ax    = X/R;
-    float ay    = Y/R;
-
-    for (int i=0; i<Nt; ++i) {
-      InDet::SiSpacePointForSeedITK* sp = data.SP_ITK[i];
-
-      float dx  = sp->x()-X;
-      float dy  = sp->y()-Y;
-      float dz  = sp->z()-Z;
-      float x   = dx*ax+dy*ay;
-      float y   = dy*ax-dx*ay;
-      float r2  = 1.f/(x*x+y*y);
-      float dr  = std::sqrt(r2);
-      float tz  = dz*dr;
-      if (i < Nb) tz = -tz;
-
-      data.X [i]   = x;
-      data.Y [i]   = y;
-      data.Tz[i]   = tz;
-      data.Zo[i]   = Z-R*tz;
-      data.R [i]   = dr;
-      data.U [i]   = x*r2;
-      data.V [i]   = y*r2;
-      data.Er[i]   = ((covz0+sp->covz())+(tz*tz)*(covr0+sp->covr()))*r2;
+        /// if the points are too close in r, abort (future ones will be even closer).
+        if (dR < m_drminSSS)
+          break;
+
+        const float dz = Z - (*iter_otherSP)->z();
+        const float dZdR = dz / dR;
+
+        /// Comparison with vertices Z coordinates
+        /// straight line extrapolation to r=0
+        const float z0 = Z - R * dZdR;
+        if (std::abs(dz) > m_dzmaxSSS || std::abs(z0) > zmax)
+          continue;
+        /// found a bottom SP candidate, write it into the data object
+        data.SP_ITK[Nb] = (*iter_otherSP);
+        data.SP_ITK[Nb]->setDZDR(dZdR);
+        /// if we are exceeding the SP capacity of our data object,
+        /// make it resize its vectors. Will add 50 slots by default,
+        /// so rarely should happen more than once per event.
+        if (++Nb == SPcapacity)
+        {
+          data.resizeSPCont();
+          SPcapacity = data.SP_ITK.size();
+        }
+      } ///< end of loop over SP in bottom candidate cell
+    }   ///< end of loop over bottom candidate cells
+
+    /// if we found no bottom candidates (remember, Nb starts counting at Nt), abort
+    if (!(Nb - Nt))
+      continue;
+
+    /// get covariance on r and z for the central SP
+    float covr0 = (*iter_centralSP)->covr();
+    float covz0 = (*iter_centralSP)->covz();
+
+    /// build a unit direction vector pointing from the IP to the central SP
+    float ax = X / R;
+    float ay = Y / R;
+
+    /// check all SP candidates we found during our loop and
+    /// compute geometrical variables w.r.t the central point.
+    for (size_t i = 0; i < Nb; ++i)
+    {
+
+      InDet::SiSpacePointForSeedITK *sp = data.SP_ITK[i];
+
+      /// transform the space point coordinates into a frame centered around the middle SP,
+      /// where the x axis points away from the detector frame origin
+      float dx = sp->x() - X;
+      float dy = sp->y() - Y;
+      float dz = sp->z() - Z;
+      float x = dx * ax + dy * ay;
+      float y = dy * ax - dx * ay;
+
+      /// inverse square distance of the candidate space point to the central point
+      float r2 = 1. / (x * x + y * y);
+      /// inverse distance of the candidate space point to the central point
+      float dr = std::sqrt(r2);
+      /// estimate slope in z - distance traveled in transverse plane vs z direction.
+      /// rough estimate of 1/tan theta from 2 points
+      float tz = dz * dr;
+
+      /// if we are looking at a bottom SP candidate, flip the sign to account for
+      /// different direction of flight (from bottom to central)
+      if (i >= Nt)
+        tz = -tz;
+
+      /// save this into our data object
+      data.X[i] = x;
+      data.Y[i] = y;
+      data.Tz[i] = tz;                                                             ///< 1/ tan theta
+      data.Zo[i] = Z - R * tz;                                                     ///< z0 estimate.
+      data.R[i] = dr;                                                              ///< inverse distance to central SP
+      data.U[i] = x * r2;                                                          ///< transformed U coordinate
+      data.V[i] = y * r2;                                                          ///< transformed V coordinate
+      data.Er[i] = ((covz0 + sp->covz()) + (tz * tz) * (covr0 + sp->covr())) * r2; ///<squared Error on 1/tan theta coming from the space-point position errors
     }
-    covr0 *= .5;
-    covz0 *= 2.;
-   
-    // Three space points comparison
-    //
-    for (int b=0; b<Nb; ++b) {
-      float  Zob  = data.Zo[b];
-      float  Tzb  = data.Tz[b];
-      float  Rb2r = data.R [b]*covr0;
-      float  Rb2z = data.R [b]*covz0;
-      float  Erb  = data.Er[b];
-      float  Vb   = data.V [b];
-      float  Ub   = data.U [b];
-      float  Tzb2 = (1.f+Tzb*Tzb);
-      float sTzb2 = std::sqrt(Tzb2);
-      float  CSA  = Tzb2*COFK;
-      float ICSA  = Tzb2*ipt2C;
-      float imax  = imaxs;
-      
-      float Se    = 1.f/sqrt(1.f+Tzb*Tzb);
-      float Ce    = Se*Tzb;
-      float Sx    = Se*ax;
-      float Sy    = Se*ay;
-
-      for (int t=Nb; t<Nt; ++t) {
-	// Trigger point
-	//	
-	float dU0   =  data.U[t]-Ub;
-        if (dU0 == 0.) continue; 
-	float A0    = (data.V[t]-Vb)/dU0;
-	float C0    = 1.f/std::sqrt(1.f+A0*A0); 
-	float S0    = A0*C0;
-	float d0[3] = {Sx*C0-Sy*S0, Sx*S0+Sy*C0, Ce};
-	float rn[3];
-        if (!(*r0)->coordinates(d0,rn)) continue;
-
-	// Bottom  point
-	//
-	float B0    = 2.*(Vb-A0*Ub);
-	float Cb    = (1.-B0*data.Y[b])*C0;
-	float Sb    = (A0+B0*data.X[b])*C0;
-	float db[3] = {Sx*Cb-Sy*Sb,Sx*Sb+Sy*Cb,Ce};
-	float rbDup[3]; //a new and different rb
-	if (!data.SP_ITK[b]->coordinates(db,rbDup)) continue;
-
-	// Top     point
-	//
-	float Ct    = (1.f-B0*data.Y[t])*C0;
-	float St    = (A0+B0*data.X[t])*C0;
-	float dt[3] = {Sx*Ct-Sy*St,Sx*St+Sy*Ct,Ce};
-	float rtDup[3]; //doesnt hide previous declaration of rt
-	if (!data.SP_ITK[t]->coordinates(dt,rtDup)) continue;
-
-	float xb    = rbDup[0]-rn[0];
-	float yb    = rbDup[1]-rn[1];
-	float xt    = rtDup[0]-rn[0];
-	float yt    = rtDup[1]-rn[1];
-
-	float rb2   = 1.f/(xb*xb+yb*yb);
-	float rt2   = 1.f/(xt*xt+yt*yt);
-	
-	float tb    =  (rn[2]-rbDup[2])*std::sqrt(rb2);
-	float tz    =  (rtDup[2]-rn[2])*std::sqrt(rt2);
-
-	float dT  = ((tb-tz)*(tb-tz)-data.R[t]*Rb2z-(Erb+data.Er[t]))-(data.R[t]*Rb2r)*((tb+tz)*(tb+tz));
-	if ( dT > ICSA) continue;
-
-	float Rn    = std::sqrt(rn[0]*rn[0]+rn[1]*rn[1]);
-	float Ax    = rn[0]/Rn;
-	float Ay    = rn[1]/Rn;
-
-	float ub    = (xb*Ax+yb*Ay)*rb2;
-	float vb    = (yb*Ax-xb*Ay)*rb2;
-	float ut    = (xt*Ax+yt*Ay)*rt2;
-	float vt    = (yt*Ax-xt*Ay)*rt2;
-	
-	float dU  = ut-ub;
-	if (dU == 0.) continue;
-	float A   = (vt-vb)/dU;
-	float S2  = 1.f+A*A;
-	float B   = vb-A*ub;
-	float B2  = B*B;
-	if (B2 > ipt2K*S2 || dT*S2 > B2*CSA) continue;
-
-	float Im  = std::abs((A-B*Rn)*Rn);
-
-	if (Im <= imax) {
-	  float dr;
-	  data.R[t] < data.R[b] ? dr = data.R[t] : dr = data.R[b];
-	  Im+=std::abs((Tzb-data.Tz[t])/(dr*sTzb2));
-	  data.CmSp_ITK.emplace_back(std::make_pair(B/std::sqrt(S2), data.SP_ITK[t]));
-	  data.SP_ITK[t]->setParam(Im);
-	}
-	
-      }
-      if (!data.CmSp_ITK.empty()) {
-        newOneSeedWithCurvaturesComparison(data, data.SP_ITK[b], (*r0), Zob);
+
+    data.nOneSeeds = 0;
+    data.mapOneSeeds_ITK.clear();
+
+    /// Three space points comparison
+    /// first, loop over the bottom point candidates
+    for (size_t b = Nt; b < Nb; ++b)
+    {
+
+      /// retrieve the geometrical paranmeters w.r.t the central SP for this candidate
+      float Zob = data.Zo[b]; ///< z0 estimate from central+bottom SP
+      float Tzb = data.Tz[b]; ///< 1/tanTheta estimate from central+bottom SP
+      float Rb2r = data.R[b] * covr0;
+      float Rb2z = data.R[b] * covz0;
+      float Erb = data.Er[b];        ///< this is the uncertainty in 1/tanTheta on the bottom segment resulting from the position errors in the 2 SP
+      float Vb = data.V[b];          ///< v-coordinate of bottom SP
+      float Ub = data.U[b];          ///< u-coordinate of bottom SP
+      float Tzb2 = (1. + Tzb * Tzb); ///< 1+1/tan²theta - converts transverse to total squared pt
+      float sTzb2 = std::sqrt(Tzb2); ///< sqrt (1+1/tan²theta) - used to convert pt to |p|
+      float Se = 1. / std::sqrt(Tzb2);
+      float Ce = Se * Tzb;
+      float Sx = Se * ax;
+      float Sy = Se * ay;
+
+      float sigmaSquaredScatteringPtDependent = Tzb2 * COFK; ///< this, when divided by the 2R², yields an approximated multiple scattering term assuming the measured pt.
+      float sigmaSquaredScatteringMinPt = Tzb2 * ipt2C;      ///< this is an approximate worst case multiple scattering term assuming the lowest
+                                                             ///  pt we allow and the estimated theta angle
+      /// max IP
+      float d0max = maxd0cut;
+
+      /// inner loop over the top point candidates
+      for (size_t t = 0; t < Nt; ++t)
+      {
+
+        /** 
+          * The following exploits the transformation u:=x/(x²+y²); v:=y/(x²+y²); 
+          * This is applied on the x,y coordinates in the frame described above, where the 
+          * origin is put in the central SP and the x axis defined to point directly away from the IP.
+          * 
+          * In this transformed u,v frame, what would be our circle in x-y space takes the form of  
+          * a linear function V = (-x0/y0) x U + 1/(2y0) =: A x U + B/2.
+          * Here, x0 and y0 describe the center point of the circle in the x-y frame. 
+          * As the origin of the x-y frame (the middle space point of our seed) is on the circle, 
+          * we have x0²+y0²=R² with circle radius R. 
+          * 
+          * For our seed, we can experimentally obtain A as the slope of the linear function, 
+          * delta V / delta U, 
+          * estimated using the delta U and delta V between the top and bottom space point. 
+          * 
+          * B is then obtained by inserting the obtained A into the 
+          * linear equation for the bottom SP, A x U + B/2 = V --> B = 2(V - A x U0 
+          * 
+          * With x0²+y0²=R², and x0=-A/B and y0=1/B, the radius of the circle is 
+          * then obtained as R²=(1+A²)/B². 
+          **/
+
+        // Trigger point
+        //
+        float dU0 = data.U[t] - Ub;
+        if (dU0 == 0.)
+          continue;
+        float A0 = (data.V[t] - Vb) / dU0;
+
+        float Cn = Ce * std::sqrt(1. + A0 * A0);
+
+        float dn[3] = {Sx - Sy * A0, Sx * A0 + Sy, Cn};
+        float rn[3];
+        if (!(*iter_centralSP)->coordinates(dn, rn))
+          continue;
+
+        // Bottom  point
+        //
+        float B0 = 2. * (Vb - A0 * Ub);
+        float Cb = 1. - B0 * data.Y[b];
+        float Sb = A0 + B0 * data.X[b];
+        float db[3] = {Sx * Cb - Sy * Sb, Sx * Sb + Sy * Cb, Cn};
+        float rb[3];
+        if (!data.SP_ITK[b]->coordinates(db, rb))
+          continue;
+
+        // Top     point
+        //
+        float Ct = 1. - B0 * data.Y[t];
+        float St = A0 + B0 * data.X[t];
+        float dt[3] = {Sx * Ct - Sy * St, Sx * St + Sy * Ct, Cn};
+        float rt[3];
+        if (!data.SP_ITK[t]->coordinates(dt, rt))
+          continue;
+
+        float xb = rb[0] - rn[0];
+        float yb = rb[1] - rn[1];
+        float zb = rb[2] - rn[2];
+        float xt = rt[0] - rn[0];
+        float yt = rt[1] - rn[1];
+        float zt = rt[2] - rn[2];
+
+        float rb2 = 1. / (xb * xb + yb * yb);
+        float rt2 = 1. / (xt * xt + yt * yt);
+
+        float tb = -zb * std::sqrt(rb2);
+        float tz = zt * std::sqrt(rt2);
+
+        /// Apply a cut on the compatibility between the r-z slope of the two seed segments.
+        /// This is done by comparing the squared difference between slopes, and comparing
+        /// to the squared uncertainty in this difference - we keep a seed if the difference
+        /// is compatible within the assumed uncertainties.
+
+        /// average value of 1/tan(theta), approximate the slope at the location of the central space point
+        float meanOneOverTanTheta = (tb + tz) / 2.;
+
+        /// squared error on the difference in tan(theta) due to space point position errors.
+        float sigmaSquaredSpacePointErrors = Erb + data.Er[t]                                                    /// pre-computed individual squared errors on 1/tan(theta) for the two segments
+                                             + 2 * Rb2z * data.R[t]                                              /// mixed term with z-uncertainty on central SP
+                                             + 2 * Rb2r * data.R[t] * meanOneOverTanTheta * meanOneOverTanTheta; // mixed term with r-uncertainy on central SP
+
+        /// start out by subtracting from the squared difference in 1/tanTheta the space-point-related squared error
+        float remainingSquaredDelta = (tb - tz) * (tb - tz) - sigmaSquaredSpacePointErrors;
+
+        /// First, we test using a generous scattering term calculated assuming the minimum pt we expect
+        /// to reconstruct.
+        if (remainingSquaredDelta - sigmaSquaredScatteringMinPt > 0)
+          continue;
+
+        float Rn = std::sqrt(rn[0] * rn[0] + rn[1] * rn[1]);
+        float Ax = rn[0] / Rn;
+        float Ay = rn[1] / Rn;
+
+        float ub = (xb * Ax + yb * Ay) * rb2;
+        float vb = (yb * Ax - xb * Ay) * rb2;
+        float ut = (xt * Ax + yt * Ay) * rt2;
+        float vt = (yt * Ax - xt * Ay) * rt2;
+
+        float dU = ut - ub;
+        if (dU == 0.)
+          continue;
+        float A = (vt - vb) / dU;
+        float onePlusAsquare = 1. + A * A;
+        float B = vb - A * ub;
+        float BSquare = B * B;
+
+        /** With this radius (and pT) estimate, we can apply our pt cut.
+     * Reminder, ipt2K is 1 / (K x 0.9 x pt-cut)², where K translates pt into 2R.
+     * So here we can apply the pt cut directly on the (2R)² estimate without
+     * the extra overhead of conversion / division.
+     * The second check is a refinement of the above Tz compatibility cut,
+     * replacing the sigmaSquaredScatteringMinPt scattering contribution which assumes the lowest pt
+     * by one based on the actual estimated pt.
+     *
+     * The second term in this if-statement applies a second version of the
+     * 1/tan(theta) compatibility, this time using a scattering term scaled by the actual measured
+     * pt. This refines the cut applied above, following the same logic ("delta² - sigma² ?<=0")
+     **/
+        if (BSquare > ipt2K * onePlusAsquare || remainingSquaredDelta * onePlusAsquare > BSquare * sigmaSquaredScatteringPtDependent)
+          continue;
+
+        /** This is an estimate of the transverse impact parameter.
+     * The reasoning is that, in the x-y frame with the central SP as origin and
+     * the x axis pointing away from the IP, we have for the distance between
+     * the IP and the middle of the circle:
+     * (x0 - r_central)²+y0² = (R + d0)²,
+     * with R being the circle radius and r_central
+     * the radial location of the central SP, placing the IP at IP at (-r_central, 0).
+     *
+     * First simplify using R² =x0²+y0², then apply the approximation d0²/R² ~ 0.
+     *
+     * Finally, consider that locally close to the central SP, the circle is parallel to the x axis,
+     * so A = 0 --> expand (2R)²=(1+A²)/B² around this point to obtain
+     * d0 = r_central x (r_central x B - A).
+     * Note that below, the variable R is the radial coordinate fo the central SP,
+     * corresponding to r_central in the notation above.
+     **/
+        float d0 = std::abs((A - B * Rn) * Rn);
+
+        /// apply d0 cut to seed
+        if (d0 <= d0max)
+        {
+          /// evaluate distance the two closest-by SP in this seed candidate
+          float dr = std::sqrt(1 / rb2);
+          if (data.R[t] < data.R[b])
+            dr = std::sqrt(1 / rt2);
+          /// obtain a quality score - start from the d0 estimate, and add
+          /// a penalty term corresponding to how far the seed segments
+          /// deviate from a straight line in r-z
+          data.SP_ITK[t]->setScorePenalty(std::abs((tb - tz) / (dr * sTzb2)));
+          data.SP_ITK[t]->setParam(d0);
+          float DR = xt * xt + yt * yt + zt * zt; // distance between top and central SP
+          data.SP_ITK[t]->setDR(DR);
+
+          if (meanOneOverTanTheta < 1e-8)
+            meanOneOverTanTheta = 1e-8;
+          if (BSquare < 1e-8)
+            BSquare = 1e-8;
+          float theta = std::atan(1. / meanOneOverTanTheta);
+          data.SP_ITK[t]->setEta(-std::log(std::tan(0.5 * theta)));
+          data.SP_ITK[t]->setPt(std::sqrt(onePlusAsquare / BSquare) / (1000 * data.K));
+
+          /// record one possible seed candidate, sort by the curvature
+          data.CmSp_ITK.emplace_back(std::make_pair(B / std::sqrt(onePlusAsquare), data.SP_ITK[t]));
+          /// store the transverse IP, will later be used as a quality estimator
+          if (data.CmSp_ITK.size() == 500)
+            break;
+        }
+
+      } ///< end loop over top space point candidates
+      /// now apply further cleaning on the seed candidates for this central+bottom pair.
+      if (!data.CmSp_ITK.empty())
+      {
+        newOneSeedWithCurvaturesComparisonSSS(data, data.SP_ITK[b], (*iter_centralSP), Zob);
       }
-    }
+    } ///< end loop over bottom space points
+    ///record seeds found in this run
     fillSeeds(data);
     nseed += data.fillOneSeeds;
-    if (nseed>=m_maxsize) {
-      data.endlist=false;
-      ++r0;
-      data.rMin_ITK = r0;
-      return;
-    }
-  }
+
+  } ///< end loop over central SP
 }
 
 ///////////////////////////////////////////////////////////////////
 // Production 3 space points seeds in ROI
 ///////////////////////////////////////////////////////////////////
 
- 
-void InDet::SiSpacePointsSeedMaker_ITK::production3SpTrigger
-(EventData& data,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rb ,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rbe,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rt ,
- std::list<InDet::SiSpacePointForSeedITK*>::iterator* rte,
- int NB, int NT, int& nseed) const
+void InDet::SiSpacePointsSeedMaker_ITK::production3SpTrigger(EventData &data,
+                                                             std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &rb,
+                                                             std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &rbe,
+                                                             std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &rt,
+                                                             std::array<std::list<InDet::SiSpacePointForSeedITK *>::iterator, arraySizeNeighbourBins> &rte,
+                                                             const int numberBottomCells, const int numberTopCells, int &nseed) const
 {
-  std::list<InDet::SiSpacePointForSeedITK*>::iterator r0=rb[0], r;
-  if (!data.endlist) {
+  std::list<InDet::SiSpacePointForSeedITK *>::iterator r0 = rb[0], r;
+  if (!data.endlist)
+  {
     r0 = data.rMin_ITK;
     data.endlist = true;
   }
 
-  constexpr float pi2 = 2.*M_PI;
+  constexpr float pi2 = 2. * M_PI;
 
   float ipt2K = data.ipt2K;
   float ipt2C = data.ipt2C;
-  float COFK  = data.COFK;
-  float imaxp = m_diver;
-  float imaxs = m_diversss;
+  float COFK = data.COFK;
+  float imaxp = m_maxdImpact;
+  float imaxs = m_maxdImpactSSS;
 
   data.CmSp_ITK.clear();
 
   // Loop through all trigger space points
   //
-  for (; r0!=rbe[0]; ++r0) {
+  for (; r0 != rbe[0]; ++r0)
+  {
     data.nOneSeeds = 0;
     data.mapOneSeeds_ITK.clear();
-	
+
     float R = (*r0)->radius();
 
-    const Trk::Surface* sur0 = (*r0)->sur();
-    float               X    = (*r0)->x();
-    float               Y    = (*r0)->y();
-    float               Z    = (*r0)->z();
-    int                 Nb   = 0;
+    const Trk::Surface *sur0 = (*r0)->sur();
+    float X = (*r0)->x();
+    float Y = (*r0)->y();
+    float Z = (*r0)->z();
+    int Nb = 0;
 
     // Bottom links production
     //
-    for (int i=0; i<NB; ++i) {
-      for (r=rb[i]; r!=rbe[i]; ++r) {
-	float Rb =(*r)->radius();
-
-	float dR = R-Rb;
-	if (dR < m_drmin || (data.iteration && (*r)->spacepoint->clusterList().second)) break;
-	if (dR > m_drmax || (*r)->sur()==sur0) continue;
-
-	// Comparison with  bottom and top Z 
-	//
-	float Tz = (Z-(*r)->z())/dR;
-	float Zo = Z-R*Tz;
-        if (Zo < data.zminB || Zo > data.zmaxB) continue;
-	float Zu = Z+(550.f-R)*Tz;
-        if (Zu < data.zminU || Zu > data.zmaxU) continue;
-	data.SP_ITK[Nb] = (*r);
-        if (++Nb==m_maxsizeSP) goto breakb;
+    for (int i = 0; i < numberBottomCells; ++i)
+    {
+      for (r = rb[i]; r != rbe[i]; ++r)
+      {
+        float Rb = (*r)->radius();
+
+        float dR = R - Rb;
+        if (dR < m_drmin || (data.iteration && (*r)->spacepoint->clusterList().second))
+          break;
+        if (dR > m_drmax || (*r)->sur() == sur0)
+          continue;
+
+        // Comparison with  bottom and top Z
+        //
+        float Tz = (Z - (*r)->z()) / dR;
+        float Zo = Z - R * Tz;
+        if (Zo < data.zminB || Zo > data.zmaxB)
+          continue;
+        float Zu = Z + (550.f - R) * Tz;
+        if (Zu < data.zminU || Zu > data.zmaxU)
+          continue;
+        data.SP_ITK[Nb] = (*r);
+        if (++Nb == m_maxsizeSP)
+          goto breakb;
       }
     }
   breakb:
-    if (!Nb || Nb==m_maxsizeSP) continue;
+    if (!Nb || Nb == m_maxsizeSP)
+      continue;
     int Nt = Nb;
-    
+
     // Top   links production
     //
-    for (int i=0; i<NT; ++i) {
-      for (r=rt[i]; r!=rte[i]; ++r) {
-	float Rt =(*r)->radius();
-	float dR = Rt-R;
-	
-	if (dR<m_drmin) {
-          rt[i]=r;
+    for (int i = 0; i < numberTopCells; ++i)
+    {
+      for (r = rt[i]; r != rte[i]; ++r)
+      {
+        float Rt = (*r)->radius();
+        float dR = Rt - R;
+
+        if (dR < m_drmin)
+        {
+          rt[i] = r;
           continue;
         }
-	if (dR>m_drmax) break;
-
-	if ((*r)->sur()==sur0) continue;
-
-	// Comparison with  bottom and top Z 
-	//
-	float Tz = ((*r)->z()-Z)/dR;
-	float Zo = Z-R*Tz;
-        if (Zo < data.zminB || Zo > data.zmaxB) continue;
-	float Zu = Z+(550.f-R)*Tz;
-        if (Zu < data.zminU || Zu > data.zmaxU) continue;
-  	data.SP_ITK[Nt] = (*r);
-        if (++Nt==m_maxsizeSP) goto breakt;
+        if (dR > m_drmax)
+          break;
+
+        if ((*r)->sur() == sur0)
+          continue;
+
+        // Comparison with  bottom and top Z
+        //
+        float Tz = ((*r)->z() - Z) / dR;
+        float Zo = Z - R * Tz;
+        if (Zo < data.zminB || Zo > data.zmaxB)
+          continue;
+        float Zu = Z + (550.f - R) * Tz;
+        if (Zu < data.zminU || Zu > data.zmaxU)
+          continue;
+        data.SP_ITK[Nt] = (*r);
+        if (++Nt == m_maxsizeSP)
+          goto breakt;
       }
     }
-    
+
   breakt:
-    if (!(Nt-Nb)) continue;
-    float covr0 = (*r0)->covr ();
-    float covz0 = (*r0)->covz ();
+    if (!(Nt - Nb))
+      continue;
+    float covr0 = (*r0)->covr();
+    float covz0 = (*r0)->covz();
 
-    float ax = X/R;
-    float ay = Y/R;
-    
-    for (int i=0; i<Nt; ++i) {
-      InDet::SiSpacePointForSeedITK* sp = data.SP_ITK[i];
-
-      float dx  = sp->x()-X;
-      float dy  = sp->y()-Y;
-      float dz  = sp->z()-Z;
-      float x   = dx*ax+dy*ay;
-      float y   = dy*ax-dx*ay;
-      float r2  = 1.f/(x*x+y*y);
-      float dr  = std::sqrt(r2);
-      float tz  = dz*dr;
-      if (i < Nb) tz = -tz;
-
-      data.X [i]   = x;
-      data.Y [i]   = y;
-      data.Tz[i]   = tz;
-      data.Zo[i]   = Z-R*tz;
-      data.R [i]   = dr;
-      data.U [i]   = x*r2;
-      data.V [i]   = y*r2;
-      data.Er[i]   = ((covz0+sp->covz())+(tz*tz)*(covr0+sp->covr()))*r2;
+    float ax = X / R;
+    float ay = Y / R;
+
+    for (int i = 0; i < Nt; ++i)
+    {
+      InDet::SiSpacePointForSeedITK *sp = data.SP_ITK[i];
+
+      float dx = sp->x() - X;
+      float dy = sp->y() - Y;
+      float dz = sp->z() - Z;
+      float x = dx * ax + dy * ay;
+      float y = dy * ax - dx * ay;
+      float r2 = 1.f / (x * x + y * y);
+      float dr = std::sqrt(r2);
+      float tz = dz * dr;
+      if (i < Nb)
+        tz = -tz;
+
+      data.X[i] = x;
+      data.Y[i] = y;
+      data.Tz[i] = tz;
+      data.Zo[i] = Z - R * tz;
+      data.R[i] = dr;
+      data.U[i] = x * r2;
+      data.V[i] = y * r2;
+      data.Er[i] = ((covz0 + sp->covz()) + (tz * tz) * (covr0 + sp->covr())) * r2;
     }
     covr0 *= .5;
     covz0 *= 2.;
-   
+
     // Three space points comparison
     //
-    for (int b=0; b<Nb; ++b) {
-      float  Zob  = data.Zo[b];
-      float  Tzb  = data.Tz[b];
-      float  Rb2r = data.R [b]*covr0;
-      float  Rb2z = data.R [b]*covz0;
-      float  Erb  = data.Er[b];
-      float  Vb   = data.V [b];
-      float  Ub   = data.U [b];
-      float  Tzb2 = (1.f+Tzb*Tzb);
-      float  CSA  = Tzb2*COFK;
-      float ICSA  = Tzb2*ipt2C;
-      float imax  = imaxp;
-      if (data.SP_ITK[b]->spacepoint->clusterList().second) imax = imaxs;
-      
-      for (int t=Nb;  t!=Nt; ++t) {
-	float dT  = ((Tzb-data.Tz[t])*(Tzb-data.Tz[t])-data.R[t]*Rb2z-(Erb+data.Er[t]))-(data.R[t]*Rb2r)*((Tzb+data.Tz[t])*(Tzb+data.Tz[t]));
-	if ( dT > ICSA) continue;
-	float dU  = data.U[t]-Ub;
-        if (dU == 0.) continue;
-	float A   = (data.V[t]-Vb)/dU;
-	float S2  = 1.f+A*A;
-	float B   = Vb-A*Ub;
-	float B2  = B*B;
-	if (B2  > ipt2K*S2 || dT*S2 > B2*CSA) continue;
-
-	float Im  = std::abs((A-B*R)*R);
-	if (Im > imax) continue;
-
-	// Azimuthal angle test
-	//
-	float y  = 1.;
-	float x  = 2.f*B*R-A;
-	float df = std::abs(std::atan2(ay*y-ax*x,ax*y+ay*x)-data.ftrig);
-	if (df > M_PI) df=pi2-df;
-	if (df > data.ftrigW) continue;
-	data.CmSp_ITK.emplace_back(std::make_pair(B/std::sqrt(S2), data.SP_ITK[t]));
+    for (int b = 0; b < Nb; ++b)
+    {
+      float Zob = data.Zo[b];
+      float Tzb = data.Tz[b];
+      float Rb2r = data.R[b] * covr0;
+      float Rb2z = data.R[b] * covz0;
+      float Erb = data.Er[b];
+      float Vb = data.V[b];
+      float Ub = data.U[b];
+      float Tzb2 = (1.f + Tzb * Tzb);
+      float CSA = Tzb2 * COFK;
+      float ICSA = Tzb2 * ipt2C;
+      float imax = imaxp;
+      if (data.SP_ITK[b]->spacepoint->clusterList().second)
+        imax = imaxs;
+
+      for (int t = Nb; t != Nt; ++t)
+      {
+        float dT = ((Tzb - data.Tz[t]) * (Tzb - data.Tz[t]) - data.R[t] * Rb2z - (Erb + data.Er[t])) - (data.R[t] * Rb2r) * ((Tzb + data.Tz[t]) * (Tzb + data.Tz[t]));
+        if (dT > ICSA)
+          continue;
+        float dU = data.U[t] - Ub;
+        if (dU == 0.)
+          continue;
+        float A = (data.V[t] - Vb) / dU;
+        float S2 = 1.f + A * A;
+        float B = Vb - A * Ub;
+        float B2 = B * B;
+        if (B2 > ipt2K * S2 || dT * S2 > B2 * CSA)
+          continue;
+
+        float Im = std::abs((A - B * R) * R);
+        if (Im > imax)
+          continue;
+
+        // Azimuthal angle test
+        //
+        float y = 1.;
+        float x = 2.f * B * R - A;
+        float df = std::abs(std::atan2(ay * y - ax * x, ax * y + ay * x) - data.ftrig);
+        if (df > M_PI)
+          df = pi2 - df;
+        if (df > data.ftrigW)
+          continue;
+        data.CmSp_ITK.emplace_back(std::make_pair(B / std::sqrt(S2), data.SP_ITK[t]));
         data.SP_ITK[t]->setParam(Im);
       }
-      if (!data.CmSp_ITK.empty()) {
+      if (!data.CmSp_ITK.empty())
+      {
         newOneSeedWithCurvaturesComparison(data, data.SP_ITK[b], (*r0), Zob);
       }
     }
     fillSeeds(data);
     nseed += data.fillOneSeeds;
-    if (nseed>=m_maxsize) {
-      data.endlist=false;
+    if (nseed >= m_maxsize)
+    {
+      data.endlist = false;
       ++r0;
       data.rMin_ITK = r0;
       return;
-    } 
+    }
   }
 }
 
 ///////////////////////////////////////////////////////////////////
-// New 3 space points pro seeds 
+// New 3 space points pro seeds
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::newOneSeed
-(EventData& data,
- InDet::SiSpacePointForSeedITK*& p1, InDet::SiSpacePointForSeedITK*& p2,
- InDet::SiSpacePointForSeedITK*& p3, float z, float q) const
+void InDet::SiSpacePointsSeedMaker_ITK::newOneSeed(EventData &data,
+                                                   InDet::SiSpacePointForSeedITK *&p1, InDet::SiSpacePointForSeedITK *&p2,
+                                                   InDet::SiSpacePointForSeedITK *&p3, float z, float seedCandidateQuality) const
 {
-  if (data.nOneSeeds < m_maxOneSize) {
-    data.OneSeeds_ITK[data.nOneSeeds].set(p1,p2,p3,z);
-    data.mapOneSeeds_ITK.insert(std::make_pair(q, &(data.OneSeeds_ITK[data.nOneSeeds])));
+  /// get the worst seed so far
+  float worstQualityInMap = std::numeric_limits<float>::min();
+  InDet::SiSpacePointsProSeedITK *worstSeedSoFar = nullptr;
+  if (!data.mapOneSeeds_ITK.empty())
+  {
+    std::multimap<float, InDet::SiSpacePointsProSeedITK *>::reverse_iterator l = data.mapOneSeeds_ITK.rbegin();
+    worstQualityInMap = (*l).first;
+    worstSeedSoFar = (*l).second;
+  }
+  /// There are three cases where we simply add our new seed to the list and push it into the map:
+  /// a) we have not yet reached our max number of seeds
+  if (data.nOneSeeds < data.maxSeedsPerSP
+      /// b) we have reached the max number but always want to keep confirmed seeds
+      /// and the new seed is a confirmed one, with worse quality than the worst one so far
+      || (m_useSeedConfirmation && data.keepAllConfirmedSeeds && worstQualityInMap <= seedCandidateQuality && isConfirmedSeed(p1, p3, seedCandidateQuality) && data.nOneSeeds < data.seedPerSpCapacity)
+      /// c) we have reached the max number but always want to keep confirmed seeds
+      ///and the new seed of higher quality than the worst one so far, with the latter however being confirmed
+      || (m_useSeedConfirmation && data.keepAllConfirmedSeeds && worstQualityInMap > seedCandidateQuality && isConfirmedSeed(worstSeedSoFar->spacepoint0(), worstSeedSoFar->spacepoint2(), worstQualityInMap) && data.nOneSeeds < data.seedPerSpCapacity))
+  {
+    data.OneSeeds_ITK[data.nOneSeeds].set(p1, p2, p3, z);
+    data.mapOneSeeds_ITK.insert(std::make_pair(seedCandidateQuality, &data.OneSeeds_ITK[data.nOneSeeds]));
     ++data.nOneSeeds;
-  } else {
-    std::multimap<float,InDet::SiSpacePointsProSeedITK*>::reverse_iterator 
-      l = data.mapOneSeeds_ITK.rbegin();
+  }
 
-    if ((*l).first <= q) return;
-    
-    InDet::SiSpacePointsProSeedITK* s = (*l).second;
-    s->set(p1,p2,p3,z);
-
-    std::multimap<float,InDet::SiSpacePointsProSeedITK*>::iterator 
-      i = data.mapOneSeeds_ITK.insert(std::make_pair(q,s));
-	
-    for (++i; i!=data.mapOneSeeds_ITK.end(); ++i) {
-      if ((*i).second==s) {
+  /// otherwise, we check if there is a poorer-quality seed that we can kick out
+  else if (worstQualityInMap > seedCandidateQuality)
+  {
+    /// Overwrite the parameters of the worst seed with the new one
+    worstSeedSoFar->set(p1, p2, p3, z);
+    /// re-insert it with its proper quality to make sure it ends up in the right place
+    std::multimap<float, InDet::SiSpacePointsProSeedITK *>::iterator
+        i = data.mapOneSeeds_ITK.insert(std::make_pair(seedCandidateQuality, worstSeedSoFar));
+    /// and remove the entry with the old quality to avoid duplicates
+    for (++i; i != data.mapOneSeeds_ITK.end(); ++i)
+    {
+      if ((*i).second == worstSeedSoFar)
+      {
         data.mapOneSeeds_ITK.erase(i);
         return;
       }
@@ -1880,57 +3194,78 @@ void InDet::SiSpacePointsSeedMaker_ITK::newOneSeed
 // New 3 space points pro seeds production
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::newOneSeedWithCurvaturesComparison
-(EventData& data, SiSpacePointForSeedITK*& SPb, SiSpacePointForSeedITK*& SP0, float Zob) const
+void InDet::SiSpacePointsSeedMaker_ITK::newOneSeedWithCurvaturesComparison(EventData &data, SiSpacePointForSeedITK *&SPb, SiSpacePointForSeedITK *&SP0, float Zob) const
 {
   const float dC = .00003;
 
-  bool  pixb = !SPb->spacepoint->clusterList().second;
+  bool pixb = !SPb->spacepoint->clusterList().second;
 
   std::sort(data.CmSp_ITK.begin(), data.CmSp_ITK.end(), comCurvatureITK());
-  std::vector<std::pair<float,InDet::SiSpacePointForSeedITK*>>::iterator j,jn,i = data.CmSp_ITK.begin(),ie = data.CmSp_ITK.end();
-  jn=i;
-      
-  for (; i!=ie; ++i) {
-    float u    = (*i).second->param();
-    bool                pixt = !(*i).second->spacepoint->clusterList().second;
-    if (pixt && std::abs(SPb->z() -(*i).second->z()) > m_dzmaxPPP) continue;
-
-    const Trk::Surface* Sui  = (*i).second->sur   ();
-    float               Ri   = (*i).second->radius();
-    float               Ci1  =(*i).first-dC;
-    float               Ci2  =(*i).first+dC;
-    float               Rmi  = 0.;
-    float               Rma  = 0.;
-    bool                in   = false;
-    
-    if      (!pixb) u-=400.;
-    else if ( pixt) u-=200.;
-
-    for (j=jn; j!=ie; ++j) {  
-      if (       j == i           ) continue;
-      if ( (*j).first < Ci1       ) {jn=j; ++jn; continue;}
-      if ( (*j).first > Ci2       ) break;
-      if ( (*j).second->sur()==Sui) continue;
-      
+  std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator j, jn, i = data.CmSp_ITK.begin(), ie = data.CmSp_ITK.end();
+  jn = i;
+
+  for (; i != ie; ++i)
+  {
+    float u = (*i).second->param();
+    bool pixt = !(*i).second->spacepoint->clusterList().second;
+    if (pixt && std::abs(SPb->z() - (*i).second->z()) > m_dzmaxPPP)
+      continue;
+
+    const Trk::Surface *Sui = (*i).second->sur();
+    float Ri = (*i).second->radius();
+    float Ci1 = (*i).first - dC;
+    float Ci2 = (*i).first + dC;
+    float Rmi = 0.;
+    float Rma = 0.;
+    bool in = false;
+
+    if (!pixb)
+      u -= 400.;
+    else if (pixt)
+      u -= 200.;
+
+    for (j = jn; j != ie; ++j)
+    {
+      if (j == i)
+        continue;
+      if ((*j).first < Ci1)
+      {
+        jn = j;
+        ++jn;
+        continue;
+      }
+      if ((*j).first > Ci2)
+        break;
+      if ((*j).second->sur() == Sui)
+        continue;
+
       float Rj = (*j).second->radius();
-      if (std::abs(Rj-Ri) < m_drmin) continue;
-
-      if (in) {
-	if      (Rj > Rma) Rma = Rj;
-	else if (Rj < Rmi) Rmi = Rj;
-	else continue;
-	if ( (Rma-Rmi) > 20.) {
-          u-=200.;
+      if (std::abs(Rj - Ri) < m_drmin)
+        continue;
+
+      if (in)
+      {
+        if (Rj > Rma)
+          Rma = Rj;
+        else if (Rj < Rmi)
+          Rmi = Rj;
+        else
+          continue;
+        if ((Rma - Rmi) > 20.)
+        {
+          u -= 200.;
           break;
         }
-      } else {
-	in=true;
-        Rma=Rmi=Rj;
-        u-=200.;
+      }
+      else
+      {
+        in = true;
+        Rma = Rmi = Rj;
+        u -= 200.;
       }
     }
-    if (u > m_umax) continue;
+    if (u > m_umax)
+      continue;
 
     newOneSeed(data, SPb, SP0, (*i).second, Zob, u);
   }
@@ -1941,148 +3276,615 @@ void InDet::SiSpacePointsSeedMaker_ITK::newOneSeedWithCurvaturesComparison
 // Fill seeds
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::fillSeeds(EventData& data) const
+void InDet::SiSpacePointsSeedMaker_ITK::fillSeeds(EventData &data) const
 {
   data.fillOneSeeds = 0;
 
-  std::multimap<float,InDet::SiSpacePointsProSeedITK*>::iterator 
-    lf = data.mapOneSeeds_ITK.begin(),
-    l  = data.mapOneSeeds_ITK.begin(),
-    le = data.mapOneSeeds_ITK.end  ();
-  
-  if (l==le) return;
+  std::multimap<float, InDet::SiSpacePointsProSeedITK *>::iterator it_seedCandidate = data.mapOneSeeds_ITK.begin();
+  std::multimap<float, InDet::SiSpacePointsProSeedITK *>::iterator it_endSeedCandidates = data.mapOneSeeds_ITK.end();
 
-  SiSpacePointsProSeedITK* s = nullptr;
+  /// no seeds - nothing to do.
+  if (it_seedCandidate == it_endSeedCandidates)
+    return;
 
-  for (; l!=le; ++l) {
-    float w = (*l).first;
-    s       = (*l).second;
-    if (l!=lf && s->spacepoint0()->radius() < 43. && w > -200.) continue;
-    if (!s->setQuality(w)) continue;
-    
-    if (data.i_seede_ITK!=data.l_seeds_ITK.end()) {
-      s  = &(*data.i_seede_ITK++);
-      *s = *(*l).second;
-    } else {
-      data.l_seeds_ITK.emplace_back(SiSpacePointsProSeedITK(*(*l).second));
-      s = &(data.l_seeds_ITK.back());
+  SiSpacePointsProSeedITK *theSeed{nullptr};
+
+  /// loop over the seed candidates we have stored in the event data
+  for (; it_seedCandidate != it_endSeedCandidates; ++it_seedCandidate)
+  {
+
+    /// quality score of the seed, lower = better, list is sorted by quality
+    float quality = (*it_seedCandidate).first;
+    theSeed = (*it_seedCandidate).second;
+
+    /// this will set the quality member of all points on the seed to the quality score of this candidate
+    if (!theSeed->setQuality(quality))
+      continue;
+
+    /// if we have space, write the seed directly into an existing slot
+    if (data.i_seede_ITK != data.l_seeds_ITK.end())
+    {
+      theSeed = &(*data.i_seede_ITK++);
+      *theSeed = *(*it_seedCandidate).second;
+    }
+    else
+    {
+      /// otherwise, extend the seed list and update the iterators
+      data.l_seeds_ITK.emplace_back(SiSpacePointsProSeedITK(*(*it_seedCandidate).second));
+      theSeed = &(data.l_seeds_ITK.back());
       data.i_seede_ITK = data.l_seeds_ITK.end();
     }
-    
-    if      (s->spacepoint0()->spacepoint->clusterList().second) w-=3000.;
-    else if (s->spacepoint1()->spacepoint->clusterList().second) w-=2000.;
-    else if (s->spacepoint2()->spacepoint->clusterList().second) w-=1000.;
 
-    data.seeds_ITK.insert(std::make_pair(w,s));
     ++data.fillOneSeeds;
-  }
+  } ///< end loop over seed candidates
 }
 
-const InDet::SiSpacePointsSeed* InDet::SiSpacePointsSeedMaker_ITK::next(const EventContext&, EventData& data) const
+const InDet::SiSpacePointsSeed *InDet::SiSpacePointsSeedMaker_ITK::next(const EventContext &, EventData &data) const
 {
-  if (not data.initialized) initializeEventData(data);
+  /// This only holds if we call next() without manually calling newEvent/find3Sp
+  if (not data.initialized)
+    initializeEventData(data);
 
-  if (data.nspoint==3) {
-    do {
-      if (data.i_seed_ITK==data.i_seede_ITK) {
+  if (data.nspoint == 3)
+  {
+    do
+    {
+      /// If we are out of seeds, call findNext to see if we can find more.
+      if (data.i_seed_ITK == data.i_seede_ITK)
+      {
+        /// findNext will call production3Sp again IF data.endlist is false,
+        /// which is only the case if the last run of production3Sp did not run to the end
+        /// or if we did not run seed finding before
+        /// For run-3 offline, this will not do anything.
         findNext(data);
-        if (data.i_seed_ITK==data.i_seede_ITK) return nullptr;
+        /// if no new seeds were found, exit
+        if (data.i_seed_ITK == data.i_seede_ITK)
+          return nullptr;
       }
-      ++data.i_seed_ITK;
-    } while (!(*data.seed_ITK++).second->set3(data.seedOutput));
+      /// iterate until we find a valid seed satisfying certain quality cuts in set3
+    } while (!(*data.i_seed_ITK++).set3(data.seedOutput));
+    /// then return this next seed candidate
     return &data.seedOutput;
-  } else {
-    if (data.i_seed_ITK==data.i_seede_ITK) {
+  }
+  else
+  {
+    /// same as above for 2SP
+    if (data.i_seed_ITK == data.i_seede_ITK)
+    {
       findNext(data);
-      if (data.i_seed_ITK==data.i_seede_ITK) return nullptr;
-    } 
+      if (data.i_seed_ITK == data.i_seede_ITK)
+        return nullptr;
+    }
     (*data.i_seed_ITK++).set2(data.seedOutput);
     return &data.seedOutput;
   }
   return nullptr;
 }
-  
 
-bool InDet::SiSpacePointsSeedMaker_ITK::isZCompatible  
-(EventData& data, float& Zv, float& R, float& T) const
+bool InDet::SiSpacePointsSeedMaker_ITK::isZCompatible(EventData &data, float &Zv, float &R, float &T) const
 {
-  if (Zv < data.zminU || Zv > data.zmaxU) return false;
-  if (!data.isvertex) return true;
+  if (Zv < data.zminU || Zv > data.zmaxU)
+    return false;
+  if (!data.isvertex)
+    return true;
 
   float dZmin = std::numeric_limits<float>::max();
-  for (const float& v: data.l_vertex) {
-    float dZ = std::abs(v-Zv);
-    if (dZ >= dZmin) break;
-    dZmin=dZ;
+  for (const float &v : data.l_vertex)
+  {
+    float dZ = std::abs(v - Zv);
+    if (dZ >= dZmin)
+      break;
+    dZmin = dZ;
   }
-  return dZmin < (m_dzver+m_dzdrver*R)*sqrt(1.+T*T);
+  return dZmin < (m_dzver + m_dzdrver * R) * sqrt(1. + T * T);
 }
 
 ///////////////////////////////////////////////////////////////////
-// New space point for seeds 
+// New space point for seeds
 ///////////////////////////////////////////////////////////////////
 
-InDet::SiSpacePointForSeedITK* InDet::SiSpacePointsSeedMaker_ITK::newSpacePoint
-(EventData& data, const Trk::SpacePoint*const& sp) const
+InDet::SiSpacePointForSeedITK *InDet::SiSpacePointsSeedMaker_ITK::newSpacePoint(EventData &data, const Trk::SpacePoint *const &sp) const
 {
-  InDet::SiSpacePointForSeedITK* sps = nullptr;
-
   float r[15];
+  return newSpacePoint(data, sp, r, true);
+}
+
+InDet::SiSpacePointForSeedITK *InDet::SiSpacePointsSeedMaker_ITK::newSpacePoint(EventData &data, const Trk::SpacePoint *const &sp, float *r, bool usePixSctInform) const
+{
+
+  InDet::SiSpacePointForSeedITK *sps = nullptr;
+
+  /// r will store the coordinates of the space point relative
+  /// to the beam spot
   convertToBeamFrameWork(data, sp, r);
 
-  if (data.checketa) {
-    float z = (std::abs(r[2])+m_zmax);
-    float x = r[0]*data.dzdrmin;
-    float y = r[1]*data.dzdrmin;
-    if ((z*z )<(x*x+y*y)) return sps;
+  /// if needed, apply eta criterion
+  if (data.checketa)
+  {
+    float z = (std::abs(r[2]) + m_zmax);
+    float x = r[0] * data.dzdrmin;
+    float y = r[1] * data.dzdrmin;
+    if ((z * z) < (x * x + y * y))
+      return sps;
   }
 
-  if (data.i_spforseed_ITK!=data.l_spforseed_ITK.end()) {
+  if (m_fastTracking)
+  {
+    float R2 = r[0] * r[0] + r[1] * r[1];
+    if (std::abs(r[2]) > m_dzMaxFast && R2 < m_R2MaxFast)
+      return 0;
+    if (std::abs(r[2]) - m_zmax > data.dzdrmax * std::sqrt(R2))
+      return 0;
+  }
+
+  if (usePixSctInform)
+  {
+    if (!sp->clusterList().second)
+      pixInform(sp, r);
+    else
+      sctInform(data, sp, r);
+  }
+
+  /// If we have previously populated the list and just reset
+  /// the iterator when re-initialising the data object,
+  /// then we re-use existing entries
+  if (data.i_spforseed_ITK != data.l_spforseed_ITK.end())
+  {
+    /// re-use existing entry at the current location
     sps = &(*data.i_spforseed_ITK++);
+    /// and then update the existing entry with the new SP and location.
+    /// Unfortunately, set still relies on C-arrays...
     sps->set(sp, r);
-  } else {
-    data.l_spforseed_ITK.emplace_back(InDet::SiSpacePointForSeedITK(sp, r));
+  }
+  else
+  {
+    /// otherwise, the list needs to grow
+    data.l_spforseed_ITK.emplace_back(InDet::SiSpacePointForSeedITK(sp, &(r[0])));
+    /// set our return pointer
     sps = &(data.l_spforseed_ITK.back());
+    /// and make sure to update the iterator
     data.i_spforseed_ITK = data.l_spforseed_ITK.end();
   }
-      
+
   return sps;
 }
 
 ///////////////////////////////////////////////////////////////////
-// New 2 space points seeds 
+// New 2 space points seeds
 ///////////////////////////////////////////////////////////////////
 
-void InDet::SiSpacePointsSeedMaker_ITK::newSeed
-(EventData& data,
- InDet::SiSpacePointForSeedITK*& p1, InDet::SiSpacePointForSeedITK*& p2, float z) const
+void InDet::SiSpacePointsSeedMaker_ITK::newSeed(EventData &data,
+                                                InDet::SiSpacePointForSeedITK *&p1, InDet::SiSpacePointForSeedITK *&p2, float z) const
 {
-  InDet::SiSpacePointForSeedITK* p3 = nullptr;
+  InDet::SiSpacePointForSeedITK *p3 = nullptr;
 
-  if (data.i_seede_ITK!=data.l_seeds_ITK.end()) {
-    InDet::SiSpacePointsProSeedITK* s = &(*data.i_seede_ITK++);
+  if (data.i_seede_ITK != data.l_seeds_ITK.end())
+  {
+    InDet::SiSpacePointsProSeedITK *s = &(*data.i_seede_ITK++);
     s->set(p1, p2, p3, z);
-  } else {
+  }
+  else
+  {
     data.l_seeds_ITK.emplace_back(InDet::SiSpacePointsProSeedITK(p1, p2, p3, z));
     data.i_seede_ITK = data.l_seeds_ITK.end();
   }
 }
- 
-void InDet::SiSpacePointsSeedMaker_ITK::initializeEventData(EventData& data) const {
+
+void InDet::SiSpacePointsSeedMaker_ITK::initializeEventData(EventData &data) const
+{
+  int seedArrayPerSPSize = (m_maxOneSizePPP > m_maxOneSizeSSS ? m_maxOneSizePPP : m_maxOneSizeSSS);
+  if (m_alwaysKeepConfirmedStripSeeds || m_alwaysKeepConfirmedPixelSeeds)
+    seedArrayPerSPSize = 50;
   data.initialize(EventData::ITK,
                   m_maxsizeSP,
-                  m_maxOneSize,
-                  0, // maxsize not used
-                  m_r_size,
-                  0, // sizeRF not used
-                  SizeRFZ,
-                  SizeRFZV,
+                  seedArrayPerSPSize,
+                  0, /// maxsize not used
+                  m_nBinsR,
+                  0, /// sizeRF not used
+                  arraySizePhiZ,
+                  arraySizePhiZV,
                   m_checketa);
 }
 
-void InDet::SiSpacePointsSeedMaker_ITK::writeNtuple(const SiSpacePointsSeed*, const Trk::Track*, int, long) const{
+///////////////////////////////////////////////////////////////////
+// New 3 space points pro seeds production
+///////////////////////////////////////////////////////////////////
+
+void InDet::SiSpacePointsSeedMaker_ITK::newOneSeedWithCurvaturesComparisonSSS(EventData &data,
+                                                                              SiSpacePointForSeedITK *&SPb, SiSpacePointForSeedITK *&SP0, float Zob) const
+{
+
+  if (m_useSeedConfirmation)
+  {
+    newOneSeedWithCurvaturesComparisonSeedConfirmation(data, SPb, SP0, Zob);
+  }
+
+  else
+  {
+
+    static const float curvatureInterval = .00003;
+
+    /// sort common SP by curvature
+    if (data.CmSp_ITK.size() > 2)
+      std::sort(data.CmSp_ITK.begin(), data.CmSp_ITK.end(), comCurvatureITK());
+
+    std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_otherSP;
+    std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_commonTopSP = data.CmSp_ITK.begin(), ie = data.CmSp_ITK.end();
+    std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_startInnerLoop = it_commonTopSP;
+
+    float Lt[4];
+
+    /// check all possible common top SP
+    for (; it_commonTopSP != ie; ++it_commonTopSP)
+    {
+
+      SiSpacePointForSeedITK *SPt = (*it_commonTopSP).second;
+      int NT = 1;
+      Lt[0] = SPt->dR();
+      float seedIP = SPt->param();
+
+      /// form a curvature interval cut
+      float minCurvature = (*it_commonTopSP).first - curvatureInterval;
+      float maxCurvature = (*it_commonTopSP).first + curvatureInterval;
+
+      /**
+         * Now we look at the other SP candidates and try to find a confirmation seed,
+         * including the same centre/lower SP and giving a compatible curvature,
+         * but with the top SP in a different layer
+         **/
+
+      for (it_otherSP = it_startInnerLoop; it_otherSP != ie; ++it_otherSP)
+      {
+        /// if we are looking at the same SP, skip it
+        if (it_otherSP == it_commonTopSP)
+          continue;
+        /// if we have a lower curvature than the minimum, skip - and remember to
+        /// not bother with this candidate again later, as the vectors are curvature-sorted
+        if ((*it_otherSP).first < minCurvature)
+        {
+          it_startInnerLoop = it_otherSP;
+          ++it_startInnerLoop;
+          continue;
+        }
+        /// abort once the the curvature gets too large
+        if ((*it_otherSP).first > maxCurvature)
+          break;
+
+        float L = (*it_otherSP).second->dR();
+
+        int k = 0;
+        for (; k != NT; ++k)
+        {
+          if (std::abs(L - Lt[k]) < 20.)
+            break;
+        }
+        if (k == NT)
+        {
+          Lt[NT] = L;
+          if (++NT == 4)
+            break;
+        }
+      }
+
+      // ITK seed quality used so far
+      float Q = seedIP - float(NT) * 100.;
+      if (NT > 2)
+        Q -= 100000.;
+      /// this is a good seed, save it (unless we have too many seeds per SP)
+      newOneSeed(data, SPb, SP0, SPt, Zob, Q);
+    } ///< end of loop over top SP candidates
+    data.CmSp_ITK.clear();
+  }
+}
+
+void InDet::SiSpacePointsSeedMaker_ITK::newOneSeedWithCurvaturesComparisonPPP(EventData &data,
+                                                                              SiSpacePointForSeedITK *&SPb, SiSpacePointForSeedITK *&SP0, float Zob) const
+{
+
+  if (m_useSeedConfirmation)
+  {
+    newOneSeedWithCurvaturesComparisonSeedConfirmation(data, SPb, SP0, Zob);
+  }
+
+  else
+  {
+
+    static const float curvatureInterval = .00003;
+
+    /// sort common SP by curvature
+    if (data.CmSp_ITK.size() > 2)
+      std::sort(data.CmSp_ITK.begin(), data.CmSp_ITK.end(), comCurvatureITK());
+
+    std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_otherSP;
+    std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_commonTopSP = data.CmSp_ITK.begin(), ie = data.CmSp_ITK.end();
+    std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_startInnerLoop = it_commonTopSP;
+
+    float Lt[4];
+
+    float Qmin = 1.e20;
+    float Rb = 2. * SPb->radius();
+    int NTc(2);
+    if (Rb > 280.)
+      NTc = 1;
+    SiSpacePointForSeedITK *SPmin = nullptr;
+    bool Qm = Rb < 120. || std::abs(Zob) > 150.;
+
+    /// check all possible common top SP
+    for (; it_commonTopSP != ie; ++it_commonTopSP)
+    {
+
+      SiSpacePointForSeedITK *SPt = (*it_commonTopSP).second;
+      int NT = 1;
+      Lt[0] = SPt->dR();
+      float seedIP = SPt->param();
+
+      /// form a curvature interval cut
+      float minCurvature = (*it_commonTopSP).first - curvatureInterval;
+      float maxCurvature = (*it_commonTopSP).first + curvatureInterval;
+
+      /**
+         * Now we look at the other SP candidates and try to find a confirmation seed,
+         * including the same centre/lower SP and giving a compatible curvature,
+         * but with the top SP in a different layer
+         **/
+
+      for (it_otherSP = it_startInnerLoop; it_otherSP != ie; ++it_otherSP)
+      {
+        /// if we are looking at the same SP, skip it
+        if (it_otherSP == it_commonTopSP)
+          continue;
+        /// if we have a lower curvature than the minimum, skip - and remember to
+        /// not bother with this candidate again later, as the vectors are curvature-sorted
+        if ((*it_otherSP).first < minCurvature)
+        {
+          it_startInnerLoop = it_otherSP;
+          ++it_startInnerLoop;
+          continue;
+        }
+        /// abort once the the curvature gets too large
+        if ((*it_otherSP).first > maxCurvature)
+          break;
+
+        float L = (*it_otherSP).second->dR();
+
+        int k = 0;
+        for (; k != NT; ++k)
+        {
+          if (std::abs(L - Lt[k]) < 20.)
+            break;
+        }
+        if (k == NT)
+        {
+          Lt[NT] = L;
+          if (++NT == 4)
+            break;
+        }
+      }
+
+      int dN = NT - NTc;
+      if (dN < 0 || (data.nOneSeeds && !dN))
+        continue;
+      if (Qm && !dN && seedIP > 1.)
+        continue;
+
+      // ITK seed quality used so far
+      float Q = 100. * seedIP + (std::abs(Zob) - float(NT) * 100.);
+      if (Q > SPb->quality() && Q > SP0->quality() && Q > SPt->quality())
+        continue;
+
+      if (dN)
+        newOneSeed(data, SPb, SP0, SPt, Zob, Q);
+      else if (Q < Qmin)
+      {
+        Qmin = Q;
+        SPmin = SPt;
+      }
+    } ///< end of loop over top SP candidates
+    if (SPmin && !data.nOneSeeds)
+      newOneSeed(data, SPb, SP0, SPmin, Zob, Qmin);
+    data.CmSp_ITK.clear();
+  }
 }
 
-bool InDet::SiSpacePointsSeedMaker_ITK::getWriteNtupleBoolProperty() const{
-    return false;
+void InDet::SiSpacePointsSeedMaker_ITK::newOneSeedWithCurvaturesComparisonSeedConfirmation(EventData &data,
+                                                                                           SiSpacePointForSeedITK *&SPb, SiSpacePointForSeedITK *&SP0, float Zob) const
+{
+  static const float curvatureInterval = .00003;
+  bool bottomSPisPixel = !SPb->spacepoint->clusterList().second;
+  float bottomSPQuality = SPb->quality();
+  float centralSPQuality = SP0->quality();
+
+  /// sort common SP by curvature
+  if (data.CmSp_ITK.size() > 2)
+    std::sort(data.CmSp_ITK.begin(), data.CmSp_ITK.end(), comCurvatureITK());
+
+  float bottomR = SPb->radius();
+  float bottomZ = SPb->z();
+
+  std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_otherSP;
+  std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_commonTopSP = data.CmSp_ITK.begin(), ie = data.CmSp_ITK.end();
+  std::vector<std::pair<float, InDet::SiSpacePointForSeedITK *>>::iterator it_startInnerLoop = it_commonTopSP;
+
+  /// check all possible common top SP
+  for (; it_commonTopSP != ie; ++it_commonTopSP)
+  {
+
+    SiSpacePointForSeedITK *SPt = (*it_commonTopSP).second;
+    /// the seed quality is set to d0 initially
+    float seedIP = SPt->param();
+    float seedQuality = seedIP + SPt->scorePenalty();
+    float originalSeedQuality = seedQuality;
+
+    if (m_maxdImpact > 50)
+    { //This only applies to LRT
+
+      float topR = SPt->radius();
+      float topZ = SPt->z();
+
+      float Zot = std::abs(topR - bottomR) > 10e-9 ? bottomZ - (bottomR - originalSeedQuality) * ((topZ - bottomZ) / (topR - bottomR)) : bottomZ;
+
+      float theta1 = std::abs(topR - bottomR) > 10e-9 ? std::atan2(topR - bottomR, topZ - bottomZ) : 0.;
+      float eta1 = theta1 > 0 ? -std::log(std::tan(.5 * theta1)) : 0.;
+
+      float theta0 = seedIP > 0 ? std::atan2(seedIP, Zot) : 0;
+      float eta0 = theta0 > 0 ? -std::log(std::tan(.5 * theta0)) : 0.;
+
+      float deltaEta = std::abs(eta1 - eta0); //For LLP daughters, the direction of the track is correlated with the direction of the LLP (which is correlated with the direction of the point of closest approach
+      //calculate weighted average of d0 and deltaEta, normalized by their maximum values
+      float f = std::min(0.5, originalSeedQuality / 200.); //0.5 and 200 are parameters chosen from a grid scan to optimize efficiency
+      seedQuality *= (1 - f) / 300.;
+      seedQuality += f * deltaEta / 2.5;
+    }
+
+    bool topSPisPixel = !SPt->spacepoint->clusterList().second;
+
+    /// check the surface the hit is on
+    const Trk::Surface *surfaceTopSP = SPt->sur();
+    float radiusTopSP = SPt->radius();
+    /// form a curvature interval cut
+    float minCurvature = (*it_commonTopSP).first - curvatureInterval;
+    float maxCurvature = (*it_commonTopSP).first + curvatureInterval;
+
+    /** Note: The score modifiers used here have the purpose of separating the candidates into
+      *        classes / groups disjoint from each other.
+      *        So the score increment (200 by default) should exceed the maximum
+      *        |d0| (base score) we expect to encounter to avoid overlap.
+      *        For LRT, we may want to tune this!
+      **/
+
+    /// if we have a SSS seed, boost the quality score by 400
+    if (!bottomSPisPixel)
+      seedQuality += m_seedScoreBonusSSS;
+    /// if we have a PPP, boost the quality by 200
+    else if (topSPisPixel)
+      seedQuality += m_seedScoreBonusPPP;
+
+    /**
+      * Now we look at the other SP candidates and try to find a confirmation seed,
+      * including the same centre/lower SP and giving a compatible curvature,
+      * but with the top SP in a different layer
+      **/
+
+    for (it_otherSP = it_startInnerLoop; it_otherSP != ie; ++it_otherSP)
+    {
+      /// if we are looking at the same SP, skip it
+      if (it_otherSP == it_commonTopSP)
+        continue;
+      /// if we have a lower curvature than the minimum, skip - and remember to
+      /// not bother with this candidate again later, as the vectors are curvature-sorted
+      if ((*it_otherSP).first < minCurvature)
+      {
+        it_startInnerLoop = it_otherSP;
+        ++it_startInnerLoop;
+        continue;
+      }
+      /// abort once the the curvature gets too large
+      if ((*it_otherSP).first > maxCurvature)
+        break;
+      /// if both SP are on the surface, skip it
+      if ((*it_otherSP).second->sur() == surfaceTopSP)
+        continue;
+      /// if the other SP is too close to the current top one, skip
+      float radiusOtherSP = (*it_otherSP).second->radius();
+      if (std::abs(radiusOtherSP - radiusTopSP) < m_drminSeedConf)
+        continue;
+      // if we have a confirmation seed, we improve the score of the seed.
+      seedQuality += m_seedScoreBonusConfirmationSeed;
+      // apply confirmation bonus only once
+      break;
+    }
+
+    /// kick this seed candidate if the score is too high (lower values = better)
+    if (seedQuality > data.maxScore)
+      continue;
+
+    /// if we have PPS seeds and no confirmation SP exists (which would give the -200 bonus)
+    /// or the hits on this seed were already used on a higher quality PPP/SSS seed, kick this one
+    if (bottomSPisPixel != topSPisPixel)
+    {
+      if (seedQuality > 0. ||
+          (seedQuality > bottomSPQuality && seedQuality > centralSPQuality && seedQuality > SPt->quality()))
+        continue;
+    }
+    /// If we have a non-confirmed seed, apply a stricter d0 cut.
+    /// This, is determined using the original cut and the score penalty modifier.
+    if (!isConfirmedSeed(SPb, SPt, seedQuality))
+    {
+      /// PPP seeds
+      double maxdImpact = m_maxdImpact - (m_dImpactCutSlopeUnconfirmedPPP * SPt->scorePenalty());
+      /// SSS seeds
+      if (!bottomSPisPixel)
+        maxdImpact = m_maxdImpactSSS - (m_dImpactCutSlopeUnconfirmedSSS * SPt->scorePenalty());
+      if (seedIP > maxdImpact)
+        continue;
+    }
+    /// this is a good seed, save it (unless we have too many seeds per SP)
+    newOneSeed(data, SPb, SP0, SPt, Zob, seedQuality);
+  } ///< end of loop over top SP candidates
+  data.CmSp_ITK.clear();
+}
+
+bool InDet::SiSpacePointsSeedMaker_ITK::isConfirmedSeed(const InDet::SiSpacePointForSeedITK *bottomSP,
+                                                        const InDet::SiSpacePointForSeedITK *topSP, float quality) const
+{
+
+  /// SSS seeds
+  if (bottomSP->spacepoint->clusterList().second)
+  {
+    return (quality < m_seedScoreThresholdSSSConfirmationSeed);
+  }
+  /// PPP seeds
+  else if (!topSP->spacepoint->clusterList().second)
+  {
+    return (quality < m_seedScoreThresholdPPPConfirmationSeed);
+  }
+  /// PPS: the confirmation is the only quality modifier applied
+  else
+    return (quality < 0.);
+}
+
+void InDet::SiSpacePointsSeedMaker_ITK::writeNtuple(const SiSpacePointsSeed* seed, const Trk::Track* track, int seedType, long eventNumber) const
+{
+  if(m_writeNtuple) {
+    std::lock_guard<std::mutex> lock(m_mutex);
+
+    if(track != nullptr) {
+      m_trackPt = (track->trackParameters()->front()->pT())/1000.f;
+      m_trackEta = std::abs(track->trackParameters()->front()->eta());
+    }
+    else {
+      m_trackPt = -1.;
+      m_trackEta = -1.; 
+    }
+    m_d0           =   seed->d0();
+    m_z0           =   seed->zVertex();
+    m_eta          =   seed->eta();
+    m_x1           =   seed->x1();
+    m_x2           =   seed->x2();
+    m_x3           =   seed->x3();
+    m_y1           =   seed->y1();
+    m_y2           =   seed->y2();
+    m_y3           =   seed->y3();      
+    m_z1           =   seed->z1();
+    m_z2           =   seed->z2();
+    m_z3           =   seed->z3();
+    m_r1           =   seed->r1();
+    m_r2           =   seed->r2();
+    m_r3           =   seed->r3();
+    m_type         =   seedType;
+    m_dzdr_b       =   seed->dzdr_b();
+    m_dzdr_t       =   seed->dzdr_t();
+    m_pt           =   seed->pt();
+    m_givesTrack   =   !(track == nullptr);
+    m_eventNumber  =   eventNumber;
+
+    m_outputTree->Fill();
+
+  }
+
+}
+
+bool InDet::SiSpacePointsSeedMaker_ITK::getWriteNtupleBoolProperty() const
+{
+  return m_writeNtuple;
 }