Skip to content

Commit 4d8743c

Browse files
committed
Add scripts to compare TTrees of two ROOT files
The macro compares the cbmsim trees of two ROOT files whose names are passed as arguments. If any difference is found the script fails. The script simply calls the macro for all relevant ROOT files which are produced when running the test suite. This allows to compare results before and after a change.
1 parent 5c616a0 commit 4d8743c

File tree

2 files changed

+382
-0
lines changed

2 files changed

+382
-0
lines changed

examples/scripts/TreeCompareAuto.C

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/********************************************************************************
2+
* Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
3+
* *
4+
* This software is distributed under the terms of the *
5+
* GNU Lesser General Public Licence (LGPL) version 3, *
6+
* copied verbatim in the file "LICENSE" *
7+
********************************************************************************/
8+
9+
// The macro compares all the leaves of two TTree objects.
10+
// The first and strongest comparison is that the entries in the two leaves
11+
// to compare are idential.
12+
// To check this one loops over the entries in the trees, calculate the
13+
// difference between the two values and fills the result in a histogram.
14+
// If all entries are absolutely identical the result in the histogram is a
15+
// delta function at 0.
16+
// If the first check for identical entries fails it is checked if there are
17+
// only some changes in the order of the entries. In this case the two
18+
// produced histgrams (one for each tree) are identical.
19+
// The last check which again is only done if both previous test fails uses a
20+
// Kolmogorov test to check if the produced hostograms are comparable on a
21+
// statistical base.
22+
// If at least for one leaf all theree comparisons fail the complete test
23+
// fails.
24+
25+
#include <RtypesCore.h>
26+
#include <TBranch.h>
27+
#include <TFile.h>
28+
#include <TH1.h>
29+
#include <TMath.h>
30+
#include <TObjArray.h>
31+
#include <TString.h>
32+
#include <TTree.h>
33+
#include <iostream>
34+
#include <sstream>
35+
#include <string>
36+
#include <utility>
37+
#include <vector>
38+
39+
std::vector<std::pair<TString, TString>> GetLeafNames(TTree*);
40+
41+
Bool_t CheckEntriesIdentical(TH1* compHist, std::stringstream& outstream);
42+
Bool_t CheckHistogramIdentical(TH1* origHist, TH1* newHist, std::stringstream& outstream);
43+
Bool_t CheckHistogramKolmogorov(TH1* origHist, TH1* newHist, std::stringstream& outstream);
44+
45+
int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "")
46+
{
47+
if (fileName1.IsNull() || fileName2.IsNull()) {
48+
cout << "Filenames are not defined" << endl;
49+
exit(42);
50+
}
51+
52+
// Get the output tree from the original file
53+
TFile* file1 = TFile::Open(fileName1, "READ");
54+
if (nullptr == file1)
55+
return 42;
56+
TTree* tree1 = (TTree*)file1->Get("cbmsim");
57+
58+
// Get the output tree from the file whcih should be compared
59+
TFile* file2 = TFile::Open(fileName2, "READ");
60+
if (nullptr == file2)
61+
return 42;
62+
TTree* tree2 = (TTree*)file2->Get("cbmsim");
63+
64+
// Add the output tree from the file to compare as friend to the tree
65+
// of the original file. This allows to access a data member of the
66+
// original file by e.g. StsHit.fX and the data element of the second tree
67+
// by tree2.StsHit.fX
68+
tree1->AddFriend(tree2, "tree2");
69+
70+
// Define pairs of data members to compare. One from each tree.
71+
// This allows to compare them also if names or the structure of
72+
// the classses change.
73+
std::vector<std::pair<TString, TString>> leaves = GetLeafNames(tree1);
74+
75+
std::stringstream outstream;
76+
Bool_t okay{kTRUE};
77+
Int_t numTestedLeaves{0};
78+
Int_t numEmptyLeaves{0};
79+
Int_t numLeaves{0};
80+
Int_t numFailedLeaves{0};
81+
Int_t numIdenticalEntries{0};
82+
Int_t numIdenticalHistograms{0};
83+
Int_t numKolmogorovHistograms{0};
84+
85+
for (auto leaf : leaves) {
86+
TString leafName = leaf.first;
87+
TString leafName1 = leaf.second;
88+
89+
TString command1 = leafName + ">>htemp";
90+
tree1->Draw(command1);
91+
int entries1{0};
92+
float low1{0.};
93+
float high1{0.};
94+
int nBins1{0};
95+
auto htemp = (TH1F*)gPad->GetPrimitive("htemp");
96+
if (htemp) {
97+
entries1 = htemp->GetEntries();
98+
nBins1 = htemp->GetNbinsX();
99+
low1 = htemp->GetXaxis()->GetXmin();
100+
high1 = htemp->GetXaxis()->GetXmax();
101+
}
102+
103+
command1 = leafName1 + ">>hist1(" + nBins1 + ", " + low1 + ", " + high1 + ")";
104+
// cout << command1 << endl;
105+
tree1->Draw(command1);
106+
auto hist1 = (TH1F*)gPad->GetPrimitive("hist1");
107+
int entries2{0};
108+
float low2{0.};
109+
float high2{0.};
110+
int nBins2{0};
111+
if (hist1) {
112+
entries2 = hist1->GetEntries();
113+
nBins2 = hist1->GetNbinsX();
114+
low2 = hist1->GetXaxis()->GetXmin();
115+
high2 = hist1->GetXaxis()->GetXmax();
116+
}
117+
118+
if ((0 == entries1 && 0 != entries2) || (0 != entries1 && 0 == entries2)) {
119+
std::cout << "One of the distributions is empty" << std::endl;
120+
okay = kFALSE;
121+
}
122+
if (0 == entries1 && 0 == entries2) {
123+
outstream << "Both Histograms are empty." << std::endl;
124+
125+
hist1->Clear();
126+
htemp->Clear();
127+
// numTestedLeaves++;
128+
// numEmptyLeaves++;
129+
continue;
130+
}
131+
132+
// When executing the draw command "Leaf1 - Leaf2" the subtraction is
133+
// executed entry by entry. If the content of the class members are
134+
// identical the result is a histogram with a delta function at 0
135+
// If the content is differnt one gets a distribution which is
136+
// detected.
137+
outstream << "Comparing " << leafName << " and " << leafName1 << std::endl;
138+
TString command = leafName + "-" + leafName1 + ">>hist(20, -10.,10.)";
139+
tree1->Draw(command);
140+
auto hist = (TH1F*)gPad->GetPrimitive("hist");
141+
numTestedLeaves++;
142+
143+
// Check if the entries in the tree are identical
144+
Bool_t leafIsIdentical = CheckEntriesIdentical(hist, outstream);
145+
146+
// If the entries are not identical check if the histograms are
147+
// identical. This is the case if the entries in the tree are sorted
148+
// differently
149+
if (leafIsIdentical) {
150+
numIdenticalEntries++;
151+
outstream << "**********************" << std::endl;
152+
hist1->Clear();
153+
hist->Clear();
154+
htemp->Clear();
155+
continue;
156+
} else {
157+
outstream << "CHecking for identical histograms" << endl;
158+
leafIsIdentical = CheckHistogramIdentical(htemp, hist1, outstream);
159+
}
160+
161+
if (leafIsIdentical) {
162+
numIdenticalHistograms++;
163+
outstream << "**********************" << std::endl;
164+
hist1->Clear();
165+
hist->Clear();
166+
htemp->Clear();
167+
continue;
168+
} else {
169+
outstream << "CHecking Kolmogorov" << endl;
170+
leafIsIdentical = CheckHistogramKolmogorov(htemp, hist1, outstream);
171+
}
172+
173+
if (leafIsIdentical) {
174+
numKolmogorovHistograms++;
175+
outstream << "**********************" << std::endl;
176+
hist1->Clear();
177+
hist->Clear();
178+
htemp->Clear();
179+
continue;
180+
} else {
181+
outstream << "Data are differnt" << std::endl;
182+
outstream << "**********************" << std::endl;
183+
outstream << "Entries: " << hist->GetEntries() << std::endl;
184+
outstream << "Mean: " << hist->GetMean() << std::endl;
185+
outstream << "RMS: " << hist->GetRMS() << std::endl;
186+
outstream << "Underflow: " << hist->GetBinContent(0) << std::endl;
187+
outstream << "Overflow: " << hist->GetBinContent(hist->GetNbinsX() + 1) << std::endl;
188+
outstream << "**********************" << std::endl;
189+
okay = kFALSE;
190+
numFailedLeaves++;
191+
}
192+
}
193+
194+
if (!okay) {
195+
std::cout << outstream.str();
196+
std::cout << "Test failed." << std::endl;
197+
std::cout << numFailedLeaves << " of " << numTestedLeaves << " leaves are different." << std::endl;
198+
return 1;
199+
}
200+
// std::cout << outstream.str();
201+
std::cout << "Tested leaves: " << numTestedLeaves << std::endl;
202+
// std::cout << "Empty leaves: " << numEmptyLeaves << std::endl;
203+
std::cout << "Leaves with identical entries: " << numIdenticalEntries << std::endl;
204+
std::cout << "Leaves with identical histograms: " << numIdenticalHistograms << std::endl;
205+
std::cout << "Leaves with kolmo histograms: " << numKolmogorovHistograms << std::endl;
206+
std::cout << "Test passed. All leaves of all branches are exactly identical." << std::endl;
207+
208+
return 0;
209+
}
210+
211+
Bool_t CheckEntriesIdentical(TH1* compHist, std::stringstream& outstream)
212+
{
213+
if ((TMath::Abs(compHist->GetMean()) > 0.000001 && TMath::Abs(compHist->GetRMS()) > 0.000001)
214+
|| (0 != compHist->GetBinContent(0)) || (0 != compHist->GetBinContent(compHist->GetNbinsX() + 1))) {
215+
return kFALSE;
216+
} else {
217+
outstream << "Entries are identical." << std::endl;
218+
outstream << "**********************" << std::endl;
219+
return kTRUE;
220+
}
221+
}
222+
223+
Bool_t CheckHistogramIdentical(TH1* origHist, TH1* newHist, std::stringstream& outstream)
224+
{
225+
if (origHist && newHist) {
226+
outstream << "Comparing histograms" << std::endl;
227+
for (int x = 0; x < origHist->GetNbinsX() + 1; ++x) {
228+
if (origHist->GetBinContent(x) != newHist->GetBinContent(x)) {
229+
return kFALSE;
230+
}
231+
}
232+
outstream << "Histograms are identical." << std::endl;
233+
outstream << "**********************" << std::endl;
234+
return kTRUE;
235+
}
236+
return kFALSE;
237+
}
238+
239+
Bool_t CheckHistogramKolmogorov(TH1* origHist, TH1* newHist, std::stringstream& outstream)
240+
{
241+
Double_t kolmo = origHist->KolmogorovTest(newHist);
242+
243+
outstream << "Result of Kolmogorov test: " << kolmo << endl;
244+
if (kolmo > 0.99) {
245+
return kTRUE;
246+
}
247+
248+
return kFALSE;
249+
}
250+
251+
std::vector<std::pair<TString, TString>> GetLeafNames(TTree* cbmsim)
252+
{
253+
254+
std::vector<TString> ListOfLeaves;
255+
256+
if (cbmsim) {
257+
TObjArray* bla1 = cbmsim->GetListOfLeaves();
258+
TIter myiter1(bla1);
259+
TBranch* branch;
260+
while ((branch = (TBranch*)myiter1.Next())) {
261+
TString mystring = branch->GetName();
262+
// cout << "Branch Name: " << mystring << endl;
263+
264+
// Generate leaf names for points, digis and hits
265+
if (mystring.Contains("Point") || mystring.Contains("Digi") || mystring.Contains("Hit")) {
266+
TObjArray* bla2 = mystring.Tokenize(".");
267+
if (bla2->GetEntriesFast() == 4) {
268+
TString _branch = ((TObjString*)(bla2->At(2)))->GetString();
269+
TString _leaf = ((TObjString*)(bla2->At(3)))->GetString();
270+
if (_leaf.EqualTo("fLink")) {
271+
TString name = _branch + "." + _leaf + ".fLinks";
272+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fFile");
273+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fType");
274+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fEntry");
275+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fIndex");
276+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fWeight");
277+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fFile");
278+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fType");
279+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fEntry");
280+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fIndex");
281+
ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fWeight");
282+
} else {
283+
ListOfLeaves.emplace_back(_branch + "." + _leaf);
284+
}
285+
}
286+
}
287+
288+
if (mystring.Contains("MCTrack")) {
289+
TObjArray* bla2 = mystring.Tokenize(".");
290+
if (bla2->GetEntriesFast() == 4) {
291+
TString _branch = ((TObjString*)(bla2->At(2)))->GetString();
292+
TString _leaf = ((TObjString*)(bla2->At(3)))->GetString();
293+
ListOfLeaves.emplace_back(_branch + "." + _leaf);
294+
}
295+
}
296+
297+
if (mystring.Contains("GeoTracks")) {
298+
continue;
299+
}
300+
}
301+
}
302+
303+
std::vector<std::pair<TString, TString>> leaves;
304+
for (auto const element : ListOfLeaves) {
305+
TString nameTree2 = "tree2." + element;
306+
leaves.emplace_back(make_pair(element, nameTree2));
307+
}
308+
return leaves;
309+
}

examples/scripts/checkfiles.sh

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/bin/bash
2+
3+
# The script compares files with the same name from two different directories.
4+
# The names of the files are defined in the scripts, the names of the two different
5+
# directories are passed as parameters when executing the script.
6+
# The listed files are produced when executing the test suite of FairRoot
7+
# The script immediately fails if a difference between files is found
8+
# The actaul comparison is done using a ROOT macro which comapares the leaves of
9+
# the ROOT tree ony by one.
10+
11+
if [[ "$#" -ne 2 ]]; then
12+
echo "Wrong number of arguments"
13+
echo "The script expects two directories as arguments"
14+
exit 1
15+
fi
16+
17+
dir1=$1
18+
dir2=$2
19+
20+
files=(
21+
simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000_t0_n1.root
22+
simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000_t0_n10.root
23+
simulation/Tutorial1/macros/tutorial1_pythia8_TGeant4_pions.mc_p2.000_t0_n10.root
24+
simulation/Tutorial1/macros/tutorial1_pythia6_TGeant4_pions.mc_p2.000_t0_n10.root
25+
simulation/Tutorial1/macros/tutorial1_urqmd_TGeant4.mc.root
26+
27+
simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n10.root
28+
simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n10.sg1.root
29+
simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n20.sg2.root
30+
simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n130.bg.root
31+
#simulation/Tutorial2/macros/digis.mc.root
32+
#simulation/Tutorial2/macros/digis.mix.mc.root
33+
34+
simulation/Tutorial4/macros/data/testrun_align_TGeant4.root
35+
simulation/Tutorial4/macros/data/testreco_align_TGeant4.root
36+
#simulation/Tutorial4/macros/data/test.ana.root
37+
38+
simulation/rutherford/macros/data/test_TGeant4.mc.root
39+
simulation/rutherford/macros/data/test1_TGeant4.mc.root
40+
41+
advanced/propagator/macros/prop.mc.root
42+
#advanced/propagator/macros/prop.rk.cal.root
43+
44+
advanced/Tutorial3/macro/data/testrun_TGeant4.root
45+
advanced/Tutorial3/macro/data/testdigi_TGeant4.root
46+
advanced/Tutorial3/macro/data/testreco_TGeant4.root
47+
advanced/Tutorial3/macro/data/testDiRePr_TGeant4.root
48+
#advanced/Tutorial3/macro/data/testrecotimebased_TGeant4.root
49+
50+
#MQ/serialization/data_io/testinput1.root
51+
#MQ/serialization/data_io/outputEx1Test.root
52+
#MQ/serialization/data_io/testinput2.root
53+
#MQ/serialization/data_io/outputEx2Test.root
54+
#MQ/pixelDetector/macros/pixel_TGeant4.mc.root
55+
#MQ/pixelDetector/macros/pixel_TGeant4.digi.root
56+
#MQ/pixelDetector/macros/pixel_TGeant4.digiToBin.root
57+
#MQ/pixelDetector/macros/MQ.pixel_TGeant4.hits.root
58+
#MQ/pixelSimSplit/run/MQ.simulation_TGeant4.data.root
59+
)
60+
61+
for i in "${files[@]}"; do
62+
file1=$dir1/$i
63+
file2=$dir2/$i
64+
65+
root -l -b -q "$VMCWORKDIR/scripts/TreeCompareAuto.C(\"$file1\", \"$file2\")"
66+
bla=$?
67+
if [ "$bla" != "0" ]; then
68+
echo "Files $file1 and $file2 are different"
69+
exit 1
70+
else
71+
echo "Files $file1 and $file2 are identical"
72+
fi
73+
done

0 commit comments

Comments
 (0)