From 86ff13083644294f29aa10c7a0e2604ed4d4d1b8 Mon Sep 17 00:00:00 2001
From: "s.zharko@gsi.de" <s.zharko@gsi.de>
Date: Tue, 21 Jan 2025 10:26:31 +0100
Subject: [PATCH] Introduction of mCBM V0 finder task

---
 algo/ca/core/tracking/CaTrackFinderWindow.cxx |   4 +-
 core/qa/CbmQaIO.cxx                           |   2 +-
 macro/beamtime/mcbm2022/mcbm_event_reco_L1.C  |  41 +-
 macro/beamtime/mcbm2024/reco_mcbm.sh          |   9 +-
 macro/mcbm/CMakeLists.txt                     |   2 +-
 macro/mcbm/mcbm_hadron_kfp_ana.C              | 281 +++++++
 macro/run/run_qa.C                            |   7 +-
 reco/KF/CbmKFParticleFinder.cxx               |   2 +-
 reco/KF/CbmKFParticleFinder.h                 |   3 +-
 reco/KF/CbmKFV0FinderQa.cxx                   |  61 ++
 reco/KF/CbmKFV0FinderQa.h                     |  66 ++
 reco/KF/CbmKFV0FinderTask.cxx                 | 774 ++++++++++++++++++
 reco/KF/CbmKFV0FinderTask.h                   | 461 +++++++++++
 reco/KF/KFParticleInterface.cmake             |   2 +
 reco/KF/KFParticleInterfaceLinkDef.h          |   2 +
 reco/L1/CbmCaTimeSliceReader.cxx              |   1 +
 .../digis/CbmAlgoBuildRawEvents.cxx           |   3 +-
 reco/tasks/CbmTaskInspectDigiTimeslice.cxx    |   2 +
 18 files changed, 1697 insertions(+), 26 deletions(-)
 create mode 100644 macro/mcbm/mcbm_hadron_kfp_ana.C
 create mode 100644 reco/KF/CbmKFV0FinderQa.cxx
 create mode 100644 reco/KF/CbmKFV0FinderQa.h
 create mode 100644 reco/KF/CbmKFV0FinderTask.cxx
 create mode 100644 reco/KF/CbmKFV0FinderTask.h

diff --git a/algo/ca/core/tracking/CaTrackFinderWindow.cxx b/algo/ca/core/tracking/CaTrackFinderWindow.cxx
index 1dd20f4040..cce227803a 100644
--- a/algo/ca/core/tracking/CaTrackFinderWindow.cxx
+++ b/algo/ca/core/tracking/CaTrackFinderWindow.cxx
@@ -498,7 +498,9 @@ namespace cbm::algo::ca
             if (wData.CurrentIteration()->GetPrimaryFlag() && (best_tr.Chi2() > 5.0)) continue;
           }
         }
-        fvTrackCandidates.push_back(best_tr);
+        // NOTE: Temporarely disable the warnings, one has to provide more specific coefficient in the capacity
+        //                   reservation
+        fvTrackCandidates.push_back_no_warning(best_tr);
         ca::Branch& tr = fvTrackCandidates.back();
         tr.SetStation(istaF);
         tr.SetId(fvTrackCandidates.size() - 1);
diff --git a/core/qa/CbmQaIO.cxx b/core/qa/CbmQaIO.cxx
index 426e3e57f3..926cfb4bfa 100644
--- a/core/qa/CbmQaIO.cxx
+++ b/core/qa/CbmQaIO.cxx
@@ -36,7 +36,7 @@ CbmQaIO::~CbmQaIO() {}
 void CbmQaIO::SetTH1Properties(TH1* pHist) const
 {
   // Set default histo properties
-  pHist->GetYaxis()->SetLabelOffset(0.95);
+  //pHist->GetYaxis()->SetLabelOffset(0.95);
 
   TPaveStats* stats = cbm::qa::util::GetHistStats(pHist);
   assert(stats);
diff --git a/macro/beamtime/mcbm2022/mcbm_event_reco_L1.C b/macro/beamtime/mcbm2022/mcbm_event_reco_L1.C
index b9bd63107f..35ba48cbc8 100644
--- a/macro/beamtime/mcbm2022/mcbm_event_reco_L1.C
+++ b/macro/beamtime/mcbm2022/mcbm_event_reco_L1.C
@@ -36,13 +36,11 @@ Bool_t mcbm_event_reco_L1(UInt_t uRunId                   = 2570,
                           Bool_t bFSD                     = kFALSE,
                           TString sInpFile                = ""
 			  )
+/* clang-format on */
 {
-  /// FIXME: Re-enable clang formatting after parameters initial values setting
-  /* clang-format on */
-
   // --- Logger settings ----------------------------------------------------
   TString logLevel     = "INFO";     //"INFO";
-  TString logVerbosity = "VERYLOW";  //"VERYLOW";
+  TString logVerbosity = "LOW";      //"VERYLOW";
   // ------------------------------------------------------------------------
 
   // -----   Environment   --------------------------------------------------
@@ -421,17 +419,28 @@ Bool_t mcbm_event_reco_L1(UInt_t uRunId                   = 2570,
     Int_t stsAddress11 = CbmStsAddress::GetAddress(1, 2, 1, 2, 0, 0);  // U1 L2 M2  6 cm
 
 
-    std::cout << "STS address01 " << std::dec << stsAddress01 << " " << std::hex << stsAddress01 << std::endl;
-    std::cout << "STS address02 " << std::dec << stsAddress02 << " " << std::hex << stsAddress02 << std::endl;
-    std::cout << "STS address03 " << std::dec << stsAddress03 << " " << std::hex << stsAddress03 << std::endl;
-    std::cout << "STS address04 " << std::dec << stsAddress04 << " " << std::hex << stsAddress04 << std::endl;
-    std::cout << "STS address05 " << std::dec << stsAddress05 << " " << std::hex << stsAddress05 << std::endl;
-    std::cout << "STS address06 " << std::dec << stsAddress06 << " " << std::hex << stsAddress06 << std::endl;
-    std::cout << "STS address07 " << std::dec << stsAddress07 << " " << std::hex << stsAddress07 << std::endl;
-    std::cout << "STS address08 " << std::dec << stsAddress08 << " " << std::hex << stsAddress08 << std::endl;
-    std::cout << "STS address09 " << std::dec << stsAddress09 << " " << std::hex << stsAddress09 << std::endl;
-    std::cout << "STS address10 " << std::dec << stsAddress10 << " " << std::hex << stsAddress10 << std::endl;
-    std::cout << "STS address11 " << std::dec << stsAddress11 << " " << std::hex << stsAddress11 << std::endl;
+    std::cout << "STS address01 " << std::dec << stsAddress01 << " " << std::hex << stsAddress01 << " "
+              << CbmStsAddress::ToString(stsAddress01) << std::endl;
+    std::cout << "STS address02 " << std::dec << stsAddress02 << " " << std::hex << stsAddress02 << " "
+              << CbmStsAddress::ToString(stsAddress02) << std::endl;
+    std::cout << "STS address03 " << std::dec << stsAddress03 << " " << std::hex << stsAddress03 << " "
+              << CbmStsAddress::ToString(stsAddress03) << std::endl;
+    std::cout << "STS address04 " << std::dec << stsAddress04 << " " << std::hex << stsAddress04 << " "
+              << CbmStsAddress::ToString(stsAddress04) << std::endl;
+    std::cout << "STS address05 " << std::dec << stsAddress05 << " " << std::hex << stsAddress05 << " "
+              << CbmStsAddress::ToString(stsAddress05) << std::endl;
+    std::cout << "STS address06 " << std::dec << stsAddress06 << " " << std::hex << stsAddress06 << " "
+              << CbmStsAddress::ToString(stsAddress06) << std::endl;
+    std::cout << "STS address07 " << std::dec << stsAddress07 << " " << std::hex << stsAddress07 << " "
+              << CbmStsAddress::ToString(stsAddress07) << std::endl;
+    std::cout << "STS address08 " << std::dec << stsAddress08 << " " << std::hex << stsAddress08 << " "
+              << CbmStsAddress::ToString(stsAddress08) << std::endl;
+    std::cout << "STS address09 " << std::dec << stsAddress09 << " " << std::hex << stsAddress09 << " "
+              << CbmStsAddress::ToString(stsAddress09) << std::endl;
+    std::cout << "STS address10 " << std::dec << stsAddress10 << " " << std::hex << stsAddress10 << " "
+              << CbmStsAddress::ToString(stsAddress10) << std::endl;
+    std::cout << "STS address11 " << std::dec << stsAddress11 << " " << std::hex << stsAddress11 << " "
+              << CbmStsAddress::ToString(stsAddress11) << std::endl;
 
     // --- Now we can define the sensor parameter set and tell recoSts to use it
     auto sensorParSet = new CbmStsParSetSensor("CbmStsParSetSensor", "STS sensor parameters"
@@ -712,7 +721,7 @@ Bool_t mcbm_event_reco_L1(UInt_t uRunId                   = 2570,
 
 
   // -----   Function needed for CTest runtime dependency   -----------------
-  //  RemoveGeoManager();
+  RemoveGeoManager();
   // ------------------------------------------------------------------------
 
   /// --- Screen output for automatic tests
diff --git a/macro/beamtime/mcbm2024/reco_mcbm.sh b/macro/beamtime/mcbm2024/reco_mcbm.sh
index 8f09705466..51c9e57176 100755
--- a/macro/beamtime/mcbm2024/reco_mcbm.sh
+++ b/macro/beamtime/mcbm2024/reco_mcbm.sh
@@ -36,6 +36,7 @@
 #   Auxiliary options:
 #     -n [--nts] <N_TS>  Number of timeslices to procede
 #     --setup, --unpack, --reco, --qa, --qa-module
+#     --data-dir             Path to output directory
 #     --param-online <path>  Path to the online parameters
 #                             NOTE: should be ${VMCWORKDIR}/parameters/online, but local user may have copy for testing
 #
@@ -143,6 +144,9 @@ while [[ $# -gt 0 ]]; do
     -n | --nts )
       N_TS=${2}
       ;;
+    --data-dir )
+      DATA_TOP_DIR=${2}
+      ;;
   esac
   shift
 done
@@ -272,7 +276,10 @@ if [[ ${DO_RECO} -eq 1 ]]; then
   DATA_PREF="${DATA_TOP_DIR}"
   PARS="${RUN},${N_TS},\"${DATA_PREF}\",\"${DATA_PREF}\",${UNP_FILE_ID},${RECO_MVD},${RECO_STS},${RECO_TRD}"
   PARS="${PARS},${RECO_TRD2d},${RECO_RICH},${RECO_MUCH},${RECO_TOF},${RECO_TOFtr},${RECO_PSD},${RECO_ALI},${RECO_EvB}"
-  PARS="${PARS},${RECO_CA},${RECO_QA},${RECO_FSD},\"${INP_FILE}\",${RECO_PV}"
+  PARS="${PARS},${RECO_CA},${RECO_QA},${RECO_FSD},\"${INP_FILE}\""
+  if [[ ${MACRO_RECO} == "*.mcbm2024*" ]]; then
+    PARS="${PARS},${RECO_PV}"
+  fi
   root -b -l -q ${MACRO_RECO}"(${PARS})" &> ${RECO_LOG}
   cat ${RECO_LOG}
 
diff --git a/macro/mcbm/CMakeLists.txt b/macro/mcbm/CMakeLists.txt
index 8056b9eeed..7313634988 100644
--- a/macro/mcbm/CMakeLists.txt
+++ b/macro/mcbm/CMakeLists.txt
@@ -138,7 +138,7 @@ ForEach(setup IN LISTS cbm_setup)
 EndForEach(setup IN LISTS cbm_setup)
 
 Install(FILES .rootrc mcbm_transport.C mcbm_digi.C mcbm_reco_event.C mcbm_match_check.C mcbm_hadron_analysis.C mcbm_qa.C
-        mcbm_transport_boxgen.C
+        mcbm_transport_boxgen.C mcbm_hadron_kfp_ana.C
         DESTINATION share/cbmroot/macro/mcbm
        )
 Install(DIRECTORY modules DESTINATION share/cbmroot/macro/mcbm)
diff --git a/macro/mcbm/mcbm_hadron_kfp_ana.C b/macro/mcbm/mcbm_hadron_kfp_ana.C
new file mode 100644
index 0000000000..b48dd99436
--- /dev/null
+++ b/macro/mcbm/mcbm_hadron_kfp_ana.C
@@ -0,0 +1,281 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   mcbm_hadron_kfp_ana.C
+/// \brief  Macro for hadron analysis with the KfParticleFinder in mCBM
+/// \since  08.01.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+
+/// \brief  Main function of the macro
+/// \param  uRunId          Run identifier
+/// \param  firstTimeslice  First timeslice
+/// \param  nTimeslices     Number of timeslices to proceed
+/// \param  sRecoFile       Input with reconstructed data tree
+/// \param  sCollTraFile    Input with transported collision MC data tree [optional]
+/// \param  sSignTraFile    Input with transported signal MC data tree [optional]
+/// \param  sBeamTraFile    Input with transported beam MC data tree [optional]
+/// \param  sSinkFile       Output file
+/// \param  sGeoFile        Geometry file
+/// \param  sParInFile      Input parameters file
+/// \param  bUseAlignment   Flag: use alignment
+/// \param  bMixEvent       Flag: perform mixed-event analysis
+/// \param  bUseMc          Flag: use MC-information (if available)
+///
+/// \note File extention conventions used:
+///        1. Input:
+///         - geometry:         <geoSetupTag>.geo.root
+///         - reco:             <dataLabel>.rec.root
+///         - transport:
+///             - collisions:   <genLabel>.tra.root
+///             - signal:       <genLabel>.tra.root
+///             - beam:         <genLabel>.tra.root
+///        2. Output:
+///         - sink:             <dataLabel>.kfp.ana.root
+///         - monitor:          <dataLabel>.kfp.mon.root
+///         - parameters:       <dataLabel>.kfp.par.root
+///
+///        <geoSetupTag> -- geometry setup tag provided by cbm::mcbm::GetSetupFromRunId(uRunId)
+///        <dataLabel>   -- label of data files reflecting the name prefix of the original TSA file
+///                           E.g.:  <dataLabel> = 2391_node8_0_0055
+///        <genLabel>    -- label of the simulated files reflecting the name prefix of the original generated file:
+///                           E.g.:  <genLabel>  = urqmd.auau.1.24gev.mbias.00109
+///
+///  TODO: Probably, use a combined label for output: <dataLabel>.<genLabel>.kfp.ana.root
+///
+/* clang-format off */
+Bool_t mcbm_hadron_kfp_ana(UInt_t uRunId         = 2391,
+                           Int_t firstTimeslice  = 0,
+                           Int_t nTimeslices     = 10,
+                           TString sRecoFile     = "./data/2391_first20Ts.rec.root",
+                           TString sCollTraFile  = "",
+                           TString sSignTraFile  = "",
+                           TString sBeamTraFile  = "",
+                           TString sSinkFile     = "./data/2391_first20Ts.kfp.ana.root",
+                           TString sGeoFile      = "./data/mcbm_beam_2022_05_23_nickel.geo.root",
+                           TString sParInFile    = "./data/2391_first20Ts.par.root",
+                           Bool_t bUseAlignment  = true,
+                           Bool_t bMixEvent      = false,
+                           Bool_t bUseMc         = false
+)
+/* clang-format on */
+{
+  // -- Logger settings ------------------------------------------------------------------------------------------------
+  const TString logLevel{"INFO"};               // Logger level
+  const TString logVerbosity{"LOW"};            // Logger verbosity
+  const bool logColored{true};                  // Colored logger
+  const TString myName{"mcbm_hadron_kfp_ana"};  // Macro name
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Environment settings -------------------------------------------------------------------------------------------
+  TString sOutDir{};
+  if (auto lastSlashPos = sSinkFile.Last('/'); lastSlashPos != -1) {
+    sOutDir = sSinkFile(0, lastSlashPos);
+  }
+  else {
+    sOutDir = "./";
+  }
+  const TString srcDir{gSystem->Getenv("VMCWORKDIR")};  // Top source directory
+  gSystem->Exec("mkdir " + sOutDir);                    // Output directory
+  //gSystem->Exec("cp $VMCWORKDIR/macro/run/.rootrc .");  // Loading environment
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Auxilary common variables --------------------------------------------------------------------------------------
+  const TString sRunId{TString::Format("%04u", uRunId)};  // String representation of the run id
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Geometry setup tag ---------------------------------------------------------------------------------------------
+  cbm::mcbm::ToForceLibLoad dummy;  /// Needed to trigger loading of the library as no fct dict in ROOT6 and CLING
+  TString geoSetupTag{""};
+  try {
+    geoSetupTag = cbm::mcbm::GetSetupFromRunId(uRunId);
+  }
+  catch (const std::invalid_argument& err) {
+    std::cout << "-E- " << myName << ": mapping from runID to geometry setup tag failed: " << err.what() << '\n';
+    return false;
+  }
+  auto* geoSetup = CbmSetup::Instance();
+  geoSetup->LoadSetup(geoSetupTag);
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Start timer ----------------------------------------------------------------------------------------------------
+  TStopwatch timer;
+  timer.Start();
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- FairRun initialisation -----------------------------------------------------------------------------------------
+  FairRunAna* run = new FairRunAna();
+  run->SetGeomFile(sGeoFile);
+  FairFileSource* inputSource = new FairFileSource(sRecoFile);
+  if (bUseMc) {
+    int nTraFiles{0};
+    if (!sCollTraFile.IsNull()) {
+      inputSource->AddFriend(sCollTraFile);
+      nTraFiles++;
+    }
+    if (!sSignTraFile.IsNull()) {
+      inputSource->AddFriend(sSignTraFile);
+      nTraFiles++;
+    }
+    if (!sBeamTraFile.IsNull()) {
+      inputSource->AddFriend(sBeamTraFile);
+      nTraFiles++;
+    }
+    if (nTraFiles == 0) {
+      std::cout << "-E- " << myName << ": MC information was required, but no transport files were provided";
+      return false;
+    }
+  }
+  run->SetSource(inputSource);
+
+  FairRootFileSink* outputSink = new FairRootFileSink(sSinkFile);
+  run->SetSink(outputSink);
+
+  TString sMonitorFile{sSinkFile};
+  sMonitorFile.ReplaceAll(".ana.", ".mon.");
+  FairMonitor::GetMonitor()->EnableMonitor(true, sMonitorFile);
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Logger settings ------------------------------------------------------------------------------------------------
+  FairLogger::GetLogger()->SetLogScreenLevel(logLevel.Data());
+  FairLogger::GetLogger()->SetLogVerbosityLevel(logVerbosity.Data());
+  FairLogger::GetLogger()->SetColoredLog(logColored);
+  //FairLogger::GetLogger()->SetLogScreenLevel("DEBUG");
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Alignment corrections ------------------------------------------------------------------------------------------
+  if (bUseAlignment) {
+    std::map<std::string, TGeoHMatrix>* matrices{nullptr};
+    TString sAlignmentMatrixFileName = srcDir + "/parameters/mcbm/AlignmentMatrices_" + geoSetupTag + ".root";
+    std::cout << "-I- " << myName << ": loading alignment matrices from file " << sAlignmentMatrixFileName << " ... ";
+    TFile* misalignmentMatrixRootfile = TFile::Open(sAlignmentMatrixFileName, "READ");
+    if (misalignmentMatrixRootfile && misalignmentMatrixRootfile->IsOpen()) {
+      gDirectory->GetObject("MisalignMatrices", matrices);
+      misalignmentMatrixRootfile->Close();
+      std::cout << "done\n";
+    }
+    else {
+      std::cout << "failed: running with misaligned geometry\n";
+    }
+    if (matrices) run->AddAlignmentMatrices(*matrices);
+  }
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- KF initialization ----------------------------------------------------------------------------------------------
+  run->AddTask(new CbmTrackingDetectorInterfaceInit());
+  run->AddTask(new CbmKF());
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- V0 Finder ------------------------------------------------------------------------------------------------------
+  auto* pV0 = new cbm::kfp::V0FinderTask(/*verbose = */ 3);
+  // TODO:  define all the setters initialization within a config file
+  // The task parameters:
+  pV0->SetMinPionDca(1.5);
+  pV0->SetMinProtonDca(0.5);
+  pV0->SetTzeroOffset(0.12);
+  pV0->SetQpAssignedUncertainty(0.10);  // 10% uncertainty for track q/p
+  pV0->SetMixedEventMode(bMixEvent);
+  pV0->SetProcessingMode(cbm::kfp::V0FinderTask::EProcessingMode::TimeBased);
+  pV0->SetPidApproach(cbm::kfp::V0FinderTask::EPidApproach::Topo);
+  pV0->SetPvFindingMode(cbm::kfp::V0FinderTask::EPvUsageMode::Target);
+
+  // Decays to be reconstructed:
+  pV0->AddDecayToReconstructionList(3122);  // Lambda
+
+  // KFParticleFinder cuts:
+  pV0->SetChiPrimaryCut2D(0.f);
+  pV0->SetLdLCut2D(1.f);
+  pV0->SetLCut(0.1);
+  pV0->SetChi2Cut2D(50.f);
+
+  // QA output setting
+  {
+    TString sQaFile{sSinkFile};
+    sQaFile.ReplaceAll(".ana.", "_qa.ana.");
+    pV0->SetQaOutputFileName(sQaFile);
+    pV0->SetRunQa(true);
+  }
+  run->AddTask(pV0);
+  // -------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Parameter database ---------------------------------------------------------------------------------------------
+  std::cout << "\n\n";
+  std::cout << "-I- " << myName << ": setting runtime DB\n";
+  FairRuntimeDb* rtdb       = run->GetRuntimeDb();
+  FairParRootFileIo* parInp = new FairParRootFileIo();
+  FairParRootFileIo* parOut = new FairParRootFileIo();
+  parInp->open(sParInFile.Data(), "READ");
+  rtdb->setFirstInput(parInp);
+  TString sParOutFile{sSinkFile};
+  sParOutFile.ReplaceAll(".ana.", ".par.");
+  parOut->open(sParOutFile.Data(), "RECREATE");
+  rtdb->setOutput(parOut);
+  // ------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Run initialisation --------------------------------------------------------------------------------------------
+  std::cout << "\n\n";
+  std::cout << "-I- " << myName << ": initialising run\n";
+  run->Init();
+  rtdb->print();
+  // ------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Run execution -------------------------------------------------------------------------------------------------
+  std::cout << "\n\n";
+  std::cout << "-I- " << myName << ": starting run\n";
+  run->Run(firstTimeslice, nTimeslices);
+  // ------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Finish --------------------------------------------------------------------------------------------------------
+  timer.Stop();
+  Double_t rtime = timer.RealTime();
+  Double_t ctime = timer.CpuTime();
+  std::cout << "-I- " << myName << "Macro finished successfully.\n";
+  std::cout << "-I- " << myName << "Output file is " << sSinkFile << '\n';
+  std::cout << "-I- " << myName << "Parameter file is " << sParOutFile << '\n';
+  std::cout << "-I- " << myName << "Real time " << rtime << " s, CPU time " << ctime << " s\n\n";
+  // ------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Resourse monitoring -------------------------------------------------------------------------------------------
+  // Extract the maximal used memory an add is as Dart measurement
+  // This line is filtered by CTest and the value send to CDash
+  FairSystemInfo sysInfo;
+  Float_t maxMemory = sysInfo.GetMaxMemory();
+  std::cout << "<DartMeasurement name=\"MaxMemory\" type=\"numeric/double\">";
+  std::cout << maxMemory;
+  std::cout << "</DartMeasurement>" << std::endl;
+
+  Float_t cpuUsage = ctime / rtime;
+  std::cout << "<DartMeasurement name=\"CpuLoad\" type=\"numeric/double\">";
+  std::cout << cpuUsage;
+  std::cout << "</DartMeasurement>" << std::endl;
+  // ------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Memory clean-up -----------------------------------------------------------------------------------------------
+  RemoveGeoManager();
+  // ------------------------------------------------------------------------------------------------------------------
+
+
+  // -- Screen output for automatic tests -----------------------------------------------------------------------------
+  std::cout << " Test passed" << std::endl;
+  std::cout << " All ok " << std::endl;
+  // ------------------------------------------------------------------------------------------------------------------
+
+
+  return true;
+}
diff --git a/macro/run/run_qa.C b/macro/run/run_qa.C
index 16880b48a3..07f9da0f08 100644
--- a/macro/run/run_qa.C
+++ b/macro/run/run_qa.C
@@ -218,10 +218,11 @@ void run_qa(TString dataTraColl,
   // ------------------------------------------------------------------------
 
   // -----   MCDataManager  -----------------------------------
+  // TODO: provide to mcbm_qa.C, run_reco_.....C, mcbm_reco_......C
   CbmMCDataManager* mcManager = new CbmMCDataManager("MCDataManager", 0);
-  mcManager->AddFile(traFileColl);
-  if (!dataTraSign.IsNull()) mcManager->AddFile(traFileSign);
-  if (!dataTraBeam.IsNull()) mcManager->AddFile(traFileBeam);
+  mcManager->AddFileToChain(traFileColl, 0);
+  if (!dataTraSign.IsNull()) mcManager->AddFileToChain(traFileSign, 1);
+  if (!dataTraBeam.IsNull()) mcManager->AddFileToChain(traFileBeam, 2);
 
   qaManager->AddTask(mcManager);
   // ------------------------------------------------------------------------
diff --git a/reco/KF/CbmKFParticleFinder.cxx b/reco/KF/CbmKFParticleFinder.cxx
index f7da8967f2..7ffc2dd17b 100644
--- a/reco/KF/CbmKFParticleFinder.cxx
+++ b/reco/KF/CbmKFParticleFinder.cxx
@@ -581,7 +581,7 @@ void CbmKFParticleFinder::FillKFPTrackVector(KFPTrackVector* tracks, const vecto
   }
 }
 
-double CbmKFParticleFinder::InversedChi2Prob(double p, int ndf) const
+double CbmKFParticleFinder::InversedChi2Prob(double p, int ndf)
 {
   double epsilon   = 1.e-14;
   double chi2Left  = 0.f;
diff --git a/reco/KF/CbmKFParticleFinder.h b/reco/KF/CbmKFParticleFinder.h
index 9e656426b0..5a38b57db5 100644
--- a/reco/KF/CbmKFParticleFinder.h
+++ b/reco/KF/CbmKFParticleFinder.h
@@ -83,8 +83,9 @@ class CbmKFParticleFinder : public FairTask {
   //Add decay to the reconstruction list. By default, if list is empty - all decays are reconstructed.
   void AddDecayToReconstructionList(int pdg);
 
+  static double InversedChi2Prob(double p, int ndf);
+
  private:
-  double InversedChi2Prob(double p, int ndf) const;
   void FillKFPTrackVector(KFPTrackVector* tracks, const std::vector<CbmStsTrack>& vRTracks,
                           const std::vector<KFFieldVector>& vField, const std::vector<int>& pdg,
                           const std::vector<int>& trackId, const std::vector<float>& vChiToPrimVtx,
diff --git a/reco/KF/CbmKFV0FinderQa.cxx b/reco/KF/CbmKFV0FinderQa.cxx
new file mode 100644
index 0000000000..6b6b1e5463
--- /dev/null
+++ b/reco/KF/CbmKFV0FinderQa.cxx
@@ -0,0 +1,61 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   CbmKFV0FinderQa.cxx
+/// \brief  A simple QA for the V0 finder class (runs together with the finder task) (implementation)
+/// \since  16.01.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#include "CbmKFV0FinderQa.h"
+
+#include <Logger.h>
+
+#include <TFile.h>
+
+using cbm::kfp::V0FinderQa;
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void V0FinderQa::InitHistograms()
+{
+  TString n{""};  // name of histogram
+  TString t{""};  // title of histogram
+
+  n                    = "tof_lst_hit_time";
+  t                    = "Time of last TOF hit in track;t [ns];Counts";
+  fph_tof_lst_hit_time = MakeQaObject<TH1D>(n, t, 200, -100., 100.);
+
+  n            = "beta_all";
+  t            = "Speed of tracks (all);#beta;Counts";
+  fph_beta_all = MakeQaObject<TH1D>(n, t, 150, 0., 1.5);
+
+  {
+    n       = "dca";
+    t       = "DCA to the origin of the selected tracks;dca [cm];Counts";
+    fph_dca = MakeQaObject<TH1D>(n, t, 120, 0., 6.);
+
+    n         = "dca2D";
+    t         = "DCA to the origin of the selected tracks;dca_{x} [cm];dca_{y} [cm];Counts";
+    fph_dca2D = MakeQaObject<TH2D>(n, t, 240, -6., 6., 240, -6., 6.);
+
+    n                   = "dca_projectionX";
+    t                   = "DCA to the origin of the selected tracks;dca_{x} [cm];Counts";
+    fph_dca_projectionX = MakeQaObject<TH1D>(n, t, 240, -6., 6.);
+
+    n                   = "dca_projectionY";
+    t                   = "DCA to the origin of the selected tracks;dca_{y} [cm];Counts";
+    fph_dca_projectionY = MakeQaObject<TH1D>(n, t, 240, -6., 6.);
+  }
+}
+
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void V0FinderQa::WriteHistograms(const TString& fileName)
+{
+  LOG(info) << "V0FinderQa: writing histograms to file: " << fileName;
+  TFile fileOut(fileName, "RECREATE");
+  this->WriteToFile(&fileOut);
+  fileOut.Close();
+}
diff --git a/reco/KF/CbmKFV0FinderQa.h b/reco/KF/CbmKFV0FinderQa.h
new file mode 100644
index 0000000000..53091e0112
--- /dev/null
+++ b/reco/KF/CbmKFV0FinderQa.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   CbmKFV0FinderQa.h
+/// \brief  A simple QA for the V0 finder class (runs together with the finder task)
+/// \since  15.01.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+// FIXME: Later move this class into a separate QA-task
+
+#pragma once
+
+#include "CbmQaIO.h"
+
+#include <TString.h>
+
+#include <array>
+#include <string>
+#include <vector>
+
+class TH1D;
+class TH2D;
+
+namespace cbm::kfp
+{
+  /// \class V0FinderQa
+  /// \brief A simple QA for the V0 finder
+  class V0FinderQa : public CbmQaIO {
+   public:
+    /// \brief  Constructor from parameters
+    /// \param  bUseMc  If MC truth should be used
+    V0FinderQa(bool bUseMc) : CbmQaIO("v0Finder"), fbUseMc(bUseMc) {}
+
+    /// \brief Destructor
+    ~V0FinderQa() = default;
+
+    V0FinderQa(const V0FinderQa&) = delete;
+    V0FinderQa(V0FinderQa&&)      = delete;
+    V0FinderQa& operator=(const V0FinderQa&) = delete;
+    V0FinderQa& operator=(V0FinderQa&&) = delete;
+
+    /// \brief Initializes histograms
+    void InitHistograms();
+
+    /// \brief Writes histograms into file
+    void WriteHistograms(const TString&);
+
+    //* Histograms (public for easier access)
+    TH1D* fph_tof_lst_hit_time{nullptr};             ///< Time of tof hits, used for track momentum estimation
+    TH1D* fph_beta_all{nullptr};                     ///< Beta of tracks
+    TH1D* fph_dca{nullptr};                          ///< Track DCA to origin
+    TH2D* fph_dca2D{nullptr};                        ///< Track DCA to origin (2D)
+    TH1D* fph_dca_projectionX{nullptr};              ///< Track DCA to origin (x-component)
+    TH1D* fph_dca_projectionY{nullptr};              ///< Track DCA to origin (y-component)
+    TH2D* fph_track_rapidity_vs_pt_all{nullptr};     ///< Phase space of all accepted tracks
+    TH2D* fph_track_rapidity_vs_pt_pion{nullptr};    ///< Phase space of pion candidates
+    TH2D* fph_track_rapidity_vs_pt_proton{nullptr};  ///< Phase space of proton candidates
+
+
+   private:
+    bool fbUseMc{false};
+
+    ClassDef(V0FinderQa, 0);
+  };
+}  // namespace cbm::kfp
diff --git a/reco/KF/CbmKFV0FinderTask.cxx b/reco/KF/CbmKFV0FinderTask.cxx
new file mode 100644
index 0000000000..2ec69f809c
--- /dev/null
+++ b/reco/KF/CbmKFV0FinderTask.cxx
@@ -0,0 +1,774 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   CbmKFV0FinderTask.cxx
+/// \brief  A FairTask for V0 candidates finding in mCBM (implementation)
+/// \since  10.01.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+#include "CbmKFV0FinderTask.h"
+
+#include "CbmEvent.h"
+#include "CbmGlobalTrack.h"
+#include "CbmKfTarget.h"
+#include "CbmStsHit.h"
+#include "CbmStsTrack.h"
+#include "CbmTofAddress.h"
+#include "CbmTofHit.h"
+#include "CbmTofTrack.h"
+#include "CbmTrdHit.h"
+#include "CbmTrdTrack.h"
+#include "FairRunAna.h"
+#include "KFPTrackVector.h"
+#include "KFVertex.h"
+#include "Logger.h"
+#include "TClonesArray.h"
+#include "TMatrixTSym.h"
+
+#include <algorithm>
+#include <cmath>
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <sstream>
+
+// Log macros:
+#define ERR_ LOG(error) << fName << ": "
+#define LOG_(SEVERITY, VERBOSITY) LOG_IF(SEVERITY, fVerbose >= VERBOSITY) << fName << ": "
+
+using cbm::kfp::V0FinderTask;
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+bool V0FinderTask::AssignMomentum(CbmGlobalTrack* pTrack, int pdg)
+{
+  const int iTofTrk = pTrack->GetTofTrackIndex();
+  if (iTofTrk < 0) {
+    ++fCounters[ECounter::TracksWoTofHits];
+    return false;  // Skip tracks, which do not have hits in TOF
+  }
+
+  const auto* pTofTrack{static_cast<const CbmTofTrack*>(fpBrTofTracks->At(iTofTrk))};
+  assert(pTofTrack);
+
+  int nTofHits = pTofTrack->GetNofTofHits();
+  if (nTofHits < 1) {
+    // Must not be called
+    return false;
+  }
+  ++fCounters[ECounter::TracksWAtLeastOneTofHit];
+
+  //int iFstTofHit = pTofTrack->GetTofHitIndex(0);
+  int iLstTofHit = pTofTrack->GetTofHitIndex(nTofHits - 1);  // last hit in TOF
+
+  const auto* pLstTofHit{static_cast<CbmTofHit*>(fpBrTofHits->At(iLstTofHit))};
+  if (fbRunQa) {
+    fpQa->fph_tof_lst_hit_time->Fill(pLstTofHit->GetTime());
+  }
+  assert(pLstTofHit);
+
+  if (pLstTofHit->GetTime() < 0) {  //< wrongly calibrated T0
+    ++fCounters[ECounter::TracksWNegativeTofHitTime];
+    return false;
+  }
+
+  auto qpAndBeta = EstimateQp(pLstTofHit, pdg);
+  if (fbRunQa) {
+    fpQa->fph_beta_all->Fill(qpAndBeta.fBeta);
+  }
+
+  if (qpAndBeta.fBeta > 1) {
+    ++fCounters[ECounter::TracksWithUnphysicalBeta];
+    return false;  // unphysical track
+  }
+
+  // Update the track with the PID hypothesis and momentum
+  FairTrackParam parFst(*pTrack->GetParamFirst());
+  FairTrackParam parLst(*pTrack->GetParamLast());
+
+  parFst.SetQp(qpAndBeta.fQp);
+  parLst.SetQp(qpAndBeta.fQp);
+  parFst.SetCovariance(4, 4, qpAndBeta.fQpVar);
+  parFst.SetCovariance(4, 4, qpAndBeta.fQpVar);
+
+  pTrack->SetParamFirst(&parFst);
+  pTrack->SetParamLast(&parLst);
+  return true;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+template<bool UseEvent>
+bool V0FinderTask::ProcessEvent(const CbmEvent* pEvent)
+{
+  int nTracks = UseEvent ? pEvent->GetNofData(ECbmDataType::kGlobalTrack) : fpBrGlobalTracks->GetEntriesFast();
+  fCounters[ECounter::TracksTotal] += nTracks;
+
+
+  // Local event counters
+  int nProtons{0};
+  int nPions{0};
+  int nTracksSelected{0};
+
+  // Local containers (per event)
+  std::vector<const CbmGlobalTrack*> vpSelectedTracks;  // Selected tracks [n selected tracks]
+  vpSelectedTracks.reserve(nTracks);
+  std::vector<int> vSelectedTrackIds;  // Indices of selected tracks [n selected tracks]
+  vSelectedTrackIds.reserve(nTracks);
+
+  // ----- Shift TOF hit times to t0
+  if constexpr (UseEvent) {
+    ShiftTofHitsToTzero(pEvent);
+  }
+
+  // ----- Select tracks
+  for (int iTrkEvt{0}; iTrkEvt < nTracks; ++iTrkEvt) {
+    const int iTrk = UseEvent ? pEvent->GetIndex(ECbmDataType::kGlobalTrack, iTrkEvt) : iTrkEvt;
+    auto* pGlobalTrack{static_cast<CbmGlobalTrack*>(fpBrGlobalTracks->At(iTrk))};
+    if (!SelectTrack(pGlobalTrack, iTrk)) {
+      continue;
+    }
+    ++nTracksSelected;
+    switch (pGlobalTrack->GetPidHypo()) {
+      case -211:  // pions
+        ++nPions;
+        break;
+      case 2212:  // protons
+        ++nProtons;
+        break;
+    }
+    vpSelectedTracks.push_back(pGlobalTrack);
+    vSelectedTrackIds.push_back(iTrk);
+  }  // END LOOP: tracks in event
+
+  if (nProtons < 1 || nPions < 1) {
+    return false;  // Nothing to search for in the event
+  }
+
+  fCounters[ECounter::TracksSelected] += nTracksSelected;
+  fCounters[ECounter::Pions] += nPions;
+  fCounters[ECounter::Protons] += nProtons;
+
+  ++fCounters[ECounter::EventsLambdaCand];
+
+  // NOTE: The variable is used to assign a KfParticle to a corresponding primary vertex:
+  //        a) pvChi2 < 3 => primary particle
+  //        b) pvChi2 > 3 => secondary particle
+  // Here we assign all particles as secondary (TODO: test and try to separate primary and secondary particles)
+  std::vector<float> vChi2ToPv;             // Chi-square to primary vertex (NOTE: not used)
+  vChi2ToPv.resize(nTracksSelected, 999.);  // Assign all tracks as secondary
+
+  KFPTrackVector kfpTracksFst = MakeKfpTrackVector(vpSelectedTracks, vSelectedTrackIds, vChi2ToPv, true);
+  KFPTrackVector kfpTracksLst = MakeKfpTrackVector(vpSelectedTracks, vSelectedTrackIds, vChi2ToPv, false);
+
+  // ----- Reconstruct topology in the event
+  fpTopoReconstructorEvent->Clear();
+  fpTopoReconstructorEvent->Init(kfpTracksFst, kfpTracksLst);
+  if (EPvUsageMode::Target == fPvUsageMode) {
+    float x{float(fpOrigin->GetX())};
+    float y{float(fpOrigin->GetY())};
+    float z{float(fpOrigin->GetZ())};
+    // NOTE: the target size can be applied as a covariance matrix, now it has all nulls
+    fpTopoReconstructorEvent->AddPV(MakeKfpPrimaryVertex(x, y, z), std::vector<int>{});
+  }
+  fpTopoReconstructorEvent->SortTracks();
+  fpTopoReconstructorEvent->ReconstructParticles();
+
+  // ----- Count number of found lambda-candidates
+  const auto& particles = fpTopoReconstructorEvent->GetParticles();
+  int nLambda = std::count_if(particles.begin(), particles.end(), [](const auto& p) { return p.GetPDG() == 3122; });
+  fCounters[ECounter::KfpLambdaCandidates] += nLambda;
+  if (nLambda > 0) {
+    ++fCounters[ECounter::KfpEventsLambdaCand];
+  }
+
+  // ----- Store particles to the run topology
+  StoreParticles();
+
+  return true;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+bool V0FinderTask::CheckTrackParam(const FairTrackParam* pParam)
+{
+  bool bOk{true};
+  bOk &= std::isfinite(pParam->GetX());
+  bOk &= std::isfinite(pParam->GetY());
+  bOk &= std::isfinite(pParam->GetZ());
+  bOk &= std::isfinite(pParam->GetTx());
+  bOk &= std::isfinite(pParam->GetTy());
+  bOk &= std::isfinite(pParam->GetQp());
+  std::array<double, 15> covMatrix = {0.};
+  if (bOk) {
+    for (int i = 0, iCov = 0; i < 5; ++i) {
+      for (int j = i; j < 5; j++, iCov++) {
+        // NOTE: In the FairTrackParam::GetCovariance(i, j) one can exchange the i and j indices, but for more
+        //       efficient calculation it is recommended to keep i < j.
+        covMatrix[iCov] = pParam->GetCovariance(i, j);
+        bOk &= std::isfinite(covMatrix[iCov]);
+      }
+    }
+  }
+  return bOk;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+V0FinderTask::DcaVector V0FinderTask::EstimateDcaToOrigin(const CbmStsTrack* pStsTrack) const
+{
+  DcaVector res;
+  if (pStsTrack->GetNofStsHits() < 2) {
+    // Too few STS hits
+    return res;
+  }
+  const auto* pHitFst{static_cast<const CbmStsHit*>(fpBrStsHits->At(pStsTrack->GetStsHitIndex(0)))};  // first hit
+  const auto* pHitSnd{static_cast<const CbmStsHit*>(fpBrStsHits->At(pStsTrack->GetStsHitIndex(1)))};  // second hit
+  assert(pHitFst);
+  assert(pHitSnd);
+  double factor{(pHitFst->GetZ() - fpOrigin->GetZ()) / (pHitSnd->GetZ() - pHitFst->GetZ())};
+  res.fX   = pHitFst->GetX() - fpOrigin->GetX() - factor * (pHitSnd->GetX() - pHitFst->GetX());
+  res.fY   = pHitFst->GetY() - fpOrigin->GetY() - factor * (pHitSnd->GetY() - pHitFst->GetY());
+  res.fAbs = std::sqrt(res.fX * res.fX + res.fY * res.fY);
+  res.fX /= res.fAbs;
+  res.fY /= res.fAbs;
+  return res;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+V0FinderTask::QpAndBeta V0FinderTask::EstimateQp(const CbmTofHit* pTofHit, int pdg) const
+{
+  // FIXME: For now we consider the origin as a precise measurement, which is not correct. The corresponding
+  //        uncertainty of the origin must be accounted here.
+  //        Also: redefine origin with reconstructed or MC PV (in other function).
+  //        Also: the t0 uncertainty should be estimated and accounted in the momentum uncertainty
+  QpAndBeta res;
+  double x{pTofHit->GetX() - fpOrigin->GetX()};
+  double y{pTofHit->GetY() - fpOrigin->GetY()};
+  double z{pTofHit->GetZ() - fpOrigin->GetZ()};
+  double x2{x * x};
+  double y2{y * y};
+  double z2{z * z};
+  double t2{pTofHit->GetTime() * pTofHit->GetTime()};
+  double r2{x2 + y2 + z2};
+  double r4{r2 * r2};
+  // FIXME: In general case, the x, y, z coordinates of the origin can have non-zero covariances, which should be
+  //        accounted in the calculation.
+  double xVar{pTofHit->GetDx() * pTofHit->GetDx() + fpOrigin->GetCovariance(0, 0)};
+  double yVar{pTofHit->GetDy() * pTofHit->GetDy() + fpOrigin->GetCovariance(1, 1)};
+  double zVar{pTofHit->GetDz() * pTofHit->GetDz() + fpOrigin->GetCovariance(2, 2)};
+  double tVar{pTofHit->GetTimeError() * pTofHit->GetTimeError()};
+  double factor{(x2 * xVar + y2 * yVar + z2 * zVar) / r4 + tVar / t2};
+  res.fBeta    = std::sqrt(r2) / (pTofHit->GetTime() * kSpeedOfLight);
+  res.fBetaVar = res.fBeta * factor;
+  if (res.fBeta >= 1) {
+    return res;  //< Non-physical beta
+    // FIXME: I would set beta to fixed large value, like 0.999999 for this case
+  }
+  double gamma{1. / std::sqrt(1. - res.fBeta * res.fBeta)};
+  double m{0};
+  double q{0};
+  switch (pdg) {
+    case -211:
+      m = kPionMass;
+      q = -1;
+      break;
+    case 2212:
+      m = kProtonMass;
+      q = 1;
+      break;
+    default:
+      res.fQp    = -1. / 99999.;  //< Give small momentum to primary tracks
+      res.fQpVar = res.fQp * res.fQp * fQpAssignedUncertainty * fQpAssignedUncertainty;
+      return res;
+      // NOTE: it would be more straight forward to assign the primary tracks a mass of pion and keep it
+  }
+  res.fQp = q / (gamma * res.fBeta * m);
+  if (fQpAssignedUncertainty > 0) {
+    res.fQpVar = res.fQp * res.fQp * fQpAssignedUncertainty * fQpAssignedUncertainty;
+  }
+  else {
+    res.fQpVar = q * q / (m * m * res.fBeta * res.fBeta) * factor;
+  }
+  return res;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void V0FinderTask::Exec(Option_t*)
+{
+  fvTrackDca.clear();
+  fvTrackDca.resize(fpBrGlobalTracks->GetEntriesFast());
+
+  // ----- Reset data containers per timeslice
+  fpTopoReconstructorRun->Clear();
+
+  // ----- Process timeslice
+  if (EProcessingMode::EventBased == fProcessingMode) {
+    ++fCounters[ECounter::EventsTotal];
+    ProcessEvent</*UseEvent = */ false>(nullptr);
+  }
+  else {
+    const int nEvents{fpBrRecoEvents->GetEntriesFast()};
+    fCounters[ECounter::EventsTotal] += nEvents;
+    for (int iEvent = 0; iEvent < nEvents; ++iEvent) {
+      const auto* pEvent = static_cast<CbmEvent*>(fpBrRecoEvents->At(iEvent));
+      ProcessEvent</*UseEvent = */ true>(pEvent);
+    }
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void V0FinderTask::Finish()
+{
+  // ----- Store QA histograms
+  if (fbRunQa) {
+    fpQa->WriteHistograms(fsQaOutputName);
+  }
+
+  // ----- Counters summary log
+  std::stringstream msg;
+  msg << "*** Counters summary ***\n";
+  msg << "\n  tracks total:                         " << fCounters[ECounter::TracksTotal];
+  msg << "\n  tracks selected:                      " << fCounters[ECounter::TracksSelected];
+  msg << "\n  tracks w/ infinit parameters:         " << fCounters[ECounter::TracksInfiniteParam];
+  msg << "\n  tracks w/o TOF hits:                  " << fCounters[ECounter::TracksWoTofHits];
+  msg << "\n  tracks w/ last TOF hit having t<0:    " << fCounters[ECounter::TracksWNegativeTofHitTime];
+  msg << "\n  tracks w/o STS hits:                  " << fCounters[ECounter::TracksWoStsHits];
+  msg << "\n  tracks w/o PID:                       " << fCounters[ECounter::TracksWoPid];
+  msg << "\n  tracks w/o momentum:                  " << fCounters[ECounter::TracksWoMomentum];
+  msg << "\n  tracks w/ at least one TOF hit:       " << fCounters[ECounter::TracksWAtLeastOneTofHit];
+  msg << "\n  tracks w/ at least two TOF hits:      " << fCounters[ECounter::TracksWAtLeastTwoTofHits];
+  msg << "\n  tracks w/ beta > 1                    " << fCounters[ECounter::TracksWithUnphysicalBeta];
+  msg << "\n  pion candidates:                      " << fCounters[ECounter::Pions];
+  msg << "\n  proton candidates:                    " << fCounters[ECounter::Protons];
+  msg << "\n  events total:                         " << fCounters[ECounter::EventsTotal];
+  msg << "\n  events w/ potential Lambda-candidate: " << fCounters[ECounter::EventsLambdaCand];
+  msg << "\n  events w/ Lambda-candidate from KFPF: " << fCounters[ECounter::KfpEventsLambdaCand];
+  msg << "\n  Lambda-candidates:                    " << fCounters[ECounter::KfpLambdaCandidates];
+  LOG_(info, 1) << msg.str();
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+int V0FinderTask::InferTrackPidTopo(double dca) const
+{
+  if (std::isnan(dca)) {
+    return -2;  //< Track PID cannot be infered
+  }
+  if (dca > fMinPionDca) {
+    return -211;  // pi-
+  }
+  else if (dca > fMinProtonDca) {
+    return 2212;  // proton
+  }
+  return kPrimaryPdg;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+InitStatus V0FinderTask::Init()
+{
+  LOG_(info, 1) << "initializing the task ... ";
+
+  LOG_(info, 1) << "using the following configuration:\n  processing mode: " << ToString(fProcessingMode)
+                << "\n  PID approach: " << ToString(fPidApproach) << "\n  " << ToString(fPvUsageMode);
+
+  // Temporary not implemented:
+  if (EPidApproach::Mc == fPidApproach || EPvUsageMode::Mc == fPvUsageMode
+      || EPvUsageMode::Reconstructed == fPvUsageMode || EPvUsageMode::ReconstructSingle == fPvUsageMode
+      || EPvUsageMode::ReconstructMultiple == fPvUsageMode) {
+    ERR_ << "Desirable configuration of PID or PV handling was not implemented yet";
+    return kFATAL;
+  }
+
+  // ----- Input data branches initialization
+  const auto* pTarget = kf::Target::Instance();  // CBM target info
+
+  auto* pFairManager = FairRootManager::Instance();
+  if (!pFairManager) {
+    ERR_ << "FairRootManager not found";
+    return kERROR;
+  }
+
+  auto InitBranch = [&](TClonesArray*& branch, const char* name) -> bool {
+    branch = dynamic_cast<TClonesArray*>(pFairManager->GetObject(name));
+    if (!branch) {
+      ERR_ << "branch \"" << name << "\" not found";
+      return false;
+    }
+    return true;
+  };
+
+  bool bBranchesInitialized{true};
+  if (EProcessingMode::TimeBased == fProcessingMode) {
+    bBranchesInitialized = InitBranch(fpBrRecoEvents, "CbmEvent") && bBranchesInitialized;
+  }
+  bBranchesInitialized = InitBranch(fpBrGlobalTracks, "GlobalTrack") && bBranchesInitialized;
+  bBranchesInitialized = InitBranch(fpBrStsTracks, "StsTrack") && bBranchesInitialized;
+  bBranchesInitialized = InitBranch(fpBrTrdTracks, "TrdTrack") && bBranchesInitialized;
+  bBranchesInitialized = InitBranch(fpBrTofTracks, "TofTrack") && bBranchesInitialized;
+  bBranchesInitialized = InitBranch(fpBrStsHits, "StsHit") && bBranchesInitialized;
+  bBranchesInitialized = InitBranch(fpBrTrdHits, "TrdHit") && bBranchesInitialized;
+  bBranchesInitialized = InitBranch(fpBrTofHits, "TofHit") && bBranchesInitialized;
+
+  if (EPvUsageMode::Reconstructed == fPvUsageMode) {
+    fpBrPrimaryVertex = dynamic_cast<CbmVertex*>(pFairManager->GetObject("PrimaryVertex."));
+    if (!fpBrPrimaryVertex) {
+      ERR_ << "branch \"PrimaryVertex.\" not found";
+      bBranchesInitialized = false;
+    }
+  }
+  else if (EPvUsageMode::Target == fPvUsageMode) {
+    double x{pTarget->GetX()};
+    double y{pTarget->GetY()};
+    double z{pTarget->GetZ()};
+    LOG_(info, 1) << "using target center as origin: r = (" << x << ", " << y << ", " << z << ") [cm]";
+    TMatrixFSym covMatrix(3);
+    covMatrix(1, 0) = 0.;
+    covMatrix(2, 0) = 0.;
+    covMatrix(2, 1) = 0.;
+    //double transverseVariance{pTarget->GetRmax() * pTarget->GetRmax() / 12.};
+    //covMatrix(0, 0) = transverseVariance;
+    //covMatrix(1, 1) = transverseVariance;
+    //covMatrix(2, 2) = pTarget->GetDz() * pTarget->GetDz() / 3.;
+    covMatrix(0, 0) = 0.;
+    covMatrix(1, 1) = 0.;
+    covMatrix(2, 2) = 0.;
+    fpOrigin->SetVertex(x, y, z, 0., 1, 0, covMatrix);
+  }
+
+  if (!bBranchesInitialized) {
+    return kERROR;
+  }
+
+  // ----- Topology reconstructor initialization
+  fpTopoReconstructorRun->SetTarget({float(pTarget->GetX()), float(pTarget->GetY()), float(pTarget->GetZ())});
+  fpTopoReconstructorEvent->SetTarget(fpTopoReconstructorRun->GetTargetPosition());
+  fpTopoReconstructorEvent->CopyCuts(fpTopoReconstructorRun.get());
+  fpTopoReconstructorEvent->GetKFParticleFinder()->SetReconstructionList(
+    fpTopoReconstructorRun->GetKFParticleFinder()->GetReconstructionList());
+
+  // ----- Auxilary variables initialization
+  std::fill(fCounters.begin(), fCounters.end(), 0);
+
+  // ----- QA initialization
+  if (fbRunQa) {
+    fpQa = std::make_unique<V0FinderQa>(fbUseMc);
+    fpQa->InitHistograms();
+  }
+
+  LOG_(info, 1) << "initializing the task ... done";
+  return kSUCCESS;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+KFPTrackVector V0FinderTask::MakeKfpTrackVector(const std::vector<const CbmGlobalTrack*>& vpTracks,
+                                                const std::vector<int>& vTrackIds, const std::vector<float>& vChi2ToPv,
+                                                bool bAtFirstPoint) const
+{
+  // TODO: Cross-check with the CbmKFParticleFinder results
+  // NOTE: Actually tracks themselves are not needed here, so one could fill and pass a vector of FairTrackParam*,
+  //       put in KFPTrackVector there is an option to set number of Pixel hits, so we will keep the signature of
+  //       the function as it is.
+  KFPTrackVector trackVector;
+  int nTracks = vpTracks.size();
+  trackVector.Resize(nTracks);
+  for (int iTrk = 0; iTrk < nTracks; ++iTrk) {
+    const auto* pTrack{vpTracks[iTrk]};
+    const auto* pParam{bAtFirstPoint ? pTrack->GetParamFirst() : pTrack->GetParamLast()};
+
+    // ----- Parameters definition
+    int pdg{pTrack->GetPidHypo()};
+    const double& tx{pParam->GetTx()};
+    const double& ty{pParam->GetTy()};
+    const double& qp{pParam->GetQp()};
+    int q{qp > 0. ? 1 : -1};
+    if (std::fabs(pdg) == 10000020030 || std::fabs(pdg == 1000020040)) {  // He3 and He4
+      // NOTE: at the moment not called, but never the less leave it here for the future pid procedures
+      q *= 2;
+    }
+    double p{q / qp};
+    double p2{p * p};
+    double t2inv{1. / (1. + tx * tx + ty * ty)};
+    double pz{std::sqrt(t2inv * p2)};
+    double px{tx * pz};
+    double py{ty * pz};
+
+    trackVector.SetParameter(pParam->GetX(), 0, iTrk);
+    trackVector.SetParameter(pParam->GetY(), 1, iTrk);
+    trackVector.SetParameter(pParam->GetZ(), 2, iTrk);
+    trackVector.SetParameter(px, 3, iTrk);
+    trackVector.SetParameter(py, 4, iTrk);
+    trackVector.SetParameter(pz, 5, iTrk);
+
+    // Jacobian matrix for (tx, ty, qp) -> (px, py, pz)
+    std::array<std::array<double, 3>, 3> Jp;
+    Jp[2][0] = -t2inv * px;         // d(pz)/d(tx)
+    Jp[2][1] = -t2inv * py;         // d(pz)/d(ty)
+    Jp[2][2] = -pz / qp;            // d(pz)/d(qp)
+    Jp[0][0] = tx * Jp[2][0] + pz;  // d(px)/d(tx)
+    Jp[0][1] = tx * Jp[2][1];       // d(px)/d(ty)
+    Jp[0][2] = tx * Jp[2][2];       // d(px)/d(qp)
+    Jp[1][0] = ty * Jp[2][0];       // d(py)/d(tx)
+    Jp[1][1] = ty * Jp[2][1] + pz;  // d(py)/d(ty)
+    Jp[1][2] = ty * Jp[2][2];       // d(py)/d(qp)
+
+    // ----- Covariance matrix definition
+    // Position covariances
+    trackVector.SetCovariance(pParam->GetCovariance(0, 0), 0, iTrk);  // var(x)
+    trackVector.SetCovariance(pParam->GetCovariance(0, 1), 1, iTrk);  // cov(x, y)
+    trackVector.SetCovariance(pParam->GetCovariance(1, 1), 2, iTrk);  // var(y)
+
+    // Momentum-position covariances
+    auto MomPosCovariance = [&](const int k, const int l) constexpr->double
+    {
+      double val{0.};
+      const auto& JpA = Jp[k];
+      for (int i = 0; i < 3; ++i) {
+        val += JpA[i] * pParam->GetCovariance(i + 2, l);
+      }
+      return val;
+    };
+    trackVector.SetCovariance(MomPosCovariance(0, 0), 6, iTrk);   // cov(x, px)
+    trackVector.SetCovariance(MomPosCovariance(0, 1), 7, iTrk);   // cov(y, px)
+    trackVector.SetCovariance(MomPosCovariance(1, 0), 10, iTrk);  // cov(x, py)
+    trackVector.SetCovariance(MomPosCovariance(1, 1), 11, iTrk);  // cov(y, py)
+    trackVector.SetCovariance(MomPosCovariance(2, 0), 15, iTrk);  // cov(x, pz)
+    trackVector.SetCovariance(MomPosCovariance(2, 1), 16, iTrk);  // cov(y, pz)
+
+    // Momentum covariances
+    auto MomentumCovariance = [&](const int k, const int l) constexpr->double
+    {
+      double val{0.};
+      const auto& JpA = Jp[k];
+      const auto& JpB = Jp[l];
+      for (int i = 0; i < 3; ++i) {
+        double factor{0.};
+        for (int j = 0; j < 3; ++j) {
+          factor += JpB[j] * pParam->GetCovariance(i + 2, j + 2);
+        }
+        val += JpA[i] * factor;
+      }
+      return val;
+    };
+    trackVector.SetCovariance(MomentumCovariance(0, 0), 9, iTrk);   // var(px)
+    trackVector.SetCovariance(MomentumCovariance(1, 0), 13, iTrk);  // cov(px, py)
+    trackVector.SetCovariance(MomentumCovariance(1, 1), 14, iTrk);  // var(py)
+    trackVector.SetCovariance(MomentumCovariance(2, 0), 18, iTrk);  // cov(px, pz)
+    trackVector.SetCovariance(MomentumCovariance(2, 1), 19, iTrk);  // cov(py, pz)
+    trackVector.SetCovariance(MomentumCovariance(2, 2), 20, iTrk);  // var(pz)
+
+    // Zero covariances
+    // NOTE: from the tracking point of view a z-coordinate in known precisely, so all the corresponding covariances
+    //       should be set to null.
+    trackVector.SetCovariance(0.f, 3, iTrk);   // cov(x,z)
+    trackVector.SetCovariance(0.f, 4, iTrk);   // cov(y,z)
+    trackVector.SetCovariance(0.f, 5, iTrk);   // var(z)
+    trackVector.SetCovariance(0.f, 8, iTrk);   // cov(z,px)
+    trackVector.SetCovariance(0.f, 12, iTrk);  // cov(z,py)
+    trackVector.SetCovariance(0.f, 17, iTrk);  // var(z,pz)
+
+    // ----- Other parameters
+    // Magnetic field
+    // NOTE: In mCBM there is no magnetic field, so here we define the coefficients with zeros
+    for (int iF = 0; iF < 10; ++iF) {
+      trackVector.SetFieldCoefficient(0.f, iF, iTrk);
+    }
+
+    trackVector.SetId(vTrackIds[iTrk], iTrk);
+    trackVector.SetPDG(pdg, iTrk);
+    trackVector.SetQ(q, iTrk);
+    trackVector.SetNPixelHits(0, iTrk);  // Number of MVD hits in track (used to define PV inside the KFPFinder)
+
+    if (EPvUsageMode::Reconstructed == fPvUsageMode || EPvUsageMode::Mc == fPvUsageMode) {
+      if (vChi2ToPv[iTrk] < kChi2PvPrimThrsh) {
+        trackVector.SetPVIndex(0, iTrk);  // Primary track
+      }
+      else {
+        trackVector.SetPVIndex(-1, iTrk);  // Secondary track
+      }
+    }
+    else {
+      trackVector.SetPVIndex(-1, iTrk);  // Secondary track
+    }
+  }
+  return trackVector;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+KFVertex V0FinderTask::MakeKfpPrimaryVertex(float x, float y, float z) const
+{
+  KFVertex kfVertex;
+  kfVertex.X() = x;
+  kfVertex.Y() = y;
+  kfVertex.Z() = z;
+  for (int iC = 0; iC < 6; ++iC) {
+    kfVertex.Covariance(iC) = 0.f;
+  }
+  kfVertex.Chi2() = -100.f;
+  return kfVertex;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+bool V0FinderTask::SelectTrack(CbmGlobalTrack* pTrack, int iTrk)
+{
+  const int iStsTrk = pTrack->GetStsTrackIndex();
+  if (iStsTrk < 0) {
+    ++fCounters[ECounter::TracksWoStsHits];
+    return false;  // Skip tracks, which do not have hits in TOF
+  }
+  const auto* pStsTrack{static_cast<const CbmStsTrack*>(fpBrStsTracks->At(iStsTrk))};
+
+  // Get PID hypothesis for the track
+  int pdgHypo{kUndefPdg};
+  if (EPidApproach::Topo == fPidApproach) {
+    auto& trkDca{fvTrackDca[iTrk]};
+    trkDca  = EstimateDcaToOrigin(pStsTrack);
+    pdgHypo = InferTrackPidTopo(trkDca.fAbs);
+    if (fbRunQa) {
+      fpQa->fph_dca->Fill(trkDca.fAbs);
+      fpQa->fph_dca2D->Fill(trkDca.fAbs * trkDca.fX, trkDca.fAbs * trkDca.fY);
+      fpQa->fph_dca_projectionX->Fill(trkDca.fAbs * trkDca.fX);
+      fpQa->fph_dca_projectionY->Fill(trkDca.fAbs * trkDca.fY);
+    }
+  }
+
+  pTrack->SetPidHypo(pdgHypo);
+  if (pdgHypo == kUndefPdg) {
+    ++fCounters[ECounter::TracksWoPid];
+    return false;  // Tracks, which do not obey the PID criteria
+  }
+
+  // Assign a momentum to the track using the last hit in TOF, reject the track, if it has no TOF hits
+  if (!AssignMomentum(pTrack, pdgHypo)) {
+    ++fCounters[ECounter::TracksWoMomentum];
+    return false;
+  }
+
+  // Check track parameters for validity
+  // FIXME: understand the reason, why the parameters are infinite
+  if (!CheckTrackParam(pTrack->GetParamFirst())) {
+    ++fCounters[ECounter::TracksInfiniteParam];
+    return false;
+  }
+
+  return true;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+double V0FinderTask::ShiftTofHitsToTzero(const CbmEvent* pEvent)
+{
+  // Define t0 from the Bmon hits
+  double t0{std::numeric_limits<double>::signaling_NaN()};
+  int nTofHits = pEvent->GetNofData(ECbmDataType::kTofHit);
+  for (int iTofHitEvt{0}; iTofHitEvt < nTofHits; ++iTofHitEvt) {
+    int iTofHit = pEvent->GetIndex(ECbmDataType::kTofHit, iTofHitEvt);
+    const auto* pTofHit{static_cast<const CbmTofHit*>(fpBrTofHits->At(iTofHit))};
+    //if (5 == CbmTofAddress::GetSmType(pTofHit->GetAddress())) {   // selection by the supermodule type
+    if (pTofHit->GetZ() == 0) {  // Take some small z-coordinate to identify BMon (TODO: provide a flag by some task)
+      t0 = pTofHit->GetTime();
+    }
+  }
+  // NOTE: t0 must be defined for each event, since the Bmon digis are used to seed the digi event builder. Basically,
+  //       the tZero must be defined as a field of CbmEvent, e.g. in a separate FairTask, or directly
+  //       in CbmAlgoBuildRawEvent
+  assert(!std::isnan(t0));
+
+  // Shift TOF times to found t0:
+  for (int iTofHitEvt{0}; iTofHitEvt < nTofHits; ++iTofHitEvt) {
+    int iTofHit = pEvent->GetIndex(ECbmDataType::kTofHit, iTofHitEvt);
+    auto* pTofHit{static_cast<CbmTofHit*>(fpBrTofHits->At(iTofHit))};
+    pTofHit->SetTime(pTofHit->GetTime() - t0 - fTzeroOffset);
+  }
+  return t0;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void V0FinderTask::StoreParticles()
+{
+  int indexOffset = fpTopoReconstructorRun->GetParticles().size();
+  for (int iPv = 0; iPv < fpTopoReconstructorEvent->NPrimaryVertices(); ++iPv) {
+    std::vector<int> vPVTrackIndexArray = fpTopoReconstructorEvent->GetPVTrackIndexArray(iPv);
+    std::for_each(vPVTrackIndexArray.begin(), vPVTrackIndexArray.end(), [&](auto& item) { item += indexOffset; });
+    fpTopoReconstructorRun->AddPV(fpTopoReconstructorEvent->GetPrimKFVertex(iPv), vPVTrackIndexArray);
+  }
+
+  for (size_t iP = 0; iP < fpTopoReconstructorEvent->GetParticles().size(); ++iP) {
+    const KFParticle& particleEvt{fpTopoReconstructorEvent->GetParticles()[iP]};
+    KFParticle particle{particleEvt};
+    particle.CleanDaughtersId();
+    int nDaughters{particleEvt.NDaughters()};
+    if (nDaughters == 1) {
+      particle.AddDaughterId(particleEvt.DaughterIds()[0]);
+    }
+    else if (nDaughters > 1) {
+      for (int iD = 0; iD < particleEvt.NDaughters(); ++iD) {
+        particle.AddDaughterId(particleEvt.DaughterIds()[iD] + indexOffset);
+      }
+    }
+    particle.SetId(particleEvt.Id() + indexOffset);
+    fpTopoReconstructorRun->AddParticle(particle);
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+std::string V0FinderTask::ToString(EProcessingMode mode)
+{
+  switch (mode) {
+    case EProcessingMode::EventBased: return "event-based";
+    case EProcessingMode::TimeBased: return "time-based";
+    default: return "";
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+std::string V0FinderTask::ToString(EPidApproach pid)
+{
+  switch (pid) {
+    case EPidApproach::Topo: return "track topology";
+    case EPidApproach::Mc: return "MC-truth";
+    default: return "";
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+std::string V0FinderTask::ToString(EPvUsageMode pvMode)
+{
+  switch (pvMode) {
+    case EPvUsageMode::Reconstructed: return "reconstructed primary vertex is used";
+    case EPvUsageMode::ReconstructSingle: return "a single primary vertex will be reconstructed";
+    case EPvUsageMode::ReconstructMultiple: return "multiple primary vertices will be reconstructed";
+    case EPvUsageMode::Target: return "target center is used for primary vertex";
+    case EPvUsageMode::Mc: return "MC-true primary vertex is used";
+    default: return "";
+  }
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+std::string V0FinderTask::ToString(const FairTrackParam* pParam)
+{
+  std::stringstream msg;
+  msg << "Track parameters: r=(" << pParam->GetX() << ", " << pParam->GetY() << ", " << pParam->GetZ()
+      << "), tx=" << pParam->GetTx() << ", ty=" << pParam->GetTy() << ", q/p=" << pParam->GetQp()
+      << ", Covariance matrix:";
+  for (int i = 0; i < 5; ++i) {
+    msg << '\n';
+    for (int j = 0; j <= i; ++j) {
+      msg << std::setw(15) << pParam->GetCovariance(i, j) << ' ';
+    }
+  }
+  return msg.str();
+}
diff --git a/reco/KF/CbmKFV0FinderTask.h b/reco/KF/CbmKFV0FinderTask.h
new file mode 100644
index 0000000000..567c0e721f
--- /dev/null
+++ b/reco/KF/CbmKFV0FinderTask.h
@@ -0,0 +1,461 @@
+/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Sergei Zharko [committer] */
+
+/// \file   CbmV0FinderTask.h
+/// \brief  A FairTask for V0 candidates finding in mCBM
+/// \since  10.01.2025
+/// \author Sergei Zharko <s.zharko@gsi.de>
+
+// TODO: Provide a configurator to the class
+
+#pragma once
+
+#include "CbmEnumArray.h"
+#include "CbmKFParticleFinder.h"  // for the CbmKFParticleFinder::InverseChi2Prob()
+#include "CbmKFV0FinderQa.h"
+#include "CbmKFVertex.h"
+#include "CbmVertex.h"
+#include "FairTask.h"
+#include "KFParticleTopoReconstructor.h"
+
+#include <memory>
+
+class CbmEvent;
+class CbmGlobalTrack;
+class CbmStsTrack;
+class CbmTofHit;
+class TClonesArray;
+class FairTrackParam;
+class KFPTrackVector;
+class KFVertex;
+class KFParticleFinder;
+
+namespace cbm::kfp
+{
+  /// \class  V0FinderTask
+  /// \brief  A class to find V0 candidates in mCBM
+  class V0FinderTask : public FairTask {
+   public:
+    /// \struct QpAndBeta
+    /// \brief  Qp and beta container
+    struct QpAndBeta {
+      double fQp{std::numeric_limits<double>::signaling_NaN()};
+      double fQpVar{std::numeric_limits<double>::signaling_NaN()};
+      double fBeta{std::numeric_limits<double>::signaling_NaN()};
+      double fBetaVar{std::numeric_limits<double>::signaling_NaN()};
+    };
+
+    /// \struct DcaVector
+    /// \brief  A vector representation of DCA to target
+    struct DcaVector {
+      double fAbs{std::numeric_limits<double>::signaling_NaN()};  ///< Absolute value
+      double fX{std::numeric_limits<double>::signaling_NaN()};    ///< X-component of the unit-vector
+      double fY{std::numeric_limits<double>::signaling_NaN()};    ///< Y-component of the unit-vector
+    };
+
+    /// \enum   Counter
+    /// \brief  Enumeration of counters (for the entire run)
+    enum class ECounter : uint8_t
+    {
+      TracksTotal,                //< Total number of tracks
+      TracksSelected,             //< Tracks, which satisfy topology PID applicability
+      TracksInfiniteParam,        //< Tracks, which have infinite parameters
+      TracksWoTofHits,            //< Tracks, which have no TOF hits
+      TracksWNegativeTofHitTime,  //< Tracks, the last TOF hit of which has a negative time (it's time is less then the t0)
+      TracksWoStsHits,            //< Tracks, which have no STS hits
+      TracksWoPid,                //< Tracks, which has undefined PID
+      TracksWoMomentum,           //< Tracks, which has undefined PID
+      TracksWAtLeastOneTofHit,   //< Tracks with at least one tof hit
+      TracksWAtLeastTwoTofHits,  //< Tracks with at least two tof hits
+      TracksWithUnphysicalBeta,  //< Tracks with beta > 1
+      TracksW2Sts2Tof,           //< Tracks with at least two STS hits and two TOF hits
+      Pions,                     //< Number of pion-candidates
+      Protons,                   //< Number of proton-candidates
+      EventsTotal,               //< Total number of events
+      EventsLambdaCand,          //< Events with at least one pion and one proton candidate
+      KfpEventsLambdaCand,       //< Events with lambda-candidates in KF-particle
+      KfpLambdaCandidates,       //< Number of lambda-candidates
+      END
+    };
+
+    /// \enum  EPidApproach
+    /// \brief PID approach used
+    enum class EPidApproach : uint8_t
+    {
+      Topo,  //< Topological PID (DCA calculation for a track)
+      Mc     //< MC-truth PID
+    };
+
+    /// \enum  EProcessingMode
+    /// \brief Data processing mode
+    enum class EProcessingMode : uint8_t
+    {
+      TimeBased,  //< Time based simulation or real data
+      EventBased  //< Event based simulation
+    };
+
+    /// \enum   EPvUsageMode
+    /// \brief  Primary vertex finding/handling mode
+    enum class EPvUsageMode : uint8_t
+    {
+      Target,               //< Use target center as a primary vertex
+      Reconstructed,        //< Use reconstructed primary vertex
+      ReconstructSingle,    //< Reconstruct a single PV from given track vectors
+      ReconstructMultiple,  //< Reconstruct multiple PVs (in case of pile-up)
+      Mc                    //< Use MC-true primary vertex
+    };
+
+    /// \brief  Constructor
+    /// \param  verbose  Verbosity of the task
+    explicit V0FinderTask(int verbose = 1) : FairTask("V0FinderTask", verbose){};
+
+    /// \brief  Destructor
+    ~V0FinderTask() = default;
+
+    // Eliminate copy and move
+    V0FinderTask(const V0FinderTask&) = delete;
+    V0FinderTask(V0FinderTask&&)      = delete;
+    V0FinderTask& operator=(const V0FinderTask&) = delete;
+    V0FinderTask& operator=(V0FinderTask&&) = delete;
+
+
+    //* FairTask overriden methods
+
+    /// \brief  Executes the task
+    void Exec(Option_t*) override;
+
+    /// \brief  Action on the end of the run
+    void Finish() override;
+
+    /// \brief  Initializes the task
+    InitStatus Init() override;
+
+
+    //* Accessors
+
+    /// \brief  Mutable access to the KfParticleFinder of the run topology reconstructor
+    KFParticleFinder* GetKFParticleFinder() { return fpTopoReconstructorRun->GetKFParticleFinder(); }
+
+    /// \brief  Constant access to the KfParticleFinder of the run topology reconstructor
+    const KFParticleFinder* GetKFParticleFinder() const { return fpTopoReconstructorRun->GetKFParticleFinder(); }
+
+    /// \brief  Gets DCA to origin
+    const auto& GetTrackDcaToOrigin() const { return fvTrackDca; }
+
+
+    //* Task logic and preliminary selection cut setters
+
+    /// \brief  Sets processing mode (time-based/event-based)
+    void SetProcessingMode(EProcessingMode mode) { fProcessingMode = mode; }
+
+    /// \brief  Sets PID approach
+    void SetPidApproach(EPidApproach pid) { fPidApproach = pid; }
+
+    /// \brief  Sets PV finding mode
+    void SetPvFindingMode(EPvUsageMode mode) { fPvUsageMode = mode; }
+
+    /// \brief  Sets the MC flag (if MC information required)
+    void SetUseMc(bool bUseMc) { fbUseMc = bUseMc; }
+
+    /// \brief  Sets the flag: if the QA should be executed
+    void SetRunQa(bool bRunQa) { fbRunQa = bRunQa; }
+
+    /// \brief  Sets minimal pion DCA to primary vertex
+    /// \param  dca  DCA [cm]
+    void SetMinPionDca(double dca) { fMinPionDca = dca; }
+
+    /// \brief  Sets minimal proton DCA to primary vertex
+    /// \param  dca  DCA [cm]
+    void SetMinProtonDca(double dca) { fMinProtonDca = dca; }
+
+    /// \brief  Sets a file name for the QA
+    /// \param  fileName  Name of file
+    void SetQaOutputFileName(const TString& fileName) { fsQaOutputName = fileName; }
+
+    /// \brief  Assignes an uncertainty to the momentum measurement
+    /// \param  uncertainty  Relative uncertainty ( = sqrt(var(q/p)) / (q/p))
+    void SetQpAssignedUncertainty(double uncertainty) { fQpAssignedUncertainty = uncertainty; }
+
+    /// \brief  Sets an offset to t0
+    /// \param  offset  An offset [ns]
+    void SetTzeroOffset(double offset) { fTzeroOffset = offset; }
+
+    /// \brief  Special settings in the mixed-event analysis mode
+    /// \param  bMixedEvent  Flag: true - run in the mixed-event mode
+    // NOTE: The KFParticleFinder has its internal mixed-event analysis mode, but was not used in mCBM Lambda-analysis
+    //       the internal mixed-event mode can be selected using the following method:
+    //          fpTopoReconstructorRun->SetMixedEventAnalysis()
+    void SetMixedEventMode(bool bMixedEvent) { fbMixedEventMode = bMixedEvent; }
+
+
+    //* KFParticleFinder setters
+
+    /// \brief  Adds particle to reconstruction list
+    /// \param  pdg  A PDG code of the particle to be reconstructed
+    void AddDecayToReconstructionList(int pdg) { GetKFParticleFinder()->AddDecayToReconstructionList(pdg); }
+
+    /// \brief  Sets P-value cut to the topology reconstructor
+    /// \param  pVal  P-value cut
+    void SetPrimaryProbCut(double pVal)
+    {
+      constexpr int Ndf{2};
+      fpTopoReconstructorRun->SetChi2PrimaryCut(CbmKFParticleFinder::InversedChi2Prob(pVal, Ndf));
+    }
+
+    /// \brief  Sets cut on the distance between secondary tracks at the DCA point
+    /// \param  cut  Cut value [cm]
+    void SetMaxDistanceBetweenParticlesCut(float cut) { GetKFParticleFinder()->SetMaxDistanceBetweenParticlesCut(cut); }
+
+    /// \brief  Sets cut on the distance to the primary vertex from the decay vertex
+    /// \param  cut  Cut value [cm]
+    void SetLCut(float cut) { GetKFParticleFinder()->SetLCut(cut); }
+
+    /// \brief  Sets cut on \f$\chi^2_{prim}\f$ of each track for 2-daughter decays
+    /// \param  cut  Cut value
+    void SetChiPrimaryCut2D(float cut) { GetKFParticleFinder()->SetChiPrimaryCut2D(cut); }
+
+    /// \brief  Sets cut on \f$\chi^2_{geo}\f$ for 2-daughter decays
+    /// \param  cut  Cut value
+    void SetChi2Cut2D(float cut) { GetKFParticleFinder()->SetChi2Cut2D(cut); }
+
+    /// \brief  Sets cut on \f$l/\Delta l\f$ for 2-daughter decays
+    /// \param  cut  Cut value
+    void SetLdLCut2D(float cut) { GetKFParticleFinder()->SetLdLCut2D(cut); }
+
+    /// \brief  Sets cuts on selection of secondary and primary candidates
+    /// \param  sigmaMass  \f$\sigma_{M}\f$ [GeV/c2]
+    /// \param  chi2Topo   \f$\chi^2_{topo}\f$
+    /// \param  ldl        \f$l/\Delta l\f$
+    void SetSecondaryCuts(float sigmaMass, float chi2Topo, float ldl)
+    {
+      GetKFParticleFinder()->SetSecondaryCuts(sigmaMass, chi2Topo, ldl);
+    }
+
+    /// \brief  Sets \f$l/\Delta l\f$ cut for \f$\Xi\f$ and \f$\Omega\f$
+    /// \param  cut  Cut value
+    void SetLdLCutXiOmega(float cut) { GetKFParticleFinder()->SetLdLCutXiOmega(cut); }
+
+    /// \brief  Sets \f$\chi^2_{topo}\f$ cut for \f$\Xi\f$ and \f$\Omega\f$
+    /// \param  cut  Cut value
+    void SetChi2TopoCutXiOmega(float cut) { GetKFParticleFinder()->SetChi2TopoCutXiOmega(cut); }
+
+    /// \brief  Sets \f$\chi^2_{geo}\f$ cut for \f$\Xi\f$ and \f$\Omega\f$
+    /// \param  cut  Cut value
+    void SetChi2CutXiOmega(float cut) { GetKFParticleFinder()->SetChi2CutXiOmega(cut); }
+
+    /// \brief  Sets \f$\chi^2_{topo}\f$ cut for resonances
+    /// \param  cut  Cut value
+    void SetChi2TopoCutResonances(float cut) { GetKFParticleFinder()->SetChi2TopoCutResonances(cut); }
+
+    /// \brief  Sets \f$\chi^2_{geo}\f$ cut for resonances
+    /// \param  cut  Cut value
+    void SetChi2CutResonances(float cut) { GetKFParticleFinder()->SetChi2CutResonances(cut); }
+
+    /// \brief  Sets the cut on transverse momentum of each daughter track of low mass vector mesons
+    /// \param  cut  Cut value [GeV/c]
+    void SetPtCutLMVM(float cut) { GetKFParticleFinder()->SetPtCutLMVM(cut); }
+
+    /// \brief  Sets the cut on momentum of each daughter track of low mass vector mesons in dimuon channel
+    /// \param  cut  Cut value [GeV/c]
+    void SetPCutLMVM(float cut) { GetKFParticleFinder()->SetPCutLMVM(cut); }
+
+    /// \brief  Sets the cut on transverse momentum of each daughter track of \f$J/\psi\f$
+    /// \param  cut  Cut value [GeV/c]
+    void SetPtCutJPsi(float cut) { GetKFParticleFinder()->SetPtCutJPsi(cut); }
+
+    /// \brief  Sets the cut on transverse momentum of each daughter track of open charm particles
+    /// \param  cut  Cut value [GeV/c]
+    void SetPtCutCharm(float cut) { GetKFParticleFinder()->SetPtCutCharm(cut); }
+
+    /// \brief  Sets cut on \f$\chi^2_{prim}\f$ of each track for open charm particles
+    /// \param  cut  Cut value [GeV/c]
+    void SetChiPrimaryCutCharm(float cut) { GetKFParticleFinder()->SetChiPrimaryCutCharm(cut); }
+
+    /// \brief  Sets \f$l/\Delta l\f$ cut for open charm with >=3 daughters
+    /// \param  cut  Cut value
+    void SetLdLCutCharmManybodyDecays(float cut) { GetKFParticleFinder()->SetLdLCutCharmManybodyDecays(cut); }
+
+    /// \brief  Sets \f$\chi^2_{topo}\f$ cut for open charm with >=3 daughters
+    /// \param  cut  Cut value
+    void SetChi2TopoCutCharmManybodyDecays(float cut) { GetKFParticleFinder()->SetChi2TopoCutCharmManybodyDecays(cut); }
+
+    /// \brief  Sets \f$\chi^2_{geo}\f$ cut for open charm with >=3 daughters
+    /// \param  cut  Cut value
+    void SetChi2CutCharmManybodyDecays(float cut) { GetKFParticleFinder()->SetChi2CutCharmManybodyDecays(cut); }
+
+    /// \brief  Sets \f$l/\Delta l\f$ cut for open charm with 2 daughters
+    /// \param  cut  Cut value
+    void SetLdLCutCharm2D(float cut) { GetKFParticleFinder()->SetLdLCutCharm2D(cut); }
+
+    /// \brief  Sets \f$\chi^2_{topo}\f$ cut for open charm with 2 daughters
+    /// \param  cut  Cut value
+    void SetChi2TopoCutCharm2D(float cut) { GetKFParticleFinder()->SetChi2TopoCutCharm2D(cut); }
+
+    /// \brief  Sets \f$\chi^2_{geo}\f$ cut for open charm with 2 daughters
+    /// \param  cut  Cut value
+    void SetChi2CutCharm2D(float cut) { GetKFParticleFinder()->SetChi2CutCharm2D(cut); }
+
+
+    //* Static helper methods
+
+    /// \brief  String representation of processing mode
+    /// \param  mode  Data processing mode
+    static std::string ToString(EProcessingMode mode);
+
+    /// \brief  String representation of PID approach
+    /// \param  pid  PID approach
+    static std::string ToString(EPidApproach pid);
+
+    /// \brief  String representation of the PV finding mode
+    /// \param  pvMode  PV finding mode
+    static std::string ToString(EPvUsageMode pvMode);
+
+    /// \brief  String representation of
+    /// \param  pParam  Parameters of the track
+    static std::string ToString(const FairTrackParam* pParam);
+
+    /// \brief   Checks track parameter validity
+    /// \param   pParam  Pointer to a track parameter
+    /// \return  true  The parameters are ok
+    /// \return  false The parameters are invalid
+    static bool CheckTrackParam(const FairTrackParam* pParam);
+
+   private:
+    //* Auxilary internal methods
+
+    /// \brief   Assigns momentum to a global track
+    /// \param   pTrack  Pointer to a global track
+    /// \param   pdg     PID hypothesis
+    /// \return  true    Momentum is set
+    /// \return  false   Momentum is not set (the track must be rejected)
+    bool AssignMomentum(CbmGlobalTrack* pTrack, int pdg);
+
+    /// \brief  Estimates distance to the origin
+    /// \param  pStsTrack  STS track
+    /// \return Vector of the DCA {abs, ex, ey}
+    DcaVector EstimateDcaToOrigin(const CbmStsTrack* pTrack) const;
+
+    /// \brief  Estimates q/p of the track, using one TOF hit (Norbert's method)
+    /// \param  pTofHit  Pointer to a TOF hit
+    /// \param  pdg      PID hypothesis
+    /// \note   Gives smaller statistical uncertainty, but has a systematic effect (to be studied with MC)
+    QpAndBeta EstimateQp(const CbmTofHit* pTofHit, int pdg) const;
+
+    /// \brief  Makes a KF-particle track vector
+    /// \param  vpTracks       Vector of pointers to global tracks
+    /// \param  vTrackIds      Vector of track indices
+    /// \param  vChi2ToPv      Vector of chi2 of assigning a track as primary
+    /// \param  bAtFirstPoint  true: the first track point is used, false: the last track point is used
+    /// \note   No track selection is performed
+    KFPTrackVector MakeKfpTrackVector(const std::vector<const CbmGlobalTrack*>& vpTracks,
+                                      const std::vector<int>& vTrackIds, const std::vector<float>& vChi2ToPv,
+                                      bool bAtFirstPoint) const;
+
+    /// \brief   Makes a KF vertex
+    /// \param   x  x-coordinate of PV [cm]
+    /// \param   y  y-coordinate of PV [cm]
+    /// \param   z  z-coordinate of PV [cm]
+    KFVertex MakeKfpPrimaryVertex(float x, float y, float z) const;
+
+    /// \brief  Infers PID hypothesis for a track using track topology
+    /// \param  dca  An absolute value of DCA to the PV estimation
+    /// \return PDG code of the particle
+    /// \return -2  If the PID analysis cannot be performed for a track of a given quality
+    int InferTrackPidTopo(double dca) const;
+
+    /// \brief  Processes one event
+    /// \tparam UseEvent  Explicit flag of using the CbmEvent pointer to access data
+    /// \param  pEvent  Pointer to event
+    /// \return true    Event is accepted for V0 reconstruction
+    /// \return false   Event is rejected for V0 reconstruction
+    /// \note   In the event-based mode, the ProcessEvent(nullptr) must be called explicitly
+    template<bool UseEvent>
+    bool ProcessEvent(const CbmEvent* pEvent);
+
+    /// \brief  Selects/rejects a track
+    /// \param  pTrack  A pointer to global track
+    /// \param  iTrk    Global index of the track
+    /// \return true:  Track is selected
+    /// \return false: Track is rejected
+    /// \note   Modifies the track, assigning momentum and PID hypothesis
+    ///
+    /// In this function the preliminary selection of tracks is performed. The selection criteria:
+    ///  1)  the track has at least two STS hits for estimation of the PDG codes (pi-, proton, or primary (kaon PDG))
+    ///  2)  the track has at least one TOF hit for estimation of its momentum
+    ///      2.1) tracks with the TOF hit times (t - t0) < 0 are rejected
+    ///      2.2) tracks with the beta > 1 are rejected
+    ///  3)  the track has defined parameters in the first hit
+    ///      NOTE: this cut was used, because the tracks with negative TOF times were not rejected. Now it seems,
+    ///            that the track parameters are always defined.
+    // TODO:  Replace with a functor (a function from a separate FairTask)
+    bool SelectTrack(CbmGlobalTrack* pTrack, int iTrk);
+
+    /// \brief  Shifts TOF hits to the t0 estimation
+    /// \param  pEvent  Pointer to the event
+    /// \return value of estimated t0 [ns]
+    double ShiftTofHitsToTzero(const CbmEvent* pEvent);
+
+    /// \brief  Stores particles, reconstructed in event to the run topology reconstructor
+    void StoreParticles();
+
+
+    //* Task constants (To be moved to a configuration)
+    static constexpr int kPrimaryPdg{321};        ///< PID hypothesis of primary tracks (kaons?)
+    static constexpr int kUndefPdg{-2};           ///< Undefined value, such tracks will be skipped
+    static constexpr float kChi2PvPrimThrsh{3.};  ///< Chi2 threshold of assigning tracks as primaries in PV reco
+
+    //* Physical constants
+    static constexpr double kPionMass{0.13957039};      ///< Pion mass [GeV/c2]
+    static constexpr double kProtonMass{0.938272088};   ///< Proton mass [GeV/c2]
+    static constexpr double kSpeedOfLight{29.9792458};  ///< Speed of light [cm/ns]
+
+    //* Different run-time flags;
+    double fTzeroOffset{0.};             ///< Offset for T0
+    double fMinPionDca{1.5};             ///< Minimum DCA to PV for pions
+    double fMinProtonDca{0.5};           ///< Minimum DCA to PV for protons
+    double fQpAssignedUncertainty{0.1};  ///< Assigned relative uncertainty for q/p estimation
+    int fPrimaryAssignedPdg{321};        ///< Assigned PDG hypothesis for primary particles
+
+    //* Input data
+    TClonesArray* fpBrRecoEvents{nullptr};
+    TClonesArray* fpBrGlobalTracks{nullptr};
+    TClonesArray* fpBrStsTracks{nullptr};
+    TClonesArray* fpBrTrdTracks{nullptr};
+    TClonesArray* fpBrTofTracks{nullptr};
+    TClonesArray* fpBrStsHits{nullptr};
+    TClonesArray* fpBrTrdHits{nullptr};
+    TClonesArray* fpBrTofHits{nullptr};
+    CbmVertex* fpBrPrimaryVertex{nullptr};
+
+    //* Temporary data and auxilary variables (per FairTask::Exec() call)
+    std::vector<DcaVector> fvTrackDca;  ///< Track DCA vector [n selected tracks]
+    std::unique_ptr<CbmVertex> fpOrigin{
+      std::make_unique<CbmVertex>()};  ///< Origin (e.g., can be either reconstructed PV or target)
+
+    //* KFParticleFinder utilities
+    /// \brief Main topology reconstructor
+    // Main topology reconstructor
+    std::unique_ptr<KFParticleTopoReconstructor> fpTopoReconstructorRun{
+      std::make_unique<KFParticleTopoReconstructor>()};
+    std::unique_ptr<KFParticleTopoReconstructor> fpTopoReconstructorEvent{
+      std::make_unique<KFParticleTopoReconstructor>()};
+
+    //* Control flags
+    EProcessingMode fProcessingMode{EProcessingMode::TimeBased};  ///< Processing mode
+    EPidApproach fPidApproach{EPidApproach::Topo};                ///< PID approach used
+    EPvUsageMode fPvUsageMode{EPvUsageMode::Target};              ///< Primary vertex mode
+    bool fbMixedEventMode{false};                                 ///< Run in a mix-event mode
+    bool fbUseMc{false};                                          ///< Run using MC-information
+    bool fbRunQa{false};                                          ///< Run QA
+    bool fbUsePvChi2Selection{false};                             ///< Select
+
+    //* QA and monitoring variables
+    cbm::core::EnumArray<ECounter, size_t> fCounters{{0}};  ///< Counters per run
+    std::unique_ptr<V0FinderQa> fpQa{nullptr};              ///< If QA is processed
+    TString fsQaOutputName{"./V0FinderQa.root"};            ///< Output QA name
+
+
+    ClassDefOverride(V0FinderTask, 0);
+  };
+}  // namespace cbm::kfp
diff --git a/reco/KF/KFParticleInterface.cmake b/reco/KF/KFParticleInterface.cmake
index d5cd174857..7f594ec0a9 100644
--- a/reco/KF/KFParticleInterface.cmake
+++ b/reco/KF/KFParticleInterface.cmake
@@ -13,6 +13,8 @@ CbmKFParticleFinder.cxx
 CbmKFParticleFinderPID.cxx
 CbmKFParticleFinderQa.cxx
 CbmKFParticleInterface.cxx
+CbmKFV0FinderTask.cxx
+CbmKFV0FinderQa.cxx
 )
 
 If(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
diff --git a/reco/KF/KFParticleInterfaceLinkDef.h b/reco/KF/KFParticleInterfaceLinkDef.h
index dedd55f91f..f04b91c6bf 100644
--- a/reco/KF/KFParticleInterfaceLinkDef.h
+++ b/reco/KF/KFParticleInterfaceLinkDef.h
@@ -12,5 +12,7 @@
 #pragma link C++ class CbmKFParticleFinderPID + ;
 #pragma link C++ class CbmKFParticleFinderQa + ;
 #pragma link C++ class CbmKFParticleInterface + ;
+#pragma link C++ class cbm::kfp::V0FinderTask + ;
+#pragma link C++ class cbm::kfp::V0FinderQa + ;
 
 #endif
diff --git a/reco/L1/CbmCaTimeSliceReader.cxx b/reco/L1/CbmCaTimeSliceReader.cxx
index 0655131131..0a41dd11d3 100644
--- a/reco/L1/CbmCaTimeSliceReader.cxx
+++ b/reco/L1/CbmCaTimeSliceReader.cxx
@@ -145,6 +145,7 @@ try {
   InitHitBranch(ca::EDetectorID::kMuch, "MuchPixelHit");
   InitHitBranch(ca::EDetectorID::kTrd, "TrdHit");
   if (!InitHitBranch(ca::EDetectorID::kTof, "TofCalHit")) {
+    LOG(warn) << "TimeSliceReader: TofCalHit branch not found, trying to get TofHit branch of uncalibrated hits";
     InitHitBranch(ca::EDetectorID::kTof, "TofHit");
   }
 
diff --git a/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.cxx b/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.cxx
index 38b304fa67..b84a2d8a3e 100644
--- a/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.cxx
+++ b/reco/eventbuilder/digis/CbmAlgoBuildRawEvents.cxx
@@ -732,8 +732,9 @@ bool CbmAlgoBuildRawEvents::SetBmonEventTime(CbmEvent* event)
       eventTime = std::min(pDigi->GetTime(), eventTime);
   }
 
-  if (timeSet)
+  if (timeSet) {
     event->SetStartTime(eventTime);
+  }
   else
     LOG(warning) << "CbmAlgoBuildRawEvents::SetBmonEventTime : failed";
 
diff --git a/reco/tasks/CbmTaskInspectDigiTimeslice.cxx b/reco/tasks/CbmTaskInspectDigiTimeslice.cxx
index 169d3cbf0e..bc8d30d69a 100644
--- a/reco/tasks/CbmTaskInspectDigiTimeslice.cxx
+++ b/reco/tasks/CbmTaskInspectDigiTimeslice.cxx
@@ -4,6 +4,8 @@
 
 #include "CbmTaskInspectDigiTimeslice.h"
 
+#include "CbmStsAddress.h"
+
 #include <FairRootManager.h>
 #include <Logger.h>
 
-- 
GitLab