ATLAS Offline Software
Loading...
Searching...
No Matches
TRootCompare.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
11
13
14#include "TClass.h"
15#include "TKey.h"
16#include "TH1.h"
17#include "TEfficiency.h"
18#include "TCanvas.h"
19#include "TVirtualPad.h"
20#include "TPaveStats.h"
21#include "TError.h"
22#include "TText.h"
23#include "TCollection.h"
24#include "THashList.h"
25#include "TAxis.h"
26
27#include <iostream>
28#include <iomanip>
29#include <algorithm>
30#include <memory>
31#include <math.h>
32
33using namespace std;
34
36 m_refFile(0),
37 m_outFile(0),
38 m_refRootDir(""),
39 m_psFile(""),
40 m_can(0),
41 m_alg(BIN),
42 m_threshold(1e-6),
43 m_histMatch(0),
44 m_histTotal(0),
46 m_drawNormalized(kFALSE),
47 m_drawDiff(kTRUE)
48{
49}
50
55
57{
58 m_histMatch = 0;
59 m_histTotal = 0;
60 m_histMissing = 0;
61
62 Int_t canvasWidth = 1000;
63 Int_t canvasHeight = 580;
64
65 if (!m_drawDiff) canvasWidth = canvasHeight;
66
67 m_can = new TCanvas("can","can",canvasWidth,canvasHeight);
68 if (m_psFile!="") printCanvas(m_psFile+"[");
69}
70
72{
73 cout << endl;
74 cout << "Summary:" << endl;
75 cout << "========" << endl;
76 cout << "Total histograms compared: " << m_histTotal << endl;
77 cout << "Missing references: " << m_histMissing << endl;
78 cout << "Matching histograms: " << m_histMatch << endl;
79 cout << "Different histograms: " << m_histTotal-m_histMatch << endl;
80 cout << "Ignored histograms: " << m_skippedObjects.size() << endl;
81
83 cout << "List of different histograms:" << endl;
84 vector<string>::iterator iter;
85 int i = 0;
86 for (iter=m_noMatch.begin(); iter!=m_noMatch.end(); ++iter) {
87 i++;
88 cout << setw(2) << i << ") " << *iter << endl;
89 }
90 }
91
92 if (verbose() && !m_skippedObjects.empty()) {
93 cout << endl << "List of ignored histograms:" << endl;
94 vector<string>::iterator iter;
95 int i = 0;
96 for (iter=m_skippedObjects.begin(); iter!=m_skippedObjects.end(); ++iter) {
97 i++;
98 cout << setw(2) << i << ") " << *iter << endl;
99 }
100 }
101 cout << endl;
102
103 if (m_outFile) {
104 cout << "Comparison results written to " << m_outFile->GetName() << endl;
105 delete m_outFile;
106 }
107
108 if (m_psFile!="") {
110 cout << "Comparison results written to " << m_psFile << endl;
111 }
112
113 delete m_can;
114}
115
116void TRootCompare::processKey(TDirectory& dir, TKey& key)
117{
118 dir.cd();
119
120 if (m_refFile==0) return;
121
122 if (TString(key.GetName()).Contains("/")) {
123 cout << "IGNORE: " << key.GetName() << " contains '/'" << endl;
124 return;
125 }
126
127 std::unique_ptr<TObject> obj(key.ReadObj());
128
129 // Extract directory name
130 TString dirName(dir.GetPath());
131 dirName.Replace(0,dirName.First(":")+2,0);
132 if (rootDir()!="") dirName.ReplaceAll(rootDir(),m_refRootDir);
133 else dirName = m_refRootDir+"/"+dirName;
134 TString keyPath(dirName+"/"+key.GetName());
135
136 if (!m_refFile->cd(dirName)) { // could not cd() into directory of histogram
138 return;
139 }
140
141 TObject* refObj = m_refFile->Get(keyPath);
142 if (!refObj) { // histogram not found
143 cout << "Cannot find " << keyPath << " in reference file" << endl;
145 return;
146 }
147
148 if (obj->Class()!=refObj->Class()) { // class types do not agree
149 cout << key.GetName() << " is of different type in file and reference file." << endl;
150 return;
151 }
152
153 if (obj->IsA()->InheritsFrom("TH1")) {
154 TH1& h = *static_cast<TH1*>(obj.get());
155 TH1& href = *static_cast<TH1*>(refObj);
156
157 // For alphanumeric axes, sort and deflate
158 if (m_sortLabels) {
160 sortAndDeflate(href);
161 }
162
163 Bool_t match = compareHist(h,href);
164 m_histTotal++;
165 if (match) {
166 m_histMatch++;
167 }
168 else { // histograms do not match
169 m_noMatch.push_back(keyPath.Data());
170
171 // Skip drawing if no output was requested
172 if (!m_outFile && m_psFile.Length()==0) return;
173
174 m_can->Clear();
175 m_can->Divide(2,1);
176 m_can->cd(1)->SetPad(0,1,1,0.90);
177 m_can->cd(2)->SetPad(0,0.90,1,0);
178 TVirtualPad* pad = m_can->cd(2);
179
180 if (m_drawDiff) pad->Divide(2,1);
181 m_can->SetName(h.GetName());
182 m_can->SetTitle(h.GetTitle());
183 // Overlayed
184 pad->cd(1);
185 if (m_drawNormalized) {
186 if (href.Integral()) href.Scale(1/href.Integral());
187 if (h.Integral()) h.Scale(1/h.Integral());
188 }
189
190 Double_t ymax = 1.05*max(h.GetMaximum(),href.GetMaximum());
191 h.SetMaximum(ymax);
192 h.SetLineColor(kBlue);
193 h.Draw();
194 m_can->Update();
195 TPaveStats* st1 = (TPaveStats*)gPad->GetPrimitive("stats");
196 if (st1) {
197 st1->SetName("stats1");
198 st1->SetLineColor(kBlue);
199 }
200
201 href.SetLineColor(kRed);
202 href.Draw("sames");
203 m_can->Update();
204 TPaveStats* st2 = (TPaveStats*)gPad->GetPrimitive("stats");
205 if (st1 && st2) {
206 // Move stat box
207 Double_t x1 = st1->GetX1NDC()-0.01;
208 st2->SetName("stats2");
209 Double_t w = st2->GetX2NDC()-st2->GetX1NDC();
210 st2->SetX1NDC(x1-w);
211 st2->SetX2NDC(x1);
212 st2->SetLineColor(kRed);
213 m_can->Modified();
214 }
215
216 TH1* hdiff = 0;
217 if (m_drawDiff) {
218 hdiff = (TH1*)h.Clone();
219 // Too many problems with difference of 2D histograms
220 if (hdiff->GetDimension()==1 &&
221 hdiff->GetNbinsX()==href.GetNbinsX()) {
222 // Difference
223 pad->cd(2);
224 hdiff->SetName(TString(href.GetName())+" (diff)");
225 hdiff->SetTitle(TString(href.GetTitle())+" (diff)");
226 hdiff->SetLineColor(kBlack);
227 hdiff->Add(&href,-1);
228 hdiff->Draw();
229 TPaveStats* st = (TPaveStats*)gPad->GetPrimitive("stats1");
230 if (st) st->SetLineColor(kBlack);
231 }
232 if(hdiff->GetDimension()==2 &&
233 hdiff->GetNbinsX()==href.GetNbinsX() &&
234 hdiff->GetNbinsY()==href.GetNbinsY()) {
235 pad->cd(2);
236 hdiff->SetName(TString(href.GetName())+" (diff)");
237 hdiff->SetTitle(TString(href.GetTitle())+" (diff)");
238 hdiff->SetLineColor(kBlack);
239 hdiff->Add(&href,-1);
240 if(hdiff->GetXaxis()->GetLabels()!=0 && hdiff->GetNbinsX()>100) {
241 TH1 * hdiffred = (TH1*)hdiff->Clone();
242 hdiffred->GetXaxis()->GetLabels()->Delete();
243 hdiffred->Reset();
244 hdiffred->SetName(TString(href.GetName())+" (diff reduced)");
245 hdiffred->SetTitle(TString(href.GetTitle())+" (diff reduced)");
246 int targetbin=1;
247 for(int x=1; x<=hdiff->GetNbinsX(); ++x) {
248 bool isEmpty(true);
249 for(int y=1;y<=hdiff->GetNbinsY();++y) {
250 if(hdiff->GetBinContent(x,y)!=0) { isEmpty=false; break; }
251 }
252 if(!isEmpty) {
253 for(int y=1;y<=hdiff->GetNbinsY();++y) {
254 if(hdiff->GetBinContent(x,y)!=0)
255 hdiffred->SetBinContent(targetbin,y,hdiff->GetBinContent(x,y));
256 }
257 hdiffred->GetXaxis()->SetBinLabel(targetbin,hdiff->GetXaxis()->GetBinLabel(x));
258 targetbin++;
259 }
260 }
261 hdiffred->LabelsDeflate();
262 hdiffred->Draw("text");
263 } else {
264 hdiff->Draw("text");
265 }
266 TPaveStats* st = (TPaveStats*)gPad->GetPrimitive("stats1");
267 if (st) st->SetLineColor(kBlack);
268 }
269 }
270
271 // Some more cosmetics before saving to ps file
272 m_can->cd(0);
273 TText text;
274 text.SetTextSize(0.03);
275 text.SetTextAlign(22);
276 TString page("page ");
277 page += m_noMatch.size();
278 text.DrawTextNDC(0.5,0.03,page);
279
280 TString title(dir.GetName());
281 title+="/";
282 title+=href.GetName();
283 text.DrawTextNDC(0.5,0.99,title);
284
285 const int maxchars = 120; // max #chars for title
286 if (m_file) {
287 text.SetTextColor(kBlue);
288 string s(m_file->GetName());
289 text.DrawTextNDC(0.5,0.93,s.substr(max(0,int(s.size()-maxchars))).c_str());
290 }
291 if (m_refFile) {
292 text.SetTextColor(kRed);
293 string s(m_refFile->GetName());
294 text.DrawTextNDC(0.5,0.96,s.substr(max(0,int(s.size()-maxchars))).c_str());
295 }
296
297 if (m_psFile!="") printCanvas(m_psFile);
298
299 // Save canvas to root file
300 if (m_outFile) {
301 createDirectory(m_outFile,dirName); // now we are in dirName
302 m_can->Write();
303 }
304
305 if (hdiff) delete hdiff;
306 }
307 }
308 else if (obj->IsA()->InheritsFrom("TEfficiency")) {
309 auto h = static_cast<TEfficiency*>(obj.get());
310 auto href = static_cast<TEfficiency*>(refObj);
311 Bool_t match = compareHist(*h->GetTotalHistogram(),*href->GetTotalHistogram()) &&
312 compareHist(*h->GetPassedHistogram(),*href->GetPassedHistogram());
313 m_histTotal++;
314
315 // We only count (mis)matches but do not draw the difference for TEfficiency (yet)
316 if (match) m_histMatch++;
317 else m_noMatch.push_back(keyPath.Data());
318 }
319}
320
321
322Bool_t TRootCompare::setReferenceFile(const char* filename,
323 const char* baseDir)
324{
325 if (filename==0) {
326 cout << "Invalid file name" << endl;
327 return kFALSE;
328 }
329
330 m_refFile = new TFile(filename);
331 if (m_refFile->IsZombie()) {
332 cout << "Cannot open reference file " << filename << endl;
333 delete m_refFile;
334 m_refFile = 0;
335 return kFALSE;
336 }
337
338 m_refRootDir = baseDir;
339 return kTRUE;
340}
341
342
343Bool_t TRootCompare::setOutputFile(const char* filename)
344{
345 if (filename==0) {
346 cout << "Invalid file name" << endl;
347 return kFALSE;
348 }
349
350 m_outFile = new TFile(filename,"recreate");
351 if (m_outFile->IsZombie()) {
352 cout << "Cannot open file " << filename << endl;
353 delete m_outFile;
354 m_outFile = 0;
355 return kFALSE;
356 }
357 return kTRUE;
358}
359
360Bool_t TRootCompare::setPsFile(const char* filename)
361{
362 if (filename==0) {
363 cout << "Invalid file name" << endl;
364 return kFALSE;
365 }
366 m_psFile = filename;
367 return kTRUE;
368}
369
370void TRootCompare::printCanvas(const char* filename)
371{
372 if (m_can==0) return;
373 if (filename==0) return;
374
375 if (TString(filename).EndsWith(".ps"))
376 m_can->Print(filename,"Landscape");
377 else
378 m_can->Print(filename);
379}
380
381
382Bool_t TRootCompare::compareHist(const TH1& h, const TH1& href)
383{
384 Bool_t result = kTRUE;
385
386 if (verbose()) {
387 cout << "Comparing " << h.GetName() << " using ";
388 }
389
391 if (verbose()) cout << "BIN: ";
392
393 if (h.GetNbinsX()!=href.GetNbinsX() ||
394 h.GetNbinsY()!=href.GetNbinsY() ||
395 h.GetNbinsZ()!=href.GetNbinsZ()) {
396 cout << h.GetName() << " has different number of bins: ("
397 << h.GetNbinsX() << "," << h.GetNbinsY() << "," << h.GetNbinsZ() << ") vs ("
398 << href.GetNbinsX() << "," << href.GetNbinsY() << "," << href.GetNbinsZ() << ")" << endl;
399 }
400
401 // This will work for histograms of all dimensions
402 for (Int_t z=1; z<=h.GetNbinsZ() && result; z++) {
403 for (Int_t y=1; y<=h.GetNbinsY() && result; y++) {
404 for (Int_t x=1; x<=h.GetNbinsX() && result; x++) {
405 Double_t binDiff = fabs(h.GetBinContent(x,y,z)-href.GetBinContent(x,y,z));
406 if (binDiff>m_threshold) {
407 result = kFALSE;
408 }
409 }
410 }
411 }
412 }
413 else if (m_alg==TRootCompare::AXIS) {
414 if (verbose()) cout << "AXIS: ";
415 const TAxis *xa(h.GetXaxis()), *xaref(href.GetXaxis());
416 const TAxis *ya(h.GetXaxis()), *yaref(href.GetXaxis());
417 const TAxis *za(h.GetXaxis()), *zaref(href.GetXaxis());
418 if( xa->GetNbins() != xaref->GetNbins() ) result = kFALSE;
419 if( result && (ya->GetNbins() != yaref->GetNbins()) ) result = kFALSE;
420 if( result && (za->GetNbins() != zaref->GetNbins()) ) result = kFALSE;
421 if( result && (fabs( xa->GetBinLowEdge(0) - xaref->GetBinLowEdge(0) ) > m_threshold ) ) result = kFALSE;
422 if( result && (fabs( ya->GetBinLowEdge(0) - yaref->GetBinLowEdge(0) ) > m_threshold ) ) result = kFALSE;
423 if( result && (fabs( za->GetBinLowEdge(0) - zaref->GetBinLowEdge(0) ) > m_threshold ) ) result = kFALSE;
424 if( result ) for (Int_t i=0; i<=xa->GetNbins() && result; i++)
425 if( fabs (xa->GetBinUpEdge(i) - xaref->GetBinUpEdge(i)) > m_threshold ) result = kFALSE;
426 if( result ) for (Int_t i=0; i<=ya->GetNbins() && result; i++)
427 if( fabs (ya->GetBinUpEdge(i) - yaref->GetBinUpEdge(i)) > m_threshold ) result = kFALSE;
428 if( result ) for (Int_t i=0; i<=za->GetNbins() && result; i++)
429 if( fabs (za->GetBinUpEdge(i) - zaref->GetBinUpEdge(i)) > m_threshold ) result = kFALSE;
430 }
431 else if (m_alg==TRootCompare::CHI2) {
432 if (verbose()) cout << "CHI2: ";
433
434 // Don't compare empty histograms
435 if (h.GetEntries()==0 && href.GetEntries()==0) result = kTRUE; // both empty
436 else if (h.Integral()==0 && href.Integral()==0) result = kTRUE; // both empty
437 else if (h.Integral()*href.Integral()==0) result = kFALSE; // one empty
438 else {
439 Double_t chi2;
440 Int_t igood;
441 Int_t ndf;
442 Double_t p = h.Chi2TestX(&href,chi2,ndf,igood);
443 // this is because of a bug in root
444 if (ndf==0) result = kTRUE;
445 else if (p>m_threshold) result = kTRUE;
446 else result = kFALSE;
447 }
448 }
449 else {
450 cout << "ERROR: Invalid algorithm." << endl;
451 }
452
453 if (verbose()) cout << result << endl;
454 return result;
455}
456
457// sort histogram axes and deflate
459{
460 if (h.GetXaxis()->IsAlphanumeric()) {
461 h.GetXaxis()->LabelsOption("a");
462 h.LabelsDeflate("X");
463 }
464 if (h.GetYaxis()->IsAlphanumeric()) {
465 h.GetYaxis()->LabelsOption("a");
466 h.LabelsDeflate("Y");
467 }
468}
469
470// Create all directories in dirpath
471void TRootCompare::createDirectory(TFile* f, const char* dirpath)
472{
473 if ((f==0) || (dirpath==0)) return;
474
475 f->cd();
476 TString s(dirpath);
477 TObjArray* a = s.Tokenize("/");
478 for (int i=0; i<a->GetEntries(); i++) {
479 const char* dir = a->At(i)->GetName();
480 if (gDirectory->GetDirectory(dir)==0) gDirectory->mkdir(dir); // create if it doesn't exist
481 gDirectory->cd(dir);
482 }
483
484}
static Double_t a
TRootCompare class.
#define y
#define x
#define z
#define max(a, b)
Definition cfImp.cxx:41
TString rootDir() const
Current directory.
Definition TFileLooper.h:86
std::vector< std::string > m_skippedObjects
Definition TFileLooper.h:99
TFile * m_file
Definition TFileLooper.h:89
Bool_t verbose() const
Query verbose mode.
Definition TFileLooper.h:80
std::vector< std::string > m_noMatch
TString m_refRootDir
void createDirectory(TFile *f, const char *dirpath)
void printCanvas(const char *filename)
virtual void processKey(TDirectory &dir, TKey &key)
Method called for every key.
TFile * m_outFile
Bool_t compareHist(const TH1 &h, const TH1 &href)
Int_t m_histTotal
void sortAndDeflate(TH1 &h)
TFile * m_refFile
virtual void beginJob()
Bool_t m_sortLabels
TCanvas * m_can
Double_t m_threshold
Bool_t m_drawNormalized
Bool_t setPsFile(const char *filename)
Int_t m_histMatch
virtual void endJob()
virtual ~TRootCompare()
Bool_t setReferenceFile(const char *filename, const char *baseDir=0)
Bool_t setOutputFile(const char *filename)
Bool_t m_drawDiff
TString m_psFile
Int_t m_histMissing
double chi2(TH1 *h0, TH1 *h1)
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition hcg.cxx:357
double ymax
Definition listroot.cxx:64
STL namespace.