From c4af53763ad85c3ea75fb10e0aba07c733928392 Mon Sep 17 00:00:00 2001 From: "s.zharko@gsi.de" <s.zharko@gsi.de> Date: Tue, 4 Jun 2024 16:38:09 +0200 Subject: [PATCH] Offline QA: 1) possibility for giving a supporting message to the checked criteria, if they are not passed 2) cleaning-up of the CA input QA --- core/qa/CbmQaManager.cxx | 2 +- core/qa/CbmQaTable.cxx | 13 +- core/qa/CbmQaTable.h | 5 +- core/qa/CbmQaTask.cxx | 5 +- core/qa/CbmQaTask.h | 8 +- ...ks_config_mcbm_beam_2022_05_23_nickel.yaml | 5 +- reco/L1/qa/CbmCaInputQaBase.cxx | 123 +++++++++++++++--- reco/L1/qa/CbmCaInputQaBase.h | 7 +- reco/L1/qa/CbmCaInputQaSts.cxx | 6 +- 9 files changed, 135 insertions(+), 39 deletions(-) diff --git a/core/qa/CbmQaManager.cxx b/core/qa/CbmQaManager.cxx index 38de139993..0af606045d 100644 --- a/core/qa/CbmQaManager.cxx +++ b/core/qa/CbmQaManager.cxx @@ -48,7 +48,7 @@ void CbmQaManager::Finish() for (const auto& [entryName, flags] : mCheckList) { LOG(info) << '\t' << left << setw(40) << entryName << right << setw(10) << (flags.fResult ? "\e[1;32mpassed\e[0m" : "\e[1;31mfailed\e[0m") - << (flags.fStatus ? "" : " IGNORED"); + << (flags.fStatus ? " " : " IGNORED ") << flags.fMsg; if (flags.fStatus) { fStatus &= flags.fResult; } diff --git a/core/qa/CbmQaTable.cxx b/core/qa/CbmQaTable.cxx index 78c4d7e3a5..578384a459 100644 --- a/core/qa/CbmQaTable.cxx +++ b/core/qa/CbmQaTable.cxx @@ -95,7 +95,7 @@ void CbmQaTable::SetRowName(Int_t iRow, const char* name) { TH2D::GetYaxis()->Se // //------------------------------------------------------------------------------------------------------------------------ // -std::string CbmQaTable::ToString(int prec) const +std::string CbmQaTable::ToString(int prec, bool drawErr) const { std::stringstream aStream; aStream.setf(std::ios::fixed); @@ -124,7 +124,16 @@ std::string CbmQaTable::ToString(int prec) const aStream << std::setw(fColWidth + 10) << std::setfill(' ') << entry << ' '; for (Int_t iCol = 0; iCol < fNcols; ++iCol) { - aStream << std::setw(fColWidth) << std::setfill(' ') << GetCell(iRow, iCol) << ' '; + std::stringstream cell; + cell.setf(std::ios::fixed); + cell.setf(std::ios::showpoint); + cell.setf(std::ios::left); + cell.precision(prec); + cell << GetCell(iRow, iCol); + if (drawErr) { + cell << "+/-" << GetCellError(iRow, iCol); + } + aStream << std::setw(fColWidth) << std::setfill(' ') << cell.str() << ' '; } aStream << '\n'; } diff --git a/core/qa/CbmQaTable.h b/core/qa/CbmQaTable.h index 3f17907761..212ba5155f 100644 --- a/core/qa/CbmQaTable.h +++ b/core/qa/CbmQaTable.h @@ -33,8 +33,9 @@ class CbmQaTable : public TH2D { virtual ~CbmQaTable(); /// Dumps table content into a string - /// \param prec Precision of numbers - std::string ToString(int prec) const; + /// \param prec Precision of numbers + /// \param useErr If true, the errors will be drawed together with the central values + std::string ToString(int prec, bool useErr = false) const; /// Dumps table content into a text file. File open mode is also controllable, for example, use /// mode = std::ios_base::app to append the table into an existing file diff --git a/core/qa/CbmQaTask.cxx b/core/qa/CbmQaTask.cxx index 471b070a8d..cd5058638d 100644 --- a/core/qa/CbmQaTask.cxx +++ b/core/qa/CbmQaTask.cxx @@ -308,4 +308,7 @@ void CbmQaTask::ReadCheckListFromConfig() // --------------------------------------------------------------------------------------------------------------------- // -void CbmQaTask::StoreCheckResult(const std::string& tag, bool result) { fmCheckList[tag] = CheckFlags{result, false}; } +void CbmQaTask::StoreCheckResult(const std::string& tag, bool result, const std::string& msg) +{ + fmCheckList[tag] = CheckFlags{msg, result, false}; +} diff --git a/core/qa/CbmQaTask.h b/core/qa/CbmQaTask.h index 6f2e6a8f7f..a2534dd2e5 100644 --- a/core/qa/CbmQaTask.h +++ b/core/qa/CbmQaTask.h @@ -49,8 +49,9 @@ class CbmQaTask : public FairTask, public CbmQaIO { /// /// If the status is set to false, the check result is ignored in the QA verdict. struct CheckFlags { - bool fResult = false; ///< Check result storage - bool fStatus = false; ///< Status of the check + std::string fMsg = ""; ///< Supporting message for the check + bool fResult = false; ///< Check result storage + bool fStatus = false; ///< Status of the check }; /// \struct ObjectComparisonConfig @@ -217,7 +218,8 @@ class CbmQaTask : public FairTask, public CbmQaIO { /// \brief Stores check flag to the check-list /// \param tag The flag name /// \param result Check result - void StoreCheckResult(const std::string& tag, bool result); + /// \param msg Supporting message (optional) + void StoreCheckResult(const std::string& tag, bool result, const std::string& msg = ""); private: /// \brief De-initializes this task diff --git a/macro/qa/configs/qa_tasks_config_mcbm_beam_2022_05_23_nickel.yaml b/macro/qa/configs/qa_tasks_config_mcbm_beam_2022_05_23_nickel.yaml index 1b1efd2be2..d71593d6cc 100644 --- a/macro/qa/configs/qa_tasks_config_mcbm_beam_2022_05_23_nickel.yaml +++ b/macro/qa/configs/qa_tasks_config_mcbm_beam_2022_05_23_nickel.yaml @@ -100,7 +100,7 @@ qa: pull_t_station_0: false pull_x_station_%d: true pull_y_station_%d: true - pull_t_station_%d: true + pull_t_station_%d: false # Time is not calibrated properly # # CA INPUT QA: TOF # @@ -114,9 +114,6 @@ qa: pull_t_station_%d: false pull_t_station_3: false CbmCaOutputQa: - specific: - testA: 42 - testB: "hello" check_histograms: - name: "all/eff_pMC" point_to_point: { use: 1 } diff --git a/reco/L1/qa/CbmCaInputQaBase.cxx b/reco/L1/qa/CbmCaInputQaBase.cxx index 5cf956ed95..e06182e6c1 100644 --- a/reco/L1/qa/CbmCaInputQaBase.cxx +++ b/reco/L1/qa/CbmCaInputQaBase.cxx @@ -76,6 +76,8 @@ CbmCaInputQaBase<DetID>::CbmCaInputQaBase(const char* name, int verbose, bool is template<ca::EDetectorID DetID> void CbmCaInputQaBase<DetID>::Check() { + LOG(info) << "\n\n ** Checking the " << fName << " **\n\n"; + int nSt = fpDetInterface->GetNtrackingStations(); // ************************************************************** @@ -134,6 +136,8 @@ void CbmCaInputQaBase<DetID>::Check() // { bool res = true; + std::stringstream msgs; + msgs.precision(3); for (int iSt = 0; iSt < nSt; ++iSt) { int nHits = fvph_hit_station_delta_z[iSt]->GetEntries(); if (!nHits) { @@ -144,12 +148,20 @@ void CbmCaInputQaBase<DetID>::Check() int iBinMin = fvph_hit_station_delta_z[iSt]->FindBin(-fConfig.fMaxDiffZStHit); int iBinMax = fvph_hit_station_delta_z[iSt]->FindBin(+fConfig.fMaxDiffZStHit); - if (fvph_hit_station_delta_z[iSt]->Integral(iBinMin, iBinMax) < nHits) { - LOG_IF(error, fVerbose > 0) << fName << ": station " << iSt << " has mismatches in hit z-positions"; + auto nHitsWithin = fvph_hit_station_delta_z[iSt]->Integral(iBinMin, iBinMax); + if (nHitsWithin < nHits) { + if (!msgs.str().empty()) { + msgs << ", "; + } + msgs << (static_cast<double>((nHits - nHitsWithin) * 100) / nHits) << "% (st. " << iSt << ")"; res = false; } } - StoreCheckResult("station_position_hit_delta_z", res); + std::string msg = msgs.str().empty() ? "" : Form("Out of range z = +-%.2f cm: ", fConfig.fMaxDiffZStHit); + if (!msg.empty()) { + msg += msgs.str(); + } + StoreCheckResult("station_position_hit_delta_z", res, msg); } // ******************************************************* @@ -175,7 +187,8 @@ void CbmCaInputQaBase<DetID>::Check() pEffTable->SetRowName(iSt, Form("station %d", iSt)); pEffTable->SetCell(iSt, 0, eff); bool res = CheckRange("Hit finder efficiency in station " + std::to_string(iSt), eff, fConfig.fEffThrsh, 1.000); - StoreCheckResult(Form("hit_efficiency_station_%d", iSt), res); + std::string msg = (res ? "" : Form("efficiency = %f lower then threshold = %f", eff, fConfig.fEffThrsh)); + StoreCheckResult(Form("hit_efficiency_station_%d", iSt), res, msg); } LOG(info) << '\n' << pEffTable->ToString(3); } @@ -210,31 +223,53 @@ void CbmCaInputQaBase<DetID>::Check() // to unity. Here we allow a RMS of the pull distributions to be varied in a predefined range. If the RMS runs out // this range, QA task fails. { - auto* pPullsTable = - MakeQaObject<CbmQaTable>("vs Station/pulls_rms", "Pulls std. dev. values in different stations", nSt, 3); - pPullsTable->SetNamesOfCols({"Pull(x) sigm", "Pull(y) sigm", "Pull(t) sigm"}); - pPullsTable->SetColWidth(20); + std::vector<CbmQaTable*> vpPullTables = { + // {x, y, t} + MakeQaObject<CbmQaTable>("vs Station/pulls_x", "x-coordinate pull quantities in diff. stations", nSt, 2), + MakeQaObject<CbmQaTable>("vs Station/pulls_y", "y-coordinate pull quantities in diff. stations", nSt, 2), + MakeQaObject<CbmQaTable>("vs Station/pulls_t", "Time pull quantities in diff. stations", nSt, 2)}; + + for (auto* pullTable : vpPullTables) { + pullTable->SetNamesOfCols({"mean", "std.dev."}); + pullTable->SetColWidth(20); + } - for (int iSt = 0; iSt < nSt + 1; ++iSt) { + // NOTE: The table format is assumed: mean | 3.5 * mean err. | sigm | 3.5 * sigm err. | + auto DefinePullTableRow = [&](const TH1* h, CbmQaTable* table, int iSt) { + table->SetCell(iSt, 0, h->GetMean(), 3.5 * h->GetMeanError()); + table->SetCell(iSt, 1, h->GetStdDev(), 3.5 * h->GetStdDevError()); + }; + for (int iSt = 0; iSt < nSt + 1; ++iSt) { // Fit pull distributions for nicer representation. Fit results are not used in further checks. - cbm::qa::util::SetLargeStats(fvph_pull_x[iSt]); cbm::qa::util::SetLargeStats(fvph_pull_y[iSt]); cbm::qa::util::SetLargeStats(fvph_pull_t[iSt]); + for (auto* pullTable : vpPullTables) { + pullTable->SetRowName(iSt, (iSt == nSt ? "all stations" : Form("station %u", iSt))); + } // Check the pull quality - StoreCheckResult(Form("pull_x_station_%d", iSt), CheckRangePull(fvph_pull_x[iSt])); - StoreCheckResult(Form("pull_y_station_%d", iSt), CheckRangePull(fvph_pull_y[iSt])); - StoreCheckResult(Form("pull_t_station_%d", iSt), CheckRangePull(fvph_pull_t[iSt])); - - pPullsTable->SetRowName(iSt, Form("station %d", iSt)); - pPullsTable->SetCell(iSt, 0, fvph_pull_x[iSt]->GetStdDev()); - pPullsTable->SetCell(iSt, 1, fvph_pull_y[iSt]->GetStdDev()); - pPullsTable->SetCell(iSt, 2, fvph_pull_t[iSt]->GetStdDev()); + { + auto [msg, status] = CheckRangePull(fvph_pull_x[iSt]); + StoreCheckResult(Form("pull_x_station_%d", iSt), status, msg); + DefinePullTableRow(fvph_pull_x[iSt], vpPullTables[0], iSt); + } + { + auto [msg, status] = CheckRangePull(fvph_pull_y[iSt]); + StoreCheckResult(Form("pull_y_station_%d", iSt), status, msg); + DefinePullTableRow(fvph_pull_y[iSt], vpPullTables[1], iSt); + } + { + auto [msg, status] = CheckRangePull(fvph_pull_t[iSt]); + StoreCheckResult(Form("pull_t_station_%d", iSt), status, msg); + DefinePullTableRow(fvph_pull_t[iSt], vpPullTables[2], iSt); + } } - LOG(info) << '\n' << pPullsTable->ToString(3); + for (auto* pullTable : vpPullTables) { + LOG(info) << '\n' << pullTable->ToString(3, true); + } } } // McUsed @@ -242,6 +277,56 @@ void CbmCaInputQaBase<DetID>::Check() LOG(info) << fMonitor.ToString(); } +// --------------------------------------------------------------------------------------------------------------------- +// +template<ca::EDetectorID DetID> +std::pair<std::string, bool> CbmCaInputQaBase<DetID>::CheckRangePull(TH1* h) const +{ + constexpr double factor = 3.5; + // Function to check overflow and underflow + constexpr auto Check = [&](double val, double err, double min, double max) -> std::pair<std::string, bool> { + std::stringstream msg; + bool res; + if (val + factor * err < min) { + msg << "underflow: " << val << " < " << min << " - " << factor << " x " << err; + res = false; + } + if (val - factor * err > max) { + msg << "overflow: " << val << " > " << max << " + " << factor << " x " << err; + res = false; + } + return std::make_pair(msg.str(), res); + }; + + std::pair<std::string, bool> ret = {"", true}; + if (h->GetEntries() >= 10) { + // Mean check + { + auto [msg, res] = Check(h->GetMean(), h->GetMeanError(), -fConfig.fPullMeanThrsh, +fConfig.fPullMeanThrsh); + if (!msg.empty()) { + ret.first += "mean "; + ret.first += msg; + ret.second = res; + } + } + // Sigma check + { + auto [msg, res] = + Check(h->GetStdDev(), h->GetStdDevError(), 1. - fConfig.fPullWidthThrsh, 1. + fConfig.fPullWidthThrsh); + if (!msg.empty()) { + if (!ret.first.empty()) { + ret.first += "; "; + } + ret.first += "std.dev. "; + ret.first += msg; + ret.second = res; + } + } + } + return ret; +} + + // --------------------------------------------------------------------------------------------------------------------- // template<ca::EDetectorID DetID> diff --git a/reco/L1/qa/CbmCaInputQaBase.h b/reco/L1/qa/CbmCaInputQaBase.h index 46ee3b7bcf..4fe628abc9 100644 --- a/reco/L1/qa/CbmCaInputQaBase.h +++ b/reco/L1/qa/CbmCaInputQaBase.h @@ -130,11 +130,8 @@ class CbmCaInputQaBase : public CbmQaTask { InitStatus InitQa() override; /// \brief Checks ranges for mean and standard deviation - /// \return False, if variable exits the range - bool CheckRangePull(TH1* h) - { - return CbmQaTask::CheckRange(h, fConfig.fPullMeanThrsh, 1. - fConfig.fPullWidthThrsh, 1. + fConfig.fPullWidthThrsh); - } + /// \return String with an error message: empty string is equivalent to success + std::pair<std::string, bool> CheckRangePull(TH1* h) const; /// \brief Returns a pointer to current hit QA data object /// diff --git a/reco/L1/qa/CbmCaInputQaSts.cxx b/reco/L1/qa/CbmCaInputQaSts.cxx index c8bb59e5b6..4d8e2dfe19 100644 --- a/reco/L1/qa/CbmCaInputQaSts.cxx +++ b/reco/L1/qa/CbmCaInputQaSts.cxx @@ -61,10 +61,12 @@ void CbmCaInputQaSts::Check() if (IsMCUsed()) { for (int idig = 0; idig <= fkMaxDigisInClusterForPulls; idig++) { cbm::qa::util::SetLargeStats(fvph_pull_u_Ndig[idig]); - StoreCheckResult(Form("pull_u_%d_digis", idig), CheckRangePull(fvph_pull_u_Ndig[idig])); + auto [msgU, resU] = CheckRangePull(fvph_pull_u_Ndig[idig]); + StoreCheckResult(Form("pull_u_%d_digis", idig), resU, msgU); cbm::qa::util::SetLargeStats(fvph_pull_v_Ndig[idig]); - StoreCheckResult(Form("pull_v_%d_digis", idig), CheckRangePull(fvph_pull_v_Ndig[idig])); + auto [msgV, resV] = CheckRangePull(fvph_pull_v_Ndig[idig]); + StoreCheckResult(Form("pull_v_%d_digis", idig), resV, msgV); } } // McUsed } -- GitLab