Appending branch of user class to existing tree

Hi PyROOTers,

I’ve defined a C++ class, TTest that inherits from TNamed, that contains a Float_t and std::vector<Float_t>. In PyROOT I was able to initialize this object, place it into a TTree, and save to a TFile. However, I want to later reopen the TFile, get the TTree, and append another TTest object as another branch. The PyROOT manual describes this with Python builtins, and the Users’ Manual mentions how to do this in C++ with TObject::kOverwrite (although I did see that AddFriend is recommended). Is there any way to accomplish this with PyROOT? I’ve attached all the scripts. For what it’s worth, the errors that I get are due to the objects not having their addresses set, and I’m not sure if / how I should AddressOf() here.

Error in <TBranchElement::Fill>: attempt to fill branch Event_0 while addresss is not set
Error in <TBranchElement::Fill>: attempt to fill branch Event_1 while addresss is not set

Thanks!
Chris

My C++ class, TTest.C:

#include "TNamed.h"
#include <iostream>
#include <vector>
class TTest : public TNamed
{
        private:
                Float_t myfloat;
                std::vector<Float_t> myvec;

        public:
                TTest() : myfloat(-1.0), myvec(-1.0, 4) {}
                TTest(const char* name) : myfloat(-1.0), myvec(-1.0, 4) {this->SetName(name); }

                Float_t Get() const { return myfloat; }
                std::vector<Float_t> getVec() { return myvec; }

                void Set(Float_t num) { myfloat = num; }
                void setVec(std::vector<Float_t> newVec) { myvec = newVec; }

        ClassDef(TTest, 1)
};
#if !defined(__CINT__)
        ClassImp(TTest);
#endif

Create TTest object and save to a TTree in a ROOT file, testTTest.py:

#!/usr/bin/python
# Compile class with `echo .L TTest.C+ | root -b`
from ROOT import gSystem, std, TTree, TFile
gSystem.Load('TTest_C')
from ROOT import TTest

object = TTest("test_obj")
object.Set(3.0) 
                
STLvec = std.vector(float)()
for i in xrange(3):
        STLvec.push_back(i)     
object.setVec(STLvec)
                
f = TFile("test.root", "recreate")
mytree = TTree("mytree", "testing tree appending")
mytree.Branch("Event_0", object) 
mytree.Fill()   
f.WriteTObject(mytree)
f.Close()

Trying to append to the already-existing TTree, testAppend.py:

#!/usr/bin/python

from ROOT import gSystem, std, TTree, TFile
gSystem.Load('TTest_C')
from ROOT import TTest

object2 = TTest("test_obj2")
object2.Set(8.0)
                
STLvec = std.vector(float)()
for i in xrange(3):
        STLvec.push_back(3.3*i)
                
object2.setVec(STLvec)
                
f = TFile("test.root", "update")
tree = f.Get("mytree")
# around here I don't know what to do...
tree.Branch("Event_1", object2)
tree.Fill()

f.WriteTObject(tree)
f.Close()

testTTest.py (417 Bytes)
TTest.C (559 Bytes)
testAppend.py (432 Bytes)

Chris,

not sure what you’re asking …

First off, in the class ctor for the vector, it is probably meant (4,-1.0) rather than (-1.0,4)? Second, I only get an error for the address of Event_0 not having been set, which is true of course, as it hasn’t been set. So … ?

Cheers,
Wim

Hi Wim,

Sorry that my question was too opaque. I’d like to take an existing TTree, which has one branch containing an object that I’ve defined, and I’d like to add a second branch containing another of my objects. The class index shows how to do this in C++ with Float_ts: root.cern.ch/root/html/TTree.html (scroll to “Adding a branch to an existing tree”), but I can’t get it to work in PyROOT with user-made objects.

And yes, I typed the vector constructor backwards in here : ) Oops.

Chris

Chris,

sorry, I still don’t see the problem. So, with a risk of stating the obvious: the example that you link to is filling a single branch (it calls Fill() on the branch), the example that you wrote is filling the whole tree (it calls Fill() on the tree). The warnings about not having an address for the earlier branch will be there in .C as well, if a fill is called on the whole tree, w/o setting an address to the existing branch (or de-activating it).

As such, either provide an address:object1 = TTest("test_obj1") tree.SetBranchAddress( "Event_0", AddressOf( object1 ) )
or call Fill() only on the newly created one, as per the example that you link.

Or what am I still missing?

Cheers,
Wim

Hi Wim,

Your response above actually did answer my question of how to append a branch to an existing tree; thanks for this. I then realized why I was confused: what I actually wanted to do was:

  1. Take an existing TTree, which already has one entry containing one of my TTest objects,
  2. Create a new object,
  3. Add this object to the TTree in a new entry,
  4. Save updated TTree.

I haven’t been able to do this in PyROOT even though it seems very basic. Sorry for the confusion.
Chris

Chris,

so, if all you want to do is add a new entry, why bother with a new branch? Just add the entry and be done:

[code]f = TFile(“test.root”, “update”)
tree = f.Get(“mytree”)
tree.SetBranchAddress(“Event_0”, object2)
tree.Fill()

f.WriteTObject(tree)
f.Close()[/code]
where object2 is your object2 created as before. Again, I’m not understanding where the pb is, as there is nothing whatsoever PyROOT specific in the above …

Cheers,
Wim

1 Like

Hi Wim,

You answered (both) my questions. I thought that I had tried what you suggested but I confused myself thinking that I had to do much more complicated things; I thought I had to use e.g. AddressOf() in PyROOT since there are no explicit pointers in python. I made it more difficult than it was.

I didn’t intend to imply there was something problematic with PyROOT, just that I didn’t know how to do something admittedly basic.

I appreciate the help.
Chris

Chris,

SetBranchAddress() is special-cased, since it is so commonly used. Either with or w/o AddressOf() on the object should work. In general, AddressOf() with a single argument is used to get a pointer to a pointer to an object, not just a pointer to an object (which should work “as-is”), although its original existence (and more common use with trees) was to get the address of a data member of a struct. The latter is needed for builtin types b/c builtin types are carried by Python as their C equivalent (that is to say, if you look e.g. at the C struct representing a python float, it has a data member of type double). Objects don’t have that problem, since they are always carried as a pointer to begin with (that is to say, the struct that represents them has a data member of type void* pointing to the actual object), which can be passed as needed.

Cheers,
Wim

Wim,

Thanks for making that clear. That will probably eliminate untold future errors on my part :slight_smile:

Chris