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