diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index 4abc148e95b04a5ef0aab0ee5332601f23703194..378c2d50bba8c75bfc95df776e82a15de6e5631f 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -127,6 +127,7 @@ set(SRCS qa/CanvasConfig.cxx qa/PadConfig.cxx qa/QaData.cxx + qa/unpack/StsDigiQa.cxx ca/TrackingChain.cxx ca/qa/CaInputQa.cxx ca/qa/CaOutputQa.cxx @@ -163,6 +164,7 @@ target_include_directories(Algo ${CMAKE_CURRENT_SOURCE_DIR}/detectors ${CMAKE_CURRENT_SOURCE_DIR}/qa ${CMAKE_CURRENT_SOURCE_DIR}/ca + ${CMAKE_CURRENT_SOURCE_DIR}/qa/unpack ${CMAKE_CURRENT_SOURCE_DIR}/ca/qa ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/core/data/global @@ -226,6 +228,8 @@ install(DIRECTORY detectors/tof TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY detectors/trd TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY detectors/trd2d TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY ca/qa TYPE INCLUDE FILES_MATCHING PATTERN "*.h") +install(DIRECTORY qa TYPE INCLUDE FILES_MATCHING PATTERN "*.h") +install(DIRECTORY qa/unpack TYPE INCLUDE FILES_MATCHING PATTERN "*.h") install(DIRECTORY ca TYPE INCLUDE FILES_MATCHING PATTERN "*.h") diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx index 9ad4d2d9a054e46d6aa09251866e822df5da87de..22f753ef4624c5e595241f4d71a9dcf6edb38600 100644 --- a/algo/global/Reco.cxx +++ b/algo/global/Reco.cxx @@ -8,6 +8,7 @@ #include "EventbuildChain.h" #include "Exceptions.h" #include "HistogramSender.h" +#include "StsDigiQa.h" #include "bmon/Unpack.h" #include "ca/TrackingChain.h" #include "compat/OpenMP.h" @@ -115,6 +116,11 @@ void Reco::Init(const Options& opts) auto walkMap = yaml::ReadFromFile<sts::WalkMap>(Opts().ParamsDir() / "StsWalkMap.yaml"); sts::Unpack::Config cfg{.readout = readout, .walkMap = walkMap}; fStsUnpack = std::make_unique<sts::Unpack>(cfg); + if (fSender != nullptr) { + fStsDigiQa = std::make_unique<sts::DigiQa>(fSender); + fStsDigiQa->RegisterReadoutSetup(readoutSetup); + fStsDigiQa->Init(); + } } if (Opts().Has(Subsystem::TOF) && Opts().Has(Step::Unpack)) { @@ -214,6 +220,12 @@ RecoResults Reco::Run(const fles::Timeslice& ts) QueueEvbuildMetrics(evbuildMonitor); } + // --- Raw digi QAs + if (fSender != nullptr && Opts().Has(Subsystem::STS) && Opts().Has(Step::Unpack)) { + fStsDigiQa->RegisterDigiData(&digis.fSts); + fStsDigiQa->Exec(); + } + sts::HitfinderMonitor stsHitfinderMonitor; PartitionedSpan<sts::Hit> stsHits; PartitionedVector<sts::Cluster> stsClusters; diff --git a/algo/global/Reco.h b/algo/global/Reco.h index 6846544a888e080c1812cecf6e55ffe70474fa7b..9a193027e82724331cce58bcbd22bf066c2f912d 100644 --- a/algo/global/Reco.h +++ b/algo/global/Reco.h @@ -45,6 +45,7 @@ namespace cbm::algo namespace sts { class Unpack; + class DigiQa; } namespace tof @@ -121,6 +122,7 @@ namespace cbm::algo // STS std::unique_ptr<sts::Unpack> fStsUnpack; + std::unique_ptr<sts::DigiQa> fStsDigiQa; ///< Raw STS-digis QA sts::HitfinderChain fStsHitFinder; // TOF diff --git a/algo/qa/CanvasConfig.cxx b/algo/qa/CanvasConfig.cxx index e0c3e3cb5973643afb63c5e18d989f4be5eec026..653e33c5471da955d5d468caf3d7abc086d347bd 100644 --- a/algo/qa/CanvasConfig.cxx +++ b/algo/qa/CanvasConfig.cxx @@ -60,5 +60,5 @@ std::string CanvasConfig::ToString() const cfg << ';' << padCfg; } } - return cfg.str(); + return cfg.str() + ";"; } diff --git a/algo/qa/QaData.cxx b/algo/qa/QaData.cxx index da0ed39365552dfb17946660ee03581cc6615d83..7ab94afa0fe4d54277b194b087a8237d2b864fa5 100644 --- a/algo/qa/QaData.cxx +++ b/algo/qa/QaData.cxx @@ -68,6 +68,6 @@ void Data::Send(std::shared_ptr<HistogramSender> histoSender) 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"; + << " 2D-profiles"; this->Reset(); } diff --git a/algo/qa/unpack/QaBase.h b/algo/qa/unpack/QaBase.h new file mode 100644 index 0000000000000000000000000000000000000000..c8b2e1a05b4984c0a4164414f565d38c65f4740d --- /dev/null +++ b/algo/qa/unpack/QaBase.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file QaBase.h +/// \date 03.03.2024 +/// \brief Base class for digi QA (header) +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "Definitions.h" +#include "PODVector.h" +#include "QaData.h" + +#include <string> + +namespace cbm::algo::sts +{ + /// \class cbm::algo::sts::QaBase + /// \brief QA module for STS raw digis + /// \tparam Digi A digi class for a given detector subsystem + /// \tparam ReadoutSetup A read-out config for a given detector subsystem + template<class Digi, class ReadoutSetup> + class QaBase { + public: + /// \brief Constructor + /// \param pSender Pointer to histogram sender + /// \param dirname + QaBase(std::shared_ptr<HistogramSender> pSender, const std::string& dirname) : fQaData(dirname), fpSender(pSender) + { + } + + /// \brief Default constructor + QaBase() = delete; + + /// \brief Copy constructor + QaBase(const QaBase&) = delete; + + /// \brief Move constructor + QaBase(QaBase&&) = delete; + + /// \brief Copy assignment operator + QaBase& operator=(const QaBase&) = delete; + + /// \brief Move assignment operator + QaBase& operator=(QaBase&&) = delete; + + /// \brief Checks, if the histogram sender is defined + bool IsSenderDefined() const { return static_cast<bool>(fpSender.get()); } + + /// \brief Register digi-qa data + void RegisterDigiData(const PODVector<Digi>* pvDigis) { fpvDigis = pvDigis; } + + /// \brief Register read-out setup config + void RegisterReadoutSetup(const ReadoutSetup& setup) { fpReadoutSetup = std::make_shared<ReadoutSetup>(setup); } + + protected: + qa::Data fQaData; ///< QA data + std::shared_ptr<HistogramSender> fpSender = nullptr; ///< Histogram sender + + std::shared_ptr<ReadoutSetup> fpReadoutSetup = nullptr; ///< Readout config instance + const PODVector<Digi>* fpvDigis = nullptr; ///< Digis input + }; +} // namespace cbm::algo::sts diff --git a/algo/qa/unpack/StsDigiQa.cxx b/algo/qa/unpack/StsDigiQa.cxx new file mode 100644 index 0000000000000000000000000000000000000000..db9a8b0472a515519d7967311d90c909930cb7dc --- /dev/null +++ b/algo/qa/unpack/StsDigiQa.cxx @@ -0,0 +1,107 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file StsDigiQa.h +/// \date 03.03.2024 +/// \brief QA module for STS raw digis (source) +/// \author Sergei Zharko <s.zharko@gsi.de> + +#include "StsDigiQa.h" + +#include "CbmStsAddress.h" + +#include <fmt/format.h> + +using cbm::algo::sts::DigiQa; +using fmt::format; + +// --------------------------------------------------------------------------------------------------------------------- +// +void DigiQa::Init() +{ + 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; + + if (!fpSender.get()) { + return; + } + + int nModules = fpReadoutSetup->modules.size(); + + // Histograms per address + { + fvphAddressChannel.resize(nModules); + fvphAddressCharge.resize(nModules); + fvphAddressChannelCharge.resize(nModules); + for (int iM = 0; iM < nModules; ++iM) { + int32_t address = fpReadoutSetup->modules.at(iM).address; + fmAddressMap[address] = iM; + + auto cName = format("sts_digi/sts_digi_vs_channel_charge_addr{:#10x}", address); + auto cTitl = format("STS digis per channel and charge for module {:#10x}", address); + auto canv = CanvasConfig(cName, cTitl, 3, 1); + { + auto pad = PadConfig(); + auto name = format("sts_digi_addr{}_channel", iM); + auto titl = format("Number of digis per channel for address {:#10x};channel;N_{{digis}}", address); + fvphAddressChannel[iM] = fQaData.MakeObj<H1D>(name, titl, 2048, -0.5, 2047.5); + pad.RegisterHistogram(fvphAddressChannel[iM], "hist"); + canv.AddPadConfig(pad); + } + { + auto pad = PadConfig(); + auto name = format("sts_digi_addr{}_charge", iM); + auto titl = format("STS digi charge for address {:#10x};charge [ADS units];N_{{digis}}", address); + fvphAddressCharge[iM] = fQaData.MakeObj<H1D>(name, titl, 32, -0.5, 31.5); + pad.RegisterHistogram(fvphAddressCharge[iM], "hist"); + canv.AddPadConfig(pad); + } + { + auto pad = PadConfig(); + auto name = format("sts_digi_addr{}_channel_charge", iM); + auto titl = format("STS digi charge for address {:#10x};charge [ADS units];channel", address); + fvphAddressChannelCharge[iM] = fQaData.MakeObj<H2D>(name, titl, 32, -0.5, 31.5, 2048, -0.5, 2047.5); + pad.RegisterHistogram(fvphAddressChannelCharge[iM], "colz"); + canv.AddPadConfig(pad); + } + fQaData.AddCanvasConfig(canv); + } + } + + fQaData.Init(fpSender); +} + +// --------------------------------------------------------------------------------------------------------------------- +// +void DigiQa::Exec() +{ + if (!fpSender.get()) { + return; + } + + // Loop over STS digis and fill histograms + for (const auto& digi : (*fpvDigis)) { + int32_t address = digi.GetAddress(); + double channel = digi.GetChannel(); + double charge = digi.GetCharge(); + + // Ensure, that the address is defined + auto itHistID = fmAddressMap.find(address); + if (itHistID == fmAddressMap.end()) { + L_(error) << format("std::DigiQa: found address {:x}, which is not defined in the ReadoutSetup config", address); + continue; + } + + int iM = itHistID->second; + fvphAddressChannel[iM]->Fill(channel); + fvphAddressCharge[iM]->Fill(charge); + fvphAddressChannelCharge[iM]->Fill(charge, channel); + } + + + fQaData.Send(fpSender); +} diff --git a/algo/qa/unpack/StsDigiQa.h b/algo/qa/unpack/StsDigiQa.h new file mode 100644 index 0000000000000000000000000000000000000000..49780f8d81b984c0e4272660c815360cdf9dbdd8 --- /dev/null +++ b/algo/qa/unpack/StsDigiQa.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2024 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file StsDigiQa.h +/// \date 03.03.2024 +/// \brief QA module for STS raw digis (header) +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "CbmStsDigi.h" +#include "Definitions.h" +#include "QaBase.h" +#include "sts/ReadoutConfig.h" + +#include <unordered_map> +#include <vector> + +namespace cbm::algo::sts +{ + using DigiQaBase = QaBase<CbmStsDigi, ReadoutSetup>; + + /// \class cbm::algo::sts::DigiQa + /// \brief QA module for STS raw digis + class DigiQa : public DigiQaBase { + public: + /// \brief Constructor + DigiQa(std::shared_ptr<HistogramSender> pSender) : DigiQaBase(pSender, "RawDigi/STS") {} + + /// \brief Default constructor + DigiQa() = delete; + + /// \brief Copy constructor + DigiQa(const DigiQa&) = delete; + + /// \brief Move constructor + DigiQa(DigiQa&&) = delete; + + /// \brief Copy assignment operator + DigiQa& operator=(const DigiQa&) = delete; + + /// \brief Move assignment operator + DigiQa& operator=(DigiQa&&) = delete; + + /// \brief Executes QA (filling histograms) + void Exec(); + + /// \brief Initializes QA (initialization of histograms and canvases) + void Init(); + + private: + std::unordered_map<int32_t, int> fmAddressMap; ///< Map of address to histogram index + + // ---- Histograms + std::vector<qa::H1D*> fvphAddressChannel; ///< hist: digi channel in different sensors + std::vector<qa::H1D*> fvphAddressCharge; ///< hist: digi charge in different sensors + std::vector<qa::H2D*> fvphAddressChannelCharge; ///< hist: digi channel vs. charge in different sensors + + qa::H2D* fvphFebAsic = nullptr; ///< hist: digi FEB vs ASIC + }; +} // namespace cbm::algo::sts diff --git a/core/base/utils/flestools/CbmFlesCanvasTools.cxx b/core/base/utils/flestools/CbmFlesCanvasTools.cxx index fbf6d17f364d838631d1dfd13e6c237f31111828..fb466c9fd77b0cb54153b55640a09972676bc4e5 100644 --- a/core/base/utils/flestools/CbmFlesCanvasTools.cxx +++ b/core/base/utils/flestools/CbmFlesCanvasTools.cxx @@ -407,6 +407,7 @@ CanvasConfig ExtractCanvasConfigFromString(std::string sFullConfig) std::cerr << "ExtractCanvasConfigFromString => Empty configuration string while " << uPadIdx << " over " << uNbPads << " pads remain! " << "last ones will have default config!" << std::endl; + continue; } // if( 0 == sNext.size() ) /// Extract Pad config diff --git a/services/histserv/app/Application.cxx b/services/histserv/app/Application.cxx index 2882c42ed7970580f09833fa237dc96d676353c2..a391c615df5650611825e3dfbbbd44be95075a0e 100644 --- a/services/histserv/app/Application.cxx +++ b/services/histserv/app/Application.cxx @@ -31,6 +31,8 @@ #include <mutex> #include <zmq_addon.hpp> +#include <fmt/format.h> + std::mutex mtx; namespace b_io = boost::iostreams; @@ -519,8 +521,15 @@ namespace cbm::services::histserv LOG(info) << " All histos found for canvas " << conf.GetName().data() << ", now preparing it"; + // Temporary solution to save canvases into directories + std::string sNameFull = conf.GetName(); + size_t lastSlashPos = sNameFull.find_last_of('/'); + std::string sNamePart = lastSlashPos > sNameFull.size() ? sNameFull : sNameFull.substr(lastSlashPos + 1); + std::string sDir = lastSlashPos > sNameFull.size() ? "" : sNameFull.substr(0, lastSlashPos); + std::string canvDir = sDir.empty() ? "canvases" : fmt::format("canvases/{}", sDir); + /// Create new canvas and pads - TCanvas* pNewCanv = new TCanvas(conf.GetName().data(), conf.GetTitle().data()); + TCanvas* pNewCanv = new TCanvas(sNamePart.c_str(), conf.GetTitle().data()); pNewCanv->Divide(conf.GetNbPadsX(), conf.GetNbPadsY()); /// Loop on pads @@ -561,7 +570,7 @@ namespace cbm::services::histserv } // for( uint32_t uObjIdx = 0; uObjIdx < uNbObj; ++uObjIdx ) } // for( uint32_t uPadIdx = 0; uPadIdx < uNbPads; ++uPadIdx ) - fvCanvas[uCanvIdx] = std::pair<TCanvas*, std::string>(pNewCanv, "canvases"); + fvCanvas[uCanvIdx] = std::pair<TCanvas*, std::string>(pNewCanv, canvDir); fServer->Register(Form("/%s", fvCanvas[uCanvIdx].second.data()), fvCanvas[uCanvIdx].first); fvbCanvasRegistered[uCanvIdx] = true;