From 4672e1dd70eddff60c7a6db188d839b7d52f0bfc Mon Sep 17 00:00:00 2001 From: Florian Uhlig Date: Fri, 30 Jun 2023 13:09:07 +0200 Subject: [PATCH 1/3] 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. --- examples/scripts/TreeCompareAuto.C | 309 +++++++++++++++++++++++++++++ examples/scripts/checkfiles.sh | 73 +++++++ 2 files changed, 382 insertions(+) create mode 100644 examples/scripts/TreeCompareAuto.C create mode 100755 examples/scripts/checkfiles.sh diff --git a/examples/scripts/TreeCompareAuto.C b/examples/scripts/TreeCompareAuto.C new file mode 100644 index 0000000000..a57fa9bbc7 --- /dev/null +++ b/examples/scripts/TreeCompareAuto.C @@ -0,0 +1,309 @@ +/******************************************************************************** + * Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +// The macro compares all the leaves of two TTree objects. +// The first and strongest comparison is that the entries in the two leaves +// to compare are idential. +// To check this one loops over the entries in the trees, calculate the +// difference between the two values and fills the result in a histogram. +// If all entries are absolutely identical the result in the histogram is a +// delta function at 0. +// If the first check for identical entries fails it is checked if there are +// only some changes in the order of the entries. In this case the two +// produced histgrams (one for each tree) are identical. +// The last check which again is only done if both previous test fails uses a +// Kolmogorov test to check if the produced hostograms are comparable on a +// statistical base. +// If at least for one leaf all theree comparisons fail the complete test +// fails. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::vector> GetLeafNames(TTree*); + +Bool_t CheckEntriesIdentical(TH1* compHist, std::stringstream& outstream); +Bool_t CheckHistogramIdentical(TH1* origHist, TH1* newHist, std::stringstream& outstream); +Bool_t CheckHistogramKolmogorov(TH1* origHist, TH1* newHist, std::stringstream& outstream); + +int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "") +{ + if (fileName1.IsNull() || fileName2.IsNull()) { + cout << "Filenames are not defined" << endl; + exit(42); + } + + // Get the output tree from the original file + TFile* file1 = TFile::Open(fileName1, "READ"); + if (nullptr == file1) + return 42; + TTree* tree1 = (TTree*)file1->Get("cbmsim"); + + // Get the output tree from the file whcih should be compared + TFile* file2 = TFile::Open(fileName2, "READ"); + if (nullptr == file2) + return 42; + TTree* tree2 = (TTree*)file2->Get("cbmsim"); + + // Add the output tree from the file to compare as friend to the tree + // of the original file. This allows to access a data member of the + // original file by e.g. StsHit.fX and the data element of the second tree + // by tree2.StsHit.fX + tree1->AddFriend(tree2, "tree2"); + + // Define pairs of data members to compare. One from each tree. + // This allows to compare them also if names or the structure of + // the classses change. + std::vector> leaves = GetLeafNames(tree1); + + std::stringstream outstream; + Bool_t okay{kTRUE}; + Int_t numTestedLeaves{0}; + Int_t numEmptyLeaves{0}; + Int_t numLeaves{0}; + Int_t numFailedLeaves{0}; + Int_t numIdenticalEntries{0}; + Int_t numIdenticalHistograms{0}; + Int_t numKolmogorovHistograms{0}; + + for (auto leaf : leaves) { + TString leafName = leaf.first; + TString leafName1 = leaf.second; + + TString command1 = leafName + ">>htemp"; + tree1->Draw(command1); + int entries1{0}; + float low1{0.}; + float high1{0.}; + int nBins1{0}; + auto htemp = (TH1F*)gPad->GetPrimitive("htemp"); + if (htemp) { + entries1 = htemp->GetEntries(); + nBins1 = htemp->GetNbinsX(); + low1 = htemp->GetXaxis()->GetXmin(); + high1 = htemp->GetXaxis()->GetXmax(); + } + + command1 = leafName1 + ">>hist1(" + nBins1 + ", " + low1 + ", " + high1 + ")"; + // cout << command1 << endl; + tree1->Draw(command1); + auto hist1 = (TH1F*)gPad->GetPrimitive("hist1"); + int entries2{0}; + float low2{0.}; + float high2{0.}; + int nBins2{0}; + if (hist1) { + entries2 = hist1->GetEntries(); + nBins2 = hist1->GetNbinsX(); + low2 = hist1->GetXaxis()->GetXmin(); + high2 = hist1->GetXaxis()->GetXmax(); + } + + if ((0 == entries1 && 0 != entries2) || (0 != entries1 && 0 == entries2)) { + std::cout << "One of the distributions is empty" << std::endl; + okay = kFALSE; + } + if (0 == entries1 && 0 == entries2) { + outstream << "Both Histograms are empty." << std::endl; + + hist1->Clear(); + htemp->Clear(); + // numTestedLeaves++; + // numEmptyLeaves++; + continue; + } + + // When executing the draw command "Leaf1 - Leaf2" the subtraction is + // executed entry by entry. If the content of the class members are + // identical the result is a histogram with a delta function at 0 + // If the content is differnt one gets a distribution which is + // detected. + outstream << "Comparing " << leafName << " and " << leafName1 << std::endl; + TString command = leafName + "-" + leafName1 + ">>hist(20, -10.,10.)"; + tree1->Draw(command); + auto hist = (TH1F*)gPad->GetPrimitive("hist"); + numTestedLeaves++; + + // Check if the entries in the tree are identical + Bool_t leafIsIdentical = CheckEntriesIdentical(hist, outstream); + + // If the entries are not identical check if the histograms are + // identical. This is the case if the entries in the tree are sorted + // differently + if (leafIsIdentical) { + numIdenticalEntries++; + outstream << "**********************" << std::endl; + hist1->Clear(); + hist->Clear(); + htemp->Clear(); + continue; + } else { + outstream << "CHecking for identical histograms" << endl; + leafIsIdentical = CheckHistogramIdentical(htemp, hist1, outstream); + } + + if (leafIsIdentical) { + numIdenticalHistograms++; + outstream << "**********************" << std::endl; + hist1->Clear(); + hist->Clear(); + htemp->Clear(); + continue; + } else { + outstream << "CHecking Kolmogorov" << endl; + leafIsIdentical = CheckHistogramKolmogorov(htemp, hist1, outstream); + } + + if (leafIsIdentical) { + numKolmogorovHistograms++; + outstream << "**********************" << std::endl; + hist1->Clear(); + hist->Clear(); + htemp->Clear(); + continue; + } else { + outstream << "Data are differnt" << std::endl; + outstream << "**********************" << std::endl; + outstream << "Entries: " << hist->GetEntries() << std::endl; + outstream << "Mean: " << hist->GetMean() << std::endl; + outstream << "RMS: " << hist->GetRMS() << std::endl; + outstream << "Underflow: " << hist->GetBinContent(0) << std::endl; + outstream << "Overflow: " << hist->GetBinContent(hist->GetNbinsX() + 1) << std::endl; + outstream << "**********************" << std::endl; + okay = kFALSE; + numFailedLeaves++; + } + } + + if (!okay) { + std::cout << outstream.str(); + std::cout << "Test failed." << std::endl; + std::cout << numFailedLeaves << " of " << numTestedLeaves << " leaves are different." << std::endl; + return 1; + } + // std::cout << outstream.str(); + std::cout << "Tested leaves: " << numTestedLeaves << std::endl; + // std::cout << "Empty leaves: " << numEmptyLeaves << std::endl; + std::cout << "Leaves with identical entries: " << numIdenticalEntries << std::endl; + std::cout << "Leaves with identical histograms: " << numIdenticalHistograms << std::endl; + std::cout << "Leaves with kolmo histograms: " << numKolmogorovHistograms << std::endl; + std::cout << "Test passed. All leaves of all branches are exactly identical." << std::endl; + + return 0; +} + +Bool_t CheckEntriesIdentical(TH1* compHist, std::stringstream& outstream) +{ + if ((TMath::Abs(compHist->GetMean()) > 0.000001 && TMath::Abs(compHist->GetRMS()) > 0.000001) + || (0 != compHist->GetBinContent(0)) || (0 != compHist->GetBinContent(compHist->GetNbinsX() + 1))) { + return kFALSE; + } else { + outstream << "Entries are identical." << std::endl; + outstream << "**********************" << std::endl; + return kTRUE; + } +} + +Bool_t CheckHistogramIdentical(TH1* origHist, TH1* newHist, std::stringstream& outstream) +{ + if (origHist && newHist) { + outstream << "Comparing histograms" << std::endl; + for (int x = 0; x < origHist->GetNbinsX() + 1; ++x) { + if (origHist->GetBinContent(x) != newHist->GetBinContent(x)) { + return kFALSE; + } + } + outstream << "Histograms are identical." << std::endl; + outstream << "**********************" << std::endl; + return kTRUE; + } + return kFALSE; +} + +Bool_t CheckHistogramKolmogorov(TH1* origHist, TH1* newHist, std::stringstream& outstream) +{ + Double_t kolmo = origHist->KolmogorovTest(newHist); + + outstream << "Result of Kolmogorov test: " << kolmo << endl; + if (kolmo > 0.99) { + return kTRUE; + } + + return kFALSE; +} + +std::vector> GetLeafNames(TTree* cbmsim) +{ + + std::vector ListOfLeaves; + + if (cbmsim) { + TObjArray* bla1 = cbmsim->GetListOfLeaves(); + TIter myiter1(bla1); + TBranch* branch; + while ((branch = (TBranch*)myiter1.Next())) { + TString mystring = branch->GetName(); + // cout << "Branch Name: " << mystring << endl; + + // Generate leaf names for points, digis and hits + if (mystring.Contains("Point") || mystring.Contains("Digi") || mystring.Contains("Hit")) { + TObjArray* bla2 = mystring.Tokenize("."); + if (bla2->GetEntriesFast() == 4) { + TString _branch = ((TObjString*)(bla2->At(2)))->GetString(); + TString _leaf = ((TObjString*)(bla2->At(3)))->GetString(); + if (_leaf.EqualTo("fLink")) { + TString name = _branch + "." + _leaf + ".fLinks"; + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fFile"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fType"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fEntry"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fIndex"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fWeight"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fFile"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fType"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fEntry"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fIndex"); + ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fWeight"); + } else { + ListOfLeaves.emplace_back(_branch + "." + _leaf); + } + } + } + + if (mystring.Contains("MCTrack")) { + TObjArray* bla2 = mystring.Tokenize("."); + if (bla2->GetEntriesFast() == 4) { + TString _branch = ((TObjString*)(bla2->At(2)))->GetString(); + TString _leaf = ((TObjString*)(bla2->At(3)))->GetString(); + ListOfLeaves.emplace_back(_branch + "." + _leaf); + } + } + + if (mystring.Contains("GeoTracks")) { + continue; + } + } + } + + std::vector> leaves; + for (auto const element : ListOfLeaves) { + TString nameTree2 = "tree2." + element; + leaves.emplace_back(make_pair(element, nameTree2)); + } + return leaves; +} diff --git a/examples/scripts/checkfiles.sh b/examples/scripts/checkfiles.sh new file mode 100755 index 0000000000..06400158c4 --- /dev/null +++ b/examples/scripts/checkfiles.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# The script compares files with the same name from two different directories. +# The names of the files are defined in the scripts, the names of the two different +# directories are passed as parameters when executing the script. +# The listed files are produced when executing the test suite of FairRoot +# The script immediately fails if a difference between files is found +# The actaul comparison is done using a ROOT macro which comapares the leaves of +# the ROOT tree ony by one. + +if [[ "$#" -ne 2 ]]; then + echo "Wrong number of arguments" + echo "The script expects two directories as arguments" + exit 1 +fi + +dir1=$1 +dir2=$2 + +files=( +simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000_t0_n1.root +simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000_t0_n10.root +simulation/Tutorial1/macros/tutorial1_pythia8_TGeant4_pions.mc_p2.000_t0_n10.root +simulation/Tutorial1/macros/tutorial1_pythia6_TGeant4_pions.mc_p2.000_t0_n10.root +simulation/Tutorial1/macros/tutorial1_urqmd_TGeant4.mc.root + +simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n10.root +simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n10.sg1.root +simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n20.sg2.root +simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n130.bg.root +#simulation/Tutorial2/macros/digis.mc.root +#simulation/Tutorial2/macros/digis.mix.mc.root + +simulation/Tutorial4/macros/data/testrun_align_TGeant4.root +simulation/Tutorial4/macros/data/testreco_align_TGeant4.root +#simulation/Tutorial4/macros/data/test.ana.root + +simulation/rutherford/macros/data/test_TGeant4.mc.root +simulation/rutherford/macros/data/test1_TGeant4.mc.root + +advanced/propagator/macros/prop.mc.root +#advanced/propagator/macros/prop.rk.cal.root + +advanced/Tutorial3/macro/data/testrun_TGeant4.root +advanced/Tutorial3/macro/data/testdigi_TGeant4.root +advanced/Tutorial3/macro/data/testreco_TGeant4.root +advanced/Tutorial3/macro/data/testDiRePr_TGeant4.root +#advanced/Tutorial3/macro/data/testrecotimebased_TGeant4.root + +#MQ/serialization/data_io/testinput1.root +#MQ/serialization/data_io/outputEx1Test.root +#MQ/serialization/data_io/testinput2.root +#MQ/serialization/data_io/outputEx2Test.root +#MQ/pixelDetector/macros/pixel_TGeant4.mc.root +#MQ/pixelDetector/macros/pixel_TGeant4.digi.root +#MQ/pixelDetector/macros/pixel_TGeant4.digiToBin.root +#MQ/pixelDetector/macros/MQ.pixel_TGeant4.hits.root +#MQ/pixelSimSplit/run/MQ.simulation_TGeant4.data.root +) + +for i in "${files[@]}"; do + file1=$dir1/$i + file2=$dir2/$i + + root -l -b -q "$VMCWORKDIR/scripts/TreeCompareAuto.C(\"$file1\", \"$file2\")" + bla=$? + if [ "$bla" != "0" ]; then + echo "Files $file1 and $file2 are different" + exit 1 + else + echo "Files $file1 and $file2 are identical" + fi +done From e07eeec94e6841569f18ffa17cce8d6e8fe98af5 Mon Sep 17 00:00:00 2001 From: Florian Uhlig Date: Wed, 5 Jul 2023 15:30:42 +0200 Subject: [PATCH 2/3] fixed file list --- examples/scripts/checkfiles.sh | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/examples/scripts/checkfiles.sh b/examples/scripts/checkfiles.sh index 06400158c4..bd2a6c7f38 100755 --- a/examples/scripts/checkfiles.sh +++ b/examples/scripts/checkfiles.sh @@ -18,8 +18,9 @@ dir1=$1 dir2=$2 files=( -simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000_t0_n1.root -simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000_t0_n10.root +simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000000_t0.000000_n1.root +simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000000_t0.000000_n10.root +simulation/Tutorial1/macros/tutorial1_TGeant4_pions.mc_p2.000000_t0.000000_n20.root simulation/Tutorial1/macros/tutorial1_pythia8_TGeant4_pions.mc_p2.000_t0_n10.root simulation/Tutorial1/macros/tutorial1_pythia6_TGeant4_pions.mc_p2.000_t0_n10.root simulation/Tutorial1/macros/tutorial1_urqmd_TGeant4.mc.root @@ -28,34 +29,18 @@ simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n10.root simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n10.sg1.root simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n20.sg2.root simulation/Tutorial2/macros/tutorial2_pions.mc_p2.000_t0_n130.bg.root -#simulation/Tutorial2/macros/digis.mc.root -#simulation/Tutorial2/macros/digis.mix.mc.root simulation/Tutorial4/macros/data/testrun_align_TGeant4.root simulation/Tutorial4/macros/data/testreco_align_TGeant4.root -#simulation/Tutorial4/macros/data/test.ana.root simulation/rutherford/macros/data/test_TGeant4.mc.root simulation/rutherford/macros/data/test1_TGeant4.mc.root advanced/propagator/macros/prop.mc.root -#advanced/propagator/macros/prop.rk.cal.root advanced/Tutorial3/macro/data/testrun_TGeant4.root advanced/Tutorial3/macro/data/testdigi_TGeant4.root advanced/Tutorial3/macro/data/testreco_TGeant4.root -advanced/Tutorial3/macro/data/testDiRePr_TGeant4.root -#advanced/Tutorial3/macro/data/testrecotimebased_TGeant4.root - -#MQ/serialization/data_io/testinput1.root -#MQ/serialization/data_io/outputEx1Test.root -#MQ/serialization/data_io/testinput2.root -#MQ/serialization/data_io/outputEx2Test.root -#MQ/pixelDetector/macros/pixel_TGeant4.mc.root -#MQ/pixelDetector/macros/pixel_TGeant4.digi.root -#MQ/pixelDetector/macros/pixel_TGeant4.digiToBin.root -#MQ/pixelDetector/macros/MQ.pixel_TGeant4.hits.root -#MQ/pixelSimSplit/run/MQ.simulation_TGeant4.data.root ) for i in "${files[@]}"; do From 070d93834d4b3b84705b39b56625d0f12e5f686e Mon Sep 17 00:00:00 2001 From: Florian Uhlig Date: Wed, 5 Jul 2023 15:31:18 +0200 Subject: [PATCH 3/3] Code cleanup Rewrite function GetLeafNames Use solution proposed by @dennisklein in #1419 Fix variable names. Remove C-style casts. Use uniqe_ptr where possible. Remove fixed tree name. Use standard types instaed of ROOT types. Use references instead of pointers where possible. Remove unneeded functions. General code improvements. --- examples/scripts/TreeCompareAuto.C | 357 ++++++++++++++++------------- 1 file changed, 196 insertions(+), 161 deletions(-) diff --git a/examples/scripts/TreeCompareAuto.C b/examples/scripts/TreeCompareAuto.C index a57fa9bbc7..f3628a8532 100644 --- a/examples/scripts/TreeCompareAuto.C +++ b/examples/scripts/TreeCompareAuto.C @@ -23,24 +23,26 @@ // fails. #include -#include +#include +#include // for TRangeDynCast #include #include +#include +#include #include -#include #include #include +#include // for std::any_of #include #include #include +#include #include #include -std::vector> GetLeafNames(TTree*); +std::vector GetLeafNames(TTree&); -Bool_t CheckEntriesIdentical(TH1* compHist, std::stringstream& outstream); -Bool_t CheckHistogramIdentical(TH1* origHist, TH1* newHist, std::stringstream& outstream); -Bool_t CheckHistogramKolmogorov(TH1* origHist, TH1* newHist, std::stringstream& outstream); +bool operator==(TH1 const&, TH1 const&); int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "") { @@ -50,82 +52,126 @@ int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "") } // Get the output tree from the original file - TFile* file1 = TFile::Open(fileName1, "READ"); - if (nullptr == file1) + std::unique_ptr file1{TFile::Open(fileName1, "READ")}; + if (!file1) { + std::cout << "Could not open file " << fileName1 << std::endl; return 42; - TTree* tree1 = (TTree*)file1->Get("cbmsim"); + } + + // Find a tree in the file + TList* keylist = file1->GetListOfKeys(); + TKey* key; + TIter keyIterator(keylist); + TString treeName; + int numTreeInFile{0}; + while ((key = static_cast(keyIterator()))) { + if (key->ReadObj()->InheritsFrom("TTree")) { + treeName = key->GetName(); + std::cout << "Found TTree with name " << treeName << std::endl; + numTreeInFile++; + } + } + if ( treeName.IsNull() ) { + std::cout << "File does not contain any TTree" << std::endl; + return 42; + } + if ( 1 != numTreeInFile ) { + std::cout << "File ontains more than one TTree" << std::endl; + return 42; + } - // Get the output tree from the file whcih should be compared - TFile* file2 = TFile::Open(fileName2, "READ"); - if (nullptr == file2) + std::unique_ptr tree1{file1->Get(treeName)}; + if (!tree1) { + std::cout << "File " << fileName1 << " does not have the tree " + << treeName << std::endl; return 42; - TTree* tree2 = (TTree*)file2->Get("cbmsim"); + } + + // Get the output tree from the file which should be compared + std::unique_ptr file2{TFile::Open(fileName2, "READ")}; + if (!file2) { + std::cout << "Could not open file " << fileName2 << std::endl; + return 42; + } + + // The name of the tree must be the same in both files + std::unique_ptr tree2{file2->Get(treeName)}; + if (!tree2) { + std::cout << "File " << fileName2 << " does not have the tree" + << treeName << std::endl; + return 42; + } // Add the output tree from the file to compare as friend to the tree // of the original file. This allows to access a data member of the // original file by e.g. StsHit.fX and the data element of the second tree // by tree2.StsHit.fX - tree1->AddFriend(tree2, "tree2"); + TString const friendName{"tree2"}; + tree1->AddFriend(tree2.get(), friendName); - // Define pairs of data members to compare. One from each tree. - // This allows to compare them also if names or the structure of - // the classses change. - std::vector> leaves = GetLeafNames(tree1); + // Get the leaf names for all leaves which should be compared + // from the first input tree + auto leaves = GetLeafNames(*tree1); + + if (0 == leaves.size()) { + std::cout << "Test passed. Tree does not contain any data which must be checked" << std::endl; + return 0; + } + + // The TCanvas is needed to suppress a info/warning + TCanvas defaultCanvas; std::stringstream outstream; - Bool_t okay{kTRUE}; - Int_t numTestedLeaves{0}; - Int_t numEmptyLeaves{0}; - Int_t numLeaves{0}; - Int_t numFailedLeaves{0}; - Int_t numIdenticalEntries{0}; - Int_t numIdenticalHistograms{0}; - Int_t numKolmogorovHistograms{0}; + bool okay{true}; + int numTestedLeaves{0}; + int numEmptyLeaves{0}; + int numLeaves{0}; + int numFailedLeaves{0}; + int numIdenticalEntries{0}; + int numIdenticalHistograms{0}; + int numKolmogorovHistograms{0}; for (auto leaf : leaves) { - TString leafName = leaf.first; - TString leafName1 = leaf.second; + TString leafName = leaf; + TString leafName1 = friendName + "." + leafName; TString command1 = leafName + ">>htemp"; tree1->Draw(command1); - int entries1{0}; - float low1{0.}; - float high1{0.}; - int nBins1{0}; - auto htemp = (TH1F*)gPad->GetPrimitive("htemp"); + int entries{0}; + float low{0.}; + float high{0.}; + int nBins{0}; + auto htemp = static_cast(gPad->GetPrimitive("htemp")); if (htemp) { - entries1 = htemp->GetEntries(); - nBins1 = htemp->GetNbinsX(); - low1 = htemp->GetXaxis()->GetXmin(); - high1 = htemp->GetXaxis()->GetXmax(); + entries = htemp->GetEntries(); + nBins = htemp->GetNbinsX(); + low = htemp->GetXaxis()->GetXmin(); + high = htemp->GetXaxis()->GetXmax(); } - command1 = leafName1 + ">>hist1(" + nBins1 + ", " + low1 + ", " + high1 + ")"; - // cout << command1 << endl; + command1 = leafName1 + ">>hist1(" + nBins + ", " + low + ", " + high + ")"; tree1->Draw(command1); - auto hist1 = (TH1F*)gPad->GetPrimitive("hist1"); - int entries2{0}; - float low2{0.}; - float high2{0.}; - int nBins2{0}; + auto hist1 = static_cast(gPad->GetPrimitive("hist1")); + int entries1{0}; + float low1{0.}; + float high1{0.}; + int nBins1{0}; if (hist1) { - entries2 = hist1->GetEntries(); - nBins2 = hist1->GetNbinsX(); - low2 = hist1->GetXaxis()->GetXmin(); - high2 = hist1->GetXaxis()->GetXmax(); + entries1 = hist1->GetEntries(); + nBins1 = hist1->GetNbinsX(); + low1 = hist1->GetXaxis()->GetXmin(); + high1 = hist1->GetXaxis()->GetXmax(); } - if ((0 == entries1 && 0 != entries2) || (0 != entries1 && 0 == entries2)) { + if ((0 == entries && 0 != entries1) || (0 != entries && 0 == entries1)) { std::cout << "One of the distributions is empty" << std::endl; - okay = kFALSE; + okay = false; } - if (0 == entries1 && 0 == entries2) { + if (0 == entries && 0 == entries1) { outstream << "Both Histograms are empty." << std::endl; hist1->Clear(); htemp->Clear(); - // numTestedLeaves++; - // numEmptyLeaves++; continue; } @@ -135,60 +181,71 @@ int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "") // If the content is differnt one gets a distribution which is // detected. outstream << "Comparing " << leafName << " and " << leafName1 << std::endl; + TString command = leafName + "-" + leafName1 + ">>hist(20, -10.,10.)"; tree1->Draw(command); - auto hist = (TH1F*)gPad->GetPrimitive("hist"); + auto hist = static_cast(gPad->GetPrimitive("hist")); numTestedLeaves++; // Check if the entries in the tree are identical - Bool_t leafIsIdentical = CheckEntriesIdentical(hist, outstream); + outstream << "Checking for identical entries" << endl; - // If the entries are not identical check if the histograms are - // identical. This is the case if the entries in the tree are sorted - // differently - if (leafIsIdentical) { + if (TMath::Abs(hist->GetMean()) < 0.000001 && TMath::Abs(hist->GetRMS()) < 0.000001 + && 0 == hist->GetBinContent(0) && 0 == hist->GetBinContent(hist->GetNbinsX() + 1)) { numIdenticalEntries++; + outstream << "Entries are identical." << std::endl; outstream << "**********************" << std::endl; hist1->Clear(); hist->Clear(); htemp->Clear(); continue; - } else { - outstream << "CHecking for identical histograms" << endl; - leafIsIdentical = CheckHistogramIdentical(htemp, hist1, outstream); } - if (leafIsIdentical) { + // If the entries are not identical check if the histograms are + // identical. This is the case if the entries in the tree are sorted + // differently + outstream << "Checking for identical histograms" << endl; + + if (*htemp == *hist1) { numIdenticalHistograms++; + outstream << "Histograms are identical." << std::endl; outstream << "**********************" << std::endl; hist1->Clear(); hist->Clear(); htemp->Clear(); continue; - } else { - outstream << "CHecking Kolmogorov" << endl; - leafIsIdentical = CheckHistogramKolmogorov(htemp, hist1, outstream); } - if (leafIsIdentical) { + // if also the histograms are not identical check if the histograms + // are equal on a statistical base. Use The Kolmogorov test for + // this. + outstream << "Checking Kolmogorov" << endl; + + double kolmo = htemp->KolmogorovTest(hist1); + + outstream << "Result of Kolmogorov test: " << kolmo << endl; + if (kolmo > 0.99) { numKolmogorovHistograms++; outstream << "**********************" << std::endl; hist1->Clear(); hist->Clear(); htemp->Clear(); continue; - } else { - outstream << "Data are differnt" << std::endl; - outstream << "**********************" << std::endl; - outstream << "Entries: " << hist->GetEntries() << std::endl; - outstream << "Mean: " << hist->GetMean() << std::endl; - outstream << "RMS: " << hist->GetRMS() << std::endl; - outstream << "Underflow: " << hist->GetBinContent(0) << std::endl; - outstream << "Overflow: " << hist->GetBinContent(hist->GetNbinsX() + 1) << std::endl; - outstream << "**********************" << std::endl; - okay = kFALSE; - numFailedLeaves++; } + + outstream << "Data are differnt" << std::endl; + outstream << "**********************" << std::endl; + outstream << "Entries: " << hist->GetEntries() << std::endl; + outstream << "Mean: " << hist->GetMean() << std::endl; + outstream << "RMS: " << hist->GetRMS() << std::endl; + outstream << "Underflow: " << hist->GetBinContent(0) << std::endl; + outstream << "Overflow: " << hist->GetBinContent(hist->GetNbinsX() + 1) << std::endl; + outstream << "**********************" << std::endl; + okay = false; + numFailedLeaves++; + hist1->Clear(); + hist->Clear(); + htemp->Clear(); } if (!okay) { @@ -197,9 +254,8 @@ int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "") std::cout << numFailedLeaves << " of " << numTestedLeaves << " leaves are different." << std::endl; return 1; } - // std::cout << outstream.str(); + std::cout << "Tested leaves: " << numTestedLeaves << std::endl; - // std::cout << "Empty leaves: " << numEmptyLeaves << std::endl; std::cout << "Leaves with identical entries: " << numIdenticalEntries << std::endl; std::cout << "Leaves with identical histograms: " << numIdenticalHistograms << std::endl; std::cout << "Leaves with kolmo histograms: " << numKolmogorovHistograms << std::endl; @@ -208,102 +264,81 @@ int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "") return 0; } -Bool_t CheckEntriesIdentical(TH1* compHist, std::stringstream& outstream) +bool operator==(TH1 const& lhs, TH1 const& rhs) { - if ((TMath::Abs(compHist->GetMean()) > 0.000001 && TMath::Abs(compHist->GetRMS()) > 0.000001) - || (0 != compHist->GetBinContent(0)) || (0 != compHist->GetBinContent(compHist->GetNbinsX() + 1))) { - return kFALSE; - } else { - outstream << "Entries are identical." << std::endl; - outstream << "**********************" << std::endl; - return kTRUE; + for (int x = 0; x < lhs.GetNbinsX() + 1; ++x) { + if (lhs.GetBinContent(x) != rhs.GetBinContent(x)) { + return false; + } } + return true; } -Bool_t CheckHistogramIdentical(TH1* origHist, TH1* newHist, std::stringstream& outstream) +std::vector split(std::string_view sv, char by) { - if (origHist && newHist) { - outstream << "Comparing histograms" << std::endl; - for (int x = 0; x < origHist->GetNbinsX() + 1; ++x) { - if (origHist->GetBinContent(x) != newHist->GetBinContent(x)) { - return kFALSE; - } - } - outstream << "Histograms are identical." << std::endl; - outstream << "**********************" << std::endl; - return kTRUE; + std::vector components; + std::string_view::size_type first{0}; + std::string_view::size_type last{sv.find(by, first)}; + while (last != std::string_view::npos) { + components.emplace_back(sv.substr(first, last - first)); + first = last + 1; + last = sv.find(by, first); } - return kFALSE; + components.emplace_back(sv.substr(first, std::string_view::npos)); + return components; } -Bool_t CheckHistogramKolmogorov(TH1* origHist, TH1* newHist, std::stringstream& outstream) +// to be replaced by std::string::contains in C++23 +// https://en.cppreference.com/w/cpp/string/basic_string/contains +bool contains(std::string_view sv, std::string_view what) { - Double_t kolmo = origHist->KolmogorovTest(newHist); - - outstream << "Result of Kolmogorov test: " << kolmo << endl; - if (kolmo > 0.99) { - return kTRUE; - } - - return kFALSE; + return sv.find(what) != std::string_view::npos; } -std::vector> GetLeafNames(TTree* cbmsim) +std::vector GetLeafNames(TTree& simTree) { + std::vector leaves; - std::vector ListOfLeaves; - - if (cbmsim) { - TObjArray* bla1 = cbmsim->GetListOfLeaves(); - TIter myiter1(bla1); - TBranch* branch; - while ((branch = (TBranch*)myiter1.Next())) { - TString mystring = branch->GetName(); - // cout << "Branch Name: " << mystring << endl; - - // Generate leaf names for points, digis and hits - if (mystring.Contains("Point") || mystring.Contains("Digi") || mystring.Contains("Hit")) { - TObjArray* bla2 = mystring.Tokenize("."); - if (bla2->GetEntriesFast() == 4) { - TString _branch = ((TObjString*)(bla2->At(2)))->GetString(); - TString _leaf = ((TObjString*)(bla2->At(3)))->GetString(); - if (_leaf.EqualTo("fLink")) { - TString name = _branch + "." + _leaf + ".fLinks"; - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fFile"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fType"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fEntry"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fIndex"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fLinks.fWeight"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fFile"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fType"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fEntry"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fIndex"); - ListOfLeaves.emplace_back(_branch + "." + _leaf + ".fEntryNr.fWeight"); - } else { - ListOfLeaves.emplace_back(_branch + "." + _leaf); - } - } - } - - if (mystring.Contains("MCTrack")) { - TObjArray* bla2 = mystring.Tokenize("."); - if (bla2->GetEntriesFast() == 4) { - TString _branch = ((TObjString*)(bla2->At(2)))->GetString(); - TString _leaf = ((TObjString*)(bla2->At(3)))->GetString(); - ListOfLeaves.emplace_back(_branch + "." + _leaf); - } - } - - if (mystring.Contains("GeoTracks")) { - continue; - } + for (auto leaf : TRangeDynCast(simTree.GetListOfLeaves())) { + if (!leaf) { + continue; + } + + std::string const fullName = leaf->GetName(); + + auto const keywords = {"Point", "Digi", "Hit", "MCTrack"}; + auto const match = std::any_of(std::cbegin(keywords), std::cend(keywords), [&](auto keyword) { + return contains(fullName, keyword); + }); + if (!match) { + continue; } - } - std::vector> leaves; - for (auto const element : ListOfLeaves) { - TString nameTree2 = "tree2." + element; - leaves.emplace_back(make_pair(element, nameTree2)); + auto const parts = split(fullName, '.'); + if (parts.size() != 4) { + continue; + } + + std::string const branchName{parts[2]}; + std::string const leafName{parts[3]}; + std::string const name{branchName + "." + leafName}; + + if (leafName == "fLink" && !contains(fullName, "MCTrack")) { + leaves.emplace_back(name + ".fLinks.fFile"); + leaves.emplace_back(name + ".fLinks.fType"); + leaves.emplace_back(name + ".fLinks.fEntry"); + leaves.emplace_back(name + ".fLinks.fIndex"); + leaves.emplace_back(name + ".fLinks.fWeight"); + leaves.emplace_back(name + ".fEntryNr.fFile"); + leaves.emplace_back(name + ".fEntryNr.fType"); + leaves.emplace_back(name + ".fEntryNr.fEntry"); + leaves.emplace_back(name + ".fEntryNr.fIndex"); + leaves.emplace_back(name + ".fEntryNr.fWeight"); + continue; + } + + leaves.emplace_back(name); } + return leaves; }