|
| 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 | +} |
0 commit comments