diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index 26ef7d4404885a88806f80d7d74286aa56979c1b..72869ece1142b2f3d30fa91edfe661db094f115d 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -123,13 +123,12 @@ set(SRCS global/RecoResultsOutputArchive.cxx qa/DigiEventQa.cxx qa/Histo1D.cxx + qa/HistogramContainer.cxx qa/CanvasConfig.cxx qa/PadConfig.cxx qa/QaData.cxx ca/TrackingChain.cxx ca/qa/CaQaBuilder.cxx - ca/qa/CaQaConfig.cxx - ca/qa/CaQaData.cxx ) set(BUILD_INFO_CXX ${CMAKE_CURRENT_BINARY_DIR}/base/BuildInfo.cxx) @@ -248,9 +247,6 @@ install( # NOTE: SZh 20.11.2023: # The ca/qa directory depends on the online qa classes, so for now it has to be a part of the Algo library. ca/qa/CaQaBuilder.h - ca/qa/CaQaConfig.h - ca/qa/CaQaData.h - ca/qa/CaQaDefinitions.h DESTINATION include/ ) diff --git a/algo/base/HistogramSender.h b/algo/base/HistogramSender.h index b44a1bfae4d6084179b842f50425bc01a589c036..ec9847312cc0dcaa2c930d8d24207c5a896ee169 100644 --- a/algo/base/HistogramSender.h +++ b/algo/base/HistogramSender.h @@ -29,7 +29,7 @@ namespace cbm::algo } /** @brief Serialize object and send it to the histogram server - ** @param obj: object to be serialized in the message, e.g. config pairs of strings or Histo1D + ** @param obj: object to be serialized in the message, e.g. config pairs of strings or QaData ** @param flags: or'ed values from zmq::send_flags, typ. zmq::send_flags::sndmore to indicate multi-parts message **/ template<typename Object> diff --git a/algo/ca/TrackingChain.cxx b/algo/ca/TrackingChain.cxx index f84d9f8fe0ad9895837559e99f9cb3d64b795579..b54a1dfc0ae9edaaee40de06157299343579a324 100644 --- a/algo/ca/TrackingChain.cxx +++ b/algo/ca/TrackingChain.cxx @@ -30,13 +30,14 @@ using cbm::algo::ca::Framework; using cbm::algo::ca::HitTypes_t; using cbm::algo::ca::InitManager; using cbm::algo::ca::Parameters; +using cbm::algo::ca::QaBuilder; using cbm::algo::ca::Track; using cbm::algo::ca::constants::clrs::CL; // clear text using cbm::algo::ca::constants::clrs::GNb; // grin bald text // --------------------------------------------------------------------------------------------------------------------- // -TrackingChain::TrackingChain(std::shared_ptr<HistogramSender> histoSender) : fQaBuilder(ca::qa::Builder(histoSender)) {} +TrackingChain::TrackingChain(std::shared_ptr<HistogramSender> histoSender) : fQaBuilder(QaBuilder(histoSender)) {} // --------------------------------------------------------------------------------------------------------------------- // diff --git a/algo/ca/TrackingChain.h b/algo/ca/TrackingChain.h index 694ed96be230008a4c12c7c6435a32b227ad66f4..43cd2b4ec95c2d282dc3e845404eb6920021f6c8 100644 --- a/algo/ca/TrackingChain.h +++ b/algo/ca/TrackingChain.h @@ -12,7 +12,6 @@ #include "CaDataManager.h" #include "CaFramework.h" #include "CaQaBuilder.h" -#include "CaQaConfig.h" #include "CaTrack.h" #include "CaTrackingMonitor.h" #include "CaVector.h" @@ -101,7 +100,7 @@ namespace cbm::algo ca::TrackingMonitorData fCaMonitorData{}; ///< CA monitor data object ca::Framework fCaFramework{}; ///< CA framework instance ca::DataManager fCaDataManager{}; ///< CA data manager - ca::qa::Builder fQaBuilder; ///< CA QA builder + ca::QaBuilder fQaBuilder; ///< CA QA builder // ************************ // ** Auxilary variables diff --git a/algo/ca/qa/CaQaBuilder.cxx b/algo/ca/qa/CaQaBuilder.cxx index 5a690e7fc776c92d1e8abbceda8ab961554b454f..87e224ef1801db69be7bfb5828e8feb54a1ea0d5 100644 --- a/algo/ca/qa/CaQaBuilder.cxx +++ b/algo/ca/qa/CaQaBuilder.cxx @@ -2,7 +2,7 @@ SPDX-License-Identifier: GPL-3.0-only Authors: Sergei Zharko [committer] */ -/// \file CaQaBuilder.cxx +/// \file CaQaQaBuilder.cxx /// \date 20.11.2023 /// \brief A QA module for CA tracking (implementation) /// \author S.Zharko <s.zharko@gsi.de> @@ -16,17 +16,18 @@ #include <fmt/format.h> -using cbm::algo::CanvasConfig; -using cbm::algo::Histo1D; -using cbm::algo::PadConfig; -using cbm::algo::QaData; +using cbm::algo::ca::QaBuilder; using cbm::algo::ca::constants::math::Pi; -using cbm::algo::ca::qa::Builder; +using cbm::algo::qa::CanvasConfig; +using cbm::algo::qa::Data; +using cbm::algo::qa::H1D; +using cbm::algo::qa::H2D; +using cbm::algo::qa::PadConfig; // --------------------------------------------------------------------------------------------------------------------- // -void Builder::Init() +void QaBuilder::Init() { using fmt::format; @@ -42,29 +43,35 @@ void Builder::Init() { auto sName = format("track_{}_theta", vsPointName[i]); auto sTitl = format("#theta at {} hit; #theta", vsPointName[i]); - fvphTrkTheta[i] = fQaData.MakeObj<Histo1D>(62, 0., 90., sName, sTitl); + fvphTrkTheta[i] = fQaData.MakeObj<H1D>(sName, sTitl, 62, 0., 90.); } { auto sName = format("track_{}_phi", vsPointName[i]); auto sTitl = format("#phi at {} hit; #phi", vsPointName[i]); - fvphTrkPhi[i] = fQaData.MakeObj<Histo1D>(62, -180., 180., sName, sTitl); + fvphTrkPhi[i] = fQaData.MakeObj<H1D>(sName, sTitl, 62, -180., 180.); + } + { + auto sName = format("track_{}_thata_phi", vsPointName[i]); + auto sTitl = format("#theta vs #phi at {} hit; #phi; #theta", vsPointName[i]); + fvphTrkPhiTheta[i] = fQaData.MakeObj<H2D>(sName, sTitl, 62, -180., 180., 62, 0., 90.); } { auto sName = format("track_{}_chi2_ndf", vsPointName[i]); auto sTitl = format("#chi^{{2}}/NDF at {} hit; #chi^{{2}}/NDF", vsPointName[i]); - fvphTrkChi2Ndf[i] = fQaData.MakeObj<Histo1D>(100, 0., 20., sName, sTitl); + fvphTrkChi2Ndf[i] = fQaData.MakeObj<H1D>(sName, sTitl, 100, 0., 20.); } { auto sName = format("track_{}_hit_station", vsPointName[i]); auto sTitl = format("Station of {} hit;ID_{{sta}}", vsPointName[i]); - fvphHitSta[i] = fQaData.MakeObj<Histo1D>(100, -.5, 10.5, sName, sTitl); + fvphHitSta[i] = fQaData.MakeObj<H1D>(sName, sTitl, 100, -.5, 10.5); } } - fphTrkNofHits = fQaData.MakeObj<Histo1D>(11, -0.5, 10.5, "n_hits", "Number of hits;N_{hit}"); + fphTrkNofHits = fQaData.MakeObj<H1D>("n_hits", "Number of hits;N_{hit}", 11, -0.5, 10.5); // ---- Init canvases { // Track parameters at first/last track hit + for (int i = 0; i < knTrkParPoints; ++i) { auto sCanvName = fmt::format("ca_trk_{}_hit", vsPointName[i]); auto sCanvTitl = fmt::format("Track parameters at {} hit", vsPointName[i]); @@ -86,6 +93,20 @@ void Builder::Init() fQaData.AddCanvasConfig(canv); } + { + auto sCanvName = "ca_trk_theta_phi"; + auto sCanvTitl = "Track #theta vs #phi"; + auto canv = CanvasConfig(sCanvName, sCanvTitl); + for (int i = 0; i < knTrkParPoints; ++i) { + PadConfig pad; + pad.SetLog(false, false, true); + pad.SetGrid(true, true); + pad.RegisterHistogram(fvphTrkPhiTheta[i], "colz"); + canv.AddPadConfig(pad); + } + fQaData.AddCanvasConfig(canv); + } + // Number of hits in track { auto canv = CanvasConfig("ca_nhits_in_trk", "Number of hit in track"); @@ -103,26 +124,26 @@ void Builder::Init() // --------------------------------------------------------------------------------------------------------------------- // -void Builder::Build() +void QaBuilder::Build() { if (!fpSender.get()) { return; } if (!fpParameters) { - LOG(error) << "cbm::algo::ca::qa::Builder::Build(): parameters object is undefined"; + LOG(error) << "cbm::algo::ca::QaBuilder::Build(): parameters object is undefined"; return; } if (!fpInputData) { - LOG(error) << "cbm::algo::ca::qa::Builder::Build(): input data object is undefined"; + LOG(error) << "cbm::algo::ca::QaBuilder::Build(): input data object is undefined"; return; } if (!fpvTracks) { - LOG(error) << "cbm::algo::ca::qa::Builder::Build(): tracks vector is undefined"; + LOG(error) << "cbm::algo::ca::QaBuilder::Build(): tracks vector is undefined"; return; } if (!fpvRecoHits) { - LOG(error) << "cbm::algo::ca::qa::Builder::Build(): reco hit indices vector is undefined"; + LOG(error) << "cbm::algo::ca::QaBuilder::Build(): reco hit indices vector is undefined"; return; } @@ -141,13 +162,14 @@ void Builder::Build() int iHit = (ip == 0 ? iFstHit : iLstHit); const auto& trkPar = (ip == 0 ? track.fParFirst : track.fParLast); const auto& hit = fpInputData->GetHit(iHit); - fvphTrkTheta[ip]->Add(trkPar.GetTheta() * 180. / Pi); - fvphTrkPhi[ip]->Add(trkPar.GetPhi() * 180. / Pi); - fvphTrkChi2Ndf[ip]->Add(trkPar.GetChiSq() / trkPar.GetNdf()); - fvphHitSta[ip]->Add(hit.Station()); + fvphTrkTheta[ip]->Fill(trkPar.GetTheta() * 180. / Pi); + fvphTrkPhi[ip]->Fill(trkPar.GetPhi() * 180. / Pi); + fvphTrkPhiTheta[ip]->Fill(trkPar.GetPhi() * 180. / Pi, trkPar.GetTheta() * 180. / Pi); + fvphTrkChi2Ndf[ip]->Fill(trkPar.GetChiSq() / trkPar.GetNdf()); + fvphHitSta[ip]->Fill(hit.Station()); } // Other distributions - fphTrkNofHits->Add(nHits); + fphTrkNofHits->Fill(nHits); trkFirstHit += nHits; } } diff --git a/algo/ca/qa/CaQaBuilder.h b/algo/ca/qa/CaQaBuilder.h index ff3293acdbbf5c2f5587d7532322f4beaae45f9a..5fa1f2321ec7a1e4a3809f1d2f022d879deed173 100644 --- a/algo/ca/qa/CaQaBuilder.h +++ b/algo/ca/qa/CaQaBuilder.h @@ -2,7 +2,7 @@ SPDX-License-Identifier: GPL-3.0-only Authors: Sergei Zharko [committer] */ -/// \file CaQaBuilder.h +/// \file CaQaQaBuilder.h /// \date 20.11.2023 /// \brief A QA module for CA tracking (header) /// \author S.Zharko <s.zharko@gsi.de> @@ -12,52 +12,54 @@ #include "CaHit.h" // for HitIndex_t #include "CaTimesliceHeader.h" #include "CaVector.h" -#include "QaData.h" +#include "QaData.h" // QA data namespace cbm::algo { - class Histo1D; - namespace ca + namespace qa { - template<typename DataT> - class Parameters; - class InputData; - class Track; - } // namespace ca -} // namespace cbm::algo::ca - -namespace cbm::algo::ca::qa + class H1D; + class H2D; + } // namespace qa +} // namespace cbm::algo + +namespace cbm::algo::ca { - /// \class cbm::algo::ca::qa::Builder - /// \brief Builder class for the CA tracking QA (header) + template<typename DataT> + class Parameters; + class InputData; + class Track; + + /// \class cbm::algo::ca::qa::QaBuilder + /// \brief QaBuilder class for the CA tracking QA (header) /// - class Builder { + class QaBuilder { public: using TrackV_t = ca::Vector<ca::Track>; using HitIndexV_t = ca::Vector<std::vector<std::pair<uint32_t, uint32_t>>>; /// \brief Default destructor /// \param pSender Pointer to the histogram sender - Builder(std::shared_ptr<HistogramSender> pSender) : fpSender(pSender){}; + QaBuilder(std::shared_ptr<HistogramSender> pSender) : fpSender(pSender){}; /// \brief Constructor from the configuration object /// \param config QA configuration object - Builder() = default; + QaBuilder() = default; /// \brief Copy constructor - Builder(const Builder&) = delete; + QaBuilder(const QaBuilder&) = delete; /// \brief Move constructor - Builder(Builder&&) = delete; + QaBuilder(QaBuilder&&) = delete; /// \brief Destructor - ~Builder() = default; + ~QaBuilder() = default; /// \brief Copy assignment operator - Builder& operator=(const Builder&) = delete; + QaBuilder& operator=(const QaBuilder&) = delete; /// \brief Move assignment operator - Builder& operator=(Builder&&) = delete; + QaBuilder& operator=(QaBuilder&&) = delete; /// \brief QA execution function void Build(); @@ -85,7 +87,7 @@ namespace cbm::algo::ca::qa void RegisterParameters(const Parameters<fvec>* pParameters) { fpParameters = pParameters; } private: - QaData fQaData{"CaQa"}; ///< QA data + qa::Data fQaData{"CaQa"}; ///< QA data std::shared_ptr<HistogramSender> fpSender = nullptr; ///< Histogram sender const Parameters<fvec>* fpParameters = nullptr; ///< Pointer to tracking parameters @@ -96,10 +98,11 @@ namespace cbm::algo::ca::qa // ----- List of the histograms ------------------------------------------------------------------------------------ static constexpr int knTrkParPoints = 2; ///< Number of track points to build par distributions - std::array<Histo1D*, knTrkParPoints> fvphTrkTheta = {{0}}; ///< hist: theta at first/last hit - std::array<Histo1D*, knTrkParPoints> fvphTrkPhi = {{0}}; ///< hist: phi at first/last hit - std::array<Histo1D*, knTrkParPoints> fvphTrkChi2Ndf = {{0}}; ///< hist: chi2/NDF at first/last hit - std::array<Histo1D*, knTrkParPoints> fvphHitSta = {{0}}; ///< hist: station of first/last hit - Histo1D* fphTrkNofHits = nullptr; ///< hist: number of hits in track + std::array<qa::H1D*, knTrkParPoints> fvphTrkTheta = {{0}}; ///< hist: theta at first/last hit + std::array<qa::H1D*, knTrkParPoints> fvphTrkPhi = {{0}}; ///< hist: phi at first/last hit + std::array<qa::H1D*, knTrkParPoints> fvphTrkChi2Ndf = {{0}}; ///< hist: chi2/NDF at first/last hit + std::array<qa::H1D*, knTrkParPoints> fvphHitSta = {{0}}; ///< hist: station of first/last hit + std::array<qa::H2D*, knTrkParPoints> fvphTrkPhiTheta = {{0}}; ///< hist: theta vs. phi at first/last hit + qa::H1D* fphTrkNofHits = nullptr; ///< hist: number of hits in track }; } // namespace cbm::algo::ca::qa diff --git a/algo/ca/qa/CaQaConfig.cxx b/algo/ca/qa/CaQaConfig.cxx deleted file mode 100644 index e9c696b7dad2fbdf0f6e327da8c3018aa7dc732f..0000000000000000000000000000000000000000 --- a/algo/ca/qa/CaQaConfig.cxx +++ /dev/null @@ -1,86 +0,0 @@ -/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt - SPDX-License-Identifier: GPL-3.0-only - Authors: Sergei Zharko [committer] */ - -/// \file CaQaConfig.cxx -/// \date 20.11.2023 -/// \brief A configuration class for the QA module of the CA tracking (implementation) -/// \author Sergei Zharko <s.zharko@gsi.de> - -#include "CaQaConfig.h" - -using cbm::algo::ca::qa::Config; - -// --------------------------------------------------------------------------------------------------------------------- -// -Config::Config() -{ - // Parameters initialization - // TODO: SZh 20.11.2023: Read from YAML - /* clang-format off */ - fvH1Pars[H1Key::FstTrkTheta] = { "track_first_theta", "#theta at first hit;#theta" , 62, 0., +90. }; - fvH1Pars[H1Key::FstTrkPhi] = { "track_first_phi" , "#phi at first hit;#phi" , 62, -180., +180. }; - fvH1Pars[H1Key::FstChi2Ndf] = { "track_first_chi2_ndf", "#chi^{2}/NDF at first hit;#chi^{2}/NDF", 100, 0., +20. }; - fvH1Pars[H1Key::FstHitSta] = { "first_hit_station", "Station of first hit;ID_{sta}" , 11, -0.5, 10.5 }; - fvH1Pars[H1Key::LstHitSta] = { "last_hit_station", "Station of last hit;ID_{sta}" , 11, -0.5, 10.5 }; - fvH1Pars[H1Key::TrkNofHits] = { "n_hits", "Number of hits;N_{hit}" , 11, -0.5, 10.5 }; - /* clang-format on */ - - // QA Canvas -} - -// --------------------------------------------------------------------------------------------------------------------- -// -std::vector<std::pair<std::string, std::string>> Config::GetHistogramConfigs() const -{ - std::vector<std::pair<std::string, std::string>> res; - auto nHistos = fvH1Pars.size(); - res.reserve(nHistos); - for (const auto& par : fvH1Pars) { - res.emplace_back(par.fName, fsQaName); - } - return res; -} - -// --------------------------------------------------------------------------------------------------------------------- -// -std::vector<std::pair<std::string, std::string>> Config::GetCanvasConfigs() const -{ - std::vector<std::pair<std::string, std::string>> res; - res.reserve(1); - { - std::string sConfig = "caQaSummary;CA QA Summary;3;2;"; - //sConfig += GetPadConfig({fvH1Pars[H1Key::FstTrkTx].fName}, 0, 1); - //sConfig += GetPadConfig({fvH1Pars[H1Key::FstTrkTy].fName}, 0, 1); - sConfig += GetPadConfig({fvH1Pars[H1Key::FstTrkTheta].fName}, 0, 1); - sConfig += GetPadConfig({fvH1Pars[H1Key::FstTrkPhi].fName}, 0, 1); - sConfig += GetPadConfig({fvH1Pars[H1Key::FstChi2Ndf].fName}, 0, 1); - sConfig += GetPadConfig({fvH1Pars[H1Key::FstHitSta].fName}, 0, 1); - sConfig += GetPadConfig({fvH1Pars[H1Key::LstHitSta].fName}, 0, 1); - sConfig += GetPadConfig({fvH1Pars[H1Key::TrkNofHits].fName}, 0, 1); - res.emplace_back("caQaSummary", sConfig); - } - return res; -} - -// --------------------------------------------------------------------------------------------------------------------- -// -std::string Config::GetPadConfig(const std::vector<std::string>& vHistNames, bool bLogX, bool bLogY) -{ - constexpr bool bGridX = true; - constexpr bool bGridY = true; - constexpr bool bLogZ = false; - - std::stringstream config; - config << bGridX << ',' << bGridY << ',' << bLogX << ',' << bLogY << ',' << bLogZ; - if (vHistNames.empty()) { - config << ','; - } - else { - for (auto& name : vHistNames) { - config << ",(" << name << ",hist)"; - } - } - config << ';'; - return config.str(); -} diff --git a/algo/ca/qa/CaQaConfig.h b/algo/ca/qa/CaQaConfig.h deleted file mode 100644 index 67f93eeae12570c053fba3dc6364d764896931d7..0000000000000000000000000000000000000000 --- a/algo/ca/qa/CaQaConfig.h +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt - SPDX-License-Identifier: GPL-3.0-only - Authors: Sergei Zharko [committer] */ - -/// \file CaQaConfig.h -/// \date 20.11.2023 -/// \brief A configuration class for the QA module of the CA tracking (header) -/// \author Sergei Zharko <s.zharko@gsi.de> - -#pragma once - -#include "CaQaDefinitions.h" - -#include <string> -#include <vector> - -namespace cbm::algo::ca::qa -{ - /// \brief Configuration structure for QA of the tracking - /// \struct cbm::algo::ca::qa::Config - class Config { - public: - /// \brief Default constructor - Config(); - - /// \brief Copy constructor - Config(const Config&) = default; - - /// \brief Move constructor - Config(Config&&) = default; - - /// \brief Destructor - ~Config() = default; - - /// \brief Access to 1D-histogram parameters - const H1KeyArray_t<H1Pars>& GetH1Pars() const { return fvH1Pars; } - - /// \brief Gets histograms configuration vector - /// \return A histogram configuration vector - /// - /// The returned configuration vector is a std::vector of std::pair of two strings. The first string is the - /// histogram name and the second string is the QA name (common for all the histograms within the QA module). - std::vector<std::pair<std::string, std::string>> GetHistogramConfigs() const; - - /// \brief Gets canvas configuration vector - /// \return A canvas configuration vector - /// - /// The returned configuration vector is a std::vector of std::pair of two strings. The first string is the - /// histogram name and the second string is the QA name (common for all the histograms within the QA module). - std::vector<std::pair<std::string, std::string>> GetCanvasConfigs() const; - - private: - /// \brief Gets pad configuration string - static std::string GetPadConfig(const std::vector<std::string>& vHistNames, bool bLogX, bool bLogY); - - std::string fsQaName = "CaQa"; ///< Name of the QA module - - H1KeyArray_t<H1Pars> fvH1Pars; ///< Parameters of 1D-histograms - std::string fsCanvasConfig = ""; ///< QA canvas configuration - }; -} // namespace cbm::algo::ca::qa diff --git a/algo/ca/qa/CaQaData.cxx b/algo/ca/qa/CaQaData.cxx deleted file mode 100644 index da495021f266cc1c85b3c1b0c974def04d296126..0000000000000000000000000000000000000000 --- a/algo/ca/qa/CaQaData.cxx +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt - SPDX-License-Identifier: GPL-3.0-only - Authors: Sergei Zharko [committer] */ - -/// \file CaQaData.h -/// \date 20.11.2023 -/// \brief A data structure for the QA module of the CA tracking (header) -/// \author S.Zharko <s.zharko@gsi.de> - -#include "CaQaData.h" - -#include <sstream> - -using cbm::algo::ca::qa::Config; -using cbm::algo::ca::qa::Data; - -// --------------------------------------------------------------------------------------------------------------------- -// -Data::Data(const Config& config) -{ - // 1D-histograms initialization - fvH1.reserve(static_cast<size_t>(H1Key::kEND)); - for (auto& par : config.GetH1Pars()) { - fvH1.emplace_back(par.fNumBins, par.fMinValue, par.fMaxValue, par.fName, par.fTitle); - } -} - -// --------------------------------------------------------------------------------------------------------------------- -// -std::string Data::ToString() const -{ - std::stringstream msg; - msg << "CA QA histogram status:"; - for (const auto& hist : fvH1) { - msg << '\n' << hist.ToString(); - } - return msg.str(); -} diff --git a/algo/ca/qa/CaQaData.h b/algo/ca/qa/CaQaData.h deleted file mode 100644 index 345651740f33f7afb10576b12a8dc1d24fd2f705..0000000000000000000000000000000000000000 --- a/algo/ca/qa/CaQaData.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt - SPDX-License-Identifier: GPL-3.0-only - Authors: Sergei Zharko [committer] */ - -/// \file CaQaData.h -/// \date 20.11.2023 -/// \brief A data structure for the QA module of the CA tracking (header) -/// \author S.Zharko <s.zharko@gsi.de> - -#pragma once - -#include "CaQaConfig.h" -#include "CaQaDefinitions.h" -#include "Histo1D.h" - -#include <string> -#include <vector> - -namespace cbm::algo::ca::qa -{ - /// \class cbm::algo::ca::qa::Data - /// \brief QA data structure for CA tracking - class Data { - public: - /// \brief Default constructor - Data() = delete; - - /// \brief Constructor from parameters - /// \param config QA configuration object - Data(const Config& config); - - /// \brief Copy constructor - Data(const Data&) = default; - - /// \brief Move constructor - Data(Data&&) = default; - - /// \brief Copy assignment operator - Data& operator=(const Data&) = default; - - /// \brief Move assignment operator - Data& operator=(Data&&) = default; - - /// \brief Destructor - ~Data() = default; - - /// \brief Accesses 1D-histogram - const Histo1D& H1(H1Key key) const { return fvH1[static_cast<size_t>(key)]; } - - /// \brief Accesses 1D-histogram (mutable) - Histo1D& H1(H1Key key) { return fvH1[static_cast<size_t>(key)]; } - - /// \brief Accesses the full histogram vector - const std::vector<Histo1D>& VectorOfH1() const { return fvH1; } - - /// \brief String representation of the data status - std::string ToString() const; - - private: - std::vector<Histo1D> fvH1 = {}; ///< Vector of 1D-histograms, indexed by H1Key - }; - -} // namespace cbm::algo::ca::qa diff --git a/algo/ca/qa/CaQaDefinitions.h b/algo/ca/qa/CaQaDefinitions.h deleted file mode 100644 index 2637d1e790369799be457185e970d009828f9cf4..0000000000000000000000000000000000000000 --- a/algo/ca/qa/CaQaDefinitions.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt - SPDX-License-Identifier: GPL-3.0-only - Authors: Sergei Zharko [committer] */ - -/// \file CaQaDefinitions.h -/// \date 20.11.2023 -/// \brief Definitions (enums and structures) for the cbm::algo::ca::qa namespace -/// \author S.Zharko <s.zharko@gsi.de> - -#pragma once - -#include "CaEnumArray.h" - -#include <iomanip> -#include <sstream> - -namespace cbm::algo::ca::qa -{ - /// \struct H1Pars - /// \brief Parameters of the 1D-histograms - /// - struct H1Pars { - std::string fName; ///< Histogram name - std::string fTitle; ///< Histogram title - uint32_t fNumBins; ///< Number of bins in histogram - double fMinValue; ///< Minimal value - double fMaxValue; ///< Maximal value - - /// \brief String representation of the object - std::string ToString() const - { - using std::setw; - std::stringstream msg; - msg << "nbins " << setw(8) << fNumBins << ", min " << setw(15) << fMinValue << ", max " << setw(15) << fMaxValue; - return msg.str(); - }; - }; - - /// \enum H1Key - /// \brief Keys for the 1D-histograms - /// - enum class H1Key - { - //TrackFstTx, ///< First hit: Slope of the track along x-axis - //TrackFstTy, ///< First hit: Slope of the track along y-axis - FstTrkTheta, ///< First hit: Polar angle of the track - FstTrkPhi, ///< First hit: Azimuthal angle of the track - FstChi2Ndf, ///< First hit: chi2 / NDF - FstHitSta, ///< Station index of first hit - LstHitSta, ///< Station index of last hit - TrkNofHits, ///< Number of hits in track - kEND ///< END - }; - - /// \brief Alias to array, indexed by H1Key - template<typename T> - using H1KeyArray_t = EnumArray<H1Key, T>; - -} // namespace cbm::algo::ca::qa diff --git a/algo/evbuild/EventbuildChain.cxx b/algo/evbuild/EventbuildChain.cxx index 2843a9843699a7ac7e8d299063f96d67bae830cb..79ab1941940bb9dea524a774d1aa0fb45b93c7e9 100644 --- a/algo/evbuild/EventbuildChain.cxx +++ b/algo/evbuild/EventbuildChain.cxx @@ -6,7 +6,7 @@ #include "CbmDigiTimeslice.h" #include "DigiData.h" -#include "Histo1D.h" +#include "HistogramContainer.h" #include "evbuild/Config.h" #include <sstream> @@ -51,7 +51,7 @@ EventbuildChain::EventbuildChain(const Config& config, std::shared_ptr<Histogram } /// => (empty) Histograms serialization and emission to close multi-part message - fSender->PrepareAndSendMsg(std::vector<Histo1D>{}, zmq::send_flags::none); + fSender->PrepareAndSendMsg(qa::HistogramContainer{}, zmq::send_flags::none); } } // ---------------------------------------------------------------------------- @@ -75,11 +75,14 @@ EventbuildChain::ResultType EventbuildChain::Run(const DigiData& timeslice) /// => Histograms serialization and emission if (fSender) { + L_(info) << "Running DigiEventQa"; // --- Run event QA DigiEventQaData qaData = fQa(events); + L_(info) << "Running DigiEventQa: done"; - fSender->PrepareAndSendMsg(qaData.fVectHistos, zmq::send_flags::none); - L_(info) << "Published histograms, nb: " << qaData.fVectHistos.size(); + fSender->PrepareAndSendMsg(qaData.fHistContainer, zmq::send_flags::none); + int nHistograms = std::distance(qaData.fHistContainer.fvH1.begin(), qaData.fHistContainer.fvH1.end()); + L_(info) << "Published histograms, nb: " << nHistograms; } // --- Some log diff --git a/algo/qa/CanvasConfig.cxx b/algo/qa/CanvasConfig.cxx index bfbb99c2f282468095454d3c93316c22e84756b1..e0c3e3cb5973643afb63c5e18d989f4be5eec026 100644 --- a/algo/qa/CanvasConfig.cxx +++ b/algo/qa/CanvasConfig.cxx @@ -12,8 +12,8 @@ #include <log.hpp> #include <sstream> -using cbm::algo::CanvasConfig; -using cbm::algo::PadConfig; +using cbm::algo::qa::CanvasConfig; +using cbm::algo::qa::PadConfig; // --------------------------------------------------------------------------------------------------------------------- // diff --git a/algo/qa/CanvasConfig.h b/algo/qa/CanvasConfig.h index 0efe885c1c57a8192520dafa3790c338130c7286..20088d8b9bba7065f1e8361265a482c5b8750dd0 100644 --- a/algo/qa/CanvasConfig.h +++ b/algo/qa/CanvasConfig.h @@ -16,7 +16,7 @@ #include <utility> #include <vector> -namespace cbm::algo +namespace cbm::algo::qa { /// \class CanvasConfig /// \brief A canvas configuration for the histogram server diff --git a/algo/qa/DigiEventQa.cxx b/algo/qa/DigiEventQa.cxx index b47b4993fa0dbf76ed76911260bef7c51f9b3ab5..3fb21feca31b337175a71013147fbcdc32cb1144 100644 --- a/algo/qa/DigiEventQa.cxx +++ b/algo/qa/DigiEventQa.cxx @@ -4,10 +4,15 @@ #include "DigiEventQa.h" +#include "Histogram.h" + +#include <functional> #include <iomanip> +#include <iostream> #include <sstream> #include <string> +using cbm::algo::qa::H1D; using std::string; using std::vector; @@ -17,16 +22,14 @@ namespace cbm::algo::evbuild // --- Execution -------------------------------------------------------- DigiEventQaData DigiEventQa::operator()(const vector<DigiEvent>& events) const { - // --- Instantiate return object DigiEventQaData result; - result.fVectHistos.reserve(fConfig.fData.size()); for (const auto& entry : fConfig.fData) { ECbmModuleId subsystem = entry.first; auto& detConfig = fConfig.fData.at(subsystem); - result.fVectHistos.emplace_back(detConfig.fNumBins, detConfig.fMinValue, detConfig.fMaxValue, - DigiEventQaConfig::GetDigiTimeHistoName(subsystem)); - result.fDigiTimeHistos.emplace(subsystem, result.fVectHistos.back()); + result.fDigiTimeHistos[subsystem] = + &(result.fHistContainer.fvH1.emplace_front(DigiEventQaConfig::GetDigiTimeHistoName(subsystem), "", + detConfig.fNumBins, detConfig.fMinValue, detConfig.fMaxValue)); } // --- Event loop. Fill histograms. @@ -43,7 +46,7 @@ namespace cbm::algo::evbuild // --- QA: digi time within event ---------------------------------------- - void DigiEventQa::QaDigiTimeInEvent(const DigiEvent& event, ECbmModuleId system, Histo1D& histo) const + void DigiEventQa::QaDigiTimeInEvent(const DigiEvent& event, ECbmModuleId system, H1D* histo) const { switch (system) { diff --git a/algo/qa/DigiEventQa.h b/algo/qa/DigiEventQa.h index 7df6945795d6d98204202256a60994c6a46a9c44..91961d46493f5f0fe9c540b809f32d01f9d0fd77 100644 --- a/algo/qa/DigiEventQa.h +++ b/algo/qa/DigiEventQa.h @@ -7,7 +7,7 @@ #include "CbmDefs.h" #include "DigiData.h" -#include "Histo1D.h" +#include "HistogramContainer.h" #include "evbuild/EventBuilderConfig.h" #include <gsl/span> @@ -24,9 +24,8 @@ namespace cbm::algo::evbuild ** @since 16 June 2023 **/ struct DigiEventQaData { - - std::vector<Histo1D> fVectHistos = {}; - std::map<ECbmModuleId, Histo1D&> fDigiTimeHistos = {}; + qa::HistogramContainer fHistContainer; + std::map<ECbmModuleId, qa::H1D*> fDigiTimeHistos = {}; size_t fNumEvents = 0; }; @@ -142,10 +141,10 @@ namespace cbm::algo::evbuild ** The templated class is required to implement the method double GetTime(). **/ template<class Digi> - void FillDeltaT(gsl::span<const Digi> digis, double eventTime, Histo1D& histo) const + void FillDeltaT(gsl::span<const Digi> digis, double eventTime, qa::H1D* histo) const { for (const Digi& digi : digis) - histo.Add(digi.GetTime() - eventTime); + histo->Fill(digi.GetTime() - eventTime); } /** @brief Fill histogram with digi time within event @@ -153,7 +152,7 @@ namespace cbm::algo::evbuild ** @param eventTime Time of event ** @param histo Histogram to be filled **/ - void QaDigiTimeInEvent(const DigiEvent& event, ECbmModuleId system, Histo1D& histo) const; + void QaDigiTimeInEvent(const DigiEvent& event, ECbmModuleId system, qa::H1D* histo) const; private: // members diff --git a/algo/qa/Histogram.h b/algo/qa/Histogram.h index 273b6578c097e35ef51307e3f4b7052994079c0a..adb2f5b80af74585359972a0691031bf3c1f2ab3 100644 --- a/algo/qa/Histogram.h +++ b/algo/qa/Histogram.h @@ -202,10 +202,16 @@ namespace cbm::algo::qa H1D() = default; /// \brief Copy constructor - explicit H1D(const H1D&) = default; + H1D(const H1D&) = default; /// \brief Move constructor - explicit H1D(H1D&&) = default; + H1D(H1D&&) = default; + + /// \brief Copy assignment operator + H1D& operator=(const H1D&) = default; + + /// \brief Move assignment operator + H1D& operator=(H1D&&) = default; /// \brief Fills histogram /// \param value Value @@ -262,10 +268,16 @@ namespace cbm::algo::qa H2D() = default; /// \brief Copy constructor - explicit H2D(const H2D&) = default; + H2D(const H2D&) = default; /// \brief Move constructor - explicit H2D(H2D&&) = default; + H2D(H2D&&) = default; + + /// \brief Copy assignment operator + H2D& operator=(const H2D&) = default; + + /// \brief Move assignment operator + H2D& operator=(H2D&&) = default; /// \brief Fills histogram /// \param valueX Value along x-axis diff --git a/algo/qa/HistogramContainer.cxx b/algo/qa/HistogramContainer.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a0fe588738daa6f7afd21b3648cf98509c0930b0 --- /dev/null +++ b/algo/qa/HistogramContainer.cxx @@ -0,0 +1,24 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file HistogramContainer.cxx +/// \date 29.02.2024 +/// \brief A histogram container for the histogram server (header) +/// \author Sergei Zharko <s.zharko@gsi.de> + +#include "HistogramContainer.h" + +using cbm::algo::qa::HistogramContainer; + +// --------------------------------------------------------------------------------------------------------------------- +// +void HistogramContainer::Reset() +{ + for (auto& h : fvH1) { + h.Reset(); + } + for (auto& h : fvH2) { + h.Reset(); + } +} diff --git a/algo/qa/HistogramContainer.h b/algo/qa/HistogramContainer.h new file mode 100644 index 0000000000000000000000000000000000000000..11dc4316cd80341741a29df198737ec92151c810 --- /dev/null +++ b/algo/qa/HistogramContainer.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file HistogramContainer.h +/// \date 29.02.2024 +/// \brief A histogram container for the histogram server (header) +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "Histogram.h" // for H1D, H2D + +#include <boost/serialization/forward_list.hpp> + +#include <forward_list> + +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 + + /// \brief Resets the histograms + void Reset(); + + private: + friend class boost::serialization::access; + template<class Archive> + void serialize(Archive& ar, const unsigned int /*version*/) + { + ar& fvH1; + ar& fvH2; + } + }; +} // namespace cbm::algo::qa diff --git a/algo/qa/PadConfig.cxx b/algo/qa/PadConfig.cxx index 4b9d1a0394e6fbd4005f91410daca602c85fabe8..93cdc8741be8a79c81136d4d493c66881d4e039b 100644 --- a/algo/qa/PadConfig.cxx +++ b/algo/qa/PadConfig.cxx @@ -12,7 +12,7 @@ #include <log.hpp> #include <sstream> -using cbm::algo::PadConfig; +using cbm::algo::qa::PadConfig; // --------------------------------------------------------------------------------------------------------------------- // diff --git a/algo/qa/PadConfig.h b/algo/qa/PadConfig.h index d95c1cdc1d7387d2aa0097616494ab052ef88bef..9c2e7bcf1a5b52bf5de08524b5cb087782281e9b 100644 --- a/algo/qa/PadConfig.h +++ b/algo/qa/PadConfig.h @@ -9,14 +9,14 @@ #pragma once -#include "Histo1D.h" +#include "Histogram.h" #include <string> #include <string_view> #include <utility> #include <vector> -namespace cbm::algo +namespace cbm::algo::qa { /// \class PadConfig /// \brief A pad configuration for the histogram server @@ -65,7 +65,9 @@ namespace cbm::algo /// \brief Registers a histogram in the pad /// \param hist Histogram object /// \param opt Draw options for the histogram - void RegisterHistogram(const Histo1D* hist, std::string_view opt) { RegisterObject(hist->Name(), opt); } + // TODO: implement a single function + void RegisterHistogram(const H1D* hist, std::string_view opt) { RegisterObject(hist->GetName(), opt); } + void RegisterHistogram(const H2D* hist, std::string_view opt) { RegisterObject(hist->GetName(), opt); } /// \brief Returns message config std::string ToString() const; diff --git a/algo/qa/QaData.cxx b/algo/qa/QaData.cxx index 955c1ba27c67966dde4db0f439b8079325f45a38..90151fe66a74a43429188878ec5910bd31ee3d1f 100644 --- a/algo/qa/QaData.cxx +++ b/algo/qa/QaData.cxx @@ -2,25 +2,32 @@ SPDX-License-Identifier: GPL-3.0-only Authors: Sergei Zharko [committer] */ -/// \file QaData.cxx +/// \file Data.cxx /// \date 12.02.2024 /// \brief A unified data-structure to handle QA objects for the online reconstruction (implementation) /// \author Sergei Zharko <s.zharko@gsi.de> #include "QaData.h" -using cbm::algo::QaData; +using cbm::algo::qa::Data; // --------------------------------------------------------------------------------------------------------------------- // -void QaData::Init(std::shared_ptr<HistogramSender> histSender) +void Data::Init(std::shared_ptr<HistogramSender> histSender) { if (histSender.get()) { // Forming a histogram config message std::vector<std::pair<std::string, std::string>> vHistCfgs; - vHistCfgs.reserve(std::distance(fvH1.begin(), fvH1.end())); - for (const auto& hist : fvH1) { - vHistCfgs.emplace_back(hist.Name(), fsName); + size_t nHistograms = 0; + // 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()); + vHistCfgs.reserve(nHistograms); + for (const auto& hist : fHistograms.fvH1) { + vHistCfgs.emplace_back(hist.GetName(), fsName); + } + for (const auto& hist : fHistograms.fvH2) { + vHistCfgs.emplace_back(hist.GetName(), fsName); } // Forming a canvas config message @@ -39,26 +46,17 @@ void QaData::Init(std::shared_ptr<HistogramSender> histSender) histSender->PrepareAndSendMsg(cfg, zmq::send_flags::sndmore); } // Histograms serialization and emission to close multi-part message - histSender->PrepareAndSendMsg(std::vector<Histo1D>{}, zmq::send_flags::none); - } -} - -// --------------------------------------------------------------------------------------------------------------------- -// -void QaData::Reset() -{ - // TODO: clear the vectors of other object types here as well - for (auto& hist : fvH1) { - hist.Clear(); + histSender->PrepareAndSendMsg(qa::HistogramContainer{}, zmq::send_flags::none); } } // --------------------------------------------------------------------------------------------------------------------- // -void QaData::Send(std::shared_ptr<HistogramSender> histoSender) +void Data::Send(std::shared_ptr<HistogramSender> histoSender) { - std::vector<Histo1D> vHisto1D{std::begin(fvH1), std::end(fvH1)}; - histoSender->PrepareAndSendMsg(vHisto1D, zmq::send_flags::none); - L_(info) << fsName << ": Published " << vHisto1D.size() << " 1D-histograms"; + 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"; this->Reset(); } diff --git a/algo/qa/QaData.h b/algo/qa/QaData.h index 263614458c37962cd41f83eef542f109b4945113..e4fcc5c170d4d4928ae1dec48e481f6900d1b378 100644 --- a/algo/qa/QaData.h +++ b/algo/qa/QaData.h @@ -2,7 +2,7 @@ SPDX-License-Identifier: GPL-3.0-only Authors: Sergei Zharko [committer] */ -/// \file QaData.cxx +/// \file Data.cxx /// \date 12.02.2024 /// \brief A unified data-structure to handle QA objects for the online reconstruction /// \author Sergei Zharko <s.zharko@gsi.de> @@ -10,40 +10,41 @@ #pragma once #include "CanvasConfig.h" -#include "Histo1D.h" +#include "HistogramContainer.h" #include "HistogramSender.h" -#include <forward_list> +#include <boost/serialization/forward_list.hpp> + #include <log.hpp> #include <memory> #include <string_view> #include <type_traits> #include <vector> -namespace cbm::algo +namespace cbm::algo::qa { - /// \class QaData + /// \class Data /// \brief Class to handle QA-objects in the online reconstruction - class QaData { + class Data { public: /// \brief Constructor /// \param name Name of the QA module (appears as the directory name in the output) - QaData(std::string_view name) : fsName(name) {} + Data(std::string_view name) : fsName(name) {} /// \brief Copy constructor - QaData(const QaData&) = default; + Data(const Data&) = default; /// \brief Move constructor - QaData(QaData&&) = default; + Data(Data&&) = default; /// \brief Copy assignment operator - QaData& operator=(const QaData&) = default; + Data& operator=(const Data&) = default; /// \brief Move assignment operator - QaData& operator=(QaData&&) = default; + Data& operator=(Data&&) = default; /// \brief Destructor - ~QaData() = default; + ~Data() = default; /// \brief Adds a canvas to the canvas config list /// \param canvas A CanvasConfig object @@ -61,7 +62,7 @@ namespace cbm::algo void Init(std::shared_ptr<HistogramSender> histoSender); /// \brief Resets the histograms - void Reset(); + void Reset() { fHistograms.Reset(); } /// \brief Sends QA data to the HistogramSender /// \param histoSender A pointer to the histogram sender @@ -70,17 +71,20 @@ namespace cbm::algo private: std::string fsName; ///< Name of the QA module (used as a directory name) - std::forward_list<Histo1D> fvH1 = {}; ///< List of 1D-histograms + qa::HistogramContainer fHistograms; ///< Histograms container std::vector<std::string> fvsCanvCfgs = {}; ///< Vector of canvas configs }; // ------------------------------------------------------------------------------------------------------------------- // template<class Obj, typename... Args> - Obj* QaData::MakeObj(Args... args) + Obj* Data::MakeObj(Args... args) { - if constexpr (std::is_same_v<Obj, cbm::algo::Histo1D>) { - return &(fvH1.emplace_front(args...)); + if constexpr (std::is_same_v<Obj, cbm::algo::qa::H1D>) { + return &(fHistograms.fvH1.emplace_front(args...)); + } + else if constexpr (std::is_same_v<Obj, cbm::algo::qa::H2D>) { + return &(fHistograms.fvH2.emplace_front(args...)); } return nullptr; } diff --git a/reco/tasks/CbmTaskDigiEventQa.cxx b/reco/tasks/CbmTaskDigiEventQa.cxx index 1ea9b16047eb207ae990c05aefb1751bc5994f7a..f8a71bb9556345f412eea817bac9a7ecb99e81b2 100644 --- a/reco/tasks/CbmTaskDigiEventQa.cxx +++ b/reco/tasks/CbmTaskDigiEventQa.cxx @@ -80,7 +80,7 @@ void CbmTaskDigiEventQa::Exec(Option_t*) // which is then added to the member histogram (another data copy). Should implement a method for direct addition TH1D + Histo1D. for (const auto& entry : result.fDigiTimeHistos) { ECbmModuleId subsystem = entry.first; - fDigiTimeHistos[subsystem]->Add(ToTH1D(entry.second)); + fDigiTimeHistos[subsystem]->Add(ToTH1D(*entry.second)); } // --- Timeslice log @@ -159,19 +159,17 @@ InitStatus CbmTaskDigiEventQa::Init() // ----- Convert CBM histogram to ROOT histogram -------------------------- -TH1D* CbmTaskDigiEventQa::ToTH1D(const cbm::algo::Histo1D& source) +TH1D* CbmTaskDigiEventQa::ToTH1D(const cbm::algo::qa::H1D& source) { bool add = TH1::AddDirectoryStatus(); TH1::AddDirectory(false); // Needed to prevent ROOT from adding histogram to its internal registry - TH1D* result = - new TH1D(source.Name().c_str(), source.Name().c_str(), source.NumBins(), source.MinValue(), source.MaxValue()); + TH1D* result = new TH1D(source.GetName().c_str(), source.GetName().c_str(), source.GetNbinsX(), source.GetMinX(), + source.GetMaxX()); TH1::AddDirectory(add); // Needed to prevent ROOT from adding histogram to its internal registry - for (uint32_t bin = 0; bin < source.NumBins(); bin++) { - result->SetBinContent(1 + bin, source.Content(bin)); + for (uint32_t bin = 0; bin <= source.GetNbinsX() + 1; bin++) { + result->SetBinContent(bin, source.GetBinContent(bin)); } - result->SetBinContent(0, source.Underflow()); - result->SetBinContent(source.NumBins(), source.Overflow()); - result->SetEntries(source.NumEntries()); + result->SetEntries(source.GetEntries()); return result; } // ---------------------------------------------------------------------------- diff --git a/reco/tasks/CbmTaskDigiEventQa.h b/reco/tasks/CbmTaskDigiEventQa.h index ebd65667530c3229353dd4ff17d2bbd14fcf26f3..f338a293793d15691e06cb00f16ff2a3b91de410 100644 --- a/reco/tasks/CbmTaskDigiEventQa.h +++ b/reco/tasks/CbmTaskDigiEventQa.h @@ -68,14 +68,14 @@ private: // methods virtual InitStatus Init(); - /** @brief Create a ROOT TH1D from a Histo1D object + /** @brief Create a ROOT TH1D from a H1D object ** @param Source histogram ** @param ROOT histogram */ - TH1D* ToTH1D(const cbm::algo::Histo1D& source); + TH1D* ToTH1D(const cbm::algo::qa::H1D& source); -private: // members + private: // members const std::vector<CbmDigiEvent>* fEvents = nullptr; //! Input data (events) size_t fNumTs = 0; ///< Number of processed timeslices size_t fNumEvents = 0; ///< Number of analysed events diff --git a/services/histserv/app/Application.cxx b/services/histserv/app/Application.cxx index 8a565c581123be174560a58528d9144ca56a1ed0..0964416f9f05a9d50296dc5350e18dc7181d994b 100644 --- a/services/histserv/app/Application.cxx +++ b/services/histserv/app/Application.cxx @@ -5,9 +5,7 @@ #include "Application.h" #include "CbmFlesCanvasTools.h" - -#include <Logger.h> - +#include "HistogramContainer.h" #include "TCanvas.h" #include "TEnv.h" #include "TFile.h" @@ -20,6 +18,8 @@ #include "TRootSniffer.h" #include "TSystem.h" +#include <Logger.h> + #include <boost/archive/binary_iarchive.hpp> #include <boost/iostreams/device/array.hpp> #include <boost/iostreams/stream.hpp> @@ -29,8 +29,6 @@ #include <mutex> #include <zmq_addon.hpp> -#include "Histo1D.h" - std::mutex mtx; namespace b_io = boost::iostreams; @@ -163,25 +161,56 @@ namespace cbm::services::histserv b_io::stream<b_io::basic_array_source<char>> s(device); b_ar::binary_iarchive iarch(s); - std::vector<cbm::algo::Histo1D> vHist; + cbm::algo::qa::HistogramContainer vHist; iarch >> vHist; - for (auto& source : vHist) { - /// copied from CbmTaskDigiEventQa::ToTH1D - /// FIXME: Should be placed in a tools/interface/whatever library with all similar functions!! - /// 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)." + /// 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 + 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()); + 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 - TH1D* result = new TH1D(source.Name().c_str(), source.Title().c_str(), // Titles in ROOT convention with ";" sep - source.NumBins(), source.MinValue(), source.MaxValue()); + 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 bin = 0; bin < source.NumBins(); bin++) { - result->SetBinContent(1 + bin, source.Content(bin)); + 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)); + } } - result->SetBinContent(0, source.Underflow()); - result->SetBinContent(source.NumBins(), source.Overflow()); - result->SetEntries(source.NumEntries()); + result->SetEntries(source.GetEntries()); if (!ReadHistogram<TH1>(result)) { // return false; } diff --git a/services/histserv/tester/Application.cxx b/services/histserv/tester/Application.cxx index 9c2b9fbdbc11bb37a41592deddaa3d7dfeca1b0c..f6a675dc7261ad4600ae289241c857ef15d1c25a 100644 --- a/services/histserv/tester/Application.cxx +++ b/services/histserv/tester/Application.cxx @@ -4,7 +4,12 @@ #include "Application.h" +#include "CanvasConfig.h" #include "CbmFlesCanvasTools.h" +#include "Histogram.h" +#include "PadConfig.h" +#include "QaData.h" +#include "ui_callbacks.h" #include <Logger.h> @@ -17,9 +22,6 @@ #include <thread> -#include "Histo1D.h" -#include "ui_callbacks.h" - std::mutex mtx; namespace b_io = boost::iostreams; @@ -37,12 +39,13 @@ namespace cbm::services::histserv_tester LOG(info) << "Options for Application."; LOG(info) << " Output ZMQ channel: " << fOpt.ComChan(); LOG(info) << " Run time duration: " << fOpt.Runtime() << " s"; + fpSender = std::make_shared<cbm::algo::HistogramSender>(fOpt.ComChan()); /// FIXME: SOMETHING_To_Replace_FairMQ!!!!!!!!!!!!! /// FIXME: Initialize communication channels of SOMETHING_To_Replace_FairMQ /// FIXME: Link channel to method in order to process received messages - // fZmqSocket.set(zmq::sockopt::rcvhwm, int(hwm)); // FIXME: need for HWM? - fZmqSocket.connect(fOpt.ComChan().c_str()); // This side "connects" to socket => Other side should have "bind"!!!! + // fZmqSocket.set(zmq::sockopt::rcvhwm, int(hwm)); // FIXME: need for HWM? (NOTE: SZh 29.02.2024: if needed, move it + // do it in the HistogramSender) } // ------------------------------------------------------------------------------------------------------------------- @@ -50,6 +53,11 @@ namespace cbm::services::histserv_tester // ----- Main Loop ----------------------------------------------------------------------------------------------- void Application::Exec() { + using cbm::algo::qa::CanvasConfig; + using cbm::algo::qa::Data; + using cbm::algo::qa::H1D; + using cbm::algo::qa::PadConfig; + const std::chrono::milliseconds interval {250}; const std::chrono::seconds runtime(fOpt.Runtime()); const std::chrono::seconds pubint(fOpt.PubInterval()); @@ -57,72 +65,66 @@ namespace cbm::services::histserv_tester sctp stopTime = startTime + runtime; sctp lastPubTime = startTime; - std::vector<cbm::algo::Histo1D> vHist; - vHist.emplace_back((fOpt.Runtime() + 2), -1.0, fOpt.Runtime() + 1.0, "testHist", - "Tester source; Runtime [s]; Entries to histo [iterations]"); - vHist.emplace_back(1001, -0.025, 50.025, "transHist", - "Tester histos transmission time; Trans. time [ms]; Messages []"); + // Init QA data helper + Data qaData("Test"); + auto* pHistTest = qaData.MakeObj<H1D>("testHist", "Tester source; Runtime [s]; Entries to histo [iterations]", + fOpt.Runtime() + 2, -1.0, fOpt.Runtime() + 1.0); + auto* pHistTrans = qaData.MakeObj<H1D>( + "transHist", "Tester histos transmission time; Trans. time [ms]; Messages []", 1001, -0.025, 50.025); /// Initial emission, including generation and serialization of configs /// => Try to evaluate "time cost" of the histo transmission, including serialization sctp transStartTime = scsc::now(); - /// => Header for multi-part message with Configuration + data - /// => Format: std::pair< Nb histogram configs, Nb canvas configs > - std::pair<uint32_t, uint32_t> pHeader(vHist.size(), 1); - PrepareAndSendMsg(pHeader, zmq::send_flags::sndmore); - - /// => Histograms configuration = destination folder in http browser, mandatory but can be empty (= root folder) - /// => 1 ZMQ message per histogram (= 1 part) - /// => If no (new) histograms declared (e.g. new canvas declaration), has to be en empty message + `0` in the header - std::pair<std::string, std::string> pFirstHist(vHist[0].Name(), "TestFolder"); - std::pair<std::string, std::string> pSecondHist(vHist[1].Name(), "TestFolder/TestSubFolder"); - PrepareAndSendMsg(pFirstHist, zmq::send_flags::sndmore); - PrepareAndSendMsg(pSecondHist, zmq::send_flags::sndmore); - - /// => Canvas configuration = in old code extracted from canvas object, here need to be made by hand - /// => 1 ZMQ message per canvas (= 1 part) - /// => If no (new) canvas declared (e.g. only histos declaration), has to be en empty message + `0` in the header - /// => Format is "CanvasName;Canvas Title;NbPadX(U);NbPadY(U);ConfigPad1(s);....;ConfigPadXY(s)" - /// => Format of Pad config is - /// "GrixX(b),GridY(b),LogX(b),LogY(b),LogZ(b),(HistoName1,DrawOptions1),...,(HistoNameZ,DrawOptionsZ)" - /// => See core/base/utils/fles/CbmFlesCanvasTools for the full code, especially GenerateCanvasConfigString - /// (CanvasConfig class not used here as library loading would need binding to ROOT library) - std::pair<std::string, std::string> pCanvConfig("TestCanvas", "TestCanvas;Canvas configs testing;2;2;"); - pCanvConfig.second += "0,0,0,0,0,(" + vHist[0].Name() + ",hist);"; // Pad 0 - pCanvConfig.second += "1,1,0,0,0,(" + vHist[0].Name() + ",hist);"; // Pad 1 - pCanvConfig.second += "1,1,1,0,0,(" + vHist[1].Name() + ",hist);"; // Pad 2 - pCanvConfig.second += "1,1,1,1,0,(" + vHist[1].Name() + ",hist),(" + vHist[0].Name() + ",hist same);"; // Pad 3 - PrepareAndSendMsg(pCanvConfig, zmq::send_flags::sndmore); - - /// => (empty) Histograms serialization and emission - PrepareAndSendMsg(vHist, zmq::send_flags::none); + { + auto canv = CanvasConfig("TestCanvas", "TestCanvas"); + auto pad1 = PadConfig(); + pad1.SetGrid(false, false); + pad1.SetLog(false, false, false); + pad1.RegisterHistogram(pHistTest, "hist"); + canv.AddPadConfig(pad1); + auto pad2 = PadConfig(); + pad2.SetGrid(true, true); + pad2.SetLog(false, false, false); + pad2.RegisterHistogram(pHistTest, "hist"); + canv.AddPadConfig(pad2); + auto pad3 = PadConfig(); + pad3.SetGrid(true, true); + pad3.SetLog(true, false, false); + pad3.RegisterHistogram(pHistTrans, "hist"); + canv.AddPadConfig(pad3); + auto pad4 = PadConfig(); + pad4.SetGrid(true, true); + pad4.SetLog(true, false, false); + pad4.RegisterHistogram(pHistTrans, "hist"); + pad4.RegisterHistogram(pHistTest, "hist same"); + canv.AddPadConfig(pad4); + qaData.AddCanvasConfig(canv); + } + qaData.Init(fpSender); // Init the QA data + lastPubTime = scsc::now(); /// No general references as member/variable bec. simple example, use directly hardcoded vector "array access" - vHist[1].Add(std::chrono::duration_cast<std::chrono::microseconds>(lastPubTime - transStartTime).count() / 1e3); + pHistTrans->Fill(std::chrono::duration_cast<std::chrono::microseconds>(lastPubTime - transStartTime).count() / 1e3); while (scsc::now() < stopTime) { // /// No general references as member/variable bec. simple example, use directly hardcoded vector "array access" - vHist[0].Add(std::chrono::duration_cast<std::chrono::milliseconds>(scsc::now() - startTime).count() / 1e3); + pHistTest->Fill(std::chrono::duration_cast<std::chrono::milliseconds>(scsc::now() - startTime).count() / 1e3); if (pubint < scsc::now() - lastPubTime) { - LOG(info) << "Pub-hist: " << scsc::now().time_since_epoch().count() << " " << vHist[0].NumEntries(); + LOG(info) << "Pub-hist: " << scsc::now().time_since_epoch().count() << " " << pHistTest->GetEntries(); /// Try to evaluate "time cost" of the histo transmission, including serialization transStartTime = scsc::now(); /// => Histograms serialization and emission - PrepareAndSendMsg(vHist, zmq::send_flags::none); - - /// => Reset all histograms as they are added on the other end! - for (std::vector<cbm::algo::Histo1D>::iterator itHist = vHist.begin(); itHist != vHist.end(); ++itHist) { + qaData.Send(fpSender); - itHist->Clear(); - } lastPubTime = scsc::now(); /// No general references as member/variable bec. simple example, use directly hardcoded vector "array access" - vHist[1].Add(std::chrono::duration_cast<std::chrono::microseconds>(lastPubTime - transStartTime).count() / 1e3); + pHistTrans->Fill(std::chrono::duration_cast<std::chrono::microseconds>(lastPubTime - transStartTime).count() + / 1e3); } std::this_thread::sleep_for(interval); @@ -132,32 +134,8 @@ namespace cbm::services::histserv_tester } /// Final publications - LOG(info) << "Pub-hist: " << scsc::now().time_since_epoch().count() << " " << vHist[0].NumEntries(); + LOG(info) << "Pub-hist: " << scsc::now().time_since_epoch().count() << " " << pHistTest->GetEntries(); /// => Histograms serialization and emission - PrepareAndSendMsg(vHist, zmq::send_flags::none); - /// => Reset all histograms as they are added on the other end! - for (std::vector<cbm::algo::Histo1D>::iterator itHist = vHist.begin(); itHist != vHist.end(); ++itHist) { - itHist->Clear(); - } - } - // ------------------------------------------------------------------------------------------------------------------- - - template<class Object> - void Application::PrepareAndSendMsg(Object& obj, zmq::send_flags flags) - { - /// Needed ressources (serializd string, boost inserter, boost stream, boost binary output archive) - std::string serial_str; - b_io::back_insert_device<std::string> inserter(serial_str); - b_io::stream<b_io::back_insert_device<std::string>> bstream(inserter); - b_ar::binary_oarchive oa(bstream); - - serial_str.clear(); - oa << obj; - bstream.flush(); - - zmq::message_t msg(serial_str.size()); - std::copy_n(static_cast<const char*>(serial_str.data()), msg.size(), static_cast<char*>(msg.data())); - fZmqSocket.send(msg, flags); + qaData.Send(fpSender); } - } // namespace cbm::services::histserv_tester diff --git a/services/histserv/tester/Application.h b/services/histserv/tester/Application.h index 61887852138aef9c84211278424a7fef26e4c623..ba000e0be4016fe6cf6cc10840596809c94934f4 100644 --- a/services/histserv/tester/Application.h +++ b/services/histserv/tester/Application.h @@ -1,20 +1,20 @@ -/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt +/* Copyright (C) 2023-2024 Facility for Antiproton and Ion Research in Europe, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Pierre-Alain Loizeau [committer] */ + Authors: Pierre-Alain Loizeau [committer], Sergei Zharko */ #ifndef CBM_SERVICES_HISTSERV_TESTER_APPLICATION_H #define CBM_SERVICES_HISTSERV_TESTER_APPLICATION_H 1 -#include <zmq.hpp> - +#include "HistogramSender.h" #include "ProgramOptions.h" +#include <memory> + namespace cbm::services::histserv_tester { class Application { - public: /** @brief Standard constructor, initialises the application ** @param opt **/ @@ -23,8 +23,14 @@ namespace cbm::services::histserv_tester /** @brief Copy constructor forbidden **/ Application(const Application&) = delete; - /** @brief Assignment operator forbidden **/ - void operator=(const Application&) = delete; + /** @brief Move constructor forbidden **/ + Application(Application&&) = delete; + + /** @brief Copy assignment operator forbidden **/ + Application& operator=(const Application&) = delete; + + /** @brief Move assignment operator forbidden **/ + Application& operator=(Application&&) = delete; /** @brief Destructor **/ ~Application() = default; @@ -32,16 +38,11 @@ namespace cbm::services::histserv_tester /** @brief Run the application **/ void Exec(); - private: - template<class Object> - void PrepareAndSendMsg(Object& obj, zmq::send_flags flags); - private: ProgramOptions const& fOpt; ///< Program options object /// Interface - zmq::context_t fZmqContext {1}; - zmq::socket_t fZmqSocket {fZmqContext, ZMQ_PUSH}; + std::shared_ptr<cbm::algo::HistogramSender> fpSender = nullptr; }; } // namespace cbm::services::histserv_tester