diff --git a/algo/CMakeLists.txt b/algo/CMakeLists.txt index 82f767e3bf236841a09e069d45d99814b5d0f8cf..c589483d0bb46f0a1dceb6b70cd009be8b83a93d 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 b1ff4c9ccc8e804a33a67fe0ab40d81d74dd616e..c9cac7a917126b96499a3c8ede8824f1ee9bb5ca 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 ed6be01c66f7bb36b25d3f1582b9084272171971..32ae1e649c1228b17c49bc59226e2e66f7033f75 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 bae8b3e2cf4be404206848facf74f594f0ea2c14..165bbca2bc929ef165dba06dddd90d4e54e570a1 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 a8ba4d7a956157aca6d7795e6a50e0cac474e25c..b298875b06ebe346d1e51ea2e79115ddedf02c57 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 647f7c0dfbb3acca86c180639167d2134eed1560..7c536e3a89c161660faba1243f268fb6feb3fff0 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 43276c739ddc21ceaf1bbd4767ad9ef0baf1e136..fa97bdcc13ac1475324f341217e1bc8a7300fcb7 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 0f192df1b6c68934269a1eea7e5561b73dd6aa5d..8f042b149daa0ae144725024d9f44e1b5e8b5f7d 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 ceb03e5ad457c9b6ceecb1145c7a5f4c15f262b9..e910a6a103d62dbcee2e17722b9ac4d0a9f924b7 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 0000000000000000000000000000000000000000..234cf13b19a8603b6f443164c1c12cb4102eb6ac --- /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 0000000000000000000000000000000000000000..6f1aa13dc6e4ee8b27a483954ae87c5f602bf7ed --- /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 bf9264801a9a444d56dac990a8998f3cc3da511a..49cd41825828f4f85903837007c6381ad834212b 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 6bed8ce5d210aaed169931991c6b79eded3603c6..3736b64f23754f992e76199f1a957d210d213be4 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