TTree reading

I am trying to read a tree, which was written to file in C++ and some class objects were saved to the tree.

I would like to read the tree using pyroot, but am getting a strange error.

If I do the following:

      self.fUniqueID    = int()  # UInt_t
      self.fBits        = int()  # UInt_t
      self.Number       = int()  # Long64_t
      self.ReadTime     = float() # Float_t
      self.ProcTime     = float() # Float_t
      self.ProcessID    = int()  # Int_t
      self.Weight       = float() # Float_t
      self.ScalePDF     = float() # Float_t
      self.AlphaQED     = float() # Float_t
      self.AlphaQCD     = float() # Float_t
      self.size         = int()  # Int_t
      
      self.chain.SetBranchAddress('Event.fUniqueID',self.fUniqueID)
      self.chain.SetBranchAddress('Event.fBits',    self.fBits)
      self.chain.SetBranchAddress('Event.Number',   self.Number)
      self.chain.SetBranchAddress('Event.ReadTime', self.ReadTime)
      self.chain.SetBranchAddress('Event.ProcTime', self.ProcTime)
      self.chain.SetBranchAddress('Event.ProcessID',self.ProcessID)
      self.chain.SetBranchAddress('Event.Weight',   self.Weight)
      self.chain.SetBranchAddress('Event.ScalePDF', self.ScalePDF)
      self.chain.SetBranchAddress('Event.AlphaQED', self.AlphaQED)
      self.chain.SetBranchAddress('Event.AlphaQCD', self.AlphaQCD)
      self.chain.SetBranchAddress('Event_size',     self.size)

where this code lives in a class and the chain is a TChain. In the initialization of the variables where I’d like the data to be stored upon calling “GetEntry()”, I have in the comment what type the variable is inside the TTree. However, I get the error below when calling the “SetBranchAddress” function with one of the variables which were initialized with “float()”. If I change the initialization to “int()” the program runs without complaint, but the data is incorrect (always 0).

  File "/afs/cern.ch/work/c/childers/snowmass/analysisCode/treeAccess/DelphesBranchEvent.py", line 24, in __init__
    self.chain.SetBranchAddress('Event.ReadTime', self.ReadTime)
TypeError: none of the 3 overloaded methods succeeded. Full details:
  Int_t TChain::SetBranchAddress(const char* bname, void* add, TBranch** ptr = 0) =>
    could not convert argument 2
  Int_t TChain::SetBranchAddress(const char* bname, void* add, TBranch** ptr, TClass* realClass, EDataType datatype, Bool_t isptr) =>
    takes at least 6 arguments (2 given)
  Int_t TChain::SetBranchAddress(const char* bname, void* add, TClass* realClass, EDataType datatype, Bool_t isptr) =>
    takes at least 5 arguments (2 given)

I’ve also tried directly accessing the variables using the TTree:

tree.class_name.variable

but since the data was saved to the tree using a C++ class, which I have not (and cannot) load into python, it fails.

Is there a better way to do this (besides working in C++ of course)?

Hi,

the reason why use of int() seems to be accepted, is b/c it generates an integer value of 0, which by convention is allowed to mean NULL (i.e. (void*)0) as well. That won’t actually work, of course.

It is not possible, in general, to take pointers to (small) integers in python b/c these values are cached and reused (with some exceptions, though), so if the value were to change through a pointer, you’d see that e.g. ‘0’ or ‘1’ would no longer be that value in other parts of the program. Same is true, but in a more limited extent, for floats.

In addition, in python an int is actually a C++ long and float is a C++ double. Are these what you need?

The normal approach to get a pointer to a valid memory location of the right type, is to create an array from module array of size 1. You can SetBranchAddress on that. However, in this case it seems that you could simply create a struct:ROOT.gROOT.ProcessLine("struct Event { int fUniqueID; ... };") myevent = ROOT.Event();and then use that object. See $ROOTSYS/tutorials/pyroot/staff.py for an example.

Cheers,
Wim

OK, So I must define a data structure via this ‘ProcessLine’ function. So I’ve tried running this code:

from ROOT import *
def main():
   gROOT.ProcessLine(
   '#include "TObject.h" \
   class LHEFEvent: public TObject{ \
      public: \
      Int_t ProcessID; \
      Float_t Weight; \
      Float_t ScalePDF; \
      Float_t AlphaQED; \
      Float_t AlphaQCD; \
      Long64_t Number; \
      Float_t ReadTime; \
      Float_t ProcTime; \
      LHEFEvent(void){}; \
   }; \
   ')
   event = LHEFEvent()

but get this error:

Traceback (most recent call last):
  File "./test.py", line 55, in <module>
    main()
  File "./test.py", line 45, in main
    event = LHEFEvent()
NameError: global name 'LHEFEvent' is not defined

Hi,

remove the #include. It’s both unnecessary (as TObject is known already) and it’s not legal C++ to follow a #include with a class definition on the same line (if you do need a #include, you can split it over 2 ProcessLine calls).

Of course, initially, I was under the impression you had a simple struct. Since it seems not, it’s better to put the class definition in a file and generate a dictionary for it (this would also be true for use from C++). For example, that default constructor that’s there in the class definition won’t work properly if defined on ProcessLine.

Cheers,
Wim

Also, I see responders frequently reference this staff.py tutorial, but it is writing to file, not reading… could be expanded. Also, I cannot run it as it complains that the ‘cernstaff.dat’ is missing. I’m running on lxplus from this version:
/afs/cern.ch/sw/lcg/app/releases/ROOT/5.30.04/x86_64-slc5-gcc43-opt/root/tutorials/pyroot/staff.py

I also tried:

from ROOT import *

gROOT.ProcessLine(
'#include "TObject.h" \
struct LHEFEvent{ \
   UInt_t fUniqueID; \
   UInt_t fBits; \
   Int_t ProcessID; \
   Float_t Weight; \
   Float_t ScalePDF; \
   Float_t AlphaQED; \
   Float_t AlphaQCD; \
   Long64_t Number; \
   Float_t ReadTime; \
   Float_t ProcTime; \
}; \
')

def main():
   event = LHEFEvent()

and still get the error:
NameError: global name ‘LHEFEvent’ is not defined

if I use “event = ROOT.LHEFEvent()” that also doesn’t work.

Hi,

again: remove the #include.

Cheers,
Wim

Thanks for the info.

So I should just compile the C++ code with the dictionary into a library? How do I load a C++ library into PyROOT?

Cheers,
Taylor

Hi,

you either rely on the auto-loader (by having a rootmap file generated), or use gSystem.Load(). Another easy way is to use gROOT.LoadMacro(‘somefile.c+’) where the ‘+’ will tickle ACLiC to run.

Cheers,
Wim