Modify the parameters of Histo2D in RDF

ROOT Version: 6.24/02
Platform: Not Provided
Compiler: Not Provided


Hi, recently I’m using RDF to generate Histo2D images, but I cannot customize the image parameters.
Part of the code is as follows

auto depositEt1 = dt.Histo2D({"depositEt1","depositEt1",bin,low,high,bin,low,high},"depositEnergyt78","depositEnergyt7");

	TCanvas *c1 = new TCanvas("c1","c1",0,0,1500,1000);
	c1->SetTopMargin(0.1);
	c1->SetBottomMargin(0.13);

	depositEt1->DrawCopy("colz");
//	depositEt1->DrawClone("colz");
//	depositEt1->Draw("colz");
	gStyle->SetPalette(1);
	depositEt1->SetContour(100);
        depositEt1->SetMaximum(200);
	depositEt1->SetMinimum(0);
	depositEt1->SetStats(0);

	depositEt1->SetTitle("");
	depositEt1->GetXaxis()->SetTitle("Channel Si #7+8");
	depositEt1->GetYaxis()->SetTitle("Channel Si #7");

	depositEt1->GetYaxis()->SetLabelFont(12);
	depositEt1->GetYaxis()->SetLabelSize(0.05);
	depositEt1->GetYaxis()->SetTitleSize(0.05);
	depositEt1->GetYaxis()->SetTitleOffset(1);
	depositEt1->GetYaxis()->SetTitleFont(22);

	depositEt1->GetXaxis()->SetLabelFont(12);
	depositEt1->GetXaxis()->SetLabelSize(0.05);
	depositEt1->GetXaxis()->SetTitleSize(0.05);
	depositEt1->GetXaxis()->SetTitleOffset(1.2);
	depositEt1->GetXaxis()->SetTitleFont(22);

	depositEt1->GetXaxis()->CenterTitle();
	depositEt1->GetYaxis()->CenterTitle();

Finally, I got an image like this

As you can see, all the statements in my code that adjust the image parameters were not executed.

Thanks a lot
han

Try with:
TH2D *depositEt1 = (TH2D*)depositEt2->DrawCopy("colz");

Thanks.
I tried this but there was an error saying

error: cannot initialize a variable of type 'TH2D *' with an rvalue of type 'TH1 *'
        TH2D *depositEt1 = depositEt2->DrawCopy("colz");

May be:

TH2D *depositEt1 = (TH2D*)depositEt2->DrawCopy("colz");

Thanks!
It works.

As an explanation, in your original example the DrawCopy was creating a copy of the histogram and drawing that, and then you were modifying not the copy, but the original histogram.

With the fix suggested, you are getting a handle to the copy and modifying that. Alternatively you can not draw a copy, but draw the histogram returned by RDF itself (by just using Draw instead of DrawCopy) – you just have to make sure that the histogram does not go out of scope as long as you need it displayed.

Cheers,
Enrico

Thanks for your explanation.
Actually, at the beginning, I tried to draw the histogram returned by RDF itself, but I got a blank figure.
like this

auto depositEt1 = dt.Histo2D({"depositEt1","depositEt1",bin,low,high,bin,low,high},"depositEnergyt78","depositEnergyt7");
TCanvas *c1 = new TCanvas("c1","c1",0,0,1500,1000);
	c1->SetTopMargin(0.1);
	c1->SetBottomMargin(0.13);
depositEt1->Draw("colz");

Is this situation caused by the histogram going out of scope?

Yes, that’s the most typical case. For example, if that code is inside a function, after the function returns the depositEt1 variable goes out of scope (and the histogram disappears from the canvas, although it can be a bit unintuitive at first).

I’m confused, how can I prevent histogram go out of scope?

Like any other variable, e.g. returning it from the function.

I modified the code like this, but there was a crash. It also generates a blank figure. :disappointed_relieved:

ROOT::RDF::RResultPtr<TH2D> plot2D_color()
{
ROOT::EnableImplicitMT(39);
TChain chaint("treet");
chaint.Add("./data/*.root");
ROOT::RDataFrame dt(chaint);

auto depositEt2 = dt.Histo2D({"depositEt2","depositEt2",bin,low,high,bin,low,high},"depositEnergyt789","depositEnergyt7");
return depositEt2;
}
void plot2D_color_test()
{
	TH2D *depositEt1=(TH2D*)plot2D_color;
	TCanvas *c1 = new TCanvas("c1","c1",0,0,1500,1000);
	c1->SetTopMargin(0.1);
	c1->SetBottomMargin(0.13);
    depositEt1->Draw("colz");
}

Here is the crash message.

root [0] 
Processing plot2D_color_test.cpp...

 *** Break *** segmentation violation

===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================
#0  0x00007f55220004fc in waitpid () from /usr/lib64/libc.so.6
#1  0x00007f5521f7dfb2 in do_system () from /usr/lib64/libc.so.6
#2  0x00007f5523014c84 in TUnixSystem::StackTrace() () from /opt/root62402/lib/libCore.so
#3  0x00007f55230168fa in TUnixSystem::DispatchSignals(ESignals) () from /opt/root62402/lib/libCore.so
#4  <signal handler called>
#5  0x00007f551acd2663 in ?? ()
#6  0x00000000000003e8 in ?? ()
#7  0x00007f551d1d7031 in cling::Value::determineStorageType(clang::QualType) () from /opt/root62402/lib/libCling.so
#8  0x00007ffea5f28c10 in ?? ()
#9  0x0000000000000000 in ?? ()
===========================================================

The lines below might hint at the cause of the crash.
You may get help by asking at the ROOT forum https://root.cern.ch/forum
Only if you are really convinced it is a bug in ROOT then please submit a
report at https://root.cern.ch/bugs Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.
===========================================================
#5  0x00007f551acd2663 in ?? ()
#6  0x00000000000003e8 in ?? ()
#7  0x00007f551d1d7031 in cling::Value::determineStorageType(clang::QualType) () from /opt/root62402/lib/libCling.so
#8  0x00007ffea5f28c10 in ?? ()
#9  0x0000000000000000 in ?? ()
===========================================================

I guess

should be

auto depositEt1 = plot2D_color();

?

But then again, depositEt1 will go out of scope when plot2D_color_test() finishes executing.

After change to

auto depositEt1 = plot2D_color();

There is no difference from the original.

I mean, after I modify it like this, doesn’t it return TH2D from a function? Why depositEt1 will go out of scope when plot2D_color_test() finishes executing?

Or, could you please give me some examples for my reference? (draw the histogram returned by RDF itself directly instead of using DrawCopy)

No, as the signature says, it’s returning a RResultPtr<TH2D>.

That’s how C++ works, local variables go out of scope and their contents deleted at the end of the scope in which they were declared.

For example:

#include <ROOT/RDataFrame.hxx>
#include <TApplication.h>

ROOT::RDF::RResultPtr<TH1D> get_result() {
   return ROOT::RDataFrame(10).Define("x", "42").Histo1D("x");
}

void plot_result(TH1D &h) {
   h.Draw();
}

int main() {
   TApplication app("app", nullptr, nullptr);

   ROOT::RDF::RResultPtr<TH1D> h = get_result();
   plot_result(*h);

   app.Run();
}

Cheers,
Enrico

Thank you, Enrico.
I understand your example.
But when I change the data source to my own, the graph will not be drawn. Why is that?
Change

To

ROOT::RDF::RResultPtr<TH1D> get_result() {
ROOT::EnableImplicitMT(39);
TChain chaint("treet");
chaint.Add("./data/*.root");
ROOT::RDataFrame dt(chaint);
return dt.Histo1D("depositEnergyt7");
}

Uhm I couldn’t say. Can you please share a minimal reproducer?

sure.
plot2D_color_test2.cpp (1.4 KB)
2021-11-1~22:54:18.root (17.8 KB)

Ah I see, I guess you are running that as a macro/script rather than as a compiled program.

Then what happens is that the ROOT interpreter starts, runs your plot2D_color_test2 function until the end (at which point the histogram goes out of scope) and then returns.

The classic way in which ROOT deals with this is by registering the histograms in a global list that does not get cleared until the end of the program. You can do the same with RDFs results, but you have to do it explicitly:

void plot2D_color_test2()
{
	ROOT::EnableImplicitMT(39);
	ROOT::RDF::RResultPtr<TH1D> depositEt1 = get_result();
    // histograms created like this are owned by ROOT by default,
    // and you get a non-owning pointer to them
    TH1D *rootOwnedHisto = new TH1D(*depositEt1);
    plot_result(*rootOwnedHisto);
}

Then you have another lifetime issue: RDataFrame starts the event loop the first time you access a result, i.e. in plot_result, but by the time you are inside plot_result your input chain chaint went out of scope!

Easy solution, you don’t need the TChain variable:

ROOT::RDataFrame dt("treet", "./*.root");

Finally, I see you are using ROOT::EnableImplicitMT(39), which means you care about performance. If that’s the case (and for a better debugging experience as well) I would suggest to transform your script into a proper, compilable C++ program.

Here it is for the reproducer you shared (basically I just added a few #includes and a main function):
plot2D_color_test2.cpp (1.8 KB)

This should work as intended. I get a plot that only contains 40 entries all at 0 but I guess that’s due to the example file.

I hope this helps!
Enrico

1 Like

Thank you very much for your help. :grinning:
I understand how it works.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.