diff --git a/Modules/ITS/include/ITS/ITSChipStatusCheck.h b/Modules/ITS/include/ITS/ITSChipStatusCheck.h index 55ac32d5a2..82dfc21777 100644 --- a/Modules/ITS/include/ITS/ITSChipStatusCheck.h +++ b/Modules/ITS/include/ITS/ITSChipStatusCheck.h @@ -52,6 +52,7 @@ class ITSChipStatusCheck : public o2::quality_control::checker::CheckInterface static const int NLayer = 7; const int StaveBoundary[NLayer + 1] = { 0, 12, 28, 48, 72, 102, 144, 192 }; std::shared_ptr tInfo; + const int FeeIDBoundaryVsBarrel[4] = { 0, 144, 252, 432 }; }; } // namespace o2::quality_control_modules::its diff --git a/Modules/ITS/include/ITS/ITSChipStatusTask.h b/Modules/ITS/include/ITS/ITSChipStatusTask.h index a89fd94265..17e84e3037 100644 --- a/Modules/ITS/include/ITS/ITSChipStatusTask.h +++ b/Modules/ITS/include/ITS/ITSChipStatusTask.h @@ -85,10 +85,13 @@ class ITSChipStatusTask final : public TaskInterface void setAxisTitle(TH1* object, const char* xTitle, const char* yTitle); void Beautify(); void getStavePoint(int layer, int stave, double* px, double* py); + int getFEEID(int barrel, int chipinbarrel); + static constexpr int NLayer = 7; static constexpr int NLayerIB = 3; const int nHicPerStave[NLayer] = { 1, 1, 1, 8, 8, 14, 14 }; const int nChipsPerHic[NLayer] = { 9, 9, 9, 14, 14, 14, 14 }; + const int nChipsPerFeeID[3] = { 3, 56, 98 }; // index is the barrel const int nChipsPerLayer[NLayer] = { 108, 144, 180, 2688, 3360, 8232, 9408 }; const int StaveBoundary[NLayer + 1] = { 0, 12, 28, 48, 72, 102, 144, 192 }; const int ChipBoundary[NLayer + 1] = { 0, 108, 252, 432, 3120, 6480, 14712, 24120 }; @@ -105,6 +108,7 @@ class ITSChipStatusTask final : public TaskInterface int nQCCycleToMonitor = 10; TString BarrelNames[3] = { "IB", "ML", "OB" }; TH2Poly* StaveOverview; + TH1D* FeeIDOverview; int NCycleForOverview = 3; int nRotationType = 1; std::vector CurrentDeadChips[3]; // Vectors of Dead Chips in current QC cycle diff --git a/Modules/ITS/include/ITS/ITSDecodingErrorCheck.h b/Modules/ITS/include/ITS/ITSDecodingErrorCheck.h index 3cdb6c8288..0e7e267878 100644 --- a/Modules/ITS/include/ITS/ITSDecodingErrorCheck.h +++ b/Modules/ITS/include/ITS/ITSDecodingErrorCheck.h @@ -44,6 +44,7 @@ class ITSDecodingErrorCheck : public o2::quality_control::checker::CheckInterfac void beautify(std::shared_ptr mo, Quality checkResult = Quality::Null) override; std::vector vListErrorIdBad, vListErrorIdMedium; bool doFlatCheck = false; + std::map> vAlreadyCheckedFeeIDs{}; // alreadychecked[fee] = o2::itsmft::GBTLinkDecodingStat statistics; private: diff --git a/Modules/ITS/itsChipStatus.json b/Modules/ITS/itsChipStatus.json index 4c0478f43f..819ef27dcb 100644 --- a/Modules/ITS/itsChipStatus.json +++ b/Modules/ITS/itsChipStatus.json @@ -48,6 +48,12 @@ "moduleName": "QcITS", "policy": "OnEachSeparately", "detectorName": "ITS", + "checkParameters": { + "feeidlimitsIB": "1,1", + "feeidlimitsML": "1,0.87", + "feeidlimitsOL": "1,0.92", + "excludedfeeid": "" + }, "dataSource": [{ "type": "Task", "name": "ITSChipStatus", diff --git a/Modules/ITS/itsDecoding.json b/Modules/ITS/itsDecoding.json index 3b85384331..fdd222a24b 100644 --- a/Modules/ITS/itsDecoding.json +++ b/Modules/ITS/itsDecoding.json @@ -48,6 +48,7 @@ "policy": "OnEachSeparately", "detectorName": "ITS", "checkParameters": { + "checkfeeIDonlyonce": "true", "DecLinkErrorLimits": "5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -1", "DecLinkErrorLimitsRatio": "0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1", "DecLinkErrorType": "0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0", diff --git a/Modules/ITS/src/ITSChipStatusCheck.cxx b/Modules/ITS/src/ITSChipStatusCheck.cxx index b8fed881df..cc79e53695 100644 --- a/Modules/ITS/src/ITSChipStatusCheck.cxx +++ b/Modules/ITS/src/ITSChipStatusCheck.cxx @@ -29,13 +29,37 @@ namespace o2::quality_control_modules::its Quality ITSChipStatusCheck::check(std::map>* moMap) { + + // limits to be used as "X,Y" --> BAD if at least X FFEIDs have at least Y chips each into error + std::vector feeidlimitsIB = convertToArray(o2::quality_control_modules::common::getFromConfig(mCustomParameters, "feeidlimitsIB", "")); + std::vector feeidlimitsML = convertToArray(o2::quality_control_modules::common::getFromConfig(mCustomParameters, "feeidlimitsML", "")); + std::vector feeidlimitsOL = convertToArray(o2::quality_control_modules::common::getFromConfig(mCustomParameters, "feeidlimitsOL", "")); + std::vector excludedfeeid = convertToArray(o2::quality_control_modules::common::getFromConfig(mCustomParameters, "excludedfeeid", "")); + + if (feeidlimitsIB.size() != 2) { + ILOG(Error, Support) << "Incorrect setting for feeidlimitsIB, check .json. Using default 1,1" << ENDM; + feeidlimitsIB.clear(); + feeidlimitsIB = std::vector{ 1, 1. }; + } + if (feeidlimitsML.size() != 2) { + ILOG(Error, Support) << "Incorrect setting for feeidlimitsML, check .json. Using default 1,1" << ENDM; + feeidlimitsML.clear(); + feeidlimitsML = std::vector{ 1, 1. }; + } + if (feeidlimitsOL.size() != 2) { + ILOG(Error, Support) << "Incorrect setting for feeidlimitsOL, check .json. Using default 1,1" << ENDM; + feeidlimitsOL.clear(); + feeidlimitsOL = std::vector{ 1, 1. }; + } + Quality result = Quality::Null; for (auto& [moName, mo] : *moMap) { + if (mo->getName() == "StaveStatusOverview") { result = Quality::Good; auto* h = dynamic_cast(mo->getObject()); if (h == nullptr) { - ILOG(Error, Support) << "could not cast ChipError plots to TH2Poly" << ENDM; + ILOG(Error, Support) << "could not cast StaveStatusOverview plot from ChipError to TH2Poly" << ENDM; continue; } for (int ilayer = 0; ilayer < NLayer; ilayer++) { @@ -49,6 +73,41 @@ Quality ITSChipStatusCheck::check(std::mapgetName() == "FEEIDOverview") { + result = Quality::Good; + auto* h = dynamic_cast(mo->getObject()); + if (h == nullptr) { + ILOG(Error, Support) << "could not cast FEEIDoverview plot from ChipStatus to TH1D" << ENDM; + continue; + } + + int nBadIB = 0; + int nBadML = 0; + int nBadOL = 0; + for (int ifee = 0; ifee < h->GetNbinsX(); ifee++) { + + if (excludedfeeid.size() > 0 && std::find(excludedfeeid.begin(), excludedfeeid.end(), ifee) != excludedfeeid.end()) { + continue; + } + + if (ifee >= FeeIDBoundaryVsBarrel[0] && ifee < FeeIDBoundaryVsBarrel[1] && h->GetBinContent(ifee + 1) >= feeidlimitsIB[1]) { + nBadIB++; + } + if (ifee >= FeeIDBoundaryVsBarrel[1] && ifee < FeeIDBoundaryVsBarrel[2] && h->GetBinContent(ifee + 1) >= feeidlimitsML[1]) { + nBadML++; + } + if (ifee >= FeeIDBoundaryVsBarrel[2] && ifee < FeeIDBoundaryVsBarrel[3] && h->GetBinContent(ifee + 1) >= feeidlimitsOL[1]) { + nBadOL++; + } + } + + if (nBadIB >= feeidlimitsIB[0] || nBadML >= feeidlimitsML[0] || nBadOL >= feeidlimitsOL[0]) { + result = Quality::Bad; + TString text = "At least one FEEid with large number of missing chips"; + result.addFlag(o2::quality_control::FlagTypeFactory::Unknown(), text.Data()); + } + } } return result; } @@ -57,10 +116,34 @@ void ITSChipStatusCheck::beautify(std::shared_ptr mo, Quality che { TString status; int textColor; - if ((mo->getName() == "StaveStatusOverview")) { + + if ((string)mo->GetName() == "FEEIDOverview") { + auto* h = dynamic_cast(mo->getObject()); + if (h == nullptr) { + ILOG(Error, Support) << "could not cast FEEIDOverview to TH1D*" << ENDM; + return; + } + + if (checkResult == Quality::Good) { + status = "Quality::GOOD"; + textColor = kGreen; + } else if (checkResult == Quality::Bad) { + status = "Quality::BAD (call expert)"; + textColor = kRed + 2; + } + + tInfo = std::make_shared(0.05, 0.95, Form("#bf{%s}", status.Data())); + tInfo->SetTextColor(textColor); + tInfo->SetTextSize(0.06); + tInfo->SetTextFont(43); + tInfo->SetNDC(); + h->GetListOfFunctions()->Add(tInfo->Clone()); + } + + if ((string)mo->getName() == "StaveStatusOverview") { auto* h = dynamic_cast(mo->getObject()); if (h == nullptr) { - ILOG(Error, Support) << "could not cast StatusOverview to TH2Poly*" << ENDM; + ILOG(Error, Support) << "could not cast StaveStatusOverview to TH2Poly*" << ENDM; return; } if (checkResult == Quality::Good) { diff --git a/Modules/ITS/src/ITSChipStatusTask.cxx b/Modules/ITS/src/ITSChipStatusTask.cxx index a9d51dd62a..e825e66266 100644 --- a/Modules/ITS/src/ITSChipStatusTask.cxx +++ b/Modules/ITS/src/ITSChipStatusTask.cxx @@ -77,6 +77,9 @@ void ITSChipStatusTask::initialize(o2::framework::InitContext& /*ctx*/) } } getObjectsManager()->startPublishing(StaveOverview); + + FeeIDOverview = new TH1D("FEEIDOverview", Form("Fraction of missing chips in the last %d cycles;QC FEEid;fraction of missing chips", NCycleForOverview), NFees, 0, NFees); + getObjectsManager()->startPublishing(FeeIDOverview); } void ITSChipStatusTask::setAxisTitle(TH1* object, const char* xTitle, const char* yTitle) @@ -131,6 +134,22 @@ void ITSChipStatusTask::getStavePoint(int layer, int stave, double* px, double* } } +int ITSChipStatusTask::getFEEID(int barrel, int chipinbarrel) +{ + if (barrel == 0) { + return chipinbarrel / nChipsPerFeeID[0]; + } + if (barrel == 1) { + return ChipsBoundaryBarrels[1] / nChipsPerFeeID[0] + chipinbarrel / nChipsPerFeeID[1]; + } + if (barrel == 2) { + return (ChipsBoundaryBarrels[1] / nChipsPerFeeID[0]) + + ((ChipsBoundaryBarrels[2] - ChipsBoundaryBarrels[1]) / nChipsPerFeeID[1]) + + chipinbarrel / nChipsPerFeeID[2]; + } + return -1; +} + void ITSChipStatusTask::monitorData(o2::framework::ProcessingContext& ctx) { auto aliveChips = ctx.inputs().get>("chipstatus"); @@ -179,6 +198,21 @@ void ITSChipStatusTask::endOfCycle() Beautify(); //---------- Shifter plot + FeeIDOverview->Reset(); + for (int iBarrel = 0; iBarrel < 3; iBarrel++) { + TH1D* hBarProj = DeadChips[iBarrel]->getNum()->ProjectionY("temp", TMath::Max(1, nQCCycleToMonitor - NCycleForOverview + 1), nQCCycleToMonitor); + + for (int ic = 0; ic < hBarProj->GetNbinsX(); ic++) { + if (hBarProj->GetBinContent(ic + 1) < NCycleForOverview) { // report only chips dead for N consecutive cycles + continue; + } + FeeIDOverview->Fill(getFEEID(iBarrel, ic), 1. / nChipsPerFeeID[iBarrel]); + } + } + FeeIDOverview->Sumw2(kFALSE); + FeeIDOverview->SetMinimum(0); + FeeIDOverview->SetMaximum(1); + int iLayer = 0; int iStave = 0; StaveOverview->Reset("content"); @@ -190,11 +224,16 @@ void ITSChipStatusTask::endOfCycle() TH1D* hBarrelProj = DeadChips[iBarrel]->getNum()->ProjectionY("dummy", nQCCycleToMonitor - iSlice, nQCCycleToMonitor - iSlice); iStave = 0; int nEmptyChips = 0; + for (int iChip = 1; iChip <= hBarrelProj->GetNbinsX(); iChip++) { + if (hBarrelProj->GetBinContent(iChip) > 0) nEmptyChips++; - if (((iChip) % (nChipsPerHic[iLayer] * nHicPerStave[iLayer]) == 0)) { - if (nEmptyChips == nChipsPerHic[iLayer] * nHicPerStave[iLayer]) { + + if ((iChip) % (nChipsPerHic[iLayer] * nHicPerStave[iLayer]) == 0) { + + if (nEmptyChips == nChipsPerHic[iLayer] * nHicPerStave[iLayer]) { // all the chips of the stave are dead + int binContent = StaveOverview->GetBinContent(iStave + StaveBoundary[iLayer]) * NCycleForOverview + 1; StaveOverview->SetBinContent(iStave + StaveBoundary[iLayer], 1. * binContent / NCycleForOverview); } @@ -204,7 +243,8 @@ void ITSChipStatusTask::endOfCycle() iLayer++; iStave = 0; } - } + + } // end of ((iChip) % (nChipsPerHic[iLayer] * nHicPerStave[iLayer]) == 0) } } } diff --git a/Modules/ITS/src/ITSDecodingErrorCheck.cxx b/Modules/ITS/src/ITSDecodingErrorCheck.cxx index b0885a2cdd..41a7ef16c9 100644 --- a/Modules/ITS/src/ITSDecodingErrorCheck.cxx +++ b/Modules/ITS/src/ITSDecodingErrorCheck.cxx @@ -53,10 +53,13 @@ Quality ITSDecodingErrorCheck::check(std::map(mCustomParameters, "checkfeeIDonlyonce", ""); + Quality result = Quality::Null; for (auto& [moName, mo] : *moMap) { (void)moName; - if (mo->getName() == "General/ChipErrorPlots") { + + if ((string)mo->getName() == "General/ChipErrorPlots") { result = Quality::Good; auto* h = dynamic_cast(mo->getObject()); if (h == nullptr) { @@ -67,6 +70,45 @@ Quality ITSDecodingErrorCheck::check(std::mapGetName() == "General/LinkErrorVsFeeid") { + + result = Quality::Good; + auto* h = dynamic_cast(mo->getObject()); + if (h == nullptr) { + ILOG(Error, Support) << "could not cast LinkErrorVsFeeid plot to TH2D*" << ENDM; + continue; + } + for (int ifee = 0; ifee < h->GetNbinsX(); ifee++) { + for (int ierr = 0; ierr < h->GetNbinsY() - 1; ierr++) { // last y bin is recovery flag: do not check + + if ((doFlatCheck && h->GetBinContent(ifee, ierr + 1) == 0) || (!doFlatCheck && h->GetBinContent(ifee + 1, ierr + 1) < vDecErrorLimits[ierr])) { // ok if below threshold + continue; + } + + if (vAlreadyCheckedFeeIDs.count(ifee) > 0) { + std::vector flagskip = vAlreadyCheckedFeeIDs[ifee]; + if (std::find(flagskip.begin(), flagskip.end(), ierr) != flagskip.end()) { + continue; // bin to be skipped + } + } + + // if here, quality is BAD + + if (checkFeeIDOnce) { // gives instruction to exclude chech on this bin from now on + if (vAlreadyCheckedFeeIDs.count(ifee) > 0) { + vAlreadyCheckedFeeIDs[ifee].push_back(ierr); + } else { + vAlreadyCheckedFeeIDs[ifee] = std::vector{ ierr }; + } + } + + result.set(Quality::Bad); + result.addFlag(o2::quality_control::FlagTypeFactory::Unknown(), Form("BAD: ID = %d, %s", ierr, std::string(statistics.ErrNames[ierr]).c_str())); + + } // end of y axis loop + } // end of x axis loop + } // end of check on General/LinkErrorVsFeeid + if (((string)mo->getName()).find("General/LinkErrorPlots") != std::string::npos) { result = Quality::Good; auto* h = dynamic_cast(mo->getObject()); @@ -137,6 +179,31 @@ void ITSDecodingErrorCheck::beautify(std::shared_ptr mo, Quality TString status; int textColor; + + if (((string)mo->GetName()).find("General/LinkErrorVsFeeid") != std::string::npos) { + + auto* h = dynamic_cast(mo->getObject()); + if (h == nullptr) { + ILOG(Error, Support) << "could not cast LinkErrorVsFeeid plot to TH2D*" << ENDM; + return; + } + if (checkResult == Quality::Good) { + status = "Quality::GOOD"; + textColor = kGreen; + } else if (checkResult == Quality::Bad) { + status = "Quality::BAD (call expert)"; + textColor = kRed + 2; + } + + tInfo = std::make_shared(0.05, 0.95, Form("#bf{%s}", status.Data())); + tInfo->SetTextColor(textColor); + tInfo->SetTextSize(0.06); + tInfo->SetTextFont(43); + tInfo->SetNDC(); + h->GetListOfFunctions()->Add(tInfo->Clone()); + + } // end of beautify LinkErrorVsFeeid + if ((((string)mo->getName()).find("General/LinkErrorPlots") != std::string::npos) || (mo->getName() == "General/ChipErrorPlots")) { auto* h = dynamic_cast(mo->getObject()); if (h == nullptr) {