From a6c95a92f8b2f36186a9353091b8e007cf7cda07 Mon Sep 17 00:00:00 2001
From: "P.-A. Loizeau" <p.-a.loizeau@gsi.de>
Date: Tue, 28 May 2024 15:32:02 +0200
Subject: [PATCH] Add in algo an example of time-in-run hist: processed TS per
 start time

- Add option to provide the start time fo the run
- Make it so that current time at Reco::Init is used if not provided
- Add new general QA of the Reco process with two plots:
==> Count of TS as function of start time in run
==> Fraction of expected TS as function of start time in run
---
 algo/CMakeLists.txt       |  1 +
 algo/base/Options.cxx     |  1 +
 algo/base/Options.h       |  8 +++--
 algo/global/Reco.cxx      | 18 +++++++++++
 algo/global/Reco.h        |  9 ++++++
 algo/qa/RecoGeneralQa.cxx | 68 +++++++++++++++++++++++++++++++++++++++
 algo/qa/RecoGeneralQa.h   | 64 ++++++++++++++++++++++++++++++++++++
 7 files changed, 166 insertions(+), 3 deletions(-)
 create mode 100644 algo/qa/RecoGeneralQa.cxx
 create mode 100644 algo/qa/RecoGeneralQa.h

diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt
index ca5a15094d..1f3952888c 100644
--- a/algo/CMakeLists.txt
+++ b/algo/CMakeLists.txt
@@ -149,6 +149,7 @@ set(SRCS
   qa/CanvasConfig.cxx
   qa/PadConfig.cxx
   qa/QaData.cxx
+  qa/RecoGeneralQa.cxx
   qa/unpack/StsDigiQa.cxx
   ca/TrackingSetup.cxx
   ca/TrackingChain.cxx
diff --git a/algo/base/Options.cxx b/algo/base/Options.cxx
index 662b21080b..55dca18c13 100644
--- a/algo/base/Options.cxx
+++ b/algo/base/Options.cxx
@@ -90,6 +90,7 @@ Options::Options(int argc, char** argv)
       "space separated list of detectors to process (sts, mvd, ...)")
     ("child-id,c", po::value(&fChildId)->default_value("00")->value_name("<id>"), "online process id on node")
     ("run-id,r", po::value(&fRunId)->default_value(2391)->value_name("<RunId>"), "Run ID, for now flesctl run index, later run start time")
+    ("run-start", po::value(&fRunStartTime)->default_value(0)->value_name("<RunStart >"), "Run start time in ns, can be fles start or online start")
     ("num-ts,n", po::value(&fNumTimeslices)->default_value(-1)->value_name("<num>"),
       "Stop after <num> timeslices (-1 = all)")
     ("skip-ts", po::value(&fSkipTimeslices)->default_value(0)->value_name("<num>"),
diff --git a/algo/base/Options.h b/algo/base/Options.h
index d6e693d156..51a8bb6fe0 100644
--- a/algo/base/Options.h
+++ b/algo/base/Options.h
@@ -46,6 +46,7 @@ namespace cbm::algo
     }
     const std::string& ChildId() const { return fChildId; }
     uint64_t RunId() const { return fRunId; }
+    uint64_t RunStart() const { return fRunStartTime; }
     bool DumpArchive() const { return fDumpArchive; }
     bool ReleaseMode() const { return fReleaseMode; }
 
@@ -86,9 +87,10 @@ namespace cbm::algo
     std::vector<RecoData> fOutputTypes;
     bool fCompressArchive = false;
     std::vector<fles::Subsystem> fDetectors;
-    std::string fChildId = "00";
-    uint64_t fRunId      = 2391;
-    bool fCollectAuxData = false;
+    std::string fChildId   = "00";
+    uint64_t fRunId        = 2391;
+    uint64_t fRunStartTime = 0;
+    bool fCollectAuxData   = false;
   };
 
 }  // namespace cbm::algo
diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx
index c200260f44..a5a3696f06 100644
--- a/algo/global/Reco.cxx
+++ b/algo/global/Reco.cxx
@@ -10,6 +10,7 @@
 #include "Exceptions.h"
 #include "HistogramSender.h"
 #include "ParFiles.h"
+#include "RecoGeneralQa.h"
 #include "StsDigiQa.h"
 #include "TrackingSetup.h"
 #include "bmon/ReadoutConfig.h"
@@ -42,6 +43,8 @@
 using namespace cbm::algo;
 using fles::Subsystem;
 
+namespace chron = std::chrono;
+
 Reco::Reco() {}
 Reco::~Reco() {}
 
@@ -85,6 +88,11 @@ void Reco::Init(const Options& opts)
   if (Opts().HistogramUri() != "") {
     fSender = std::make_shared<HistogramSender>(Opts().HistogramUri(), Opts().HistogramHwm());
     // fContext.sender = fSender;
+
+    fRunStartTimeNs = Opts().RunStart();
+    if (0 == fRunStartTimeNs) {
+      fRunStartTimeNs = chron::duration_cast<chron::nanoseconds>(chron::system_clock::now().time_since_epoch()).count();
+    }
   }
 
   xpu::device_prop props{xpu::device::active()};
@@ -102,6 +110,11 @@ void Reco::Init(const Options& opts)
   ParFiles parFiles(opts.RunId());
   L_(info) << "Using parameter files for setup " << parFiles.setup;
 
+  // General QA
+  if (fSender != nullptr) {
+    fGeneralQa = std::make_unique<qa::RecoGeneralQa>(fRunStartTimeNs, fSender);
+  }
+
   // Unpackers
   if (Opts().Has(Subsystem::BMON) && Opts().Has(Step::Unpack)) {
     bmon::ReadoutSetup readoutSetup =
@@ -361,6 +374,11 @@ RecoResults Reco::Run(const fles::Timeslice& ts)
       results.tofHits = std::move(recoData.tofHits);
       results.trdHits = std::move(recoData.trdHits);
     }
+
+    // General QA
+    if (fSender != nullptr) {
+      (*fGeneralQa)(ts);
+    }
   }
   PrintTimings(procMon.time);
   if (prevTsId) {
diff --git a/algo/global/Reco.h b/algo/global/Reco.h
index b2b559651a..a3fa9c2411 100644
--- a/algo/global/Reco.h
+++ b/algo/global/Reco.h
@@ -85,6 +85,11 @@ namespace cbm::algo
 
   template<class Unpacker>
   using UnpackResult_t = std::tuple<algo_traits::Output_t<Unpacker>, algo_traits::Aux_t<Unpacker>>;
+
+  namespace qa
+  {
+    class RecoGeneralQa;
+  }
 }  // namespace cbm::algo
 
 namespace cbm::algo
@@ -131,9 +136,13 @@ namespace cbm::algo
     ChainContext fContext;
     xpu::timings fTimesliceTimesAcc;
     std::shared_ptr<HistogramSender> fSender;
+    uint64_t fRunStartTimeNs = 0;
 
     std::optional<u64> prevTsId;
 
+    // General QA
+    std::unique_ptr<qa::RecoGeneralQa> fGeneralQa;  ///< QA of online processing itself
+
     // BMON
     std::unique_ptr<bmon::Unpack> fBmonUnpack;
 
diff --git a/algo/qa/RecoGeneralQa.cxx b/algo/qa/RecoGeneralQa.cxx
new file mode 100644
index 0000000000..87687520d5
--- /dev/null
+++ b/algo/qa/RecoGeneralQa.cxx
@@ -0,0 +1,68 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: P.-A. Loizeau [committer] */
+
+#include "RecoGeneralQa.h"
+
+#include <cmath>
+
+using cbm::algo::qa::Data;
+using cbm::algo::qa::H1D;
+
+namespace cbm::algo::qa
+{
+  RecoGeneralQa::RecoGeneralQa(const uint64_t& runStartTimeNs, std::shared_ptr<HistogramSender> pSender)
+    : fRunStartTimeNs(runStartTimeNs)
+    , fpSender(pSender)
+  {
+  }
+
+  // ---   Execution   --------------------------------------------------------
+  void RecoGeneralQa::operator()(const fles::Timeslice& ts)
+  {
+    if (!fpSender.get()) {
+      return;
+    }
+
+    if (fInitNotDone) {
+      double_t dBegAxisX  = 0.0;
+      double_t dSizeTsSec = 0.128;  /// FIXME: Avoid default value in case first component has only a single MS
+      if (ts.num_components() != 0 && ts.num_microslices(0) > 1) {
+        dSizeTsSec = ((ts.descriptor(0, 1).idx - ts.descriptor(0, 0).idx) * ts.num_core_microslices()) * 1.0e-9;
+      }
+      auto cName = "processed_ts";
+      auto cTitl = "Statistics of TS processed online";
+      auto canv  = CanvasConfig(cName, cTitl, 2, 1);
+      {
+        auto pad              = PadConfig();
+        auto name             = "timeslices_count_evo";
+        auto titl             = "Number of processed TS vs time in run; time in run [s]; Nb TS []";
+        int32_t nbBins        = std::ceil(7200.0 / (dSizeTsSec * kNbTsPerBinCount));  // a bit more than 2h range
+        double_t dEndAxisX    = nbBins * dSizeTsSec * kNbTsPerBinCount;
+        fphTimeslicesCountEvo = fQaData.MakeObj<H1D>(name, titl, nbBins, dBegAxisX, dEndAxisX);
+        pad.RegisterHistogram(fphTimeslicesCountEvo, "hist");
+        canv.AddPadConfig(pad);
+      }
+      {
+        auto pad                 = PadConfig();
+        auto name                = "timeslices_fraction_evo";
+        auto titl                = "Fraction of TS processed vs time in run; time in run [s]; Processed TS []";
+        int32_t nbBins           = std::ceil(7200.0 / (dSizeTsSec * kNbTsPerBinFrac));  // a bit more than 2h range
+        double_t dEndAxisX       = nbBins * dSizeTsSec * kNbTsPerBinFrac;
+        fphTimeslicesFractionEco = fQaData.MakeObj<H1D>(name, titl, nbBins, dBegAxisX, dEndAxisX);
+        pad.RegisterHistogram(fphTimeslicesFractionEco, "hist");
+        canv.AddPadConfig(pad);
+      }
+      fQaData.AddCanvasConfig(canv);
+
+      fQaData.Init(fpSender);
+      fInitNotDone = false;
+    }
+
+    fphTimeslicesCountEvo->Fill((ts.start_time() - fRunStartTimeNs) * 1e-9);
+    fphTimeslicesFractionEco->Fill((ts.start_time() - fRunStartTimeNs) * 1e-9, 1.0 / kNbTsPerBinFrac);
+
+    fQaData.Send(fpSender);  // Send and reset!
+  }
+
+}  // namespace cbm::algo::qa
diff --git a/algo/qa/RecoGeneralQa.h b/algo/qa/RecoGeneralQa.h
new file mode 100644
index 0000000000..2b859e9c6d
--- /dev/null
+++ b/algo/qa/RecoGeneralQa.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: P.-A. Loizeau [committer] */
+
+#ifndef ALGO_QA_RECOGENERALQA_H
+#define ALGO_QA_RECOGENERALQA_H 1
+
+#include "CbmDefs.h"
+#include "HistogramSender.h"
+#include "QaData.h"
+
+#include <StorableTimeslice.hpp>
+
+namespace cbm::algo::qa
+{
+  /** @class RecoQa
+   ** @brief General QA for a Reco cycle on a single TS
+   ** @author P.-A. Loizeau <p.-a.loizeau@gsi.de>
+   ** @since 27 Mai 2024
+   **/
+  class RecoGeneralQa {
+   public:
+    /** @brief Constructor **/
+    RecoGeneralQa(const uint64_t& runStartTimeNs, std::shared_ptr<HistogramSender> pSender);
+
+    /// \brief Default constructor
+    RecoGeneralQa() = delete;
+
+    /// \brief Copy constructor
+    RecoGeneralQa(const RecoGeneralQa&) = delete;
+
+    /// \brief Move constructor
+    RecoGeneralQa(RecoGeneralQa&&) = delete;
+
+    /// \brief Copy assignment operator
+    RecoGeneralQa& operator=(const RecoGeneralQa&) = delete;
+
+    /// \brief Move assignment operator
+    RecoGeneralQa& operator=(RecoGeneralQa&&) = delete;
+
+    /** @brief Execution: fill histograms and emit them (FIXME: control emission frequency)
+     ** @param Reference to current TS
+     ** @return nothing
+     **/
+    void operator()(const fles::Timeslice& ts);
+
+   private:  // methods
+   private:  // members
+    uint64_t fRunStartTimeNs;
+    std::shared_ptr<HistogramSender> fpSender = nullptr;  ///< Histogram sender
+    qa::Data fQaData{"Reco"};                             ///< QA data, with folder named Reco as hist destination
+    bool fInitNotDone = true;
+
+    // Constants
+    static const int32_t kNbTsPerBinCount = 100;   // 100 TS duration per bin for raw counts
+    static const int32_t kNbTsPerBinFrac  = 1000;  // 1000 TS duration per bin for fractions
+
+    // ---- Histograms
+    qa::H1D* fphTimeslicesCountEvo    = nullptr;  ///< hist: timeslices vs time in run in s, binned for 100 TS
+    qa::H1D* fphTimeslicesFractionEco = nullptr;  ///< hist: fraction of al ts vs time in run in s, binned for 1000 TS
+  };
+}  // namespace cbm::algo::qa
+
+#endif /* ALGO_QA_RECOGENERALQA_H */
-- 
GitLab