TClonesArray

Hi !
I have a little problem handling TClonesArrays in Python.
In my own Event class I have a TClonesArray that contains a list of Candidate Objects.
I am creating a new TFile in Python with a new TTree that holds my custom events and I can copy all events from the old file to the one I created in python. No Problem so far.
I can also write a fancy filter that writes out less events than in the original sample.
What I would like to do, however, is to write events with a modified TClonesArray, i.e. one where only one of the original entries survives.

I can interactively look at a random event, and do a TClonesArray.RemoveAt() and even iterate ofer the resulting TClonesArray and it gives me only the surviving items.
However, trying to write an event after performing a RemoveAt on some members of the list seems to fail.

Unfortunately, the TMatrixD magic doesn’t seem to work with TClonesArray. I would like to perform something like a Clear followed by a list[0] = survivingCandidate.
What would be a good way to do that ?
The traceback is attached. You can see that it fails at TFile::Fill()

Cheers,
Jan
traceback.txt (2.97 KB)

Jan,

not sure why you get a crash, but there may be some problems with object ownership. I think the (default) memory heuristics that PyROOT uses work just fine for TClonesArray, but maybe your script proves me wrong. Can you give me a simple example that reproduces the problem?

Cheers,
Wim

Hi Wim,

I sure can.
Please create a dictionary from the C file.
Run the following code

[code]from ROOT import TFile, TTree, gSystem, AddressOf
gSystem.Load(‘JansEvent_cc’)
from ROOT import JansEvent

ev = JansEvent()
inf = TFile(‘x.root’)
int = inf.Get(‘postCut’)
int.SetBranchAddress(‘event’, AddressOf(ev))

f = TFile(‘new.root’, ‘recreate’)
t = TTree(‘t’, ‘Test’)
t.Branch(‘event’, ‘JansEvent’, ev, 32000, 99)

int.GetEvent(0)
#ev.bList.RemoveAt(1)
t.Fill()
t.Write()
f.Close()
[/code]
Commenting out the RemoveAt line results in the crash.
Hmmm. Should have posted to the Bug database probably. Sorry.
[edit] You may have to comment out the GClonesArray.hh include.
The example should work fine without it [/edit]

Cheers,
Jan
JansEvent.h (5.29 KB)
JansEvent.c (3.56 KB)
x.root (16.3 KB)

Jan,

ok, back at this one: it took me a while to realize that not only the python, but also the equivalent .C macro crashes. I.e., I shouldn’t be searching for a problem in PyROOT, but in ROOT itself. It’s here (src/Tree/TBranchElement.cxx), as per your traceback:

TClonesArray *clones = (TClonesArray*)fObject; if (!clones) return; Int_t n = clones->GetEntriesFast(); fInfo->WriteBufferClones(b,clones,n,fID,fOffset);

Iow, once you do a RemoveAt(), (or python del, since that subsquently calls RemoveAt() again), GetEntriesFast() gives the wrong size and writing the TClonessArray en bloc causes a segfault.

A solution is to call ev.bList.Compress() just before writing (or after each RemoveAt()). After that, the writing of the TClonesArray as a block of course works.

As for “list[0] = survivingCandidate” or something similar: it’ll need to call placement new, since we’re dealing with a TClonessArray. I’m unsure how to do that generally.

HTH,
Wim

Hi Wim,

interesting fact: Calling Compress() on the TClonesArray gets rid of the crash, but the next call to an entry in the array in the next event fails to cast it down.

This happens when I use setBranchAddress to my custom class as well as when using TChain.event

Just FYI.
Cheers,
Jan

Jan,

that error I can only reproduce by accessing an unoccupied slot in the TClonesArray. Not sure how I can handle the error more appropriately: it is, after all, not quite an our of range error. Maybe an IndexError exception will do?

Cheers,
Wim

Hi Wim,

I am not sure I understand what you mean.
The problem I am having is that calling Compress() causes this Error Message on a later event. Without Compress() everything runs well. I can investigate further, but what I suspect naively is that Compress reduces the available number of slots and when I read an event with more than the changed number of available slots, I get an error. But I know nothing about the internals of TClonesArray, so this is just wild speculation. I can try to investigate further when I find some time.

Cheers,
Jan

PS: Is there another function in TCLonesArray that I should call after writing the event to disk and before reading in the next event or before accessing the candidates in the next event that could remedy this ?

You should never call TClonesArray::Compress while writing a Tree (or you have to set addresses again).
At the end or beginning of each event, call TClonesArray::Clear

Rene

Hm.
Calling TClonesArray::Clear() before the fill causes the same problem as TClonesArray::Compress(). The TObject isn’t cast down in the next event.

Traceback (most recent call last): ... AttributeError: 'TObject' object has no attribute 'gamma'
Calling Clear() after the fill causes a crash. *** Break *** segmentation violation Generating stack trace... /usr/bin/addr2line: python: No such file or directory /usr/bin/addr2line: python: No such file or directory /usr/bin/addr2line: python: No such file or directory /usr/bin/addr2line: python: No such file or directory /usr/bin/addr2line: python: No such file or directory /usr/bin/addr2line: python: No such file or directory /usr/bin/addr2line: python: No such file or directory /usr/bin/addr2line: python: No such file or directory /usr/bin/addr2line: python: No such file or directory 0x011f9ed9 in TStreamerInfo::WriteBufferClones(TBuffer&, TClonesArray*, int, int, int) + 0x41 from /u1/local/root/lib/root/libCore.so 0x007c4aa2 in TBranchElement::FillLeaves(TBuffer&) + 0x4b4 from /u1/local/root/lib/root/libTree.so 0x007b509d in TBranch::Fill() + 0x32d from /u1/local/root/lib/root/libTree.so 0x007c4526 in TBranchElement::Fill() + 0x1f0 from /u1/local/root/lib/root/libTree.so 0x007c44bb in TBranchElement::Fill() + 0x185 from /u1/local/root/lib/root/libTree.so 0x007c44bb in TBranchElement::Fill() + 0x185 from /u1/local/root/lib/root/libTree.so 0x007efdd5 in TTree::Fill() + 0xa7 from /u1/local/root/lib/root/libTree.so 0x00814e71 in <unknown> from /u1/local/root/lib/root/libTree.so 0x0178ad72 in G__CallFunc::Execute(void*) + 0x94 from /u1/local/root/lib/root/libCint.so 0x0064f046 in PyROOT::TIntExecutor::Execute(G__CallFunc*, void*) + 0x24 from /u1/local/root/lib/root/libPyROOT.so 0x00654b70 in PyROOT::TMethodHolder::CallFast(void*) + 0x2c from /u1/local/root/lib/root/libPyROOT.so 0x00654c5b in PyROOT::TMethodHolder::CallSafe(void*) + 0x4d from /u1/local/root/lib/root/libPyROOT.so 0x00654370 in PyROOT::TMethodHolder::Execute(void*) + 0xce from /u1/local/root/lib/root/libPyROOT.so 0x00654592 in PyROOT::TMethodHolder::operator()(PyROOT::ObjectProxy*, _object*, _object*) + 0x17a from /u1/local/root/lib/root/libPyROOT.so 0x00654e2f in PyROOT::(anonymous namespace)::mp_call(PyROOT::MethodProxy*, _object*, _object*) + 0x5b from /u1/local/root/lib/root/libPyROOT.so 0x0805bd60 in PyObject_Call + 0x1c from python 0x080a927f in <unknown> from python 0x080a8d37 in <unknown> from python 0x080a73b3 in PyEval_EvalFrame + 0x223b from python 0x080a91b9 in <unknown> from python 0x080a8da7 in <unknown> from python 0x080a73b3 in PyEval_EvalFrame + 0x223b from python 0x080a7e6e in PyEval_EvalCodeEx + 0x40a from python 0x080aa8ca in PyEval_EvalCode + 0x22 from python 0x080d22a5 in <unknown> from python 0x080d1b09 in PyRun_SimpleFileExFlags + 0x191 from python 0x080554cb in Py_Main + 0x56b from python 0x08054f5b in main + 0x17 from python 0x0048c78a in __libc_start_main + 0xda from /lib/tls/libc.so.6 0x08054eb9 in ldexp + 0x61 from python Traceback (most recent call last): File "cutAnalysis.py", line 71, in ? writeFilteredTuple(iSam, cuts) File "/afs/slac.stanford.edu/u/br/jstrube/BaBar/StandaloneRoot/cutLibrary.py", line 117, in writeFilteredTuple t.Fill() SystemError: problem in C++; program state has been reset
I’m open to other ideas :neutral_face:
Jan

Hi,

Can you send the instruction (and maybe additional files) to exactly reproduce your problem?

Cheers,
Philippe.

Hi Philippe,

Thanks for following up on this. Instructions:
[ul]
Get the files from here:
root.cern.ch/phpBB2/download.php?id=992
root.cern.ch/phpBB2/download.php?id=993
root.cern.ch/phpBB2/download.php?id=994
(These are the “new” Tuples from the other thread)
Run this code from this thread

[code]from ROOT import TFile, TTree, gSystem, AddressOf
gSystem.Load(‘JansEvent_cc’)
from ROOT import JansEvent

ev = JansEvent()
inf = TFile(‘x.root’)
int = inf.Get(‘postCut’)
int.SetBranchAddress(‘event’, AddressOf(ev))

f = TFile(‘new.root’, ‘recreate’)
t = TTree(‘t’, ‘Test’)
t.Branch(‘event’, ‘JansEvent’, ev, 32000, 99)

int.GetEvent(0)
#ev.bList.RemoveAt(1)
#ev.bList.Clear()
#ev.bList.Compress()
t.Fill()
int.GetEvent(1)
ev.bList[0].gamma
t.Write()
f.Close() [/code][/ul]
You may have to change the inf = TFile line. It’s the example from this thread, but the files from the other thread.
I hope this runs. If it doesn’t reproduce the problem for you, let me know, then I come up with a better example.
In summary:
[ol]
Removing items and writing to file crashes the program.
Calling Clear() or Compress() on the TClonesArray any time before TFile::Fill() causes the event to be written, but in the next event the TClonesArray entries aren’t cast down any more.
Just writing the correct candidate to position 0 in the Array and ignoring the rest (instead of RemoveAt’ing them) isn’t yet implemented in python.
Getting rid of the TClonesArray and using an STL vector doesn’t (seem to) work from the python side, either. I can create an STL vector in python, but I am not able to read it from a file.
[/ol]On this last part I only did a naive test, though. Do STL vectors need any special treatment when they are persisted ? Like extra streamer info somewhere ?
Cheers,
Jan

Hi,

Somehow the information got lost somwhere but I just fixed a problem with the generation of the TTree in the cases of your classes. In order to test, you should try with the HEAD of the cvs repository. (The object were corrupted).

Also note that the file you mention in your last mail are insufficient to compiler JanEvent.cc (missing files).
So please try with the latest version of ROOT and if it still does not work, please upload one tar file containing all the necessary files to reproduce the crash.

Cheers,
Philippe.

Hi Philippe,

Hmm. It works some way I don’t understand, but not how it’s supposed to.
Please run the attached bug.py. x.root must be in the same directory.

Please have a look at the file.
If you remove the first Clear(), you will see the failure to downcast
if you remove the second Clear() you will see the crash
if you leave in both clears, it runs without any messages, but it’s clearly wrong.

The reason why it took me a while to reproduce the problem is this:
Direct access to the contents of the TClonesArray works !
It’s when I loop over them that it shows problems.

So:
looping over them with python iterators doesn’t work, because it doesn’t cast down.
looping over them with direct access in the range (0…GetEntries()) doesn’t work, because after the Clear(), GetEntries() reports 0…
Removing the Clear() and then accessing by number again fails to cast down.
Hope that helps.
Jan
pyroot.tar (30 KB)

Your event.tar only contains soft links!:

<airdrie> ls -l total 0 JansEvent.cc -> ../SkimR16/BetaMiniUser/JansEvent.cc JansEvent.hh -> ../SkimR16/BetaMiniUser/JansEvent.rdl Please upload a working copy.

Thanks,
Philippe.

Ooops.
Sorry about that. I didn’t know tar does that by default.
This is the new one.
Jan
event.tar (20 KB)

Could please fully try your example before uploading it (by that I mean, go to a fresh new directory and do all the steps you expect me to have to do and see if they all do what you expect!)? There is yet another problem :frowning::

root [0] .L JansEvent.cc+ Info in <TWinNTSystem::ACLiC>: creating shared library c:\Download\jan\JansEvent_cc.dll 2072772110_cint.cxx c:\download\jan\JansEvent.hh(37) : fatal error C1083: Cannot open include file: 'GClonesArray.hh': No such file or direc tory Error: external preprocessing failed. FILE: LINE:0 !!!Removing c:\Download\jan\s3bc_.cxx c:\Download\jan\s3bc_.h !!! Error: rootcint: error loading headers... Error in <ACLiC>: Dictionary generation failed! Info in <ACLiC>: Invoking compiler to check macro's validity JansEvent.cc c:\download\jan\JansEvent.hh(37) : fatal error C1083: Cannot open include file: 'GClonesArray.hh': No such file or directory

Cheers,
Philippe.

Hi Philippe,

I’m sorry. I mentioned earlier in this thread that this include is not necessary for the example. Please remove it. I should have wrapped it in an #ifdef block.
Apologies.

Jan

Hi Jan,

I will try again. However I noted:struct JansEvent { #if !defined(__CINT__) typedef GClonesArray<B_Parameters> B_List; #else typedef TClonesArray B_List; #endif B_List bList; };Besides the fact that this is yet another thing I need to change to compile, is actually technically wrong and possibly very damaging. In essense this lies to CINT (and thus the ROOT I/O) on the nature of bList and
JansEvent. This has no chance of working unless GClonesArray<B_Parameters> are strictly the same in memory.

Philippe.

Hi Philippe,

GClonesArray is a wrapper around TClonesArray that just provides STL-style accessors and iterators. The data structure should be the same in memory. (I haven’t explicitly checked, which I probably should, but never noticed any problems with it)
Thanks for noting this, though.

Jan

Well … fix my fixed up version of the library and the ROOT from cvs, I have absolutely no problem. I even translated (see attachement) your python script into C++ and ran with valgrind with no problem.
So either this problem comes from your GClonesArray either there is a more complex issue. If removing the GClonesArray does not solve the issue for you please provide me with[ul]
a single tested tar file containing of the files you need
the exact instruction to follow to execute
YOUR output from following this instruction
include a stack trace in case of core dump.[/ul]
Note that to wrap a TClonesArray with a different interface we recommend to simply do (with any #ifdef):

class GClonesArray { private: TClonesArray data; // or *data public: void push_back(...);.... };In particular the ROOT I/O framework does not (completely) support class inherting from TClonesArray.

Cheers,
Philippe
bug.C (884 Bytes)