Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add compare macro #1419

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open

Conversation

fuhlig1
Copy link
Member

@fuhlig1 fuhlig1 commented Jun 30, 2023

Add a macro which allows to compare the cbmsim tree of two different ROOT macros. The added script does this comparison for all relevant ROOT files produced by the test suite. This allows to easily compare the results before and after a change of FairRoot to see if there are unexpected changes.

Checklist:

@fuhlig1 fuhlig1 changed the base branch from master to dev June 30, 2023 11:23
Copy link
Member

@ChristianTackeGSI ChristianTackeGSI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round of comments.

Could we please get a test for this tooling somehow?

Maybe we could integrate it in some other test by running the simulation with two seeds and then compare the results? (which should hopefully be quite the same?)

examples/scripts/TreeCompareAuto.C Outdated Show resolved Hide resolved
examples/scripts/TreeCompareAuto.C Outdated Show resolved Hide resolved
examples/scripts/TreeCompareAuto.C Outdated Show resolved Hide resolved
examples/scripts/TreeCompareAuto.C Outdated Show resolved Hide resolved
examples/scripts/TreeCompareAuto.C Outdated Show resolved Hide resolved
examples/scripts/TreeCompareAuto.C Outdated Show resolved Hide resolved
examples/scripts/TreeCompareAuto.C Outdated Show resolved Hide resolved
examples/scripts/TreeCompareAuto.C Outdated Show resolved Hide resolved
@fuhlig1
Copy link
Member Author

fuhlig1 commented Jun 30, 2023

The test will be the next step. First I want to get the macro and the script in which are the prerequisites for the test.
I will look for the comments probably only next week.

Comment on lines 21 to 58
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
Copy link
Member

@dennisklein dennisklein Jun 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should generate this list somehow, having it hardcoded like this will be a nightmare to maintain.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree but I am not sure if this will be so easy. For the time being it would be good if we could keep the hard coded list.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An idea would be to glob for ${dir1}/**/*.root and then iterate this list of files, if such a root file also exists in $dir2. We may have to add then another sanity check to the TreeCompareAuto.C macro that it will skip non-FairRoot root files, if there were any.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is that not all ROOT files should be tested. Not all off them contain the tree. That means that we have to filter the list afterwards to get only the list of files we want. In my opinion this isn't better than having a fixed list.

One solution would be to provide some benchmark files you want to compare and loop over these files and compare them with the newly produced ones. The downside of this is that we have to provide the benchmark files somehow.

Copy link
Member

@dennisklein dennisklein Jul 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another approach would be:

  • Add a CMake list variable FAIRROOT_SIMTREE_FILES_FOR_INTEGRITY_TESTING
  • Append file names to it in each of the local CMakeLists.txt files from each example/test the file belongs to
  • Then configure_file() this checkfiles.sh script in a way that the contents of the CMake variable is added

Then the "authority" of naming relevant root files stays local to the individual example/test/lib. What do you think?

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.
@fuhlig1
Copy link
Member Author

fuhlig1 commented Jul 5, 2023

@ChristianTackeGSI, @dennisklein,

thanks for all the suggestions. I hope I addressed them now all.

return 0;
}

TCanvas c1;
Copy link
Member

@dennisklein dennisklein Jul 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TCanvas c1;

and see following comment further down

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to suppress a warning/info from ROOT. I add a comment and renamed the variable.

Comment on lines +105 to +150
TString command1 = leafName + ">>htemp";
tree1->Draw(command1);
int entries{0};
float low{0.};
float high{0.};
int nBins{0};
auto htemp = static_cast<TH1F*>(gPad->GetPrimitive("htemp"));
if (htemp) {
entries = htemp->GetEntries();
nBins = htemp->GetNbinsX();
low = htemp->GetXaxis()->GetXmin();
high = htemp->GetXaxis()->GetXmax();
}
Copy link
Member

@dennisklein dennisklein Jul 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TString command1 = leafName + ">>htemp";
tree1->Draw(command1);
int entries{0};
float low{0.};
float high{0.};
int nBins{0};
auto htemp = static_cast<TH1F*>(gPad->GetPrimitive("htemp"));
if (htemp) {
entries = htemp->GetEntries();
nBins = htemp->GetNbinsX();
low = htemp->GetXaxis()->GetXmin();
high = htemp->GetXaxis()->GetXmax();
}
TH1F htemp;
long long entries = tree1->GetEntries();
float value = 0;
tree1->SetBranchAddress(leafName, &value);
for (long long i = 0; i < entries; ++i) {
tree1->GetEntry(i);
htemp->Fill(value);
}
float low = htemp.GetXaxis()->GetXmin();
float high = htemp.GetXaxis()->GetXmax();
int nBins = htemp.GetNbinsX();

Why not something simple like this (have not tested the above code, but there should be some code similar to the above)?

What is the benefit to go through all this Draw() logic and global referencing via gPad?

We could also get rid of the htemp->Clear(); etc?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the hist case a bit further down, one could use a TFormula, but since it is just a subtraction, not even needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will not work since the value can be anything and must not be a float.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then why do you cast it to a TH1F* where the F stands for float? (Also, how are low and high then supposed to work if it is not a float?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Also, how are low and high then supposed to work if it is not a float?)

Btw, TAxis::GetXmax() returns a double?!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our case, it should rather depend on the data in the root file, should it not?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can pass any number to TH1F::Fill() which will find the proper bin to increase. The type of the histogram only defines the maximum value which can be stored per bin. TH1C can only store values less than 128 per bin. TH1F stores the bin content in a float value.

These are the values you have to pass to the constructor which is the same for all types of one dimensional histograms

[in] | name | name of histogram (avoid blanks) | const char*
[in] | title | histogram title. | const char*
[in] | nbins | number of bins | int
[in] | xlow | low edge of first bin | double
[in] | xup | upper edge of last bin (not included in last bin) | double

TAxis::GetMax returns the value value of the upper edge of the last bin which is a double.

Copy link
Member

@dennisklein dennisklein Jul 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thx, this I misunderstood. But then the common type to fill the histogram with is just double? So, read the tree content into doubles (instead of float as I did in my suggestion)?

Copy link
Member

@dennisklein dennisklein Jul 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or as a question: Which H1F API is used by TTree::Draw to add its stored values to the histogram?

Copy link
Member

@dennisklein dennisklein Jul 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And whatever the code looks like, my points are:

  1. Why do we need to involve a TCanvas object, a TPad object and a function called Draw - all clearly graphics related entities - in a script that does not want to produce any graphics? Especially, if we have to create a random TCanvas object in the middle which is not used and just for some side effect? Don't you agree that this is a terrible program to write, read and maintain (source code readability AND computational complexity)?
  2. Can we produce the histogram object without the indirection through the global object gPad?

    Rewrite function GetLeafNames
    Use solution proposed by @dennisklein in FairRootGroup#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.
Copy link
Member

@ChristianTackeGSI ChristianTackeGSI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't follow all the discussions. So please excuse, if I duplicate anything! (In that case, just write a short "dupe" and press the resolve button!)

Comment on lines +185 to +187
TString command = leafName + "-" + leafName1 + ">>hist(20, -10.,10.)";
tree1->Draw(command);
auto hist = static_cast<TH1F*>(gPad->GetPrimitive("hist"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please call this histogram hdifferences or something like that?

Comment on lines +63 to +67
TKey* key;
TIter keyIterator(keylist);
TString treeName;
int numTreeInFile{0};
while ((key = static_cast<TKey*>(keyIterator()))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be replaced by a TRangeDynCast?
Something like this maybe?

for (auto key : TRangeDynCast<TKey const>(keylist)) {
    if (!key) {
        continue;
    }
    …
}


bool operator==(TH1 const&, TH1 const&);

int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int TreeCompareAuto(TString fileName1 = "", TString fileName2 = "")
int TreeCompareAuto(TString fileName1, TString fileName2)

We anyways expect non-empty arguments.

@ChristianTackeGSI
Copy link
Member

Hi @fuhlig1 could you consider adding a compile check for the macro like in #1421?

Copy link
Member

@ChristianTackeGSI ChristianTackeGSI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I recently learned, ReadObj returns an owning pointer.

Comment on lines +68 to +72
if (key->ReadObj()->InheritsFrom("TTree")) {
treeName = key->GetName();
std::cout << "Found TTree with name " << treeName << std::endl;
numTreeInFile++;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TKey::ReadObj returns an owning pointer (see #1461).

Maybe move this in front of the loop?

    std::unique_ptr<TTree> tree1;

And then:

Suggested change
if (key->ReadObj()->InheritsFrom("TTree")) {
treeName = key->GetName();
std::cout << "Found TTree with name " << treeName << std::endl;
numTreeInFile++;
}
tree1.reset(key->ReadObject<TTree>());
if (tree1) {
treeName = key->GetName();
std::cout << "Found TTree with name " << treeName << std::endl;
numTreeInFile++;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants