List of TH1F is not returned from the function

Hello,
I have recently made ‘brew upgrade’ (that was not a proper intension)
Now I can not get a list of TH1F from function in python 3.8 .
How should it be done?
The code takes a list of filenames and TH1F name and should give the list of TH1Fs
The code is below:

def get_hists(file_names, hist_name, mark):
    NPoints = len(file_names)
    h_array = [TH1F for y in range(0,NPoints)]
    f = [TFile for y in range(0,NPoints)]

    for i in range(0,NPoints):
        f[i] = TFile.Open(file_names[i])
        hist_new_name = hist_name+'_{0:d}'.format(i)+mark 
        h = f[i].Get(hist_name).Clone(hist_new_name) 
        h_array[i] = h  
        h_array[i].SetTitle(hist_new_name)
    
    return h_array

This is how it is implemented:

files = ['FIle_0.root',...,'File_20.root']
h_array  = [TH1F for i in range(0,NPoints)]
h_array  = get_hists(files  , 'h_y_endpoint', '_B0_i'  )

c = TCanvas()
h_array[0].Draw()

All the list elements have ‘None’ type.
When I draw inside the function in the loop everything is OK.
But as soon as the file is changed, the histogram is ‘None’.
The error is:
File “./plot_Y.py”, line 268, in
h_array[0].Draw()
AttributeError: ‘CPyCppyy_NoneType’ object has no attribute ‘Draw’


Please read tips for efficient and successful posting and posting code

ROOT Version: 6.22/00
Platform: MAC OS 10.15.4
Compiler: Apple clang version 11.0.3 (clang-1103.0.32.59)


I think @etejedor can help you with this issue

Hi,

From reading the code I don’t see any obvious problem, since you are cloning the histograms, so that when the files are destroyed you should keep the cloned ones (their references are stored in h_array).

What happens if, after the loop (still inside the function), you try to Draw h_array[0]?

Hello,
Inside the function, it works without errors
Here is how print (h_array) works before (0), inside (1) and after (2) the function
`
print(h_array)

h_array = get_hists(files , ‘h_y_endpoint’, ‘_B0_i’ )

print(h_array)
`
Output:
0: [<class cppyy.gbl.TH1F at 0x7fbdd9606f80>, …, <class cppyy.gbl.TH1F at 0x7fbdd9606f80>]
1: [<cppyy.gbl.TH1F object at 0x7fbdd9d85840>, …, <cppyy.gbl.TH1F object at 0x7fbdd93e5560>]
2: [None, None, None, …, None]

Ok thank you, can you let me know what ROOT version you were using before so I can compare?

Hello,
I think it was 20.04, I will try to switch back to it

By the way,
Are you aware of this thing?
https://root.cern.ch/root/html524/src/TMath.h.html#411
It was strongly criticized and recommended never to use ROOT for calculations during the sPHENIX collaboration meeting 2 days ago:

cout << TMath::ACos(2) << endl;
0

cout << acos(2) << endl;
nan

Hi,

I tried the following example:

import ROOT

def foo():
    f = ROOT.TFile.Open("./tutorials/hsimple.root")
    h1 = f.Get('hpx')
    h2 = h1.Clone('h2')
    print("H1", h1)
    print("H2", h2)
    return h2

hist = foo()

print("FINAL HIST", hist)

both with 6.22 and 6.20 and the result is the same: the final histogram becomes None because, when its TFile is being destructed, it destroys its associated histograms too. I think this is what happens in your code too.

In any case, there is a solution to free the histogram from being tied to the TFile. You need to write:

def get_hists(file_names, hist_name, mark):
    NPoints = len(file_names)
    h_array = [TH1F for y in range(0,NPoints)]
    f = [TFile for y in range(0,NPoints)]

    for i in range(0,NPoints):
        f[i] = TFile.Open(file_names[i])
        hist_new_name = hist_name+'_{0:d}'.format(i)+mark 
        h = f[i].Get(hist_name).Clone(hist_new_name)
        h.SetDirectory(0)  # notice this line !!!! 
        h_array[i] = h  
        h_array[i].SetTitle(hist_new_name)
    
    return h_array

As for the TMath question, perhaps @moneta can comment on it.

Hello @etejedor,
This solution is working
Thank you, the issue was probably due to the fact that previously I have not been using separate files, but was reading from one.

Cheers,
Evgeny

Hi,

Thank you for noticing that TMath::Acos(2) returns 0. A protection to avoid values outside the [-1,1] range was added many years ago. I don’t know the reason. I think it is more correct to return a NaN as in std::acos.

I will make a PR for this,
Thank you

Lorenzo

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