ATLAS Offline Software
TRootCompare.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
3 */
4 
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 
33 using 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),
45  m_histMissing(0),
46  m_drawNormalized(kFALSE),
47  m_drawDiff(kTRUE)
48 {
49 }
50 
52 {
53  delete m_refFile;
54 }
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 
82  if (m_histMatch!=m_histTotal) {
83  cout << "List of different histograms:" << endl;
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;
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!="") {
109  printCanvas(m_psFile+"]");
110  cout << "Comparison results written to " << m_psFile << endl;
111  }
112 
113  delete m_can;
114 }
115 
116 void 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
137  m_histMissing++;
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;
144  m_histMissing++;
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 = *((TH1*)obj.get());
155  TH1& href = *((TH1*)refObj);
156 
157  // For alphanumeric axes, sort and deflate
158  if (m_sortLabels) {
159  sortAndDeflate(h);
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 = (TEfficiency*)obj.get();
310  auto href = (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 
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 
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 
361 {
362  if (filename==0) {
363  cout << "Invalid file name" << endl;
364  return kFALSE;
365  }
366  m_psFile = filename;
367  return kTRUE;
368 }
369 
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 
382 Bool_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 
390  if (m_alg==TRootCompare::BIN) {
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
471 void 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 }
xAOD::iterator
JetConstituentVector::iterator iterator
Definition: JetConstituentVector.cxx:68
TRootCompare.h
TRootCompare class.
covarianceTool.ndf
ndf
Definition: covarianceTool.py:678
plotBeamSpotCompare.x1
x1
Definition: plotBeamSpotCompare.py:216
python.CaloRecoConfig.f
f
Definition: CaloRecoConfig.py:127
python.SystemOfUnits.s
int s
Definition: SystemOfUnits.py:131
get_generator_info.result
result
Definition: get_generator_info.py:21
python.PerfMonSerializer.p
def p
Definition: PerfMonSerializer.py:743
max
#define max(a, b)
Definition: cfImp.cxx:41
TFileLooper::verbose
Bool_t verbose() const
Query verbose mode.
Definition: TFileLooper.h:80
TRootCompare::m_alg
ALG m_alg
Definition: TRootCompare.h:67
TRootCompare::TRootCompare
TRootCompare()
Definition: TRootCompare.cxx:35
TFileLooper::m_skippedObjects
std::vector< std::string > m_skippedObjects
Definition: TFileLooper.h:99
TRootCompare::setReferenceFile
Bool_t setReferenceFile(const char *filename, const char *baseDir=0)
Definition: TRootCompare.cxx:322
TRootCompare::m_sortLabels
Bool_t m_sortLabels
Definition: TRootCompare.h:74
TRootCompare::printCanvas
void printCanvas(const char *filename)
Definition: TRootCompare.cxx:370
TRootCompare::m_drawDiff
Bool_t m_drawDiff
Definition: TRootCompare.h:73
TRootCompare::CHI2
@ CHI2
Definition: TRootCompare.h:30
DataModelTestDataCommonDict::xa
std::vector< DMTest::B > xa
Definition: DataModelTestDataCommonDict.h:43
TRootCompare::endJob
virtual void endJob()
Definition: TRootCompare.cxx:71
TRootCompare::setPsFile
Bool_t setPsFile(const char *filename)
Definition: TRootCompare.cxx:360
TRootCompare::processKey
virtual void processKey(TDirectory &dir, TKey &key)
Method called for every key.
Definition: TRootCompare.cxx:116
x
#define x
TRootCompare::m_histTotal
Int_t m_histTotal
Definition: TRootCompare.h:70
TRootCompare::setOutputFile
Bool_t setOutputFile(const char *filename)
Definition: TRootCompare.cxx:343
ParseInputs.gDirectory
gDirectory
Definition: Final2012/ParseInputs.py:133
lumiFormat.i
int i
Definition: lumiFormat.py:92
z
#define z
h
covarianceTool.title
title
Definition: covarianceTool.py:542
TRootCompare::m_psFile
TString m_psFile
Definition: TRootCompare.h:64
TRootCompare::m_histMissing
Int_t m_histMissing
Definition: TRootCompare.h:71
chi2
double chi2(TH1 *h0, TH1 *h1)
Definition: comparitor.cxx:522
TRootCompare::sortAndDeflate
void sortAndDeflate(TH1 &h)
Definition: TRootCompare.cxx:458
TRootCompare::m_histMatch
Int_t m_histMatch
Definition: TRootCompare.h:69
beamspotman.dir
string dir
Definition: beamspotman.py:623
TRootCompare::createDirectory
void createDirectory(TFile *f, const char *dirpath)
Definition: TRootCompare.cxx:471
TFileLooper::rootDir
TString rootDir() const
Current directory.
Definition: TFileLooper.h:86
TRootCompare::m_outFile
TFile * m_outFile
Definition: TRootCompare.h:62
TFileLooper::m_file
TFile * m_file
Definition: TFileLooper.h:89
TRootCompare::m_can
TCanvas * m_can
Definition: TRootCompare.h:65
TRootCompare::m_noMatch
std::vector< std::string > m_noMatch
Definition: TRootCompare.h:76
MyPlots.page
page
Definition: MyPlots.py:234
TRootCompare::m_refFile
TFile * m_refFile
Definition: TRootCompare.h:61
DiTauMassTools::MaxHistStrategyV2::e
e
Definition: PhysicsAnalysis/TauID/DiTauMassTools/DiTauMassTools/HelperFunctions.h:26
a
TList * a
Definition: liststreamerinfos.cxx:10
TRootCompare::compareHist
Bool_t compareHist(const TH1 &h, const TH1 &href)
Definition: TRootCompare.cxx:382
y
#define y
TRootCompare::beginJob
virtual void beginJob()
Definition: TRootCompare.cxx:56
CaloCellTimeCorrFiller.filename
filename
Definition: CaloCellTimeCorrFiller.py:24
makeTransCanvas.text
text
Definition: makeTransCanvas.py:11
TRootCompare::BIN
@ BIN
Definition: TRootCompare.h:30
python.IoTestsLib.w
def w
Definition: IoTestsLib.py:200
python.PyAthena.obj
obj
Definition: PyAthena.py:135
TRootCompare::AXIS
@ AXIS
Definition: TRootCompare.h:30
TRootCompare::m_refRootDir
TString m_refRootDir
Definition: TRootCompare.h:63
match
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition: hcg.cxx:356
ymax
double ymax
Definition: listroot.cxx:64
TRootCompare::m_threshold
Double_t m_threshold
Definition: TRootCompare.h:68
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37
TRootCompare::~TRootCompare
virtual ~TRootCompare()
Definition: TRootCompare.cxx:51
TRootCompare::m_drawNormalized
Bool_t m_drawNormalized
Definition: TRootCompare.h:72