Root I/O reading a variable length array branch using streamer

Hi,

I am new to streamer. I want to read from a root file, which is a flat tree containing variable length array branches. I was following the instruction about streamer from root user’s guide, but still got stuck.

I have a root tree, and I automatic generate the class by using
Events->MakeClass(“NanoTree”).

It generates the NanoTree Class. Since it contains variable length array, I modified some variables to be, for example:

before:
UInt_t nMuon;
Float_t Muon_eta[8]; //[nMuon]
Float_t Muon_mass[8]; //[nMuon]
Float_t Muon_phi[8]; //[nMuon]
Float_t Muon_pt[8]; //[nMuon]
Bool_t Muon_tightId[8]; //[nMuon]
Int_t Muon_charge[8]; //[nMuon]

after:
UInt_t nMuon;
Float_t *Muon_eta; //[nMuon]
Float_t *Muon_mass; //[nMuon]
Float_t *Muon_phi; //[nMuon]
Float_t *Muon_pt; //[nMuon]
Bool_t *Muon_tightId; //[nMuon]
Int_t *Muon_charge; //[nMuon]

And I set up streamer for the class. However, it does not work. The code complies but it always shows segment fault when I run it. (I understand the automatically generated .h file set the length of the array to be the maximum number of muons that one event could have within that root file. However, I would like to read it depends on the nMuon, because I want the code to work for other root files. )

Here is what I’ve done, please help me check if I missed anything.
I called macro ClassDef(NanoTree,1) at the end of the class declaration in the NanoTree.h file.
I also called macro ClassImp(NanoTree) in the beginning of NanoTree.C file.
I made the EventLinkDef.h and automatically generated the EventDict.cxx. The EventDict.cxx does have the streamer defined. The Makefile is also shown here.


EventLinkDef.h

#ifdef CINT

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ class NanoTree+;

#endif

Makefile

CC=g++ -lTMVA
CFLAGS=root-config --cflags --libs

all:
rootcling -f EventDict.cxx -c NanoTree.h EventLinkDef.h
(CC) (CFLAGS) EventDict.cxx NanoTree.C NanoTreeLoop.C -o NanoTreeLoop

(NanoTreeLoop.C is just the main execution for running the analysis code over a list of root files that has the same structure.)


Please read tips for efficient and successful posting and posting code

ROOT Version: 6.12/07
Platform: Not Provided
Compiler: Not Provided


Hello @Yao_Yao,

Can you please attach all the required files, e.g. EventDict.cxx and NanoTree.C. We cannot provide you a more detailed diagnostic without taking a look at how you are actually doing things. Thanks!

Cheers,
J.

EventDict.cxx (6.1 KB) EventLinkDef.h (156 Bytes) NanoTree.C (4.6 KB) NanoTree.h (1.9 KB) NanoTreeLoop.C (239 Bytes)

The segment fault disappears if I change *Muon_ to Muon_[8] in the NanoTree.h file.

Do you have any clue on anything that I missed?

Hi @Yao_Yao (and welcome to the ROOT forum!)

the problem might not have to do with streamers but rather with standard C++ pointer shenanigans.

Do I understand the code correctly in that Muon_eta, Muon_mass and the other pointers are never initialized to anything? If yes, that’s the problem: Float_t Muon_eta[8]; creates an array, and ROOT later fills that array with the appropriate contents. Float_t *Muon_eta; creates a pointer that points to nothing meaningful, and it needs to be assigned a value corresponding to the address of an array of appropriate size.

See this small reproducer that should clarify what’s going on:

#include <TFile.h>
#include <TTree.h>
#include <iostream>

int main() {
   // create a file with a variable-sized array
   {
      TFile f("f.root", "recreate");
      TTree t("t", "t");
      int n = 8;
      int *x = new int[n]{1,2,3,4,5,6,7,8};
      t.Branch("n", &n);
      t.Branch("x", x, "x[n]/I");
      t.Fill();
      n = 3;
      x[0] = 9;
      x[1] = 10;
      x[2] = 11;
      t.Fill();
      t.Write();
   }

   TFile f("f.root");
   auto t = f.Get<TTree>("t");
   //int *x; // leads to segmentation violation
   int *x = new int[8]; // works
   t->SetBranchAddress("x", x);

   // check the contents of the array
   t->GetEntry(0);
   for (int i = 0; i < 8; ++i)
      std::cout << x[i] << std::endl;
   t->GetEntry(1);
   for (int i = 0; i < 3; ++i)
      std::cout << x[i] << std::endl;

   return 0;
}

It’s a bit of a chicken-and-egg problem to initialize those arrays to a size that you know will be ok for the TTree before you read the TTree. Maybe @pcanal has a suggestion.

Otherwise you can use MakeSelector instead of MakeClass to read the TTree use TTreeReader, which is a higher-level and more well-behaved interface, or even use RDataFrame, which is a bit of a different programming paradigm but typically works really well for NanoAODs.

Hope this helps!
Enrico

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