I’m having problems with adding trees to TChain. I have multiple root files, and some of them include more than one tree (all have similar structure and name). But when I’m adding the files to my chain, it seems that only the first tree from every file is added. My analysis class is made by MakeClass and I create the chain in the header file like this:
TChain *chain = new TChain(“cms_totem”,"");
If you use TChain::Add with just the path to a .root file, the TTree that is added is the one with the name that you gave to the TChain. In your case, that’s “cms_totem”. In TChain::Add, instead of just the path to a .root file, you can “continue” the path name to include directories inside the root file and the names of specific trees. So you can do:
tc.Add("path/to/file.root"); // Adds "tree1" from file.root
tc.Add("path/to/file.root/othertree"); // Adds the othertree.
I’ve never used this functionality myself, but that’s what’s written in the docs.
Okay, that’s one way to do it, but not so nice when I have almost 100 root files and they include different number of trees. And in my case all the TTrees have the same name (if multiple trees, they are named “cms_totem;1”, “cms_totem;2” etc.), so shouldn’t they all be added automatically just by doing
Objects with the same name in .root files are given “cycles” (I think that’s the term) with the ;1, ;2, ;3,…extension to tell them apart. From what I remember, the default behavior in ROOT is to only use the “youngest” cycle ;1 and ignore the others. So if you have a file with tree;1 and tree;2 and you do file.Get(“tree”), it will get tree;1.
To access older cycles I think you have to specify them explicitly. Fortunately TTree::Add says that it supports wildcard * characters, so you could try “cms_totem;*” Does that work?
No, I’ve tried that too but wilcard * works only in the root file name, for example
but not like “cms_totem;*”.
Now I’m thinking to solve this by using TDirectoryFile::GetListOfKeys() to create a TList from which to parse cycles and the size, and then to loop over all the trees for every file separately. Not the easiest way though…
Looping over the GetListOfKeys might work, but as you note, it’ll be tricky to get right. If truly all your TTrees have the same name, then you could just loop over integers until you run out of trees in that file, like this:
That might be easier than trying to iterate over the list returned by GetListOfKeys. I’m assuming the cycle numbers are contiguous, if “middle cycles” have been deleted it’ll need extra code. Also note that the “-1” is very important, according to the docs, if that parameter is non-negative, TChain::Add always returns success (see the end of http://root.cern.ch/root/html/TChain.html#TChain:Add@1).
Thanks for the code. But unfortunately all the cycles doesn’t start from 1, for example in one file I have only “cms_totem;2” and “cms_totem;3”. So the code needs to be modified a bit. I haven’t checked all the files, but if some includes, say, cycles 2 and 4 it might be a bit tricky to loop with the way you are supposing. Or are there some tricks for that?
I don’t know what happens if you try to run an analysis on a TChain to which were added non-existent TTrees. Possibly nothing bad happens even you add “cms_totem;i” for i in 0…9999, but that probably depends on the details of how you are doing the analysis.
I can think of a trick to identify the valid keys: use TFile::Get to try to get the “cms_totem;i” for a bunch of i values. If the key was for a valid TTree, then the pointer returned is non-null. If it was an invalid one, it’s null, so you could try something like:
for(int i = 0; i < 9999; i++)
TString treename = TString::Format("cms_totem;%d",i);
TTree * mytree = (TTree *)myfile->Get(treename);
int status = mychain.Add(TString("/path/to/file.root/"+treename,-1);
assert(status == 1);
I put the assert in there as a double-check. Basically this would use TFile::Get to test if the key is valid. It might be slow though, as it probably retrieves a chunk of the TTree from the file.