TClonesArray problem

Hi PyROOTers,

I have recently started using PyROOT and love it thus far. I’m getting into a bind with using TClonesArray though. As an example, I have code to fill a branch in a TTree with a TClonesArray of TH2Fs. In the example below, I try to populate the first element of the TClonesArray, fill the TTree and repeat 3 times.

[code]import ROOT

ROOT.gROOT.Reset()
ROOT.gStyle.SetPalette(1)

fileName = "testTCA.root"
treeName = "MyTree"
branchName = “MyBranch”

def treeWrite():

f = ROOT.TFile(fileName, “RECREATE”)
t = ROOT.TTree(treeName, “my test tree”)

tca = ROOT.TClonesArray(“TH2F”, 5);
t.Branch(branchName, “TClonesArray”, ROOT.AddressOf(tca), 3200, 0)

print "putting data into tree"
for nn in range(3):
name = “image”+str(nn)
#name = "image"
img = ROOT.TH2F(name, name, 100, 0, 100, 100, 0, 100)
print name
for ii in range(100):
for jj in range(100):
img.SetBinContent(ii, jj, (nn+1)*(ii+jj))
print "assign"
tca[0] = img
print "fill"
t.Fill()

#print "tca.Delete()"
#tca.Delete()
#print "after"

print "f.Write()"
f.Write()
print "f.Close()"
f.Close()

if name == “main”:
treeWrite()
[/code]

When I run this code, I find that the first loop completes successfully, but I get a seg. fault when trying to populate the TClonesArray the second time:

[code]$ python useTCA.py
putting data into tree
image0
assign
fill
image1
assign

*** Break *** segmentation violation
/Users/jbattat/computing/root/pyROOT/5821: No such file or directory.
Attaching to process 5821.
Reading symbols for shared libraries . done
Reading symbols for shared libraries … done
0x95a68189 in wait4 ()

========== STACKS OF ALL THREADS ==========

… much more follows …

[/code]

The “No such file or directory” line always refers to the process number of the current python process (not sure if that’s a useful clue).

I did see Wim’s earlier post (http://root.cern.ch/phpBB2/viewtopic.php?t=2704&start=25) which was a great help for me to get this far, but I could not solve my current problem with the information there.

Any help you could provide would be greatly appreciated.

By the way, I made a corresponding example in C++, which works just fine:

[code]#include
#include “TCanvas.h”
#include “TFile.h”
#include “TTree.h”
#include “TH2F.h”
#include “TString.h”
#include “TClonesArray.h”

using namespace std;

void treeWrite() {

TFile* f = new TFile(“testTCAC.root”, “RECREATE”);
TTree* t = new TTree(“MyTree”, “my test tree”);

TClonesArray* tca = new TClonesArray(“TH2F”, 5);
t->Branch(“MyBranch”, “TClonesArray”, &tca, 3200, 0);

cout << “putting data into tree” << endl;
for (int nn=0; nn<3; nn++) {
TString name = “image”;
name += nn;
cout << name << endl;
TH2F* img = new TH2F(name, name, 100, 0, 100, 100, 0, 100);
for (int ii=0; ii<100; ii++) {
for (int jj=0; jj<100; jj++) {
img->SetBinContent(ii, jj, (nn+1)*(ii+jj));
}
}
new( (*tca)[0]) TH2F(*img);
t->Fill();

// this line causes a crash in pyROOT
tca->Delete();
delete img;

}

cout << “f->Write()” << endl;
f->Write();
cout << “f->Close()” << endl;
f->Close();
}
[/code][/url]

Hi,

the difference is that the python code makes a memcpy of the object, whereas the C++ code does a copy ctor. Normally that’s okay (the object simply “moves”), but for histograms, the TFile keeps track of them, so in the python case, the TH2F is being deleted twice: once by the normal machinery, once again by the TFile.

Please use:ROOT.TH2F.AddDirectory( False )at the beginning of treeWrite() and reset it back to True at the end.

HTH,
Wim

P.S. if in the C++ example I delete the TTree, TFile, and TClonesArray, the example crashes as well.

Hi Wim,

Thank you very much for providing the solution. I can now successfully generate the output file containing TClonesArrays full of TH2F data. I’ve also been able to access the TH2F data in the file using C++, but I have a problem trying to access the data with PyROOT.

Hopefully you can provide guidance here as well.

This is the code I use to read the TH2F data from the .root file:

[code]def treeRead():

f = ROOT.TFile(fileName)
t = f.Get(treeName)

create objects to hold the tree contents

tca = ROOT.TClonesArray(“TH2F”, 5)
t.SetBranchAddress(branchName, ROOT.AddressOf(tca))

c1 = ROOT.TCanvas(“c1”, “c1”)

populate variables from tree

print "Getting data from tree"
for ii in range(t.GetEntries()):
print " item "+str(ii)
t.GetEntry(ii)

# access those variables
tca[0].Draw("colz")
c1.Update()
raw_input("hit enter to continue")[/code]

When I run treeRead(), I get the following error:

Getting data from tree item 0 Traceback (most recent call last): File "useTCA.py", line 74, in <module> tree() File "useTCA.py", line 71, in tree treeRead() File "useTCA.py", line 65, in treeRead tca[0].Draw("colz") TypeError: 'PyROOT_NoneType' object is unsubscriptable

If I run help(tca) before the t.GetEntry(ii) call, python tells me that tca is a TClonesArray. If I run help(tca) after the t.GetEntry(ii) call, python tells me that tca is PyROOT_NoneType.

By the way, I get the same error whether or not I have:

ROOT.TH2F.AddDirectory( False ) in treeRead(), and whether I do tca[0] or tca.At(0).

Here is the C++ code that successfully reads the TH2F data from the root file:

[code]void read() {
TFile* f = new TFile(“testTCA.root”);
TTree* t = (TTree*)f->Get(“MyTree”);

// create objects to hold the tree contents
TClonesArray* tca = new TClonesArray(“TH2F”, 5);
t->SetBranchAddress(“MyBranch”, &tca);

TCanvas* c1 = new TCanvas(“c1”, “c1”);

// populate variables from tree
cout << “Getting data from tree” << endl;
for (int ii=0; iiGetEntries(); ii++) {
cout << " item " << ii << endl;
t->GetEntry(ii);

// access those variables
tca->At(0)->Draw("colz");
c1->Update();

}
}[/code]

Any assistance you could provide would be greatly appreciated.

Many thanks,
James

James,

the TClonesArray gets deleted (or at least, its dtor gets called) in each GetEntry() call, I don’t know why, but it also happens in the C++ example: there, however, the previously given &tca pointer gets reset to the new address. That same mechanism, however, does not work in python: although the reset per se works, the TClonesArray notifies of its demise and that makes PyROOT set the object to None (even though its internal address is about to be changed to a fresh object). You can either do:tca.ResetBit(ROOT.TObject.kMustCleanup)to prevent the notification (must be called just after creation of tca, and after every GetEntry() in the loop, as every time the tca will be pointing to a fresh TClonesArray with kMustCleanup set). Or you can use this syntax, which will get the address explicitly:[code]def treeRead():

f = ROOT.TFile(fileName)
t = f.Get(treeName)

create objects to hold the tree contents

tca = ROOT.TClonesArray(“TH2F”, 5)
t.SetBranchAddress(branchName, tca)

c1 = ROOT.TCanvas(“c1”, “c1”)

for evt in t:
evt.MyBranch[0].Draw(“colz”)
c1.Update()
raw_input(“hit enter to continue”)[/code]
Cheers,
Wim

Hi Wim,

I really appreciate your quick replies. I used the alternate loop notation that you suggested and it worked like a charm.

Thanks again,
James