I have several ROOT files, all containing a single tree. I would like to use a for loop to run through each file and access each tree and its branches individually. In my current attempt I am using the following script:
for(int l = 1; l <= 2; l++){
string s = "pos" + to_string(l) + ".root";
cout << s << endl;
TFile *datafile=TFile::Open(s.c_str());
TTree *datatree = (TTree*)(datafile->Get("dstree"));
...
}
Upon compilation and execution I receive errors regarding memory allocation. At the bottom of this loop I have tried to use delete datafile and datafile->Close(). I assume there is a much more elegant and efficient method for trying to do what I am attempting.
I would suggest to initialize TFile* and TTree* pointers before the for loop starts.
TFile* datafile = nullptr;
TTree* datatree = nullptr;
for(int l = 1; l <= 2; l++){
string s = "pos" + to_string(l) + ".root";
cout << s << endl;
datafile=TFile::Open(s.c_str());
datatree = (TTree*)(datafile->Get("dstree"));
...
}
delete datatree; // Delete tree before file, not vice versa
delete datafile;
Even better solution is to use std::unique_ptr instead of usual pointers. Those clean memory for you and make sure you don’t get memory leaks. They are available since c++11 standart
std::unique_ptr <TFile> datafile;
std::unique_ptr <TTree> datatree;
for(int l = 1; l <= 2; l++){
string s = "pos" + to_string(l) + ".root";
cout << s << endl;
datafile.reset( TFile::Open(s.c_str()) );
datatree.reset( (TTree*)(datafile->Get("dstree")) );
...
}
// No need to delete anything. Unique pointers are smart.
Thanks so much. Unfortunately after implementing your suggestions, specifically, just moving the initialization of the TFile and TTree outside of the loop. I am now receiving an error when attempting to access the entries of the Tree inside a nested for loop:
for(int i = 1; i <= 1; i++){
datatree->GetEntry(i);
...
}
Which generates an error:
ERROR leaf:ph_z, len=1141825230 and max=849
ERROR leaf:ph_time, len=1141825230 and max=849
[/usr/local/Cellar/root/6.22.04/lib/root/libCling.so] TCling__PrintStackTrace (no debug info)
[/usr/local/Cellar/root/6.22.04/lib/root/libCling.so] TClingCallbacks::PrintStackTrace() (no debug info)
[/usr/local/Cellar/root/6.22.04/lib/root/libCling.so] cling::MultiplexInterpreterCallbacks::PrintStackTrace() (no debug info)
[/usr/local/Cellar/root/6.22.04/lib/root/libCling.so] cling_runtime_internal_throwIfInvalidPointer (no debug info)
[< unknown binary>] (no debug info)
[< unknown binary>] (no debug info)
libc++abi.dylib: terminating with uncaught exception of type cling::InvalidDerefException: Trying to access a pointer that points to an invalid memory address.
Again thanks for your help. And any other help is greatly appreciated.
I think Trying to access a pointer error could mean that you are trying to access branch which doesn’t exist, or writing a branch into the variable which is not initialized.
Seeing more code and TTree structure from TBrowser would help to investigate
But you can check:
ph_z and ph_time leafs exist in your tree and you can see it through TBrowser. Check that the names are exactly the same
Somewhere in the code you probably read them into variables approx like this:
// Make sure 1st line exists!
// You need initialize variables before passing their pointer
// to SetBranchAddress
double ph_z, ph_time;
tree->SetBranchAddress("ph_z", &ph_z);
tree->SetBranchAddress("ph_z", &ph_time);
Note that in the suggestion 1. (the one without unique_ptr) the delete only calls the destructor for the last TFile opened, all the others are leaked (or better, will be cleaned up at the end of the process by ROOT’s garbage collection logic) and in suggestion 2. the TFile will be destructed/closed before the TTree contained in it, which will result in double deletes.
That’s a bit vague, can you provide a minimal reproducer and post what errors are printed on screen exactly (with stacktraces if present)?
The typical pattern would be:
for(int l = 1; l <= 2; l++){
string s = "pos" + to_string(l) + ".root";
cout << s << endl;
std::unique_ptr<TFile> datafile(TFile::Open(s.c_str()));
// option 1: let TFile delete this TTree object when it is destroyed/closed
auto *datatree = datafile->Get<TTree>("dstree");
// option 2: manage TTree's lifetime too, as long as it's deleted before the file is closed things are ok
std::unique_ptr<TTree> datatree(datafile->Get<TTree>("dstree"));
...
// no need to call TFile::Close or `delete`s here
}
Thanks a lot of all of your help. I have implemented your suggestions and now receive these errors:
When my script appears like:
for(int l = 1; l <= 4; l++) {
TString s = "pos" + to_string(l) + ".root";
unique_ptr<TFile> datafile(TFile::Open(s));
auto *datatree = datafile->Get<TTree>("dstree");
int nentries = datatree->GetEntries();
datatree->SetBranchAddress("nph", &nph);
datatree->SetBranchAddress("y", &y);
datatree->SetBranchAddress("z", &z);
float ph_y[nph], ph_z[nph], ph_wl[nph];
int ph_pid[nph];
float y_bar, z_bar, y_w, z_w, count;
datatree->SetBranchAddress("ph_y", ph_y);
datatree->SetBranchAddress("ph_z", ph_z);
datatree->SetBranchAddress("ph_pid", ph_pid);
for(int i = 1; i <= nentries; i++)
{
datatree->GetEntry(i);
}
}
I obtain the error:
[/usr/local/Cellar/root/6.22.04/lib/root/libCling.so] TCling__PrintStackTrace (no debug info)
[/usr/local/Cellar/root/6.22.04/lib/root/libCling.so] TClingCallbacks::PrintStackTrace() (no debug info)
[/usr/local/Cellar/root/6.22.04/lib/root/libCling.so] cling::MultiplexInterpreterCallbacks::PrintStackTrace() (no debug info)
[/usr/local/Cellar/root/6.22.04/lib/root/libCling.so] cling_runtime_internal_throwIfInvalidPointer (no debug info)
[<unknown binary>] (no debug info)
[<unknown binary>] (no debug info)
libc++abi.dylib: terminating with uncaught exception of type cling::InvalidDerefException: Trying to dereference null pointer or trying to call routine taking non-null arguments
Within the nested for loop where I call data tree->GetEntry(i) I intend on accessing the branches I have addressed above it, in case this will also create an issue down the line.
Please let me know if there is any other information that will help in understanding this issue. Thanks.
I don’t see in your code nph, y, z variables initialized.
When you do datatree->SetBranchAddress("nph", &nph);
you tell your program that you want to take tree branch called "nph" and write its values to the address of nph variable in your code everytime you call datatree->GetEntry(i).
So it tries to pass branch value to the non-existing variable address! As you didn’t initialized this variable.
I think this should help.
int nph;
float y, z; // initializing variables
datatree->SetBranchAddress("nph", &nph); // passing address to those variables
datatree->SetBranchAddress("y", &y);
datatree->SetBranchAddress("z", &z);
When you create float ph_y[nph], ph_z[nph], ph_wl[nph];
Your value of nph is undefined.
nph will be filled with some value when datatree->GetEntry(i); will be called. But without this call is just some nonsense value which is not nice to pass as an array size.
You can substitute it for:
float ph_y[10000], ph_z[10000], ph_wl[10000];
int ph_pid[10000];
Where 10000 is just some large number that you sure nph will not be larger than 10000 in all trees.