diff --git a/algo/ca/core/tracking/CaTrackExtender.cxx b/algo/ca/core/tracking/CaTrackExtender.cxx
index 7af7d187ecb36098eec904f015d1c69e734c6077..6e3ccf6656918097c4b26cce737695dc4552dd34 100644
--- a/algo/ca/core/tracking/CaTrackExtender.cxx
+++ b/algo/ca/core/tracking/CaTrackExtender.cxx
@@ -55,9 +55,9 @@ void TrackExtender::FitBranchFast(const ca::Branch& t, TrackParamV& Tout, const
   const int iFirstHit         = (direction == FitDirection::kUpstream) ? nHits - 1 : 0;
   const int iLastHit          = (direction == FitDirection::kUpstream) ? 0 : nHits - 1;
 
-  const ca::Hit& hit0 = fInputData->GetHit(hits[iFirstHit]);
-  const ca::Hit& hit1 = fInputData->GetHit(hits[iFirstHit + step]);
-  const ca::Hit& hit2 = fInputData->GetHit(hits[iFirstHit + 2 * step]);
+  const ca::Hit& hit0 = frWData->Hit(hits[iFirstHit]);
+  const ca::Hit& hit1 = frWData->Hit(hits[iFirstHit + step]);
+  const ca::Hit& hit2 = frWData->Hit(hits[iFirstHit + 2 * step]);
 
   int ista0 = hit0.Station();
   int ista1 = hit1.Station();
@@ -113,7 +113,7 @@ void TrackExtender::FitBranchFast(const ca::Branch& t, TrackParamV& Tout, const
   fld.Set(fldB2, fldZ2, fldB1, fldZ1, fldB0, fldZ0);
 
   for (int i = iFirstHit + step; step * i <= step * iLastHit; i += step) {
-    const ca::Hit& hit           = fInputData->GetHit(hits[i]);
+    const ca::Hit& hit           = frWData->Hit(hits[i]);
     int ista                     = hit.Station();
     const ca::Station<fvec>& sta = fParameters.GetStation(ista);
 
@@ -162,9 +162,9 @@ void TrackExtender::FindMoreHits(ca::Branch& t, TrackParamV& Tout, const FitDire
   const int iFirstHit         = (direction == FitDirection::kUpstream) ? 2 : t.NofHits() - 3;
   //  int ista = fInputData->GetHit(t.Hits[iFirstHit]).iSt + 2 * step; // current station. set to the end of track
 
-  const ca::Hit& hit0 = fInputData->GetHit(t.Hits()[iFirstHit]);  // optimize
-  const ca::Hit& hit1 = fInputData->GetHit(t.Hits()[iFirstHit + step]);
-  const ca::Hit& hit2 = fInputData->GetHit(t.Hits()[iFirstHit + 2 * step]);
+  const ca::Hit& hit0 = frWData->Hit(t.Hits()[iFirstHit]);  // optimize
+  const ca::Hit& hit1 = frWData->Hit(t.Hits()[iFirstHit + step]);
+  const ca::Hit& hit2 = frWData->Hit(t.Hits()[iFirstHit + 2 * step]);
 
   const int ista0 = hit0.Station();
   const int ista1 = hit1.Station();
@@ -267,7 +267,7 @@ void TrackExtender::FindMoreHits(ca::Branch& t, TrackParamV& Tout, const FitDire
 
 
     const ca::Hit& hit = frWData->Hit(iHit_best);
-    newHits.push_back(hit.Id());
+    newHits.push_back(iHit_best);
 
     fit.Extrapolate(hit.Z(), fld);
     fit.FilterHit(hit, fmask(sta.timeInfo));
@@ -308,10 +308,9 @@ void TrackExtender::FindMoreHits(ca::Branch& t, TrackParamV& Tout, const FitDire
 }  // void ca::Framework::FindMoreHits
 
 /// Try to extrapolate and find additional hits on other stations
-fscal TrackExtender::ExtendBranch(ca::Branch& t, const InputData& input, WindowData& wData)
+fscal TrackExtender::ExtendBranch(ca::Branch& t, WindowData& wData)
 {
-  fInputData = &input;
-  frWData    = &wData;
+  frWData = &wData;
   //   const unsigned int minNHits = 3;
 
   TrackParamV T;
diff --git a/algo/ca/core/tracking/CaTrackExtender.h b/algo/ca/core/tracking/CaTrackExtender.h
index be2dfea9c050f8ef792db655c85acc7893d12fe3..48260c0b8432ffd142c0d41ede908dfcb67a118f 100644
--- a/algo/ca/core/tracking/CaTrackExtender.h
+++ b/algo/ca/core/tracking/CaTrackExtender.h
@@ -51,7 +51,7 @@ namespace cbm::algo::ca
 
     /// Find additional hits for existing track both downstream and upstream
     /// \return chi2
-    fscal ExtendBranch(ca::Branch& t, const ca::InputData& input, WindowData& wData);
+    fscal ExtendBranch(ca::Branch& t, WindowData& wData);
 
    private:
     ///-------------------------------
@@ -88,7 +88,6 @@ namespace cbm::algo::ca
     /// Data members
 
     const Parameters<fvec>& fParameters;  ///< Object of Framework parameters class
-    const InputData* fInputData;          ///< Tracking input data
     WindowData* frWData;
     fscal fDefaultMass{constants::phys::MuonMass};  ///< mass of the propagated particle [GeV/c2]
   };
diff --git a/algo/ca/core/tracking/CaTrackFinder.cxx b/algo/ca/core/tracking/CaTrackFinder.cxx
index 9326f1d97b0f37967d058d951f838fff8748e456..c3650fe62102f80a8da91a14d1f58b294f093ec0 100644
--- a/algo/ca/core/tracking/CaTrackFinder.cxx
+++ b/algo/ca/core/tracking/CaTrackFinder.cxx
@@ -114,9 +114,7 @@ TrackFinder::Output_t TrackFinder::FindTracks(const InputData& input, TimesliceH
   fStatNhitsTotal = 0;
 
   // calculate possible event time for the hits (fHitTimeInfo array)
-  const int nDataStreams = input.GetNdataStreams();
-
-  for (int iStream = 0; iStream < nDataStreams; ++iStream) {
+  for (int iStream = 0; iStream < input.GetNdataStreams(); ++iStream) {
 
     fscal maxTimeBeforeHit = std::numeric_limits<fscal>::lowest();
     const int nStreamHits  = input.GetStreamNhits(iStream);
@@ -203,38 +201,35 @@ TrackFinder::Output_t TrackFinder::FindTracks(const InputData& input, TimesliceH
   }
 
   // int nWindowsThread = nWindows / fNofThreads;
-  fscal windowDelta = fWindowLength - fWindowOverlap - fWindowMargin;
+  const fscal windowDelta = fWindowLength - fWindowOverlap - fWindowMargin;
   //LOG(info) << "CA: estimated number of time windows: " << nWindows;
 
-  std::vector<fscal> vWindowStartThread(fNofThreads), vWindowEndThread(fNofThreads);
-
-  vWindowStartThread[0] = fStatTsStart;
+  std::vector<std::pair<fscal, fscal>> vWindowRangeThread(fNofThreads);
   {  // Estimation of number of hits in time windows
     //Timer time;
     //time.Start();
-    const int nSt = fParameters.GetNstationsActive();
+    const HitIndex_t nHitsTot = input.GetNhits();
+    const int nSt             = fParameters.GetNstationsActive();
 
     // Count number of hits per window and station
-    const double a      = fStatTsStart + fWindowOverlap + fWindowMargin;
-    const double b      = fWindowLength - fWindowOverlap - fWindowMargin;
-    HitIndex_t nHitsTot = input.GetNhits();
+    const double a = fStatTsStart + fWindowOverlap + fWindowMargin;
+    const double b = fWindowLength - fWindowOverlap - fWindowMargin;
     std::vector<HitIndex_t> nHitsWindowSta(nWindows * nSt, 0);
+
     for (HitIndex_t iHit = 0; iHit < nHitsTot; ++iHit) {
-      const auto& hit = input.GetHit(iHit);
-      //const auto& timeInfo = fHitTimeInfo[iHit];
-      size_t iWindow = static_cast<size_t>((hit.T() - a) / b);
+      const auto& hit      = input.GetHit(iHit);
+      const size_t iWindow = static_cast<size_t>((hit.T() - a) / b);
       if (iWindow >= static_cast<size_t>(nWindows)) {
         LOG(error) << "ca: Hit out of range: iHit = " << iHit << ", time = " << hit.T() * 1.e-6
                    << " ms, window = " << iWindow;
         continue;
       }
-
       ++nHitsWindowSta[hit.Station() + iWindow * nSt];
     }
 
     // Remove hits from the "monster events"
     if (ca::TrackingMode::kMcbm == fTrackingMode) {
-      auto maxNofHitsSta = static_cast<HitIndex_t>(50 * fWindowLength / 1.e3);
+      const auto maxNofHitsSta = static_cast<HitIndex_t>(50 * fWindowLength / 1.e3);
       for (auto& content : nHitsWindowSta) {
         if (content > maxNofHitsSta) {
           content = 0;
@@ -251,32 +246,33 @@ TrackFinder::Output_t TrackFinder::FindTracks(const InputData& input, TimesliceH
     }
 
     // Get time range for threads
-    HitIndex_t nHitsPerThread = nHitsCollected / fNofThreads;
-    auto windowIt             = nHitsWindow.begin();
-    size_t iWbegin            = 0;
+    const HitIndex_t nHitsPerThread = nHitsCollected / fNofThreads;
+    auto windowIt                   = nHitsWindow.begin();
+    vWindowRangeThread[0].first     = fStatTsStart;
     for (int iTh = 1; iTh < fNofThreads; ++iTh) {
-      windowIt                  = std::lower_bound(windowIt, nHitsWindow.end(), iTh * nHitsPerThread);
-      iWbegin                   = std::distance(nHitsWindow.begin(), windowIt) + 1;
-      vWindowStartThread[iTh]   = fStatTsStart + iWbegin * windowDelta;
-      vWindowEndThread[iTh - 1] = vWindowStartThread[iTh];
+      windowIt                           = std::lower_bound(windowIt, nHitsWindow.end(), iTh * nHitsPerThread);
+      const size_t iWbegin               = std::distance(nHitsWindow.begin(), windowIt) + 1;
+      vWindowRangeThread[iTh].first      = fStatTsStart + iWbegin * windowDelta;
+      vWindowRangeThread[iTh - 1].second = vWindowRangeThread[iTh].first;
     }
+    vWindowRangeThread[fNofThreads - 1].second = fStatTsEnd;
+
     //time.Stop();
     //LOG(info) << "Thread boarders estimation time: " << time.GetTotalMs() << " ms";
     LOG(debug) << "Fraction of hits from monster events: " << static_cast<double>(nHitsTot - nHitsCollected) / nHitsTot;
   }
 
-
   // cut data into sub-timeslices and process them one by one
   //for (int iThread = 0; iThread < fNofThreads; ++iThread) {
   //  vWindowStartThread[iThread] = fStatTsStart + iThread * nWindowsThread * windowDelta;
   //  vWindowEndThread[iThread] =
   //    vWindowStartThread[iThread] + nWindowsThread * windowDelta + fWindowOverlap + fWindowMargin;
   //}
-  vWindowEndThread[fNofThreads - 1] = fStatTsEnd;
 
   for (int iThread = 0; iThread < fNofThreads; ++iThread) {
-    double start = vWindowStartThread[iThread] * 1.e-6;
-    double end   = vWindowEndThread[iThread] * 1.e-6;
+    auto& entry  = vWindowRangeThread[iThread];
+    double start = entry.first * 1.e-6;
+    double end   = entry.second * 1.e-6;
     LOG(trace) << "Thread: " << iThread << " from " << start << " ms  to " << end << " ms (delta = " << end - start
                << " ms)";
   }
@@ -287,8 +283,8 @@ TrackFinder::Output_t TrackFinder::FindTracks(const InputData& input, TimesliceH
   fMonitorData.StopTimer(ETimer::PrepareTimeslice);
   // Save tracks
   if (fNofThreads == 1) {
-    this->FindTracksThread(input, 0, std::ref(vWindowStartThread[0]), std::ref(vWindowEndThread[0]),
-                           std::ref(vStatNwindows[0]), std::ref(vStatNhitsProcessed[0]));
+    this->FindTracksThread(input, 0, std::ref(vWindowRangeThread[0]), std::ref(vStatNwindows[0]),
+                           std::ref(vStatNhitsProcessed[0]));
     fMonitorData.StartTimer(ETimer::StoreTracksFinal);
     recoTracks = std::move(fvRecoTracks[0]);
     recoHits   = std::move(fvRecoHitIndices[0]);
@@ -299,8 +295,8 @@ TrackFinder::Output_t TrackFinder::FindTracks(const InputData& input, TimesliceH
     vThreadList.reserve(fNofThreads);
     for (int iTh = 0; iTh < fNofThreads; ++iTh) {
       vThreadList.emplace_back(&TrackFinder::FindTracksThread, this, std::ref(input), iTh,
-                               std::ref(vWindowStartThread[iTh]), std::ref(vWindowEndThread[iTh]),
-                               std::ref(vStatNwindows[iTh]), std::ref(vStatNhitsProcessed[iTh]));
+                               std::ref(vWindowRangeThread[iTh]), std::ref(vStatNwindows[iTh]),
+                               std::ref(vStatNhitsProcessed[iTh]));
     }
     for (auto& th : vThreadList) {
       if (th.joinable()) {
@@ -350,29 +346,32 @@ TrackFinder::Output_t TrackFinder::FindTracks(const InputData& input, TimesliceH
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
-void TrackFinder::FindTracksThread(const InputData& input, int iThread, fscal& windowStart, fscal& windowEnd,
+void TrackFinder::FindTracksThread(const InputData& input, int iThread, std::pair<fscal, fscal>& windowRange,
                                    int& statNwindows, int& statNhitsProcessed)
 {
   //std::stringstream filename;
   //filename << "./dbg_caTrackFinder::FindTracksThread_" << iThread << ".txt";
   //std::ofstream out(filename.str());
-  auto& monitor = fvMonitorDataThread[iThread];
   Timer timer;
   timer.Start();
+
+  auto& monitor = fvMonitorDataThread[iThread];
   monitor.StartTimer(ETimer::TrackingThread);
   monitor.StartTimer(ETimer::PrepareThread);
 
   // Init vectors
-  auto& wData = fvWData[iThread];
+  auto& tracks     = fvRecoTracks[iThread];
+  auto& hitIndices = fvRecoHitIndices[iThread];
+  auto& wData      = fvWData[iThread];
   {
     const int nStations          = fParameters.GetNstationsActive();
     const size_t nHitsTot        = input.GetNhits();
     const size_t nHitsExpected   = 2 * nHitsTot;
     const size_t nTracksExpected = 2 * nHitsTot / nStations;
-    fvRecoTracks[iThread].clear();
-    fvRecoTracks[iThread].reserve(nTracksExpected / fNofThreads);
-    fvRecoHitIndices[iThread].clear();
-    fvRecoHitIndices[iThread].reserve(nHitsExpected / fNofThreads);
+    tracks.clear();
+    tracks.reserve(nTracksExpected / fNofThreads);
+    hitIndices.clear();
+    hitIndices.reserve(nHitsExpected / fNofThreads);
     if (iThread != 0) {
       wData.HitKeyFlags() = fvWData[0].HitKeyFlags();
     }
@@ -382,25 +381,26 @@ void TrackFinder::FindTracksThread(const InputData& input, int iThread, fscal& w
     }
   }
 
-  const int nDataStreams = input.GetNdataStreams();
-  std::vector<HitIndex_t> sliceFirstHit(nDataStreams, 0);
-  std::vector<HitIndex_t> sliceLastHit(nDataStreams, 0);
+  // Begin and end index of hit-range for streams
+  std::vector<std::pair<HitIndex_t, HitIndex_t>> streamHitRanges(input.GetNdataStreams(), {0, 0});
 
   // Define first hit, skip all the hits, which are before the first window
-  for (int iStream = 0; iStream < nDataStreams; ++iStream) {
-    sliceFirstHit[iStream] = input.GetStreamStartIndex(iStream);
-    sliceLastHit[iStream]  = input.GetStreamStopIndex(iStream);
-    for (HitIndex_t caHitId = sliceFirstHit[iStream]; caHitId < sliceLastHit[iStream]; ++caHitId) {
+  for (size_t iStream = 0; iStream < streamHitRanges.size(); ++iStream) {
+    auto& range  = streamHitRanges[iStream];
+    range.first  = input.GetStreamStartIndex(iStream);
+    range.second = input.GetStreamStopIndex(iStream);
+
+    for (HitIndex_t caHitId = range.first; caHitId < range.second; ++caHitId) {
       const ca::Hit& h = input.GetHit(caHitId);
       if (wData.IsHitKeyUsed(h.FrontKey()) || wData.IsHitKeyUsed(h.BackKey())) {
         continue;
       }
       const CaHitTimeInfo& info = fHitTimeInfo[caHitId];
-      if (info.fMaxTimeBeforeHit < windowStart) {
-        sliceFirstHit[iStream] = caHitId + 1;
+      if (info.fMaxTimeBeforeHit < windowRange.first) {
+        range.first = caHitId + 1;
       }
-      if (info.fMinTimeAfterHit > windowEnd) {
-        sliceLastHit[iStream] = caHitId;
+      if (info.fMinTimeAfterHit > windowRange.second) {
+        range.second = caHitId;
       }
     }
   }
@@ -408,58 +408,60 @@ void TrackFinder::FindTracksThread(const InputData& input, int iThread, fscal& w
   int statLastLogTimeChunk = -1;
 
   // Track finder algorithm for the time window
-  ca::TrackFinderWindow trackFinderWindow(fParameters, fDefaultMass, fTrackingMode, fvMonitorDataThread[iThread]);
+  ca::TrackFinderWindow trackFinderWindow(fParameters, fDefaultMass, fTrackingMode, monitor);
   trackFinderWindow.InitTimeslice(input.GetNhitKeys());
 
   monitor.StopTimer(ETimer::PrepareThread);
 
-  bool areUntouchedDataLeft = true;  // is the whole TS processed
-
-  while (areUntouchedDataLeft) {
-    fvMonitorDataThread[iThread].IncrementCounter(ECounter::SubTS);
+  while (true) {
+    monitor.IncrementCounter(ECounter::SubTS);
     // select the sub-slice hits
     for (int iS = 0; iS < fParameters.GetNstationsActive(); ++iS) {
       wData.TsHitIndices(iS).clear();
     }
-    areUntouchedDataLeft = false;
+    bool areUntouchedDataLeft = false;  // is the whole TS processed
 
     // TODO: SG: skip empty regions and start the subslice with the earliest hit
 
     statNwindows++;
     //out << statNwindows << ' ';
 
-    int statNwindowHits = 0;
+    monitor.StartTimer(ETimer::PrepareWindow);
 
-    fvMonitorDataThread[iThread].StartTimer(ETimer::PrepareWindow);
-    for (int iStream = 0; iStream < nDataStreams; ++iStream) {
-      for (ca::HitIndex_t caHitId = sliceFirstHit[iStream]; caHitId < sliceLastHit[iStream]; ++caHitId) {
-        const CaHitTimeInfo& info = fHitTimeInfo[caHitId];
-        const ca::Hit& h          = input.GetHit(caHitId);
+    for (auto& range : streamHitRanges) {
+      for (HitIndex_t caHitId = range.first; caHitId < range.second; ++caHitId) {
+        const ca::Hit& h = input.GetHit(caHitId);
         if (wData.IsHitKeyUsed(h.FrontKey()) || wData.IsHitKeyUsed(h.BackKey())) {
           // the hit is already reconstructed
           continue;
         }
-        if (info.fEventTimeMin > windowStart + fWindowLength) {  // the hit is too late for the sub slice
+        const CaHitTimeInfo& info = fHitTimeInfo[caHitId];
+        if (info.fEventTimeMax < windowRange.first) {
+          // the hit belongs to previous sub-slices
+          continue;
+        }
+        if (info.fMinTimeAfterHit > windowRange.first + fWindowLength) {
+          // this hit and all later hits are out of the sub-slice
           areUntouchedDataLeft = true;
-          if (info.fMinTimeAfterHit > windowStart + fWindowLength) {
-            // this hit and all later hits are out of the sub-slice
-            break;
-          }
+          break;
+        }
+        if (info.fEventTimeMin > windowRange.first + fWindowLength) {
+          // the hit is too late for the sub slice
+          areUntouchedDataLeft = true;
+          continue;
+        }
+
+        // the hit belongs to the sub-slice
+        wData.TsHitIndices(h.Station()).push_back(caHitId);
+        if (info.fMaxTimeBeforeHit < windowRange.first + fWindowLength - fWindowOverlap) {
+          range.first = caHitId + 1;  // this hit and all hits before are before the overlap
         }
-        else if (windowStart <= info.fEventTimeMax) {  // the hit belongs to the sub-slice
-          wData.TsHitIndices(h.Station()).push_back(caHitId);
-          statNwindowHits++;
-          if (info.fMaxTimeBeforeHit < windowStart + fWindowLength - fWindowOverlap) {
-            // this hit and all hits before are before the overlap
-            sliceFirstHit[iStream] = caHitId + 1;
-          }
-        }  // else the hit has been alread processed in previous sub-slices
       }
     }
 
     //out << statNwindowHits << ' ';
     //if (statNwindowHits == 0) {  // Empty window
-    //  fvMonitorDataThread[iThread].StopTimer(ETimer::PrepareWindow);
+    //  monitor.StopTimer(ETimer::PrepareWindow);
     //  out << 0 << ' ' << 0 << ' ' << 0 << '\n';
     //  continue;
     //}
@@ -470,54 +472,55 @@ void TrackFinder::FindTracksThread(const InputData& input, int iThread, fscal& w
       for (int ista = 0; ista < fParameters.GetNstationsActive(); ++ista) {
         int nHitsSta = static_cast<int>(wData.TsHitIndices(ista).size());
         if (nHitsSta > maxStationHits) {
-          statNwindowHits -= nHitsSta;
           wData.TsHitIndices(ista).clear();
         }
       }
     }
 
+    int statNwindowHits = 0;
+    for (int ista = 0; ista < fParameters.GetNstationsActive(); ++ista) {
+      statNwindowHits += wData.TsHitIndices(ista).size();
+    }
     statNhitsProcessed += statNwindowHits;
 
     // print the LOG for every 10 ms of data processed
     if constexpr (0) {
-      int currentChunk = (int) ((windowStart - fStatTsStart) / 10.e6);
+      int currentChunk = (int) ((windowRange.first - fStatTsStart) / 10.e6);
       if (!areUntouchedDataLeft || currentChunk > statLastLogTimeChunk) {
         statLastLogTimeChunk = currentChunk;
-        double dataRead      = 100. * (windowStart + fWindowLength - fStatTsStart) / (fStatTsEnd - fStatTsStart);
+        double dataRead      = 100. * (windowRange.first + fWindowLength - fStatTsStart) / (fStatTsEnd - fStatTsStart);
         if (dataRead > 100.) {
           dataRead = 100.;
         }
-        LOG(debug) << "CA tracker process sliding window N " << statNwindows << ": time " << windowStart / 1.e6
+        LOG(debug) << "CA tracker process sliding window N " << statNwindows << ": time " << windowRange.first / 1.e6
                    << " ms + " << fWindowLength / 1.e3 << " us) with " << statNwindowHits << " hits. "
                    << " Processing " << dataRead << " % of the TS time and "
                    << 100. * statNhitsProcessed / fStatNhitsTotal << " % of TS hits."
-                   << " Already reconstructed " << fvRecoTracks[iThread].size() << " tracks on thread #" << iThread;
+                   << " Already reconstructed " << tracks.size() << " tracks on thread #" << iThread;
       }
     }
 
-
     //out << statNwindowHits << ' ';
-
-    fvMonitorDataThread[iThread].StopTimer(ETimer::PrepareWindow);
+    monitor.StopTimer(ETimer::PrepareWindow);
 
     //Timer trackingInWindow;  //DBG
     //trackingInWindow.Start();
-    fvMonitorDataThread[iThread].StartTimer(ETimer::TrackingWindow);
+    monitor.StartTimer(ETimer::TrackingWindow);
     trackFinderWindow.CaTrackFinderSlice(input, wData);
-    fvMonitorDataThread[iThread].StopTimer(ETimer::TrackingWindow);
+    monitor.StopTimer(ETimer::TrackingWindow);
     //trackingInWindow.Stop();
     //out << trackingInWindow.GetTotalMs() << ' ';
 
     // save reconstructed tracks with no hits in the overlap region
-    //if (windowStart > 13.23e6 && windowStart < 13.26e6) {
-    windowStart += fWindowLength - fWindowOverlap;
+    //if (windowRange.first > 13.23e6 && windowRange.first < 13.26e6) {
+    windowRange.first += fWindowLength - fWindowOverlap;
     // we should add hits from reconstructed but not stored tracks to the new sub-timeslice
     // we do it in a simple way by extending the tsStartNew
     // TODO: only add those hits from the region before tsStartNew that belong to the not stored tracks
     //out << fvWData[iThread].RecoHitIndices().size() << ' ';
     //out << fvWData[iThread].RecoTracks().size() << '\n';
 
-    fvMonitorDataThread[iThread].StartTimer(ETimer::StoreTracksWindow);
+    monitor.StartTimer(ETimer::StoreTracksWindow);
     auto trackFirstHit = wData.RecoHitIndices().begin();
 
     for (const auto& track : wData.RecoTracks()) {
@@ -525,7 +528,7 @@ void TrackFinder::FindTracksThread(const InputData& input, int iThread, fscal& w
       const bool isTrackCompletelyInOverlap =
         std::all_of(trackFirstHit, trackFirstHit + track.fNofHits, [&](int caHitId) {
           CaHitTimeInfo& info = fHitTimeInfo[caHitId];
-          return info.fEventTimeMax >= windowStart;
+          return info.fEventTimeMax >= windowRange.first;
         });
 
       // Don't save tracks from the overlap region, since they might have additional hits in the next subslice.
@@ -539,27 +542,30 @@ void TrackFinder::FindTracksThread(const InputData& input, int iThread, fscal& w
         wData.IsHitKeyUsed(h.BackKey())  = static_cast<int>(useFlag);
 
         if (useFlag) {
-          fvRecoHitIndices[iThread].push_back(caHitId);
+          hitIndices.push_back(caHitId);
         }
       }
       if (useFlag) {
-        fvRecoTracks[iThread].push_back(track);
+        tracks.push_back(track);
       }
 
       trackFirstHit += track.fNofHits;
     }  // sub-timeslice tracks
-    fvMonitorDataThread[iThread].StopTimer(ETimer::StoreTracksWindow);
+    monitor.StopTimer(ETimer::StoreTracksWindow);
 
-    if (windowStart > windowEnd) {
+    if (windowRange.first > windowRange.second) {
       break;
     }
     else {
-      windowStart -= fWindowMargin;  // do 5 ns margin
+      windowRange.first -= fWindowMargin;  // do 5 ns margin
     }
-  }
-  fvMonitorDataThread[iThread].StopTimer(ETimer::TrackingThread);
+    if (!areUntouchedDataLeft) {
+      break;
+    }
+  }  // while(true)
+  monitor.StopTimer(ETimer::TrackingThread);
   //timer.Stop();
   //LOG(info) << "CA: finishing tracking on thread " << iThread << " (time: " << timer.GetTotalMs() << " ms, "
   //          << "hits processed: " << statNhitsProcessed << ", "
-  //          << "hits used: " << fvRecoHitIndices[iThread].size() << ')';
+  //          << "hits used: " << hitIndices.size() << ')';
 }
diff --git a/algo/ca/core/tracking/CaTrackFinder.h b/algo/ca/core/tracking/CaTrackFinder.h
index 21f4d56551d639cdb07ba03abb43d56a06bf6169..5a1cd6d810617c0f11636b6a24256f093697c041 100644
--- a/algo/ca/core/tracking/CaTrackFinder.h
+++ b/algo/ca/core/tracking/CaTrackFinder.h
@@ -57,7 +57,7 @@ namespace cbm::algo::ca
    private:
     // -------------------------------
     // Private methods
-    void FindTracksThread(const InputData& input, int iThread, fscal& windowStart, fscal& windowEnd, int& statNwindows,
+    void FindTracksThread(const InputData& input, int iThread, std::pair<fscal, fscal>& windowRange, int& statNwindows,
                           int& statNhitsProcessed);
     //   bool checkTripletMatch(const ca::Triplet& l, const ca::Triplet& r, fscal& dchi2) const;
 
diff --git a/algo/ca/core/tracking/CaTrackFinderWindow.cxx b/algo/ca/core/tracking/CaTrackFinderWindow.cxx
index 6acaa5091c51db548a95d1a9733bfd06f889dd51..7b8e04d7dec6871537dc85f0a8e1c6de66476e53 100644
--- a/algo/ca/core/tracking/CaTrackFinderWindow.cxx
+++ b/algo/ca/core/tracking/CaTrackFinderWindow.cxx
@@ -44,7 +44,6 @@ using Track = cbm::algo::ca::Track;
 using namespace cbm::algo::ca;
 
 // ---------------------------------------------------------------------------------------------------------------------
-//
 TrackFinderWindow::TrackFinderWindow(const ca::Parameters<fvec>& pars, const fscal mass, const ca::TrackingMode& mode,
                                      ca::TrackingMonitorData& monitorData)
   : fParameters(pars)
@@ -55,20 +54,9 @@ TrackFinderWindow::TrackFinderWindow(const ca::Parameters<fvec>& pars, const fsc
   , fCloneMerger(pars, mass)
   , fTrackFitter(pars, mass, mode)
 {
-  for (unsigned int i = 0; i < constants::size::MaxNstations; ++i) {
-    fvTriplets[i].SetName(std::stringstream() << "TrackFinderWindow::fTriplets[" << i << "]");
-  }
 }
 
-
-// ---------------------------------------------------------------------------------------------------------------------
-//
-TrackFinderWindow::~TrackFinderWindow() {}
-
 // ---------------------------------------------------------------------------------------------------------------------
-//
-
-
 bool TrackFinderWindow::checkTripletMatch(const ca::Triplet& l, const ca::Triplet& r, fscal& dchi2,
                                           WindowData& wData) const
 {
@@ -129,7 +117,73 @@ bool TrackFinderWindow::checkTripletMatch(const ca::Triplet& l, const ca::Triple
 // **************************************************************************************************
 
 // ---------------------------------------------------------------------------------------------------------------------
-//
+void TrackFinderWindow::CaTrackFinderSlice(const ca::InputData& input, WindowData& wData)
+{
+  // Init windows
+  frMonitorData.StartTimer(ETimer::InitWindow);
+  ReadWindowData(input.GetHits(), wData);
+  frMonitorData.StopTimer(ETimer::InitWindow);
+
+  // Init grids
+  frMonitorData.StartTimer(ETimer::PrepareGrid);
+  PrepareGrid(input.GetHits(), wData);
+  frMonitorData.StopTimer(ETimer::PrepareGrid);
+
+  // Run CA iterations
+  frMonitorData.StartTimer(ETimer::FindTracks);
+  auto& caIterations = fParameters.GetCAIterations();
+  for (auto iter = caIterations.begin(); iter != caIterations.end(); ++iter) {
+
+    // ----- Prepare iteration
+    frMonitorData.StartTimer(ETimer::PrepareIteration);
+    PrepareCAIteration(*iter, wData, iter == caIterations.begin());
+    frMonitorData.StopTimer(ETimer::PrepareIteration);
+
+    // ----- Triplets construction -----
+    frMonitorData.StartTimer(ETimer::ConstructTriplets);
+    ConstructTriplets(wData);
+    frMonitorData.StopTimer(ETimer::ConstructTriplets);
+
+    // ----- Search for neighbouring triplets -----
+    frMonitorData.StartTimer(ETimer::SearchNeighbours);
+    SearchNeighbors(wData);
+    frMonitorData.StopTimer(ETimer::SearchNeighbours);
+
+    // ----- Collect track candidates and create tracks
+    frMonitorData.StartTimer(ETimer::CreateTracks);
+    CreateTracks(wData, *iter, std::get<2>(fTripletData));
+    frMonitorData.StopTimer(ETimer::CreateTracks);
+
+    // ----- Suppress strips of suppressed hits
+    frMonitorData.StartTimer(ETimer::SuppressHitKeys);
+    for (unsigned int ih = 0; ih < wData.Hits().size(); ih++) {
+      if (wData.IsHitSuppressed(ih)) {
+        const ca::Hit& hit                 = wData.Hit(ih);
+        wData.IsHitKeyUsed(hit.FrontKey()) = 1;
+        wData.IsHitKeyUsed(hit.BackKey())  = 1;
+      }
+    }
+    frMonitorData.StopTimer(ETimer::SuppressHitKeys);
+  }  // ---- Loop over Track Finder iterations: END ----//
+  frMonitorData.StopTimer(ETimer::FindTracks);
+
+  // Fit tracks
+  frMonitorData.StartTimer(ETimer::FitTracks);
+  fTrackFitter.FitCaTracks(input, wData);
+  frMonitorData.StopTimer(ETimer::FitTracks);
+
+  // Merge clones
+  frMonitorData.StartTimer(ETimer::MergeClones);
+  fCloneMerger.Exec(input, wData);
+  frMonitorData.StopTimer(ETimer::MergeClones);
+
+  // Fit tracks
+  frMonitorData.StartTimer(ETimer::FitTracks);
+  fTrackFitter.FitCaTracks(input, wData);
+  frMonitorData.StopTimer(ETimer::FitTracks);
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
 void TrackFinderWindow::ReadWindowData(const Vector<Hit>& hits, WindowData& wData)
 {
   int nHits = 0;
@@ -165,17 +219,9 @@ void TrackFinderWindow::ReadWindowData(const Vector<Hit>& hits, WindowData& wDat
   wData.RecoHitIndices().reserve(2 * nHits);
 }
 
-void TrackFinderWindow::CaTrackFinderSlice(const ca::InputData& input, WindowData& wData)
+// ---------------------------------------------------------------------------------------------------------------------
+void TrackFinderWindow::PrepareGrid(const Vector<Hit>& hits, WindowData& wData)
 {
-  /********************************/ /**
-   * CATrackFinder routine setting
-   ***********************************/
-
-  frMonitorData.StartTimer(ETimer::InitWindow);
-  ReadWindowData(input.GetHits(), wData);
-  frMonitorData.StopTimer(ETimer::InitWindow);
-
-  frMonitorData.StartTimer(ETimer::PrepareGrid);
   for (int iS = 0; iS < fParameters.GetNstationsActive(); ++iS) {
 
     fscal lasttime  = std::numeric_limits<fscal>::infinity();
@@ -186,11 +232,10 @@ void TrackFinderWindow::CaTrackFinderSlice(const ca::InputData& input, WindowDat
     fscal gridMaxY  = 0.1;
 
     for (ca::HitIndex_t ih = 0; ih < wData.TsHitIndices(iS).size(); ++ih) {
-      const ca::Hit& h = input.GetHit(wData.TsHitIndex(iS, ih));
+      const ca::Hit& h = hits[wData.TsHitIndex(iS, ih)];
 
       gridMinX = std::min(gridMinX, h.X());
       gridMinY = std::min(gridMinY, h.Y());
-
       gridMaxX = std::max(gridMaxX, h.X());
       gridMaxY = std::max(gridMaxY, h.Y());
 
@@ -227,402 +272,372 @@ void TrackFinderWindow::CaTrackFinderSlice(const ca::InputData& input, WindowDat
                    wData.HitKeyFlags());
     /* clang-format on */
   }
-  frMonitorData.StopTimer(ETimer::PrepareGrid);
-
-  // ----- Find tracks -----
-  //
-  frMonitorData.StartTimer(ETimer::FindTracks);
-  auto& caIterations = fParameters.GetCAIterations();
-
-  for (auto iter = caIterations.begin(); iter != caIterations.end(); ++iter) {
+}
 
-    // ----- Prepare iteration
-    //
-    frMonitorData.StartTimer(ETimer::PrepareIteration);
-    const auto& caIteration = *iter;  // Dereferencing iterator to get the value
-    wData.SetCurrentIteration(&caIteration);
+// ---------------------------------------------------------------------------------------------------------------------
+void TrackFinderWindow::PrepareCAIteration(const ca::Iteration& caIteration, WindowData& wData, const bool isFirst)
+{
+  wData.SetCurrentIteration(&caIteration);
 
-    // Use iter to check if it is not the first element
-    if (iter != caIterations.begin()) {
-      for (int ista = 0; ista < fParameters.GetNstationsActive(); ++ista) {
-        wData.Grid(ista).RemoveUsedHits(wData.Hits(), wData.HitKeyFlags());
-      }
+  // Check if it is not the first element
+  if (!isFirst) {
+    for (int ista = 0; ista < fParameters.GetNstationsActive(); ++ista) {
+      wData.Grid(ista).RemoveUsedHits(wData.Hits(), wData.HitKeyFlags());
     }
+  }
 
-    // --> frAlgo.fIsWindowHitSuppressed.reset(frAlgo.fWindowHits.size(), 0);
-    wData.ResetHitSuppressionFlags();  // TODO: ??? No effect?
-                                       //
-    for (int j = 0; j < fParameters.GetNstationsActive(); j++) {
-      const size_t nHitsStation = wData.TsHitIndices(j).size();
-      fvTriplets[j].clear();
-      fvTriplets[j].reserve(2 * nHitsStation);
-    }
+  // --> frAlgo.fIsWindowHitSuppressed.reset(frAlgo.fWindowHits.size(), 0);
+  wData.ResetHitSuppressionFlags();  // TODO: ??? No effect?
 
-    // #pragma omp  task
-    {
-      // --- SET PARAMETERS FOR THE ITERATION ---
-      // define the target
-      const fscal SigmaTargetX = caIteration.GetTargetPosSigmaX();
-      const fscal SigmaTargetY = caIteration.GetTargetPosSigmaY();  // target constraint [cm]
+  // --- SET PARAMETERS FOR THE ITERATION ---
+  // define the target
+  const fscal SigmaTargetX = caIteration.GetTargetPosSigmaX();
+  const fscal SigmaTargetY = caIteration.GetTargetPosSigmaY();  // target constraint [cm]
 
-      // Select magnetic field. For primary tracks - fVtxFieldValue, for secondary tracks - st.fieldSlice
-      if (caIteration.GetPrimaryFlag()) {
-        wData.TargB() = fParameters.GetVertexFieldValue();
-      }
-      else {
-        wData.TargB() = fParameters.GetStation(0).fieldSlice.GetFieldValue(0, 0);
-      }  // NOTE: calculates field frAlgo.fTargB in the center of 0th station
-
-      wData.TargetMeasurement().SetX(fParameters.GetTargetPositionX());
-      wData.TargetMeasurement().SetY(fParameters.GetTargetPositionY());
-      wData.TargetMeasurement().SetDx2(SigmaTargetX * SigmaTargetX);
-      wData.TargetMeasurement().SetDxy(0);
-      wData.TargetMeasurement().SetDy2(SigmaTargetY * SigmaTargetY);
-      wData.TargetMeasurement().SetNdfX(1);
-      wData.TargetMeasurement().SetNdfY(1);
-    }
+  // Select magnetic field. For primary tracks - fVtxFieldValue, for secondary tracks - st.fieldSlice
+  if (caIteration.GetPrimaryFlag()) {
+    wData.TargB() = fParameters.GetVertexFieldValue();
+  }
+  else {
+    wData.TargB() = fParameters.GetStation(0).fieldSlice.GetFieldValue(0, 0);
+  }  // NOTE: calculates field frAlgo.fTargB in the center of 0th station
+
+  wData.TargetMeasurement().SetX(fParameters.GetTargetPositionX());
+  wData.TargetMeasurement().SetY(fParameters.GetTargetPositionY());
+  wData.TargetMeasurement().SetDx2(SigmaTargetX * SigmaTargetX);
+  wData.TargetMeasurement().SetDxy(0);
+  wData.TargetMeasurement().SetDy2(SigmaTargetY * SigmaTargetY);
+  wData.TargetMeasurement().SetNdfX(1);
+  wData.TargetMeasurement().SetNdfY(1);
+}
 
-    frMonitorData.StopTimer(ETimer::PrepareIteration);
+// ---------------------------------------------------------------------------------------------------------------------
+void TrackFinderWindow::ConstructTriplets(WindowData& wData)
+{
+  auto& [vHitFirstTriplet, vHitNofTriplets, vTriplets] = fTripletData;
+  vHitFirstTriplet.reset(wData.Hits().size(), 0);  /// link hit -> first triplet { hit, *, *}
+  vHitNofTriplets.reset(wData.Hits().size(), 0);   /// link hit ->n triplets { hit, *, *}
 
-    // ----- Triplets construction -----
-    //
-    frMonitorData.StartTimer(ETimer::ConstructTriplets);
-    ca::TripletConstructor constructor(fParameters, input, wData, fDefaultMass, fTrackingMode);
+  ca::TripletConstructor constructor(fParameters, wData, fDefaultMass, fTrackingMode);
 
-    // indices of the two neighbouring station, taking into account allowed gaps
-    std::vector<std::pair<int, int>> staPattern;
-    for (int gap = 0; gap <= wData.CurrentIteration()->GetMaxStationGap(); gap++) {
-      for (int i = 0; i <= gap; i++) {
-        staPattern.push_back(std::make_pair(1 + i, 2 + gap));
-      }
+  // prepare triplet storage
+  for (int j = 0; j < fParameters.GetNstationsActive(); j++) {
+    const size_t nHitsStation = wData.TsHitIndices(j).size();
+    vTriplets[j].clear();
+    vTriplets[j].reserve(2 * nHitsStation);
+  }
+
+  // indices of the two neighbouring station, taking into account allowed gaps
+  std::vector<std::pair<int, int>> staPattern;
+  for (int gap = 0; gap <= wData.CurrentIteration()->GetMaxStationGap(); gap++) {
+    for (int i = 0; i <= gap; i++) {
+      staPattern.push_back(std::make_pair(1 + i, 2 + gap));
     }
+  }
 
-    Vector<int> vHitFirstTriplet(wData.Hits().size(), 0);  /// link hit -> first triplet { hit, *, *}
-    Vector<int> vHitNofTriplets(wData.Hits().size(), 0);   /// link hit ->n triplets { hit, *, *}
-
-    const int iStFirst = wData.CurrentIteration()->GetFirstStationIndex();
-    for (int istal = fParameters.GetNstationsActive() - 2; istal >= iStFirst; istal--) {
-      // start with downstream chambers
-      const auto& grid = wData.Grid(istal);
-      for (auto& entry : grid.GetEntries()) {
-        ca::HitIndex_t ihitl = entry.GetObjectId();
-        const size_t oldSize = fvTriplets[istal].size();
-        for (auto& pattern : staPattern) {
-          constructor.CreateTripletsForHit(istal, istal + pattern.first, istal + pattern.second, ihitl);
-          fvTriplets[istal].insert(fvTriplets[istal].end(), constructor.GetTriplets().begin(),
-                                   constructor.GetTriplets().end());
-        }
-        vHitFirstTriplet[ihitl] = PackTripletId(istal, oldSize);
-        vHitNofTriplets[ihitl]  = fvTriplets[istal].size() - oldSize;
+  for (int istal = fParameters.GetNstationsActive() - 2; istal >= wData.CurrentIteration()->GetFirstStationIndex();
+       istal--) {
+    // start with downstream chambers
+    const auto& grid = wData.Grid(istal);
+    for (auto& entry : grid.GetEntries()) {
+      ca::HitIndex_t ihitl = entry.GetObjectId();
+      const size_t oldSize = vTriplets[istal].size();
+      for (auto& pattern : staPattern) {
+        constructor.CreateTripletsForHit(fvTriplets, istal, istal + pattern.first, istal + pattern.second, ihitl);
+        vTriplets[istal].insert(vTriplets[istal].end(), fvTriplets.begin(), fvTriplets.end());
       }
-    }  // istal
-    frMonitorData.StopTimer(ETimer::ConstructTriplets);
-
-    // ----- Search for neighbouring triplets -----
-    //
-    frMonitorData.StartTimer(ETimer::SearchNeighbours);
+      vHitFirstTriplet[ihitl] = PackTripletId(istal, oldSize);
+      vHitNofTriplets[ihitl]  = vTriplets[istal].size() - oldSize;
+    }
+  }  // istal
+}
 
-    for (int istal = fParameters.GetNstationsActive() - 2; istal >= iStFirst; istal--) {
-      // start with downstream chambers
+// ---------------------------------------------------------------------------------------------------------------------
+void TrackFinderWindow::SearchNeighbors(WindowData& wData)
+{
+  auto& [vHitFirstTriplet, vHitNofTriplets, vTriplets] = fTripletData;
 
-      for (ca::Triplet& tr : fvTriplets[istal]) {
-        unsigned int nNeighbours   = vHitNofTriplets[tr.GetMHit()];
-        unsigned int neighLocation = vHitFirstTriplet[tr.GetMHit()];
-        unsigned int neighStation  = TripletId2Station(neighLocation);
-        unsigned int neighTriplet  = TripletId2Triplet(neighLocation);
+  for (int istal = fParameters.GetNstationsActive() - 2; istal >= wData.CurrentIteration()->GetFirstStationIndex();
+       istal--) {
+    // start with downstream chambers
 
-        if (nNeighbours > 0) {
-          assert((int) neighStation >= istal + 1
-                 && (int) neighStation <= istal + 1 + wData.CurrentIteration()->GetMaxStationGap());
-        }
+    for (ca::Triplet& tr : vTriplets[istal]) {
+      unsigned int nNeighbours   = vHitNofTriplets[tr.GetMHit()];
+      unsigned int neighLocation = vHitFirstTriplet[tr.GetMHit()];
+      unsigned int neighStation  = TripletId2Station(neighLocation);
+      unsigned int neighTriplet  = TripletId2Triplet(neighLocation);
 
-        unsigned char level = 0;
+      if (nNeighbours > 0) {
+        assert((int) neighStation >= istal + 1
+               && (int) neighStation <= istal + 1 + wData.CurrentIteration()->GetMaxStationGap());
+      }
 
-        for (unsigned int iN = 0; iN < nNeighbours; ++iN, ++neighTriplet, ++neighLocation) {
+      unsigned char level = 0;
 
-          ca::Triplet& neighbour = fvTriplets[neighStation][neighTriplet];
+      for (unsigned int iN = 0; iN < nNeighbours; ++iN, ++neighTriplet, ++neighLocation) {
 
-          fscal dchi2 = 0.;
-          if (!checkTripletMatch(tr, neighbour, dchi2, wData)) continue;
+        ca::Triplet& neighbour = vTriplets[neighStation][neighTriplet];
 
-          if (tr.GetFNeighbour() == 0) {
-            tr.SetFNeighbour(neighLocation);
-          }
-          tr.SetNNeighbours(neighLocation - tr.GetFNeighbour() + 1);
+        fscal dchi2 = 0.;
+        if (!checkTripletMatch(tr, neighbour, dchi2, wData)) continue;
 
-          level = std::max(level, static_cast<unsigned char>(neighbour.GetLevel() + 1));
+        if (tr.GetFNeighbour() == 0) {
+          tr.SetFNeighbour(neighLocation);
         }
-        tr.SetLevel(level);
-      }  // neighbour search
-
-      frMonitorData.IncrementCounter(ECounter::Triplet, fvTriplets[istal].size());
+        tr.SetNNeighbours(neighLocation - tr.GetFNeighbour() + 1);
 
-    }  // istal
-    frMonitorData.StopTimer(ETimer::SearchNeighbours);
+        level = std::max(level, static_cast<unsigned char>(neighbour.GetLevel() + 1));
+      }
+      tr.SetLevel(level);
+    }
+    frMonitorData.IncrementCounter(ECounter::Triplet, vTriplets[istal].size());
+  }
+}
 
-    // ----- Collect track candidates and create tracks
-    //
-    frMonitorData.StartTimer(ETimer::CreateTracks);
+// ---------------------------------------------------------------------------------------------------------------------
+void TrackFinderWindow::CreateTracks(WindowData& wData, const ca::Iteration& caIteration, TripletArray_t& vTriplets)
+{
+  // min level to start triplet. So min track length = min_level+3.
+  const int min_level = wData.CurrentIteration()->GetTrackFromTripletsFlag()
+                          ? 0
+                          : std::min(caIteration.GetMinNhits(), caIteration.GetMinNhitsStation0()) - 3;
+
+  // collect consequtive: the longest tracks, shorter, more shorter and so on
+  for (int firstTripletLevel = fParameters.GetNstationsActive() - 3; firstTripletLevel >= min_level;
+       firstTripletLevel--) {
+    // choose length in triplets number - firstTripletLevel - the maximum possible triplet level among all triplets in the searched candidate
+    CreateTrackCandidates(wData, vTriplets, min_level, firstTripletLevel);
+    DoCompetitionLoop(wData);
+    SelectTracks(wData);
+  }
+}
 
-    // min level to start triplet. So min track length = min_level+3.
-    const int min_level = wData.CurrentIteration()->GetTrackFromTripletsFlag()
-                            ? 0
-                            : std::min(caIteration.GetMinNhits(), caIteration.GetMinNhitsStation0()) - 3;
+// ---------------------------------------------------------------------------------------------------------------------
+void TrackFinderWindow::CreateTrackCandidates(WindowData& wData, TripletArray_t& vTriplets, const int min_level,
+                                              const int firstTripletLevel)
+{
+  //  how many levels to check
+  int nlevel = (fParameters.GetNstationsActive() - 2) - firstTripletLevel + 1;
 
-    ca::Branch new_tr[constants::size::MaxNstations];
-    ca::Branch best_tr;
+  const unsigned char min_best_l =
+    (firstTripletLevel > min_level) ? firstTripletLevel + 2 : min_level + 3;  // loose maximum
 
-    // collect consequtive: the longest tracks, shorter, more shorter and so on
-    for (int firstTripletLevel = fParameters.GetNstationsActive() - 3; firstTripletLevel >= min_level;
-         firstTripletLevel--) {
-      // choose length in triplets number - firstTripletLevel - the maximum possible triplet level among all triplets in the searched candidate
+  // Uses persistent field to save memory allocations.
+  // fNewTr is only used here!
+  ca::Branch(&new_tr)[constants::size::MaxNstations] = fNewTr;
 
-      //  how many levels to check
-      int nlevel = (fParameters.GetNstationsActive() - 2) - firstTripletLevel + 1;
+  fvTrackCandidates.clear();
+  fvTrackCandidates.reserve(wData.Hits().size() / 10);
 
-      const unsigned char min_best_l =
-        (firstTripletLevel > min_level) ? firstTripletLevel + 2 : min_level + 3;  // loose maximum
+  for (const auto& h : wData.Hits()) {
+    fvHitKeyToTrack[h.FrontKey()] = -1;
+    fvHitKeyToTrack[h.BackKey()]  = -1;
+  }
 
-      fvTrackCandidates.clear();
-      fvTrackCandidates.reserve(wData.Hits().size() / 10);
+  //== Loop over triplets with the required level, find and store track candidates
+  for (int istaF = wData.CurrentIteration()->GetFirstStationIndex();
+       istaF <= fParameters.GetNstationsActive() - 3 - firstTripletLevel; ++istaF) {
 
-      for (const auto& h : wData.Hits()) {
-        fvHitKeyToTrack[h.FrontKey()] = -1;
-        fvHitKeyToTrack[h.BackKey()]  = -1;
-      }
+    if (--nlevel == 0) break;  //TODO: SG: this is not needed
 
-      //== Loop over triplets with the required level, find and store track candidates
+    for (ca::Triplet& first_trip : vTriplets[istaF]) {
 
-      for (int istaF = iStFirst; istaF <= fParameters.GetNstationsActive() - 3 - firstTripletLevel; ++istaF) {
+      const auto& fstTripLHit = wData.Hit(first_trip.GetLHit());
+      if (wData.IsHitKeyUsed(fstTripLHit.FrontKey()) || wData.IsHitKeyUsed(fstTripLHit.BackKey())) {
+        continue;
+      }
 
-        if (--nlevel == 0) break;  //TODO: SG: this is not needed
+      //  skip track candidates that are too short
 
-        for (ca::Triplet& first_trip : fvTriplets[istaF]) {
+      int minNhits = wData.CurrentIteration()->GetMinNhits();
 
-          const auto& fstTripLHit = wData.Hit(first_trip.GetLHit());
-          if (wData.IsHitKeyUsed(fstTripLHit.FrontKey()) || wData.IsHitKeyUsed(fstTripLHit.BackKey())) {
-            continue;
-          }
+      if (fstTripLHit.Station() == 0) {
+        minNhits = wData.CurrentIteration()->GetMinNhitsStation0();
+      }
+      if (wData.CurrentIteration()->GetTrackFromTripletsFlag()) {
+        minNhits = 0;
+      }
 
-          //  skip track candidates that are too short
+      if (3 + first_trip.GetLevel() < minNhits) {
+        continue;
+      }
 
-          int minNhits = wData.CurrentIteration()->GetMinNhits();
+      // Collect triplets, which can start a track with length equal to firstTipletLevel + 3. This cut suppresses
+      // ghost tracks, but does not affect the efficiency
+      if (first_trip.GetLevel() < firstTripletLevel) {
+        continue;
+      }
 
-          if (fstTripLHit.Station() == 0) {
-            minNhits = wData.CurrentIteration()->GetMinNhitsStation0();
-          }
-          if (wData.CurrentIteration()->GetTrackFromTripletsFlag()) {
-            minNhits = 0;
-          }
+      ca::Branch curr_tr;
+      curr_tr.AddHit(first_trip.GetLHit());
+      curr_tr.SetChi2(first_trip.GetChi2());
 
-          if (3 + first_trip.GetLevel() < minNhits) {
-            continue;
-          }
+      ca::Branch best_tr = curr_tr;
 
-          // Collect triplets, which can start a track with length equal to firstTipletLevel + 3. This cut suppresses
-          // ghost tracks, but does not affect the efficiency
-          if (first_trip.GetLevel() < firstTripletLevel) {
-            continue;
-          }
+      /// reqursive func to build a tree of possible track-candidates and choose the best
+      CAFindTrack(istaF, best_tr, &first_trip, curr_tr, min_best_l, new_tr, wData, vTriplets);
 
-          ca::Branch curr_tr;
-          curr_tr.AddHit(fstTripLHit.Id());
-          curr_tr.SetChi2(first_trip.GetChi2());
+      if (best_tr.NofHits() < firstTripletLevel + 2) continue;  // loose maximum one hit
 
-          best_tr = curr_tr;
+      if (best_tr.NofHits() < min_level + 3) continue;  // should find all hits for min_level
 
-          /// reqursive func to build a tree of possible track-candidates and choose the best
-          CAFindTrack(istaF, best_tr, &first_trip, curr_tr, min_best_l, new_tr, wData);
+      if (best_tr.NofHits() < minNhits) {
+        continue;
+      }
 
-          if (best_tr.NofHits() < firstTripletLevel + 2) continue;  // loose maximum one hit
+      int ndf = best_tr.NofHits() * 2 - 5;
 
-          if (best_tr.NofHits() < min_level + 3) continue;  // should find all hits for min_level
+      // TODO: automatize the NDF calculation
 
-          if (best_tr.NofHits() < minNhits) {
-            continue;
-          }
+      if (ca::TrackingMode::kGlobal == fTrackingMode || ca::TrackingMode::kMcbm == fTrackingMode) {
+        ndf = best_tr.NofHits() * 2 - 4;
+      }
 
-          int ndf = best_tr.NofHits() * 2 - 5;
+      best_tr.SetChi2(best_tr.Chi2() / ndf);
+      if (fParameters.GetGhostSuppression()) {
+        if (3 == best_tr.NofHits()) {
+          if (!wData.CurrentIteration()->GetPrimaryFlag() && (istaF != 0)) continue;  // too /*short*/ non-MAPS track
+          if (wData.CurrentIteration()->GetPrimaryFlag() && (best_tr.Chi2() > 5.0)) continue;
+        }
+      }
+      fvTrackCandidates.push_back(best_tr);
+      ca::Branch& tr = fvTrackCandidates.back();
+      tr.SetStation(istaF);
+      tr.SetId(fvTrackCandidates.size() - 1);
+      tr.SetAlive(true);
+      if constexpr (fDebug) {
+        std::stringstream s;
+        s << "iter " << wData.CurrentIteration()->GetName() << ", track candidate " << fvTrackCandidates.size() - 1
+          << " found, L = " << best_tr.NofHits() << " chi2= " << best_tr.Chi2() << " hits: ";
+        for (auto hitIdLoc : tr.Hits()) {
+          const auto hitId = wData.Hit(hitIdLoc).Id();
+          s << hitId << " (mc " << ca::Framework::GetMcTrackIdForCaHit(hitId) << ") ";
+        }
+        LOG(info) << s.str();
+      }
+    }  // itrip
+  }    // istaF
 
-          // TODO: automatize the NDF calculation
+  for (ca::Branch& tr : fvTrackCandidates) {
+    tr.SetAlive(false);
+  }
+}
 
-          if (ca::TrackingMode::kGlobal == fTrackingMode || ca::TrackingMode::kMcbm == fTrackingMode) {
-            ndf = best_tr.NofHits() * 2 - 4;
-          }
+// ---------------------------------------------------------------------------------------------------------------------
+void TrackFinderWindow::DoCompetitionLoop(const WindowData& wData)
+{
+  for (int iComp = 0; (iComp < 100); ++iComp) {
 
-          best_tr.SetChi2(best_tr.Chi2() / ndf);
-          if (fParameters.GetGhostSuppression()) {
-            if (3 == best_tr.NofHits()) {
-              if (!wData.CurrentIteration()->GetPrimaryFlag() && (istaF != 0))
-                continue;  // too /*short*/ non-MAPS track
-              if (wData.CurrentIteration()->GetPrimaryFlag() && (best_tr.Chi2() > 5.0)) continue;
-            }
-          }
-          fvTrackCandidates.push_back(best_tr);
-          ca::Branch& tr = fvTrackCandidates.back();
-          tr.SetStation(istaF);
-          tr.SetId(fvTrackCandidates.size() - 1);
-          tr.SetAlive(true);
-          if constexpr (fDebug) {
-            std::stringstream s;
-            s << "iter " << wData.CurrentIteration()->GetName() << ", track candidate " << fvTrackCandidates.size() - 1
-              << " found, L = " << best_tr.NofHits() << " chi2= " << best_tr.Chi2() << " hits: ";
-            for (auto hitId : tr.Hits()) {
-              s << hitId << " (mc " << ca::Framework::GetMcTrackIdForCaHit(hitId) << ") ";
-            }
-            LOG(info) << s.str();
-          }
-        }  // itrip
-      }    // istaF
+    bool repeatCompetition = false;
 
-      for (ca::Branch& tr : fvTrackCandidates) {
-        tr.SetAlive(false);
+    // == Loop over track candidates and mark their strips
+    for (ca::Branch& tr : fvTrackCandidates) {
+      if (tr.IsAlive()) {
+        continue;
       }
+      for (auto& hitId : tr.Hits()) {
 
-      bool repeatCompetition = true;
-      int iComp              = 0;
-      for (iComp = 0; (iComp < 100) && repeatCompetition; ++iComp) {
-
-        repeatCompetition = false;
-
-        // == Loop over track candidates and mark their strips
-        for (ca::Branch& tr : fvTrackCandidates) {
-          if (tr.IsAlive()) {
-            continue;
-          }
-          for (auto& hitId : tr.Hits()) {
-
-            auto updateStrip = [&](int& strip) {
-              if ((strip >= 0) && (strip != tr.Id())) {  // strip is used by other candidate
-                const auto& other = fvTrackCandidates[strip];
-                if (!other.IsAlive() && tr.IsBetterThan(other)) {  // D.S. 29.7.24: should this be || instead of && ?
-                  strip = tr.Id();
-                }
-                else {
-                  return false;
-                }
-              }
-              else {
-                strip = tr.Id();
-              }
-              return true;
-            };
-
-            const ca::Hit& h = input.GetHit(hitId);
-            if (!updateStrip(fvHitKeyToTrack[h.FrontKey()])) {  // front  strip
-              break;
+        auto updateStrip = [&](int& strip) {
+          if ((strip >= 0) && (strip != tr.Id())) {  // strip is used by other candidate
+            const auto& other = fvTrackCandidates[strip];
+            if (!other.IsAlive() && tr.IsBetterThan(other)) {  // D.S. 29.7.24: should this be || instead of && ?
+              strip = tr.Id();
             }
-            if (!updateStrip(fvHitKeyToTrack[h.BackKey()])) {  // back  strip
-              break;
-            }
-          }  // loop over hits
-        }    // itrack
-
-        // == Check if some suppressed candidates are still alive
-
-        for (ca::Branch& tr : fvTrackCandidates) {
-          if (tr.IsAlive()) {
-            continue;
-          }
-
-          tr.SetAlive(true);
-          for (const auto& hitIndex : tr.Hits()) {
-            if (!tr.IsAlive()) break;
-            const ca::Hit& h = input.GetHit(hitIndex);
-            tr.SetAlive((fvHitKeyToTrack[h.FrontKey()] == tr.Id()) && (fvHitKeyToTrack[h.BackKey()] == tr.Id()));
-          }
-
-          if (!tr.IsAlive()) {  // release strips
-            for (auto hitId : tr.Hits()) {
-              const ca::Hit& h = input.GetHit(hitId);
-              if (fvHitKeyToTrack[h.FrontKey()] == tr.Id()) {
-                fvHitKeyToTrack[h.FrontKey()] = -1;
-              }
-              if (fvHitKeyToTrack[h.BackKey()] == tr.Id()) {
-                fvHitKeyToTrack[h.BackKey()] = -1;
-              }
+            else {
+              return false;
             }
           }
           else {
-            repeatCompetition = true;
+            strip = tr.Id();
           }
-        }  // itrack
-
-      }  // competitions
+          return true;
+        };
 
-      // LOG(info) << " N Competitions: " << iComp ;
+        const ca::Hit& h = wData.Hit(hitId);
+        if (!updateStrip(fvHitKeyToTrack[h.FrontKey()])) {  // front  strip
+          break;
+        }
+        if (!updateStrip(fvHitKeyToTrack[h.BackKey()])) {  // back  strip
+          break;
+        }
+      }  // loop over hits
+    }    // itrack
 
-      // ==
+    // == Check if some suppressed candidates are still alive
 
-      for (Tindex iCandidate = 0; iCandidate < (Tindex) fvTrackCandidates.size(); ++iCandidate) {
-        ca::Branch& tr = fvTrackCandidates[iCandidate];
+    for (ca::Branch& tr : fvTrackCandidates) {
+      if (tr.IsAlive()) {
+        continue;
+      }
 
-        if constexpr (fDebug) {
-          LOG(info) << "iter " << wData.CurrentIteration()->GetName() << ", track candidate " << iCandidate
-                    << ": alive = " << tr.IsAlive();
-        }
-        if (!tr.IsAlive()) continue;
+      tr.SetAlive(true);
+      for (const auto& hitIndex : tr.Hits()) {
+        if (!tr.IsAlive()) break;
+        const ca::Hit& h = wData.Hit(hitIndex);
+        tr.SetAlive((fvHitKeyToTrack[h.FrontKey()] == tr.Id()) && (fvHitKeyToTrack[h.BackKey()] == tr.Id()));
+      }
 
-        if (wData.CurrentIteration()->GetExtendTracksFlag()) {
-          if (tr.NofHits() < fParameters.GetNstationsActive()) {
-            fTrackExtender.ExtendBranch(tr, input, wData);
+      if (!tr.IsAlive()) {  // release strips
+        for (auto hitId : tr.Hits()) {
+          const ca::Hit& h = wData.Hit(hitId);
+          if (fvHitKeyToTrack[h.FrontKey()] == tr.Id()) {
+            fvHitKeyToTrack[h.FrontKey()] = -1;
           }
-        }
-
-        for (auto iHit : tr.Hits()) {
-          const ca::Hit& hit = input.GetHit(iHit);
-          /// used strips are marked
-          wData.IsHitKeyUsed(hit.FrontKey()) = 1;
-          wData.IsHitKeyUsed(hit.BackKey())  = 1;
-          wData.RecoHitIndices().push_back(iHit);
-        }
-        Track t;
-        t.fNofHits = tr.NofHits();
-        wData.RecoTracks().push_back(t);
-        if (0) {  // SG debug
-          std::stringstream s;
-          s << "store track " << iCandidate << " chi2= " << tr.Chi2() << "\n";
-          s << " hits: ";
-          for (unsigned int ih = 0; ih < tr.Hits().size(); ih++) {
-            s << ca::Framework::GetMcTrackIdForCaHit(tr.Hits()[ih]) << " ";
+          if (fvHitKeyToTrack[h.BackKey()] == tr.Id()) {
+            fvHitKeyToTrack[h.BackKey()] = -1;
           }
-          LOG(info) << s.str();
         }
+      }
+      else {
+        repeatCompetition = true;
+      }
+    }  // itrack
 
-      }  // tracks
+    if (!repeatCompetition) break;
+  }  // competitions
+}
 
-    }  // firstTripletLevel
-    frMonitorData.StopTimer(ETimer::CreateTracks);
+// ---------------------------------------------------------------------------------------------------------------------
+void TrackFinderWindow::SelectTracks(WindowData& wData)
+{
+  for (Tindex iCandidate = 0; iCandidate < (Tindex) fvTrackCandidates.size(); ++iCandidate) {
+    ca::Branch& tr = fvTrackCandidates[iCandidate];
 
-    frMonitorData.StartTimer(ETimer::SuppressHitKeys);
-    // suppress strips of suppressed hits
-    for (unsigned int ih = 0; ih < wData.Hits().size(); ih++) {
-      if (wData.IsHitSuppressed(ih)) {
-        const ca::Hit& hit                 = wData.Hit(ih);
-        wData.IsHitKeyUsed(hit.FrontKey()) = 1;
-        wData.IsHitKeyUsed(hit.BackKey())  = 1;
-      }
+    if constexpr (fDebug) {
+      LOG(info) << "iter " << wData.CurrentIteration()->GetName() << ", track candidate " << iCandidate
+                << ": alive = " << tr.IsAlive();
     }
-    frMonitorData.StopTimer(ETimer::SuppressHitKeys);
-  }  // ---- Loop over Track Finder iterations: END -----------------------------------------------------------//
-  frMonitorData.StopTimer(ETimer::FindTracks);
+    if (!tr.IsAlive()) continue;
 
-  // Fit tracks
-  frMonitorData.StartTimer(ETimer::FitTracks);
-  fTrackFitter.FitCaTracks(input, wData);
-  frMonitorData.StopTimer(ETimer::FitTracks);
-
-  // Merge clones
-  frMonitorData.StartTimer(ETimer::MergeClones);
-  fCloneMerger.Exec(input, wData);
-  frMonitorData.StopTimer(ETimer::MergeClones);
+    if (wData.CurrentIteration()->GetExtendTracksFlag()) {
+      if (tr.NofHits() < fParameters.GetNstationsActive()) {
+        fTrackExtender.ExtendBranch(tr, wData);
+      }
+    }
 
-  frMonitorData.StartTimer(ETimer::FitTracks);
-  fTrackFitter.FitCaTracks(input, wData);
-  frMonitorData.StopTimer(ETimer::FitTracks);
+    for (auto iHit : tr.Hits()) {
+      const ca::Hit& hit = wData.Hit(iHit);
+      /// used strips are marked
+      wData.IsHitKeyUsed(hit.FrontKey()) = 1;
+      wData.IsHitKeyUsed(hit.BackKey())  = 1;
+      wData.RecoHitIndices().push_back(hit.Id());
+    }
+    Track t;
+    t.fNofHits = tr.NofHits();
+    wData.RecoTracks().push_back(t);
+    if (0) {  // SG debug
+      std::stringstream s;
+      s << "store track " << iCandidate << " chi2= " << tr.Chi2() << "\n";
+      s << " hits: ";
+      for (auto hitLoc : tr.Hits()) {
+        auto hitId = wData.Hit(hitLoc).Id();
+        s << ca::Framework::GetMcTrackIdForCaHit(hitId) << " ";
+      }
+      LOG(info) << s.str();
+    }
+  }  // tracks
 }
 
-
 /** *************************************************************
      *                                                              *
      *     The routine performs recursive search for tracks         *
@@ -632,8 +647,10 @@ void TrackFinderWindow::CaTrackFinderSlice(const ca::InputData& input, WindowDat
      *                                                              *
      ****************************************************************/
 
+// ---------------------------------------------------------------------------------------------------------------------
 void TrackFinderWindow::CAFindTrack(int ista, ca::Branch& best_tr, const ca::Triplet* curr_trip, ca::Branch& curr_tr,
-                                    unsigned char min_best_l, ca::Branch* new_tr, WindowData& wData)
+                                    unsigned char min_best_l, ca::Branch* new_tr, WindowData& wData,
+                                    TripletArray_t& vTriplets)
 /// recursive search for tracks
 /// input: @ista - station index, @&best_tr - best track for the privious call
 /// output: @&NCalls - number of function calls
@@ -647,10 +664,10 @@ void TrackFinderWindow::CAFindTrack(int ista, ca::Branch& best_tr, const ca::Tri
     const auto& hitR = wData.Hit(curr_trip->GetRHit());
 
     if (!(wData.IsHitKeyUsed(hitM.FrontKey()) || wData.IsHitKeyUsed(hitM.BackKey()))) {
-      curr_tr.AddHit(hitM.Id());
+      curr_tr.AddHit(curr_trip->GetMHit());
     }
     if (!(wData.IsHitKeyUsed(hitR.FrontKey()) || wData.IsHitKeyUsed(hitR.BackKey()))) {
-      curr_tr.AddHit(hitR.Id());
+      curr_tr.AddHit(curr_trip->GetRHit());
     }
 
     //if( curr_tr.NofHits() < min_best_l - 1 ) return; // suppouse that only one hit can be added by extender
@@ -681,7 +698,7 @@ void TrackFinderWindow::CAFindTrack(int ista, ca::Branch& best_tr, const ca::Tri
       unsigned int Station = TripletId2Station(ID);
       unsigned int Triplet = TripletId2Triplet(ID);
 
-      const ca::Triplet& new_trip = fvTriplets[Station][Triplet];
+      const ca::Triplet& new_trip = vTriplets[Station][Triplet];
 
       fscal dchi2 = 0.;
       if (!checkTripletMatch(*curr_trip, new_trip, dchi2, wData)) continue;
@@ -732,12 +749,12 @@ void TrackFinderWindow::CAFindTrack(int ista, ca::Branch& best_tr, const ca::Tri
 
         // add new hit
         new_tr[ista] = curr_tr;
-        new_tr[ista].AddHit(hitL.Id());
+        new_tr[ista].AddHit(new_trip.GetLHit());
 
         const int new_ista = ista + new_trip.GetMSta() - new_trip.GetLSta();
         new_tr[ista].SetChi2(new_chi2);
 
-        CAFindTrack(new_ista, best_tr, &new_trip, new_tr[ista], min_best_l, new_tr, wData);
+        CAFindTrack(new_ista, best_tr, &new_trip, new_tr[ista], min_best_l, new_tr, wData, vTriplets);
       }  // add triplet to track
     }    // for neighbours
   }      // level = 0
diff --git a/algo/ca/core/tracking/CaTrackFinderWindow.h b/algo/ca/core/tracking/CaTrackFinderWindow.h
index 8983a527feda0851b203522f9f38aad5969490c3..eb7296cf63ec46abf92e6ef89c40c2c140a78947 100644
--- a/algo/ca/core/tracking/CaTrackFinderWindow.h
+++ b/algo/ca/core/tracking/CaTrackFinderWindow.h
@@ -35,7 +35,7 @@ namespace cbm::algo::ca
     TrackFinderWindow(const ca::Parameters<fvec>& pars, const fscal mass, const ca::TrackingMode& mode,
                       ca::TrackingMonitorData& monitorData);
     /// Destructor
-    ~TrackFinderWindow();
+    ~TrackFinderWindow() = default;
 
     /// Copy constructor
     TrackFinderWindow(const TrackFinderWindow&) = default;
@@ -58,10 +58,35 @@ namespace cbm::algo::ca
     ///-------------------------------
     /// Private methods
 
+    typedef std::array<Vector<ca::Triplet>, constants::size::MaxNstations> TripletArray_t;
+
+    // TripletData_t:
+    // 1 -> vHitFirstTriplet   /// link hit -> first triplet { hit, *, *}
+    // 2 -> vHitNofTriplets    /// link hit ->n triplets { hit, *, *}
+    // 3 -> vTriplets;
+    typedef std::tuple<Vector<int>, Vector<int>, TripletArray_t> TripletData_t;
+
+    void ConstructTriplets(WindowData& wData);
+
     void ReadWindowData(const Vector<Hit>& hits, WindowData& wData);
 
+    void PrepareGrid(const Vector<Hit>& hits, WindowData& wData);
+
+    void PrepareCAIteration(const ca::Iteration& caIteration, WindowData& wData, const bool isFirst);
+
+    void CreateTracks(WindowData& wData, const ca::Iteration& caIteration, TripletArray_t& vTriplets);
+
+    void CreateTrackCandidates(WindowData& wData, TripletArray_t& vTriplets, const int min_level,
+                               const int firstTripletLevel);
+
+    void DoCompetitionLoop(const WindowData& wData);
+
+    void SelectTracks(WindowData& wData);
+
+    void SearchNeighbors(WindowData& wData);
+
     void CAFindTrack(int ista, ca::Branch& best_tr, const ca::Triplet* curr_trip, ca::Branch& curr_tr,
-                     unsigned char min_best_l, ca::Branch* new_tr, WindowData& wData);
+                     unsigned char min_best_l, ca::Branch* new_tr, WindowData& wData, TripletArray_t& vTriplets);
 
     bool checkTripletMatch(const ca::Triplet& l, const ca::Triplet& r, fscal& dchi2, WindowData& wData) const;
 
@@ -86,16 +111,6 @@ namespace cbm::algo::ca
     ///-------------------------------
     /// Data members
 
-    /// \brief Created triplets vs station index
-    std::array<Vector<ca::Triplet>, constants::size::MaxNstations> fvTriplets;
-
-    /// \brief Track candidates created out of adjacent triplets before the final track selection.
-    /// \note  The candidates may share any amount of hits.
-    Vector<ca::Branch> fvTrackCandidates{"TrackFinderWindow::fTrackCandidates"};
-
-    /// \note Global array for a given thread
-    Vector<int> fvHitKeyToTrack{"TrackFinderWindow::fvHitKeyToTrack"};
-
     static constexpr bool fDebug = false;  // print debug info
 
     const Parameters<fvec>& fParameters;            ///< Object of Framework parameters class
@@ -106,6 +121,23 @@ namespace cbm::algo::ca
     TrackExtender fTrackExtender;        ///< Object of the track extender algorithm
     CloneMerger fCloneMerger;            ///< Object of  the clone merger algorithm
     TrackFitter fTrackFitter;            ///< Object of the track extender algorithm
+
+    /// \note Global array for a given thread
+    Vector<int> fvHitKeyToTrack{"TrackFinderWindow::fvHitKeyToTrack"};
+
+    // Persistent to avoid memory allocations.
+    // Only used in CreateTrackCandidates().
+    ca::Branch fNewTr[constants::size::MaxNstations];
+
+    // Track candidates created out of adjacent triplets before the final track selection.
+    // The candidates may share any amount of hits.
+    Vector<ca::Branch> fvTrackCandidates{"TrackFinderWindow::fvTrackCandidates"};
+
+    // Triplets and related meta data
+    TripletData_t fTripletData;
+
+    // Triplet temporary storage. Only used in ConstructTriplets().
+    Vector<ca::Triplet> fvTriplets;
   };
 
   // ********************************************
diff --git a/algo/ca/core/tracking/CaTrackFit.h b/algo/ca/core/tracking/CaTrackFit.h
index 3aea397d7ffbea2a56f9ab8bf7346899eb3d4c21..dfff3a899c45bda34770d41a064db07b9cf059b8 100644
--- a/algo/ca/core/tracking/CaTrackFit.h
+++ b/algo/ca/core/tracking/CaTrackFit.h
@@ -48,6 +48,8 @@ namespace cbm::algo::ca
 
     TrackFit(const TrackParamBase<DataT>& t) { SetTrack(t); }
 
+    TrackFit(const DataTmask& m, bool fitV) : fMask(m), fDoFitVelocity(fitV) {}
+
     template<typename T>
     TrackFit(const TrackParamBase<T>& t)
     {
diff --git a/algo/ca/core/tracking/CaTripletConstructor.cxx b/algo/ca/core/tracking/CaTripletConstructor.cxx
index 8fdc40b109bd0c86ae0ebe9fd4b6d75b38b11f9e..63d42c05323e5ec9404f819b84b4bbf08e61cd17 100644
--- a/algo/ca/core/tracking/CaTripletConstructor.cxx
+++ b/algo/ca/core/tracking/CaTripletConstructor.cxx
@@ -19,10 +19,9 @@
 
 using namespace cbm::algo::ca;
 
-TripletConstructor::TripletConstructor(const ca::Parameters<fvec>& pars, const ca::InputData& input, WindowData& wData,
-                                       const fscal mass, const ca::TrackingMode& mode)
+TripletConstructor::TripletConstructor(const ca::Parameters<fvec>& pars, WindowData& wData, const fscal mass,
+                                       const ca::TrackingMode& mode)
   : fParameters(pars)
-  , fInputData(input)
   , frWData(wData)
   , fDefaultMass(mass)
   , fTrackingMode(mode)
@@ -37,9 +36,6 @@ TripletConstructor::TripletConstructor(const ca::Parameters<fvec>& pars, const c
                     - fParameters.GetStations().cbegin();
 
   fIsTargetField = (fabs(frWData.TargB().y[0]) > 0.001);
-
-  fFit.SetMask(fmask::One());
-  fFit.SetDoFitVelocity(true);
 }
 
 
@@ -87,64 +83,60 @@ bool TripletConstructor::InitStations(int istal, int istam, int istar)
 }
 
 
-void TripletConstructor::CreateTripletsForHit(int istal, int istam, int istar, ca::HitIndex_t iHitL)
+void TripletConstructor::CreateTripletsForHit(Vector<ca::Triplet>& tripletsOut, int istal, int istam, int istar,
+                                              ca::HitIndex_t iHitL)
 {
   if (!InitStations(istal, istam, istar)) {
+    tripletsOut.clear();
     return;
   }
 
-  if (frWData.CurrentIteration()->GetElectronFlag()) {
-    fFit.SetParticleMass(constants::phys::ElectronMass);
-  }
-  else {
-    fFit.SetParticleMass(fDefaultMass);
-  }
+  ca::TrackFit<fvec> fit(fmask::One(), true);
+  fit.SetParticleMass(frWData.CurrentIteration()->GetElectronFlag() ? constants::phys::ElectronMass : fDefaultMass);
+  fit.SetQp0(frWData.CurrentIteration()->GetMaxQp());
 
   fIhitL           = iHitL;
   const auto& hitl = frWData.Hit(fIhitL);
 
   // fit a straight line through the target and the left hit.
+  TrackParamV& T = fit.Tr();
   {
-
     /// Get the field approximation. Add the target to parameters estimation.
     /// Propagaete to the middle station of a triplet.
-
-    fFit.SetQp0(frWData.CurrentIteration()->GetMaxQp());
-
-    ca::FieldValue<fvec> lB, mB, cB, rB _fvecalignment;
-    ca::FieldValue<fvec> l_B_global, centB_global _fvecalignment;
+    //ca::FieldValue<fvec> lB, mB, cB, rB _fvecalignment;           currently not used
+    //ca::FieldValue<fvec> l_B_global, centB_global _fvecalignment; currently not used
 
     // get the magnetic field along the track.
     // (suppose track is straight line with origin in the target)
-    TrackParamV& T  = fFit.Tr();
-    const fvec dzli = 1.f / (hitl.Z() - fParameters.GetTargetPositionZ());
-
-    T.X()    = hitl.X();
-    T.Y()    = hitl.Y();
-    T.Z()    = hitl.Z();
-    T.Tx()   = (hitl.X() - fParameters.GetTargetPositionX()) * dzli;
-    T.Ty()   = (hitl.Y() - fParameters.GetTargetPositionY()) * dzli;
-    T.Qp()   = fvec(0.);
-    T.Time() = hitl.T();
-    T.Vi()   = constants::phys::SpeedOfLightInv;
-
-    const fvec maxSlopePV = frWData.CurrentIteration()->GetMaxSlopePV();
-    const fvec maxQp      = frWData.CurrentIteration()->GetMaxQp();
-    const fvec txErr2     = maxSlopePV * maxSlopePV / fvec(9.);
-    const fvec qpErr2     = maxQp * maxQp / fvec(9.);
-
-    T.ResetErrors(1., 1., txErr2, txErr2, qpErr2, (fStaL->timeInfo ? hitl.dT2() : 1.e6), 1.e10);
-
-    T.InitVelocityRange(1. / frWData.CurrentIteration()->GetMaxQp());
-
-    T.C00() = hitl.dX2();
-    T.C10() = hitl.dXY();
-    T.C11() = hitl.dY2();
-
-    T.Ndf()     = (frWData.CurrentIteration()->GetPrimaryFlag()) ? fvec(2.) : fvec(0.);
-    T.NdfTime() = (fStaL->timeInfo ? 0 : -1);
+    {
+      const fvec dzli = 1.f / (hitl.Z() - fParameters.GetTargetPositionZ());
+
+      T.X()    = hitl.X();
+      T.Y()    = hitl.Y();
+      T.Z()    = hitl.Z();
+      T.Tx()   = (hitl.X() - fParameters.GetTargetPositionX()) * dzli;
+      T.Ty()   = (hitl.Y() - fParameters.GetTargetPositionY()) * dzli;
+      T.Qp()   = fvec(0.);
+      T.Time() = hitl.T();
+      T.Vi()   = constants::phys::SpeedOfLightInv;
+
+      const fvec maxSlopePV = frWData.CurrentIteration()->GetMaxSlopePV();
+      const fvec maxQp      = frWData.CurrentIteration()->GetMaxQp();
+      const fvec txErr2     = maxSlopePV * maxSlopePV / fvec(9.);
+      const fvec qpErr2     = maxQp * maxQp / fvec(9.);
+
+      T.ResetErrors(1., 1., txErr2, txErr2, qpErr2, (fStaL->timeInfo ? hitl.dT2() : 1.e6), 1.e10);
+      T.InitVelocityRange(1. / frWData.CurrentIteration()->GetMaxQp());
+
+      T.C00()     = hitl.dX2();
+      T.C10()     = hitl.dXY();
+      T.C11()     = hitl.dY2();
+      T.Ndf()     = (frWData.CurrentIteration()->GetPrimaryFlag()) ? fvec(2.) : fvec(0.);
+      T.NdfTime() = (fStaL->timeInfo ? 0 : -1);
+    }
 
-    // NDF = number of track parameters (6: x, y, tx, ty, qp, time) - number of measured parameters (3: x, y, time) on station or (2: x, y) on target
+    // NDF = number of track parameters (6: x, y, tx, ty, qp, time)
+    //       - number of measured parameters (3: x, y, time) on station or (2: x, y) on target
 
     // field made by  the left hit, the target and the station istac in-between.
     // is used for extrapolation to the target and to the middle hit
@@ -164,66 +156,66 @@ void TripletConstructor::CreateTripletsForHit(int istal, int istam, int istar, c
     }
 
     // add the target constraint
-
-    fFit.FilterWithTargetAtLine(fParameters.GetTargetPositionZ(), frWData.TargetMeasurement(), fld0);
-
-    fFit.MultipleScattering(fParameters.GetMaterialThickness(fIstaL, T.GetX(), T.GetY()));
+    fit.FilterWithTargetAtLine(fParameters.GetTargetPositionZ(), frWData.TargetMeasurement(), fld0);
+    fit.MultipleScattering(fParameters.GetMaterialThickness(fIstaL, T.GetX(), T.GetY()));
 
     // extrapolate to the middle hit
-
-    fFit.ExtrapolateLine(fStaM->fZ, fFldL);
-
-    fTrL = T;
+    fit.ExtrapolateLine(fStaM->fZ, fFldL);
   }
 
-  FindDoublets();
-  FitDoublets();
-  FindTriplets();
-}
-
-
-void TripletConstructor::FindDoublets()
-{
   /// Find the doublets. Reformat data into portions of doublets.
-  int iMC = -1;
-  if (fParameters.DevIsMatchDoubletsViaMc()) {
-    iMC = ca::Framework::GetMcTrackIdForWindowHit(fIhitL);
-    if (iMC < 0) {
+  auto FindDoubletHits = [&]() {
+    const bool matchMc = fParameters.DevIsMatchDoubletsViaMc();
+    const int iMC      = matchMc ? ca::Framework::GetMcTrackIdForWindowHit(fIhitL) : -1;
+    fDoubletData.second.clear();
+    if (iMC < 0 && matchMc) {
       return;
     }
+    CollectHits(fDoubletData.second, fit, fIstaM, frWData.CurrentIteration()->GetDoubletChi2Cut(), iMC,
+                fParameters.GetMaxDoubletsPerSinglet());
+  };
+
+  FindDoubletHits();
+  FindDoublets(fit);
+
+  //D.Smith 28.8.24: Moving this upward (before doublet finding) changes QA output slightly
+  if (fIstaR >= fParameters.GetNstationsActive()) {
+    tripletsOut.clear();
+    return;
   }
 
-  CollectHits(fTrL, fIstaM, frWData.CurrentIteration()->GetDoubletChi2Cut(), iMC, fHitsM_2,
-              fParameters.GetMaxDoubletsPerSinglet());
+  FindTripletHits();
+  FindTriplets();
+  SelectTriplets(tripletsOut);
 }
 
 
-void TripletConstructor::FitDoublets()
+void TripletConstructor::FindDoublets(ca::TrackFit<fvec>& fit)
 {
-
   // ---- Add the middle hits to parameters estimation ----
+  Vector<TrackParamV>& tracks   = fDoubletData.first;
+  Vector<ca::HitIndex_t>& hitsM = fDoubletData.second;
 
-  Vector<ca::HitIndex_t> hitsMtmp("TripletConstructor::hitsMtmp", fHitsM_2);
-
-  fHitsM_2.clear();
-  fTracks_2.clear();
-  fTracks_2.reserve(hitsMtmp.size());
+  tracks.clear();
+  tracks.reserve(hitsM.size());
 
   const bool isMomentumFitted = (fIsTargetField || (fStaL->fieldStatus != 0) || (fStaM->fieldStatus != 0));
 
-  double maxQp = frWData.CurrentIteration()->GetMaxQp();
-  for (unsigned int i2 = 0; i2 < hitsMtmp.size(); i2++) {
+  const TrackParamV Tr = fit.Tr();  // copy contents of fit
 
-    const ca::HitIndex_t indM = hitsMtmp[i2];
+  auto it2 = hitsM.begin();
+  for (auto it = hitsM.begin(); it != hitsM.end(); it++) {
+
+    const ca::HitIndex_t indM = *it;
     const ca::Hit& hitm       = frWData.Hit(indM);
 
     if (frWData.IsHitSuppressed(indM)) {
       continue;
     }
 
-    TrackParamV& T2 = fFit.Tr();
-    T2              = fTrL;
-    fFit.SetQp0(fvec(0.f));
+    TrackParamV& T2 = fit.Tr();
+    T2              = Tr;
+    fit.SetQp0(fvec(0.f));
 
     fvec z_2 = hitm.Z();
     ca::MeasurementXy<fvec> m_2(hitm.X(), hitm.Y(), hitm.dX2(), hitm.dY2(), hitm.dXY(), fvec::One(), fvec::One());
@@ -231,13 +223,12 @@ void TripletConstructor::FitDoublets()
     fvec dt2_2 = hitm.dT2();
 
     // add the middle hit
-
-    fFit.ExtrapolateLineNoField(z_2);
-    fFit.FilterXY(m_2);
-    fFit.FilterTime(t_2, dt2_2, fmask(fStaM->timeInfo));
-    fFit.SetQp0(isMomentumFitted ? fFit.Tr().GetQp() : maxQp);
-    fFit.MultipleScattering(fParameters.GetMaterialThickness(fIstaM, T2.GetX(), T2.Y()));
-    fFit.SetQp0(fFit.Tr().Qp());
+    fit.ExtrapolateLineNoField(z_2);
+    fit.FilterXY(m_2);
+    fit.FilterTime(t_2, dt2_2, fmask(fStaM->timeInfo));
+    fit.SetQp0(isMomentumFitted ? fit.Tr().GetQp() : frWData.CurrentIteration()->GetMaxQp());
+    fit.MultipleScattering(fParameters.GetMaterialThickness(fIstaM, T2.GetX(), T2.Y()));
+    fit.SetQp0(fit.Tr().Qp());
 
     // check if there are other hits close to the doublet on the same station
     if (ca::kMcbm != fTrackingMode) {
@@ -247,9 +238,9 @@ void TripletConstructor::FitDoublets()
       const fscal ty = T2.Ty()[0];
       const fscal tt = T2.Vi()[0] * sqrt(1. + tx * tx + ty * ty);  // dt/dl * dl/dz
 
-      for (unsigned int iClone = i2 + 1; iClone < hitsMtmp.size(); iClone++) {
+      for (auto itClone = it + 1; itClone != hitsM.end(); itClone++) {
 
-        const int indClone      = hitsMtmp[iClone];
+        const int indClone      = *itClone;
         const ca::Hit& hitClone = frWData.Hit(indClone);
 
         const fscal dz = hitClone.Z() - T2.Z()[0];
@@ -283,61 +274,56 @@ void TripletConstructor::FitDoublets()
       }
     }
 
-    fHitsM_2.push_back(indM);
-    fTracks_2.push_back(T2);
-  }  // i2
+    tracks.push_back(T2);
+    *it2 = indM;
+    it2++;
+  }  // it
+  hitsM.erase(it2, hitsM.end());
 }
 
 
-void TripletConstructor::FindTriplets()
+void TripletConstructor::FindTripletHits()
 {
-  if (fIstaR >= fParameters.GetNstationsActive()) {
-    return;
-  }
-  FindRightHit();
-  FitTriplets();
-  StoreTriplets();
-}
-
+  //auto& [tracks_2, hitsM_2] = doublets;   TO DO: Reactivate when MacOS compiler bug is fixed.
+  Vector<TrackParamV>& tracks_2   = fDoubletData.first;
+  Vector<ca::HitIndex_t>& hitsM_2 = fDoubletData.second;
 
-void TripletConstructor::FindRightHit()
-{
+  Vector<ca::HitIndex_t>& hitsM_3 = std::get<1>(fTripletData);
+  Vector<ca::HitIndex_t>& hitsR_3 = std::get<2>(fTripletData);
 
   /// Add the middle hits to parameters estimation. Propagate to right station.
   /// Find the triplets(right hit). Reformat data in the portion of triplets.
-
-  fFit.SetQp0(fvec(0.));
-
-  if (fIstaR >= fParameters.GetNstationsActive()) return;
+  ca::TrackFit<fvec> fit(fmask::One(), true);
+  fit.SetParticleMass(frWData.CurrentIteration()->GetElectronFlag() ? constants::phys::ElectronMass : fDefaultMass);
+  fit.SetQp0(fvec(0.));
 
   {
-    int maxTriplets = fHitsM_2.size() * fParameters.GetMaxTripletPerDoublets();
-    fHitsM_3.clear();
-    fHitsR_3.clear();
-    fHitsM_3.reserve(maxTriplets);
-    fHitsR_3.reserve(maxTriplets);
+    const int maxTriplets = hitsM_2.size() * fParameters.GetMaxTripletPerDoublets();
+    hitsM_3.clear();
+    hitsR_3.clear();
+    hitsM_3.reserve(maxTriplets);
+    hitsR_3.reserve(maxTriplets);
   }
   // ---- Add the middle hits to parameters estimation. Propagate to right station. ----
 
   const double maxSlope       = frWData.CurrentIteration()->GetMaxSlope();
   const double tripletChi2Cut = frWData.CurrentIteration()->GetTripletChi2Cut();
-  for (unsigned int i2 = 0; i2 < fHitsM_2.size(); i2++) {
+  for (unsigned int i2 = 0; i2 < hitsM_2.size(); i2++) {
 
-    fFit.SetTrack(fTracks_2[i2]);
-    TrackParamV& T2 = fFit.Tr();
+    fit.SetTrack(tracks_2[i2]);
+    TrackParamV& T2 = fit.Tr();
 
     // extrapolate to the right hit station
-
-    fFit.Extrapolate(fStaR->fZ, fFldL);
+    fit.Extrapolate(fStaR->fZ, fFldL);
 
     if constexpr (fDebugDublets) {
-      ca::HitIndex_t iwhit[2] = {fIhitL, fHitsM_2[i2]};
+      ca::HitIndex_t iwhit[2] = {fIhitL, hitsM_2[i2]};
       ca::HitIndex_t ihit[2]  = {frWData.Hit(iwhit[0]).Id(), frWData.Hit(iwhit[1]).Id()};
 
       const int ih0     = ihit[0];
       const int ih1     = ihit[1];
-      const ca::Hit& h0 = fInputData.GetHit(ih0);
-      const ca::Hit& h1 = fInputData.GetHit(ih1);
+      const ca::Hit& h0 = frWData.Hit(iwhit[0]);
+      const ca::Hit& h1 = frWData.Hit(iwhit[1]);
 
       LOG(info) << "\n======  Extrapolated Doublet : "
                 << "  iter " << frWData.CurrentIteration()->GetName() << " hits: {" << fIstaL << "/" << ih0 << " "
@@ -364,7 +350,7 @@ void TripletConstructor::FindRightHit()
         return true;
       }
       if (fParameters.DevIsMatchTripletsViaMc()) {
-        int indM = fHitsM_2[i2];
+        int indM = hitsM_2[i2];
         iMC      = ca::Framework::GetMcTrackIdForWindowHit(fIhitL);
         if (iMC < 0 || iMC != ca::Framework::GetMcTrackIdForWindowHit(indM)) {
           return true;
@@ -389,7 +375,7 @@ void TripletConstructor::FindRightHit()
     }
 
     Vector<ca::HitIndex_t> collectedHits;
-    CollectHits(T2, fIstaR, tripletChi2Cut, iMC, collectedHits, fParameters.GetMaxTripletPerDoublets());
+    CollectHits(collectedHits, fit, fIstaR, tripletChi2Cut, iMC, fParameters.GetMaxTripletPerDoublets());
 
     if (collectedHits.size() >= fParameters.GetMaxTripletPerDoublets()) {
       // FU, 28.08.2024, Comment the following log lines since it spams the output
@@ -404,8 +390,8 @@ void TripletConstructor::FindRightHit()
     }
     for (ca::HitIndex_t& irh : collectedHits) {
       if constexpr (fDebugDublets) {
-        ca::HitIndex_t ihit = frWData.Hit(irh).Id();
-        const ca::Hit& h    = fInputData.GetHit(ihit);
+        const ca::Hit& h    = frWData.Hit(irh);
+        ca::HitIndex_t ihit = h.Id();
         LOG(info) << "  hit " << ihit << " " << h.ToString();
       }
       if (frWData.IsHitSuppressed(irh)) {
@@ -415,8 +401,8 @@ void TripletConstructor::FindRightHit()
         continue;
       }
       // pack the triplet
-      fHitsM_3.push_back(fHitsM_2[i2]);
-      fHitsR_3.push_back(irh);
+      hitsM_3.push_back(hitsM_2[i2]);
+      hitsR_3.push_back(irh);
     }  // search area
     if constexpr (fDebugDublets) {
       LOG(info) << "======== end of extrapolated doublet ==== \n";
@@ -424,16 +410,17 @@ void TripletConstructor::FindRightHit()
   }  // i2
 }
 
-
-void TripletConstructor::FitTriplets()
+void TripletConstructor::FindTriplets()
 {
   constexpr int nIterations = 2;
 
-  const Tindex n3 = fHitsM_3.size();
-  assert(fHitsM_3.size() == fHitsR_3.size());
+  Vector<TrackParamV>& tracks   = std::get<0>(fTripletData);
+  Vector<ca::HitIndex_t>& hitsM = std::get<1>(fTripletData);
+  Vector<ca::HitIndex_t>& hitsR = std::get<2>(fTripletData);
+  assert(hitsM.size() == hitsR.size());
 
-  fTracks_3.clear();
-  fTracks_3.reset(n3, TrackParamV());
+  tracks.clear();
+  tracks.reserve(hitsM.size());
 
   /// Refit Triplets
   if constexpr (fDebugTriplets) {
@@ -443,13 +430,7 @@ void TripletConstructor::FitTriplets()
 
   ca::TrackFit<fvec> fit;
   fit.SetMask(fmask::One());
-
-  if (frWData.CurrentIteration()->GetElectronFlag()) {
-    fit.SetParticleMass(constants::phys::ElectronMass);
-  }
-  else {
-    fit.SetParticleMass(fDefaultMass);
-  }
+  fit.SetParticleMass(frWData.CurrentIteration()->GetElectronFlag() ? constants::phys::ElectronMass : fDefaultMass);
 
   // prepare data
   const int NHits       = 3;  // triplets
@@ -461,12 +442,10 @@ void TripletConstructor::FitTriplets()
   const bool isMomentumFitted = ((fStaL->fieldStatus != 0) || (fStaM->fieldStatus != 0) || (fStaR->fieldStatus != 0));
   const fvec ndfTrackModel    = 4 + (isMomentumFitted ? 1 : 0);  // straight line or track with momentum
 
-  const fvec maxQp = frWData.CurrentIteration()->GetMaxQp();
-
-  for (int i3 = 0; i3 < n3; ++i3) {
+  for (size_t i3 = 0; i3 < hitsM.size(); ++i3) {
 
     // prepare data
-    const ca::HitIndex_t iwhit[NHits] = {fIhitL, fHitsM_3[i3], fHitsR_3[i3]};
+    const ca::HitIndex_t iwhit[NHits] = {fIhitL, hitsM[i3], hitsR[i3]};
 
     const ca::HitIndex_t ihit[NHits] = {frWData.Hit(iwhit[0]).Id(), frWData.Hit(iwhit[1]).Id(),
                                         frWData.Hit(iwhit[2]).Id()};
@@ -476,6 +455,9 @@ void TripletConstructor::FitTriplets()
       int mc2 = ca::Framework::GetMcTrackIdForCaHit(ihit[1]);
       int mc3 = ca::Framework::GetMcTrackIdForCaHit(ihit[2]);
       if ((mc1 != mc2) || (mc1 != mc3)) {
+        // D.S.: Added to preserve the ordering when switching from index-based
+        // access to push_back(). Discuss with SZ.
+        tracks.push_back(TrackParamV());
         continue;
       }
     }
@@ -485,7 +467,7 @@ void TripletConstructor::FitTriplets()
     ca::MeasurementXy<fvec> mxy[NHits];
 
     for (int ih = 0; ih < NHits; ++ih) {
-      const ca::Hit& hit = fInputData.GetHit(ihit[ih]);
+      const ca::Hit& hit = frWData.Hit(iwhit[ih]);
       mxy[ih] = ca::MeasurementXy<fvec>(hit.X(), hit.Y(), hit.dX2(), hit.dY2(), hit.dXY(), fvec::One(), fvec::One());
 
       x[ih]   = hit.X();
@@ -503,6 +485,7 @@ void TripletConstructor::FitTriplets()
 
     fvec tx[3] = {(x[1] - x[0]) / (z[1] - z[0]), (x[2] - x[0]) / (z[2] - z[0]), (x[2] - x[1]) / (z[2] - z[1])};
     fvec ty[3] = {(y[1] - y[0]) / (z[1] - z[0]), (y[2] - y[0]) / (z[2] - z[0]), (y[2] - y[1]) / (z[2] - z[1])};
+
     for (int ih = 0; ih < NHits; ++ih) {
       fvec dz = (sta[ih].fZ - z[ih]);
       B[ih]   = sta[ih].fieldSlice.GetFieldValue(x[ih] + tx[ih] * dz, y[ih] + ty[ih] * dz);
@@ -526,6 +509,7 @@ void TripletConstructor::FitTriplets()
     for (int iiter = 0; iiter < nIterations; ++iiter) {
 
       auto fitTrack = [&](int startIdx, int endIdx, int step, FitDirection direction) {
+        const fvec maxQp = frWData.CurrentIteration()->GetMaxQp();
         fit.SetQp0(T.Qp());
         fit.Qp0()(fit.Qp0() > maxQp)  = maxQp;
         fit.Qp0()(fit.Qp0() < -maxQp) = -maxQp;
@@ -569,7 +553,7 @@ void TripletConstructor::FitTriplets()
       fitTrack(NHits - 1, -1, -1, FitDirection::kUpstream);
     }  // for iiter
 
-    fTracks_3[i3] = T;
+    tracks.push_back(T);
 
     if constexpr (fDebugTriplets) {
       int ih0 = ihit[0];
@@ -580,9 +564,9 @@ void TripletConstructor::FitTriplets()
       int mc3 = ca::Framework::GetMcTrackIdForCaHit(ih2);
 
       if (1 || (mc1 >= 0) && (mc1 == mc2) && (mc1 == mc3)) {
-        const ca::Hit& h0 = fInputData.GetHit(ih0);
-        const ca::Hit& h1 = fInputData.GetHit(ih1);
-        const ca::Hit& h2 = fInputData.GetHit(ih2);
+        const ca::Hit& h0 = frWData.Hit(iwhit[0]);
+        const ca::Hit& h1 = frWData.Hit(iwhit[1]);
+        const ca::Hit& h2 = frWData.Hit(iwhit[2]);
         //const CbmL1MCTrack& mctr = CbmL1::Instance()->GetMcTracks()[mc1];
         LOG(info) << "== fitted triplet: "
                   << " iter " << frWData.CurrentIteration()->GetName() << " hits: {" << fIstaL << "/" << ih0 << " "
@@ -599,41 +583,41 @@ void TripletConstructor::FitTriplets()
       }
     }
   }  //i3
-
-}  // FitTriplets
+}  // FindTriplets
 
 
-void TripletConstructor::StoreTriplets()
+void TripletConstructor::SelectTriplets(Vector<ca::Triplet>& tripletsOut)
 {
-  /// Selects good triplets and saves them into fTriplets.
+  /// Selects good triplets and saves them into tripletsOut.
   /// Finds neighbouring triplets at the next station.
 
-  const Tindex n3 = fHitsM_3.size();
+  Vector<TrackParamV>& tracks   = std::get<0>(fTripletData);
+  Vector<ca::HitIndex_t>& hitsM = std::get<1>(fTripletData);
+  Vector<ca::HitIndex_t>& hitsR = std::get<2>(fTripletData);
 
   bool isMomentumFitted = ((fStaL->fieldStatus != 0) || (fStaM->fieldStatus != 0) || (fStaR->fieldStatus != 0));
 
-  fTriplets.clear();
-  fTriplets.reserve(n3);
+  tripletsOut.clear();
+  tripletsOut.reserve(hitsM.size());
 
-  const fscal tripletFinalChi2Cut = frWData.CurrentIteration()->GetTripletFinalChi2Cut();
-  for (Tindex i3 = 0; i3 < n3; ++i3) {
+  for (size_t i3 = 0; i3 < hitsM.size(); ++i3) {
 
-    TrackParamV& T3 = fTracks_3[i3];
+    TrackParamV& T3 = tracks[i3];
 
     // TODO: SG: normalize chi2, separate cuts on time and space
 
     const fscal chi2 = T3.GetChiSq()[0] + T3.GetChiSqTime()[0];
 
     const ca::HitIndex_t ihitl = fIhitL;
-    const ca::HitIndex_t ihitm = fHitsM_3[i3];
-    const ca::HitIndex_t ihitr = fHitsR_3[i3];
+    const ca::HitIndex_t ihitm = hitsM[i3];
+    const ca::HitIndex_t ihitr = hitsR[i3];
 
     CBMCA_DEBUG_ASSERT(ihitl < frWData.HitStartIndexOnStation(fIstaL + 1));
     CBMCA_DEBUG_ASSERT(ihitm < frWData.HitStartIndexOnStation(fIstaM + 1));
     CBMCA_DEBUG_ASSERT(ihitr < frWData.HitStartIndexOnStation(fIstaR + 1));
 
     if (!frWData.CurrentIteration()->GetTrackFromTripletsFlag()) {
-      if (chi2 > tripletFinalChi2Cut) {
+      if (chi2 > frWData.CurrentIteration()->GetTripletFinalChi2Cut()) {
         continue;
       }
     }
@@ -651,24 +635,22 @@ void TripletConstructor::StoreTriplets()
     // removing it leads to a higher ghost ratio
     Cqp += 0.001;
 
-    fTriplets.emplace_back(ihitl, ihitm, ihitr, fIstaL, fIstaM, fIstaR, 0, 0, 0, chi2, qp, Cqp, T3.Tx()[0], T3.C22()[0],
-                           T3.Ty()[0], T3.C33()[0], isMomentumFitted);
+    tripletsOut.emplace_back(ihitl, ihitm, ihitr, fIstaL, fIstaM, fIstaR, 0, 0, 0, chi2, qp, Cqp, T3.Tx()[0],
+                             T3.C22()[0], T3.Ty()[0], T3.C33()[0], isMomentumFitted);
   }
 }
 
 
-void TripletConstructor::CollectHits(const TrackParamV& Tr, const int iSta, const double chi2Cut, const int iMC,
-                                     Vector<ca::HitIndex_t>& collectedHits, int maxNhits)
+void TripletConstructor::CollectHits(Vector<ca::HitIndex_t>& collectedHits, ca::TrackFit<fvec>& fit, const int iSta,
+                                     const double chi2Cut, const int iMC, const int maxNhits)
 {
   /// Collect hits on a station
-
   collectedHits.clear();
   collectedHits.reserve(maxNhits);
 
   const ca::Station<fvec>& sta = fParameters.GetStation(iSta);
 
-  fFit.SetTrack(Tr);
-  TrackParamV& T = fFit.Tr();
+  TrackParamV& T = fit.Tr();
   //LOG(info) << T.chi2[0] ;
   T.ChiSq() = 0.;
 
@@ -730,7 +712,7 @@ void TripletConstructor::CollectHits(const TrackParamV& Tr, const int iSta, cons
     const fscal z = hit.Z();
 
     // check y-boundaries
-    const auto [y, C11] = fFit.ExtrapolateLineYdY2(z);
+    const auto [y, C11] = fit.ExtrapolateLineYdY2(z);
     const fscal dy_est  = sqrt(Pick_m22[0] * C11[0]) + hit.RangeY();
     const fscal dY      = hit.Y() - y[0];
 
@@ -742,7 +724,7 @@ void TripletConstructor::CollectHits(const TrackParamV& Tr, const int iSta, cons
     }
 
     // check x-boundaries
-    const auto [x, C00] = fFit.ExtrapolateLineXdX2(z);
+    const auto [x, C00] = fit.ExtrapolateLineXdX2(z);
     const fscal dx_est  = sqrt(Pick_m22[0] * C00[0]) + hit.RangeX();
     const fscal dX      = hit.X() - x[0];
 
@@ -756,7 +738,7 @@ void TripletConstructor::CollectHits(const TrackParamV& Tr, const int iSta, cons
     // check chi2
     ca::MeasurementXy<fvec> mxy(hit.X(), hit.Y(), hit.dX2(), hit.dY2(), hit.dXY(), fvec::One(), fvec::One());
 
-    const fvec C10            = fFit.ExtrapolateLineDxy(z);
+    const fvec C10            = fit.ExtrapolateLineDxy(z);
     const auto [chi2x, chi2u] = ca::TrackFit<fvec>::GetChi2XChi2U(mxy, x, y, C00, C10, C11);
 
     // TODO: adjust the cut, cut on chi2x & chi2u separately
diff --git a/algo/ca/core/tracking/CaTripletConstructor.h b/algo/ca/core/tracking/CaTripletConstructor.h
index af9bed635c144b58073a61ee1204f2eb0cd8237f..6282b0da73cec42920508f154000259ab532c2b0 100644
--- a/algo/ca/core/tracking/CaTripletConstructor.h
+++ b/algo/ca/core/tracking/CaTripletConstructor.h
@@ -30,8 +30,8 @@ namespace cbm::algo::ca
 
     /// Constructor
     /// \param nThreads  Number of threads for multi-threaded mode
-    TripletConstructor(const ca::Parameters<fvec>& pars, const ca::InputData& input, WindowData& wData,
-                       const fscal mass, const ca::TrackingMode& mode);
+    TripletConstructor(const ca::Parameters<fvec>& pars, WindowData& wData, const fscal mass,
+                       const ca::TrackingMode& mode);
 
     /// Copy constructor
     TripletConstructor(const TripletConstructor&) = delete;
@@ -49,37 +49,32 @@ namespace cbm::algo::ca
     ~TripletConstructor() = default;
 
     ///                             ------  FUNCTIONAL PART ------
-
-    void CreateTripletsForHit(int istal, int istam, int istar, ca::HitIndex_t ihl);
-
-    const Vector<ca::Triplet>& GetTriplets() const { return fTriplets; }
+    void CreateTripletsForHit(Vector<ca::Triplet>& tripletsOut, int istal, int istam, int istar, ca::HitIndex_t ihl);
 
    private:
+    typedef std::pair<Vector<TrackParamV>, Vector<ca::HitIndex_t>> Doublet_t;
+    typedef std::tuple<Vector<TrackParamV>, Vector<ca::HitIndex_t>, Vector<ca::HitIndex_t>> Triplet_t;
+
     bool InitStations(int istal, int istam, int istar);
 
     /// Find the doublets. Reformat data in the portion of doublets.
-    void FindDoublets();
-    void FitDoublets();
-
-    /// Find triplets on station
-    void FindTriplets();
+    void FindDoublets(ca::TrackFit<fvec>& fit);
 
     /// Add the middle hits to parameters estimation. Propagate to right station.
     /// Find the triplets (right hit). Reformat data in the portion of triplets.
-    void FindRightHit();
+    void FindTripletHits();
 
-    /// Fit Triplets
-    void FitTriplets();
+    /// Find triplets on station
+    void FindTriplets();
 
-    /// Select triplets. Save them into vTriplets.
-    void StoreTriplets();
+    /// Select good triplets.
+    void SelectTriplets(Vector<ca::Triplet>& tripletsOut);
 
-    void CollectHits(const TrackParamV& Tr, const int iSta, const double chi2Cut, const int iMC,
-                     Vector<ca::HitIndex_t>& collectedHits, int maxNhits);
+    void CollectHits(Vector<ca::HitIndex_t>& collectedHits, ca::TrackFit<fvec>& fit, const int iSta,
+                     const double chi2Cut, const int iMC, const int maxNhits);
 
    private:
     const Parameters<fvec>& fParameters;  ///< Object of Framework parameters class
-    const InputData& fInputData;          ///< Tracking input data
     WindowData& frWData;
     fscal fDefaultMass{constants::phys::MuonMass};  ///< mass of the propagated particle [GeV/c2]
     ca::TrackingMode fTrackingMode;
@@ -103,19 +98,13 @@ namespace cbm::algo::ca
       fFld1Sta[3];  // three stations for approximating the field between the left and the right hit
 
     ca::HitIndex_t fIhitL;  ///< index of the left hit in fAlgo->fWindowHits
-    TrackParamV fTrL;
     ca::FieldRegion<fvec> fFldL;
 
-    Vector<ca::HitIndex_t> fHitsM_2{"TripletConstructor::fHitsM_2"};
-    Vector<TrackParamV> fTracks_2{"TripletConstructor::fTracks_2"};
-
-    Vector<TrackParamV> fTracks_3{"TripletConstructor::fTracks_3"};
-    Vector<ca::HitIndex_t> fHitsM_3{"TripletConstructor::fHitsM_3"};
-    Vector<ca::HitIndex_t> fHitsR_3{"TripletConstructor::fHitsR_3"};
-
-    Vector<ca::Triplet> fTriplets{"TripletConstructor::fTriplets"};
+    // Persistent storage for triplet tracks and hits
+    Triplet_t fTripletData;
 
-    ca::TrackFit<fvec> fFit;
+    // Persistent storage for doublet tracks and hits
+    Doublet_t fDoubletData;
 
    private:
     static constexpr bool fDebugDublets     = false;  // print debug info for dublets
diff --git a/algo/ca/core/utils/CaVector.h b/algo/ca/core/utils/CaVector.h
index 16fee44c40f0ccb15a052db5a5a6bbd557b99b82..aabed81edf283f68c2e159fca1f97827c7d01c2d 100644
--- a/algo/ca/core/utils/CaVector.h
+++ b/algo/ca/core/utils/CaVector.h
@@ -265,6 +265,7 @@ namespace cbm::algo::ca
     using Tbase::cend;
     using Tbase::clear;
     using Tbase::end;
+    using Tbase::erase;
     using Tbase::insert;  //TODO:: make it private
     using Tbase::pop_back;
     using Tbase::rbegin;