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