Looping over ROOT files and access their TTrees

Hi all,

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.

Any and all help is greatly appreciated.

ROOT Version: 6.22/04
Platform: macosx64
Compiler: gcc


Hi @Fr4nk,

Welcome to the ROOT forum!

Some suggestions how to improve the code

  1. 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;
  1. 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.

I hope this will help.

cheers,
Bohdan

Hi Bohdan,

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.

Hi @Fr4nk,

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:

  1. 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

  2. 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);

cheers,
Bohdan

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.

1 Like

Hi @Fr4nk,

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
}

Cheers,
Enrico

Hi @eguiraud and @FoxWise,

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.

Hi @Fr4nk

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);

cheers,
Bohdan

Hi @FoxWise,

Thanks! I did initialize those variables above the outer for loop.

int nph;
float y, z;

   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];

      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 apologize for not having them in the previous post. Thanks again for all of your help and attention to this issue.

Regards,

Frank

Is it possible to share a root file so we can try to reproduce the error?

Hi @FoxWise,

Here are two files:

pos1.root: https://drive.google.com/file/d/1Ivq0pS1sDryVtYgrvBTqgqtBKnONtEnZ/view?usp=sharing

pos2.root: https://drive.google.com/file/d/1-az70biH9k-VUNcdMuTDLVDzGNIGBaRf/view?usp=sharing

Thanks again for all of the help.

Best regards,

Frank

Hi,

I so also note that I have a “debugging” comment in my script, that the error appears when it is included:

int nph;
float y, z;

   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];

      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);
      }

    cout << "Here" << endl;

   } 

Thanks again for all of your help.

Hi @Fr4nk,

Ah error is obvious, sorry.

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.

1 Like

Hi @FoxWise,

Thank so much, and such an obvious solution; always make sure your arrays are initialized properly.

And thank you @eguiraud for all of your help, time and consideration as well.

Best regards,

Frank

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.