From ddac3c05fadc204fb699907265339c9560e65c0a Mon Sep 17 00:00:00 2001 From: "s.zharko@gsi.de" <s.zharko@gsi.de> Date: Thu, 6 Mar 2025 22:21:58 +0100 Subject: [PATCH] online: - QA for V0-trigger - Masking channels (BMON as example) in digi event-selector --- algo/CMakeLists.txt | 4 ++ algo/base/Definitions.h | 4 +- algo/evbuild/EventbuildChain.h | 5 ++ algo/evselector/DigiEventSelector.cxx | 25 ++++++++ algo/evselector/DigiEventSelector.h | 9 +++ algo/evselector/DigiEventSelectorConfig.cxx | 14 ++++- algo/evselector/DigiEventSelectorConfig.h | 9 ++- algo/global/Reco.cxx | 6 ++ algo/qa/QaTaskHeader.h | 2 +- algo/qa/trigger/V0TriggerQa.cxx | 34 +++++++++++ algo/qa/trigger/V0TriggerQa.h | 68 +++++++++++++++++++++ algo/trigger/V0Trigger.cxx | 10 +++ algo/trigger/V0Trigger.h | 11 ++++ 13 files changed, 194 insertions(+), 7 deletions(-) create mode 100644 algo/qa/trigger/V0TriggerQa.cxx create mode 100644 algo/qa/trigger/V0TriggerQa.h diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index 82f767e3bf..c589483d0b 100644 --- a/algo/CMakeLists.txt +++ b/algo/CMakeLists.txt @@ -159,6 +159,7 @@ set(SRCS qa/QaManager.cxx qa/hitfind/BmonHitfindQa.cxx qa/hitfind/BmonHitfindQaParameters.cxx + qa/trigger/V0TriggerQa.cxx qa/unpack/StsDigiQa.cxx ca/TrackingSetup.cxx ca/TrackingChain.cxx @@ -200,6 +201,7 @@ target_include_directories(Algo ${CMAKE_CURRENT_SOURCE_DIR}/qa ${CMAKE_CURRENT_SOURCE_DIR}/qa/unpack ${CMAKE_CURRENT_SOURCE_DIR}/qa/hitfind + ${CMAKE_CURRENT_SOURCE_DIR}/qa/trigger ${CMAKE_CURRENT_SOURCE_DIR}/kf ${CMAKE_CURRENT_SOURCE_DIR}/kf/core ${CMAKE_CURRENT_SOURCE_DIR}/kf/core/utils @@ -275,6 +277,8 @@ if (NOT CBM_ONLINE_STANDALONE) ${CMAKE_CURRENT_SOURCE_DIR}/detectors ${CMAKE_CURRENT_SOURCE_DIR}/qa ${CMAKE_CURRENT_SOURCE_DIR}/qa/unpack + ${CMAKE_CURRENT_SOURCE_DIR}/qa/hitfind + ${CMAKE_CURRENT_SOURCE_DIR}/qa/trigger ${CMAKE_CURRENT_SOURCE_DIR}/kf ${CMAKE_CURRENT_SOURCE_DIR}/kf/core ${CMAKE_CURRENT_SOURCE_DIR}/kf/core/utils diff --git a/algo/base/Definitions.h b/algo/base/Definitions.h index b1ff4c9ccc..c9cac7a917 100644 --- a/algo/base/Definitions.h +++ b/algo/base/Definitions.h @@ -79,6 +79,7 @@ namespace cbm::algo RecoFsd, Tracking, V0Finder, + V0Trigger, }; } // namespace cbm::algo @@ -150,7 +151,8 @@ CBM_ENUM_DICT(cbm::algo::QaStep, {"RecoTof", cbm::algo::QaStep::RecoTof}, {"RecoFsd", cbm::algo::QaStep::RecoFsd}, {"Tracking", cbm::algo::QaStep::Tracking}, - {"V0Finder", cbm::algo::QaStep::V0Finder} + {"V0Finder", cbm::algo::QaStep::V0Finder}, + {"V0Trigger", cbm::algo::QaStep::V0Trigger} ); #endif diff --git a/algo/evbuild/EventbuildChain.h b/algo/evbuild/EventbuildChain.h index ed6be01c66..32ae1e649c 100644 --- a/algo/evbuild/EventbuildChain.h +++ b/algo/evbuild/EventbuildChain.h @@ -64,6 +64,11 @@ namespace cbm::algo::evbuild /** @brief Registers tracking setup **/ void RegisterTrackingSetup(std::shared_ptr<TrackingSetup> pSetup) { fSelector.RegisterTrackingSetup(pSetup); } + /** @brief Sets V0 trigger QA + ** @param pQa Qa module + **/ + void SetV0TriggerQa(std::shared_ptr<V0TriggerQa> pQa) { fV0Trigger.SetQa(pQa); } + private: // members Config fConfig; ///< Global configuration ECbmModuleId fTriggerDet = ECbmModuleId::kNotExist; ///< Trigger detector diff --git a/algo/evselector/DigiEventSelector.cxx b/algo/evselector/DigiEventSelector.cxx index bae8b3e2cf..165bbca2bc 100644 --- a/algo/evselector/DigiEventSelector.cxx +++ b/algo/evselector/DigiEventSelector.cxx @@ -38,12 +38,16 @@ namespace cbm::algo::evbuild case ECbmModuleId::kTof: if (!CheckTofLayers(event.fTof, entry.second)) return false; break; + case ECbmModuleId::kBmon: + if (!CheckBmonDiamonds(event.fBmon, entry.second)) return false; + break; default: throw std::runtime_error("Number of layers for " + ::ToString(entry.first) + " is not implemented"); break; } } + return true; } // -------------------------------------------------------------------------- @@ -127,6 +131,27 @@ namespace cbm::algo::evbuild // -------------------------------------------------------------------------- + // ----- Check number of digis in selected BMON diamonds ------------------ + bool DigiEventSelector::CheckBmonDiamonds(gsl::span<const CbmBmonDigi> digis, size_t minNum) const + { + auto itMaskedChan = fConfig.fMaskedChannels.find(ECbmModuleId::kBmon); + if (itMaskedChan == fConfig.fMaskedChannels.end() || itMaskedChan->second.size() == 0) { + return digis.size() >= minNum; + } + + size_t nAcceptedDigis{0}; + const auto& maskedChannels = itMaskedChan->second; + for (const auto& digi : digis) { + if (maskedChannels.find(digi.GetAddress()) == maskedChannels.end()) { + ++nAcceptedDigis; + } + } + + return nAcceptedDigis >= minNum; + } + // -------------------------------------------------------------------------- + + // ----- Info to string ------------------------------------------------- std::string DigiEventSelector::ToString() const { diff --git a/algo/evselector/DigiEventSelector.h b/algo/evselector/DigiEventSelector.h index a8ba4d7a95..b298875b06 100644 --- a/algo/evselector/DigiEventSelector.h +++ b/algo/evselector/DigiEventSelector.h @@ -74,6 +74,15 @@ namespace cbm::algo::evbuild bool CheckTofLayers(gsl::span<const CbmTofDigi> digis, size_t minNum) const; + // FIXME: apply for all detectors + /** @brief Test for digis in selected (=not masked) BMON diamonds + ** @param digis Vector of BMON digis + ** @param minNum Requested minimum of not masked BMON digis + ** @return True if the number of not masked BMON digis is above the threshold + **/ + bool CheckBmonDiamonds(gsl::span<const CbmBmonDigi> digis, size_t minNum) const; + + private: // members DigiEventSelectorConfig fConfig; ///< Configuration / parameters std::shared_ptr<TrackingSetup> fpTrackingSetup = nullptr; ///< Tracking setup (access to stations info) diff --git a/algo/evselector/DigiEventSelectorConfig.cxx b/algo/evselector/DigiEventSelectorConfig.cxx index 647f7c0dfb..7c536e3a89 100644 --- a/algo/evselector/DigiEventSelectorConfig.cxx +++ b/algo/evselector/DigiEventSelectorConfig.cxx @@ -1,6 +1,6 @@ -/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2022-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Shreya Roy. Pierre-Alain Loizeau, Volker Friese [committer], Dominik Smith */ + Authors: Shreya Roy. Pierre-Alain Loizeau, Volker Friese [committer], Dominik Smith, Sergei Zharko */ #include "DigiEventSelectorConfig.h" @@ -34,6 +34,15 @@ namespace cbm::algo::evbuild L_(warning) << "DigiEventSelectorConfig: Ignoring minimum 0 for layers in " << ::ToString(det); } } + if (auto maskedChannels = config["maskedChannels"]) { + for (YAML::const_iterator it = maskedChannels.begin(); it != maskedChannels.end(); ++it) { + auto det = ToCbmModuleIdCaseInsensitive(it->first.as<std::string>()); + auto value = it->second.as<std::vector<uint32_t>>(); + if (value.size() > 0) { + fMaskedChannels[det] = std::unordered_set<uint32_t>(value.begin(), value.end()); + } + } + } } // -------------------------------------------------------------------------- @@ -50,6 +59,7 @@ namespace cbm::algo::evbuild auto det = ToString(entry.first); result["minLayers"][det] = entry.second; } + // FIXME: implement masked channels storage return result; } // -------------------------------------------------------------------------- diff --git a/algo/evselector/DigiEventSelectorConfig.h b/algo/evselector/DigiEventSelectorConfig.h index 43276c739d..fa97bdcc13 100644 --- a/algo/evselector/DigiEventSelectorConfig.h +++ b/algo/evselector/DigiEventSelectorConfig.h @@ -1,6 +1,6 @@ -/* Copyright (C) 2022 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +/* Copyright (C) 2022-2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt SPDX-License-Identifier: GPL-3.0-only - Authors: Shreya Roy. Pierre-Alain Loizeau, Volker Friese [committer], Dominik Smith */ + Authors: Shreya Roy. Pierre-Alain Loizeau, Volker Friese [committer], Dominik Smith, Sergei Zharko */ #ifndef CBM_ALGO_EVBUILD_DIGIEVENTSELECTORCONFIG_H #define CBM_ALGO_EVBUILD_DIGIEVENTSELECTORCONFIG_H 1 @@ -9,6 +9,7 @@ #include <cstdint> #include <map> +#include <unordered_set> #include <yaml-cpp/yaml.h> @@ -45,10 +46,12 @@ namespace cbm::algo::evbuild /** @brief Save to YAML **/ YAML::Node ToYaml() const; - private: std::map<ECbmModuleId, size_t> fMinNumDigis; ///< Key: detector, value: minimal number of digis std::map<ECbmModuleId, size_t> fMinNumLayers; ///< Key: detector, value: Minimal number of layers + + /** @brief A map of masked digi addresses, which should not participate in the event building **/ + std::map<ECbmModuleId, std::unordered_set<uint32_t>> fMaskedChannels; }; diff --git a/algo/global/Reco.cxx b/algo/global/Reco.cxx index 0f192df1b6..8f042b149d 100644 --- a/algo/global/Reco.cxx +++ b/algo/global/Reco.cxx @@ -192,6 +192,12 @@ void Reco::Init(const Options& opts) evbuild::Config config(YAML::LoadFile(configFile.string())); fEventBuild = std::make_unique<evbuild::EventbuildChain>(config, (Opts().Has(QaStep::EventBuilding) ? fSender : nullptr)); + if (fQaManager != nullptr && Opts().Has(QaStep::V0Trigger)) { + // FIXME: Replace with a common function SetTriggerQa(fQaManager) + auto pTriggerQa = std::make_shared<evbuild::V0TriggerQa>(fQaManager); + pTriggerQa->Init(); + fEventBuild->SetV0TriggerQa(pTriggerQa); + } fEventBuild->RegisterTrackingSetup(pTrackingSetup); } diff --git a/algo/qa/QaTaskHeader.h b/algo/qa/QaTaskHeader.h index ceb03e5ad4..e910a6a103 100644 --- a/algo/qa/QaTaskHeader.h +++ b/algo/qa/QaTaskHeader.h @@ -51,7 +51,7 @@ namespace cbm::algo::qa /// /// The task can be inactive, if a nullptr qa::Manager was passed to the constructor. If it is the case, /// the fpData instance is not defined, and no actions on the task should be performed - bool IsActive() const { return static_cast<bool>(fpData != nullptr); } + bool IsActive() const { return fpData.get(); } /// \brief Gets name of the task const std::string& GetTaskName() { return fsName; } diff --git a/algo/qa/trigger/V0TriggerQa.cxx b/algo/qa/trigger/V0TriggerQa.cxx new file mode 100644 index 0000000000..234cf13b19 --- /dev/null +++ b/algo/qa/trigger/V0TriggerQa.cxx @@ -0,0 +1,34 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file V0TriggerQa.cxx +/// \brief A V0-trigger QA (implementation) +/// \since 06.03.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#include "qa/trigger/V0TriggerQa.h" + +#include "qa/Histogram.h" + +using cbm::algo::evbuild::V0TriggerQa; + +// --------------------------------------------------------------------------------------------------------------------- +// +void V0TriggerQa::Init() +{ + // Histograms + fphPairDeltaT = MakeObj<qa::H1D>("v0trigger_pair_delta_t", "Time difference of track pair;#Delta t [ns];Counts", + kPairDeltaTB, kPairDeltaTL, kPairDeltaTU); + fphPairZVertex = MakeObj<qa::H1D>("v0trigger_pair_z_vertex", "z-verex of track pair;z [cm];Counts", kPairZVertexB, + kPairZVertexL, kPairZVertexU); + fphPairDca = MakeObj<qa::H1D>("v0trigger_pair_dca", "Track pair distance of closest approach;DCA [cm];Counts", + kPairDcaB, kPairDcaL, kPairDcaU); + + // Canvas + auto canv = qa::CanvasConfig(GetTaskName(), "V0 Trigger summary", 3, 1); + canv.AddPadConfig(qa::PadConfig(fphPairDeltaT, "hist")); + canv.AddPadConfig(qa::PadConfig(fphPairZVertex, "hist")); + canv.AddPadConfig(qa::PadConfig(fphPairDca, "hist")); + AddCanvasConfig(canv); +} diff --git a/algo/qa/trigger/V0TriggerQa.h b/algo/qa/trigger/V0TriggerQa.h new file mode 100644 index 0000000000..6f1aa13dc6 --- /dev/null +++ b/algo/qa/trigger/V0TriggerQa.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2025 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt + SPDX-License-Identifier: GPL-3.0-only + Authors: Sergei Zharko [committer] */ + +/// \file V0TriggerQa.h +/// \brief A V0-trigger QA +/// \since 06.03.2025 +/// \author Sergei Zharko <s.zharko@gsi.de> + +#pragma once + +#include "qa/QaTaskHeader.h" + +namespace cbm::algo::qa +{ + class H1D; +} // namespace cbm::algo::qa + + +namespace cbm::algo::evbuild +{ + /// \class V0TriggerQa + /// \brief A QA module for the V0-trigger + class V0TriggerQa : public qa::TaskHeader { + public: + friend class V0Trigger; // for access to histograms + + /// \brief Constructor + /// \param pManager Pointer to the QA manager + /// \param name Name of the QA (directory) + V0TriggerQa(const std::unique_ptr<qa::Manager>& pManager) : qa::TaskHeader(pManager, "V0Trigger") {} + + /// \brief Copy constructor + V0TriggerQa(const V0TriggerQa&) = delete; + + /// \brief Move constructor + V0TriggerQa(V0TriggerQa&&) = delete; + + /// \brief Destructor + ~V0TriggerQa() = default; + + /// \brief Copy assignment operator + V0TriggerQa& operator=(const V0TriggerQa&) = delete; + + /// \brief Move assignment operator + V0TriggerQa& operator=(V0TriggerQa&&) = delete; + + /// \brief Initializes the task + void Init(); + + private: + //* Constants + static constexpr int kPairDeltaTB{100}; ///< Track pair time difference: n bins + static constexpr double kPairDeltaTL{-50.}; ///< Track pair time difference: lower bound [ns] + static constexpr double kPairDeltaTU{+50.}; ///< Track pair time difference: upper bound [ns] + static constexpr int kPairZVertexB{120}; ///< Track pair z vertex: n bins + static constexpr double kPairZVertexL{-60.}; ///< Track pair z vertex: lower bound [cm] + static constexpr double kPairZVertexU{+60.}; ///< Track pair z vertex: upper bound [cm] + static constexpr int kPairDcaB{100}; ///< Track pair DCA: n bins + static constexpr double kPairDcaL{-2.}; ///< Track pair DCA: lower bound [cm] + static constexpr double kPairDcaU{+2.}; ///< Track pair DCA: upper bound [cm] + + //* Histograms + qa::H1D* fphPairDeltaT{nullptr}; ///< Track pair delta T + qa::H1D* fphPairZVertex{nullptr}; ///< Track pair z-vertex + qa::H1D* fphPairDca{nullptr}; ///< Track pair distance at closest approach + }; +} // namespace cbm::algo::evbuild diff --git a/algo/trigger/V0Trigger.cxx b/algo/trigger/V0Trigger.cxx index bf9264801a..49cd418258 100644 --- a/algo/trigger/V0Trigger.cxx +++ b/algo/trigger/V0Trigger.cxx @@ -32,6 +32,11 @@ namespace cbm::algo::evbuild // Check track time difference float time1 = trackIter1->fParPV.GetTime(); float time2 = trackIter2->fParPV.GetTime(); + + if (fpQa->IsActive()) { + fpQa->fphPairDeltaT->Fill(time1 - time2); + } + if (time2 < time1) { result.second.errTracksUnsorted++; continue; @@ -42,6 +47,11 @@ namespace cbm::algo::evbuild // Check PCA cuts auto [zVertex, dist] = CalcPCA(trackIter1->fParPV, trackIter2->fParPV); + if (fpQa->IsActive()) { + fpQa->fphPairZVertex->Fill(zVertex); + fpQa->fphPairDca->Fill(dist); + } + if (dist < config.PairDist_max()) { result.second.numTrackPairsAfterDistCut++; if (zVertex >= config.PairZ_min() && zVertex <= config.PairZ_max()) { diff --git a/algo/trigger/V0Trigger.h b/algo/trigger/V0Trigger.h index 6bed8ce5d2..3736b64f23 100644 --- a/algo/trigger/V0Trigger.h +++ b/algo/trigger/V0Trigger.h @@ -7,6 +7,7 @@ #include "CaTrack.h" #include "CaVector.h" #include "V0TriggerConfig.h" +#include "qa/trigger/V0TriggerQa.h" #include <utility> #include <vector> @@ -50,6 +51,7 @@ namespace cbm::algo::evbuild /** @brief Constructor **/ V0Trigger() = default; + /** @brief Execution ** @param tracks Input track vector ** @param config Trigger configuration @@ -57,6 +59,13 @@ namespace cbm::algo::evbuild **/ Result operator()(const TrackVector& tracks, const V0TriggerConfig& config) const; + + /** @brief Sets QA module + ** @param pQa Pointer to the QA module + **/ + void SetQa(std::shared_ptr<V0TriggerQa> pQa) { fpQa = pQa; } + + /** @brief Info to string **/ std::string ToString() const; @@ -84,6 +93,8 @@ namespace cbm::algo::evbuild ** @return decision **/ bool IsPrimary(const TrackParam& track, const V0TriggerConfig& config) const; + + std::shared_ptr<V0TriggerQa> fpQa{std::make_shared<V0TriggerQa>(nullptr)}; //! QA module }; } // namespace cbm::algo::evbuild -- GitLab