From 05951078d7fb66290e44c950b8c3414346af29fa Mon Sep 17 00:00:00 2001
From: "s.zharko@gsi.de" <s.zharko@gsi.de>
Date: Thu, 23 Jan 2025 19:57:01 +0100
Subject: [PATCH] reco_mcbm.sh and KFPF lambda in mCBM:

1. reco_mcbm.sh:  New options (separate directory for different steps, lambda in mCBM, retrieving a tsa file name from and index list)
2. reco_mcbm_job.sh:  Settings to run reco_mcbm.sh from the box on virgo (vs. runID and jobID)
3. cbm::kfp::V0FinderTask: minor bug fixes
---
 macro/beamtime/mcbm2024/mcbm_event_reco_L1.C |   2 +-
 macro/beamtime/mcbm2024/reco_mcbm.sh         | 324 ++++++++++++++++---
 macro/beamtime/mcbm2024/reco_mcbm_job.sh     | 131 ++++++++
 macro/mcbm/mcbm_hadron_kfp_ana.C             |   7 +-
 reco/KF/CbmKFV0FinderQa.cxx                  |   4 +
 reco/KF/CbmKFV0FinderQa.h                    |   2 +-
 reco/KF/CbmKFV0FinderTask.cxx                |  23 +-
 reco/KF/CbmKFV0FinderTask.h                  |   1 +
 reco/L1/CbmL1.cxx                            |   9 +-
 9 files changed, 456 insertions(+), 47 deletions(-)
 create mode 100755 macro/beamtime/mcbm2024/reco_mcbm_job.sh

diff --git a/macro/beamtime/mcbm2024/mcbm_event_reco_L1.C b/macro/beamtime/mcbm2024/mcbm_event_reco_L1.C
index 7be5360c69..67e2848c21 100644
--- a/macro/beamtime/mcbm2024/mcbm_event_reco_L1.C
+++ b/macro/beamtime/mcbm2024/mcbm_event_reco_L1.C
@@ -727,7 +727,7 @@ Bool_t mcbm_event_reco_L1(UInt_t uRunId                   = 3105,
 
 
   // -----   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 51c9e57176..cd5ac8a665 100755
--- a/macro/beamtime/mcbm2024/reco_mcbm.sh
+++ b/macro/beamtime/mcbm2024/reco_mcbm.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+# Copyright (C) 2024-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
 # SPDX-License-Identifier: GPL-3.0-only
 # Authors: Sergei Zharko [committer], Pierre-Alain Loizeau
 #
@@ -9,7 +9,7 @@
 # @author Sergei Zharko <s.zharko@gsi.de>
 #
 # **********************************************************************************************************************
-# ***                                      User Manual (v. 0.0.2, 05.07.2024)                                        ***
+# ***                                      User Manual (v. 0.1.0, 22.01.2025)                                        ***
 # **********************************************************************************************************************
 #
 # 1. Definitions
@@ -24,22 +24,44 @@
 # 2. Introduction
 #
 #   This script aims to provide a generic data reconstruction scenario in mCBM, which includes:
-#     - setup files generation:  [--setup]
-#     - TSA file unpacking       [--unpack]
-#     - reconstruction           [--reco]
-#     - main QA                  [--qa]
-#     - reconstruciton QA (Alex) [--qa-module]
+#     - setup files generation:      [--setup]
+#     - setup files generation:      [--setup-only]  (Special flag, to be called, if the other steps must not be selected)
+#     - TSA file unpacking           [--unpack]
+#     - reconstruction               [--reco]
+#     - main QA                      [--qa]
+#     - reconstruciton QA (Alex)     [--qa-module]
+#     - KF Particle Finder (Lambda)  [--kfpf]
 #
 #   Required options:
 #     --tsa <path>           Path to the input TSA file
 #                             NOTE: must contain a run index as a first integer in its base name.
+#     OR
+#
+#     --tsa-index-file  <file>  A path to a text file with a list of TSAs. The line of the file equals to the job number
+#
+#     OR
+#
+#     --run <runID>  Run identifier, BUT ONLY, IF ONLY THE SETUP IS NEEDED (--setup-only)
+#
 #   Auxiliary options:
 #     -n [--nts] <N_TS>  Number of timeslices to procede
 #     --setup, --unpack, --reco, --qa, --qa-module
-#     --data-dir             Path to output directory
+#     --data-dir     <path>  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
 #
+#     --setup-dir    <path>  Path to the setup output
+#     --unpack-dir   <path>  Path to the unpacking output
+#     --reco-dir     <path>  Path to the reconstruction output
+#     --qa-dir       <path>  Path to the QA output
+#     --kfpf-dir     <path>  Path to the KFPF output
+#     NOTE: if any of these parameters are not provided, the corresponding data will be stored to the <top_dir>. If the 
+#           corresponding path is a relative path (!=realpath), the <top_dir> will be selected is the parent directory
+#
+#   Options for running on the batch farm:
+#     -j [--job] <jobId>        Index of the job (default == 1)
+#        
+#
 # 3. File names involved:
 #   <geo>:       Input geometry file:  <top_dir>/<setup>.geo.root
 #   <par>:       Input parameter file:  <top_dir>/<setup>.par.root --copy--> <top_dir>/<label>.par.root
@@ -48,6 +70,8 @@
 #   <reco>:      Reconstruction output: <top_dir>/<label>.reco.root --ln--> <top_dir>/<label>.rec.root
 #   <qa>:        Main QA output: <top_dir>/<label>.qa.root
 #   <qa-module>: Reconstruction module QA: <top_dir>/<label>.rqa.root
+#   <kfp.ana>:   Output from the lambda analysis: <top_dir>/<label>.kfp.ana.root
+#                [optional] QA file:  <top_dir>/<label>.qa.kfp.ana.root
 #
 # 4. Knwon missing features
 #
@@ -96,6 +120,7 @@ RECO_EvB=1
 RECO_CA=1
 RECO_PV=1
 RECO_QA=0
+LAMBDA_MIXED_EVENT=0
 
 
 # ----------------------------------------------------------------------------------------------------------------------
@@ -107,37 +132,67 @@ RECO_QA=0
 # *************************
 
 # ----- Execution steps
+I_WANT_ONLY_SETUP=0
 DO_CREATE_SETUP=0
 DO_UNPACK=0
 DO_RECO=0
 DO_QA=0         # Main QA Macro
 DO_QA_MODULE=0  # QA Macro by Alexandru
+DO_LAMBDA=0     # Lambda analysis using KFParticleFinder
+STEPS_TO_PRINT=""
+
 ONLINE_PAR=${VMCWORKDIR}/parameters/online
 
+
+# ----- Subdirectories
+DIR_SETUP=""
+DIR_UNPACK=""
+DIR_RECO=""
+DIR_QA=""
+DIR_KFPF=""
+
 # ----- Run information
 RUN=-1
+RUN_IF_ONLY_SETUP_NEEDED=0
 TSA=
 N_TS=-1
+TSA_INDEX=0
+JOB_ID=1
 while [[ $# -gt 0 ]]; do
   case ${1} in
     --setup )
       DO_CREATE_SETUP=1
+      STEPS_TO_PRINT="${STEPS_TO_PRINT} --setup"
       ;;
     --unpack )
       DO_UNPACK=1
+      STEPS_TO_PRINT="${STEPS_TO_PRINT} --unpack"
       ;;
     --reco )
       DO_RECO=1
+      STEPS_TO_PRINT="${STEPS_TO_PRINT} --reco"
       ;;
     --qa )
       DO_QA=1
+      STEPS_TO_PRINT="${STEPS_TO_PRINT} --qa"
       ;;
     --qa-module )
       DO_QA_MODULE=1
+      STEPS_TO_PRINT="${STEPS_TO_PRINT} --qa-module"
+      ;;
+    --kfpf )
+      DO_LAMBDA=1
+      STEPS_TO_PRINT="${STEPS_TO_PRINT} --kfpf"
       ;;
     --tsa )
       TSA=${2}
       ;;
+    --tsa-index-file )
+      TSA_INDEX=${2}
+      ;;
+    -j | --job )
+      JOB_ID=${2}
+      ;;
     --param-online )
       ONLINE_PAR=${2}
       ;;
@@ -147,10 +202,105 @@ while [[ $# -gt 0 ]]; do
     --data-dir )
       DATA_TOP_DIR=${2}
       ;;
+    --setup-dir )
+      DIR_SETUP=${2}
+      ;;
+    --unpack-dir )
+      DIR_UNPACK=${2}
+      ;;
+    --reco-dir )
+      DIR_RECO=${2}
+      ;;
+    --qa-dir )
+      DIR_QA=${2}
+      ;;
+    --kfpf-dir )
+      DIR_KFPF=${2}
+      ;;
+    --setup-only )
+      I_WANT_ONLY_SETUP=1
+      ;;
+    --run )
+      RUN_IF_ONLY_SETUP_NEEDED=${2}
+      ;;
   esac
   shift
 done
 
+
+# ------ Re-define the paths to step directories
+DATA_TOP_DIR=$(realpath -m ${DATA_TOP_DIR})
+
+if [[ -z ${DIR_SETUP} ]]; then 
+  DIR_SETUP=${DATA_TOP_DIR}
+else
+  DIR_SETUP=$(realpath -m ${DIR_SETUP})
+fi
+
+if [[ -z ${DIR_UNPACK} ]]; then 
+  DIR_UNPACK=${DATA_TOP_DIR}
+else
+  DIR_UNPACK=$(realpath -m ${DIR_UNPACK})
+fi
+
+
+if [[ -z ${DIR_RECO} ]]; then 
+  DIR_RECO=${DATA_TOP_DIR}
+else
+  DIR_RECO=$(realpath -m ${DIR_RECO})
+fi
+
+if [[ -z ${DIR_QA} ]]; then 
+  DIR_QA=${DATA_TOP_DIR}
+else
+  DIR_QA=$(realpath -m ${DIR_QA})
+fi
+
+if [[ -z ${DIR_KFPF} ]]; then 
+  DIR_KFPF=${DATA_TOP_DIR}
+else
+  DIR_KFPF=$(realpath -m ${DIR_KFPF})
+fi
+
+printf "\n"
+printf "\n"
+printf "\t********************************************************************************************\n"
+printf "\t***                                                                                      ***\n"
+printf "\t***    --- The Compressed Baryonic Matter Experiment: Data Reconstruction Routine ---    ***\n"
+printf "\t***                                                                                      ***\n"
+printf "\t********************************************************************************************\n\n\n"
+
+
+
+printf "***\n"
+printf "***  Data directories:\n"
+printf "***\n\n"
+printf "\tSetup:                  ${DIR_SETUP}\n"
+printf "\tUnpacking output:       ${DIR_UNPACK}\n"
+printf "\tReconstruction output:  ${DIR_RECO}\n"
+printf "\tQA output:              ${DIR_QA}\n"
+printf "\tKFPF output:            ${DIR_KFPF}\n\n\n"
+
+# ----- Redifine the TSA input, if the TSA index and job were provided
+#
+# TODO: Add check on the integer for JOB_ID
+if [[ ${TSA_INDEX} != 0 && ! -z ${TSA_INDEX} ]]; then
+  if [[ ${JOB_ID} -le 0 ]]; then 
+    exit 100;  # illegal job number
+  fi
+  TSA=$(sed -n "${JOB_ID}p" "${TSA_INDEX}")
+fi
+printf "***\n"
+printf "***  Input:\n"
+printf "***\n\n"
+printf "\tTSA:           ${TSA}\n"
+printf "\tJOB:           ${JOB_ID}\n"
+printf "\tROUTINE STEPS: ${STEPS_TO_PRINT}\n"
+printf "\n\n"
+
+
+
+
 # ----- Check the environment
 #
 if [[ -z "${VMCWORKDIR}" ]]; then
@@ -177,19 +327,48 @@ fi
 
 printf "I- Online binary path: %s, with parameters: %s\n" ${ONLINE_BINARY} ${ONLINE_PAR}
 
+
 # ----- Check the TSA input and retrieve the run index
-if [[ -z ${TSA} ]]; then
-  printf "E- TSA input file is not defined\n"
-  exit 3
+if [[ ${I_WANT_ONLY_SETUP} -ne 1 ]]; then
+  if [[ -z ${TSA} ]]; then
+    printf "E- TSA input file is not defined\n"
+    exit 3
+  fi
+  TSA_INFO=($(basename $(echo ${TSA}) | grep -oE '[0-9]*'))
+
+  RUN=${TSA_INFO[0]}  # Implying, that TSA basename contains run index as the first integer
+fi
+
+
+if [[ ${I_WANT_ONLY_SETUP} -eq 1 ]]; then 
+  DO_CREATE_SETUP=1
+  DO_UNPACK=0
+  DO_RECO=0
+  DO_QA=0
+  DO_QA_MODULE=0
+  DO_LAMBDA=0
+  STEPS_TO_PRINT=" --setup"
+  if [[ ${RUN} -lt 0 ]]; then 
+    RUN=${RUN_IF_ONLY_SETUP_NEEDED}
+  fi
 fi
-TSA_INFO=($(basename $(echo ${TSA}) | grep -oE '[0-9]*'))
 
-RUN=${TSA_INFO[0]}  # Implying, that TSA basename contains run index as the first integer
 if [[ ${RUN} -lt 0 ]]; then
   printf "E- Run number is undefined, please try again with option -r <RUN> or --run <RUN>\n"
 fi
 
-mkdir -p ${DATA_TOP_DIR}
+if [[ DO_UNPACK -eq 1 ]]; then
+  mkdir -p ${DIR_UNPACK}
+fi
+if [[ DO_RECO -eq 1 ]]; then 
+  mkdir -p ${DIR_RECO}
+fi
+if [[ DO_QA -eq 1 && DO_QA_MODULE -eq 1 ]]; then
+  mkdir -p ${DIR_QA}
+fi 
+if [[ DO_LAMBDA -eq 1 ]]; then
+  mkdir -p ${DIR_KFPF}
+fi
 
 # ----- Filenames definition
 TSA_INP=${TSA}
@@ -197,10 +376,14 @@ MACRO_SETUP="${VMCWORKDIR}/macro/run/create_mcbm_geo_setup.C"
 MACRO_RECO="${VMCWORKDIR}/macro/beamtime/mcbm2022/mcbm_event_reco_L1.C"
 MACRO_QA="${VMCWORKDIR}/macro/mcbm/mcbm_qa.C"
 MACRO_QA_MODULE="${VMCWORKDIR}/macro/qa/run_recoQa.C"
+MACRO_KFPF="${VMCWORKDIR}/macro/mcbm/mcbm_hadron_kfp_ana.C"
 SETUP_NAME=
 
-# ----- !!! Setting selections vs.
-
+# ----- !!! Setting selections vs. run number 
+# FIXME: map of the run number range to a particular setup geometry tag must be stored in a text (yaml/ascii/json/...) file
+#        in the cbmroot_parameters or cbmroot_geometry. Until then the setup tags are defined here explicitly.
+#
+#  
 if [[ ${RUN} -ge 2350 && ${RUN} -le 2397 ]]; then
   SETUP_NAME="mcbm_beam_2022_05_23_nickel"
   MACRO_RECO="${VMCWORKDIR}/macro/beamtime/mcbm2022/mcbm_event_reco_L1.C"
@@ -222,10 +405,21 @@ else
 fi
 
 # -----
-# File
-FILE_LABEL=$(printf $(basename ${TSA_INP}) | cut -f 1 -d '.')
-OUT_DIGI="${DATA_TOP_DIR}/${FILE_LABEL}.digi.out"
-INP_FILE="${DATA_TOP_DIR}/${FILE_LABEL}.digi.root"
+# Data file names
+FILE_LABEL="test"
+if [[ ${I_WANT_ONLY_SETUP} -ne 1 ]]; then
+  FILE_LABEL=$(printf $(basename ${TSA_INP}) | cut -f 1 -d '.')
+fi
+DIGI_ONLINE_FILE="${DIR_UNPACK}/${FILE_LABEL}.digi.out"
+DIGI_OFFLINE_FILE="${DIR_UNPACK}/${FILE_LABEL}.digi.root"
+SETUP_PAR_FILE="${DIR_SETUP}/${SETUP_NAME}.par.root"
+SETUP_GEO_FILE="${DIR_SETUP}/${SETUP_NAME}.geo.root"
+RECO_PAR_FILE="${DIR_RECO}/${FILE_LABEL}.par.root"
+RECO_FILE="${DIR_RECO}/${FILE_LABEL}.rec.root"
+KF_SETUP_FILE="${DIR_RECO}/${FILE_LABEL}.rec.kf.setup"
+KF_MATERIAL="${DIR_RECO}/${SETUP_NAME}."
+CA_PAR_FILE="${DIR_RECO}/${FILE_LABEL}.rec.ca.par"
+
 
 # -----
 # Log Files
@@ -234,6 +428,7 @@ UNPACK_LOG="run_mcbm_unpack.log"
 RECO_LOG="run_mcbm_reco.log"
 RECO_QA_LOG="run_mcbm_reco_qa.log"
 QA_LOG="run_mcbm_qa.log"
+KFPF_LOG="run_mcbm_kfpf.log"
 
 #  *********************
 #  ** Steps execution **
@@ -241,7 +436,7 @@ QA_LOG="run_mcbm_qa.log"
 
 # ----- Create setup files
 if [[ ${DO_CREATE_SETUP} -eq 1 ]]; then
-  root -b -l -q ${MACRO_SETUP}"(${RUN}, \"${DATA_TOP_DIR}\")" &> ${SETUP_LOG}
+  root -b -l -q ${MACRO_SETUP}"(${RUN}, \"${DIR_SETUP}\")" &> ${SETUP_LOG}
   cat ${SETUP_LOG}
 
   if [[ (1 -ne $(grep -c " Test passed" "${SETUP_LOG}")) || (1 -ne $(grep -c " All ok " "${SETUP_LOG}")) ]]; then
@@ -252,14 +447,18 @@ if [[ ${DO_CREATE_SETUP} -eq 1 ]]; then
   rm ${SETUP_LOG}
 fi
 
+if [[ ${I_WANT_ONLY_SETUP} -eq 1 ]]; then
+  exit 0
+fi
+
 # ----- Run unpacker
 if [[ ${DO_UNPACK} -eq 1 ]]; then
   # TODO: Define unpack options for different setups
-  ${ONLINE_BINARY} --steps Unpack -i ${TSA_INP} -r ${RUN} -p ${ONLINE_PAR} -O DigiTimeslice -o ${OUT_DIGI} -s STS BMON TOF TRD RICH -n ${N_TS}
+  ${ONLINE_BINARY} --steps Unpack -i ${TSA_INP} -r ${RUN} -p ${ONLINE_PAR} -O DigiTimeslice -o ${DIGI_ONLINE_FILE} -s STS BMON TOF TRD RICH -n ${N_TS}
 
-  root -l -b -q ${VMCWORKDIR}/macro/run/run_inspect_digi_timeslice.C"(\"${OUT_DIGI}\", \"${INP_FILE}\")" &> ${UNPACK_LOG}
+  root -l -b -q ${VMCWORKDIR}/macro/run/run_inspect_digi_timeslice.C"(\"${DIGI_ONLINE_FILE}\", \"${DIGI_OFFLINE_FILE}\")" &> ${UNPACK_LOG}
   cat ${UNPACK_LOG}
-  rm ${OUT_DIGI}
+  rm ${DIGI_ONLINE_FILE}
 
   if [[ 1 -ne $(grep -c "Macro finished successfully." "${UNPACK_LOG}") ]]; then
     printf "\nUnpacked data file conversion of %s failed, stopping there\n" "${TSA_INP}"
@@ -272,11 +471,26 @@ fi
 # ----- Run reconstruction
 if [[ ${DO_RECO} -eq 1 ]]; then
   UNP_FILE_ID=-1
-  cp "${DATA_TOP_DIR}/${SETUP_NAME}.par.root" "${DATA_TOP_DIR}/${FILE_LABEL}.par.root"
-  DATA_PREF="${DATA_TOP_DIR}"
-  PARS="${RUN},${N_TS},\"${DATA_PREF}\",\"${DATA_PREF}\",${UNP_FILE_ID},${RECO_MVD},${RECO_STS},${RECO_TRD}"
+  RECO_DIGI_INPUT="${DIR_RECO}/${FILE_LABEL}.digi.root";
+  RECO_GEO_INPUT="${DIR_RECO}/${SETUP_NAME}.geo.root"
+  if [[ ${RECO_DIGI_INPUT} != ${DIGI_OFFLINE_FILE} ]]; then 
+    pushd .
+    cd ${DIR_RECO}
+    ln -s -f ${DIGI_OFFLINE_FILE} $(basename ${RECO_DIGI_INPUT})
+    popd
+  fi 
+  if [[ ${SETUP_GEO_FILE} != ${RECO_GEO_INPUT} ]]; then 
+    pushd .
+    cd ${DIR_RECO}
+    ln -s -f ${SETUP_GEO_FILE} $(basename ${RECO_GEO_INPUT})
+    popd
+  fi
+  
+  cp ${SETUP_PAR_FILE} ${RECO_PAR_FILE}
+  
+  PARS="${RUN},${N_TS},\"${DIR_RECO}\",\"${DIR_RECO}\",${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}\""
+  PARS="${PARS},${RECO_CA},${RECO_QA},${RECO_FSD},\"${RECO_DIGI_INPUT}\""
   if [[ ${MACRO_RECO} == "*.mcbm2024*" ]]; then
     PARS="${PARS},${RECO_PV}"
   fi
@@ -290,16 +504,35 @@ if [[ ${DO_RECO} -eq 1 ]]; then
   fi
   rm ${RECO_LOG}
 
-  ln -s -f "${FILE_LABEL}.digi.root" "${DATA_TOP_DIR}/${FILE_LABEL}.raw.root"  # TMP for QA
+  # ln -s -f "${FILE_LABEL}.digi.root" "${DATA_TOP_DIR}/${FILE_LABEL}.raw.root"  # TMP for QA
   # Commented out as the output of mcbm_event_reco_L1.C is already [...].rec.root
   #  ln -s -f "${FILE_LABEL}.reco.root" "${DATA_TOP_DIR}/${FILE_LABEL}.rec.root"  # TMP for QA
 fi
 
 # ----- Run QA of reco modules
 if [[ ${DO_QA_MODULE} -eq 1 ]]; then
-  QA_GEO="${DATA_TOP_DIR}/${SETUP_NAME}.geo.root"
-  QA_REC="${DATA_TOP_DIR}/${FILE_LABEL}.rec.root"
-  ln -s -f ${QA_GEO} "${SETUP_NAME}.geo.root"
+  QA_GEO="${DIR_QA}/${SETUP_NAME}.geo.root"
+  QA_REC="${DIR_QA}/${FILE_LABEL}.rec.root"
+  QA_PAR="${DIR_QA}/${FILE_LABEL}.par.root"
+  if [[ ${QA_REC} != ${RECO_FILE} ]]; then 
+    pushd .
+    cd ${DIR_QA}
+    ln -s -f $(realpath ${RECO_FILE}) $(basename ${QA_REC})
+    popd
+  fi
+  if [[ ${QA_GEO} != ${SETUP_GEO_FILE} ]]; then 
+    pushd .
+    cd ${DIR_QA}
+    ln -s -f $(realpath ${SETUP_GEO_FILE}) $(basename ${QA_GEO})
+    popd
+  fi
+  if [[ ${QA_REC} != ${RECO_FILE} ]]; then 
+    pushd .
+    cd ${DIR_QA}
+    ln -s -f $(realpath ${RECO_PAR_FILE}) $(basename ${QA_PAR})
+    popd
+  fi
+
   PARS="-1,\"${QA_REC}\",\"${SETUP_NAME}\",kFALSE,${RECO_ALI}"
   root -b -l -q ${MACRO_QA_MODULE}"(${PARS})" &> ${RECO_QA_LOG}
   cat ${RECO_QA_LOG}
@@ -318,11 +551,11 @@ if [[ ${DO_QA} -eq 1 ]]; then
   CONFIG=""
   BENCHMARK=""
   #PARS="1,\"${DATA_TOP_DIR}/${FILE_LABEL}\",\"${SETUP_NAME}\",${USE_MC},\"${CONFIG}\",\"${BENCHMARK}\""
-  QA_RAW="${DATA_TOP_DIR}/${FILE_LABEL}.raw.root"
-  QA_REC="${DATA_TOP_DIR}/${FILE_LABEL}.rec.root"
-  QA_PAR="${DATA_TOP_DIR}/${FILE_LABEL}.par.root"
-  QA_GEO="${DATA_TOP_DIR}/${SETUP_NAME}.geo.root"
-  QA_OUT="${DATA_TOP_DIR}/${FILE_LABEL}.qa.root"
+  QA_RAW=${DIGI_OFFLINE_FILE}
+  QA_REC=${RECO_FILE}
+  QA_PAR=${RECO_PAR_FILE}
+  QA_GEO=${SETUP_QA_LOG}
+  QA_OUT="${DIR_QA}/${FILE_LABEL}.qa.root"
 
   PARS="0,\"\",\"${QA_RAW}\",\"${QA_REC}\",\"${QA_PAR}\",\"${QA_GEO}\",\"${QA_OUT}\",\"${SETUP_NAME}\""
   PARS="${PARS},${USE_MC},\"${CONFIG}\",\"${BENCHMARK}\",${RECO_ALI}"
@@ -338,4 +571,21 @@ if [[ ${DO_QA} -eq 1 ]]; then
   rm ${QA_LOG}
 fi
 
+# ----- Run Lambda reconstuction using the KFParticleFinder
+if [[ ${DO_LAMBDA} -eq 1 ]]; then
+  LAMBDA_FST_TS=0
+  LAMBDA_LST_TS=0  # Run all the data produced by reconstruction
+  LAMBDA_IN_REC=${RECO_FILE}
+  LAMBDA_IN_TRA="\"\",\"\",\"\""  # Empty for real data
+  LAMBDA_OUT="${DIR_KFPF}/${FILE_LABEL}.kfp.ana.root"
+  LAMBDA_IN_GEO=${SETUP_QA_LOG}
+  LAMBDA_IN_PAR=${RECO_PAR_FILE}
+  LAMBDA_USE_MC="false"
+  PARS="${RUN},${LAMBDA_FST_TS},${LAMBDA_LST_TS},\"${LAMBDA_IN_REC}\",${LAMBDA_IN_TRA},\"${LAMBDA_OUT}\",\"${LAMBDA_IN_GEO}\""
+  PARS="${PARS},\"${LAMBDA_IN_PAR}\",${RECO_ALI},${LAMBDA_MIXED_EVENT},${LAMBDA_USE_MC}"
+
+  root -b -l -q ${MACRO_KFPF}"(${PARS})"
+fi
+
+
 printf "Reconstruction of %s succeeded\n" "${TSA_INP}"
diff --git a/macro/beamtime/mcbm2024/reco_mcbm_job.sh b/macro/beamtime/mcbm2024/reco_mcbm_job.sh
new file mode 100755
index 0000000000..e549d0d36a
--- /dev/null
+++ b/macro/beamtime/mcbm2024/reco_mcbm_job.sh
@@ -0,0 +1,131 @@
+#!/bin/bash
+# Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+# SPDX-License-Identifier: GPL-3.0-only
+# Authors: Sergei Zharko [committer]
+#
+# @file   reco_mcbm_job.sh
+# @brief  Script to run a particular job on VIRGO
+# @since  23.01.2025
+# @author Sergei Zharko <s.zharko@gsi.de>
+#
+# The script defines environment to reconstruct time slices vs. a particular job using VIRGO.
+# To run the script one has to export a variable MCBM_DATA_DIR -- a path to the real data directory.
+#
+#
+# OPTIONS:
+#
+# 1. Required:
+# (A) SETUP GENERATION:
+#    --setup:      Runs in setup generation mode
+#    --run <num>   Sets run ID (<num> must be an integer with 4 digits)
+# 
+# (B) DATA RECONSTRUCTION:
+#    --tsa-index <path>   Path to the text file with absolute paths to input TSA files 
+#                         Each line of the file corresponds to the job index.
+#    --job | -j  <num>    Job index (from 1 to number of files, defined under the option --tsa-index)
+#    --run <num>   Sets run ID (<num> must be an integer with 4 digits)
+#    
+# 
+# 2. Optional (in mode (B)):
+#    --unpack, --reco, --qa, --qa-module, --kfpf, -n  -- See the reco_mcbm.sh script for details
+#
+
+RECO_OPTIONS=""
+RUN=""
+JOB=""
+DO_GENERATE_SETUP=0
+TSA_INDEX=
+# ------ Option selection ----------------------------------------------------------------------------------------------
+while [[ $# -gt 0 ]]; do
+  case ${1} in
+    --setup )
+      DO_GENERATE_SETUP=1
+      ;;
+    --unpack )
+      RECO_OPTIONS="${RECO_OPTIONS} --unpack"
+      ;;
+    --reco )
+      RECO_OPTIONS="${RECO_OPTIONS} --reco"
+      ;;
+    --qa )
+      RECO_OPTIONS="${RECO_OPTIONS} --qa"
+      ;;
+    --qa-module )
+      RECO_OPTIONS="${RECO_OPTIONS} --qa-module"
+      ;;
+    --kfpf )
+      RECO_OPTIONS="${RECO_OPTIONS} --kfpf"
+      ;;
+    --run )
+      RUN=${2}
+      ;;
+    -n )
+      RECO_OPTIONS="${RECO_OPTIONS} -n ${2}"
+      ;;
+    -j | --job )
+      JOB=${2}
+      ;;
+    --tsa-index )
+      TSA_INDEX=${2}
+      ;;
+  esac
+  shift
+done
+
+# ------ Variable check ------------------------------------------------------------------------------------------------
+if [[ -z "${VMCWORKDIR}" ]]; then
+  printf "E- CBM environment is not defined (VMCWORKDIR is not found). Please, configure your CbmRoot\n"
+  exit 1
+fi
+
+if [[ -z ${RUN} ]]; then
+  printf "E- The run identifier is not specified. Please, specify it using the option \"--run <run_id>\"\n"
+  exit 2
+fi
+
+if [[ -z ${JOB} && ${DO_GENERATE_SETUP} -ne 1 ]]; then 
+  printf "E- The job number is not specified. Please set the job number using the option \"-j <job>\"\n"
+  exit 3
+fi 
+
+if [[ -z ${MCBM_DATA_DIR} ]]; then
+  printf "E- The data directory is not identified. Please provide the path to the directory, using the following bash "
+  printf "command\n\t export MCBM_DATA_DIR=<path to data directory>\n"
+  exit 4
+fi
+
+# ------ Script execution ----------------------------------------------------------------------------------------------
+mkdir -p ${MCBM_DATA_DIR}
+
+DIR_UNPACK=${MCBM_DATA_DIR}/unpacked_data/${RUN}
+DIR_RECO=${MCBM_DATA_DIR}/reconstructed_data/${RUN}
+DIR_SETUP=${MCBM_DATA_DIR}/setups
+DIR_QA=${MCBM_DATA_DIR}/qa/${RUN}
+DIR_KFPF=${MCBM_DATA_DIR}/kfpf/${RUN}
+
+# NOTE: either the setup is generated, or the data are produced.
+if [[ ${DO_GENERATE_SETUP} -eq 1 ]]; then
+  RECO_OPTIONS=" --setup-only --run ${RUN}"
+else 
+  if [[ -f ${TSA_INDEX} ]]; then 
+    TSA_INDEX=$(realpath ${TSA_INDEX})
+    RECO_OPTIONS="${RECO_OPTIONS} --tsa-index-file ${TSA_INDEX}"
+  else 
+    printf "E- TSA index file ${TSA_INDEX} was not found\n"
+    exit 5
+  fi
+fi
+
+RECO_OPTIONS="${RECO_OPTIONS} --setup-dir ${DIR_SETUP} --unpack-dir ${DIR_UNPACK} --reco-dir ${DIR_RECO} --qa-dir ${DIR_QA} --kfpf-dir ${DIR_KFPF}"
+
+RECO_SCRIPT=${VMCWORKDIR}/macro/beamtime/mcbm2024/reco_mcbm.sh
+
+if [[ ! -x ${RECO_SCRIPT} ]]; then
+  printf "E- The reco script (${RECO_SCRIPT}) either does not exist, or is not executable. Please make it executable "
+  printf "with the \"chmod +x\" command\n"
+  exit 6
+fi
+
+${RECO_SCRIPT} ${RECO_OPTIONS}
+
+
diff --git a/macro/mcbm/mcbm_hadron_kfp_ana.C b/macro/mcbm/mcbm_hadron_kfp_ana.C
index b48dd99436..bd44f80b29 100644
--- a/macro/mcbm/mcbm_hadron_kfp_ana.C
+++ b/macro/mcbm/mcbm_hadron_kfp_ana.C
@@ -47,7 +47,7 @@
 /* clang-format off */
 Bool_t mcbm_hadron_kfp_ana(UInt_t uRunId         = 2391,
                            Int_t firstTimeslice  = 0,
-                           Int_t nTimeslices     = 10,
+                           Int_t nTimeslices     = 0,
                            TString sRecoFile     = "./data/2391_first20Ts.rec.root",
                            TString sCollTraFile  = "",
                            TString sSignTraFile  = "",
@@ -182,7 +182,8 @@ Bool_t mcbm_hadron_kfp_ana(UInt_t uRunId         = 2391,
   // The task parameters:
   pV0->SetMinPionDca(1.5);
   pV0->SetMinProtonDca(0.5);
-  pV0->SetTzeroOffset(0.12);
+  //pV0->SetTzeroOffset(0.12);
+  pV0->SetTzeroOffset(0.);
   pV0->SetQpAssignedUncertainty(0.10);  // 10% uncertainty for track q/p
   pV0->SetMixedEventMode(bMixEvent);
   pV0->SetProcessingMode(cbm::kfp::V0FinderTask::EProcessingMode::TimeBased);
@@ -201,7 +202,7 @@ Bool_t mcbm_hadron_kfp_ana(UInt_t uRunId         = 2391,
   // QA output setting
   {
     TString sQaFile{sSinkFile};
-    sQaFile.ReplaceAll(".ana.", "_qa.ana.");
+    sQaFile.ReplaceAll(".kfp.ana.", ".qa.kfp.ana.");
     pV0->SetQaOutputFileName(sQaFile);
     pV0->SetRunQa(true);
   }
diff --git a/reco/KF/CbmKFV0FinderQa.cxx b/reco/KF/CbmKFV0FinderQa.cxx
index 6b6b1e5463..a079aa3b5e 100644
--- a/reco/KF/CbmKFV0FinderQa.cxx
+++ b/reco/KF/CbmKFV0FinderQa.cxx
@@ -46,6 +46,10 @@ void V0FinderQa::InitHistograms()
     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.);
+
+    n                    = "lambda_cand_mass";
+    t                    = "Mass of lambda candidate;m [GeV/^{2}];Counts";
+    fph_lambda_cand_mass = MakeQaObject<TH1D>(n, t, 200, 1.05, 1.15);
   }
 }
 
diff --git a/reco/KF/CbmKFV0FinderQa.h b/reco/KF/CbmKFV0FinderQa.h
index 53091e0112..bf608d4aae 100644
--- a/reco/KF/CbmKFV0FinderQa.h
+++ b/reco/KF/CbmKFV0FinderQa.h
@@ -56,7 +56,7 @@ namespace cbm::kfp
     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
-
+    TH1D* fph_lambda_cand_mass{nullptr};             ///< Mass of lambda candidates
 
    private:
     bool fbUseMc{false};
diff --git a/reco/KF/CbmKFV0FinderTask.cxx b/reco/KF/CbmKFV0FinderTask.cxx
index 2ec69f809c..e29f13b209 100644
--- a/reco/KF/CbmKFV0FinderTask.cxx
+++ b/reco/KF/CbmKFV0FinderTask.cxx
@@ -119,7 +119,11 @@ bool V0FinderTask::ProcessEvent(const CbmEvent* pEvent)
 
   // ----- Shift TOF hit times to t0
   if constexpr (UseEvent) {
-    ShiftTofHitsToTzero(pEvent);
+    auto t0 = ShiftTofHitsToTzero(pEvent);
+    if (std::isnan(t0)) {
+      ++fCounters[ECounter::EventsWoTzero];
+      return false;
+    }
   }
 
   // ----- Select tracks
@@ -176,8 +180,16 @@ bool V0FinderTask::ProcessEvent(const CbmEvent* pEvent)
   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; });
+  int nLambda = 0;
+  //std::count_if(particles.begin(), particles.end(), [](const auto& p) { return p.GetPDG() == 3122; });
+  for (const auto& particle : fpTopoReconstructorEvent->GetParticles()) {
+    if (particle.GetPDG() == 3122) {
+      ++nLambda;
+      if (fbRunQa) {
+        fpQa->fph_lambda_cand_mass->Fill(particle.GetMass());
+      }
+    }
+  }
   fCounters[ECounter::KfpLambdaCandidates] += nLambda;
   if (nLambda > 0) {
     ++fCounters[ECounter::KfpEventsLambdaCand];
@@ -346,6 +358,7 @@ void V0FinderTask::Finish()
   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/o t-zero:                    " << fCounters[ECounter::EventsWoTzero];
   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];
@@ -680,7 +693,9 @@ double V0FinderTask::ShiftTofHitsToTzero(const CbmEvent* pEvent)
   // 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));
+  if (std::isnan(t0)) {
+    return t0;
+  }
 
   // Shift TOF times to found t0:
   for (int iTofHitEvt{0}; iTofHitEvt < nTofHits; ++iTofHitEvt) {
diff --git a/reco/KF/CbmKFV0FinderTask.h b/reco/KF/CbmKFV0FinderTask.h
index 567c0e721f..6fb0bbf368 100644
--- a/reco/KF/CbmKFV0FinderTask.h
+++ b/reco/KF/CbmKFV0FinderTask.h
@@ -73,6 +73,7 @@ namespace cbm::kfp
       Pions,                     //< Number of pion-candidates
       Protons,                   //< Number of proton-candidates
       EventsTotal,               //< Total number of events
+      EventsWoTzero,             //< Number of events with undefined t-zero
       EventsLambdaCand,          //< Events with at least one pion and one proton candidate
       KfpEventsLambdaCand,       //< Events with lambda-candidates in KF-particle
       KfpLambdaCandidates,       //< Number of lambda-candidates
diff --git a/reco/L1/CbmL1.cxx b/reco/L1/CbmL1.cxx
index 593b352728..37d8c61be3 100644
--- a/reco/L1/CbmL1.cxx
+++ b/reco/L1/CbmL1.cxx
@@ -446,7 +446,14 @@ try {
     // *************************
     {
       auto trackerSetup  = pSetupBuilder->MakeSetup<float>(cbm::algo::kf::EFieldMode::Intrpl);
-      TString outputFile = fSTAPDataDir + "/" + fSTAPDataPrefix + "." + TString(kSTAPSetupSuffix.data());
+      TString geoTag     = "";
+      if (auto* pSetup = CbmSetup::Instance()) {
+        geoTag = pSetup->GetProvider()->GetSetup().GetTag();
+      }
+      if (geoTag.IsNull()) {
+        geoTag = fSTAPDataPrefix;
+      }
+      TString outputFile = fSTAPDataDir + "/" + geoTag + "." + TString(kSTAPSetupSuffix.data());
       cbm::algo::kf::SetupBuilder::Store(trackerSetup, outputFile.Data());
       fInitManager.SetGeometrySetup(trackerSetup);
     }
-- 
GitLab