BranchStatus/TBranch::GetEntry in TChain::MakeClass skeleton

Dear ROOTers,

I’m using (with ROOT version 5.28.00h or 5.26.00e) the skeleton produced by TChain::MakeClass (well, to be precise 3 skeleton classes from 3 chains… I suspect it doesn’t matter here, but, in case, see old post TChain::MakeClass including friend chains) to write my selection and analysis algorithm.

So far I always used TChain::SetBranchStatus() before looping over the events to limit the amount of data to be read from disk, but I could do better if I could split the reading from disk in two steps. So I tried to specify with TChain::SetBranchStatus() a first set of (not-to-heavy-to-load) branches, required for the first set of cuts. Then, for events passing such cuts, I tried to manually load extra branches with TBranch::GetEntry(localentry, 1), where I expected with the 1 argument to force the reading also of non-active branches. Please take into account that each branch is a vector or a vector< vector > branch.

I get crash with sparks as soon as my code arrives to read my first non-active branch, so I’m wondering if I’m doing wrong or if this is not the intended use of the two possibility, i.e. I have to use either TChain::GetEntry() after disabling not used branches OR TBranch::GetEntry() on the branches I’m interested.

I did the following observations on the variables defined in the skeleton typically like this:

vector<float>* var;
[...]
TBranch* b_var;

They are defined as pointer (is there a reason why as a pointer, besides the fact that TChain::SetBranchAddress() wants the address of a pointer to the object?), so they are typically set to 0 until the object is allocated (and my code crashes because the object is non allocated after TBranch::GetEntry() ).

The skeleton constructor always calls the Init() method after opening, in case a new tree is not specified, the TFile used when generating the skeleton (why that? I have to delete and recreate fChain to have the list of TFiles in each specific run).

I noticed that TChain::SetBranchAddress() contained in Init() returns 5 (“Underlying TBranch not yet available so no check was made”) and both pointers, var and b_var, remains 0.

Then I noticed that TChain::GetEntry() correctly allocates both var and b_var on activates branches, while only b_var are non-null for non-activate branches.

At this point, b_var->GetEntry(localentry,1) returns 0 if the branch is non-active, and the var pointer stays zero.

What am I doing wrong? How am I supposed to use the TBranch::GetEntry() ?

Thanks a lot and sorry for this long post.
Matteo

Hi Matteo,

TBranch::GetEntry(localentry, 1)is a nop (it is disabled) for branch whose status has been set to 0.

I strongly recommend that in your case rather than relying on SetBranchStatus (i.e. do not use it at all), you have 2 sets of direct calls to branch->GetEntry (one for the light branch and one for the ‘heavy’ branches).

Cheers,
Philippe.

Thanks a lot Philippe,

I didn’t get that point. But I have still a problem, because just after Init() the branch pointer b_var is not yet allocated. A workaround is to do a TChain::GetEntry(0) just at the beginning, but in principle am I supposed to allocate it? Which TBranch constructor should I use for a vector< float > or vector< vector< float > > branch?

Is there a reason for opening the old list of TFiles (used in skeleton generation) when no tree is specified in the constructor?

Thanks again,
Matteo

Hi,

[quote] But I have still a problem, because just after Init() the branch pointer b_var is not yet allocated.
[/quote] humm … the b_var pointer should/must be initialized during Init via a call like: fChain->SetBranchAddress("var", &var, & b_var);

[quote]Which TBranch constructor should I use for a vector< float > or vector< vector< float > > branch?[/quote]When reading (MakeClass) or when writing? The ‘constructor’ should only be used during writing (hence usually not in MakeClass).

Also, please remember that the default behavior in MakeClass is to [b]not[/not] support objects. To support objects reading you need to remove the line fChain->SetMakeClass(1); … and you should also note that it will mean that all the objects needs to be read via their full object form (instead of the decomposed form preferred by MakeClass).

Cheers,
Philippe.

Hi Philippe,

Thanks for your reply.

I checked that in my case both var and b_var are still 0 after this call.

The generated skeleton contains:

void XENON100_T3skeleton::Init(TTree *tree)
{
   [...]
   var = 0; //it is a vector< vector< float > >*
   // Set branch addresses and branch pointers
   if (!tree) return;
   fChain = tree;
   fCurrent = -1;
   fChain->SetMakeClass(1);      //  <------ Here it is!!!

   fChain->SetBranchAddress("var", &var, &b_var);
   [...]
}

The data that I want to analyse are all organized in float, vector and vector< vector< float > > within the TTree.
Do you mean that I should remove the line fChain->SetMakeClass(1) ?

I admit that I’m still confused about the difference between MakeClass mode and not-MakeClass mode.

What do you mean e.g. with:

Thanks a lot again,
Matteo

Hi Matteo,

If you store complex objects in a TTree they can be split into several branches. With SetMakeClass(1), you can then set the address of ‘just’ one of the data member to its fundamental type, while without it, you need to retrieve it by setting the address of the object. For example:class Object { float fPx; float fPy; float fPz; };and create the branch:TTree *tree = new TTree("T","T"); Object obj; tree->Branch("obj.",&obj);For reading, you would need to do either:tree->SetMakeClass(1); float fPx; tree->SetBranchAddress("obj.fPx",&fPx);orObject *obj = 0; tree->SetBranchAddress("obj.",&fPx);or you could let the TTree allocate the address and retrieve it with GetAddress.

In your case, it sounds like you can simply drop the SetMakeClass(1) since you either have simple branches or vectors.

Cheers,
Philippe.