diff --git a/Modules/MUON/MCH/CMakeLists.txt b/Modules/MUON/MCH/CMakeLists.txt index 4cc5211cda..39f1785ab8 100644 --- a/Modules/MUON/MCH/CMakeLists.txt +++ b/Modules/MUON/MCH/CMakeLists.txt @@ -35,6 +35,7 @@ set(SRCS src/DecodingPostProcessing.cxx src/DigitsPostProcessing.cxx src/PreclustersPostProcessing.cxx + src/QualityAggregatorTask.cxx src/PostProcessingConfigMCH.cxx src/TrendingTracks.cxx src/ClustersTask.cxx @@ -76,6 +77,7 @@ set(HEADERS include/MCH/DedodingPostProcessing.h include/MCH/DigitsPostProcessing.h include/MCH/PreclustersPostProcessing.h + include/MCH/QualityAggregatorTask.h include/MCH/PostProcessingConfigMCH.h include/MCH/TrendingTracks.h include/MCH/ClustersTask.h @@ -161,6 +163,7 @@ add_root_dictionary(${MODULE_NAME} include/MCH/DecodingPostProcessing.h include/MCH/DigitsPostProcessing.h include/MCH/PreclustersPostProcessing.h + include/MCH/QualityAggregatorTask.h include/MCH/PostProcessingConfigMCH.h include/MCH/TrendingTracks.h include/MCH/ClustersTask.h diff --git a/Modules/MUON/MCH/include/MCH/DecodingCheck.h b/Modules/MUON/MCH/include/MCH/DecodingCheck.h index 21af60144d..c317743189 100644 --- a/Modules/MUON/MCH/include/MCH/DecodingCheck.h +++ b/Modules/MUON/MCH/include/MCH/DecodingCheck.h @@ -47,20 +47,40 @@ class DecodingCheck : public o2::quality_control::checker::CheckInterface std::string getAcceptedType() override; private: - std::string mGoodFracHistName{ "DecodingErrors/LastCycle/GoodBoardsFractionPerDE" }; - std::string mSyncFracHistName{ "SyncErrors/LastCycle/SyncedBoardsFractionPerDE" }; + std::string mGoodFracHistName{ "DecodingErrors/GoodBoardsFractionPerDE" }; + std::string mGoodFracPerSolarHistName{ "DecodingErrors/GoodBoardsFractionPerSolar" }; + std::string mSyncFracHistName{ "SyncErrors/SyncedBoardsFractionPerDE" }; + std::string mSyncFracPerSolarHistName{ "SyncErrors/SyncedBoardsFractionPerSolar" }; + std::string mGoodFracRefCompHistName{ "DecodingErrors/RefComp/GoodBoardsFractionPerDE" }; + std::string mGoodFracPerSolarRefCompHistName{ "DecodingErrors/RefComp/GoodBoardsFractionPerSolar" }; + std::string mSyncFracRefCompHistName{ "SyncErrors/RefComp/SyncedBoardsFractionPerDE" }; + std::string mSyncFracPerSolarRefCompHistName{ "SyncErrors/RefComp/SyncedBoardsFractionPerSolar" }; int mMaxBadST12{ 2 }; int mMaxBadST345{ 3 }; double mMinGoodErrorFrac{ 0.9 }; std::array, 5> mMinGoodErrorFracPerStation; + double mMinGoodErrorFracPerSolar{ 0.5 }; + double mMinGoodErrorFracRatio{ 0.9 }; + double mMinGoodErrorFracRatioPerSolar{ 0.9 }; + double mMinGoodSyncFrac{ 0.9 }; std::array, 5> mMinGoodSyncFracPerStation; + double mMinGoodSyncFracPerSolar{ 0.5 }; + double mMinGoodSyncFracRatio{ 0.9 }; + double mMinGoodSyncFracRatioPerSolar{ 0.9 }; + double mMinHeartBeatRate{ 0 }; double mMaxHeartBeatRate{ 2 }; + double mGoodFracRatioPlotRange{ 0.2 }; + double mGoodFracRatioPerSolarPlotRange{ 0.2 }; + double mSyncFracRatioPlotRange{ 0.2 }; + double mSyncFracRatioPerSolarPlotRange{ 0.2 }; + QualityChecker mQualityChecker; + std::array mSolarQuality; - ClassDefOverride(DecodingCheck, 1); + ClassDefOverride(DecodingCheck, 2); }; } // namespace o2::quality_control_modules::muonchambers diff --git a/Modules/MUON/MCH/include/MCH/DecodingErrorsPlotter.h b/Modules/MUON/MCH/include/MCH/DecodingErrorsPlotter.h index f032c32f30..e1cc0d1f09 100644 --- a/Modules/MUON/MCH/include/MCH/DecodingErrorsPlotter.h +++ b/Modules/MUON/MCH/include/MCH/DecodingErrorsPlotter.h @@ -53,10 +53,12 @@ class DecodingErrorsPlotter : public HistPlotter std::string mPath; - std::unique_ptr mHistogramGoodBoardsPerDE; ///< fraction of boards with errors per DE std::unique_ptr mHistogramErrorsPerDE; ///< error codes per DE std::unique_ptr mHistogramErrorsPerChamber; ///< error codes per chamber std::unique_ptr mHistogramErrorsPerFeeId; ///< error codes per FEE ID + + std::unique_ptr mHistogramGoodBoardsPerDE; ///< fraction of boards without errors per DE + std::unique_ptr mHistogramGoodBoardsPerSolar; ///< fraction of boards with errors per DE }; } // namespace muonchambers diff --git a/Modules/MUON/MCH/include/MCH/DecodingPostProcessing.h b/Modules/MUON/MCH/include/MCH/DecodingPostProcessing.h index 4c6ab1d4f1..b9df5538e0 100644 --- a/Modules/MUON/MCH/include/MCH/DecodingPostProcessing.h +++ b/Modules/MUON/MCH/include/MCH/DecodingPostProcessing.h @@ -21,12 +21,12 @@ #include "MCH/PostProcessingConfigMCH.h" #include "MCH/Helpers.h" -#include "Common/TH2Ratio.h" #include "MCH/HistoOnCycle.h" #include "MCH/DecodingErrorsPlotter.h" #include "MCH/HeartBeatPacketsPlotter.h" #include "MCH/FECSyncStatusPlotter.h" -#include "QualityControl/PostProcessingInterface.h" +#include "Common/ReferenceComparatorTask.h" +#include "Common/TH2Ratio.h" #include @@ -43,7 +43,7 @@ namespace o2::quality_control_modules::muonchambers { /// \brief A post-processing task which trends MCH hits and stores them in a TTree and produces plots. -class DecodingPostProcessing : public PostProcessingInterface +class DecodingPostProcessing : public ReferenceComparatorTask { public: DecodingPostProcessing() = default; @@ -73,6 +73,8 @@ class DecodingPostProcessing : public PostProcessingInterface static std::string hbPacketsSourceName() { return "hbpackets"; } static std::string syncStatusSourceName() { return "syncstatus"; } + TH1* getHistogram(std::string_view plotName); + bool mFullHistos{ false }; bool mEnableLastCycleHistos{ false }; bool mEnableTrending{ false }; @@ -96,7 +98,10 @@ class DecodingPostProcessing : public PostProcessingInterface std::unique_ptr mSyncStatusPlotter; std::unique_ptr mSyncStatusPlotterOnCycle; - std::unique_ptr mHistogramQualityPerDE; ///< quality flags for each DE, to be filled by checker task + std::unique_ptr mHistogramQualityPerDE; ///< quality flags for each DE, to be filled by checker task + std::unique_ptr mHistogramQualityPerSolar; ///< quality flags for each SOLAR, to be filled by checker task + + std::vector mHistogramsAll; }; template diff --git a/Modules/MUON/MCH/include/MCH/DigitsCheck.h b/Modules/MUON/MCH/include/MCH/DigitsCheck.h index be229c5e4e..4fbad5487b 100644 --- a/Modules/MUON/MCH/include/MCH/DigitsCheck.h +++ b/Modules/MUON/MCH/include/MCH/DigitsCheck.h @@ -50,27 +50,58 @@ class DigitsCheck : public o2::quality_control::checker::CheckInterface std::string getAcceptedType() override; private: - std::array checkMeanRates(TH1F* h); - std::array checkMeanRatesRatio(TH1F* h); - std::array checkBadChannels(TH1F* h); - std::array checkBadChannelsRatio(TH1F* h); + std::array checkMeanRates(TH1* h); + std::array checkBadChannels(TH1* h); + std::array checkMeanRateRatios(TH1* h); + std::array checkBadChannelRatios(TH1* h); + void checkSolarMeanRates(TH1* h); + void checkSolarBadChannels(TH1* h); + void checkSolarMeanRateRatios(TH1* h); + void checkSolarBadChannelRatios(TH1* h); - std::string mMeanRateHistName{ "RatesSignal/LastCycle/MeanRate" }; - std::string mGoodChanFracHistName{ "RatesSignal/LastCycle/GoodChannelsFraction" }; + std::string mMeanRateHistName{ "RatesSignal/MeanRate" }; + std::string mGoodChanFracHistName{ "RatesSignal/GoodChannelsFraction" }; + std::string mMeanRatePerSolarHistName{ "RatesSignal/MeanRatePerSolar" }; + std::string mGoodChanFracPerSolarHistName{ "RatesSignal/GoodChannelsFractionPerSolar" }; + std::string mMeanRateRefCompHistName{ "RatesSignal/RefComp/MeanRate" }; + std::string mGoodChanFracRefCompHistName{ "RatesSignal/RefComp/GoodChannelsFraction" }; + std::string mMeanRatePerSolarRefCompHistName{ "RatesSignal/RefComp/MeanRatePerSolar" }; + std::string mGoodChanFracPerSolarRefCompHistName{ "RatesSignal/RefComp/GoodChannelsFractionPerSolar" }; int mMaxBadST12{ 2 }; int mMaxBadST345{ 3 }; + + // Rate lower threshold double mMinRate{ 0.001 }; std::array, 5> mMinRatePerStation; + double mMinRatePerSolar{ 0.001 }; + // Rate upper threshold double mMaxRate{ 10 }; std::array, 5> mMaxRatePerStation; + double mMaxRatePerSolar{ 10 }; + // Rate ratio threshold + double mMinRateRatio{ 0.9 }; + double mMinRateRatioPerSolar{ 0.9 }; + + // Good channels fraction threshold double mMinGoodFraction{ 0.9 }; std::array, 5> mMinGoodFractionPerStation; + double mMinGoodFractionPerSolar{ 0.5 }; + // Good channels ratio threshold + double mMinGoodFractionRatio{ 0.9 }; + double mMinGoodFractionRatioPerSolar{ 0.9 }; + + // Vertical plot ranges double mRatePlotScaleMin{ 0 }; double mRatePlotScaleMax{ 10 }; + double mRateRatioPlotScaleRange{ 0.2 }; + double mRateRatioPerSolarPlotScaleRange{ 0.2 }; + double mGoodFractionRatioPlotScaleRange{ 0.2 }; + double mGoodFractionRatioPerSolarPlotScaleRange{ 0.2 }; QualityChecker mQualityChecker; + std::array mSolarQuality; - ClassDefOverride(DigitsCheck, 2); + ClassDefOverride(DigitsCheck, 3); }; } // namespace o2::quality_control_modules::muonchambers diff --git a/Modules/MUON/MCH/include/MCH/DigitsPostProcessing.h b/Modules/MUON/MCH/include/MCH/DigitsPostProcessing.h index e4ad45397f..d53a7ff1ab 100644 --- a/Modules/MUON/MCH/include/MCH/DigitsPostProcessing.h +++ b/Modules/MUON/MCH/include/MCH/DigitsPostProcessing.h @@ -24,6 +24,7 @@ #include "MCH/RatesPlotter.h" #include "MCH/RatesTrendsPlotter.h" #include "MCH/OrbitsPlotter.h" +#include "Common/ReferenceComparatorTask.h" #include "Common/TH2Ratio.h" #include "QualityControl/PostProcessingInterface.h" @@ -40,7 +41,7 @@ namespace o2::quality_control_modules::muonchambers { /// \brief A post-processing task which processes and trends MCH digits and produces plots. -class DigitsPostProcessing : public PostProcessingInterface +class DigitsPostProcessing : public ReferenceComparatorTask { public: DigitsPostProcessing() = default; @@ -62,6 +63,8 @@ class DigitsPostProcessing : public PostProcessingInterface static std::string orbitsSourceName() { return "orbits"; } static std::string orbitsSignalSourceName() { return "orbits_signal"; } + TH1* getHistogram(std::string_view plotName); + bool mFullHistos{ false }; bool mEnableLastCycleHistos{ false }; bool mEnableTrending{ false }; @@ -99,7 +102,10 @@ class DigitsPostProcessing : public PostProcessingInterface std::unique_ptr mOrbitsPlotterSignal; std::unique_ptr mOrbitsPlotterSignalOnCycle; - std::unique_ptr mHistogramQualityPerDE; ///< quality flags for each DE, to be filled by checker task + std::unique_ptr mHistogramQualityPerDE; ///< quality flags for each DE, to be filled by checker task + std::unique_ptr mHistogramQualityPerSolar; ///< quality flags for each SOLAR, to be filled by checker task + + std::vector mHistogramsAll; }; } // namespace o2::quality_control_modules::muonchambers diff --git a/Modules/MUON/MCH/include/MCH/EfficiencyPlotter.h b/Modules/MUON/MCH/include/MCH/EfficiencyPlotter.h index ab276d93e8..1e72c8f3b6 100644 --- a/Modules/MUON/MCH/include/MCH/EfficiencyPlotter.h +++ b/Modules/MUON/MCH/include/MCH/EfficiencyPlotter.h @@ -69,6 +69,7 @@ class EfficiencyPlotter : public HistPlotter std::unique_ptr mElecMapReductor; std::array, 2> mHistogramMeanEfficiencyPerDE; + std::unique_ptr mHistogramMeanEfficiencyPerSolar; std::array>, 2> mHistogramEfficiencyDE; // 2D hit rate map for each DE std::array, 2> mHistogramEfficiencyGlobal; // Efficiency histogram (global XY view) diff --git a/Modules/MUON/MCH/include/MCH/FECSyncStatusPlotter.h b/Modules/MUON/MCH/include/MCH/FECSyncStatusPlotter.h index 439af50499..39afd9e7ae 100644 --- a/Modules/MUON/MCH/include/MCH/FECSyncStatusPlotter.h +++ b/Modules/MUON/MCH/include/MCH/FECSyncStatusPlotter.h @@ -49,11 +49,11 @@ class FECSyncStatusPlotter : public HistPlotter histograms().emplace_back(HistInfo{ h, drawOptions, displayHints }); } - o2::mch::raw::Elec2DetMapper mElec2DetMapper; - o2::mch::raw::FeeLink2SolarMapper mFeeLink2SolarMapper; + o2::mch::raw::Det2ElecMapper mDet2ElecMapper; std::unique_ptr mGoodBoardsFractionPerDE; ///< fraction of out-of-sync DS boards per detection element - std::unique_ptr mGoodTFFractionPerDE; ///< fraction of out-of-sync DS boards per chamber + std::unique_ptr mGoodTFFractionPerDE; ///< fraction of in-sync TFs per DS boards, averaged on detection elements + std::unique_ptr mGoodTFFractionPerSolar; ///< fraction of in-sync TFs per DS boards, averaged on SOLAR boards }; } // namespace muonchambers diff --git a/Modules/MUON/MCH/include/MCH/Helpers.h b/Modules/MUON/MCH/include/MCH/Helpers.h index e44d340c4d..95cfb26802 100644 --- a/Modules/MUON/MCH/include/MCH/Helpers.h +++ b/Modules/MUON/MCH/include/MCH/Helpers.h @@ -53,6 +53,12 @@ int getDEindex(int de); constexpr int getNumDE() { return (4 * 4 + 18 * 2 + 26 * 4); } int getNumDEinChamber(int chIndex); std::pair getDEindexInChamber(int deId); +int getDEFromIndex(int index); + +int getSolarIndex(int solarId); +int getSolarIdFromIndex(int index); +constexpr int getNumSolar() { return 624; } +int getNumSolarPerChamber(int chamberId); void getThresholdsPerStation(o2::quality_control::core::CustomParameters customParameters, const o2::quality_control::core::Activity& activity, @@ -62,10 +68,16 @@ void getThresholdsPerStation(o2::quality_control::core::CustomParameters customP o2::quality_control::core::Quality checkDetectorQuality(gsl::span& deQuality); -void addChamberDelimiters(TH1F* h, float xmin = 0, float xmax = 0); -void addChamberDelimiters(TH2F* h); +void addChamberLabelsForDE(TH1* h); +void addChamberDelimiters(TH1* h, float xmin = 0, float xmax = 0); +void addChamberDelimiters(TH2* h); +void addChamberLabelsForSolar(TH1* h); +void addChamberDelimitersToSolarHistogram(TH1* h, float xmin = 0, float xmax = 0); +void addChamberDelimitersToSolarHistogram(TH2* h); +void drawThreshold(TH1* histogram, double threshold); void drawThresholdsPerStation(TH1* histogram, const std::array, 5>& thresholdsPerStation, double defaultThreshold); void addDEBinLabels(TH1* histogram); +void addSolarBinLabels(TH1* histogram); std::string getHistoPath(int deId); bool matchHistName(std::string hist, std::string name); diff --git a/Modules/MUON/MCH/include/MCH/LinkDef.h b/Modules/MUON/MCH/include/MCH/LinkDef.h index 1f8433bc6c..d23c1a2b2d 100644 --- a/Modules/MUON/MCH/include/MCH/LinkDef.h +++ b/Modules/MUON/MCH/include/MCH/LinkDef.h @@ -17,6 +17,7 @@ #pragma link C++ class o2::quality_control_modules::muonchambers::DecodingPostProcessing + ; #pragma link C++ class o2::quality_control_modules::muonchambers::DigitsPostProcessing + ; #pragma link C++ class o2::quality_control_modules::muonchambers::PreclustersPostProcessing + ; +#pragma link C++ class o2::quality_control_modules::muonchambers::QualityAggregatorTask + ; // Trending #pragma link C++ class o2::quality_control_modules::muonchambers::TrendingTracks + ; // Checks diff --git a/Modules/MUON/MCH/include/MCH/PreclustersCheck.h b/Modules/MUON/MCH/include/MCH/PreclustersCheck.h index 907c796ed5..401e58e871 100644 --- a/Modules/MUON/MCH/include/MCH/PreclustersCheck.h +++ b/Modules/MUON/MCH/include/MCH/PreclustersCheck.h @@ -48,21 +48,33 @@ class PreclustersCheck : public o2::quality_control::checker::CheckInterface std::string getAcceptedType() override; private: - std::array checkMeanEfficiencies(TH1F* h); - std::array checkMeanEfficienciesRatio(TH1F* h); + std::array checkMeanEfficiencies(TH1* h); + std::array checkMeanEfficiencyRatios(TH1* h); + void checkSolarMeanEfficiencies(TH1* h); + void checkSolarMeanEfficiencyRatios(TH1* h); - std::string mMeanEffHistNameB{ "Efficiency/LastCycle/MeanEfficiencyB" }; - std::string mMeanEffHistNameNB{ "Efficiency/LastCycle/MeanEfficiencyNB" }; + std::string mMeanEffHistNameB{ "Efficiency/MeanEfficiencyB" }; + std::string mMeanEffHistNameNB{ "Efficiency/MeanEfficiencyNB" }; + std::string mMeanEffPerSolarHistName{ "Efficiency/MeanEfficiencyPerSolar" }; + std::string mMeanEffRefCompHistNameB{ "Efficiency/RefComp/MeanEfficiencyB" }; + std::string mMeanEffRefCompHistNameNB{ "Efficiency/RefComp/MeanEfficiencyNB" }; + std::string mMeanEffPerSolarRefCompHistName{ "Efficiency/RefComp/MeanEfficiencyPerSolar" }; int mMaxBadST12{ 2 }; int mMaxBadST345{ 3 }; double mMinEfficiency{ 0.8 }; std::array, 5> mMinEfficiencyPerStation; + double mMinEfficiencyPerSolar{ 0.5 }; + double mMinEfficiencyRatio{ 0.9 }; + double mMinEfficiencyRatioPerSolar{ 0.9 }; double mPseudoeffPlotScaleMin{ 0.0 }; - double mPseudoeffPlotScaleMax{ 1.0 }; + double mPseudoeffPlotScaleMax{ 1.05 }; + double mEfficiencyRatioScaleRange{ 0.2 }; + double mEfficiencyRatioPerSolarScaleRange{ 0.2 }; QualityChecker mQualityChecker; + std::array mSolarQuality; - ClassDefOverride(PreclustersCheck, 2); + ClassDefOverride(PreclustersCheck, 3); }; } // namespace o2::quality_control_modules::muonchambers diff --git a/Modules/MUON/MCH/include/MCH/PreclustersPostProcessing.h b/Modules/MUON/MCH/include/MCH/PreclustersPostProcessing.h index b83db27abd..351e4ca3d4 100644 --- a/Modules/MUON/MCH/include/MCH/PreclustersPostProcessing.h +++ b/Modules/MUON/MCH/include/MCH/PreclustersPostProcessing.h @@ -19,11 +19,8 @@ #ifndef QC_MODULE_MCH_PP_PRECLUSTERS_H #define QC_MODULE_MCH_PP_PRECLUSTERS_H -#include "QualityControl/PostProcessingInterface.h" - #include "MCH/PostProcessingConfigMCH.h" #include "MCH/Helpers.h" -#include "Common/TH2Ratio.h" #include "MCH/HistoOnCycle.h" #include "MCH/EfficiencyPlotter.h" #include "MCH/EfficiencyTrendsPlotter.h" @@ -31,6 +28,8 @@ #include "MCH/ClusterSizeTrendsPlotter.h" #include "MCH/ClusterChargePlotter.h" #include "MCH/ClusterChargeTrendsPlotter.h" +#include "Common/ReferenceComparatorTask.h" +#include "Common/TH2Ratio.h" namespace o2::quality_control::repository { @@ -45,7 +44,7 @@ namespace o2::quality_control_modules::muonchambers { /// \brief A post-processing task which processes and trends MCH pre-clusters and produces plots. -class PreclustersPostProcessing : public PostProcessingInterface +class PreclustersPostProcessing : public ReferenceComparatorTask { public: PreclustersPostProcessing() = default; @@ -73,6 +72,8 @@ class PreclustersPostProcessing : public PostProcessingInterface static std::string clusterChargeSourceName() { return "clcharge"; } static std::string clusterSizeSourceName() { return "clsize"; } + TH1* getHistogram(std::string_view plotName); + bool mFullHistos{ false }; bool mEnableLastCycleHistos{ false }; bool mEnableTrending{ false }; @@ -102,7 +103,10 @@ class PreclustersPostProcessing : public PostProcessingInterface std::unique_ptr mClusterChargeTrendsPlotter; std::unique_ptr mClusterSizeTrendsPlotter; - std::unique_ptr mHistogramQualityPerDE; ///< quality flags for each DE, to be filled by checker task + std::unique_ptr mHistogramQualityPerDE; ///< quality flags for each DE, to be filled by checker task + std::unique_ptr mHistogramQualityPerSolar; ///< quality flags for each SOLAR, to be filled by checker task + + std::vector mHistogramsAll; }; template diff --git a/Modules/MUON/MCH/include/MCH/QualityAggregatorTask.h b/Modules/MUON/MCH/include/MCH/QualityAggregatorTask.h new file mode 100644 index 0000000000..679ff623d8 --- /dev/null +++ b/Modules/MUON/MCH/include/MCH/QualityAggregatorTask.h @@ -0,0 +1,70 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file QualityAggregatorTask.h +/// \author Andrea Ferrero andrea.ferrero@cern.ch +/// \brief Post-processing of the MCH summary qualities +/// \since 21/06/2022 +/// + +#ifndef QC_MODULE_MCH_PP_QUALITY_H +#define QC_MODULE_MCH_PP_QUALITY_H + +#include "QualityControl/PostProcessingInterface.h" +#include "CCDB/CcdbApi.h" + +#include + +using namespace o2::framework; + +using namespace o2::quality_control; +using namespace o2::quality_control::postprocessing; + +class TH2F; + +namespace o2::quality_control_modules::muonchambers +{ + +/// \brief A post-processing task which processes and trends MCH pre-clusters and produces plots. +class QualityAggregatorTask : public PostProcessingInterface +{ + public: + using CcdbApi = o2::ccdb::CcdbApi; + + QualityAggregatorTask() = default; + ~QualityAggregatorTask() override = default; + + void configure(const boost::property_tree::ptree& config) override; + void initialize(Trigger, framework::ServiceRegistryRef) override; + void update(Trigger, framework::ServiceRegistryRef) override; + void finalize(Trigger, framework::ServiceRegistryRef) override; + + private: + CcdbApi mAPI; + std::string mCCDBpath{ "http://ccdb-test.cern.ch:8080" }; // CCDB path + + std::string mObjectPathBadDE{ "MCH/Calib/BadDE" }; + std::string mObjectPathBadSOLAR{ "MCH/Calib/BadSOLAR" }; + + std::vector mDEPlotPaths; + std::optional> mPreviousBadDEs; + + std::vector mSOLARPlotPaths; + std::optional> mPreviousBadSolarBoards; + + std::unique_ptr mHistogramQualityPerDE; ///< quality flags for each DE, to be filled by checker task + std::unique_ptr mHistogramQualityPerSolar; ///< quality flags for each SOLAR, to be filled by checker task +}; + +} // namespace o2::quality_control_modules::muonchambers + +#endif // QC_MODULE_MCH_PP_QUALITY_H diff --git a/Modules/MUON/MCH/include/MCH/RatesPlotter.h b/Modules/MUON/MCH/include/MCH/RatesPlotter.h index 2d4649a1cb..c3068c67cb 100644 --- a/Modules/MUON/MCH/include/MCH/RatesPlotter.h +++ b/Modules/MUON/MCH/include/MCH/RatesPlotter.h @@ -69,8 +69,10 @@ class RatesPlotter : public HistPlotter std::unique_ptr mHistogramRatePerStation; std::unique_ptr mHistogramMeanRatePerDE; + std::unique_ptr mHistogramMeanRatePerSolar; std::unique_ptr mHistogramGoodChannelsFractionPerDE; + std::unique_ptr mHistogramGoodChannelsFractionPerSolar; std::map> mHistogramRateDE[2]; // 2D hit rate map for each DE std::shared_ptr mHistogramRateGlobal[2]; // Rate histogram (global XY view) diff --git a/Modules/MUON/MCH/include/MCH/TH2ElecMapReductor.h b/Modules/MUON/MCH/include/MCH/TH2ElecMapReductor.h index e8b9cccf51..9fb185ce10 100644 --- a/Modules/MUON/MCH/include/MCH/TH2ElecMapReductor.h +++ b/Modules/MUON/MCH/include/MCH/TH2ElecMapReductor.h @@ -41,6 +41,14 @@ class TH2ElecMapReductor : public quality_control::postprocessing::ReductorTObje float getChamberValue(int chid); float getDeValue(int deid, int cathode = 2); float getOrbits() { return meanOrbits; } + + // SOLAR related accessors + float getSolarValue(int solarId); + int getSolarNumPads(int solarId); + int getSolarNumPadsBad(int solarId); + int getSolarNumPadsNoStat(int solarId); + + // DE related accessors int getNumPads(int deid, int cathode); int getNumPads(int deid) { @@ -59,6 +67,7 @@ class TH2ElecMapReductor : public quality_control::postprocessing::ReductorTObje private: static constexpr int sDeNum{ 156 }; + static constexpr uint32_t sSolarIndexMax{ 32 * 24 }; int checkPadMapping(uint16_t feeId, uint8_t linkId, uint8_t eLinkId, o2::mch::raw::DualSampaChannelId channel, int& cid); @@ -70,10 +79,19 @@ class TH2ElecMapReductor : public quality_control::postprocessing::ReductorTObje float mMin; float mMax; + // SOLAR related values + int solarNumPads[sSolarIndexMax]; + int solarNumPadsBad[sSolarIndexMax]; + int solarNumPadsNoStat[sSolarIndexMax]; + float solarValues[sSolarIndexMax]; + + // DE related values int deNumPads[2][sDeNum]; int deNumPadsBad[2][sDeNum]; int deNumPadsNoStat[2][sDeNum]; float deValues[3][sDeNum]; + + // chamber related values float chValues[10]; float meanOrbits; float entries; diff --git a/Modules/MUON/MCH/src/DecodingCheck.cxx b/Modules/MUON/MCH/src/DecodingCheck.cxx index bc66e789b0..0533284267 100644 --- a/Modules/MUON/MCH/src/DecodingCheck.cxx +++ b/Modules/MUON/MCH/src/DecodingCheck.cxx @@ -18,12 +18,15 @@ #include "MCH/DecodingCheck.h" #include "MCH/Helpers.h" #include "MUONCommon/Helpers.h" +#include "QualityControl/ReferenceUtils.h" +#include "QualityControl/QcInfoLogger.h" // ROOT #include #include #include #include +#include #include #include @@ -42,9 +45,15 @@ void DecodingCheck::startOfActivity(const Activity& activity) mGoodFracHistName = getConfigurationParameter(mCustomParameters, "GoodFracHistName", mGoodFracHistName, activity); mSyncFracHistName = getConfigurationParameter(mCustomParameters, "SyncFracHistName", mSyncFracHistName, activity); + mGoodFracPerSolarHistName = getConfigurationParameter(mCustomParameters, "GoodFracPerSolarHistName", mGoodFracPerSolarHistName, activity); + mSyncFracPerSolarHistName = getConfigurationParameter(mCustomParameters, "SyncFracPerSolarHistName", mSyncFracPerSolarHistName, activity); + getThresholdsPerStation(mCustomParameters, activity, "MinGoodErrorFrac", mMinGoodErrorFracPerStation, mMinGoodErrorFrac); getThresholdsPerStation(mCustomParameters, activity, "MinGoodSyncFrac", mMinGoodSyncFracPerStation, mMinGoodSyncFrac); + mMinGoodErrorFracPerSolar = getConfigurationParameter(mCustomParameters, "MinGoodErrorFracPerSolar", mMinGoodErrorFracPerSolar, activity); + mMinGoodSyncFracPerSolar = getConfigurationParameter(mCustomParameters, "MinGoodSyncFracPerSolar", mMinGoodSyncFracPerSolar, activity); + mMaxBadST12 = getConfigurationParameter(mCustomParameters, "MaxBadDE_ST12", mMaxBadST12, activity); mMaxBadST345 = getConfigurationParameter(mCustomParameters, "MaxBadDE_ST345", mMaxBadST345, activity); mQualityChecker.mMaxBadST12 = mMaxBadST12; @@ -63,6 +72,7 @@ Quality DecodingCheck::check(std::mapgetName(), mGoodFracPerSolarHistName)) { + auto* h = dynamic_cast(mo->getObject()); + if (!h) { + return result; + } + + for (int solar = 0; solar < h->GetXaxis()->GetNbins(); solar++) { + int bin = solar + 1; + double value = h->GetBinContent(bin); + if (value >= mMinGoodErrorFracPerSolar) { + continue; + } + mSolarQuality[solar] = Quality::Bad; + } + } + if (matchHistName(mo->getName(), mSyncFracHistName)) { auto* h = dynamic_cast(mo->getObject()); if (!h) { @@ -129,6 +155,22 @@ Quality DecodingCheck::check(std::mapgetName(), mSyncFracPerSolarHistName)) { + auto* h = dynamic_cast(mo->getObject()); + if (!h) { + return result; + } + + for (int solar = 0; solar < h->GetXaxis()->GetNbins(); solar++) { + int bin = solar + 1; + double value = h->GetBinContent(bin); + if (value >= mMinGoodSyncFracPerSolar) { + continue; + } + mSolarQuality[solar] = Quality::Bad; + } + } } return mQualityChecker.getQuality(); } @@ -137,6 +179,93 @@ std::string DecodingCheck::getAcceptedType() { return "TH1"; } void DecodingCheck::beautify(std::shared_ptr mo, Quality checkResult) { + if ((mo->getName().find("RefComp/") != std::string::npos)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (!canvas) { + return; + } + + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (!ratioPlot) { + return; + } + + std::string messages; + auto refCompPlots = o2::quality_control::checker::getPlotsFromCanvas(canvas, messages); + + double ratioPlotRange{ -1 }; + double ratioThreshold{ -1 }; + bool isSolar{ false }; + + if (matchHistName(mo->getName(), mGoodFracRefCompHistName)) { + ratioPlotRange = mGoodFracRatioPlotRange; + ratioThreshold = mMinGoodErrorFracRatio; + isSolar = false; + } else if (matchHistName(mo->getName(), mGoodFracPerSolarRefCompHistName)) { + ratioPlotRange = mGoodFracRatioPerSolarPlotRange; + ratioThreshold = mMinGoodErrorFracRatioPerSolar; + isSolar = true; + } else if (matchHistName(mo->getName(), mSyncFracRefCompHistName)) { + ratioPlotRange = mSyncFracRatioPlotRange; + ratioThreshold = mMinGoodSyncFracRatio; + isSolar = false; + } else if (matchHistName(mo->getName(), mSyncFracPerSolarRefCompHistName)) { + ratioPlotRange = mSyncFracRatioPerSolarPlotRange; + ratioThreshold = mMinGoodSyncFracRatioPerSolar; + isSolar = true; + } + + if (ratioPlotRange < 0) { + return; + } + + ratioPlot->SetMinimum(1.0 - ratioPlotRange); + ratioPlot->SetMaximum(1.0 + ratioPlotRange); + ratioPlot->GetXaxis()->SetTickLength(0); + + if (isSolar) { + addChamberDelimitersToSolarHistogram(ratioPlot); + addSolarBinLabels(ratioPlot); + } else { + addChamberDelimiters(ratioPlot); + addDEBinLabels(ratioPlot); + } + drawThreshold(ratioPlot, ratioThreshold); + + if (refCompPlots.first) { + refCompPlots.first->SetMinimum(0); + refCompPlots.first->SetMaximum(1.05); + if (isSolar) { + addChamberDelimitersToSolarHistogram(refCompPlots.first); + addChamberLabelsForSolar(refCompPlots.first); + addSolarBinLabels(refCompPlots.first); + if (refCompPlots.second) { + addSolarBinLabels(refCompPlots.second); + } + } else { + addChamberDelimiters(refCompPlots.first); + addChamberLabelsForDE(refCompPlots.first); + addDEBinLabels(refCompPlots.first); + if (refCompPlots.second) { + addDEBinLabels(refCompPlots.second); + } + } + } + + if (refCompPlots.second) { + if (checkResult == Quality::Good) { + refCompPlots.second->SetLineColor(kGreen + 2); + } else if (checkResult == Quality::Bad) { + refCompPlots.second->SetLineColor(kRed); + } else if (checkResult == Quality::Medium) { + refCompPlots.second->SetLineColor(kOrange - 3); + } else if (checkResult == Quality::Null) { + refCompPlots.second->SetLineColor(kViolet - 6); + } + } + return; + } + if (mo->getName().find("DecodingErrorsPerDE") != std::string::npos) { auto* h = dynamic_cast(mo->getObject()); if (!h) { @@ -196,7 +325,7 @@ void DecodingCheck::beautify(std::shared_ptr mo, Quality checkRes h->GetYaxis()->SetTitle("good boards fraction"); addChamberDelimiters(h, scaleMin, scaleMax); - addDEBinLabels(h); + addChamberLabelsForDE(h); // only the plot used for the check is beautified by changing the color // and adding the horizontal lines corresponding to the thresholds @@ -214,6 +343,37 @@ void DecodingCheck::beautify(std::shared_ptr mo, Quality checkRes } } + if (mo->getName().find("GoodBoardsFractionPerSolar") != std::string::npos) { + auto* h = dynamic_cast(mo->getObject()); + if (!h) { + return; + } + + float scaleMin{ 0 }; + float scaleMax{ 1.05 }; + h->SetMinimum(scaleMin); + h->SetMaximum(scaleMax); + h->GetYaxis()->SetTitle("good boards fraction"); + + addChamberDelimitersToSolarHistogram(h, scaleMin, scaleMax); + addChamberLabelsForSolar(h); + + // only the plot used for the check is beautified by changing the color + // and adding the horizontal lines corresponding to the thresholds + if (matchHistName(mo->getName(), mGoodFracPerSolarHistName)) { + if (checkResult == Quality::Good) { + h->SetFillColor(kGreen); + } else if (checkResult == Quality::Bad) { + h->SetFillColor(kRed); + } else if (checkResult == Quality::Medium) { + h->SetFillColor(kOrange); + } + h->SetLineColor(kBlack); + + drawThreshold(h, mMinGoodErrorFracPerSolar); + } + } + if (mo->getName().find("SyncedBoardsFractionPerDE") != std::string::npos) { auto* h = dynamic_cast(mo->getObject()); if (!h) { @@ -226,7 +386,7 @@ void DecodingCheck::beautify(std::shared_ptr mo, Quality checkRes h->SetMaximum(scaleMax); addChamberDelimiters(h, scaleMin, scaleMax); - addDEBinLabels(h); + addChamberLabelsForDE(h); // only the plot used for the check is beautified by changing the color // and adding the horizontal lines corresponding to the thresholds @@ -244,6 +404,46 @@ void DecodingCheck::beautify(std::shared_ptr mo, Quality checkRes } } + if (mo->getName().find("SyncedBoardsFractionPerSolar") != std::string::npos) { + auto* h = dynamic_cast(mo->getObject()); + if (!h) { + return; + } + + float scaleMin{ 0 }; + float scaleMax{ 1.05 }; + h->SetMinimum(scaleMin); + h->SetMaximum(scaleMax); + + addChamberDelimitersToSolarHistogram(h, scaleMin, scaleMax); + addChamberLabelsForSolar(h); + + // only the plot used for the check is beautified by changing the color + // and adding the horizontal lines corresponding to the thresholds + if (matchHistName(mo->getName(), mSyncFracPerSolarHistName)) { + if (checkResult == Quality::Good) { + h->SetFillColor(kGreen); + } else if (checkResult == Quality::Bad) { + h->SetFillColor(kRed); + } else if (checkResult == Quality::Medium) { + h->SetFillColor(kOrange); + } + h->SetLineColor(kBlack); + + drawThreshold(h, mMinGoodSyncFracPerSolar); + } + } + + // Normalize the heartBeat rate plots + if (mo->getName().find("HBRate_ST") != std::string::npos) { + TH2F* h = dynamic_cast(mo->getObject()); + if (!h) { + return; + } + h->SetMinimum(mMinHeartBeatRate); + h->SetMaximum(mMaxHeartBeatRate); + } + // update quality flags for each DE if (mo->getName().find("QualityFlagPerDE") != std::string::npos) { TH2F* h = dynamic_cast(mo->getObject()); @@ -251,30 +451,75 @@ void DecodingCheck::beautify(std::shared_ptr mo, Quality checkRes return; } - for (int deId = 0; deId < mQualityChecker.mQuality.size(); deId++) { + std::string badDEs; + for (int deIndex = 0; deIndex < mQualityChecker.mQuality.size(); deIndex++) { float ybin = 0; - if (mQualityChecker.mQuality[deId] == Quality::Good) { + if (mQualityChecker.mQuality[deIndex] == Quality::Good) { ybin = 3; } - if (mQualityChecker.mQuality[deId] == Quality::Medium) { + if (mQualityChecker.mQuality[deIndex] == Quality::Medium) { ybin = 2; } - if (mQualityChecker.mQuality[deId] == Quality::Bad) { + if (mQualityChecker.mQuality[deIndex] == Quality::Bad) { ybin = 1; + std::string deIdStr = std::to_string(getDEFromIndex(deIndex)); + if (badDEs.empty()) { + badDEs = deIdStr; + } else { + badDEs += std::string(" ") + deIdStr; + } } - h->SetBinContent(deId + 1, ybin, 1); + h->SetBinContent(deIndex + 1, ybin, 1); + + if (!badDEs.empty()) { + std::string text = std::string("Bad DEs: ") + badDEs; + TPaveLabel* label = new TPaveLabel(0.2, 0.8, 0.8, 0.88, text.c_str(), "blNDC"); + label->SetBorderSize(1); + h->GetListOfFunctions()->Add(label); + + ILOG(Warning, Support) << "[DecodingCheck] " << text << ENDM; + } } } - // Normalize the heartBeat rate plots - if (mo->getName().find("HBRate_ST") != std::string::npos) { + // update quality flags for each SOLAR + if (mo->getName().find("QualityFlagPerSolar") != std::string::npos) { TH2F* h = dynamic_cast(mo->getObject()); if (!h) { return; } - h->SetMinimum(mMinHeartBeatRate); - h->SetMaximum(mMaxHeartBeatRate); + + std::string badSolarBoards; + for (int solar = 0; solar < mSolarQuality.size(); solar++) { + float ybin = 0; + if (mSolarQuality[solar] == Quality::Good) { + ybin = 3; + } + if (mSolarQuality[solar] == Quality::Medium) { + ybin = 2; + } + if (mSolarQuality[solar] == Quality::Bad) { + ybin = 1; + std::string solarId = std::to_string(getSolarIdFromIndex(solar)); + if (badSolarBoards.empty()) { + badSolarBoards = solarId; + } else { + badSolarBoards += std::string(" ") + solarId; + } + } + + h->SetBinContent(solar + 1, ybin, 1); + } + + if (!badSolarBoards.empty()) { + std::string badSolarList = std::string("Bad SOLAR boards: ") + badSolarBoards; + TPaveLabel* label = new TPaveLabel(0.2, 0.8, 0.8, 0.88, badSolarList.c_str(), "blNDC"); + label->SetBorderSize(1); + h->GetListOfFunctions()->Add(label); + + ILOG(Warning, Support) << "[DecodingCheck] " << badSolarList << ENDM; + } } } diff --git a/Modules/MUON/MCH/src/DecodingErrorsPlotter.cxx b/Modules/MUON/MCH/src/DecodingErrorsPlotter.cxx index 6fd55fc7bd..03080083b1 100644 --- a/Modules/MUON/MCH/src/DecodingErrorsPlotter.cxx +++ b/Modules/MUON/MCH/src/DecodingErrorsPlotter.cxx @@ -60,8 +60,14 @@ DecodingErrorsPlotter::DecodingErrorsPlotter(std::string path) : mPath(path) // Number of decoding errors, grouped by FEE ID and normalized to the number of processed TF mHistogramGoodBoardsPerDE = std::make_unique(TString::Format("%sGoodBoardsFractionPerDE", path.c_str()), "Fraction of boards not in error", getNumDE(), 0, getNumDE()); + addDEBinLabels(mHistogramGoodBoardsPerDE.get()); addHisto(mHistogramGoodBoardsPerDE.get(), false, "", ""); + mHistogramGoodBoardsPerSolar = std::make_unique(TString::Format("%sGoodBoardsFractionPerSolar", path.c_str()), + "Fraction of boards not in error per SOLAR board", getNumSolar(), 0, getNumSolar()); + addSolarBinLabels(mHistogramGoodBoardsPerSolar.get()); + addHisto(mHistogramGoodBoardsPerSolar.get(), false, "", ""); + //-------------------------------------------- // Decoding errors per chamber, DE and FEEID //-------------------------------------------- @@ -122,7 +128,8 @@ void DecodingErrorsPlotter::update(TH2F* h) mHistogramErrorsPerDE->Reset("ICES"); mHistogramErrorsPerChamber->Reset("ICES"); - std::array, getNumDE()> goodBoardsFraction; + std::array, getNumDE()> goodBoardsFractionPerDE; + std::array, getNumSolar()> goodBoardsFractionPerSolar; // loop over bins in electronics coordinates, and map the channels to the corresponding cathode pads int nbinsx = h->GetXaxis()->GetNbins(); @@ -144,9 +151,10 @@ void DecodingErrorsPlotter::update(TH2F* h) // where one bin is one physical pad // get the unique solar ID and the DS address associated to this digit int feeId = -1; + int solarId = -1; std::optional dsElecId = mDet2ElecMapper(dsDetId); if (dsElecId) { - uint32_t solarId = dsElecId->solarId(); + solarId = dsElecId->solarId(); uint32_t dsAddr = dsElecId->elinkId(); std::optional feeLinkId = mSolar2FeeLinkMapper(solarId); @@ -154,6 +162,10 @@ void DecodingErrorsPlotter::update(TH2F* h) feeId = feeLinkId->feeId(); } } + int solarIndex = (solarId >= 0) ? getSolarIndex(solarId) : -1; + if (solarIndex < 0) { + continue; + } bool hasError = false; @@ -171,21 +183,33 @@ void DecodingErrorsPlotter::update(TH2F* h) incrementBin(mHistogramErrorsPerChamber.get(), chamber, j, count); } - goodBoardsFraction[deIndex].first += 1; + goodBoardsFractionPerDE[deIndex].first += 1; + goodBoardsFractionPerSolar[solarIndex].first += 1; if (!hasError) { - goodBoardsFraction[deIndex].second += 1; + goodBoardsFractionPerDE[deIndex].second += 1; + goodBoardsFractionPerSolar[solarIndex].second += 1; } } // update fraction of good boards per DE for (int i = 0; i < getNumDE(); i++) { - if (goodBoardsFraction[i].first > 0) { - float frac = goodBoardsFraction[i].second / goodBoardsFraction[i].first; + if (goodBoardsFractionPerDE[i].first > 0) { + float frac = goodBoardsFractionPerDE[i].second / goodBoardsFractionPerDE[i].first; mHistogramGoodBoardsPerDE->SetBinContent(i + 1, frac); } else { mHistogramGoodBoardsPerDE->SetBinContent(i + 1, 0); } } + + // update fraction of good boards per Solar + for (int i = 0; i < getNumSolar(); i++) { + if (goodBoardsFractionPerSolar[i].first > 0) { + float frac = goodBoardsFractionPerSolar[i].second / goodBoardsFractionPerSolar[i].first; + mHistogramGoodBoardsPerSolar->SetBinContent(i + 1, frac); + } else { + mHistogramGoodBoardsPerSolar->SetBinContent(i + 1, 0); + } + } } } // namespace muonchambers diff --git a/Modules/MUON/MCH/src/DecodingPostProcessing.cxx b/Modules/MUON/MCH/src/DecodingPostProcessing.cxx index a53c38b3dd..4c0f2f3d37 100644 --- a/Modules/MUON/MCH/src/DecodingPostProcessing.cxx +++ b/Modules/MUON/MCH/src/DecodingPostProcessing.cxx @@ -19,6 +19,7 @@ #include "MCH/DecodingPostProcessing.h" #include "MCH/PostProcessingConfigMCH.h" #include "MUONCommon/Helpers.h" +#include "Common/ReferenceComparatorPlot.h" #include #include "QualityControl/QcInfoLogger.h" #include "QualityControl/DatabaseInterface.h" @@ -33,6 +34,8 @@ using namespace o2::mch::raw; void DecodingPostProcessing::configure(const boost::property_tree::ptree& config) { + ReferenceComparatorTask::configure(config); + mConfig = PostProcessingConfigMCH(getID(), config); } @@ -48,6 +51,13 @@ void DecodingPostProcessing::createDecodingErrorsHistos(Trigger t, repository::D mErrorsPlotter = std::make_unique("DecodingErrors/"); mErrorsPlotter->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mErrorsPlotter->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + if (mEnableLastCycleHistos) { // Helpers to extract plots from last cycle auto obj = mCcdbObjects.find(errorsSourceName()); @@ -59,6 +69,13 @@ void DecodingPostProcessing::createDecodingErrorsHistos(Trigger t, repository::D mErrorsPlotterOnCycle.reset(); mErrorsPlotterOnCycle = std::make_unique("DecodingErrors/LastCycle/"); mErrorsPlotterOnCycle->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + + for (auto& hinfo : mErrorsPlotterOnCycle->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } } } @@ -74,6 +91,13 @@ void DecodingPostProcessing::createHeartBeatPacketsHistos(Trigger t, repository: mHBPacketsPlotter = std::make_unique("HeartBeatPackets/", mFullHistos); mHBPacketsPlotter->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mHBPacketsPlotter->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + if (mEnableLastCycleHistos) { // Helpers to extract plots from last cycle auto obj = mCcdbObjects.find(hbPacketsSourceName()); @@ -85,6 +109,13 @@ void DecodingPostProcessing::createHeartBeatPacketsHistos(Trigger t, repository: mHBPacketsPlotterOnCycle.reset(); mHBPacketsPlotterOnCycle = std::make_unique("HeartBeatPackets/LastCycle/", mFullHistos); mHBPacketsPlotterOnCycle->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + + for (auto& hinfo : mHBPacketsPlotterOnCycle->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } } } @@ -100,6 +131,13 @@ void DecodingPostProcessing::createSyncStatusHistos(Trigger t, repository::Datab mSyncStatusPlotter = std::make_unique("SyncErrors/"); mSyncStatusPlotter->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mSyncStatusPlotter->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + if (mEnableLastCycleHistos) { // Helpers to extract plots from last cycle auto obj = mCcdbObjects.find(syncStatusSourceName()); @@ -111,6 +149,13 @@ void DecodingPostProcessing::createSyncStatusHistos(Trigger t, repository::Datab mSyncStatusPlotterOnCycle.reset(); mSyncStatusPlotterOnCycle = std::make_unique("SyncErrors/LastCycle/"); mSyncStatusPlotterOnCycle->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + + for (auto& hinfo : mSyncStatusPlotterOnCycle->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } } } @@ -118,6 +163,8 @@ void DecodingPostProcessing::createSyncStatusHistos(Trigger t, repository::Datab void DecodingPostProcessing::initialize(Trigger t, framework::ServiceRegistryRef services) { + ReferenceComparatorTask::initialize(t, services); + auto& qcdb = services.get(); const auto& activity = t.activity; @@ -156,6 +203,9 @@ void DecodingPostProcessing::initialize(Trigger t, framework::ServiceRegistryRef mHistogramQualityPerDE.reset(); mHistogramQualityPerDE = std::make_unique("QualityFlagPerDE", "Quality Flag vs DE", getNumDE(), 0, getNumDE(), 3, 0, 3); + addDEBinLabels(mHistogramQualityPerDE.get()); + addChamberDelimiters(mHistogramQualityPerDE.get()); + addChamberLabelsForDE(mHistogramQualityPerDE.get()); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(1, "Bad"); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(2, "Medium"); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(3, "Good"); @@ -164,6 +214,20 @@ void DecodingPostProcessing::initialize(Trigger t, framework::ServiceRegistryRef getObjectsManager()->startPublishing(mHistogramQualityPerDE.get(), core::PublicationPolicy::ThroughStop); getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerDE.get(), "colz"); getObjectsManager()->setDisplayHint(mHistogramQualityPerDE.get(), "gridy"); + + mHistogramQualityPerSolar.reset(); + mHistogramQualityPerSolar = std::make_unique("QualityFlagPerSolar", "Quality Flag vs Solar", getNumSolar(), 0, getNumSolar(), 3, 0, 3); + addSolarBinLabels(mHistogramQualityPerSolar.get()); + addChamberDelimitersToSolarHistogram(mHistogramQualityPerSolar.get()); + addChamberLabelsForSolar(mHistogramQualityPerSolar.get()); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(1, "Bad"); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(2, "Medium"); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(3, "Good"); + mHistogramQualityPerSolar->SetOption("col"); + mHistogramQualityPerSolar->SetStats(0); + getObjectsManager()->startPublishing(mHistogramQualityPerSolar.get(), core::PublicationPolicy::ThroughStop); + getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerSolar.get(), "col"); + getObjectsManager()->setDisplayHint(mHistogramQualityPerSolar.get(), "gridy"); } //_________________________________________________________________________________________ @@ -222,6 +286,18 @@ void DecodingPostProcessing::updateSyncStatusHistos(Trigger t, repository::Datab //_________________________________________________________________________________________ +TH1* DecodingPostProcessing::getHistogram(std::string_view plotName) +{ + TH1* result{ nullptr }; + for (auto hist : mHistogramsAll) { + if (plotName == hist->GetName()) { + result = hist; + break; + } + } + return result; +} + void DecodingPostProcessing::update(Trigger t, framework::ServiceRegistryRef services) { auto& qcdb = services.get(); @@ -229,6 +305,16 @@ void DecodingPostProcessing::update(Trigger t, framework::ServiceRegistryRef ser updateDecodingErrorsHistos(t, &qcdb); updateHeartBeatPacketsHistos(t, &qcdb); updateSyncStatusHistos(t, &qcdb); + + auto& comparatorPlots = getComparatorPlots(); + for (auto& [plotName, plot] : comparatorPlots) { + TH1* hist = getHistogram(plotName); + if (!hist) { + continue; + } + + plot->update(hist); + } } //_________________________________________________________________________________________ diff --git a/Modules/MUON/MCH/src/DigitsCheck.cxx b/Modules/MUON/MCH/src/DigitsCheck.cxx index 4ee43ffb5a..e1a0f3b646 100644 --- a/Modules/MUON/MCH/src/DigitsCheck.cxx +++ b/Modules/MUON/MCH/src/DigitsCheck.cxx @@ -16,6 +16,7 @@ #include "MCH/DigitsCheck.h" #include "MUONCommon/Helpers.h" +#include "QualityControl/ReferenceUtils.h" #include "QualityControl/MonitorObject.h" #include "QualityControl/QcInfoLogger.h" @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -40,25 +42,50 @@ void DigitsCheck::configure() void DigitsCheck::startOfActivity(const Activity& activity) { + // input histogram names customization mMeanRateHistName = getConfigurationParameter(mCustomParameters, "MeanRateHistName", mMeanRateHistName, activity); mGoodChanFracHistName = getConfigurationParameter(mCustomParameters, "GoodChanFracHistName", mGoodChanFracHistName, activity); + mMeanRatePerSolarHistName = getConfigurationParameter(mCustomParameters, "MeanRatePerSolarHistName", mMeanRatePerSolarHistName, activity); + mGoodChanFracPerSolarHistName = getConfigurationParameter(mCustomParameters, "GoodChanFracPerSolarHistName", mGoodChanFracPerSolarHistName, activity); + + mMeanRateRefCompHistName = getConfigurationParameter(mCustomParameters, "MeanRateRefCompHistName", mMeanRateRefCompHistName, activity); + mGoodChanFracRefCompHistName = getConfigurationParameter(mCustomParameters, "GoodChanFracRefCompHistName", mGoodChanFracRefCompHistName, activity); + + mMeanRatePerSolarRefCompHistName = getConfigurationParameter(mCustomParameters, "MeanRatePerSolarRefCompHistName", mMeanRatePerSolarRefCompHistName, activity); + mGoodChanFracPerSolarRefCompHistName = getConfigurationParameter(mCustomParameters, "GoodChanFracPerSolarRefCompHistName", mGoodChanFracPerSolarRefCompHistName, activity); + + // threshold customization getThresholdsPerStation(mCustomParameters, activity, "MinRate", mMinRatePerStation, mMinRate); getThresholdsPerStation(mCustomParameters, activity, "MaxRate", mMaxRatePerStation, mMaxRate); + mMinRateRatio = getConfigurationParameter(mCustomParameters, "MinRateRatio", mMinRateRatio, activity); + + mMinRatePerSolar = getConfigurationParameter(mCustomParameters, "MinRatePerSolar", mMinRatePerSolar, activity); + mMaxRatePerSolar = getConfigurationParameter(mCustomParameters, "MaxRatePerSolar", mMaxRatePerSolar, activity); + mMinRateRatioPerSolar = getConfigurationParameter(mCustomParameters, "MinRateRatioPerSolar", mMinRateRatioPerSolar, activity); + getThresholdsPerStation(mCustomParameters, activity, "MinGoodFraction", mMinGoodFractionPerStation, mMinGoodFraction); + mMinGoodFractionRatio = getConfigurationParameter(mCustomParameters, "MinGoodFractionRatio", mMinGoodFractionRatio, activity); + + mMinGoodFractionPerSolar = getConfigurationParameter(mCustomParameters, "MinGoodFractionPerSolar", mMinGoodFractionPerSolar, activity); + mMinGoodFractionRatioPerSolar = getConfigurationParameter(mCustomParameters, "MinGoodFractionRatioPerSolar", mMinGoodFractionRatioPerSolar, activity); mMaxBadST12 = getConfigurationParameter(mCustomParameters, "MaxBadDE_ST12", mMaxBadST12, activity); mMaxBadST345 = getConfigurationParameter(mCustomParameters, "MaxBadDE_ST345", mMaxBadST345, activity); mRatePlotScaleMin = getConfigurationParameter(mCustomParameters, "RatePlotScaleMin", mRatePlotScaleMin, activity); mRatePlotScaleMax = getConfigurationParameter(mCustomParameters, "RatePlotScaleMax", mRatePlotScaleMax, activity); + mRateRatioPlotScaleRange = getConfigurationParameter(mCustomParameters, "RateRatioPlotScaleRange", mRateRatioPlotScaleRange, activity); + mRateRatioPerSolarPlotScaleRange = getConfigurationParameter(mCustomParameters, "RateRatioPerSolarPlotScaleRange", mRateRatioPerSolarPlotScaleRange, activity); + mGoodFractionRatioPlotScaleRange = getConfigurationParameter(mCustomParameters, "GoodFractionRatioPlotScaleRange", mGoodFractionRatioPlotScaleRange, activity); + mGoodFractionRatioPerSolarPlotScaleRange = getConfigurationParameter(mCustomParameters, "GoodFractionRatioPerSolarPlotScaleRange", mGoodFractionRatioPerSolarPlotScaleRange, activity); mQualityChecker.mMaxBadST12 = mMaxBadST12; mQualityChecker.mMaxBadST345 = mMaxBadST345; } template -std::array checkPlot(TH1F* h, Lambda check) +std::array checkPlot(TH1* h, Lambda check) { std::array result; std::fill(result.begin(), result.end(), Quality::Null); @@ -84,7 +111,7 @@ std::array checkPlot(TH1F* h, Lambda check) return result; } -std::array DigitsCheck::checkMeanRates(TH1F* h) +std::array DigitsCheck::checkMeanRates(TH1* h) { auto checkFunction = [&](double val, int station) -> bool { auto minRate = mMinRate; @@ -102,7 +129,16 @@ std::array DigitsCheck::checkMeanRates(TH1F* h) return checkPlot(h, checkFunction); } -std::array DigitsCheck::checkBadChannels(TH1F* h) +std::array DigitsCheck::checkMeanRateRatios(TH1* h) +{ + auto checkFunction = [&](double val, int /*station*/) -> bool { + auto minRateRatio = mMinRateRatio; + return (val >= minRateRatio); + }; + return checkPlot(h, checkFunction); +} + +std::array DigitsCheck::checkBadChannels(TH1* h) { auto checkFunction = [&](double val, int station) -> bool { auto minGoodFraction = mMinGoodFraction; @@ -116,6 +152,63 @@ std::array DigitsCheck::checkBadChannels(TH1F* h) return checkPlot(h, checkFunction); } +std::array DigitsCheck::checkBadChannelRatios(TH1* h) +{ + auto checkFunction = [&](double val, int /*station*/) -> bool { + auto minGoodFractionRatio = mMinGoodFractionRatio; + return (val >= minGoodFractionRatio); + }; + return checkPlot(h, checkFunction); +} + +void DigitsCheck::checkSolarMeanRates(TH1* h) +{ + for (int solar = 0; solar < h->GetXaxis()->GetNbins(); solar++) { + int bin = solar + 1; + double value = h->GetBinContent(bin); + if (value >= mMinRatePerSolar && value <= mMaxRatePerSolar) { + continue; + } + mSolarQuality[solar] = Quality::Bad; + } +} + +void DigitsCheck::checkSolarMeanRateRatios(TH1* h) +{ + for (int solar = 0; solar < h->GetXaxis()->GetNbins(); solar++) { + int bin = solar + 1; + double value = h->GetBinContent(bin); + if (value >= mMinRateRatioPerSolar) { + continue; + } + mSolarQuality[solar] = Quality::Bad; + } +} + +void DigitsCheck::checkSolarBadChannels(TH1* h) +{ + for (int solar = 0; solar < h->GetXaxis()->GetNbins(); solar++) { + int bin = solar + 1; + double value = h->GetBinContent(bin); + if (value >= mMinGoodFractionPerSolar) { + continue; + } + mSolarQuality[solar] = Quality::Bad; + } +} + +void DigitsCheck::checkSolarBadChannelRatios(TH1* h) +{ + for (int solar = 0; solar < h->GetXaxis()->GetNbins(); solar++) { + int bin = solar + 1; + double value = h->GetBinContent(bin); + if (value >= mMinGoodFractionRatioPerSolar) { + continue; + } + mSolarQuality[solar] = Quality::Bad; + } +} + template static T* getHisto(TCanvas* c, std::string hname) { @@ -151,9 +244,11 @@ static T* getHisto(std::shared_ptr mo) Quality DigitsCheck::check(std::map>* moMap) { mQualityChecker.reset(); + std::fill(mSolarQuality.begin(), mSolarQuality.end(), Quality::Good); for (auto& [moName, mo] : *moMap) { + // DE histograms if (matchHistName(mo->getName(), mMeanRateHistName)) { TH1F* h = getHisto(mo); if (h) { @@ -161,7 +256,6 @@ Quality DigitsCheck::check(std::map> mQualityChecker.addCheckResult(q); } } - if (matchHistName(mo->getName(), mGoodChanFracHistName)) { TH1F* h = getHisto(mo); if (h) { @@ -169,6 +263,60 @@ Quality DigitsCheck::check(std::map> mQualityChecker.addCheckResult(q); } } + // comparisons with reference + if (matchHistName(mo->getName(), mMeanRateRefCompHistName)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (canvas) { + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (ratioPlot) { + auto q = checkMeanRateRatios(ratioPlot); + mQualityChecker.addCheckResult(q); + } + } + } + if (matchHistName(mo->getName(), mGoodChanFracRefCompHistName)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (canvas) { + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (ratioPlot) { + auto q = checkBadChannelRatios(ratioPlot); + mQualityChecker.addCheckResult(q); + } + } + } + + // SOLAR histograms + if (matchHistName(mo->getName(), mMeanRatePerSolarHistName)) { + TH1F* h = getHisto(mo); + if (h) { + checkSolarMeanRates(h); + } + } + if (matchHistName(mo->getName(), mGoodChanFracPerSolarHistName)) { + TH1F* h = getHisto(mo); + if (h) { + checkSolarBadChannels(h); + } + } + // comparisons with reference + if (matchHistName(mo->getName(), mMeanRatePerSolarRefCompHistName)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (canvas) { + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (ratioPlot) { + checkSolarMeanRateRatios(ratioPlot); + } + } + } + if (matchHistName(mo->getName(), mGoodChanFracPerSolarRefCompHistName)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (canvas) { + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (ratioPlot) { + checkSolarBadChannelRatios(ratioPlot); + } + } + } } return mQualityChecker.getQuality(); @@ -189,6 +337,103 @@ static void updateTitle(TH1* hist, std::string suffix) void DigitsCheck::beautify(std::shared_ptr mo, Quality checkResult) { + if ((mo->getName().find("RefComp/") != std::string::npos)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (!canvas) { + return; + } + + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (!ratioPlot) { + return; + } + + std::string messages; + auto refCompPlots = o2::quality_control::checker::getPlotsFromCanvas(canvas, messages); + + double ratioPlotRange{ -1 }; + double ratioThreshold{ -1 }; + double plotScaleMin{ -1 }; + double plotScaleMax{ -1 }; + bool isSolar{ false }; + + if (matchHistName(mo->getName(), mMeanRateRefCompHistName)) { + ratioPlotRange = mRateRatioPlotScaleRange; + ratioThreshold = mMinRateRatio; + plotScaleMin = mRatePlotScaleMin; + plotScaleMax = mRatePlotScaleMax; + isSolar = false; + } else if (matchHistName(mo->getName(), mMeanRatePerSolarRefCompHistName)) { + ratioPlotRange = mRateRatioPerSolarPlotScaleRange; + ratioThreshold = mMinRateRatioPerSolar; + plotScaleMin = mRatePlotScaleMin; + plotScaleMax = mRatePlotScaleMax; + isSolar = true; + } else if (matchHistName(mo->getName(), mGoodChanFracRefCompHistName)) { + ratioPlotRange = mGoodFractionRatioPlotScaleRange; + ratioThreshold = mMinGoodFractionRatio; + plotScaleMin = 0; + plotScaleMax = 1.05; + isSolar = false; + } else if (matchHistName(mo->getName(), mGoodChanFracPerSolarRefCompHistName)) { + ratioPlotRange = mGoodFractionRatioPerSolarPlotScaleRange; + ratioThreshold = mMinGoodFractionRatioPerSolar; + plotScaleMin = 0; + plotScaleMax = 1.05; + isSolar = true; + } + + if (ratioPlotRange < 0) { + return; + } + + ratioPlot->SetMinimum(1.0 - ratioPlotRange); + ratioPlot->SetMaximum(1.0 + ratioPlotRange); + ratioPlot->GetXaxis()->SetTickLength(0); + + if (isSolar) { + addChamberDelimitersToSolarHistogram(ratioPlot); + addSolarBinLabels(ratioPlot); + } else { + addChamberDelimiters(ratioPlot); + addDEBinLabels(ratioPlot); + } + drawThreshold(ratioPlot, ratioThreshold); + + if (refCompPlots.first) { + refCompPlots.first->SetMinimum(plotScaleMin); + refCompPlots.first->SetMaximum(plotScaleMax); + if (isSolar) { + addChamberDelimitersToSolarHistogram(refCompPlots.first); + addChamberLabelsForSolar(refCompPlots.first); + addSolarBinLabels(refCompPlots.first); + if (refCompPlots.second) { + addSolarBinLabels(refCompPlots.second); + } + } else { + addChamberDelimiters(refCompPlots.first); + addChamberLabelsForDE(refCompPlots.first); + addDEBinLabels(refCompPlots.first); + if (refCompPlots.second) { + addDEBinLabels(refCompPlots.second); + } + } + } + + if (refCompPlots.second) { + if (checkResult == Quality::Good) { + refCompPlots.second->SetLineColor(kGreen + 2); + } else if (checkResult == Quality::Bad) { + refCompPlots.second->SetLineColor(kRed); + } else if (checkResult == Quality::Medium) { + refCompPlots.second->SetLineColor(kOrange - 3); + } else if (checkResult == Quality::Null) { + refCompPlots.second->SetLineColor(kViolet - 6); + } + } + return; + } + if (mo->getName().find("Occupancy_Elec") != std::string::npos || mo->getName().find("OccupancySignal_Elec") != std::string::npos) { auto* h = dynamic_cast(mo->getObject()); @@ -232,8 +477,13 @@ void DigitsCheck::beautify(std::shared_ptr mo, Quality checkResul h->SetMaximum(scaleMax); h->GetYaxis()->SetTitle("rate (kHz)"); - addChamberDelimiters(h, scaleMin, scaleMax); - addDEBinLabels(h); + if (mo->getName().find("MeanRatePerSolar") != std::string::npos) { + addChamberDelimitersToSolarHistogram(h, scaleMin, scaleMax); + addChamberLabelsForSolar(h); + } else { + addChamberDelimiters(h, scaleMin, scaleMax); + addChamberLabelsForDE(h); + } // only the plot used for the check is beautified by changing the color // and adding the horizontal lines corresponding to the thresholds @@ -250,6 +500,21 @@ void DigitsCheck::beautify(std::shared_ptr mo, Quality checkResul drawThresholdsPerStation(h, mMinRatePerStation, mMinRate); drawThresholdsPerStation(h, mMaxRatePerStation, mMaxRate); } + + // also beautify the SOLAR plot + if (matchHistName(mo->getName(), mMeanRatePerSolarHistName)) { + if (checkResult == Quality::Good) { + h->SetFillColor(kGreen); + } else if (checkResult == Quality::Bad) { + h->SetFillColor(kRed); + } else if (checkResult == Quality::Medium) { + h->SetFillColor(kOrange); + } + h->SetLineColor(kBlack); + + drawThreshold(h, mMinRatePerSolar); + drawThreshold(h, mMaxRatePerSolar); + } } if (mo->getName().find("GoodChannelsFraction") != std::string::npos) { @@ -264,8 +529,13 @@ void DigitsCheck::beautify(std::shared_ptr mo, Quality checkResul h->SetMaximum(scaleMax); h->GetYaxis()->SetTitle("fraction"); - addChamberDelimiters(h, scaleMin, scaleMax); - addDEBinLabels(h); + if (mo->getName().find("GoodChannelsFractionPerSolar") != std::string::npos) { + addChamberDelimitersToSolarHistogram(h, scaleMin, scaleMax); + addChamberLabelsForSolar(h); + } else { + addChamberDelimiters(h, scaleMin, scaleMax); + addChamberLabelsForDE(h); + } // only the plot used for the check is beautified by changing the color // and adding the horizontal lines corresponding to the thresholds @@ -281,6 +551,20 @@ void DigitsCheck::beautify(std::shared_ptr mo, Quality checkResul drawThresholdsPerStation(h, mMinGoodFractionPerStation, mMinGoodFraction); } + + // also beautify the SOLAR plot + if (matchHistName(mo->getName(), mGoodChanFracPerSolarHistName)) { + if (checkResult == Quality::Good) { + h->SetFillColor(kGreen); + } else if (checkResult == Quality::Bad) { + h->SetFillColor(kRed); + } else if (checkResult == Quality::Medium) { + h->SetFillColor(kOrange); + } + h->SetLineColor(kBlack); + + drawThreshold(h, mMinGoodFractionPerSolar); + } } // update quality flags for each DE @@ -290,22 +574,74 @@ void DigitsCheck::beautify(std::shared_ptr mo, Quality checkResul return; } - addChamberDelimiters(h); - addDEBinLabels(h); + std::string badDEs; + for (int deIndex = 0; deIndex < mQualityChecker.mQuality.size(); deIndex++) { + float ybin = 0; + if (mQualityChecker.mQuality[deIndex] == Quality::Good) { + ybin = 3; + } + if (mQualityChecker.mQuality[deIndex] == Quality::Medium) { + ybin = 2; + } + if (mQualityChecker.mQuality[deIndex] == Quality::Bad) { + ybin = 1; + std::string deIdStr = std::to_string(getDEFromIndex(deIndex)); + if (badDEs.empty()) { + badDEs = deIdStr; + } else { + badDEs += std::string(" ") + deIdStr; + } + } + + h->SetBinContent(deIndex + 1, ybin, 1); + } + + if (!badDEs.empty()) { + std::string text = std::string("Bad DEs: ") + badDEs; + TPaveLabel* label = new TPaveLabel(0.2, 0.8, 0.8, 0.88, text.c_str(), "blNDC"); + label->SetBorderSize(1); + h->GetListOfFunctions()->Add(label); + + ILOG(Warning, Support) << "[DecodingCheck] " << text << ENDM; + } + } + + // update quality flags for each SOLAR + if (mo->getName().find("QualityFlagPerSolar") != std::string::npos) { + TH2F* h = getHisto(mo); + if (!h) { + return; + } - for (int deId = 0; deId < mQualityChecker.mQuality.size(); deId++) { + std::string badSolarBoards; + for (int solar = 0; solar < mSolarQuality.size(); solar++) { float ybin = 0; - if (mQualityChecker.mQuality[deId] == Quality::Good) { + if (mSolarQuality[solar] == Quality::Good) { ybin = 3; } - if (mQualityChecker.mQuality[deId] == Quality::Medium) { + if (mSolarQuality[solar] == Quality::Medium) { ybin = 2; } - if (mQualityChecker.mQuality[deId] == Quality::Bad) { + if (mSolarQuality[solar] == Quality::Bad) { ybin = 1; + std::string solarId = std::to_string(getSolarIdFromIndex(solar)); + if (badSolarBoards.empty()) { + badSolarBoards = solarId; + } else { + badSolarBoards += std::string(" ") + solarId; + } } - h->SetBinContent(deId + 1, ybin, 1); + h->SetBinContent(solar + 1, ybin, 1); + } + + if (!badSolarBoards.empty()) { + std::string badSolarList = std::string("Bad SOLAR boards: ") + badSolarBoards; + TPaveLabel* label = new TPaveLabel(0.2, 0.8, 0.8, 0.88, badSolarList.c_str(), "blNDC"); + label->SetBorderSize(1); + h->GetListOfFunctions()->Add(label); + + ILOG(Warning, Support) << "[DigitsCheck] " << badSolarList << ENDM; } } } diff --git a/Modules/MUON/MCH/src/DigitsPostProcessing.cxx b/Modules/MUON/MCH/src/DigitsPostProcessing.cxx index f273301cb0..3f3b6341fe 100644 --- a/Modules/MUON/MCH/src/DigitsPostProcessing.cxx +++ b/Modules/MUON/MCH/src/DigitsPostProcessing.cxx @@ -18,6 +18,8 @@ #include "MCH/DigitsPostProcessing.h" #include "MUONCommon/Helpers.h" +#include "Common/ReferenceComparatorPlot.h" +#include "QualityControl/ReferenceUtils.h" #include "QualityControl/QcInfoLogger.h" #include "QualityControl/DatabaseInterface.h" #include @@ -27,6 +29,8 @@ using namespace o2::quality_control_modules::muon; void DigitsPostProcessing::configure(const boost::property_tree::ptree& config) { + ReferenceComparatorTask::configure(config); + mConfig = PostProcessingConfigMCH(getID(), config); } @@ -42,10 +46,24 @@ void DigitsPostProcessing::createRatesHistos(Trigger t, repository::DatabaseInte mRatesPlotter = std::make_unique("Rates/", mChannelRateMin, mChannelRateMax, true, mFullHistos); mRatesPlotter->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mRatesPlotter->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + mRatesPlotterSignal.reset(); mRatesPlotterSignal = std::make_unique("RatesSignal/", mChannelRateMin, mChannelRateMax, true, mFullHistos); mRatesPlotterSignal->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mRatesPlotterSignal->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + //---------------------------------- // Rate plotters for last cycle //---------------------------------- @@ -68,9 +86,23 @@ void DigitsPostProcessing::createRatesHistos(Trigger t, repository::DatabaseInte mRatesPlotterOnCycle = std::make_unique("Rates/LastCycle/", mChannelRateMin, mChannelRateMax, false, mFullHistos); mRatesPlotterOnCycle->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mRatesPlotterOnCycle->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + mRatesPlotterSignalOnCycle.reset(); mRatesPlotterSignalOnCycle = std::make_unique("RatesSignal/LastCycle/", mChannelRateMin, mChannelRateMax, false, mFullHistos); mRatesPlotterSignalOnCycle->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + + for (auto& hinfo : mRatesPlotterSignalOnCycle->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } } //---------------------------------- @@ -100,10 +132,24 @@ void DigitsPostProcessing::createOrbitHistos(Trigger t, repository::DatabaseInte mOrbitsPlotter = std::make_unique("Orbits/"); mOrbitsPlotter->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mOrbitsPlotter->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + mOrbitsPlotterSignal.reset(); mOrbitsPlotterSignal = std::make_unique("OrbitsSignal/"); mOrbitsPlotterSignal->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mOrbitsPlotterSignal->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + //---------------------------------- // Orbit plotters for last cycle //---------------------------------- @@ -126,9 +172,23 @@ void DigitsPostProcessing::createOrbitHistos(Trigger t, repository::DatabaseInte mOrbitsPlotterOnCycle = std::make_unique("Orbits/LastCycle/"); mOrbitsPlotterOnCycle->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mOrbitsPlotterOnCycle->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + mOrbitsPlotterSignalOnCycle.reset(); mOrbitsPlotterSignalOnCycle = std::make_unique("OrbitsSignal/LastCycle/"); mOrbitsPlotterSignalOnCycle->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + + for (auto& hinfo : mOrbitsPlotterSignalOnCycle->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } } } @@ -136,6 +196,8 @@ void DigitsPostProcessing::createOrbitHistos(Trigger t, repository::DatabaseInte void DigitsPostProcessing::initialize(Trigger t, framework::ServiceRegistryRef services) { + ReferenceComparatorTask::initialize(t, services); + auto& qcdb = services.get(); const auto& activity = t.activity; @@ -177,14 +239,31 @@ void DigitsPostProcessing::initialize(Trigger t, framework::ServiceRegistryRef s mHistogramQualityPerDE.reset(); mHistogramQualityPerDE = std::make_unique("QualityFlagPerDE", "Quality Flag vs DE", getNumDE(), 0, getNumDE(), 3, 0, 3); + addDEBinLabels(mHistogramQualityPerDE.get()); + addChamberDelimiters(mHistogramQualityPerDE.get()); + addChamberLabelsForDE(mHistogramQualityPerDE.get()); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(1, "Bad"); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(2, "Medium"); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(3, "Good"); - mHistogramQualityPerDE->SetOption("colz"); + mHistogramQualityPerDE->SetOption("col"); mHistogramQualityPerDE->SetStats(0); getObjectsManager()->startPublishing(mHistogramQualityPerDE.get(), core::PublicationPolicy::ThroughStop); - getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerDE.get(), "colz"); + getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerDE.get(), "col"); getObjectsManager()->setDisplayHint(mHistogramQualityPerDE.get(), "gridy"); + + mHistogramQualityPerSolar.reset(); + mHistogramQualityPerSolar = std::make_unique("QualityFlagPerSolar", "Quality Flag vs Solar", getNumSolar(), 0, getNumSolar(), 3, 0, 3); + addSolarBinLabels(mHistogramQualityPerSolar.get()); + addChamberDelimitersToSolarHistogram(mHistogramQualityPerSolar.get()); + addChamberLabelsForSolar(mHistogramQualityPerSolar.get()); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(1, "Bad"); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(2, "Medium"); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(3, "Good"); + mHistogramQualityPerSolar->SetOption("col"); + mHistogramQualityPerSolar->SetStats(0); + getObjectsManager()->startPublishing(mHistogramQualityPerSolar.get(), core::PublicationPolicy::ThroughStop); + getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerSolar.get(), "col"); + getObjectsManager()->setDisplayHint(mHistogramQualityPerSolar.get(), "gridy"); } //_________________________________________________________________________________________ @@ -269,12 +348,34 @@ void DigitsPostProcessing::updateOrbitHistos(Trigger t, repository::DatabaseInte //_________________________________________________________________________________________ +TH1* DigitsPostProcessing::getHistogram(std::string_view plotName) +{ + TH1* result{ nullptr }; + for (auto hist : mHistogramsAll) { + if (plotName == hist->GetName()) { + result = hist; + break; + } + } + return result; +} + void DigitsPostProcessing::update(Trigger t, framework::ServiceRegistryRef services) { auto& qcdb = services.get(); updateRateHistos(t, &qcdb); updateOrbitHistos(t, &qcdb); + + auto& comparatorPlots = getComparatorPlots(); + for (auto& [plotName, plot] : comparatorPlots) { + TH1* hist = getHistogram(plotName); + if (!hist) { + continue; + } + + plot->update(hist); + } } //_________________________________________________________________________________________ diff --git a/Modules/MUON/MCH/src/EfficiencyPlotter.cxx b/Modules/MUON/MCH/src/EfficiencyPlotter.cxx index d003a50041..fc3b64610e 100644 --- a/Modules/MUON/MCH/src/EfficiencyPlotter.cxx +++ b/Modules/MUON/MCH/src/EfficiencyPlotter.cxx @@ -47,9 +47,15 @@ EfficiencyPlotter::EfficiencyPlotter(std::string path, bool fullPlots) mHistogramMeanEfficiencyPerDE[ci] = std::make_unique(TString::Format("%sMeanEfficiency%s", path.c_str(), sc[ci].c_str()), TString::Format("Mean Efficiency vs DE (%s)", sc[ci].c_str()), getNumDE(), 0, getNumDE()); + addDEBinLabels(mHistogramMeanEfficiencyPerDE[ci].get()); addHisto(mHistogramMeanEfficiencyPerDE[ci].get(), false, "histo", "histo"); } + mHistogramMeanEfficiencyPerSolar = std::make_unique(TString::Format("%sMeanEfficiencyPerSolar", path.c_str()), "Mean Efficiency per SOLAR board", + getNumSolar(), 0, getNumSolar()); + addSolarBinLabels(mHistogramMeanEfficiencyPerSolar.get()); + addHisto(mHistogramMeanEfficiencyPerSolar.get(), false, "histo", "histo"); + //-------------------------------------------------- // Efficiency histograms in global detector coordinates //-------------------------------------------------- @@ -93,6 +99,11 @@ void EfficiencyPlotter::fillAverageHistograms() mHistogramMeanEfficiencyPerDE[ci]->SetBinError(de + 1, 0.1); } } + + for (size_t solar = 0; solar < mHistogramMeanEfficiencyPerSolar->GetXaxis()->GetNbins(); solar++) { + mHistogramMeanEfficiencyPerSolar->SetBinContent(solar + 1, mElecMapReductor->getSolarValue(solar)); + mHistogramMeanEfficiencyPerSolar->SetBinError(solar + 1, 0.1); + } } //_________________________________________________________________________________________ diff --git a/Modules/MUON/MCH/src/FECSyncStatusPlotter.cxx b/Modules/MUON/MCH/src/FECSyncStatusPlotter.cxx index 014e6c3452..a153e015b8 100644 --- a/Modules/MUON/MCH/src/FECSyncStatusPlotter.cxx +++ b/Modules/MUON/MCH/src/FECSyncStatusPlotter.cxx @@ -42,16 +42,20 @@ static void setYAxisLabels(TH2F* hErrors) FECSyncStatusPlotter::FECSyncStatusPlotter(std::string path) { - mElec2DetMapper = createElec2DetMapper(); - mFeeLink2SolarMapper = createFeeLink2SolarMapper(); + mDet2ElecMapper = o2::mch::raw::createDet2ElecMapper(); //-------------------------------------------- // Fraction of synchronized boards per DE //-------------------------------------------- - mGoodTFFractionPerDE = std::make_unique(TString::Format("%sSyncedBoardsFractionPerDE", path.c_str()), "Synchronized boards fraction", getNumDE(), 0, getNumDE()); + mGoodTFFractionPerDE = std::make_unique(TString::Format("%sSyncedBoardsFractionPerDE", path.c_str()), "Synchronized boards fraction per DE", getNumDE(), 0, getNumDE()); + addDEBinLabels(mGoodTFFractionPerDE.get()); addHisto(mGoodTFFractionPerDE.get(), false, "hist", ""); + mGoodTFFractionPerSolar = std::make_unique(TString::Format("%sSyncedBoardsFractionPerSolar", path.c_str()), "Synchronized boards fraction per SOLAR", getNumSolar(), 0, getNumSolar()); + addSolarBinLabels(mGoodTFFractionPerSolar.get()); + addHisto(mGoodTFFractionPerSolar.get(), false, "hist", ""); + mGoodBoardsFractionPerDE = std::make_unique(TString::Format("%sSyncedBoardsFractionPerDEalt", path.c_str()), "Synchronized boards fraction (v.2)", getNumDE(), 0, getNumDE()); addHisto(mGoodBoardsFractionPerDE.get(), false, "hist", ""); } @@ -72,6 +76,12 @@ void FECSyncStatusPlotter::update(TH2F* h) std::fill(deGoodBoards.begin(), deGoodBoards.end(), 0); std::fill(deGoodFrac.begin(), deGoodFrac.end(), 0); + std::vector solarNumBoards(static_cast(getNumSolar())); + std::vector solarGoodFrac(static_cast(getNumSolar())); + + std::fill(solarNumBoards.begin(), solarNumBoards.end(), 0); + std::fill(solarGoodFrac.begin(), solarGoodFrac.end(), 0); + int nbinsx = h->GetXaxis()->GetNbins(); for (int i = 1; i <= nbinsx; i++) { // address of the DS board in detector representation @@ -83,6 +93,17 @@ void FECSyncStatusPlotter::update(TH2F* h) continue; } + auto dsElecId = mDet2ElecMapper(dsDetId); + if (!dsElecId) { + continue; + } + auto solarId = dsElecId->solarId(); + + int solarIndex = getSolarIndex(solarId); + if (solarIndex < 0) { + continue; + } + auto nGood = h->GetBinContent(i, 1); auto nBad = h->GetBinContent(i, 2); auto nMissing = h->GetBinContent(i, 3); @@ -94,6 +115,9 @@ void FECSyncStatusPlotter::update(TH2F* h) deGoodBoards[deIndex] += 1; } deGoodFrac[deIndex] += goodFrac; + + solarNumBoards[solarIndex] += 1; + solarGoodFrac[solarIndex] += goodFrac; } // update the average number of out-of-sync boards @@ -106,6 +130,14 @@ void FECSyncStatusPlotter::update(TH2F* h) mGoodTFFractionPerDE->SetBinContent(i + 1, 0); } } + + for (size_t i = 0; i < solarNumBoards.size(); i++) { + if (solarNumBoards[i] > 0) { + mGoodTFFractionPerSolar->SetBinContent(i + 1, solarGoodFrac[i] / solarNumBoards[i]); + } else { + mGoodTFFractionPerSolar->SetBinContent(i + 1, 0); + } + } } } // namespace muonchambers diff --git a/Modules/MUON/MCH/src/Helpers.cxx b/Modules/MUON/MCH/src/Helpers.cxx index 9334a6d1a3..5d58ae3bd2 100644 --- a/Modules/MUON/MCH/src/Helpers.cxx +++ b/Modules/MUON/MCH/src/Helpers.cxx @@ -21,6 +21,7 @@ #include "QualityControl/QcInfoLogger.h" #include "Common/Utils.h" #include "MCHRawElecMap/Mapper.h" +#include "MCHMappingInterface/CathodeSegmentation.h" #include #include #include @@ -149,6 +150,126 @@ int getDEindex(int deId) return idx.first + offset; } +int getDEFromIndex(int index) +{ + int deId = 0; + for (int chamber = 9; chamber >= 0; chamber--) { + int offset = getChamberOffset(chamber); + if (offset > index) { + continue; + } + + int indexInChamber = index - offset; + + int nDE = getNumDEinChamber(chamber); + if (nDE == 0) { + return 0; + } + // number of detectors in one half chamber + int nDEhc = nDE / 2; + + // minimum and maximum detector indexes for the L side + int lMin = (nDEhc + 1) / 2; + int lMax = lMin + nDEhc - 1; + if (indexInChamber < nDEhc) { + // detector on the L side, simply add lMin + // std::cout << "DE on L side, indexInChamber=" << indexInChamber << " nDEhc=" << nDEhc << std::endl; + deId = lMin + indexInChamber; + } else { + // number of detectors in one quarter of chamber + // std::cout << "DE on R side, indexInChamber=" << indexInChamber << " nDEhc=" << nDEhc << std::endl; + // detector on the R side, compute deId separately above and below middle horizontal axis + int indexInHalfChamber = indexInChamber - nDEhc; + deId = lMin - indexInHalfChamber - 1; + if (deId < 0) { + deId = lMax + (nDEhc - indexInHalfChamber); + } + } + + deId += (chamber + 1) * 100; + break; + } + + return deId; +} + +//_________________________________________________________________________________________ + +std::map buildSolarIdToSolarIndexMap() +{ + static std::map m; + if (m.empty()) { + uint32_t solarIndex{ 0 }; + // fill a sorted list of DetectionElement IDs + std::set deIds; + o2::mch::mapping::forEachDetectionElement([&](int deId) { + deIds.insert(deId); + }); + // loop over DE IDs, and add all the corresponding SOLAR IDs to the output vector + for (auto deId : deIds) { + auto solarIds = o2::mch::raw::getSolarUIDs(deId); + for (uint32_t solarId : solarIds) { + m.emplace(std::make_pair(solarId, solarIndex)); + solarIndex++; + } + } + } + return m; +} + +std::vector buildSolarIndexToSolarIdMap() +{ + static std::vector v; + if (v.empty()) { + auto m = buildSolarIdToSolarIndexMap(); + v.resize(m.size()); + for (auto [solarId, solarIndex] : m) { + v[solarIndex] = solarId; + } + } + return v; +} + +int getSolarIndex(int solarId) +{ + static std::map m = buildSolarIdToSolarIndexMap(); + try { + return m.at(solarId); + } catch (const std::exception&) { + ILOG(Error, Support) << "Invalid Solar Id: " << solarId << ENDM; + } + return -1; +} + +int getSolarIdFromIndex(int index) +{ + static std::vector v = buildSolarIndexToSolarIdMap(); + try { + return v.at(index); + } catch (const std::exception&) { + ILOG(Error, Support) << "Invalid Solar Index: " << index << ENDM; + } + return -1; +} + +int getNumSolarPerChamber(int chamberId) +{ + static std::array v{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + if (v[0] == 0) { + o2::mch::mapping::forEachDetectionElement([&](int deId) { + int chId = deId / 100; + if (chId > 0 && chId <= 10) { + auto solarIds = o2::mch::raw::getSolarUIDs(deId); + v[chId - 1] += solarIds.size(); + } + }); + } + if (chamberId > 0 && chamberId <= 10) { + return v[chamberId - 1]; + } + return -1; +} + //_________________________________________________________________________________________ void getThresholdsPerStation(o2::quality_control::core::CustomParameters customParameters, @@ -310,7 +431,7 @@ Quality QualityChecker::getQuality() //_________________________________________________________________________________________ -void addChamberDelimiters(TH1F* h, float xmin, float xmax) +void addChamberLabelsForDE(TH1* h) { if (!h) { return; @@ -327,11 +448,35 @@ void addChamberDelimiters(TH1F* h, float xmin, float xmax) xtitle->SetTextFont(42); h->GetListOfFunctions()->Add(xtitle); + // draw x-axis labels + for (int ch = 0; ch <= 9; ch += 1) { + float x1 = static_cast(getChamberOffset(ch)); + float x2 = (ch < 9) ? static_cast(getChamberOffset(ch + 1)) : h->GetXaxis()->GetXmax(); + float x0 = 0.8 * (x1 + x2) / (2 * h->GetXaxis()->GetXmax()) + 0.1; + float y0 = 0.05; + TText* label = new TText(); + label->SetNDC(); + label->SetText(x0, y0, TString::Format("%d", ch + 1)); + label->SetTextSize(10); + label->SetTextFont(42); + label->SetTextAlign(22); + h->GetListOfFunctions()->Add(label); + } +} + +//_________________________________________________________________________________________ + +void addChamberDelimiters(TH1* h, float xmin, float xmax) +{ + if (!h) { + return; + } + float scaleMin = xmin; float scaleMax = xmax; if (xmin == xmax) { - scaleMin = h->GetMinimum() * 0.95; - scaleMax = h->GetMaximum() * 1.05; + scaleMin = h->GetMinimum(); + scaleMax = h->GetMaximum(); } // draw chamber delimiters @@ -342,16 +487,38 @@ void addChamberDelimiters(TH1F* h, float xmin, float xmax) delimiter->SetLineStyle(kDashed); h->GetListOfFunctions()->Add(delimiter); } +} + +//_________________________________________________________________________________________ + +void addChamberLabelsForSolar(TH1* h) +{ + if (!h) { + return; + } + // disable ticks on horizontal axis + h->GetXaxis()->SetTickLength(0.0); + h->GetXaxis()->SetLabelSize(0.0); + h->GetYaxis()->SetTickLength(0); + + TText* xtitle = new TText(); + xtitle->SetNDC(); + xtitle->SetText(0.87, 0.03, "chamber"); + xtitle->SetTextSize(10); + xtitle->SetTextFont(42); + h->GetListOfFunctions()->Add(xtitle); // draw x-axis labels - for (int ch = 0; ch <= 9; ch += 1) { - float x1 = static_cast(getChamberOffset(ch)); - float x2 = (ch < 9) ? static_cast(getChamberOffset(ch + 1)) : h->GetXaxis()->GetXmax(); - float x0 = 0.8 * (x1 + x2) / (2 * h->GetXaxis()->GetXmax()) + 0.1; + float xMin{ 0 }; + float xMax{ 0 }; + for (int ch = 1; ch <= 10; ch += 1) { + xMin = xMax; + xMax += static_cast(getNumSolarPerChamber(ch)); + float x0 = 0.8 * (xMax + xMin) / (2 * h->GetXaxis()->GetXmax()) + 0.1; float y0 = 0.05; TText* label = new TText(); label->SetNDC(); - label->SetText(x0, y0, TString::Format("%d", ch + 1)); + label->SetText(x0, y0, TString::Format("%d", ch)); label->SetTextSize(10); label->SetTextFont(42); label->SetTextAlign(22); @@ -361,7 +528,50 @@ void addChamberDelimiters(TH1F* h, float xmin, float xmax) //_________________________________________________________________________________________ -void addChamberDelimiters(TH2F* h) +void addChamberDelimitersToSolarHistogram(TH1* h, float xmin, float xmax) +{ + if (!h) { + return; + } + + float scaleMin = xmin; + float scaleMax = xmax; + if (xmin == xmax) { + scaleMin = h->GetMinimum(); + scaleMax = h->GetMaximum(); + } + + // draw chamber delimiters + float xpos{ 0 }; + for (int ch = 1; ch <= 9; ch++) { + xpos += static_cast(getNumSolarPerChamber(ch)); + TLine* delimiter = new TLine(xpos, scaleMin, xpos, scaleMax); + delimiter->SetLineColor(kBlack); + delimiter->SetLineStyle(kDashed); + h->GetListOfFunctions()->Add(delimiter); + } + + // draw x-axis labels + /*float xMin{ 0 }; + float xMax{ 0 }; + for (int ch = 1; ch <= 10; ch += 1) { + xMin = xMax; + xMax += static_cast(getNumSolarPerChamber(ch)); + float x0 = 0.8 * (xMax + xMin) / (2 * h->GetXaxis()->GetXmax()) + 0.1; + float y0 = 0.05; + TText* label = new TText(); + label->SetNDC(); + label->SetText(x0, y0, TString::Format("%d", ch)); + label->SetTextSize(10); + label->SetTextFont(42); + label->SetTextAlign(22); + h->GetListOfFunctions()->Add(label); + }*/ +} + +//_________________________________________________________________________________________ + +void addChamberDelimiters(TH2* h) { if (!h) { return; @@ -408,6 +618,66 @@ void addChamberDelimiters(TH2F* h) //_________________________________________________________________________________________ +void addChamberDelimitersToSolarHistogram(TH2* h) +{ + if (!h) { + return; + } + // disable ticks on horizontal axis + h->GetXaxis()->SetTickLength(0.0); + h->GetXaxis()->SetLabelSize(0.0); + h->GetYaxis()->SetTickLength(0); + + TText* xtitle = new TText(); + xtitle->SetNDC(); + xtitle->SetText(0.87, 0.03, "chamber"); + xtitle->SetTextSize(10); + xtitle->SetTextFont(42); + h->GetListOfFunctions()->Add(xtitle); + + float scaleMin = h->GetYaxis()->GetXmin(); + float scaleMax = h->GetYaxis()->GetXmax(); + + // draw chamber delimiters + float xpos{ 0 }; + for (int ch = 1; ch <= 9; ch++) { + xpos += static_cast(getNumSolarPerChamber(ch)); + TLine* delimiter = new TLine(xpos, scaleMin, xpos, scaleMax); + delimiter->SetLineColor(kBlack); + delimiter->SetLineStyle(kDashed); + h->GetListOfFunctions()->Add(delimiter); + } + + // draw x-axis labels + float xMin{ 0 }; + float xMax{ 0 }; + for (int ch = 1; ch <= 10; ch += 1) { + xMin = xMax; + xMax += static_cast(getNumSolarPerChamber(ch)); + float x0 = 0.8 * (xMax + xMin) / (2 * h->GetXaxis()->GetXmax()) + 0.1; + float y0 = 0.05; + TText* label = new TText(); + label->SetNDC(); + label->SetText(x0, y0, TString::Format("%d", ch)); + label->SetTextSize(10); + label->SetTextFont(42); + label->SetTextAlign(22); + h->GetListOfFunctions()->Add(label); + } +} + +//_________________________________________________________________________________________ + +void drawThreshold(TH1* histogram, double threshold) +{ + TLine* l = new TLine(histogram->GetXaxis()->GetXmin(), threshold, histogram->GetXaxis()->GetXmax(), threshold); + l->SetLineColor(kBlue); + l->SetLineStyle(kDashed); + histogram->GetListOfFunctions()->Add(l); +} + +//_________________________________________________________________________________________ + void drawThresholdsPerStation(TH1* histogram, const std::array, 5>& thresholdsPerStation, double defaultThreshold) { double xmin = 0; @@ -442,6 +712,18 @@ void addDEBinLabels(TH1* histogram) //_________________________________________________________________________________________ +void addSolarBinLabels(TH1* histogram) +{ + auto solarIds = o2::mch::raw::getSolarUIDs(); + for (auto solar : solarIds) { + int bin = getSolarIndex(solar) + 1; + auto binLabel = std::string("S") + std::to_string(solar); + histogram->GetXaxis()->SetBinLabel(bin, binLabel.c_str()); + } +} + +//_________________________________________________________________________________________ + void splitDataSourceName(std::string s, std::string& type, std::string& name) { std::string delimiter = ":"; diff --git a/Modules/MUON/MCH/src/PedestalsCheck.cxx b/Modules/MUON/MCH/src/PedestalsCheck.cxx index a4761d3bf4..7d50ac6c9c 100644 --- a/Modules/MUON/MCH/src/PedestalsCheck.cxx +++ b/Modules/MUON/MCH/src/PedestalsCheck.cxx @@ -291,6 +291,7 @@ void PedestalsCheck::beautify(std::shared_ptr mo, Quality checkRe h->SetMaximum(1.1); addChamberDelimiters(h, 0, 1.1); + addChamberLabelsForDE(h); TLine* delimiter = new TLine(h->GetXaxis()->GetXmin(), mMaxEmptyFractionPerDE, h->GetXaxis()->GetXmax(), mMaxEmptyFractionPerDE); delimiter->SetLineColor(kBlack); @@ -314,6 +315,7 @@ void PedestalsCheck::beautify(std::shared_ptr mo, Quality checkRe h->SetMaximum(1.1); addChamberDelimiters(h, 0, 1.1); + addChamberLabelsForDE(h); TLine* delimiter = new TLine(h->GetXaxis()->GetXmin(), mMaxBadFractionPerDE, h->GetXaxis()->GetXmax(), mMaxBadFractionPerDE); delimiter->SetLineColor(kBlack); @@ -343,6 +345,7 @@ void PedestalsCheck::beautify(std::shared_ptr mo, Quality checkRe h->SetMaximum(max); addChamberDelimiters(h, min, max); + addChamberLabelsForDE(h); TLine* delimiter = new TLine(h->GetXaxis()->GetXmin(), mMinStatisticsPerDE, h->GetXaxis()->GetXmax(), mMinStatisticsPerDE); delimiter->SetLineColor(kBlack); @@ -369,6 +372,7 @@ void PedestalsCheck::beautify(std::shared_ptr mo, Quality checkRe h->SetMaximum(max); addChamberDelimiters(h, min, max); + addChamberLabelsForDE(h); } if (mo->getName().find("Pedestals_Elec") != std::string::npos) { diff --git a/Modules/MUON/MCH/src/PreclustersCheck.cxx b/Modules/MUON/MCH/src/PreclustersCheck.cxx index 29a5c259c7..74e022f8d6 100644 --- a/Modules/MUON/MCH/src/PreclustersCheck.cxx +++ b/Modules/MUON/MCH/src/PreclustersCheck.cxx @@ -16,6 +16,7 @@ #include "MCH/PreclustersCheck.h" #include "MCH/Helpers.h" #include "MUONCommon/Helpers.h" +#include "QualityControl/ReferenceUtils.h" #include "QualityControl/QcInfoLogger.h" #include "QualityControl/MonitorObject.h" @@ -25,6 +26,7 @@ #include #include #include +#include #include #include @@ -40,13 +42,25 @@ void PreclustersCheck::configure() void PreclustersCheck::startOfActivity(const Activity& activity) { + mMeanEffHistNameB = getConfigurationParameter(mCustomParameters, "MeanEffHistNameB", mMeanEffHistNameB, activity); + mMeanEffHistNameNB = getConfigurationParameter(mCustomParameters, "MeanEffHistNameNB", mMeanEffHistNameNB, activity); + mMeanEffPerSolarHistName = getConfigurationParameter(mCustomParameters, "MeanEffPerSolarHistName", mMeanEffPerSolarHistName, activity); + + mMeanEffRefCompHistNameB = getConfigurationParameter(mCustomParameters, "MeanEffRefCompHistNameB", mMeanEffRefCompHistNameB, activity); + mMeanEffRefCompHistNameNB = getConfigurationParameter(mCustomParameters, "MeanEffRefCompHistNameNB", mMeanEffRefCompHistNameNB, activity); + mMeanEffPerSolarRefCompHistName = getConfigurationParameter(mCustomParameters, "MeanEffPerSolarRefCompHistName", mMeanEffPerSolarRefCompHistName, activity); + getThresholdsPerStation(mCustomParameters, activity, "MinEfficiency", mMinEfficiencyPerStation, mMinEfficiency); + mMinEfficiencyPerSolar = getConfigurationParameter(mCustomParameters, "MinEfficiencyPerSolar", mMinEfficiencyPerSolar, activity); + + mMinEfficiencyRatio = getConfigurationParameter(mCustomParameters, "MinEfficiencyRatio", mMinEfficiencyRatio, activity); + mMinEfficiencyRatioPerSolar = getConfigurationParameter(mCustomParameters, "MinEfficiencyRatioPerSolar", mMinEfficiencyRatioPerSolar, activity); mPseudoeffPlotScaleMin = getConfigurationParameter(mCustomParameters, "PseudoeffPlotScaleMin", mPseudoeffPlotScaleMin, activity); mPseudoeffPlotScaleMax = getConfigurationParameter(mCustomParameters, "PseudoeffPlotScaleMax", mPseudoeffPlotScaleMax, activity); - mMeanEffHistNameB = getConfigurationParameter(mCustomParameters, "MeanEffHistNameB", mMeanEffHistNameB, activity); - mMeanEffHistNameNB = getConfigurationParameter(mCustomParameters, "MeanEffHistNameNB", mMeanEffHistNameNB, activity); + mEfficiencyRatioScaleRange = getConfigurationParameter(mCustomParameters, "EfficiencyRatioScaleRange", mEfficiencyRatioScaleRange, activity); + mEfficiencyRatioPerSolarScaleRange = getConfigurationParameter(mCustomParameters, "EfficiencyRatioPerSolarScaleRange", mEfficiencyRatioPerSolarScaleRange, activity); mMaxBadST12 = getConfigurationParameter(mCustomParameters, "MaxBadDE_ST12", mMaxBadST12, activity); mMaxBadST345 = getConfigurationParameter(mCustomParameters, "MaxBadDE_ST345", mMaxBadST345, activity); @@ -88,11 +102,15 @@ static T* getHisto(std::shared_ptr mo) } template -std::array checkPlot(TH1F* h, Lambda check) +std::array checkPlot(TH1* h, Lambda check) { std::array result; std::fill(result.begin(), result.end(), Quality::Null); + if (h->GetEntries() == 0) { + return result; + } + for (auto de : o2::mch::constants::deIdsForAllMCH) { int chamberId = (de - 100) / 100; int stationId = chamberId / 2; @@ -114,7 +132,7 @@ std::array checkPlot(TH1F* h, Lambda check) return result; } -std::array PreclustersCheck::checkMeanEfficiencies(TH1F* h) +std::array PreclustersCheck::checkMeanEfficiencies(TH1* h) { auto checkFunction = [&](double val, int station) -> bool { auto minEfficiency = mMinEfficiency; @@ -128,25 +146,89 @@ std::array PreclustersCheck::checkMeanEfficiencies(TH1F* h) return checkPlot(h, checkFunction); } +std::array PreclustersCheck::checkMeanEfficiencyRatios(TH1* h) +{ + auto checkFunction = [&](double val, int /*station*/) -> bool { + auto minEfficiency = mMinEfficiencyRatio; + return (val >= minEfficiency); + }; + return checkPlot(h, checkFunction); +} + +void PreclustersCheck::checkSolarMeanEfficiencies(TH1* h) +{ + for (int solar = 0; solar < h->GetXaxis()->GetNbins(); solar++) { + int bin = solar + 1; + double value = h->GetBinContent(bin); + if (value >= mMinEfficiencyPerSolar) { + continue; + } + mSolarQuality[solar] = Quality::Bad; + } +} + +void PreclustersCheck::checkSolarMeanEfficiencyRatios(TH1* h) +{ + for (int solar = 0; solar < h->GetXaxis()->GetNbins(); solar++) { + int bin = solar + 1; + double value = h->GetBinContent(bin); + if (value >= mMinEfficiencyRatio) { + continue; + } + mSolarQuality[solar] = Quality::Bad; + } +} + Quality PreclustersCheck::check(std::map>* moMap) { ILOG(Debug, Devel) << "Entered PreclustersCheck::check" << ENDM; ILOG(Debug, Devel) << " received a list of size : " << moMap->size() << ENDM; - for (const auto& item : *moMap) { - ILOG(Debug, Devel) << "Object: " << item.second->getName() << ENDM; + for (auto& [moName, mo] : *moMap) { + ILOG(Debug, Devel) << "Object: " << moName << " | " << mo->getName() << ENDM; } mQualityChecker.reset(); + std::fill(mSolarQuality.begin(), mSolarQuality.end(), Quality::Good); for (auto& [moName, mo] : *moMap) { - if (matchHistName(mo->getName(), mMeanEffHistNameB) || matchHistName(mo->getName(), mMeanEffHistNameNB)) { + if (matchHistName(moName, mMeanEffHistNameB) || matchHistName(moName, mMeanEffHistNameNB)) { TH1F* h = getHisto(mo); if (h) { auto q = checkMeanEfficiencies(h); mQualityChecker.addCheckResult(q); } } + + if (matchHistName(moName, mMeanEffPerSolarHistName)) { + TH1F* h = getHisto(mo); + if (h) { + checkSolarMeanEfficiencies(h); + } + } + + // Ratios with reference + if (matchHistName(moName, mMeanEffRefCompHistNameB) || matchHistName(moName, mMeanEffRefCompHistNameNB)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (canvas) { + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (ratioPlot) { + auto q = checkMeanEfficiencyRatios(ratioPlot); + mQualityChecker.addCheckResult(q); + } + } + } + + if (matchHistName(moName, mMeanEffPerSolarRefCompHistName)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (canvas) { + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (ratioPlot) { + ILOG(Debug, Devel) << "Checking eff ratio for SOLAR:" << ENDM; + checkSolarMeanEfficiencyRatios(ratioPlot); + } + } + } } return mQualityChecker.getQuality(); @@ -156,6 +238,92 @@ std::string PreclustersCheck::getAcceptedType() { return "TH1"; } void PreclustersCheck::beautify(std::shared_ptr mo, Quality checkResult) { + if ((mo->getName().find("RefComp/") != std::string::npos)) { + TCanvas* canvas = dynamic_cast(mo->getObject()); + if (!canvas) { + return; + } + + auto ratioPlot = o2::quality_control::checker::getRatioPlotFromCanvas(canvas); + if (!ratioPlot) { + return; + } + + std::string messages; + auto refCompPlots = o2::quality_control::checker::getPlotsFromCanvas(canvas, messages); + + double ratioPlotRange{ -1 }; + double ratioThreshold{ -1 }; + double plotScaleMin{ -1 }; + double plotScaleMax{ -1 }; + bool isSolar{ false }; + + if (matchHistName(mo->getName(), mMeanEffRefCompHistNameB) || + matchHistName(mo->getName(), mMeanEffRefCompHistNameNB)) { + ratioPlotRange = mEfficiencyRatioScaleRange; + ratioThreshold = mMinEfficiencyRatio; + plotScaleMin = mPseudoeffPlotScaleMin; + plotScaleMax = mPseudoeffPlotScaleMax; + isSolar = false; + } else if (matchHistName(mo->getName(), mMeanEffPerSolarRefCompHistName)) { + ratioPlotRange = mEfficiencyRatioPerSolarScaleRange; + ratioThreshold = mMinEfficiencyRatioPerSolar; + plotScaleMin = mPseudoeffPlotScaleMin; + plotScaleMax = mPseudoeffPlotScaleMax; + isSolar = true; + } + + if (ratioPlotRange < 0) { + return; + } + + ratioPlot->SetMinimum(1.0 - ratioPlotRange); + ratioPlot->SetMaximum(1.0 + ratioPlotRange); + ratioPlot->GetXaxis()->SetTickLength(0); + + if (isSolar) { + addChamberDelimitersToSolarHistogram(ratioPlot); + addSolarBinLabels(ratioPlot); + } else { + addChamberDelimiters(ratioPlot); + addDEBinLabels(ratioPlot); + } + drawThreshold(ratioPlot, ratioThreshold); + + if (refCompPlots.first) { + refCompPlots.first->SetMinimum(plotScaleMin); + refCompPlots.first->SetMaximum(plotScaleMax); + if (isSolar) { + addChamberDelimitersToSolarHistogram(refCompPlots.first); + addChamberLabelsForSolar(refCompPlots.first); + addSolarBinLabels(refCompPlots.first); + if (refCompPlots.second) { + addSolarBinLabels(refCompPlots.second); + } + } else { + addChamberDelimiters(refCompPlots.first); + addChamberLabelsForDE(refCompPlots.first); + addDEBinLabels(refCompPlots.first); + if (refCompPlots.second) { + addDEBinLabels(refCompPlots.second); + } + } + } + + if (refCompPlots.second) { + if (checkResult == Quality::Good) { + refCompPlots.second->SetLineColor(kGreen + 2); + } else if (checkResult == Quality::Bad) { + refCompPlots.second->SetLineColor(kRed); + } else if (checkResult == Quality::Medium) { + refCompPlots.second->SetLineColor(kOrange - 3); + } else if (checkResult == Quality::Null) { + refCompPlots.second->SetLineColor(kViolet - 6); + } + } + return; + } + if ((mo->getName().find("ChargeMPV") != std::string::npos)) { TH1F* h = getHisto(mo); if (!h) { @@ -197,12 +365,19 @@ void PreclustersCheck::beautify(std::shared_ptr mo, Quality check h->SetMaximum(1.05 * h->GetMaximum()); } - addChamberDelimiters(h, h->GetMinimum(), h->GetMaximum()); - addDEBinLabels(h); + if (mo->getName().find("MeanEfficiencyPerSolar") != std::string::npos) { + addChamberDelimitersToSolarHistogram(h, h->GetMinimum(), h->GetMaximum()); + addChamberLabelsForSolar(h); + } else { + addChamberDelimiters(h, h->GetMinimum(), h->GetMaximum()); + addChamberLabelsForDE(h); + } // only the plot used for the check is beautified by changing the color // and adding the horizontal lines corresponding to the thresholds - if (matchHistName(mo->getName(), mMeanEffHistNameB) || matchHistName(mo->getName(), mMeanEffHistNameNB)) { + if (matchHistName(mo->getName(), mMeanEffHistNameB) || + matchHistName(mo->getName(), mMeanEffHistNameNB) || + matchHistName(mo->getName(), mMeanEffPerSolarHistName)) { if (checkResult == Quality::Good) { h->SetFillColor(kGreen); } else if (checkResult == Quality::Bad) { @@ -212,7 +387,11 @@ void PreclustersCheck::beautify(std::shared_ptr mo, Quality check } h->SetLineColor(kBlack); - drawThresholdsPerStation(h, mMinEfficiencyPerStation, mMinEfficiency); + if (matchHistName(mo->getName(), mMeanEffPerSolarHistName)) { + drawThreshold(h, mMinEfficiencyPerSolar); + } else { + drawThresholdsPerStation(h, mMinEfficiencyPerStation, mMinEfficiency); + } } } @@ -236,19 +415,74 @@ void PreclustersCheck::beautify(std::shared_ptr mo, Quality check return; } - for (int deId = 0; deId < mQualityChecker.mQuality.size(); deId++) { + std::string badDEs; + for (int deIndex = 0; deIndex < mQualityChecker.mQuality.size(); deIndex++) { float ybin = 0; - if (mQualityChecker.mQuality[deId] == Quality::Good) { + if (mQualityChecker.mQuality[deIndex] == Quality::Good) { ybin = 3; } - if (mQualityChecker.mQuality[deId] == Quality::Medium) { + if (mQualityChecker.mQuality[deIndex] == Quality::Medium) { ybin = 2; } - if (mQualityChecker.mQuality[deId] == Quality::Bad) { + if (mQualityChecker.mQuality[deIndex] == Quality::Bad) { ybin = 1; + std::string deIdStr = std::to_string(getDEFromIndex(deIndex)); + if (badDEs.empty()) { + badDEs = deIdStr; + } else { + badDEs += std::string(" ") + deIdStr; + } } - h->SetBinContent(deId + 1, ybin, 1); + h->SetBinContent(deIndex + 1, ybin, 1); + } + + if (!badDEs.empty()) { + std::string text = std::string("Bad DEs: ") + badDEs; + TPaveLabel* label = new TPaveLabel(0.2, 0.85, 0.8, 0.92, text.c_str(), "blNDC"); + label->SetBorderSize(1); + h->GetListOfFunctions()->Add(label); + + ILOG(Warning, Support) << "[PreclustersCheck] " << text << ENDM; + } + } + + // update quality flags for each SOLAR + if (mo->getName().find("QualityFlagPerSolar") != std::string::npos) { + TH2F* h = getHisto(mo); + if (!h) { + return; + } + + std::string badSolarBoards; + for (int solar = 0; solar < mSolarQuality.size(); solar++) { + float ybin = 0; + if (mSolarQuality[solar] == Quality::Good) { + ybin = 3; + } + if (mSolarQuality[solar] == Quality::Medium) { + ybin = 2; + } + if (mSolarQuality[solar] == Quality::Bad) { + ybin = 1; + std::string solarId = std::to_string(getSolarIdFromIndex(solar)); + if (badSolarBoards.empty()) { + badSolarBoards = solarId; + } else { + badSolarBoards += std::string(" ") + solarId; + } + } + + h->SetBinContent(solar + 1, ybin, 1); + } + + if (!badSolarBoards.empty()) { + std::string badSolarList = std::string("Bad SOLAR boards: ") + badSolarBoards; + TPaveLabel* label = new TPaveLabel(0.2, 0.85, 0.8, 0.92, badSolarList.c_str(), "blNDC"); + label->SetBorderSize(1); + h->GetListOfFunctions()->Add(label); + + ILOG(Warning, Support) << "[PreclustersCheck] " << badSolarList << ENDM; } } } diff --git a/Modules/MUON/MCH/src/PreclustersPostProcessing.cxx b/Modules/MUON/MCH/src/PreclustersPostProcessing.cxx index b6e5b4b14d..ab10511a7e 100644 --- a/Modules/MUON/MCH/src/PreclustersPostProcessing.cxx +++ b/Modules/MUON/MCH/src/PreclustersPostProcessing.cxx @@ -18,6 +18,8 @@ #include "MCH/PreclustersPostProcessing.h" #include "MUONCommon/Helpers.h" +#include "Common/ReferenceComparatorPlot.h" +#include "QualityControl/ReferenceUtils.h" #include "QualityControl/QcInfoLogger.h" using namespace o2::quality_control_modules::muonchambers; @@ -25,6 +27,8 @@ using namespace o2::quality_control_modules::muon; void PreclustersPostProcessing::configure(const boost::property_tree::ptree& config) { + ReferenceComparatorTask::configure(config); + mConfig = PostProcessingConfigMCH(getID(), config); } @@ -40,6 +44,13 @@ void PreclustersPostProcessing::createEfficiencyHistos(Trigger t, repository::Da mEfficiencyPlotter = std::make_unique("Efficiency/", mFullHistos); mEfficiencyPlotter->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + for (auto& hinfo : mEfficiencyPlotter->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } + if (mEnableLastCycleHistos) { // Helpers to extract plots from last cycle auto obj = mCcdbObjects.find(effSourceName()); @@ -50,6 +61,13 @@ void PreclustersPostProcessing::createEfficiencyHistos(Trigger t, repository::Da mEfficiencyPlotterOnCycle.reset(); mEfficiencyPlotterOnCycle = std::make_unique("Efficiency/LastCycle/", mFullHistos); mEfficiencyPlotterOnCycle->publish(getObjectsManager(), core::PublicationPolicy::ThroughStop); + + for (auto& hinfo : mEfficiencyPlotterOnCycle->histograms()) { + TH1* hist = dynamic_cast(hinfo.object); + if (hist) { + mHistogramsAll.push_back(hist); + } + } } //---------------------------------- @@ -139,6 +157,8 @@ void PreclustersPostProcessing::createClusterSizeHistos(Trigger t, repository::D void PreclustersPostProcessing::initialize(Trigger t, framework::ServiceRegistryRef services) { + ReferenceComparatorTask::initialize(t, services); + auto& qcdb = services.get(); const auto& activity = t.activity; @@ -172,11 +192,14 @@ void PreclustersPostProcessing::initialize(Trigger t, framework::ServiceRegistry createClusterSizeHistos(t, &qcdb); //-------------------------------------------------- - // Detector quality histogram + // Quality histogram //-------------------------------------------------- mHistogramQualityPerDE.reset(); mHistogramQualityPerDE = std::make_unique("QualityFlagPerDE", "Quality Flag vs DE", getNumDE(), 0, getNumDE(), 3, 0, 3); + addDEBinLabels(mHistogramQualityPerDE.get()); + addChamberDelimiters(mHistogramQualityPerDE.get()); + addChamberLabelsForDE(mHistogramQualityPerDE.get()); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(1, "Bad"); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(2, "Medium"); mHistogramQualityPerDE->GetYaxis()->SetBinLabel(3, "Good"); @@ -185,6 +208,20 @@ void PreclustersPostProcessing::initialize(Trigger t, framework::ServiceRegistry getObjectsManager()->startPublishing(mHistogramQualityPerDE.get(), core::PublicationPolicy::ThroughStop); getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerDE.get(), "colz"); getObjectsManager()->setDisplayHint(mHistogramQualityPerDE.get(), "gridy"); + + mHistogramQualityPerSolar.reset(); + mHistogramQualityPerSolar = std::make_unique("QualityFlagPerSolar", "Quality Flag vs Solar", getNumSolar(), 0, getNumSolar(), 3, 0, 3); + addSolarBinLabels(mHistogramQualityPerSolar.get()); + addChamberDelimitersToSolarHistogram(mHistogramQualityPerSolar.get()); + addChamberLabelsForSolar(mHistogramQualityPerSolar.get()); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(1, "Bad"); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(2, "Medium"); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(3, "Good"); + mHistogramQualityPerSolar->SetOption("col"); + mHistogramQualityPerSolar->SetStats(0); + getObjectsManager()->startPublishing(mHistogramQualityPerSolar.get(), core::PublicationPolicy::ThroughStop); + getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerSolar.get(), "col"); + getObjectsManager()->setDisplayHint(mHistogramQualityPerSolar.get(), "gridy"); } //_________________________________________________________________________________________ @@ -270,6 +307,18 @@ void PreclustersPostProcessing::updateClusterSizeHistos(Trigger t, repository::D //_________________________________________________________________________________________ +TH1* PreclustersPostProcessing::getHistogram(std::string_view plotName) +{ + TH1* result{ nullptr }; + for (auto hist : mHistogramsAll) { + if (plotName == hist->GetName()) { + result = hist; + break; + } + } + return result; +} + void PreclustersPostProcessing::update(Trigger t, framework::ServiceRegistryRef services) { auto& qcdb = services.get(); @@ -277,10 +326,21 @@ void PreclustersPostProcessing::update(Trigger t, framework::ServiceRegistryRef updateEfficiencyHistos(t, &qcdb); updateClusterChargeHistos(t, &qcdb); updateClusterSizeHistos(t, &qcdb); + + auto& comparatorPlots = getComparatorPlots(); + for (auto& [plotName, plot] : comparatorPlots) { + TH1* hist = getHistogram(plotName); + if (!hist) { + continue; + } + + plot->update(hist); + } } //_________________________________________________________________________________________ -void PreclustersPostProcessing::finalize(Trigger t, framework::ServiceRegistryRef) +void PreclustersPostProcessing::finalize(Trigger t, framework::ServiceRegistryRef services) { + ReferenceComparatorTask::finalize(t, services); } diff --git a/Modules/MUON/MCH/src/PreclustersTask.cxx b/Modules/MUON/MCH/src/PreclustersTask.cxx index 2b9bb096cd..181374144e 100644 --- a/Modules/MUON/MCH/src/PreclustersTask.cxx +++ b/Modules/MUON/MCH/src/PreclustersTask.cxx @@ -91,17 +91,17 @@ void PreclustersTask::initialize(o2::framework::InitContext& /*ctx*/) publishObject(mHistogramClusterCharge.get(), "colz", false); mHistogramClusterChargePerStation[0] = std::make_unique("ClusterCharge/ClusterChargeDistributionB", - "Cluster charge distribution (B)", + "Pre-cluster charge distribution (B)", 256, 0, 256 * 50, 5, 1, 6); publishObject(mHistogramClusterChargePerStation[0].get(), "colz", false); mHistogramClusterChargePerStation[1] = std::make_unique("ClusterCharge/ClusterChargeDistributionNB", - "Cluster charge distribution (NB)", + "Pre-cluster charge distribution (NB)", 256, 0, 256 * 50, 5, 1, 6); publishObject(mHistogramClusterChargePerStation[1].get(), "colz", false); mHistogramClusterChargePerStation[2] = std::make_unique("ClusterCharge/ClusterChargeDistribution", - "Cluster charge distribution", + "Pre-cluster charge distribution", 256, 0, 256 * 50, 5, 1, 6); publishObject(mHistogramClusterChargePerStation[2].get(), "colz", false); @@ -115,17 +115,17 @@ void PreclustersTask::initialize(o2::framework::InitContext& /*ctx*/) publishObject(mHistogramClusterSize.get(), "colz", false); mHistogramClusterSizePerStation[0] = std::make_unique("ClusterSize/ClusterSizeDistributionB", - "Cluster size distribution (B)", + "Pre-cluster size distribution (B)", 50, 0, 50, 5, 1, 6); publishObject(mHistogramClusterSizePerStation[0].get(), "colz", false); mHistogramClusterSizePerStation[1] = std::make_unique("ClusterSize/ClusterSizeDistributionNB", - "Cluster size distribution (NB)", + "Pre-cluster size distribution (NB)", 50, 0, 50, 5, 1, 6); publishObject(mHistogramClusterSizePerStation[1].get(), "colz", false); mHistogramClusterSizePerStation[2] = std::make_unique("ClusterSize/ClusterSizeDistribution", - "Cluster size distribution", + "Pre-cluster size distribution", 50, 0, 50, 5, 1, 6); publishObject(mHistogramClusterSizePerStation[2].get(), "colz", false); @@ -299,6 +299,7 @@ void PreclustersTask::plotPrecluster(const o2::mch::PreCluster& preCluster, gsl: auto stationId = ((chamberId - 1) / 2) + 1; if (stationId < 1 || stationId > 5) { + ILOG(Info, Devel) << "[PreclustersTask::plotPrecluster()] wrong station ID for DE" << deId << AliceO2::InfoLogger::InfoLogger::endm; return; } const o2::mch::mapping::Segmentation& segment = o2::mch::mapping::segmentation(deId); @@ -341,6 +342,10 @@ void PreclustersTask::plotPrecluster(const o2::mch::PreCluster& preCluster, gsl: if (segment.findPadPairByPosition(Xcog, Ycog, padIdB, padIdNB)) { getFecChannel(deId, padIdB, fecIdB, channelB); getFecChannel(deId, padIdNB, fecIdNB, channelNB); + } else { + ILOG(Info, Devel) << "[PreclustersTask::plotPrecluster()] could not find pad in DE" << deId + << " for pre-cluster at (" << Xcog << "," << Ycog << ")" << AliceO2::InfoLogger::InfoLogger::endm; + return; } // criteria to define a "good" charge cluster in one cathode: diff --git a/Modules/MUON/MCH/src/QualityAggregatorTask.cxx b/Modules/MUON/MCH/src/QualityAggregatorTask.cxx new file mode 100644 index 0000000000..7dff36bc9f --- /dev/null +++ b/Modules/MUON/MCH/src/QualityAggregatorTask.cxx @@ -0,0 +1,395 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file QualityAggregatorTask.cxx +/// \author Andrea Ferrero andrea.ferrero@cern.ch +/// \brief Post-processing of the MCH summary qualities +/// \since 21/06/2022 +/// + +#include "MCH/QualityAggregatorTask.h" +#include "MCH/Helpers.h" +#include "QualityControl/QcInfoLogger.h" +#include "QualityControl/DatabaseInterface.h" +#include "QualityControl/ActivityHelpers.h" +#include "QualityControl/ReferenceUtils.h" +#include "Common/Utils.h" +#include "CCDB/CcdbObjectInfo.h" +#include "CommonUtils/MemFileHelper.h" +#include + +#include + +#include +#include + +using namespace o2::quality_control::core; +using namespace o2::quality_control::repository; +using namespace o2::quality_control_modules::common; +using namespace o2::quality_control_modules::muonchambers; + +template +o2::ccdb::CcdbObjectInfo createCcdbInfo(const T& object, uint64_t timeStamp, std::string_view reason) +{ + auto clName = o2::utils::MemFileHelper::getClassName(object); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + std::map md; + md["upload-reason"] = reason; + constexpr auto fiveDays = 5 * o2::ccdb::CcdbObjectInfo::DAY; + return o2::ccdb::CcdbObjectInfo("MCH/Calib/BadDE", clName, flName, md, timeStamp, timeStamp + fiveDays); +} + +/* +std::string toCSV(const std::set& deIds) +{ + std::stringstream csv; + csv << fmt::format("deid\n"); + + for (auto deId : deIds) { + csv << fmt::format("{}\n", deId); + } + + return csv.str(); +} +*/ +//_________________________________________________________________________________________ +// Helper function for retrieving a MonitorObject from the QCDB, in the form of a std::pair, bool> +// A non-null MO is returned in the first element of the pair if the MO is found in the QCDB +// The second element of the pair is set to true if the MO has a time stamp more recent than a user-supplied threshold + +static std::pair, bool> getMO(DatabaseInterface& qcdb, const std::string& fullPath, Trigger trigger, long notOlderThan) +{ + // find the time-stamp of the most recent object matching the current activity + // if ignoreActivity is true the activity matching criteria are not applied + auto objectTimestamp = trigger.timestamp; + const auto filterMetadata = activity_helpers::asDatabaseMetadata(trigger.activity, false); + const auto objectValidity = qcdb.getLatestObjectValidity(trigger.activity.mProvenance + "/" + fullPath, filterMetadata); + if (objectValidity.isValid()) { + objectTimestamp = objectValidity.getMax() - 1; + } else { + ILOG(Warning, Devel) << "Could not find the object '" << fullPath << "' for activity " << trigger.activity << ENDM; + return { nullptr, false }; + } + + auto [success, path, name] = o2::quality_control::core::RepoPathUtils::splitObjectPath(fullPath); + if (!success) { + return { nullptr, false }; + } + // retrieve QO from CCDB + auto qo = qcdb.retrieveMO(path, name, objectTimestamp, trigger.activity); + if (!qo) { + return { nullptr, false }; + } + + long elapsed = static_cast(trigger.timestamp) - objectTimestamp; + // check if the object is not older than a given number of milliseconds + if (elapsed > notOlderThan) { + ILOG(Warning, Devel) << "Object '" << fullPath << "' for activity " << trigger.activity << " is too old: " << elapsed << " > " << notOlderThan << ENDM; + return { qo, false }; + } + + return { qo, true }; +} + +void QualityAggregatorTask::configure(const boost::property_tree::ptree& config) +{ + // input plots + if (const auto& inputs = config.get_child_optional("qc.postprocessing." + getID() + ".inputsDE"); inputs.has_value()) { + for (const auto& input : inputs.value()) { + mDEPlotPaths.push_back(input.second.get_value()); + } + } + if (const auto& inputs = config.get_child_optional("qc.postprocessing." + getID() + ".inputsSOLAR"); inputs.has_value()) { + for (const auto& input : inputs.value()) { + mSOLARPlotPaths.push_back(input.second.get_value()); + } + } +} + +//_________________________________________________________________________________________ + +void QualityAggregatorTask::initialize(Trigger t, framework::ServiceRegistryRef services) +{ + mCCDBpath = getFromExtendedConfig(t.activity, mCustomParameters, "CCDBpath", mCCDBpath); + mAPI.init(mCCDBpath); + + mObjectPathBadDE = getFromExtendedConfig(t.activity, mCustomParameters, "objectPathBadDE", mObjectPathBadDE); + mObjectPathBadSOLAR = getFromExtendedConfig(t.activity, mCustomParameters, "objectPathBadSOLAR", mObjectPathBadSOLAR); + + //-------------------------------------------------- + // Quality histogram + //-------------------------------------------------- + + mHistogramQualityPerDE.reset(); + mHistogramQualityPerDE = std::make_unique("QualityFlagPerDE", "Quality Flag vs DE", getNumDE(), 0, getNumDE(), 3, 0, 3); + addDEBinLabels(mHistogramQualityPerDE.get()); + addChamberDelimiters(mHistogramQualityPerDE.get()); + addChamberLabelsForDE(mHistogramQualityPerDE.get()); + mHistogramQualityPerDE->GetYaxis()->SetBinLabel(1, "Bad"); + mHistogramQualityPerDE->GetYaxis()->SetBinLabel(2, "Medium"); + mHistogramQualityPerDE->GetYaxis()->SetBinLabel(3, "Good"); + mHistogramQualityPerDE->SetOption("colz"); + mHistogramQualityPerDE->SetStats(0); + getObjectsManager()->startPublishing(mHistogramQualityPerDE.get(), core::PublicationPolicy::ThroughStop); + getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerDE.get(), "colz"); + getObjectsManager()->setDisplayHint(mHistogramQualityPerDE.get(), "gridy"); + + mHistogramQualityPerSolar.reset(); + mHistogramQualityPerSolar = std::make_unique("QualityFlagPerSolar", "Quality Flag vs Solar", getNumSolar(), 0, getNumSolar(), 3, 0, 3); + addSolarBinLabels(mHistogramQualityPerSolar.get()); + addChamberDelimitersToSolarHistogram(mHistogramQualityPerSolar.get()); + addChamberLabelsForSolar(mHistogramQualityPerSolar.get()); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(1, "Bad"); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(2, "Medium"); + mHistogramQualityPerSolar->GetYaxis()->SetBinLabel(3, "Good"); + mHistogramQualityPerSolar->SetOption("col"); + mHistogramQualityPerSolar->SetStats(0); + getObjectsManager()->startPublishing(mHistogramQualityPerSolar.get(), core::PublicationPolicy::ThroughStop); + getObjectsManager()->setDefaultDrawOptions(mHistogramQualityPerSolar.get(), "col"); + getObjectsManager()->setDisplayHint(mHistogramQualityPerSolar.get(), "gridy"); +} + +//_________________________________________________________________________________________ + +void QualityAggregatorTask::update(Trigger trigger, framework::ServiceRegistryRef services) +{ + ILOG(Info, Devel) << "QualityAggregatorTask::update() called" << ENDM; + auto& qcdb = services.get(); + + // =================== + // Detection elements + // =================== + + std::set badDEs; + std::array deQuality; + std::fill(deQuality.begin(), deQuality.end(), 3); + // fill vector of bad DEs + for (auto plotPath : mDEPlotPaths) { + auto [mo, success] = getMO(qcdb, plotPath, trigger, 600000); + if (!success || !mo) { + ILOG(Warning, Devel) << "Could not retrieve object " << plotPath << ENDM; + continue; + } + + TH2F* hist = dynamic_cast(mo->getObject()); + if (!hist) { + ILOG(Warning, Devel) << "Could not cast the object '" << plotPath << "' to TH2F" << ENDM; + continue; + } + + if (hist->GetXaxis()->GetNbins() != getNumDE()) { + ILOG(Warning, Devel) << "Wrong number of bins for object '" << plotPath << "': " + << hist->GetXaxis()->GetNbins() << " while " << getNumDE() << " were expected" << ENDM; + continue; + } + + for (int index = 0; index < hist->GetXaxis()->GetNbins(); index++) { + int q = 0; + for (int qindex = 1; qindex <= hist->GetYaxis()->GetNbins(); qindex++) { + if (hist->GetBinContent(index + 1, qindex) != 0) { + q = qindex; + break; + } + } + if (q < deQuality[index]) { + deQuality[index] = q; + } + + bool isBad = (q == 1); + if (!isBad) { + continue; + } + + int deId = getDEFromIndex(index); + badDEs.insert(deId); + + ILOG(Debug, Devel) << "Bad detection element DE" << deId << " found in \'" << plotPath << "\'" << ENDM; + } + } + mHistogramQualityPerDE->Reset(); + for (int index = 0; index < mHistogramQualityPerDE->GetXaxis()->GetNbins(); index++) { + mHistogramQualityPerDE->SetBinContent(index + 1, deQuality[index], 1); + } + + std::string badDEsStr; + for (auto deId : badDEs) { + if (badDEsStr.empty()) { + badDEsStr = std::to_string(deId); + } else { + badDEsStr += std::string(" ") + std::to_string(deId); + } + } + if (!badDEsStr.empty()) { + std::string badDEList = std::string("Bad DEs: ") + badDEsStr; + TPaveLabel* label = new TPaveLabel(0.2, 0.8, 0.8, 0.88, badDEList.c_str(), "blNDC"); + label->SetBorderSize(1); + mHistogramQualityPerDE->GetListOfFunctions()->Add(label); + + ILOG(Warning, Support) << "[QualityAggregator] " << badDEList << ENDM; + } + + // check if the list of bad DEs has changed + bool changedDEs = (!mPreviousBadDEs.has_value() || mPreviousBadDEs.value() != badDEs); + + if (changedDEs) { + std::string sBadDEs = "deid\n"; + for (auto deId : badDEs) { + sBadDEs += fmt::format("{}\n", deId); + } + TObjString badDEsObject(sBadDEs.c_str()); + + // time stamp + auto uploadTS = o2::ccdb::getCurrentTimestamp(); + + // CCDB object info + auto clName = o2::utils::MemFileHelper::getClassName(badDEsObject); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + constexpr auto fiveDays = 5 * o2::ccdb::CcdbObjectInfo::DAY; + auto info = o2::ccdb::CcdbObjectInfo(mObjectPathBadDE, clName, flName, std::map(), uploadTS, uploadTS + fiveDays); + + // CCDB object image + auto image = o2::ccdb::CcdbApi::createObjectImage(&badDEsObject, &info); + + ILOG(Info, Devel) << "Storing object " << info.getPath() + << " of type " << info.getObjectType() + << " / " << info.getFileName() + << " and size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() + << " : " << info.getEndValidityTimestamp() << ENDM; + + int res = mAPI.storeAsBinaryFile(image->data(), image->size(), info.getFileName(), info.getObjectType(), info.getPath(), info.getMetaData(), info.getStartValidityTimestamp(), info.getEndValidityTimestamp()); + if (res) { + ILOG(Error, Ops) << fmt::format("uploading to {} / {} failed for [{}:{}]", mAPI.getURL(), info.getPath(), info.getStartValidityTimestamp(), info.getEndValidityTimestamp()) << ENDM; + } else { + ILOG(Info, Devel) << fmt::format("uploading to {} / {} succeeded for [{}:{}]", mAPI.getURL(), info.getPath(), info.getStartValidityTimestamp(), info.getEndValidityTimestamp()) << ENDM; + mPreviousBadDEs = badDEs; + } + } + + // ============= + // SOLAR boards + // ============= + + std::set badSolarBoards; + std::array solarQuality; + std::fill(solarQuality.begin(), solarQuality.end(), 3); + // fill vector of bad Solars + for (auto plotPath : mSOLARPlotPaths) { + auto [mo, success] = getMO(qcdb, plotPath, trigger, 600000); + if (!success || !mo) { + ILOG(Warning, Devel) << "Could not retrieve object " << plotPath << ENDM; + continue; + } + + TH2F* hist = dynamic_cast(mo->getObject()); + if (!hist) { + ILOG(Warning, Devel) << "Could not cast the object '" << plotPath << "' to TH2F" << ENDM; + continue; + } + + if (hist->GetXaxis()->GetNbins() != getNumSolar()) { + ILOG(Warning, Devel) << "Wrong number of bins for object '" << plotPath << "': " + << hist->GetXaxis()->GetNbins() << " while " << getNumSolar() << " were expected" << ENDM; + continue; + } + + for (int index = 0; index < hist->GetXaxis()->GetNbins(); index++) { + int q = 0; + for (int qindex = 1; qindex <= hist->GetYaxis()->GetNbins(); qindex++) { + if (hist->GetBinContent(index + 1, qindex) != 0) { + q = qindex; + break; + } + } + if (q < solarQuality[index]) { + solarQuality[index] = q; + } + + bool isBad = (q == 1); + if (!isBad) { + continue; + } + + int solarId = getSolarIdFromIndex(index); + badSolarBoards.insert(solarId); + + ILOG(Debug, Devel) << "Bad SOLAR " << solarId << " found in \'" << plotPath << "\'" << ENDM; + } + } + + mHistogramQualityPerSolar->Reset(); + for (int index = 0; index < mHistogramQualityPerSolar->GetXaxis()->GetNbins(); index++) { + mHistogramQualityPerSolar->SetBinContent(index + 1, solarQuality[index], 1); + } + + std::string badSolarBoardsStr; + for (auto solarId : badSolarBoards) { + if (badSolarBoardsStr.empty()) { + badSolarBoardsStr = std::to_string(solarId); + } else { + badSolarBoardsStr += std::string(" ") + std::to_string(solarId); + } + } + if (!badSolarBoardsStr.empty()) { + std::string badSolarList = std::string("Bad SOLAR boards: ") + badSolarBoardsStr; + TPaveLabel* label = new TPaveLabel(0.2, 0.8, 0.8, 0.88, badSolarList.c_str(), "blNDC"); + label->SetBorderSize(1); + mHistogramQualityPerSolar->GetListOfFunctions()->Add(label); + + ILOG(Warning, Support) << "[QualityAggregator] " << badSolarList << ENDM; + } + + // check if the list of bad Solars has changed + bool changedSolarBoards = (!mPreviousBadSolarBoards.has_value() || mPreviousBadSolarBoards.value() != badSolarBoards); + + if (changedSolarBoards) { + std::string sBadSolars = "solarid\n"; + for (auto solarId : badSolarBoards) { + sBadSolars += fmt::format("{}\n", solarId); + } + TObjString badSolarBoardsObject(sBadSolars.c_str()); + + // time stamp + auto uploadTS = o2::ccdb::getCurrentTimestamp(); + + // CCDB object info + auto clName = o2::utils::MemFileHelper::getClassName(badSolarBoardsObject); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + constexpr auto fiveDays = 5 * o2::ccdb::CcdbObjectInfo::DAY; + auto info = o2::ccdb::CcdbObjectInfo(mObjectPathBadSOLAR, clName, flName, std::map(), uploadTS, uploadTS + fiveDays); + + // CCDB object image + auto image = o2::ccdb::CcdbApi::createObjectImage(&badSolarBoardsObject, &info); + + ILOG(Info, Devel) << "Storing object " << info.getPath() + << " of type " << info.getObjectType() + << " / " << info.getFileName() + << " and size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() + << " : " << info.getEndValidityTimestamp() << ENDM; + ILOG(Info, Devel) << badSolarBoardsObject.String().Data() << ENDM; + + int res = mAPI.storeAsBinaryFile(image->data(), image->size(), info.getFileName(), info.getObjectType(), info.getPath(), info.getMetaData(), info.getStartValidityTimestamp(), info.getEndValidityTimestamp()); + if (res) { + ILOG(Error, Ops) << fmt::format("uploading to {} / {} failed for [{}:{}]", mAPI.getURL(), info.getPath(), info.getStartValidityTimestamp(), info.getEndValidityTimestamp()) << ENDM; + } else { + ILOG(Info, Devel) << fmt::format("uploading to {} / {} succeeded for [{}:{}]", mAPI.getURL(), info.getPath(), info.getStartValidityTimestamp(), info.getEndValidityTimestamp()) << ENDM; + mPreviousBadSolarBoards = badSolarBoards; + } + } +} + +//_________________________________________________________________________________________ + +void QualityAggregatorTask::finalize(Trigger t, framework::ServiceRegistryRef) +{ +} diff --git a/Modules/MUON/MCH/src/RatesPlotter.cxx b/Modules/MUON/MCH/src/RatesPlotter.cxx index ff62b2f086..22e88a6229 100644 --- a/Modules/MUON/MCH/src/RatesPlotter.cxx +++ b/Modules/MUON/MCH/src/RatesPlotter.cxx @@ -45,7 +45,9 @@ RatesPlotter::RatesPlotter(std::string path, float rateMin, float rateMax, bool //---------------------------------- int nbins = 100; - auto xbins = makeLogBinning(rateMin / 10, rateMax * 10, nbins); + double xMax = rateMax * 10; + double xMin = (rateMin > 0) ? rateMin / 10 : xMax / 1000000; + auto xbins = makeLogBinning(xMin, xMax, nbins); std::vector ybins{ 1, 2, 3, 4, 5, 6 }; mHistogramRatePerStation = std::make_unique(TString::Format("%sRatesDistribution", path.c_str()), "Rates distribution", @@ -65,16 +67,28 @@ RatesPlotter::RatesPlotter(std::string path, float rateMin, float rateMax, bool mHistogramMeanRatePerDE = std::make_unique(TString::Format("%sMeanRate", path.c_str()), "Mean Rate", getNumDE(), 0, getNumDE()); + addDEBinLabels(mHistogramMeanRatePerDE.get()); addHisto(mHistogramMeanRatePerDE.get(), false, "histo", "logy"); + mHistogramMeanRatePerSolar = std::make_unique(TString::Format("%sMeanRatePerSolar", path.c_str()), "Mean Rate per SOLAR board", + getNumSolar(), 0, getNumSolar()); + addSolarBinLabels(mHistogramMeanRatePerSolar.get()); + addHisto(mHistogramMeanRatePerSolar.get(), false, "histo", "logy"); + //---------------------------------- // "Good" channels fraction histograms //---------------------------------- mHistogramGoodChannelsFractionPerDE = std::make_unique(TString::Format("%sGoodChannelsFraction", path.c_str()), "Good channels fraction", getNumDE(), 0, getNumDE()); + addDEBinLabels(mHistogramGoodChannelsFractionPerDE.get()); addHisto(mHistogramGoodChannelsFractionPerDE.get(), false, "histo", ""); + mHistogramGoodChannelsFractionPerSolar = std::make_unique(TString::Format("%sGoodChannelsFractionPerSolar", path.c_str()), + "Good channels fraction per SOLAR board", getNumSolar(), 0, getNumSolar()); + addSolarBinLabels(mHistogramGoodChannelsFractionPerSolar.get()); + addHisto(mHistogramGoodChannelsFractionPerSolar.get(), false, "histo", ""); + //-------------------------------------------------- // Rates histograms in global detector coordinates //-------------------------------------------------- @@ -131,6 +145,24 @@ void RatesPlotter::fillAverageHistos(TH2F* hRates) mHistogramGoodChannelsFractionPerDE->SetBinError(de + 1, 1); } } + + for (size_t solar = 0; solar < mHistogramMeanRatePerSolar->GetXaxis()->GetNbins(); solar++) { + mHistogramMeanRatePerSolar->SetBinContent(solar + 1, mElecMapReductor->getSolarValue(solar)); + mHistogramMeanRatePerSolar->SetBinError(solar + 1, 0.1); + } + + for (size_t solar = 0; solar < mHistogramGoodChannelsFractionPerSolar->GetXaxis()->GetNbins(); solar++) { + float nPads = mElecMapReductor->getSolarNumPads(solar); + float nPadsBad = mElecMapReductor->getSolarNumPadsBad(solar) + mElecMapReductor->getSolarNumPadsNoStat(solar); + float nPadsGood = nPads - nPadsBad; + if (nPads > 0) { + mHistogramGoodChannelsFractionPerSolar->SetBinContent(solar + 1, nPadsGood / nPads); + mHistogramGoodChannelsFractionPerSolar->SetBinError(solar + 1, 0.1); + } else { + mHistogramGoodChannelsFractionPerSolar->SetBinContent(solar + 1, 0); + mHistogramGoodChannelsFractionPerSolar->SetBinError(solar + 1, 1); + } + } } //_________________________________________________________________________________________ diff --git a/Modules/MUON/MCH/src/TH2ElecMapReductor.cxx b/Modules/MUON/MCH/src/TH2ElecMapReductor.cxx index 2d77c7da60..1ea30a33fb 100644 --- a/Modules/MUON/MCH/src/TH2ElecMapReductor.cxx +++ b/Modules/MUON/MCH/src/TH2ElecMapReductor.cxx @@ -107,6 +107,38 @@ int TH2ElecMapReductor::getNumPadsNoStat(int deid, int cathode) return deNumPadsNoStat[cathode][deid]; } +float TH2ElecMapReductor::getSolarValue(int solarId) +{ + if (solarId < 0 || solarId >= getNumSolar()) { + return 0; + } + return solarValues[solarId]; +} + +int TH2ElecMapReductor::getSolarNumPads(int solarId) +{ + if (solarId < 0 || solarId >= getNumSolar()) { + return 0; + } + return solarNumPads[solarId]; +} + +int TH2ElecMapReductor::getSolarNumPadsBad(int solarId) +{ + if (solarId < 0 || solarId >= getNumSolar()) { + return 0; + } + return solarNumPadsBad[solarId]; +} + +int TH2ElecMapReductor::getSolarNumPadsNoStat(int solarId) +{ + if (solarId < 0 || solarId >= getNumSolar()) { + return 0; + } + return solarNumPadsNoStat[solarId]; +} + void TH2ElecMapReductor::update(TObject* obj) { if (sDeNum != getNumDE()) { @@ -126,6 +158,19 @@ void TH2ElecMapReductor::update(TObject* obj) return; } + // cumulative numerators and denominators for the computation of + // the average value over SOLAR boards + std::vector solarValueNum(getNumSolar()); + std::vector solarValueDen(getNumSolar()); + std::fill(solarValueNum.begin(), solarValueNum.end(), 0); + std::fill(solarValueDen.begin(), solarValueDen.end(), 0); + + for (int solar = 0; solar < sSolarIndexMax; solar++) { + solarNumPads[solar] = 0; + solarNumPadsBad[solar] = 0; + solarNumPadsNoStat[solar] = 0; + } + // cumulative numerators and denominators for the computation of // the average value over detection elements and chambers std::vector deValueNum(getNumDE()); @@ -174,6 +219,17 @@ void TH2ElecMapReductor::update(TObject* obj) continue; } + auto dsElecId = mDet2ElecMapper(dsDetId); + if (!dsElecId) { + continue; + } + auto solarId = dsElecId->solarId(); + + int solarIndex = getSolarIndex(solarId); + if (solarIndex < 0) { + continue; + } + for (int j = 1; j <= nbinsy; j++) { int channel = j - 1; @@ -185,24 +241,30 @@ void TH2ElecMapReductor::update(TObject* obj) } int cathode = segment.isBendingPad(padId) ? 0 : 1; + solarNumPads[solarIndex] += 1; deNumPads[cathode][deIndex] += 1; // here we assume that if the number of bins differs between numerator and denominator, // the denominator contains a single common scaling factor in the first bin (uniform scaling) Float_t stat = (hr->getNum()->GetNcells() == hr->getDen()->GetNcells()) ? hr->getDen()->GetBinContent(i, j) : hr->getDen()->GetBinContent(1, 1); if (stat == 0) { + solarNumPadsNoStat[solarIndex] += 1; deNumPadsNoStat[cathode][deIndex] += 1; continue; } Float_t value = h->GetBinContent(i, j); if (value <= mMin || value >= mMax) { + solarNumPadsBad[solarIndex] += 1; deNumPadsBad[cathode][deIndex] += 1; } nOrbits += stat; nPads += 1; + solarValueNum[solarIndex] += value; + solarValueDen[solarIndex] += 1; + deValueNum[deIndex] += value; deValueDen[deIndex] += 1; if (cathode == 0) { @@ -222,6 +284,14 @@ void TH2ElecMapReductor::update(TObject* obj) } // update the average values + for (size_t solar = 0; solar < solarValueDen.size(); solar++) { + // integrated values + if (solarValueDen[solar] > 0) { + solarValues[solar] = solarValueNum[solar] / solarValueDen[solar]; + } else { + solarValues[solar] = 0; + } + } for (size_t de = 0; de < deValueDenB.size(); de++) { // integrated values if (deValueDenB[de] > 0) {