CbmRoot
Loading...
Searching...
No Matches
CbmQaCheckerHist1DHandler.cxx
Go to the documentation of this file.
1/* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Sergei Zharko [committer] */
4
9
11
13#include "Logger.h"
14#include "TCanvas.h"
15#include "TError.h"
16#include "TFolder.h"
17#include "TGraphAsymmErrors.h"
18#include "TH1.h"
19#include "TLegend.h"
20#include "TMath.h"
21#include "TMultiGraph.h"
22#include "TObject.h"
23#include "TStyle.h"
24
25#include <bitset>
26#include <limits>
27
30
31// ---------------------------------------------------------------------------------------------------------------------
32//
33Hist1DHandler::Hist1DHandler(int iObject, int iFile, int iDataset) : ObjectHandler(iObject, iFile, iDataset, "TH1") {}
34
35// ---------------------------------------------------------------------------------------------------------------------
36//
37
38
39// ---------------------------------------------------------------------------------------------------------------------
40// TODO: move somewhere else (probably unite with the CbmQaCompare in a future merger request)
42{
43 auto* pNumerator = static_cast<const TH1*>(fvpObjects[iVersion]);
44 auto* pDenominator = static_cast<const TH1*>(fvpObjects[fpObjDB->GetDefaultID()]);
45 if (pNumerator->GetNbinsX() != pDenominator->GetNbinsX() || pNumerator->GetNbinsY() != pDenominator->GetNbinsY()
46 || pNumerator->GetNbinsZ() != pDenominator->GetNbinsZ()) {
48 }
49
50 bool bCompareExact{fCmpBits[static_cast<uint8_t>(ECmpMethod::Exact)]};
51 bool bCompareRatio{fCmpBits[static_cast<uint8_t>(ECmpMethod::Ratio)]};
52 bool bCompareChi2{fCmpBits[static_cast<uint8_t>(ECmpMethod::Chi2)]};
53
54 bool bEqualExactly{true};
55 bool bEqualByRatio{true};
56
57 double ratioLo{1.};
58 double ratioUp{1.};
59
60 if (bCompareExact || bCompareRatio) {
61 bool bRatioMayBeWrong =
62 false; // handling a case, when the histograms contain different empty bins, but the ratio remains 1
63 for (int iBinX{0}; iBinX <= pNumerator->GetNbinsX(); ++iBinX) {
64 for (int iBinY{0}; iBinY <= pNumerator->GetNbinsY(); ++iBinY) {
65 for (int iBinZ{0}; iBinZ <= pNumerator->GetNbinsZ(); ++iBinZ) {
66 auto numBinContent{pNumerator->GetBinContent(iBinX, iBinY, iBinZ)};
67 auto denBinContent{pDenominator->GetBinContent(iBinX, iBinY, iBinZ)};
68 double ratio{static_cast<double>(numBinContent) / denBinContent};
69 // Check bin content
70 if (!TMath::IsNaN(numBinContent) && !TMath::IsNaN(denBinContent)) {
71 if (numBinContent != denBinContent) {
72 bEqualExactly = false;
73 if (bCompareRatio) {
74 bool numEmpty{numBinContent < 1.e-4};
75 bool denEmpty{denBinContent < 1.e-4};
76 if (numEmpty || denEmpty) {
77 bRatioMayBeWrong &= (!numEmpty || !denEmpty);
78 continue; // Ignoring empty bin in ratio estimation (but only, if it is empty in both histograms)
79 }
80 ratioLo = std::min(ratioLo, ratio);
81 ratioUp = std::max(ratioUp, ratio);
82 }
83 }
84 }
85 else {
86 if (TMath::IsNaN(numBinContent) != TMath::IsNaN(denBinContent)) {
87 bEqualExactly = false;
88 }
89 }
90 auto numBinError{pNumerator->GetBinError(iBinX, iBinY, iBinZ)};
91 auto denBinError{pDenominator->GetBinError(iBinX, iBinY, iBinZ)};
92 // Check bin error
93 if (!TMath::IsNaN(numBinError) && !TMath::IsNaN(denBinError)) {
94 if (numBinError != denBinError) {
95 bEqualExactly = false;
96 }
97 }
98 else {
99 if (TMath::IsNaN(numBinError) != TMath::IsNaN(denBinError)) {
100 bEqualExactly = false;
101 }
102 }
103 }
104 }
105 }
106
107 if (bCompareExact && bEqualExactly) {
108 return ECmpInference::StronglyEqual; // chi2 and ratio tests can be ignored
109 }
110
111 if (bRatioMayBeWrong && (std::fabs(1 - 1.e-4) < ratioLo) && std::fabs(1 + 1.e+4) > ratioUp) {
112 LOG(warn) << "Hist1DHandler::Compare: file " << fpObjDB->GetInputFileName(fDatasetID, fFileID, 0)
113 << ", object: " << pNumerator->GetName()
114 << " has a ratio equal to 1., but some of bins are still different "
115 << "(empty/non-empty bin case)";
116 }
117
118 if (bCompareRatio) {
119 bEqualByRatio = (ratioLo >= fpObjDB->GetRatioRangeMin() && ratioUp <= fpObjDB->GetRatioRangeMax());
120 }
121 }
122
123 bool bEqualByChi2{true};
124 double pVal{0.};
125 if (bCompareChi2) { // perform only, if the histograms were different
126 auto currErrorLevel{gErrorIgnoreLevel};
127 gErrorIgnoreLevel = kFatal;
128 double chi2{0.};
129 int ndf{0};
130 int igood{0};
131 pVal = pDenominator->Chi2TestX(pNumerator, chi2, ndf, igood);
132 bEqualByChi2 = (pVal >= fpObjDB->GetPvalThreshold());
133 gErrorIgnoreLevel = currErrorLevel;
134 }
135
136 // Do not account for method, if it was not required
137 // strongly eq = (!bCmp_0 v bEq_0) ^ (!bCmp_1 v bEq_1) ^ .. ^ (!bCmp_N v bEq_N)
138 // weakly eq = (!bCmp_0 v bEq_0) v (!bCmp_1 v bEq_1) v .. v (!bCmp_N v bEq_N)
139 bEqualExactly = !bCompareExact || bEqualExactly;
140 bEqualByRatio = !bCompareRatio || bEqualByRatio;
141 bEqualByChi2 = !bCompareChi2 || bEqualByChi2;
142 bool bEqualStrongly = bEqualExactly && bEqualByRatio && bEqualByChi2;
143 bool bEqualWeakly = bEqualExactly || bEqualByRatio || bEqualByChi2;
144 ECmpInference res = bEqualStrongly ? ECmpInference::StronglyEqual
146
147 if (ECmpInference::StronglyEqual != res) {
148 auto ResultMsg = [](bool flag) -> std::string {
149 constexpr const char* kEqual = "\e[1;32mconsistent\e[0m";
150 constexpr const char* kDiff = "\e[1;31minconsistent\e[0m";
151 return flag ? kEqual : kDiff;
152 };
153 std::stringstream msg;
154 msg << "Found mismatch: file: " << fpObjDB->GetInputFileName(iVersion, fFileID, fDatasetID)
155 << ", object: " << fpObjDB->GetObject(fFileID, fObjectID) << "\n";
156 if (bCompareExact) {
157 msg << "\texact -> " << ResultMsg(bEqualExactly) << '\n';
158 }
159 if (bCompareRatio) {
160 msg << "\tratio -> " << ResultMsg(bEqualByRatio) << "(lo: " << ratioLo << ", up: " << ratioUp << ")\n";
161 }
162 if (bCompareChi2) {
163 msg << "\tchi2 -> " << ResultMsg(bEqualByChi2) << "(p-val: " << pVal << ")\n";
164 }
165 msg << "Inference: " << ToString(res);
166
167 LOG(info) << msg.str();
168 }
169
170 return res;
171}
172
173// ---------------------------------------------------------------------------------------------------------------------
174//
176{
177 int nVersions = fpObjDB->GetNofVersions();
178 int iDef = fpObjDB->GetDefaultID();
179
180 // ----- Option parsing
181 std::string sOption = opt;
182 for (auto& ch : sOption) {
183 ch = std::tolower(ch);
184 }
185 bool bDrawRatio = sOption.find("r") != std::string::npos;
186 bool bDrawDiff = sOption.find("d") != std::string::npos;
187 int nPads = static_cast<int>(bDrawRatio) + static_cast<int>(bDrawDiff) + 1;
188
189 // ----- Canvas: comparison of original histograms, ratios and subtractions
190 std::string sCanvName = fsBaseName + "_cmp_canvas";
191 std::string sCanvTitle = "Comparison result for \"" + fsBaseName + "\"";
192 fpCanvas = std::make_shared<TCanvas>(sCanvName.data(), sCanvTitle.data(), 500 * nPads, 500);
193 fpCanvas->cd();
194
195 TPad* pPadOrig = nullptr;
196 TPad* pPadRatio = nullptr;
197 TPad* pPadDiff = nullptr;
198
199 double xPadSize = 1.0 / nPads;
200 double xNext = 0.;
201
202 pPadOrig = new TPad("p1", "p1", 0.0, 0.0, xPadSize, 1.0);
203 pPadOrig->SetMargin(0.20, 0.05, 0.20, 0.10);
204 fpCanvas->cd();
205 xNext += xPadSize;
206
207 if (bDrawRatio) {
208 pPadRatio = new TPad("p2", "p2", xNext, 0.0, xNext + xPadSize, 1.0);
209 pPadRatio->SetMargin(0.20, 0.05, 0.20, 0.10);
210 fpCanvas->cd();
211 xNext += xPadSize;
212 }
213
214 if (bDrawDiff) {
215 pPadDiff = new TPad("p3", "p3", xNext, 0.0, xNext + xPadSize, 1.0);
216 pPadDiff->SetMargin(0.20, 0.05, 0.20, 0.10);
217 fpCanvas->cd();
218 xNext += xPadSize;
219 }
220
221 // Title definitions
222 const char* title = fvpObjects[0]->GetTitle();
223 const char* titleRatio = Form("Ratio to %s", fpObjDB->GetVersionLabel(iDef).data());
224 const char* titleDiff = Form("Difference from %s", fpObjDB->GetVersionLabel(iDef).data());
225 const char* xAxisTitle = static_cast<TH1*>(fvpObjects[0])->GetXaxis()->GetTitle();
226 const char* yAxisTitle = static_cast<TH1*>(fvpObjects[0])->GetYaxis()->GetTitle();
227
228 // Original histograms
229 pPadOrig->cd();
230 auto* pMultiGraphOrig = new TMultiGraph(fsBaseName.data(), title);
231 for (int iV = 0; iV < nVersions; ++iV) {
232 auto* pCopy = new TGraphAsymmErrors((TH1*) fvpObjects[iV]);
233 pCopy->SetMarkerStyle(20);
234 pCopy->SetTitle(fpObjDB->GetVersionLabel(iV).data());
235 pMultiGraphOrig->Add(pCopy, "P");
236 }
237 pMultiGraphOrig->GetXaxis()->SetTitle(xAxisTitle);
238 pMultiGraphOrig->GetYaxis()->SetTitle(yAxisTitle);
239 pMultiGraphOrig->Draw("A pmc plc");
240 pPadOrig->BuildLegend();
241
242 auto* pDefault = static_cast<TH1*>(fvpObjects[iDef]);
243
244 // Histogram ratios to default
245 if (bDrawRatio) {
246 pPadRatio->cd();
247 auto* pMultiGraphRatio = new TMultiGraph((fsBaseName + "_ratio").data(), titleRatio);
248 for (int iV = 0; iV < nVersions; ++iV) {
249 if (iV == iDef) {
250 continue;
251 }
252 auto* pRatio = static_cast<TH1*>(fvpObjects[iV]->Clone());
253 pRatio->SetDirectory(fpOutDir);
254 auto currErrorLevel = gErrorIgnoreLevel;
255 gErrorIgnoreLevel = kError;
256 auto* pCopy = new TGraphAsymmErrors(pRatio, pDefault, "pois");
257 gErrorIgnoreLevel = currErrorLevel;
258 pCopy->SetMarkerStyle(20);
259 pMultiGraphRatio->Add(pCopy, "P");
260 if (pRatio) {
261 delete pRatio;
262 pRatio = nullptr;
263 }
264 }
265 pMultiGraphRatio->GetYaxis()->SetTitle("ratio");
266 pMultiGraphRatio->GetXaxis()->SetTitle(xAxisTitle);
267 pMultiGraphRatio->Draw("A pmc plc");
268 }
269
270 // Histogram differences to default
271 if (bDrawDiff) {
272 pPadDiff->cd();
273 auto* pMultiGraphDiff = new TMultiGraph((fsBaseName + "_diff").data(), titleDiff);
274 for (int iV = 0; iV < nVersions; ++iV) {
275 if (iV == iDef) {
276 continue;
277 }
278 auto* pDiff = static_cast<TH1*>(fvpObjects[iV]->Clone());
279 pDiff->SetDirectory(fpOutDir);
280 pDiff->Add(pDefault, -1.);
281 auto* pCopy = new TGraphAsymmErrors(pDiff);
282 pCopy->SetMarkerStyle(20);
283 pMultiGraphDiff->Add(pCopy, "P");
284 if (pDiff) {
285 delete pDiff;
286 pDiff = nullptr;
287 }
288 }
289 pMultiGraphDiff->GetYaxis()->SetTitle("difference");
290 pMultiGraphDiff->GetXaxis()->SetTitle(xAxisTitle);
291 pMultiGraphDiff->Draw("A pmc plc");
292 }
293 fpCanvas->cd();
294 pPadOrig->Draw();
295 if (pPadRatio) pPadRatio->Draw();
296 if (pPadDiff) pPadDiff->Draw();
297}
298
Handler class for 1D-histograms (including TProfile objects) (declaration)
Common definitions for QA-Checker framework.
ECmpInference Compare(int iVersion) const override
Compares objects to default.
void CreateCanvases(Option_t *opt="") override
Creates object comparison canvas.
Hist1DHandler(int iObject, int iFile, int iDataset)
Constructor.
Base abstract class for object handler.
std::string fsBaseName
Base names of the object.
std::shared_ptr< TCanvas > fpCanvas
Comparison canvas.
std::shared_ptr< ObjectDB > fpObjDB
Pointer to object database.
std::vector< TNamed * > fvpObjects
Vector of objects.
TDirectory * fpOutDir
Pointer to directory.
std::bitset< static_cast< size_t >(ECmpMethod::END)> fCmpBits
Bitset for comparison methods.
ECmpInference
The object comparison inference.
@ Different
Neither of the comparison methods showed equality.
@ WeaklyEqual
At least one of the comparison methods showed equality.
@ StronglyEqual
All the comparison methods gave equality.
@ Exact
exact equality (point-by-point, error-by-error)
@ Chi2
equality within a chi2 hypothesis test
@ Ratio
ratio equality (max and min ratios do not exceed a required range)
std::string ToString(ECmpInference inference)
String representation of the ECmpInference enum.