[ROOT] TTree, TBranch, TClonesArray and names of Branches

From: Christian Holm Christensen (cholm@hehi03.nbi.dk)
Date: Tue Apr 18 2000 - 15:00:03 MEST


Hi ROOT'ers, 

I have a few questions to the routines that generate names for
TBranch'es, and how to Scan/Draw/Query a TTree. 

Suppose you have the simple classes FooTable, and FooData: 

  class FooData : public TObject {
  private:
    Float_t fData;
  public:
    FooData(void) : fData(0) {}
    FooData(Float_t data) : fData(data) {}

    Float_t GetData(void) const { return fData; }
    void    SetData(Float_t data) { fData = data; }

    ClassDef(FooData,1) // 
  };

  class FooTable : public TObject { 
  private:
    TClonesArray* fArray;
    Char_t        fName[16];
    Int_t         fUser;
    Int_t         fIndex;
  public:
    FooTable(void) :  fUser(-1), fIndex(0) { 
      strcpy(fName, "Table");
      fArray = new TClonesArray("FooData"); }
    FooTable(const Char_t* name, Int_t user) : fUser(user), fIndex(0) {
      strncpy(fName,name,15);
      fArray = new TClonesArray("FooData"); }

    void     Add(Float_t x) { new((*fArray)[fIndex++]) FooData(x); }
    FooData* At(Int_t i) { return (FooData*)fArray->At(i); }
    void     SetUser(Int_t user) { fUser = user; }
    Int_t    GetUser(void) const { return fUser; }
    Char_t*  GetName(void) const { return fName; }

    ClassDef(FooTable,1) // 
  };

Now I want to make one TTree with two TBranch'es, both containing
FooTable objects: 

  ClassImp(FooTable);
  ClassImp(FooData);
  TROOT fooRoot("FooRoot", "FooRoot");

  int main(int argc, char** argv) {
    TFile* file      = new TFile("File.root", "RECREATE");
    FooTable* table1 = new FooTable("Table1", 10);
    FooTable* table2 = new FooTable("Table2", 20);

    TTree* tree      = new TTree("Tree", "A Foo Tree");
    TBranch* branch1 = tree->Branch(table1->GetName(), "FooTable", &table1);
    TBranch* branch2 = tree->Branch(table2->GetName(), "FooTable", &table2);

    for (Int_t i = 0; i < 10; i++) {
      table1->Add((Float_t)i / 10 + i); 
      table2->Add(i * 10 + i);
    }

    tree->Fill();
    tree->Print(); 
    tree->Write();
    file->Close();
    return 0;
  }

Now, when I look at the outout from TTree::Print(), I see the
following TBranch names:

   Table1
   fArray_
   fArray.fData 
   fArray.fUniqueID
   fArray.fBits
   fUser
   fName[16]
   fIndex
   fUniqueID
   fBits
   Table2
   fArray_
   fArray.fData 
   fArray.fUniqueID
   fArray.fBits
   fUser
   fName[16]
   fIndex
   fUniqueID
   fBits

That is Table1::fArray::fData and Table2::fArray::fData _both_ have
the _same_ name: fArray::fData. This means, when I try to
Scan/Draw/Query the TTree for these members, I allways get the first
occurence, that is Table1.fArray:  

  root [0] gSystem->Load("libFoo.so");
  root [1] TFile* file = new TFile("File.root", "READ");
  root [2] TTree* tree = (TTree*)file->Get("Tree"); 
  root [3] tree->Scan("fArray.fData[1]", "fUser==10")
  ************************
  *    Row   * fArray.fD *
  ************************
  *        0 *       1.1 *
  ************************
  ==> 1 selected entry
  root [4] tree->Scan("fArray.fData[1]", "fUser==20")
  ************************
  *    Row   * fArray.fD *
  ************************
  ************************
  ==> 0 selected entries

The last line fails misarably, because the first entry of fUser is 10
- it never sees the second entry where fUser == 20. Now, if I get the
branches specifically, then everything is fine:
  
  root [5] FooTable* table1 = new FooTable
  root [6] FooTable* table2 = new FooTable
  root [7] TBranch* branch1 = tree->GetBranch("Table1")
  root [8] TBranch* branch2 = tree->GetBranch("Table2")
  root [9] branch1->SetAddress(&table1)
  root [10] branch2->SetAddress(&table2)
  root [11] tree->GetEntry()
  (Int_t)273
  root [11] table1->GetUser()
  (Int_t)10
  root [12] table2->GetUser()
  (Int_t)20
  root [13] table1->At(1)->GetData()
  (Float_t)1.10000002384185791e+00
  root [14] table2->At(1)->GetData()
  (Float_t)1.10000000000000000e+01

Now, I really want to use the Scan/Draw/Query functionallity of TTree,
so looking into the code of that class, I figured out I could set
names of my TBranches to end in a '.': 

    FooTable* table1 = new FooTable("Table1.", 10);
    FooTable* table2 = new FooTable("Table2.", 20);

    TTree* tree      = new TTree("Tree", "A Foo Tree");
    TBranch* branch1 = tree->Branch(table1->GetName(), "FooTable", &table1);
    TBranch* branch2 = tree->Branch(table2->GetName(), "FooTable", &table2);

Doing that, I get the list of TBranch names: 

   Table1
   Table1.fArray_
   Table1.fArray.fData 
   Table1.fArray.fUniqueID
   Table1.fArray.fBits
   Table1.fUser
   Table1.fName[16]
   Table1.fIndex
   Table1.fUniqueID
   Table1.fBits
   Table2
   Table2.fArray_
   Table2.fArray.fData 
   Table2.fArray.fUniqueID
   Table2.fArray.fBits
   Table2.fUser
   Table2.fName[16]
   Table2.fIndex
   Table2.fUniqueID
   Table2.fBits

And now it's possible to uniquely identify my entries: 

  root [0] gSystem->Load("libFoo.so");
  root [1] TFile* file = new TFile("File.root", "READ");
  root [2] TTree* tree = (TTree*)file->Get("Tree");
  root [3] tree->Scan("Table1.fArray.fData[1]")             
  ************************
  *    Row   * Table1.fA *
  ************************
  *        0 *       1.1 *
  ************************
  root [4] tree->Scan("Table2.fArray.fData[1]")
  ************************
  *    Row   * Table2.fA *
  ************************
  *        0 *        11 *
  ************************

However, when I get my TBranch'es  

  root [5] FooTable* table1 = new FooTable
  root [6] FooTable* table2 = new FooTable
  root [7] TBranch* branch1 = tree->GetBranch("Table1.")
  root [8] TBranch* branch2 = tree->GetBranch("Table2.")
  root [9] branch1->SetAddress(&table1)
  root [10] branch2->SetAddress(&table2)
  root [11] tree->GetEntry()
  (Int_t)228
  root [12] table1->GetUser()
  (Int_t)10
  root [13] table2->GetUser()
  (Int_t)20
  root [14] table1->At(1)->GetData()
  Error: illegal pointer to class object At(1) 0x0 441  FILE:/tmp/fileYb8Tcw_cint LINE:1
  *** Interpreter error recovered ***
  root [15]  table2->At(1)->GetData()
  Error: illegal pointer to class object At(1) 0x0 441  FILE:/tmp/fileSyRJwq_cint LINE:1
  *** Interpreter error recovered ***

it fails misarbly, because the fArray member of the objects table1 and
table2 wasn't read in when TTree::GetEntry() was performed. The remedy
is to get the TBranch'es Table1.fArray, and Table2.fArray, and then
set address of those TBracnh'es to the FooTable::fArray's addresses. 

And now for the question: is this right? Am I missing something here?
If not - is this really what is intended? It seems, that _either_ you
can Scan/Draw/Query your TTree, _or_ you can get the TBranches by
hand, you can't have it both ways in an easy way. (I really don't want
to set the addresses of several Sub-TBranch'es. No more then the
Top-TBranch'es addresses should be set by the user.) Further, you have
to decide at creation time which sort of TTree you want to create:
_either_ one for queries, _or_ one for branch analysis. I, for one can
imagine several instances where you'd like to do both. 

I've attched the full source code for this example for your pleasure
and reference. 

Thanks for all your help and patience, 

Christian  -----------------------------------------------------------
Holm Christensen                             Phone:  (+45) 35 35 96 91 
  Sankt Hansgade 23, 1. th.                  Office: (+45) 353  25 305 
  DK-2200 Copenhagen N                       Web:    www.nbi.dk/~cholm    
  Denmark                                    Email:       cholm@nbi.dk

 

  










This archive was generated by hypermail 2b29 : Tue Jan 02 2001 - 11:50:24 MET