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

From: Rene Brun (Rene.Brun@cern.ch)
Date: Tue Apr 18 2000 - 15:31:16 MEST


Hi Christian,
When a branch references the same class, one can add a "." (dot) character
at the end of the branch name. When splitting the object, the generated
branches will be labelled branch.subbranch.
This information is added in 2.24.
In your example, replace the two statements:

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

The result of tree.Print will give:

******************************************************************************
*Tree    :Tree      : A Foo Tree                                             *
*Entries :        1 : Total  Size =         0 bytes  File  Size =          0 *
*        :          : Tree compression factor =   1.00                       *
******************************************************************************
*Branch  :Table1.   : Table1.                                                *
*Entries :        1 : BranchObject (see below)                               *
*............................................................................*
*Branch  :Table1.fArray_ : Table1.fArray_/I                                  *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table1.fArray.fData : fData[Table1.fArray_]/F                      *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table1.fArray.fUniqueID : fUniqueID[Table1.fArray_]/i              *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table1.fArray.fBits : fBits[Table1.fArray_]/i                      *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table1.fName[16] : fName[16]                                       *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table1.fUser : fUser                                               *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table1.fIndex : fIndex                                             *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table1.fUniqueID : fUniqueID                                       *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table1.fBits : fBits                                               *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table2.   : Table2.                                                *
*Entries :        1 : BranchObject (see below)                               *
*............................................................................*
*Branch  :Table2.fArray_ : Table2.fArray_/I                                  *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table2.fArray.fData : fData[Table2.fArray_]/F                      *
*Entries :        1 : Total  Size =         0 bytes  File Size  =          0 *
*Baskets :        0 : Basket Size =     32000 bytes  Compression=   1.00     *
*............................................................................*
*Branch  :Table2.fArray.fUniqueID : fUniqueID[Table2.fArray_]/i              *


Rene Brun

Christian Holm Christensen wrote:
> 
> 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
> 
> 
> 
> 
> 
>   --------------------------------------------------------------------------------
> 
>                    Name: Foo.hh
>    Foo.hh          Type: Plain Text (Text/Plain)
>                Encoding: 7bit
>             Description: Declaration file for classes FooTable and FooData
> 
>                    Name: Foo.cc
>    Foo.cc          Type: Plain Text (Text/Plain)
>                Encoding: 7bit
>             Description: Implementation file for classes FooTable and FooData
> 
>                           Name: FooLinkDef.hh
>    FooLinkDef.hh          Type: Plain Text (Text/Plain)
>                       Encoding: 7bit
>                    Description: Link specification file for classes FooTable and FooData
> 
>                        Name: FooMain.cc
>    FooMain.cc          Type: Plain Text (Text/Plain)
>                    Encoding: 7bit
>                 Description: Test program - writes file File.root
> 
>                      Name: Makefile
>    Makefile          Type: Plain Text (Text/Plain)
>                  Encoding: 7bit
>               Description: GNU Makefile for Linux



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