From 0da6d651e752bfc1306b70120a8d332013494f0e Mon Sep 17 00:00:00 2001
From: "s.zharko@gsi.de" <s.zharko@gsi.de>
Date: Sun, 3 Mar 2024 22:27:28 +0100
Subject: [PATCH] online-qa: Histogram-server setup for accepting TProfile and
 TProfile2D objects

---
 algo/ca/qa/CaInputQa.cxx              | 41 ++++++++++++++++---
 algo/ca/qa/CaInputQa.h                |  2 +
 algo/ca/qa/CaOutputQa.cxx             |  2 +-
 algo/ca/qa/CaQaBase.h                 |  2 +-
 algo/qa/HistogramContainer.cxx        | 12 +++---
 algo/qa/HistogramContainer.h          | 12 +++---
 algo/qa/QaData.cxx                    | 13 +++++-
 algo/qa/QaData.h                      |  6 +++
 services/histserv/app/Application.cxx | 58 +++++++++++----------------
 services/histserv/app/CMakeLists.txt  |  2 +
 10 files changed, 95 insertions(+), 55 deletions(-)

diff --git a/algo/ca/qa/CaInputQa.cxx b/algo/ca/qa/CaInputQa.cxx
index ec3eba9e6c..b8e6352854 100644
--- a/algo/ca/qa/CaInputQa.cxx
+++ b/algo/ca/qa/CaInputQa.cxx
@@ -33,6 +33,7 @@ void InputQa::Init()
   using cbm::algo::qa::H1D;
   using cbm::algo::qa::H2D;
   using cbm::algo::qa::PadConfig;
+  using cbm::algo::qa::Prof2D;
   using fmt::format;
 
   if (!fpSender.get()) {
@@ -53,6 +54,7 @@ void InputQa::Init()
     fvphHitOccupXY.resize(nSt);
     fvphHitOccupZX.resize(nSt);
     fvphHitOccupZY.resize(nSt);
+    fvphHitUsageXY.resize(nSt);
 
     // Station sizes in transverse plane
     std::vector<double> vMinX(nSt);
@@ -114,6 +116,12 @@ void InputQa::Init()
           fvphHitOccupZY[iSt][hitSet] = fQaData.MakeObj<H2D>(name, titl, nBinsZ, zMinA, zMaxA, nBinsXY, yMinA, yMaxA);
         }
       }
+      {
+        auto name = format("hit_usage_xy_sta_{}", iSt);
+        auto titl = format("Hit usage in XY plane for station {} ({}{});x [cm];y [cm]", iSt, kDetName[detID], iStLoc);
+        fvphHitUsageXY[iSt] =
+          fQaData.MakeObj<Prof2D>(name, titl, nBinsXY, vMinX[iSt], vMaxX[iSt], nBinsXY, vMinY[iSt], vMaxY[iSt], 0., 1.);
+      }
     }
   }
 
@@ -155,6 +163,18 @@ void InputQa::Init()
         fQaData.AddCanvasConfig(canv);
       }
     }
+    // Hit usage profiles
+    {
+      auto name = format("ca_hit_usage_xy");
+      auto titl = format("Hit usage in different stations in XY plane");
+      auto canv = CanvasConfig(name, titl);
+      for (int iSt = 0; iSt < nSt; ++iSt) {
+        auto pad = PadConfig();
+        pad.RegisterHistogram(fvphHitUsageXY[iSt], "colz");
+        canv.AddPadConfig(pad);
+      }
+      fQaData.AddCanvasConfig(canv);
+    }
   }
   fQaData.Init(fpSender);
 }
@@ -172,14 +192,25 @@ void InputQa::Exec()
     assert(false);
   }
 
+  int nHitsInput = fpInputData->GetHits().size();
+  // Map: if hit used in tracking
+  std::vector<unsigned char> vbHitUsed(nHitsInput, false);
+  for (int iH : (*fpvRecoHits)) {
+    vbHitUsed[iH] = true;
+  }
+
   // Fill input hit histograms
   {
-    for (const auto& hit : fpInputData->GetHits()) {
+    for (int iH = 0; iH < nHitsInput; ++iH) {
+      const auto& hit = fpInputData->GetHit(iH);
       FillHitDistributionsForHitSet(EHitSet::Input, hit);
-    }
-
-    for (int iH : (*fpvRecoHits)) {
-      FillHitDistributionsForHitSet(EHitSet::Used, fpInputData->GetHit(iH));
+      if (vbHitUsed[iH]) {
+        FillHitDistributionsForHitSet(EHitSet::Used, hit);
+      }
+      int iSt  = hit.Station();
+      double x = hit.X();
+      double y = hit.Y();
+      fvphHitUsageXY[iSt]->Fill(x, y, static_cast<double>(vbHitUsed[iH]));
     }
   }
 
diff --git a/algo/ca/qa/CaInputQa.h b/algo/ca/qa/CaInputQa.h
index baeedcb976..47b355395c 100644
--- a/algo/ca/qa/CaInputQa.h
+++ b/algo/ca/qa/CaInputQa.h
@@ -78,5 +78,7 @@ namespace cbm::algo::ca
     OccupHistContainer_t fvphHitOccupXY;  ///< hist: Hit occupancy in different stations in XY plane
     OccupHistContainer_t fvphHitOccupZX;  ///< hist: Hit occupancy in different stations in ZX plane
     OccupHistContainer_t fvphHitOccupZY;  ///< hist: Hit occupancy in different stations in ZY plane
+
+    std::vector<qa::Prof2D*> fvphHitUsageXY;  ///< prof: Hit usage in different stations in XY plane
   };
 }  // namespace cbm::algo::ca
diff --git a/algo/ca/qa/CaOutputQa.cxx b/algo/ca/qa/CaOutputQa.cxx
index d9c8fd0cc3..9957c745a5 100644
--- a/algo/ca/qa/CaOutputQa.cxx
+++ b/algo/ca/qa/CaOutputQa.cxx
@@ -2,7 +2,7 @@
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Sergei Zharko [committer] */
 
-/// \file   CaQaOutputQa.cxx
+/// \file   CaOutputQa.cxx
 /// \date   20.11.2023
 /// \brief  A QA module for CA tracking (implementation)
 /// \author S.Zharko <s.zharko@gsi.de>
diff --git a/algo/ca/qa/CaQaBase.h b/algo/ca/qa/CaQaBase.h
index 3a85a06f6a..e1fbeb6d1c 100644
--- a/algo/ca/qa/CaQaBase.h
+++ b/algo/ca/qa/CaQaBase.h
@@ -2,7 +2,7 @@
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Sergei Zharko [committer] */
 
-/// \file   QaBase.h
+/// \file   CaQaBase.h
 /// \date   29.02.2024
 /// \brief  Base class for tracking QA (header)
 /// \author Sergei Zharko <s.zharko@gsi.de>
diff --git a/algo/qa/HistogramContainer.cxx b/algo/qa/HistogramContainer.cxx
index 08ef1af72a..8db32a09a1 100644
--- a/algo/qa/HistogramContainer.cxx
+++ b/algo/qa/HistogramContainer.cxx
@@ -21,10 +21,10 @@ void HistogramContainer::Reset()
   for (auto& h : fvH2) {
     h.Reset();
   }
-  //for (auto& h : fvP1) {
-  //  h.Reset();
-  //}
-  //for (auto& h : fvP2) {
-  //  h.Reset();
-  //}
+  for (auto& h : fvP1) {
+    h.Reset();
+  }
+  for (auto& h : fvP2) {
+    h.Reset();
+  }
 }
diff --git a/algo/qa/HistogramContainer.h b/algo/qa/HistogramContainer.h
index 0420d87e8d..eb8630a6ca 100644
--- a/algo/qa/HistogramContainer.h
+++ b/algo/qa/HistogramContainer.h
@@ -20,10 +20,10 @@ namespace cbm::algo::qa
   /// \struct HistogramContainer
   /// \brief  Structure to keep the histograms for sending them on the histogram server
   struct HistogramContainer {
-    std::forward_list<qa::H1D> fvH1 = {};  ///< List of 1D-histograms
-    std::forward_list<qa::H2D> fvH2 = {};  ///< List of 2D-histograms
-    //std::forward_list<qa::Prof1D> fvP1 = {};  ///< List of 1D-profiles
-    //std::forward_list<qa::Prof2D> fvP2 = {};  ///< List of 2D-profiles
+    std::forward_list<qa::H1D> fvH1    = {};  ///< List of 1D-histograms
+    std::forward_list<qa::H2D> fvH2    = {};  ///< List of 2D-histograms
+    std::forward_list<qa::Prof1D> fvP1 = {};  ///< List of 1D-profiles
+    std::forward_list<qa::Prof2D> fvP2 = {};  ///< List of 2D-profiles
 
     /// \brief Resets the histograms
     void Reset();
@@ -35,8 +35,8 @@ namespace cbm::algo::qa
     {
       ar& fvH1;
       ar& fvH2;
-      //ar& fvP1;
-      //ar& fvP2;
+      ar& fvP1;
+      ar& fvP2;
     }
   };
 }  // namespace cbm::algo::qa
diff --git a/algo/qa/QaData.cxx b/algo/qa/QaData.cxx
index 90151fe66a..da0ed39365 100644
--- a/algo/qa/QaData.cxx
+++ b/algo/qa/QaData.cxx
@@ -22,6 +22,8 @@ void Data::Init(std::shared_ptr<HistogramSender> histSender)
     // NOTE: Important to keep the order of filling the histograms: 1D -> 2D -> ..
     nHistograms += std::distance(fHistograms.fvH1.begin(), fHistograms.fvH1.end());
     nHistograms += std::distance(fHistograms.fvH2.begin(), fHistograms.fvH2.end());
+    nHistograms += std::distance(fHistograms.fvP1.begin(), fHistograms.fvP1.end());
+    nHistograms += std::distance(fHistograms.fvP2.begin(), fHistograms.fvP2.end());
     vHistCfgs.reserve(nHistograms);
     for (const auto& hist : fHistograms.fvH1) {
       vHistCfgs.emplace_back(hist.GetName(), fsName);
@@ -29,6 +31,12 @@ void Data::Init(std::shared_ptr<HistogramSender> histSender)
     for (const auto& hist : fHistograms.fvH2) {
       vHistCfgs.emplace_back(hist.GetName(), fsName);
     }
+    for (const auto& hist : fHistograms.fvP1) {
+      vHistCfgs.emplace_back(hist.GetName(), fsName);
+    }
+    for (const auto& hist : fHistograms.fvP2) {
+      vHistCfgs.emplace_back(hist.GetName(), fsName);
+    }
 
     // Forming a canvas config message
     std::vector<std::pair<std::string, std::string>> vCanvCfgs;
@@ -57,6 +65,9 @@ void Data::Send(std::shared_ptr<HistogramSender> histoSender)
   histoSender->PrepareAndSendMsg(fHistograms, zmq::send_flags::none);
   auto nH1 = std::distance(fHistograms.fvH1.begin(), fHistograms.fvH1.end());
   auto nH2 = std::distance(fHistograms.fvH2.begin(), fHistograms.fvH2.end());
-  L_(info) << fsName << ": Published " << nH1 << " 1D- and " << nH2 << " 2D-histograms";
+  auto nP1 = std::distance(fHistograms.fvP1.begin(), fHistograms.fvP1.end());
+  auto nP2 = std::distance(fHistograms.fvP2.begin(), fHistograms.fvP2.end());
+  L_(info) << fsName << ": Published " << nH1 << " 1D- and " << nH2 << " 2D-histograms, " << nP1 << " 1D- and " << nP2
+           << "2D-profiles";
   this->Reset();
 }
diff --git a/algo/qa/QaData.h b/algo/qa/QaData.h
index e4fcc5c170..4eb94575f5 100644
--- a/algo/qa/QaData.h
+++ b/algo/qa/QaData.h
@@ -86,6 +86,12 @@ namespace cbm::algo::qa
     else if constexpr (std::is_same_v<Obj, cbm::algo::qa::H2D>) {
       return &(fHistograms.fvH2.emplace_front(args...));
     }
+    else if constexpr (std::is_same_v<Obj, cbm::algo::qa::Prof1D>) {
+      return &(fHistograms.fvP1.emplace_front(args...));
+    }
+    else if constexpr (std::is_same_v<Obj, cbm::algo::qa::Prof2D>) {
+      return &(fHistograms.fvP2.emplace_front(args...));
+    }
     return nullptr;
   }
 }  // namespace cbm::algo
diff --git a/services/histserv/app/Application.cxx b/services/histserv/app/Application.cxx
index 0964416f9f..2882c42ed7 100644
--- a/services/histserv/app/Application.cxx
+++ b/services/histserv/app/Application.cxx
@@ -5,6 +5,7 @@
 #include "Application.h"
 
 #include "CbmFlesCanvasTools.h"
+#include "CbmQaOnlineInterface.h"
 #include "HistogramContainer.h"
 #include "TCanvas.h"
 #include "TEnv.h"
@@ -15,6 +16,7 @@
 #include "TMessage.h"
 #include "TObjArray.h"
 #include "TProfile.h"
+#include "TProfile2D.h"
 #include "TRootSniffer.h"
 #include "TSystem.h"
 
@@ -166,57 +168,40 @@ namespace cbm::services::histserv
 
     /// copied from CbmTaskDigiEventQa::ToTH1D
     /// FIXME: Should be placed in a tools/interface/whatever library with all similar functions!!
-    /// TODO:  SZh 29.02.2024: Use core/qa/CbmQaOnlineInterface  (for now a fast solution)
     /// FIXME: Reverse OP need to be implemented + CI unit tests for back and forth in each direction (ROOT <-> Algo)
     /// FIXME: Lead to "Warning in <TROOT::Append>: Replacing existing TH1: xxxxxx (Potential memory leak)."
 
-    // Collect 1D-histograms
+    // Collect histograms
     for (auto& source : vHist.fvH1) {
-      bool add = TH1::AddDirectoryStatus();
-      TH1::AddDirectory(false);  // Needed to prevent ROOT from adding histogram to its internal registry
-      const char* hName = source.GetName().c_str();
-      const char* hTitl = source.GetTitle().c_str();
-      uint32_t nBinsX   = source.GetNbinsX();
-      double xMin       = source.GetMinX();
-      double xMax       = source.GetMaxX();
-      TH1* result       = new TH1D(hName, hTitl, nBinsX, xMin, xMax);
-      TH1::AddDirectory(add);  // Needed to prevent ROOT from adding histogram to its internal registry
-      for (uint32_t bin = 0; bin <= nBinsX + 1; bin++) {
-        result->SetBinContent(bin, source.GetBinContent(bin));
-      }
-      result->SetEntries(source.GetEntries());
+      TH1* result = cbm::qa::OnlineInterface::ROOTHistogram(source);
       if (!ReadHistogram<TH1>(result)) {  //
         return false;
       }
       delete result;
     }
-
-    // Collect 2D-histograms
     for (auto& source : vHist.fvH2) {
-      bool add = TH1::AddDirectoryStatus();
-      TH1::AddDirectory(false);  // Needed to prevent ROOT from adding histogram to its internal registry
-      const char* hName = source.GetName().c_str();
-      const char* hTitl = source.GetTitle().c_str();
-      uint32_t nBinsX   = source.GetNbinsX();
-      double xMin       = source.GetMinX();
-      double xMax       = source.GetMaxX();
-      uint32_t nBinsY   = source.GetNbinsY();
-      double yMin       = source.GetMinY();
-      double yMax       = source.GetMaxY();
-      TH1* result       = new TH2D(hName, hTitl, nBinsX, xMin, xMax, nBinsY, yMin, yMax);
-      TH1::AddDirectory(add);  // Needed to prevent ROOT from adding histogram to its internal registry
-      for (uint32_t binX = 0; binX <= nBinsX + 1; binX++) {
-        for (uint32_t binY = 0; binY <= nBinsY + 1; binY++) {
-          result->SetBinContent(binX, binY, source.GetBinContent(binX, binY));
-        }
+      TH1* result = cbm::qa::OnlineInterface::ROOTHistogram(source);
+      if (!ReadHistogram<TH1>(result)) {  //
+        return false;
+      }
+      delete result;
+    }
+    for (auto& source : vHist.fvP1) {
+      TH1* result = cbm::qa::OnlineInterface::ROOTHistogram(source);
+      if (!ReadHistogram<TH1>(result)) {  //
+        return false;
       }
-      result->SetEntries(source.GetEntries());
+      delete result;
+    }
+    for (auto& source : vHist.fvP2) {
+      TH1* result = cbm::qa::OnlineInterface::ROOTHistogram(source);
       if (!ReadHistogram<TH1>(result)) {  //
         return false;
       }
       delete result;
     }
 
+
     /// If new histos received, try to prepare as many canvases as possible
     /// Should be expensive on start and cheap afterward
     if (!fbAllCanvasReady) {
@@ -555,7 +540,10 @@ namespace cbm::services::histserv
         if ("nullptr" != sName) {
           TObject* pObj = fArrayHisto[FindHistogram(sName)];
 
-          if (nullptr != dynamic_cast<TProfile*>(pObj)) {
+          if (nullptr != dynamic_cast<TProfile2D*>(pObj)) {
+            dynamic_cast<TProfile2D*>(pObj)->Draw(conf.GetOption(uPadIdx, uObjIdx).data());
+          }  // if( nullptr != dynamic_cast< TProfile *>( pObj ) )
+          else if (nullptr != dynamic_cast<TProfile*>(pObj)) {
             dynamic_cast<TProfile*>(pObj)->Draw(conf.GetOption(uPadIdx, uObjIdx).data());
           }  // if( nullptr != dynamic_cast< TProfile *>( pObj ) )
           else if (nullptr != dynamic_cast<TH2*>(pObj)) {
diff --git a/services/histserv/app/CMakeLists.txt b/services/histserv/app/CMakeLists.txt
index 3d24099563..1435a08a01 100644
--- a/services/histserv/app/CMakeLists.txt
+++ b/services/histserv/app/CMakeLists.txt
@@ -5,6 +5,7 @@ set(INCLUDE_DIRECTORIES
   ${INCLUDE_DIRECTORIES}
   ${CMAKE_CURRENT_SOURCE_DIR}
   ${CBMROOT_SOURCE_DIR}/algo/qa/
+  ${CBMROOT_SOURCE_DIR}/core/qa/
   )
 
 set(SRCS
@@ -28,6 +29,7 @@ add_executable(histserv_nofairmq ${SRCS} ${HEADERS})
 target_link_libraries(histserv_nofairmq
   PUBLIC
     Algo
+    CbmQaBase
     CbmFlibFlesTools
     CbmServicesHistServ
   PRIVATE
-- 
GitLab