Smart Pointers in RDF and legacy compatibility


ROOT Version: 6.26.06
Platform: MacOS Ventura 13.2.1
Compiler: clang14.0.3


Hello everyone,

Is there an easy way to create something like a shared pointer for e.g. a ROOT::RDF::RResultPtr<TH1D> and a TH1D* object? Sorry if the question does not make sense, I have started working with RDF only recently. Please let me know if I can clarify.

For most uses, using smart pointers has not been an issue, but there were some specific cases where it seemed to matter, in my case with TSpectrum::Search. It seems the object containing the data needs to have a viable conversion to TH1*, which seems was not possible with ROOT::RDF::RResultPtr<TH1D>. It seems there is no trivial way to typecast a ROOT::RDF::RResultPtr<TH1D> object as a TH1D* one, unless, of course, I dereference the object first and define a new pointer, which is my current solution. But that way I cannot exploit the advantages of pass by pointer anymore which would be quite useful to me.

What I first had actually was to define histograms already as TH1D*, but then I realised that this goes against the lazy action philosophy of RDF, essentially starting the event loop every time I defined a new histogram.

Thanks a lot in advance!

Dear @eska ,

Thank you for reaching out. I don’t know the specific workflow of your application, but this should work:

ROOT::RDataFrame df{...};
auto h = df.Histo1D(...);

TSpectrum s;
s.Search(h.GetPtr());

The RResultPtr::GetPtr method returns a pointer to the encapsulated object.

Cheers,
Vincenzo

1 Like

Hi Vincenzo,

Beautiful, this works perfectly, thanks so much!!

If I may ask, there was one thing I was wondering about. Is the inverse also possible? I.e. converting a TH1D* to a ROOT::RDF::RResultPtr<TH1D>. I did not realise before, but I use a TH2::ProjectionX somewhere in my code, which calls the same function in which I perform the peak finding, but the problem is that there is no method named TH1D::GetPtr so I get an error.

Edit: Removed a question about dot and arrow operators, as I realised that RResultPtr::GetPtr is a method of RResultPtr and not the histogram.

This is not possible since we are talking about two very different things. An RResultPtr is encapsulating a result of a particular operation inside of an RDataFrame computation graph. In particular, one such result could be a TH1D histogram. On the other hand, if you start from a TH1D histogram object there is no guarantee that this belongs to a certain RDataFrame grap. For example, you may have created it manually in some other way.

but I use a TH2::ProjectionX somewhere in my code, which calls the same function in which I perform the peak finding, but the problem is that there is no method named TH1D::GetPtr so I get an error

I don’t understand what is the problem here. If you need to create a TH2D object as a part of your RDataFrame computations, then you have the Histo2D operation. Then you can get the ProjectionX from the RResultPtr<TH2D> resulting from that call. But maybe you have something different in mind, let me know.

Cheers,
Vincenzo

I see, that makes a lot of sense. Thanks a lot for the clarification.

Sorry, I think a simplified example may help demonstrate what i wanted to ask.

template<typeame T>
double getPeak(T hist){
    TSpectrum s;
    int npeaks = s.Search(h.GetPtr()); // works if T is ROOT::RDF::RResultPtr<TH1D>, fails if T is TH1D*
    //int npeaks = s.Search(h); // works if T is TH1D*, fails if T is ROOT::RDF::RResultPtr<TH1D>
    double peak = 0;
    //peak = ...; //pick the desired peak, not relevant for issue
    return peak;
}

template<typename T>
void build1dPlot(T hist){
    TString name = hist->GetName();
    double peak = getPeak(hist);
    hist->Fit("f","gaus",peak-20,peak+20);
    TCanvas * c = new TCanvas();
    c->cd();
    hist->Draw();
    c->Update();
    c->SaveAs(name+".png");
}

void main() {
    ROOT::RDataFrame df{...};
    auto h = df.Histo1D(...);
    auto h2 = df.Histo2D('x','y');
    build1dPlot(h);                 // WORKS
    build1dPlot(h2->ProjectionX()); // Error: TH1D* has no member named GetPtr
}

So my problem comes from the fact that I use the same function for both h and h2->ProjectionX() to find the peak within my function that builds the plots. I do many customisations to the plots, so for me to have these functions is essential. If possible, I want to avoid as much as possible overloads to keep my code relatively clean, which is why I started using templates.

One simple solution is to simply define a new histo auto h2x = df.Histo1D('x') but using TH2::ProjectionX would be more handy for me.

Please let me know if I can clarify more.

One way to do what you want with minimal disruption (have not tested the code but it should give you the idea):

double getPeak(TH1D &h){
    TSpectrum s;
    int npeaks = s.Search(&h);
    //int npeaks = s.Search(&h);
    double peak = 0;
    //peak = ...; //pick the desired peak, not relevant for issue
    return peak;
}

void build1dPlot(TH1D& hist){
    TString name = hist.GetName();
    double peak = getPeak(&hist);
    hist.Fit("f","gaus",peak-20,peak+20);
    TCanvas * c = new TCanvas();
    c->cd();
    hist.Draw();
    c->Update();
    c->SaveAs(name+".png");
}

void main() {
    ROOT::RDataFrame df{...};
    auto h = df.Histo1D(...);
    auto h2 = df.Histo2D('x','y');
    build1dPlot(*h);
    build1dPlot(*h2->ProjectionX());
}

or you can add a simple function that always normalizes to TH1D* and pass every histogram through that:

TH1D *GetHistPtr(RResultPtr<TH1D> &r) { return r.GetPtr(); }
TH1D *GetHistPtr(TH1D *h) { return h; }

Hi @eguiraud,

Thanks a lot! I did a few first tests with your second suggestion and it seems to work like a charm.

Cheers

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