From 63866f6530459d40e88fe15e770c33513735015e Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Wed, 2 Jul 2025 14:40:02 +0200 Subject: [PATCH 01/20] feat: add extra amplitude graphs --- Modules/FIT/FV0/include/FV0/DigitQcTask.h | 2 ++ Modules/FIT/FV0/src/DigitQcTask.cxx | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Modules/FIT/FV0/include/FV0/DigitQcTask.h b/Modules/FIT/FV0/include/FV0/DigitQcTask.h index 091c78ec72..acd58e097d 100644 --- a/Modules/FIT/FV0/include/FV0/DigitQcTask.h +++ b/Modules/FIT/FV0/include/FV0/DigitQcTask.h @@ -157,6 +157,8 @@ class DigitQcTask final : public TaskInterface // Objects which will be published std::unique_ptr mHistAmp2Ch; + std::unique_ptr mHistAmpAll; + std::array, sNCHANNELS_FV0_PLUSREF> mHistAmpPerCh; std::unique_ptr mHistTime2Ch; std::unique_ptr mHistEventDensity2Ch; std::unique_ptr mHistChDataBits; diff --git a/Modules/FIT/FV0/src/DigitQcTask.cxx b/Modules/FIT/FV0/src/DigitQcTask.cxx index 29904f1071..eca13ec356 100644 --- a/Modules/FIT/FV0/src/DigitQcTask.cxx +++ b/Modules/FIT/FV0/src/DigitQcTask.cxx @@ -187,6 +187,18 @@ void DigitQcTask::initialize(o2::framework::InitContext& /*ctx*/) mHistTime2Ch->SetOption("colz"); mHistAmp2Ch = std::make_unique("AmpPerChannel", "Amplitude vs Channel;Channel;Amp", sNCHANNELS_FV0_PLUSREF, 0, sNCHANNELS_FV0_PLUSREF, 4200, -100, 4100); mHistAmp2Ch->SetOption("colz"); + mHistAmpAll = std::make_unique( + "AmpDistributionAll", + "Amplitude distribution;QTC amp;Counts", + 4096, -0.5, 4095.5); + for (std::size_t ch = 0; ch < sNCHANNELS_FV0_PLUSREF; ++ch) { + mHistAmpPerCh[ch] = std::make_unique( + Form("AmpDistCh%zu", ch), + Form("Amplitude distribution channel %zu;QTC amp;Counts", ch), + 4096, -0.5, 4095.5); + getObjectsManager()->startPublishing(mHistAmpPerCh[ch].get()); + } + getObjectsManager()->startPublishing(mHistAmpAll.get()); mHistBC = std::make_unique("BC", "BC;BC;counts;", sBCperOrbit, 0, sBCperOrbit); mHistChDataBits = std::make_unique("ChannelDataBits", "ChannelData bits per ChannelID;Channel;Bit", sNCHANNELS_FV0_PLUSREF, 0, sNCHANNELS_FV0_PLUSREF, mMapPMbits.size(), 0, mMapPMbits.size()); mHistChDataBits->SetOption("colz"); @@ -384,6 +396,10 @@ void DigitQcTask::startOfActivity(const Activity& activity) ILOG(Debug, Devel) << "startOfActivity" << activity.mId << ENDM; mHistTime2Ch->Reset(); mHistAmp2Ch->Reset(); + mHistAmpAll->Reset(); + for (auto& h : mHistAmpPerCh) { + if (h) h->Reset(); + } mHistBC->Reset(); mHistChDataBits->Reset(); mHistGateTimeRatio2Ch->Reset(); @@ -483,6 +499,8 @@ void DigitQcTask::monitorData(o2::framework::ProcessingContext& ctx) for (const auto& chData : vecChData) { mHistTime2Ch->Fill(static_cast(chData.ChId), static_cast(chData.CFDTime)); mHistAmp2Ch->Fill(static_cast(chData.ChId), static_cast(chData.QTCAmpl)); + mHistAmpAll->Fill(static_cast(chData.QTCAmpl)); + mHistAmpPerCh[chData.ChId]->Fill(static_cast(chData.QTCAmpl)); mHistEventDensity2Ch->Fill(static_cast(chData.ChId), static_cast(digit.mIntRecord.differenceInBC(mStateLastIR2Ch[chData.ChId]))); mStateLastIR2Ch[chData.ChId] = digit.mIntRecord; mHistChannelID->Fill(chData.ChId); @@ -674,6 +692,10 @@ void DigitQcTask::reset() mHistGateTimeRatio2Ch->Reset(); mHistTime2Ch->Reset(); mHistAmp2Ch->Reset(); + mHistAmpAll->Reset(); + for (auto& h : mHistAmpPerCh) { + if (h) h->Reset(); + } mHistBC->Reset(); mHistChDataBits->Reset(); mHistCFDEff->Reset(); From 0c1bbd87df08a0c51255b9e23b04e450c3323b78 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Sat, 2 Aug 2025 15:09:35 +0200 Subject: [PATCH 02/20] feat: better customization option for trending --- .../QualityControl/TrendingTaskConfig.h | 21 +++ Framework/src/TrendingTask.cxx | 177 +++++++++++++----- Framework/src/TrendingTaskConfig.cxx | 32 +++- 3 files changed, 179 insertions(+), 51 deletions(-) diff --git a/Framework/include/QualityControl/TrendingTaskConfig.h b/Framework/include/QualityControl/TrendingTaskConfig.h index 939de60b5d..fbf4a98b48 100644 --- a/Framework/include/QualityControl/TrendingTaskConfig.h +++ b/Framework/include/QualityControl/TrendingTaskConfig.h @@ -32,6 +32,18 @@ struct TrendingTaskConfig : PostProcessingConfig { TrendingTaskConfig(std::string name, const boost::property_tree::ptree& config); ~TrendingTaskConfig() = default; + // graph style configuration + struct GraphStyle { + int lineColor = -1; + int lineStyle = -1; + int lineWidth = -1; + int markerColor = -1; + int markerStyle = -1; + float markerSize = -1.f; + int fillColor = -1; + int fillStyle = -1; + }; + // this corresponds to one TTree::Draw() call, i.e. one graph or histogram drawing struct Graph { std::string name; @@ -40,6 +52,14 @@ struct TrendingTaskConfig : PostProcessingConfig { std::string selection; std::string option; // the list of possible options are documented in TGraphPainter and THistPainter std::string errors; + GraphStyle style; + }; + + // legend configuration + struct LegendConfig { + bool enabled{false}; + int nColumns{1}; + float x1{0.30f}, y1{0.20f}, x2{0.55f}, y2{0.35f}; // NDC coords }; // this corresponds to one canvas which can include multiple graphs @@ -49,6 +69,7 @@ struct TrendingTaskConfig : PostProcessingConfig { std::string graphAxisLabel; std::string graphYRange; int colorPalette = 0; + LegendConfig legend; std::vector graphs; }; diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index 1f6ed5accf..f91d3cb427 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -142,6 +142,29 @@ void TrendingTask::initializeTrend(o2::quality_control::repository::DatabaseInte } } +static inline bool hasAnyStyle(const TrendingTaskConfig::GraphStyle& s) { + return s.lineColor >= 0 || s.lineStyle >= 0 || s.lineWidth >= 0 || + s.markerColor >= 0 || s.markerStyle >= 0 || s.markerSize > 0.f || + s.fillColor >= 0 || s.fillStyle >= 0; +} + +template // TGraph*, TH1*, etc. (anything with TAttLine/TAttMarker/TAttFill) +static inline void applyStyleIfAny(T* obj, const TrendingTaskConfig::GraphStyle& s) { + if (!hasAnyStyle(s) || !obj) return; + + // Colors + if (s.lineColor >= 0) obj->SetLineColor(s.lineColor); + if (s.markerColor >= 0) obj->SetMarkerColor(s.markerColor); + + if (s.lineStyle >= 0) obj->SetLineStyle(s.lineStyle); // TAttLine + if (s.lineWidth >= 0) obj->SetLineWidth(s.lineWidth); // TAttLine + if (s.markerStyle >= 0) obj->SetMarkerStyle(s.markerStyle); // TAttMarker + if (s.markerSize > 0.f) obj->SetMarkerSize(s.markerSize); // TAttMarker + if (s.fillColor >= 0) obj->SetFillColor(s.fillColor); // TAttFill + if (s.fillStyle >= 0) obj->SetFillStyle(s.fillStyle); // TAttFill +} + + void TrendingTask::initialize(Trigger, framework::ServiceRegistryRef services) { // removing leftovers from any previous runs @@ -185,8 +208,8 @@ bool TrendingTask::trendValues(const Trigger& t, repository::DatabaseInterface& } mMetaData.runNumber = t.activity.mId; std::snprintf(mMetaData.runNumberStr, MaxRunNumberStringLength + 1, "%d", t.activity.mId); - bool wereAllSourcesInvoked = true; + for (auto& dataSource : mConfig.dataSources) { if (!reductor_helpers::updateReductor(mReductors[dataSource.name].get(), t, dataSource, qcdb, *this)) { wereAllSourcesInvoked = false; @@ -310,59 +333,114 @@ std::string TrendingTask::deduceGraphLegendOptions(const TrendingTaskConfig::Gra TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) { auto* c = new TCanvas(); - auto* legend = new TLegend(0.3, 0.2); + // Legend (NDC coordinates if enabled in config) + TLegend* legend = nullptr; + if (plotConfig.legend.enabled) { + legend = new TLegend(plotConfig.legend.x1, plotConfig.legend.y1, + plotConfig.legend.x2, plotConfig.legend.y2, + /*header=*/nullptr, /*option=*/"NDC"); + if (plotConfig.legend.nColumns > 0) { + legend->SetNColumns(plotConfig.legend.nColumns); + } + } else { + legend = new TLegend(0.30, 0.20, 0.55, 0.35, nullptr, "NDC"); + } + legend->SetBorderSize(0); + legend->SetFillStyle(0); + legend->SetTextSize(0.03); + legend->SetMargin(0.15); + + // Keep palette behavior unless user forces explicit colors via per-graph style if (plotConfig.colorPalette != 0) { - // this will work just once until we bump ROOT to a version which contains this commit: - // https://github.com/root-project/root/commit/0acdbd5be80494cec98ff60ba9a73cfe70a9a57a - // and enable the commented out line - // perhaps JSROOT >7.7.1 will allow us to retain the palette as well. gStyle->SetPalette(plotConfig.colorPalette); - // This makes ROOT store the selected palette for each generated plot. - // TColor::DefinedColors(1); // TODO enable when available } else { - // we set the default palette - gStyle->SetPalette(); + gStyle->SetPalette(); // default } - // regardless whether we draw a graph or a histogram, a histogram is always used by TTree::Draw to draw axes and title - // we attempt to keep it to do some modifications later + // Helpers + auto resolveColor = [](int idx) -> Color_t { + if (idx >= 0) { + return static_cast(idx); + } + return static_cast(-1); + }; + + auto getLastDrawnGraph = []() -> TGraph* { + if (!gPad) return nullptr; + TGraph* last = nullptr; + TIter it(gPad->GetListOfPrimitives()); + while (TObject* obj = it()) { + if (obj->InheritsFrom(TGraph::Class())) { + last = static_cast(obj); + } + } + return last; + }; + + auto applyStyleToGraph = [&](TGraph* gr, const TrendingTaskConfig::GraphStyle& st) { + if (!gr) return; + const Color_t ln = resolveColor(st.lineColor); + const Color_t mk = resolveColor(st.markerColor); + const Color_t fl = resolveColor(st.fillColor); + + if (ln >= 0) gr->SetLineColor(ln); + if (st.lineStyle >= 0) gr->SetLineStyle(st.lineStyle); + if (st.lineWidth >= 0) gr->SetLineWidth(st.lineWidth); + + if (mk >= 0) gr->SetMarkerColor(mk); + if (st.markerStyle >= 0) gr->SetMarkerStyle(st.markerStyle); + if (st.markerSize >= 0.f) gr->SetMarkerSize(st.markerSize); + + if (fl >= 0) gr->SetFillColor(fl); + if (st.fillStyle >= 0) gr->SetFillStyle(st.fillStyle); + }; + + // Regardless of drawing kind, TTree::Draw produces a TH1 "htemp" used for axes/title TH1* background = nullptr; bool firstGraphInPlot = true; - // by "graph" we consider anything we can draw, not necessarily TGraph, and we draw all on the same canvas + for (const auto& graphConfig : plotConfig.graphs) { - // we determine the order of the plotConfig, i.e. if it is a histogram (1), graphConfig (2), or any higher dimension. + // plotOrder: 1 -> histogram; 2 -> graph; >=3 -> multi-dim const size_t plotOrder = std::count(graphConfig.varexp.begin(), graphConfig.varexp.end(), ':') + 1; - // having "SAME" at the first TTree::Draw() call will not work, we have to add it only in subsequent Draw calls + // Add "SAME" from the second draw on this canvas std::string option = firstGraphInPlot ? graphConfig.option : "SAME " + graphConfig.option; + // Draw main series mTrend->Draw(graphConfig.varexp.c_str(), graphConfig.selection.c_str(), option.c_str()); - // For graphs, we allow to draw errors if they are specified. + // Optionally draw errors (xerr, yerr) as TGraphErrors on top TGraphErrors* graphErrors = nullptr; if (!graphConfig.errors.empty()) { - if (plotOrder != 2) { - ILOG(Error, Support) << "Non empty graphErrors seen for the plotConfig '" << plotConfig.name << "', which is not a graphConfig, ignoring." << ENDM; - } else { - // We generate some 4-D points, where 2 dimensions represent graph points and 2 others are the error bars + if (plotOrder == 2) { std::string varexpWithErrors(graphConfig.varexp + ":" + graphConfig.errors); mTrend->Draw(varexpWithErrors.c_str(), graphConfig.selection.c_str(), "goff"); - graphErrors = new TGraphErrors(mTrend->GetSelectedRows(), mTrend->GetVal(1), mTrend->GetVal(0), + graphErrors = new TGraphErrors(mTrend->GetSelectedRows(), + mTrend->GetVal(1), mTrend->GetVal(0), mTrend->GetVal(2), mTrend->GetVal(3)); graphErrors->SetName((graphConfig.name + "_errors").c_str()); graphErrors->SetTitle((graphConfig.title + " errors").c_str()); - // We draw on the same plotConfig as the main graphConfig, but only error bars graphErrors->Draw("SAME E"); + } else { + ILOG(Error, Support) << "Non-empty 'errors' for plot '" << plotConfig.name + << "' but varexp is not a 2D graph; ignoring errors." << ENDM; } } - if (auto graph = dynamic_cast(c->FindObject("Graph"))) { - graph->SetName(graphConfig.name.c_str()); - graph->SetTitle(graphConfig.title.c_str()); - legend->AddEntry(graph, graphConfig.title.c_str(), deduceGraphLegendOptions(graphConfig).c_str()); + // Style objects after Draw so we override palette/auto styling when requested + if (plotOrder >= 2) { + if (auto* gr = getLastDrawnGraph()) { + applyStyleToGraph(gr, graphConfig.style); + // Keep errors visually consistent with the main series + if (graphErrors) { + applyStyleToGraph(graphErrors, graphConfig.style); + } + } } - if (auto htemp = dynamic_cast(c->FindObject("htemp"))) { + + // Handle axes/title carrier histogram + if (auto* htemp = dynamic_cast(c->FindObject("htemp"))) { if (plotOrder == 1) { htemp->SetName(graphConfig.name.c_str()); htemp->SetTitle(graphConfig.title.c_str()); @@ -370,15 +448,17 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) } else { htemp->SetName("background"); htemp->SetTitle("background"); - // htemp was used by TTree::Draw only to draw axes and title, not to plot data, no need to add it to legend } - // QCG doesn't empty the buffers before visualizing the plotConfig, nor does ROOT when saving the file, - // so we have to do it here. htemp->BufferEmpty(); - // we keep the pointer to bg histogram for later postprocessing - if (background == nullptr) { - background = htemp; - } + if (!background) background = htemp; + } + + // Legend entry for graphs + if (auto* gr = dynamic_cast(c->FindObject("Graph"))) { + gr->SetName(graphConfig.name.c_str()); + gr->SetTitle(graphConfig.title.c_str()); + legend->AddEntry(gr, graphConfig.title.c_str(), + deduceGraphLegendOptions(graphConfig).c_str()); } firstGraphInPlot = false; @@ -387,51 +467,48 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) c->SetName(plotConfig.name.c_str()); c->SetTitle(plotConfig.title.c_str()); - // Postprocessing the plotConfig - adding specified titles, configuring time-based plots, flushing buffers. - // Notice that axes and title are drawn using a histogram, even in the case of graphs. + // Post-process: title, axes labels, time axis formatting, y-range if (background) { - // The title of background histogram is printed, not the title of canvas => we set it as well. background->SetTitle(plotConfig.title.c_str()); - // We have to update the canvas to make the title appear and access it in the next step. c->Update(); - // After the update, the title has a different size and it is not in the center anymore. We have to fix that. - if (auto title = dynamic_cast(c->GetPrimitive("title"))) { + if (auto* title = dynamic_cast(c->GetPrimitive("title"))) { title->SetBBoxCenterX(c->GetBBoxCenter().fX); c->Modified(); c->Update(); } else { - ILOG(Error, Devel) << "Could not get the title TPaveText of the plotConfig '" << plotConfig.name << "'." << ENDM; + ILOG(Error, Devel) << "Could not get TPaveText for title of '" << plotConfig.name << "'." << ENDM; } if (!plotConfig.graphAxisLabel.empty()) { setUserAxesLabels(background->GetXaxis(), background->GetYaxis(), plotConfig.graphAxisLabel); } - if (plotConfig.graphs.back().varexp.find(":time") != std::string::npos) { - // We have to explicitly configure showing time on x axis. - formatTimeXAxis(background); - } else if (plotConfig.graphs.back().varexp.find(":meta.runNumber") != std::string::npos) { - formatRunNumberXAxis(background); + if (!plotConfig.graphs.empty()) { + const auto& lastVar = plotConfig.graphs.back().varexp; + if (lastVar.find(":time") != std::string::npos) { + formatTimeXAxis(background); + } else if (lastVar.find(":meta.runNumber") != std::string::npos) { + formatRunNumberXAxis(background); + } } - // Set the user-defined range on the y axis if needed. if (!plotConfig.graphYRange.empty()) { setUserYAxisRange(background, plotConfig.graphYRange); c->Modified(); c->Update(); } } else { - ILOG(Error, Devel) << "Could not get the htemp histogram of the plotConfig '" << plotConfig.name << "'." << ENDM; + ILOG(Error, Devel) << "Could not get 'htemp' for plot '" << plotConfig.name << "'." << ENDM; } - if (plotConfig.graphs.size() > 1) { + if (plotConfig.graphs.size() > 1 || plotConfig.legend.enabled) { legend->Draw(); } else { delete legend; } + c->Modified(); c->Update(); - return c; } diff --git a/Framework/src/TrendingTaskConfig.cxx b/Framework/src/TrendingTaskConfig.cxx index 04de31261b..77dbbe14db 100644 --- a/Framework/src/TrendingTaskConfig.cxx +++ b/Framework/src/TrendingTaskConfig.cxx @@ -36,14 +36,34 @@ TrendingTaskConfig::TrendingTaskConfig(std::string id, const boost::property_tre for (const auto& [_, graphConfig] : graphsConfig.value()) { // first we use name of the graph, if absent, we use graph title, if absent, we use plot (object) name. const auto& name = graphConfig.get("name", graphConfig.get("title", plotConfig.get("name"))); + GraphStyle style; + style.lineColor = graphConfig.get("style.lineColor", -1); + style.lineStyle = graphConfig.get("style.lineStyle", -1); + style.lineWidth = graphConfig.get("style.lineWidth", -1); + style.markerColor = graphConfig.get("style.markerColor", -1); + style.markerStyle = graphConfig.get("style.markerStyle", -1); + style.markerSize = graphConfig.get("style.markerSize", -1.f); + style.fillColor = graphConfig.get("style.fillColor", -1); + style.fillStyle = graphConfig.get("style.fillStyle", -1); + graphs.push_back({ name, graphConfig.get("title", ""), graphConfig.get("varexp"), graphConfig.get("selection", ""), graphConfig.get("option", ""), - graphConfig.get("graphErrors", "") }); + graphConfig.get("graphErrors", ""), + style }); } } else { + GraphStyle style; + style.lineColor = plotConfig.get("style.lineColor", -1); + style.lineStyle = plotConfig.get("style.lineStyle", -1); + style.lineWidth = plotConfig.get("style.lineWidth", -1); + style.markerColor = plotConfig.get("style.markerColor", -1); + style.markerStyle = plotConfig.get("style.markerStyle", -1); + style.markerSize = plotConfig.get("style.markerSize", -1.f); + style.fillColor = plotConfig.get("style.fillColor", -1); + style.fillStyle = plotConfig.get("style.fillStyle", -1); graphs.push_back({ plotConfig.get("name", ""), plotConfig.get("title", ""), plotConfig.get("varexp"), @@ -51,11 +71,21 @@ TrendingTaskConfig::TrendingTaskConfig(std::string id, const boost::property_tre plotConfig.get("option", ""), plotConfig.get("graphErrors", "") }); } + + LegendConfig leg; + leg.enabled = plotConfig.get("legend.enabled", false); + leg.nColumns = plotConfig.get("legend.nColumns", 1); + leg.x1 = plotConfig.get("legend.x1", 0.30f); + leg.y1 = plotConfig.get("legend.y1", 0.20f); + leg.x2 = plotConfig.get("legend.x2", 0.55f); + leg.y2 = plotConfig.get("legend.y2", 0.35f); + plots.push_back({ plotConfig.get("name"), plotConfig.get("title", ""), plotConfig.get("graphAxisLabel", ""), plotConfig.get("graphYRange", ""), plotConfig.get("colorPalette", 0), + leg, graphs }); } From a30908b3fbf96e4e72d40bdf801287facc39dfe0 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Sat, 2 Aug 2025 15:09:52 +0200 Subject: [PATCH 03/20] chore: add example workflow for better trending customization --- Modules/FIT/FT0/etc/ft0-post-processing.json | 56 ++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Modules/FIT/FT0/etc/ft0-post-processing.json b/Modules/FIT/FT0/etc/ft0-post-processing.json index 07546c96e2..57e7a75498 100644 --- a/Modules/FIT/FT0/etc/ft0-post-processing.json +++ b/Modules/FIT/FT0/etc/ft0-post-processing.json @@ -22,6 +22,33 @@ "url": "" } }, + "tasks": { + "Digits": { + "active": "true", + "className": "o2::quality_control_modules::ft0::DigitQcTask", + "moduleName": "QcFT0", + "detectorName": "FT0", + "cycleDurationSeconds": "60", + "resetAfterCycles": "1", + "dataSource": { + "type": "direct", + "query": "digits:FT0/DIGITSBC/0;channels:FT0/DIGITSCH/0" + }, + "taskParameters": { + "#ChannelIDs": "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215", + "ChannelIDsAmpVsTime": "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215", + "trgThresholdTimeLow": "-100", + "trgThresholdTimeHigh": "100", + "trgModeSide": "A+C", + "trgModeThresholdVar": "Ampl", + "trgThresholdSCenA": "20", + "trgThresholdCenA": "40", + "trgOrGate": "153", + "trgChargeLevelLow": "0", + "trgChargeLevelHigh": "4095" + } + } + }, "checks": { "ASideInnerMIPCheck": { "active": "true", @@ -416,6 +443,7 @@ "className": "o2::quality_control::postprocessing::TrendingTask", "moduleName": "QcFT0", "detectorName": "FT0", + "resumeTrend": "true", "dataSources": [ { "type": "repository", @@ -431,6 +459,34 @@ } ], "plots": [ + { + "name": "trend_cycle_duration_ntf_corr", + "title": "cycle duration: ns/TF;time;cycle duration [ns/TimeFrames]", + "legend": { "enabled": true, "x1": 0.70, "y1": 0.70, "x2": 0.93, "y2": 0.90, "nColumns": 1 }, + "graphs": [ + { + "title": "cycle duration [ns]", + "varexp": "CycleDuration.entries:time", + "selection": "", + "option": "*LP", + "style": { "lineColor": 38, "markerColor": 4, "markerStyle": 20, "lineWidth": 2 } + }, + { + "title": "cycle duration [TimeFrames]", + "varexp": "CycleDurationNTF.entries:time", + "selection": "", + "option": "*LP", + "style": { "lineColor": 8, "markerColor": 3, "markerStyle": 30, "lineWidth": 2 } + }, + { + "title": "cycle duration: ns/TF;time;cycle duration [ns/TimeFrames]", + "varexp": "CycleDuration.entries/CycleDurationNTF.entries:time", + "selection": "", + "option": "*LP", + "style": { "lineColor": 31, "markerColor": 30, "markerStyle": 23, "lineWidth": 2 } + } + ] + }, { "name": "trend_cycle_duration", "title": "cycle duration [ns]", From 75ea28371291d12b7c48cbfcc72f775ea93ff70c Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 11:21:19 +0200 Subject: [PATCH 04/20] chore: add docs about extra trending options --- doc/PostProcessing.md | 124 +++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 32 deletions(-) diff --git a/doc/PostProcessing.md b/doc/PostProcessing.md index 137e48a999..2d7c6eb817 100644 --- a/doc/PostProcessing.md +++ b/doc/PostProcessing.md @@ -1,27 +1,31 @@ # Post-processing + + + * [The post-processing framework](#the-post-processing-framework) - * [Post-processing interface](#post-processing-interface) - * [Configuration](#configuration) - * [Running it](#running-it) + * [Post-processing interface](#post-processing-interface) + * [Configuration](#configuration) + * [Running it](#running-it) * [Convenience classes](#convenience-classes) - * [The TrendingTask class](#the-trendingtask-class) - * [The SliceTrendingTask class](#the-slicetrendingtask-class) - * [The ReferenceComparatorTask class](#the-referencecomparatortask-class) - * [The CcdbInspectorTask class](#the-ccdbinspectortask-class) - * [The QualityTask class](#the-qualitytask-class) - * [The BigScreen class](#the-bigscreen-class) + * [The TrendingTask class](#the-trendingtask-class) + * [The SliceTrendingTask class](#the-slicetrendingtask-class) + * [The ReferenceComparatorTask class](#the-referencecomparatortask-class) + * [The CcdbInspectorTask class](#the-ccdbinspectortask-class) + * [The QualityTask class](#the-qualitytask-class) + * [The BigScreen class](#the-bigscreen-class) * [More examples](#more-examples) + ## The post-processing framework This framework is intended for planned post-processing of objects generated by QC Tasks, Checks and correlating them with other data. The most common use-cases include correlation and trending of different properties of the detectors. - The users can write their own Post-processing Tasks or use the ones provided by the framework (see [Convenience classes](#convenience-classes)) which are supposed to cover the usual needs. Post-processing Tasks run asynchronously to data-taking, but can be triggered by a set of selected events. + The users can write their own Post-processing Tasks or use the ones provided by the framework (see [Convenience classes](#convenience-classes)) which are supposed to cover the usual needs. Post-processing Tasks run asynchronously to data-taking, but can be triggered by a set of selected events. ### Post-processing interface @@ -31,7 +35,7 @@ Any Post-processing Task should inherit PostProcessingInterface, which includes * `initialize` - initializes the task and its data, given the event which it was triggered by. * `update` - updates the task and its data, given the event which it was triggered by. * `finalize` - finalizes the processing, given the event which it was triggered by. - + Interfaces to databases and other services are accesible via `ServiceRegistry`, which is an argument to the last three methods. They are invoked when any of the specified triggers is up, which can be: * Start Of Run - triggers when receives SOSOR message from `aliecs.run` kafka topic which has **DIFFERENT** run number **AND** environment id than `Activity` class in config @@ -156,8 +160,8 @@ Each of the three methods can be invoked by one or more triggers. Below are list * `"newobject:[qcdb/ccdb]:"` - New Object - triggers when an object in QCDB or CCDB is updated (applicable for synchronous processing). For example: `"newobject:qcdb:qc/TST/MO/QcTask/Example"` * `"foreachobject:[qcdb/ccdb]:"` - For Each Object - triggers for each object in QCDB or CCDB which matches the activity indicated in the QC config file (applicable for both synchronous and asynchronous processing). This trigger contains monitor cycle of required object in its metadata since v1.178.0 * `"foreachlatest:[qcdb/ccdb]:"` - For Each Latest - triggers for the latest object version in QCDB or CCDB - for each matching activity (applicable for asynchronous processing). It sorts objects in ascending order by period, - pass and run. + for each matching activity (applicable for asynchronous processing). It sorts objects in ascending order by period, + pass and run. * `"once"` - Once - triggers only first time it is checked * `"always"` - Always - triggers each time it is checked * `"userorcontrol"` - triggers when upon corresponding START and STOP state transitions. This is the recommended trigger for `initTrigger` and `stopTrigger`. @@ -300,7 +304,7 @@ Multiple graphs can be drawn on one plot, if needed. As this class is a post-processing task, it inherits also its configuration JSON template. It extends it, though, some additional parameters. -``` json +```json { "qc": { ... @@ -334,7 +338,7 @@ The `"names"` array should point to one or more objects under a common `"path"` Field `"reductorParameters"` is used to configure Reductors (classes inherited from `"o2::quality_control::postprocessing::Reductor"` interface). It uses same format as `"extendedTaskParameters"` field. -``` json +```json { ... "dataSources": [ @@ -380,7 +384,7 @@ Optionally, one can use `"graphError"` to add x and y error bars to a graph, as The `"name"` and `"varexp"` are the only compulsory arguments, others can be omitted to reduce configuration files size. `"graphAxisLabel"` allows the user to set axis labels in the form of `"Label Y axis: Label X axis"`. With `"graphYRange"` numerical values for fixed ranges of the y axis can be provided in the form of `"Min:Max"`. -``` json +```json { ... "plots": [ @@ -420,6 +424,62 @@ The `"name"` and `"varexp"` are the only compulsory arguments, others can be omi } ``` +There is also a possibility to explicitly change the legend, and line, fill and marker styles in case of a need for more customized plots. Example is given below: + +```json +{ + ... + "plots": [ + { + "name": "trend_cycle_duration_ntf_corr", + "title": "cycle duration: ns/TF;time;cycle duration [ns/TimeFrames]", + "legend": { "enabled": true, "x1": 0.70, "y1": 0.70, "x2": 0.93, "y2": 0.90, "nColumns": 1 }, + "graphs": [ + { + "title": "cycle duration [ns]", + "varexp": "CycleDuration.entries:time", + "selection": "", + "option": "*LP", + "style": { "lineColor": 38, "markerColor": 4, "markerStyle": 20, "lineWidth": 2 } + }, + { + "title": "cycle duration [TimeFrames]", + "varexp": "CycleDurationNTF.entries:time", + "selection": "", + "option": "*LP", + "style": { "lineColor": 8, "markerColor": 3, "markerStyle": 30, "lineWidth": 2 } + }, + { + "title": "cycle duration: ns/TF;time;cycle duration [ns/TimeFrames]", + "varexp": "CycleDuration.entries/CycleDurationNTF.entries:time", + "selection": "", + "option": "*LP", + "style": { "lineColor": 31, "markerColor": 30, "markerStyle": 23, "lineWidth": 2 } + } + ] + } + ], + ... +} +``` + +The specific options are: + +* `legend` ~ allows to specify precise location and shape of legend, as well as disable it completely + * `enabled` ~ needs to be *true* to enable the manual changes to the legend + * `x1`, `y1`, `x2`, `y2` ~ manual coordinates of the legend box + * `nColumns` ~ number of columns in the legend +* `style` ~ allows to change the style of the line and marker + * `lineWidth` ~ width of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) + * `lineStyle` ~ style of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) + * `lineColor` ~ color of the line, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) + * `markerColor` ~ color of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) + * `markerStyle` ~ style of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) + * `markerSize` ~ size of the marker, float as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) + * `fillColor` ~ color of the fill, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) + * `fillStyle` ~ style of the fill, integer as defined by ROOT [TAttFill class](https://root.cern/doc/master/classTAttFill.html) + + To decide whether plots should be generated during each update or just during finalization, use the boolean flag `"producePlotsOnUpdate"`. @@ -479,7 +539,7 @@ The options for `"TrendingType"` are limited to: The field `"graphErrors"` is set up as `"graphErrors":"Var1:Var2"` where `Var1` is the error along y and `Var2` the error along x. For `Var1(2)` numerical values or the options listed for `Var` above can be set. The original histogram does not need to be provided as the task will take the histogram specified in `"varexp": "Histogram.Var:TrendingType"`. In `"graphYRange"` and `"graphXRange"` numerical values for fixed ranges of the x and y axis can be provided in the form of `"Min:Max"`. If provided, the task will set all x (or y) axis on the canvas to this range. `"graphAxisLabel"` allows the user to set axis labels in the form of `"Label Y axis: Label X axis"`. -``` +``` { ... "plots": [ @@ -551,25 +611,25 @@ The checker extracts the current and reference plots from the stored MO, and com Four comparison modules are provided in the framework: 1. `o2::quality_control_modules::common::ObjectComparatorDeviation`: comparison based on the average relative deviation between the bins of the current and reference histograms; the module accepts the following configuration parameters: - * `threshold`: the maximum allowed average relative deviation between current and reference histograms - * `rangeX`, `rangeY`: if set, the comparison is restricted to the bins in the specified X and Y ranges; bins outside the ranges are ignored + * `threshold`: the maximum allowed average relative deviation between current and reference histograms + * `rangeX`, `rangeY`: if set, the comparison is restricted to the bins in the specified X and Y ranges; bins outside the ranges are ignored 2. `o2::quality_control_modules::common::ObjectComparatorBinByBinDeviation`: comparison based on the relative deviation between each bin of the current and reference histograms; the module accepts the following configuration parameters: - * `threshold`: the maximum allowed relative deviation for each bin between current and reference histograms - * `rangeX`, `rangeY`: if set, the comparison is restricted to the bins in the specified X and Y ranges; bins outside the ranges are ignored - * `maxAllowedBadBins`: the maximum number of bins above threshold for which the quality is still considered Good + * `threshold`: the maximum allowed relative deviation for each bin between current and reference histograms + * `rangeX`, `rangeY`: if set, the comparison is restricted to the bins in the specified X and Y ranges; bins outside the ranges are ignored + * `maxAllowedBadBins`: the maximum number of bins above threshold for which the quality is still considered Good 3. `o2::quality_control_modules::common::ObjectComparatorChi2`: comparison based on a standard chi2 test between the current and reference histograms; the module accepts the following configuration parameters: - * `threshold`: the minimum allowed chi2 probability - * `rangeX`, `rangeY`: if set, the Chi2 test is restricted to the bins in the specified X and Y ranges; bins outside the ranges are ignored + * `threshold`: the minimum allowed chi2 probability + * `rangeX`, `rangeY`: if set, the Chi2 test is restricted to the bins in the specified X and Y ranges; bins outside the ranges are ignored 4. `o2::quality_control_modules::common::ObjectComparatorKolmogorov`: comparison based on a standard Kolmogorov test between the current and reference histograms; the module accepts the following configuration parameters: - * `threshold`: the minimum allowed Kolmogorov probability + * `threshold`: the minimum allowed Kolmogorov probability All the configuration parameters of the comparison modules optionally allow to restrict their validity to specific plots. The following example specifies a threshold value common to all the plots, and then overrides the threshold and X range for all plots named `TrackEta`: ```json "extendedCheckParameters": { - "default": { - "default": { + "default": { + "default": { "moduleName" : "QualityControl", "comparatorName" : "o2::quality_control_modules::common::ObjectComparatorChi2", "threshold" : "0.5", @@ -656,8 +716,8 @@ In the example configuration below, the relationship between the input and outpu "detectorName": "MCH", "policy": "OnAny", "extendedCheckParameters": { - "default": { - "default": { + "default": { + "default": { "moduleName" : "QualityControl", "comparatorName" : "o2::quality_control_modules::common::ObjectComparatorChi2", "threshold" : "0.5", @@ -914,8 +974,8 @@ The task is configured as follows: "moduleName": "QualityControl", "detectorName": "GLO", "extendedTaskParameters": { - "default": { - "default": { + "default": { + "default": { "nRows": "4", "nCols": "5", "borderWidth": "1", @@ -1063,7 +1123,7 @@ Use ForEachObject as the update trigger: ``` Since objects are usually published in collections at the same time, you can use a path for one object to be triggered - for a collection of them (all objects produced by a QC Task). + for a collection of them (all objects produced by a QC Task). Use the Activity which matches the run, and (optionally) period and pass name: From ccdaf5ca73a5c281dc3c83bd65d22c584757f0ba Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 11:21:53 +0200 Subject: [PATCH 05/20] chore: add comments about expected format of the style values --- Framework/include/QualityControl/TrendingTaskConfig.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Framework/include/QualityControl/TrendingTaskConfig.h b/Framework/include/QualityControl/TrendingTaskConfig.h index fbf4a98b48..98e1150a8f 100644 --- a/Framework/include/QualityControl/TrendingTaskConfig.h +++ b/Framework/include/QualityControl/TrendingTaskConfig.h @@ -33,6 +33,12 @@ struct TrendingTaskConfig : PostProcessingConfig { ~TrendingTaskConfig() = default; // graph style configuration + // colors as defined by ROOT TColor class: + // https://root.cern/doc/master/classTColor.html + // marker colors and styles are as defined by ROOT TAttMarker class + // https://root.cern/doc/master/classTAttMarker.html + // line styles are as defined by ROOT TAttLine class + // https://root.cern/doc/master/classTAttLine.html struct GraphStyle { int lineColor = -1; int lineStyle = -1; From 645366919928a4b0e52ed38405aef413725a6bb7 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 11:22:21 +0200 Subject: [PATCH 06/20] chore: various code-style corrections --- Framework/src/TrendingTask.cxx | 113 +++++++++++++-------------------- 1 file changed, 45 insertions(+), 68 deletions(-) diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index f91d3cb427..370202d7a5 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -142,28 +142,6 @@ void TrendingTask::initializeTrend(o2::quality_control::repository::DatabaseInte } } -static inline bool hasAnyStyle(const TrendingTaskConfig::GraphStyle& s) { - return s.lineColor >= 0 || s.lineStyle >= 0 || s.lineWidth >= 0 || - s.markerColor >= 0 || s.markerStyle >= 0 || s.markerSize > 0.f || - s.fillColor >= 0 || s.fillStyle >= 0; -} - -template // TGraph*, TH1*, etc. (anything with TAttLine/TAttMarker/TAttFill) -static inline void applyStyleIfAny(T* obj, const TrendingTaskConfig::GraphStyle& s) { - if (!hasAnyStyle(s) || !obj) return; - - // Colors - if (s.lineColor >= 0) obj->SetLineColor(s.lineColor); - if (s.markerColor >= 0) obj->SetMarkerColor(s.markerColor); - - if (s.lineStyle >= 0) obj->SetLineStyle(s.lineStyle); // TAttLine - if (s.lineWidth >= 0) obj->SetLineWidth(s.lineWidth); // TAttLine - if (s.markerStyle >= 0) obj->SetMarkerStyle(s.markerStyle); // TAttMarker - if (s.markerSize > 0.f) obj->SetMarkerSize(s.markerSize); // TAttMarker - if (s.fillColor >= 0) obj->SetFillColor(s.fillColor); // TAttFill - if (s.fillStyle >= 0) obj->SetFillStyle(s.fillStyle); // TAttFill -} - void TrendingTask::initialize(Trigger, framework::ServiceRegistryRef services) { @@ -208,8 +186,8 @@ bool TrendingTask::trendValues(const Trigger& t, repository::DatabaseInterface& } mMetaData.runNumber = t.activity.mId; std::snprintf(mMetaData.runNumberStr, MaxRunNumberStringLength + 1, "%d", t.activity.mId); + bool wereAllSourcesInvoked = true; - for (auto& dataSource : mConfig.dataSources) { if (!reductor_helpers::updateReductor(mReductors[dataSource.name].get(), t, dataSource, qcdb, *this)) { wereAllSourcesInvoked = false; @@ -344,7 +322,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) legend->SetNColumns(plotConfig.legend.nColumns); } } else { - legend = new TLegend(0.30, 0.20, 0.55, 0.35, nullptr, "NDC"); + legend = new TLegend(0.3, 0.2); } legend->SetBorderSize(0); legend->SetFillStyle(0); @@ -354,6 +332,8 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) // Keep palette behavior unless user forces explicit colors via per-graph style if (plotConfig.colorPalette != 0) { gStyle->SetPalette(plotConfig.colorPalette); + // This makes ROOT store the selected palette for each generated plot. + // TColor::DefinedColors(1); // TODO enable when available } else { gStyle->SetPalette(); // default } @@ -367,7 +347,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) }; auto getLastDrawnGraph = []() -> TGraph* { - if (!gPad) return nullptr; + if (!gPad) { return nullptr; } TGraph* last = nullptr; TIter it(gPad->GetListOfPrimitives()); while (TObject* obj = it()) { @@ -378,65 +358,52 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) return last; }; - auto applyStyleToGraph = [&](TGraph* gr, const TrendingTaskConfig::GraphStyle& st) { - if (!gr) return; - const Color_t ln = resolveColor(st.lineColor); - const Color_t mk = resolveColor(st.markerColor); - const Color_t fl = resolveColor(st.fillColor); + auto applyStyleToGraph = [&](TGraph* graph, const TrendingTaskConfig::GraphStyle& style) { + if (!graph) { return; } + const Color_t lineColor = resolveColor(style.lineColor); + const Color_t markerColor = resolveColor(style.markerColor); + const Color_t fillColor = resolveColor(style.fillColor); - if (ln >= 0) gr->SetLineColor(ln); - if (st.lineStyle >= 0) gr->SetLineStyle(st.lineStyle); - if (st.lineWidth >= 0) gr->SetLineWidth(st.lineWidth); + if (lineColor >= 0) { graph->SetLineColor(lineColor); } + if (style.lineStyle >= 0) { graph->SetLineStyle(style.lineStyle); } + if (style.lineWidth >= 0) { graph->SetLineWidth(style.lineWidth); } - if (mk >= 0) gr->SetMarkerColor(mk); - if (st.markerStyle >= 0) gr->SetMarkerStyle(st.markerStyle); - if (st.markerSize >= 0.f) gr->SetMarkerSize(st.markerSize); + if (markerColor >= 0) { graph->SetMarkerColor(markerColor); } + if (style.markerStyle >= 0) { graph->SetMarkerStyle(style.markerStyle); } + if (style.markerSize >= 0.f) { graph->SetMarkerSize(style.markerSize); } - if (fl >= 0) gr->SetFillColor(fl); - if (st.fillStyle >= 0) gr->SetFillStyle(st.fillStyle); + if (fillColor >= 0) { graph->SetFillColor(fillColor); } + if (style.fillStyle >= 0) { graph->SetFillStyle(style.fillStyle); } }; - // Regardless of drawing kind, TTree::Draw produces a TH1 "htemp" used for axes/title + // regardless whether we draw a graph or a histogram, a histogram is always used by TTree::Draw to draw axes and title + // we attempt to keep it to do some modifications later TH1* background = nullptr; bool firstGraphInPlot = true; - + // by "graph" we consider anything we can draw, not necessarily TGraph, and we draw all on the same canvas for (const auto& graphConfig : plotConfig.graphs) { - // plotOrder: 1 -> histogram; 2 -> graph; >=3 -> multi-dim + // we determine the order of the plotConfig, i.e. if it is a histogram (1), graphConfig (2), or any higher dimension. const size_t plotOrder = std::count(graphConfig.varexp.begin(), graphConfig.varexp.end(), ':') + 1; - // Add "SAME" from the second draw on this canvas + // having "SAME" at the first TTree::Draw() call will not work, we have to add it only in subsequent Draw calls std::string option = firstGraphInPlot ? graphConfig.option : "SAME " + graphConfig.option; // Draw main series mTrend->Draw(graphConfig.varexp.c_str(), graphConfig.selection.c_str(), option.c_str()); - // Optionally draw errors (xerr, yerr) as TGraphErrors on top + // For graphs, we allow to draw errors if they are specified. TGraphErrors* graphErrors = nullptr; - if (!graphConfig.errors.empty()) { - if (plotOrder == 2) { + if (plotOrder != 2) { + ILOG(Error, Support) << "Non empty graphErrors seen for the plotConfig '" << plotConfig.name << "', which is not a graphConfig, ignoring." << ENDM; + } else { + // We generate some 4-D points, where 2 dimensions represent graph points and 2 others are the error bars std::string varexpWithErrors(graphConfig.varexp + ":" + graphConfig.errors); mTrend->Draw(varexpWithErrors.c_str(), graphConfig.selection.c_str(), "goff"); - graphErrors = new TGraphErrors(mTrend->GetSelectedRows(), - mTrend->GetVal(1), mTrend->GetVal(0), + graphErrors = new TGraphErrors(mTrend->GetSelectedRows(), mTrend->GetVal(1), mTrend->GetVal(0), mTrend->GetVal(2), mTrend->GetVal(3)); graphErrors->SetName((graphConfig.name + "_errors").c_str()); graphErrors->SetTitle((graphConfig.title + " errors").c_str()); graphErrors->Draw("SAME E"); - } else { - ILOG(Error, Support) << "Non-empty 'errors' for plot '" << plotConfig.name - << "' but varexp is not a 2D graph; ignoring errors." << ENDM; - } - } - - // Style objects after Draw so we override palette/auto styling when requested - if (plotOrder >= 2) { - if (auto* gr = getLastDrawnGraph()) { - applyStyleToGraph(gr, graphConfig.style); - // Keep errors visually consistent with the main series - if (graphErrors) { - applyStyleToGraph(graphErrors, graphConfig.style); - } - } } // Handle axes/title carrier histogram @@ -449,15 +416,25 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) htemp->SetName("background"); htemp->SetTitle("background"); } + // QCG doesn't empty the buffers before visualizing the plotConfig, nor does ROOT when saving the file, + // so we have to do it here. htemp->BufferEmpty(); - if (!background) background = htemp; + if (!background) { background = htemp; } } - // Legend entry for graphs - if (auto* gr = dynamic_cast(c->FindObject("Graph"))) { - gr->SetName(graphConfig.name.c_str()); - gr->SetTitle(graphConfig.title.c_str()); - legend->AddEntry(gr, graphConfig.title.c_str(), + // Legend entry and styling for graphs + if (auto* graph = dynamic_cast(c->FindObject("Graph"))) { + if (plotOrder >= 2) { + // Style objects after Draw so we override palette/auto styling when requested + applyStyleToGraph(graph, graphConfig.style); + // Keep errors visually consistent with the main series + if (graphErrors) { + applyStyleToGraph(graphErrors, graphConfig.style); + } + } + graph->SetName(graphConfig.name.c_str()); + graph->SetTitle(graphConfig.title.c_str()); + legend->AddEntry(graph, graphConfig.title.c_str(), deduceGraphLegendOptions(graphConfig).c_str()); } From eeae9fa1d01d90bd4c778ad95fa33e2ec50c8bbd Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 11:34:56 +0200 Subject: [PATCH 07/20] feat: legend enabled by default --- Framework/include/QualityControl/TrendingTaskConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/include/QualityControl/TrendingTaskConfig.h b/Framework/include/QualityControl/TrendingTaskConfig.h index 98e1150a8f..8ae42ed0ba 100644 --- a/Framework/include/QualityControl/TrendingTaskConfig.h +++ b/Framework/include/QualityControl/TrendingTaskConfig.h @@ -63,7 +63,7 @@ struct TrendingTaskConfig : PostProcessingConfig { // legend configuration struct LegendConfig { - bool enabled{false}; + bool enabled{true}; int nColumns{1}; float x1{0.30f}, y1{0.20f}, x2{0.55f}, y2{0.35f}; // NDC coords }; From 2f42fbc0bcad9599b244137f89c2bd973f5bcb74 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 11:35:57 +0200 Subject: [PATCH 08/20] chore: rollback change to logs and comments wording --- Framework/src/TrendingTask.cxx | 65 +++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index 370202d7a5..43146da83b 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -393,37 +393,23 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) // For graphs, we allow to draw errors if they are specified. TGraphErrors* graphErrors = nullptr; - if (plotOrder != 2) { - ILOG(Error, Support) << "Non empty graphErrors seen for the plotConfig '" << plotConfig.name << "', which is not a graphConfig, ignoring." << ENDM; - } else { - // We generate some 4-D points, where 2 dimensions represent graph points and 2 others are the error bars - std::string varexpWithErrors(graphConfig.varexp + ":" + graphConfig.errors); - mTrend->Draw(varexpWithErrors.c_str(), graphConfig.selection.c_str(), "goff"); - graphErrors = new TGraphErrors(mTrend->GetSelectedRows(), mTrend->GetVal(1), mTrend->GetVal(0), - mTrend->GetVal(2), mTrend->GetVal(3)); - graphErrors->SetName((graphConfig.name + "_errors").c_str()); - graphErrors->SetTitle((graphConfig.title + " errors").c_str()); - graphErrors->Draw("SAME E"); - } - - // Handle axes/title carrier histogram - if (auto* htemp = dynamic_cast(c->FindObject("htemp"))) { - if (plotOrder == 1) { - htemp->SetName(graphConfig.name.c_str()); - htemp->SetTitle(graphConfig.title.c_str()); - legend->AddEntry(htemp, graphConfig.title.c_str(), "lpf"); + if (!graphConfig.errors.empty()) { + if (plotOrder != 2) { + ILOG(Error, Support) << "Non empty graphErrors seen for the plotConfig '" << plotConfig.name << "', which is not a graphConfig, ignoring." << ENDM; } else { - htemp->SetName("background"); - htemp->SetTitle("background"); + // We generate some 4-D points, where 2 dimensions represent graph points and 2 others are the error bars + std::string varexpWithErrors(graphConfig.varexp + ":" + graphConfig.errors); + mTrend->Draw(varexpWithErrors.c_str(), graphConfig.selection.c_str(), "goff"); + graphErrors = new TGraphErrors(mTrend->GetSelectedRows(), mTrend->GetVal(1), mTrend->GetVal(0), + mTrend->GetVal(2), mTrend->GetVal(3)); + graphErrors->SetName((graphConfig.name + "_errors").c_str()); + graphErrors->SetTitle((graphConfig.title + " errors").c_str()); + graphErrors->Draw("SAME E"); } - // QCG doesn't empty the buffers before visualizing the plotConfig, nor does ROOT when saving the file, - // so we have to do it here. - htemp->BufferEmpty(); - if (!background) { background = htemp; } } // Legend entry and styling for graphs - if (auto* graph = dynamic_cast(c->FindObject("Graph"))) { + if (auto graph = dynamic_cast(c->FindObject("Graph"))) { if (plotOrder >= 2) { // Style objects after Draw so we override palette/auto styling when requested applyStyleToGraph(graph, graphConfig.style); @@ -438,6 +424,22 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) deduceGraphLegendOptions(graphConfig).c_str()); } + // Legend entry and styling for histograms + if (auto htemp = dynamic_cast(c->FindObject("htemp"))) { + if (plotOrder == 1) { + htemp->SetName(graphConfig.name.c_str()); + htemp->SetTitle(graphConfig.title.c_str()); + legend->AddEntry(htemp, graphConfig.title.c_str(), "lpf"); + } else { + htemp->SetName("background"); + htemp->SetTitle("background"); + } + // QCG doesn't empty the buffers before visualizing the plotConfig, nor does ROOT when saving the file, + // so we have to do it here. + htemp->BufferEmpty(); + if (!background) { background = htemp; } + } + firstGraphInPlot = false; } @@ -446,15 +448,18 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) // Post-process: title, axes labels, time axis formatting, y-range if (background) { + // The title of background histogram is printed, not the title of canvas => we set it as well. background->SetTitle(plotConfig.title.c_str()); + // We have to update the canvas to make the title appear and access it in the next step. c->Update(); - if (auto* title = dynamic_cast(c->GetPrimitive("title"))) { + // After the update, the title has a different size and it is not in the center anymore. We have to fix that. + if (auto title = dynamic_cast(c->GetPrimitive("title"))) { title->SetBBoxCenterX(c->GetBBoxCenter().fX); c->Modified(); c->Update(); } else { - ILOG(Error, Devel) << "Could not get TPaveText for title of '" << plotConfig.name << "'." << ENDM; + ILOG(Error, Devel) << "Could not get the title TPaveText of the plotConfig '" << plotConfig.name << "'." << ENDM; } if (!plotConfig.graphAxisLabel.empty()) { @@ -470,13 +475,14 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) } } + // Set the user-defined range on the y axis if needed. if (!plotConfig.graphYRange.empty()) { setUserYAxisRange(background, plotConfig.graphYRange); c->Modified(); c->Update(); } } else { - ILOG(Error, Devel) << "Could not get 'htemp' for plot '" << plotConfig.name << "'." << ENDM; + ILOG(Error, Devel) << "Could not get the htemp histogram of the plotConfig '" << plotConfig.name << "'." << ENDM; } if (plotConfig.graphs.size() > 1 || plotConfig.legend.enabled) { @@ -487,5 +493,6 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) c->Modified(); c->Update(); + return c; } From fb175455014e49726771b7a91992fa14c9bb5699 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 11:36:25 +0200 Subject: [PATCH 09/20] feat: allow disabling the legend explicitly --- Framework/src/TrendingTask.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index 43146da83b..ab71d4fe89 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -485,7 +485,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) ILOG(Error, Devel) << "Could not get the htemp histogram of the plotConfig '" << plotConfig.name << "'." << ENDM; } - if (plotConfig.graphs.size() > 1 || plotConfig.legend.enabled) { + if (plotConfig.graphs.size() > 1 && plotConfig.legend.enabled) { legend->Draw(); } else { delete legend; From 53468f68f052ccf5d1f0a42607ea3260fc804205 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 11:55:16 +0200 Subject: [PATCH 10/20] feat: lambda functions reworked as static functions --- .../include/QualityControl/TrendingTask.h | 2 + Framework/src/TrendingTask.cxx | 62 +++++++------------ 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/Framework/include/QualityControl/TrendingTask.h b/Framework/include/QualityControl/TrendingTask.h index 9b37bee237..4cde3c579c 100644 --- a/Framework/include/QualityControl/TrendingTask.h +++ b/Framework/include/QualityControl/TrendingTask.h @@ -73,6 +73,8 @@ class TrendingTask : public PostProcessingInterface static void formatTimeXAxis(TH1* background); static void formatRunNumberXAxis(TH1* background); static std::string deduceGraphLegendOptions(const TrendingTaskConfig::Graph& graphConfig); + static Color_t resolveColor(int idx); + static void applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::GraphStyle& style); /// returns true only if all datasources were available to update reductor bool trendValues(const Trigger& t, repository::DatabaseInterface&); diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index ab71d4fe89..45a004511b 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -142,6 +142,30 @@ void TrendingTask::initializeTrend(o2::quality_control::repository::DatabaseInte } } +Color_t TrendingTask::resolveColor(int idx) { + if (idx >= 0) { + return static_cast(idx); + } + return static_cast(-1); +} + +void TrendingTask::applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::GraphStyle& style) { + if (!graph) { return; } + const Color_t lineColor = resolveColor(style.lineColor); + const Color_t markerColor = resolveColor(style.markerColor); + const Color_t fillColor = resolveColor(style.fillColor); + + if (lineColor >= 0) { graph->SetLineColor(lineColor); } + if (style.lineStyle >= 0) { graph->SetLineStyle(style.lineStyle); } + if (style.lineWidth >= 0) { graph->SetLineWidth(style.lineWidth); } + + if (markerColor >= 0) { graph->SetMarkerColor(markerColor); } + if (style.markerStyle >= 0) { graph->SetMarkerStyle(style.markerStyle); } + if (style.markerSize >= 0.f) { graph->SetMarkerSize(style.markerSize); } + + if (fillColor >= 0) { graph->SetFillColor(fillColor); } + if (style.fillStyle >= 0) { graph->SetFillStyle(style.fillStyle); } +} void TrendingTask::initialize(Trigger, framework::ServiceRegistryRef services) { @@ -338,44 +362,6 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) gStyle->SetPalette(); // default } - // Helpers - auto resolveColor = [](int idx) -> Color_t { - if (idx >= 0) { - return static_cast(idx); - } - return static_cast(-1); - }; - - auto getLastDrawnGraph = []() -> TGraph* { - if (!gPad) { return nullptr; } - TGraph* last = nullptr; - TIter it(gPad->GetListOfPrimitives()); - while (TObject* obj = it()) { - if (obj->InheritsFrom(TGraph::Class())) { - last = static_cast(obj); - } - } - return last; - }; - - auto applyStyleToGraph = [&](TGraph* graph, const TrendingTaskConfig::GraphStyle& style) { - if (!graph) { return; } - const Color_t lineColor = resolveColor(style.lineColor); - const Color_t markerColor = resolveColor(style.markerColor); - const Color_t fillColor = resolveColor(style.fillColor); - - if (lineColor >= 0) { graph->SetLineColor(lineColor); } - if (style.lineStyle >= 0) { graph->SetLineStyle(style.lineStyle); } - if (style.lineWidth >= 0) { graph->SetLineWidth(style.lineWidth); } - - if (markerColor >= 0) { graph->SetMarkerColor(markerColor); } - if (style.markerStyle >= 0) { graph->SetMarkerStyle(style.markerStyle); } - if (style.markerSize >= 0.f) { graph->SetMarkerSize(style.markerSize); } - - if (fillColor >= 0) { graph->SetFillColor(fillColor); } - if (style.fillStyle >= 0) { graph->SetFillStyle(style.fillStyle); } - }; - // regardless whether we draw a graph or a histogram, a histogram is always used by TTree::Draw to draw axes and title // we attempt to keep it to do some modifications later TH1* background = nullptr; From d228aa2c9b4b81268aa7fa90c376d737657ce3d7 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 17:04:59 +0200 Subject: [PATCH 11/20] doc: document overriding option params --- .../include/QualityControl/TrendingTaskConfig.h | 1 + doc/PostProcessing.md | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Framework/include/QualityControl/TrendingTaskConfig.h b/Framework/include/QualityControl/TrendingTaskConfig.h index 8ae42ed0ba..44331a1f3b 100644 --- a/Framework/include/QualityControl/TrendingTaskConfig.h +++ b/Framework/include/QualityControl/TrendingTaskConfig.h @@ -39,6 +39,7 @@ struct TrendingTaskConfig : PostProcessingConfig { // https://root.cern/doc/master/classTAttMarker.html // line styles are as defined by ROOT TAttLine class // https://root.cern/doc/master/classTAttLine.html + // WARNING: Any parameters in this struct will override colliding parameters in option struct GraphStyle { int lineColor = -1; int lineStyle = -1; diff --git a/doc/PostProcessing.md b/doc/PostProcessing.md index 2d7c6eb817..eac00721f1 100644 --- a/doc/PostProcessing.md +++ b/doc/PostProcessing.md @@ -478,7 +478,8 @@ The specific options are: * `markerSize` ~ size of the marker, float as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) * `fillColor` ~ color of the fill, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) * `fillStyle` ~ style of the fill, integer as defined by ROOT [TAttFill class](https://root.cern/doc/master/classTAttFill.html) - + +WARNING: Any style parameters specified will override colliding parameters in option. To decide whether plots should be generated during each update or just during finalization, use the boolean flag `"producePlotsOnUpdate"`. @@ -628,8 +629,8 @@ The following example specifies a threshold value common to all the plots, and t ```json "extendedCheckParameters": { - "default": { - "default": { + "default": { + "default": { "moduleName" : "QualityControl", "comparatorName" : "o2::quality_control_modules::common::ObjectComparatorChi2", "threshold" : "0.5", @@ -716,8 +717,8 @@ In the example configuration below, the relationship between the input and outpu "detectorName": "MCH", "policy": "OnAny", "extendedCheckParameters": { - "default": { - "default": { + "default": { + "default": { "moduleName" : "QualityControl", "comparatorName" : "o2::quality_control_modules::common::ObjectComparatorChi2", "threshold" : "0.5", @@ -974,8 +975,8 @@ The task is configured as follows: "moduleName": "QualityControl", "detectorName": "GLO", "extendedTaskParameters": { - "default": { - "default": { + "default": { + "default": { "nRows": "4", "nCols": "5", "borderWidth": "1", From c2946ab308e9a10aecd4af3b8c8aa63c1aa22e28 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 17:57:02 +0200 Subject: [PATCH 12/20] chore: further code-style changes --- Framework/src/TrendingTask.cxx | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index 45a004511b..6eba633d26 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -142,18 +142,11 @@ void TrendingTask::initializeTrend(o2::quality_control::repository::DatabaseInte } } -Color_t TrendingTask::resolveColor(int idx) { - if (idx >= 0) { - return static_cast(idx); - } - return static_cast(-1); -} - void TrendingTask::applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::GraphStyle& style) { if (!graph) { return; } - const Color_t lineColor = resolveColor(style.lineColor); - const Color_t markerColor = resolveColor(style.markerColor); - const Color_t fillColor = resolveColor(style.fillColor); + const Color_t lineColor = style.lineColor; + const Color_t markerColor = style.markerColor; + const Color_t fillColor = style.fillColor; if (lineColor >= 0) { graph->SetLineColor(lineColor); } if (style.lineStyle >= 0) { graph->SetLineStyle(style.lineStyle); } @@ -338,7 +331,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) // Legend (NDC coordinates if enabled in config) TLegend* legend = nullptr; - if (plotConfig.legend.enabled) { + if (plotConfig.legend.x1 && plotConfig.legend.y1 && plotConfig.legend.x2 && plotConfig.legend.y2) { legend = new TLegend(plotConfig.legend.x1, plotConfig.legend.y1, plotConfig.legend.x2, plotConfig.legend.y2, /*header=*/nullptr, /*option=*/"NDC"); @@ -419,6 +412,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) } else { htemp->SetName("background"); htemp->SetTitle("background"); + // htemp was used by TTree::Draw only to draw axes and title, not to plot data, no need to add it to legend } // QCG doesn't empty the buffers before visualizing the plotConfig, nor does ROOT when saving the file, // so we have to do it here. @@ -432,7 +426,8 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) c->SetName(plotConfig.name.c_str()); c->SetTitle(plotConfig.title.c_str()); - // Post-process: title, axes labels, time axis formatting, y-range + // Postprocessing the plotConfig - adding specified titles, configuring time-based plots, flushing buffers. + // Notice that axes and title are drawn using a histogram, even in the case of graphs. if (background) { // The title of background histogram is printed, not the title of canvas => we set it as well. background->SetTitle(plotConfig.title.c_str()); @@ -452,13 +447,11 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) setUserAxesLabels(background->GetXaxis(), background->GetYaxis(), plotConfig.graphAxisLabel); } - if (!plotConfig.graphs.empty()) { - const auto& lastVar = plotConfig.graphs.back().varexp; - if (lastVar.find(":time") != std::string::npos) { - formatTimeXAxis(background); - } else if (lastVar.find(":meta.runNumber") != std::string::npos) { - formatRunNumberXAxis(background); - } + if (plotConfig.graphs.back().varexp.find(":time") != std::string::npos) { + // We have to explicitly configure showing time on x axis. + formatTimeXAxis(background); + } else if (plotConfig.graphs.back().varexp.find(":meta.runNumber") != std::string::npos) { + formatRunNumberXAxis(background); } // Set the user-defined range on the y axis if needed. @@ -471,7 +464,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) ILOG(Error, Devel) << "Could not get the htemp histogram of the plotConfig '" << plotConfig.name << "'." << ENDM; } - if (plotConfig.graphs.size() > 1 && plotConfig.legend.enabled) { + if (plotConfig.graphs.size() > 1) { legend->Draw(); } else { delete legend; From c8898b5dcf1a74d6e18bae4df23b4c24694e10e3 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Thu, 7 Aug 2025 22:29:04 +0200 Subject: [PATCH 13/20] chore: remove legend.enabled + clang-format --- .../QualityControl/TrendingTaskConfig.h | 21 +++---- Framework/src/TrendingTask.cxx | 62 +++++++++++++------ Framework/src/TrendingTaskConfig.cxx | 41 ++++++------ Modules/FIT/FV0/src/DigitQcTask.cxx | 12 ++-- 4 files changed, 79 insertions(+), 57 deletions(-) diff --git a/Framework/include/QualityControl/TrendingTaskConfig.h b/Framework/include/QualityControl/TrendingTaskConfig.h index 44331a1f3b..76656c7dd1 100644 --- a/Framework/include/QualityControl/TrendingTaskConfig.h +++ b/Framework/include/QualityControl/TrendingTaskConfig.h @@ -41,14 +41,14 @@ struct TrendingTaskConfig : PostProcessingConfig { // https://root.cern/doc/master/classTAttLine.html // WARNING: Any parameters in this struct will override colliding parameters in option struct GraphStyle { - int lineColor = -1; - int lineStyle = -1; - int lineWidth = -1; - int markerColor = -1; - int markerStyle = -1; - float markerSize = -1.f; - int fillColor = -1; - int fillStyle = -1; + int lineColor = -1; + int lineStyle = -1; + int lineWidth = -1; + int markerColor = -1; + int markerStyle = -1; + float markerSize = -1.f; + int fillColor = -1; + int fillStyle = -1; }; // this corresponds to one TTree::Draw() call, i.e. one graph or histogram drawing @@ -64,9 +64,8 @@ struct TrendingTaskConfig : PostProcessingConfig { // legend configuration struct LegendConfig { - bool enabled{true}; - int nColumns{1}; - float x1{0.30f}, y1{0.20f}, x2{0.55f}, y2{0.35f}; // NDC coords + int nColumns{ 1 }; + float x1{ 0.30f }, y1{ 0.20f }, x2{ 0.55f }, y2{ 0.35f }; // NDC coords }; // this corresponds to one canvas which can include multiple graphs diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index 6eba633d26..19b8e31a59 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -142,22 +142,41 @@ void TrendingTask::initializeTrend(o2::quality_control::repository::DatabaseInte } } -void TrendingTask::applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::GraphStyle& style) { - if (!graph) { return; } +void TrendingTask::applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::GraphStyle& style) +{ + if (!graph) { + return; + } const Color_t lineColor = style.lineColor; const Color_t markerColor = style.markerColor; const Color_t fillColor = style.fillColor; - if (lineColor >= 0) { graph->SetLineColor(lineColor); } - if (style.lineStyle >= 0) { graph->SetLineStyle(style.lineStyle); } - if (style.lineWidth >= 0) { graph->SetLineWidth(style.lineWidth); } + if (lineColor >= 0) { + graph->SetLineColor(lineColor); + } + if (style.lineStyle >= 0) { + graph->SetLineStyle(style.lineStyle); + } + if (style.lineWidth >= 0) { + graph->SetLineWidth(style.lineWidth); + } - if (markerColor >= 0) { graph->SetMarkerColor(markerColor); } - if (style.markerStyle >= 0) { graph->SetMarkerStyle(style.markerStyle); } - if (style.markerSize >= 0.f) { graph->SetMarkerSize(style.markerSize); } + if (markerColor >= 0) { + graph->SetMarkerColor(markerColor); + } + if (style.markerStyle >= 0) { + graph->SetMarkerStyle(style.markerStyle); + } + if (style.markerSize >= 0.f) { + graph->SetMarkerSize(style.markerSize); + } - if (fillColor >= 0) { graph->SetFillColor(fillColor); } - if (style.fillStyle >= 0) { graph->SetFillStyle(style.fillStyle); } + if (fillColor >= 0) { + graph->SetFillColor(fillColor); + } + if (style.fillStyle >= 0) { + graph->SetFillStyle(style.fillStyle); + } } void TrendingTask::initialize(Trigger, framework::ServiceRegistryRef services) @@ -203,7 +222,7 @@ bool TrendingTask::trendValues(const Trigger& t, repository::DatabaseInterface& } mMetaData.runNumber = t.activity.mId; std::snprintf(mMetaData.runNumberStr, MaxRunNumberStringLength + 1, "%d", t.activity.mId); - + bool wereAllSourcesInvoked = true; for (auto& dataSource : mConfig.dataSources) { if (!reductor_helpers::updateReductor(mReductors[dataSource.name].get(), t, dataSource, qcdb, *this)) { @@ -329,7 +348,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) { auto* c = new TCanvas(); - // Legend (NDC coordinates if enabled in config) + // Legend TLegend* legend = nullptr; if (plotConfig.legend.x1 && plotConfig.legend.y1 && plotConfig.legend.x2 && plotConfig.legend.y2) { legend = new TLegend(plotConfig.legend.x1, plotConfig.legend.y1, @@ -377,13 +396,14 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) ILOG(Error, Support) << "Non empty graphErrors seen for the plotConfig '" << plotConfig.name << "', which is not a graphConfig, ignoring." << ENDM; } else { // We generate some 4-D points, where 2 dimensions represent graph points and 2 others are the error bars - std::string varexpWithErrors(graphConfig.varexp + ":" + graphConfig.errors); - mTrend->Draw(varexpWithErrors.c_str(), graphConfig.selection.c_str(), "goff"); - graphErrors = new TGraphErrors(mTrend->GetSelectedRows(), mTrend->GetVal(1), mTrend->GetVal(0), - mTrend->GetVal(2), mTrend->GetVal(3)); - graphErrors->SetName((graphConfig.name + "_errors").c_str()); - graphErrors->SetTitle((graphConfig.title + " errors").c_str()); - graphErrors->Draw("SAME E"); + std::string varexpWithErrors(graphConfig.varexp + ":" + graphConfig.errors); + mTrend->Draw(varexpWithErrors.c_str(), graphConfig.selection.c_str(), "goff"); + graphErrors = new TGraphErrors(mTrend->GetSelectedRows(), mTrend->GetVal(1), mTrend->GetVal(0), + mTrend->GetVal(2), mTrend->GetVal(3)); + graphErrors->SetName((graphConfig.name + "_errors").c_str()); + graphErrors->SetTitle((graphConfig.title + " errors").c_str()); + // We draw on the same plotConfig as the main graphConfig, but only error bars + graphErrors->Draw("SAME E"); } } @@ -417,7 +437,9 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) // QCG doesn't empty the buffers before visualizing the plotConfig, nor does ROOT when saving the file, // so we have to do it here. htemp->BufferEmpty(); - if (!background) { background = htemp; } + if (!background) { + background = htemp; + } } firstGraphInPlot = false; diff --git a/Framework/src/TrendingTaskConfig.cxx b/Framework/src/TrendingTaskConfig.cxx index 77dbbe14db..75bb4a0a3b 100644 --- a/Framework/src/TrendingTaskConfig.cxx +++ b/Framework/src/TrendingTaskConfig.cxx @@ -37,14 +37,14 @@ TrendingTaskConfig::TrendingTaskConfig(std::string id, const boost::property_tre // first we use name of the graph, if absent, we use graph title, if absent, we use plot (object) name. const auto& name = graphConfig.get("name", graphConfig.get("title", plotConfig.get("name"))); GraphStyle style; - style.lineColor = graphConfig.get("style.lineColor", -1); - style.lineStyle = graphConfig.get("style.lineStyle", -1); - style.lineWidth = graphConfig.get("style.lineWidth", -1); - style.markerColor = graphConfig.get("style.markerColor", -1); - style.markerStyle = graphConfig.get("style.markerStyle", -1); - style.markerSize = graphConfig.get("style.markerSize", -1.f); - style.fillColor = graphConfig.get("style.fillColor", -1); - style.fillStyle = graphConfig.get("style.fillStyle", -1); + style.lineColor = graphConfig.get("style.lineColor", -1); + style.lineStyle = graphConfig.get("style.lineStyle", -1); + style.lineWidth = graphConfig.get("style.lineWidth", -1); + style.markerColor = graphConfig.get("style.markerColor", -1); + style.markerStyle = graphConfig.get("style.markerStyle", -1); + style.markerSize = graphConfig.get("style.markerSize", -1.f); + style.fillColor = graphConfig.get("style.fillColor", -1); + style.fillStyle = graphConfig.get("style.fillStyle", -1); graphs.push_back({ name, graphConfig.get("title", ""), @@ -56,14 +56,14 @@ TrendingTaskConfig::TrendingTaskConfig(std::string id, const boost::property_tre } } else { GraphStyle style; - style.lineColor = plotConfig.get("style.lineColor", -1); - style.lineStyle = plotConfig.get("style.lineStyle", -1); - style.lineWidth = plotConfig.get("style.lineWidth", -1); - style.markerColor = plotConfig.get("style.markerColor", -1); - style.markerStyle = plotConfig.get("style.markerStyle", -1); - style.markerSize = plotConfig.get("style.markerSize", -1.f); - style.fillColor = plotConfig.get("style.fillColor", -1); - style.fillStyle = plotConfig.get("style.fillStyle", -1); + style.lineColor = plotConfig.get("style.lineColor", -1); + style.lineStyle = plotConfig.get("style.lineStyle", -1); + style.lineWidth = plotConfig.get("style.lineWidth", -1); + style.markerColor = plotConfig.get("style.markerColor", -1); + style.markerStyle = plotConfig.get("style.markerStyle", -1); + style.markerSize = plotConfig.get("style.markerSize", -1.f); + style.fillColor = plotConfig.get("style.fillColor", -1); + style.fillStyle = plotConfig.get("style.fillStyle", -1); graphs.push_back({ plotConfig.get("name", ""), plotConfig.get("title", ""), plotConfig.get("varexp"), @@ -73,12 +73,11 @@ TrendingTaskConfig::TrendingTaskConfig(std::string id, const boost::property_tre } LegendConfig leg; - leg.enabled = plotConfig.get("legend.enabled", false); leg.nColumns = plotConfig.get("legend.nColumns", 1); - leg.x1 = plotConfig.get("legend.x1", 0.30f); - leg.y1 = plotConfig.get("legend.y1", 0.20f); - leg.x2 = plotConfig.get("legend.x2", 0.55f); - leg.y2 = plotConfig.get("legend.y2", 0.35f); + leg.x1 = plotConfig.get("legend.x1", 0.30f); + leg.y1 = plotConfig.get("legend.y1", 0.20f); + leg.x2 = plotConfig.get("legend.x2", 0.55f); + leg.y2 = plotConfig.get("legend.y2", 0.35f); plots.push_back({ plotConfig.get("name"), plotConfig.get("title", ""), diff --git a/Modules/FIT/FV0/src/DigitQcTask.cxx b/Modules/FIT/FV0/src/DigitQcTask.cxx index eca13ec356..beeefcc368 100644 --- a/Modules/FIT/FV0/src/DigitQcTask.cxx +++ b/Modules/FIT/FV0/src/DigitQcTask.cxx @@ -192,11 +192,11 @@ void DigitQcTask::initialize(o2::framework::InitContext& /*ctx*/) "Amplitude distribution;QTC amp;Counts", 4096, -0.5, 4095.5); for (std::size_t ch = 0; ch < sNCHANNELS_FV0_PLUSREF; ++ch) { - mHistAmpPerCh[ch] = std::make_unique( + mHistAmpPerCh[ch] = std::make_unique( Form("AmpDistCh%zu", ch), Form("Amplitude distribution channel %zu;QTC amp;Counts", ch), 4096, -0.5, 4095.5); - getObjectsManager()->startPublishing(mHistAmpPerCh[ch].get()); + getObjectsManager()->startPublishing(mHistAmpPerCh[ch].get()); } getObjectsManager()->startPublishing(mHistAmpAll.get()); mHistBC = std::make_unique("BC", "BC;BC;counts;", sBCperOrbit, 0, sBCperOrbit); @@ -396,9 +396,10 @@ void DigitQcTask::startOfActivity(const Activity& activity) ILOG(Debug, Devel) << "startOfActivity" << activity.mId << ENDM; mHistTime2Ch->Reset(); mHistAmp2Ch->Reset(); - mHistAmpAll->Reset(); + mHistAmpAll->Reset(); for (auto& h : mHistAmpPerCh) { - if (h) h->Reset(); + if (h) + h->Reset(); } mHistBC->Reset(); mHistChDataBits->Reset(); @@ -694,7 +695,8 @@ void DigitQcTask::reset() mHistAmp2Ch->Reset(); mHistAmpAll->Reset(); for (auto& h : mHistAmpPerCh) { - if (h) h->Reset(); + if (h) + h->Reset(); } mHistBC->Reset(); mHistChDataBits->Reset(); From 4ee7132b2cba0eeb13891811dea625b1f45db830 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Fri, 8 Aug 2025 09:09:43 +0200 Subject: [PATCH 14/20] doc: update info about custom params --- doc/PostProcessing.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/PostProcessing.md b/doc/PostProcessing.md index eac00721f1..6e8efa3f10 100644 --- a/doc/PostProcessing.md +++ b/doc/PostProcessing.md @@ -466,8 +466,7 @@ There is also a possibility to explicitly change the legend, and line, fill and The specific options are: * `legend` ~ allows to specify precise location and shape of legend, as well as disable it completely - * `enabled` ~ needs to be *true* to enable the manual changes to the legend - * `x1`, `y1`, `x2`, `y2` ~ manual coordinates of the legend box + * `x1`, `y1`, `x2`, `y2` ~ manual coordinates of the legend box, all need to be present for custom legend placement to take account * `nColumns` ~ number of columns in the legend * `style` ~ allows to change the style of the line and marker * `lineWidth` ~ width of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) @@ -477,12 +476,12 @@ The specific options are: * `markerStyle` ~ style of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) * `markerSize` ~ size of the marker, float as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) * `fillColor` ~ color of the fill, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) - * `fillStyle` ~ style of the fill, integer as defined by ROOT [TAttFill class](https://root.cern/doc/master/classTAttFill.html) + * `fillStyle` e~ style of the fill, integer as defined by ROOT [TAttFill class](https://root.cern/doc/master/classTAttFill.html) WARNING: Any style parameters specified will override colliding parameters in option. To decide whether plots should be generated during each update or just during finalization, -use the boolean flag `"producePlotsOnUpdate"`. +use the boolean flag `"producePlotnd placsOnUpdate"`. To pick up the last existing trend which matches the specified Activity, set `"resumeTrend"` to `"true"`. From ae64a1fe4cf9c069f89fc52f5d321e7ee0e67657 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Fri, 8 Aug 2025 09:50:05 +0200 Subject: [PATCH 15/20] fix: correct wording in docs --- doc/PostProcessing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/PostProcessing.md b/doc/PostProcessing.md index 6e8efa3f10..bbde6e1d82 100644 --- a/doc/PostProcessing.md +++ b/doc/PostProcessing.md @@ -481,7 +481,7 @@ The specific options are: WARNING: Any style parameters specified will override colliding parameters in option. To decide whether plots should be generated during each update or just during finalization, -use the boolean flag `"producePlotnd placsOnUpdate"`. +use the boolean flag `"producePlotsOnUpdate"`. To pick up the last existing trend which matches the specified Activity, set `"resumeTrend"` to `"true"`. From 4c70bbf3fbc98829f98d6c62e59c1ebd55d2d502 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Fri, 8 Aug 2025 15:02:29 +0200 Subject: [PATCH 16/20] chore: fruther code-style changes --- .../include/QualityControl/TrendingTask.h | 1 - Framework/src/TrendingTask.cxx | 15 ++++------ doc/PostProcessing.md | 28 +++++++++---------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/Framework/include/QualityControl/TrendingTask.h b/Framework/include/QualityControl/TrendingTask.h index 4cde3c579c..db1dcb485e 100644 --- a/Framework/include/QualityControl/TrendingTask.h +++ b/Framework/include/QualityControl/TrendingTask.h @@ -73,7 +73,6 @@ class TrendingTask : public PostProcessingInterface static void formatTimeXAxis(TH1* background); static void formatRunNumberXAxis(TH1* background); static std::string deduceGraphLegendOptions(const TrendingTaskConfig::Graph& graphConfig); - static Color_t resolveColor(int idx); static void applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::GraphStyle& style); /// returns true only if all datasources were available to update reductor diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index 19b8e31a59..e39466324a 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -147,12 +147,9 @@ void TrendingTask::applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::Gr if (!graph) { return; } - const Color_t lineColor = style.lineColor; - const Color_t markerColor = style.markerColor; - const Color_t fillColor = style.fillColor; - if (lineColor >= 0) { - graph->SetLineColor(lineColor); + if (style.lineColor >= 0) { + graph->SetLineColor(style.lineColor); } if (style.lineStyle >= 0) { graph->SetLineStyle(style.lineStyle); @@ -161,8 +158,8 @@ void TrendingTask::applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::Gr graph->SetLineWidth(style.lineWidth); } - if (markerColor >= 0) { - graph->SetMarkerColor(markerColor); + if (style.markerColor >= 0) { + graph->SetMarkerColor(style.markerColor); } if (style.markerStyle >= 0) { graph->SetMarkerStyle(style.markerStyle); @@ -171,8 +168,8 @@ void TrendingTask::applyStyleToGraph(TGraph* graph, const TrendingTaskConfig::Gr graph->SetMarkerSize(style.markerSize); } - if (fillColor >= 0) { - graph->SetFillColor(fillColor); + if (style.fillColor >= 0) { + graph->SetFillColor(style.fillColor); } if (style.fillStyle >= 0) { graph->SetFillStyle(style.fillStyle); diff --git a/doc/PostProcessing.md b/doc/PostProcessing.md index bbde6e1d82..67002ea91a 100644 --- a/doc/PostProcessing.md +++ b/doc/PostProcessing.md @@ -465,20 +465,20 @@ There is also a possibility to explicitly change the legend, and line, fill and The specific options are: -* `legend` ~ allows to specify precise location and shape of legend, as well as disable it completely - * `x1`, `y1`, `x2`, `y2` ~ manual coordinates of the legend box, all need to be present for custom legend placement to take account - * `nColumns` ~ number of columns in the legend -* `style` ~ allows to change the style of the line and marker - * `lineWidth` ~ width of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) - * `lineStyle` ~ style of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) - * `lineColor` ~ color of the line, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) - * `markerColor` ~ color of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) - * `markerStyle` ~ style of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) - * `markerSize` ~ size of the marker, float as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) - * `fillColor` ~ color of the fill, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) - * `fillStyle` e~ style of the fill, integer as defined by ROOT [TAttFill class](https://root.cern/doc/master/classTAttFill.html) - -WARNING: Any style parameters specified will override colliding parameters in option. +* `"legend"` ~ allows to specify precise location and shape of legend, as well as disable it completely + * `"x1"`, `"y1"`, `"x2"`, `"y2"` ~ manual coordinates of the legend box, all need to be present for custom legend placement to take account + * `"nColumns"` ~ number of columns in the legend +* `"style"` ~ allows to change the style of the line and marker + * `"lineWidth"` ~ width of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) + * `"lineStyle"` ~ style of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) + * `"lineColor"` ~ color of the line, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) + * `"markerColor"` ~ color of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) + * `"markerStyle"` ~ style of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) + * `"markerSize"` ~ size of the marker, float as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) + * `"fillColor"` ~ color of the fill, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) + * `"fillStyle"` ~ style of the fill, integer as defined by ROOT [TAttFill class](https://root.cern/doc/master/classTAttFill.html) + +WARNING: Any style parameters specified will override colliding parameters in `"option"`. To decide whether plots should be generated during each update or just during finalization, use the boolean flag `"producePlotsOnUpdate"`. From 5f629aae90880b3bebd97a8c35a04c16447d5675 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Tue, 12 Aug 2025 09:50:59 +0200 Subject: [PATCH 17/20] fix: set default coords to -1 --- Framework/include/QualityControl/TrendingTaskConfig.h | 2 +- Framework/src/TrendingTask.cxx | 4 ++-- Framework/src/TrendingTaskConfig.cxx | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Framework/include/QualityControl/TrendingTaskConfig.h b/Framework/include/QualityControl/TrendingTaskConfig.h index 76656c7dd1..2d5b33820f 100644 --- a/Framework/include/QualityControl/TrendingTaskConfig.h +++ b/Framework/include/QualityControl/TrendingTaskConfig.h @@ -65,7 +65,7 @@ struct TrendingTaskConfig : PostProcessingConfig { // legend configuration struct LegendConfig { int nColumns{ 1 }; - float x1{ 0.30f }, y1{ 0.20f }, x2{ 0.55f }, y2{ 0.35f }; // NDC coords + float x1{ -1.f }, y1{ -1.f }, x2{ -1.f }, y2{ -1.f }; // NDC coords }; // this corresponds to one canvas which can include multiple graphs diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index e39466324a..52188516d0 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -347,10 +347,10 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) // Legend TLegend* legend = nullptr; - if (plotConfig.legend.x1 && plotConfig.legend.y1 && plotConfig.legend.x2 && plotConfig.legend.y2) { + if (plotConfig.legend.x1 >= 0 && plotConfig.legend.y1 >= 0 && plotConfig.legend.x2 >= 0 && plotConfig.legend.y2 >= 0) { legend = new TLegend(plotConfig.legend.x1, plotConfig.legend.y1, plotConfig.legend.x2, plotConfig.legend.y2, - /*header=*/nullptr, /*option=*/"NDC"); + nullptr, "NDC"); if (plotConfig.legend.nColumns > 0) { legend->SetNColumns(plotConfig.legend.nColumns); } diff --git a/Framework/src/TrendingTaskConfig.cxx b/Framework/src/TrendingTaskConfig.cxx index 75bb4a0a3b..8b79ced4b6 100644 --- a/Framework/src/TrendingTaskConfig.cxx +++ b/Framework/src/TrendingTaskConfig.cxx @@ -74,10 +74,10 @@ TrendingTaskConfig::TrendingTaskConfig(std::string id, const boost::property_tre LegendConfig leg; leg.nColumns = plotConfig.get("legend.nColumns", 1); - leg.x1 = plotConfig.get("legend.x1", 0.30f); - leg.y1 = plotConfig.get("legend.y1", 0.20f); - leg.x2 = plotConfig.get("legend.x2", 0.55f); - leg.y2 = plotConfig.get("legend.y2", 0.35f); + leg.x1 = plotConfig.get("legend.x1", -1.f); + leg.y1 = plotConfig.get("legend.y1", -1.f); + leg.x2 = plotConfig.get("legend.x2", -1.f); + leg.y2 = plotConfig.get("legend.y2", -1.f); plots.push_back({ plotConfig.get("name"), plotConfig.get("title", ""), From 0bf41ebf70bdd619db71fa7a867b005a2f7b2b24 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Tue, 12 Aug 2025 09:53:33 +0200 Subject: [PATCH 18/20] chore: comment changes --- Framework/src/TrendingTask.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Framework/src/TrendingTask.cxx b/Framework/src/TrendingTask.cxx index 52188516d0..d92e7baba5 100644 --- a/Framework/src/TrendingTask.cxx +++ b/Framework/src/TrendingTask.cxx @@ -434,6 +434,7 @@ TCanvas* TrendingTask::drawPlot(const TrendingTaskConfig::Plot& plotConfig) // QCG doesn't empty the buffers before visualizing the plotConfig, nor does ROOT when saving the file, // so we have to do it here. htemp->BufferEmpty(); + // we keep the pointer to bg histogram for later postprocessing if (!background) { background = htemp; } From 2d87d07ac5a33fec836dadffa6ac6486812a4360 Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Wed, 13 Aug 2025 13:53:03 +0200 Subject: [PATCH 19/20] doc: correct the docs --- doc/PostProcessing.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/doc/PostProcessing.md b/doc/PostProcessing.md index 67002ea91a..addddec862 100644 --- a/doc/PostProcessing.md +++ b/doc/PostProcessing.md @@ -433,7 +433,7 @@ There is also a possibility to explicitly change the legend, and line, fill and { "name": "trend_cycle_duration_ntf_corr", "title": "cycle duration: ns/TF;time;cycle duration [ns/TimeFrames]", - "legend": { "enabled": true, "x1": 0.70, "y1": 0.70, "x2": 0.93, "y2": 0.90, "nColumns": 1 }, + "legend": { "x1": 0.70, "y1": 0.70, "x2": 0.93, "y2": 0.90, "nColumns": 1 }, "graphs": [ { "title": "cycle duration [ns]", @@ -469,17 +469,20 @@ The specific options are: * `"x1"`, `"y1"`, `"x2"`, `"y2"` ~ manual coordinates of the legend box, all need to be present for custom legend placement to take account * `"nColumns"` ~ number of columns in the legend * `"style"` ~ allows to change the style of the line and marker - * `"lineWidth"` ~ width of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) - * `"lineStyle"` ~ style of the line, integer as defined by ROOT [TAttLine class](https://root.cern/doc/master/classTAttLine.html) - * `"lineColor"` ~ color of the line, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) - * `"markerColor"` ~ color of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) - * `"markerStyle"` ~ style of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) - * `"markerSize"` ~ size of the marker, float as defined by ROOT [TAttMarker class](https://root.cern/doc/master/classTAttMarker.html) - * `"fillColor"` ~ color of the fill, an integer as defined by ROOT [TColor class](https://root.cern/doc/master/classTColor.html) - * `"fillStyle"` ~ style of the fill, integer as defined by ROOT [TAttFill class](https://root.cern/doc/master/classTAttFill.html) + * `"lineWidth"` ~ width of the line, integer as defined by ROOT [TAttLine class](https://root.cern.ch/doc/master/classTAttLine.html) + * `"lineStyle"` ~ style of the line, integer as defined by ROOT [TAttLine class](https://root.cern.ch/doc/master/classTAttLine.html) + * `"lineColor"` ~ color of the line, an integer as defined by ROOT [TColor class](https://root.cern.ch/doc/master/classTColor.html) + * `"markerColor"` ~ color of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern.ch/doc/master/classTAttMarker.html) + * `"markerStyle"` ~ style of the marker, integer as defined by ROOT [TAttMarker class](https://root.cern.ch/doc/master/classTAttMarker.html) + * `"markerSize"` ~ size of the marker, float as defined by ROOT [TAttMarker class](https://root.cern.ch/doc/master/classTAttMarker.html) + * `"fillColor"` ~ color of the fill, an integer as defined by ROOT [TColor class](https://root.cern.ch/doc/master/classTColor.html) + * `"fillStyle"` ~ style of the fill, integer as defined by ROOT [TAttFill class](https://root.cern.ch/doc/master/classTAttFill.html) + WARNING: Any style parameters specified will override colliding parameters in `"option"`. +However, lines, fills and markers still have to be enabled in `"option"` to appear (e.g. with `"option" : "LPF"`). + To decide whether plots should be generated during each update or just during finalization, use the boolean flag `"producePlotsOnUpdate"`. From bcadfa3a30f820eaf8adfca7c5f614349b757acd Mon Sep 17 00:00:00 2001 From: mvishiu11 Date: Wed, 13 Aug 2025 14:06:51 +0200 Subject: [PATCH 20/20] fix: remove digit qc task changes --- Modules/FIT/FV0/include/FV0/DigitQcTask.h | 2 -- Modules/FIT/FV0/src/DigitQcTask.cxx | 24 ----------------------- 2 files changed, 26 deletions(-) diff --git a/Modules/FIT/FV0/include/FV0/DigitQcTask.h b/Modules/FIT/FV0/include/FV0/DigitQcTask.h index acd58e097d..091c78ec72 100644 --- a/Modules/FIT/FV0/include/FV0/DigitQcTask.h +++ b/Modules/FIT/FV0/include/FV0/DigitQcTask.h @@ -157,8 +157,6 @@ class DigitQcTask final : public TaskInterface // Objects which will be published std::unique_ptr mHistAmp2Ch; - std::unique_ptr mHistAmpAll; - std::array, sNCHANNELS_FV0_PLUSREF> mHistAmpPerCh; std::unique_ptr mHistTime2Ch; std::unique_ptr mHistEventDensity2Ch; std::unique_ptr mHistChDataBits; diff --git a/Modules/FIT/FV0/src/DigitQcTask.cxx b/Modules/FIT/FV0/src/DigitQcTask.cxx index beeefcc368..29904f1071 100644 --- a/Modules/FIT/FV0/src/DigitQcTask.cxx +++ b/Modules/FIT/FV0/src/DigitQcTask.cxx @@ -187,18 +187,6 @@ void DigitQcTask::initialize(o2::framework::InitContext& /*ctx*/) mHistTime2Ch->SetOption("colz"); mHistAmp2Ch = std::make_unique("AmpPerChannel", "Amplitude vs Channel;Channel;Amp", sNCHANNELS_FV0_PLUSREF, 0, sNCHANNELS_FV0_PLUSREF, 4200, -100, 4100); mHistAmp2Ch->SetOption("colz"); - mHistAmpAll = std::make_unique( - "AmpDistributionAll", - "Amplitude distribution;QTC amp;Counts", - 4096, -0.5, 4095.5); - for (std::size_t ch = 0; ch < sNCHANNELS_FV0_PLUSREF; ++ch) { - mHistAmpPerCh[ch] = std::make_unique( - Form("AmpDistCh%zu", ch), - Form("Amplitude distribution channel %zu;QTC amp;Counts", ch), - 4096, -0.5, 4095.5); - getObjectsManager()->startPublishing(mHistAmpPerCh[ch].get()); - } - getObjectsManager()->startPublishing(mHistAmpAll.get()); mHistBC = std::make_unique("BC", "BC;BC;counts;", sBCperOrbit, 0, sBCperOrbit); mHistChDataBits = std::make_unique("ChannelDataBits", "ChannelData bits per ChannelID;Channel;Bit", sNCHANNELS_FV0_PLUSREF, 0, sNCHANNELS_FV0_PLUSREF, mMapPMbits.size(), 0, mMapPMbits.size()); mHistChDataBits->SetOption("colz"); @@ -396,11 +384,6 @@ void DigitQcTask::startOfActivity(const Activity& activity) ILOG(Debug, Devel) << "startOfActivity" << activity.mId << ENDM; mHistTime2Ch->Reset(); mHistAmp2Ch->Reset(); - mHistAmpAll->Reset(); - for (auto& h : mHistAmpPerCh) { - if (h) - h->Reset(); - } mHistBC->Reset(); mHistChDataBits->Reset(); mHistGateTimeRatio2Ch->Reset(); @@ -500,8 +483,6 @@ void DigitQcTask::monitorData(o2::framework::ProcessingContext& ctx) for (const auto& chData : vecChData) { mHistTime2Ch->Fill(static_cast(chData.ChId), static_cast(chData.CFDTime)); mHistAmp2Ch->Fill(static_cast(chData.ChId), static_cast(chData.QTCAmpl)); - mHistAmpAll->Fill(static_cast(chData.QTCAmpl)); - mHistAmpPerCh[chData.ChId]->Fill(static_cast(chData.QTCAmpl)); mHistEventDensity2Ch->Fill(static_cast(chData.ChId), static_cast(digit.mIntRecord.differenceInBC(mStateLastIR2Ch[chData.ChId]))); mStateLastIR2Ch[chData.ChId] = digit.mIntRecord; mHistChannelID->Fill(chData.ChId); @@ -693,11 +674,6 @@ void DigitQcTask::reset() mHistGateTimeRatio2Ch->Reset(); mHistTime2Ch->Reset(); mHistAmp2Ch->Reset(); - mHistAmpAll->Reset(); - for (auto& h : mHistAmpPerCh) { - if (h) - h->Reset(); - } mHistBC->Reset(); mHistChDataBits->Reset(); mHistCFDEff->Reset();