Reading array branch

Hello all,

This is maybe a more C++ related question, but I didn’t find a solution for the problem.

  1. I have a tree that as branch as an array, that is, if I do
t1->Print()

I have as output something like:

*.......................................................................................................................*
*Br  109 :myval :  myval[npoints]/F                                                                  
*Entries :      110 : Total  Size=       2135 bytes  File Size  =        354             
*Baskets :        1 : Basket Size=      32000 bytes  Compression=   4.01         
*.......................................................................................................................*

I have a script that reads everything just fine. However, I am currently using something like:

const Int_t npoints = 10;
class DATA{
public:
    Floart_t val[npoints];
};

And this works fine. However, I have different npoints for different root files and I would like to be able to run it more easily.
One way that works, but gives me " *** Break *** segmentation violation" error is doing so:

class DATA{
public: 
    Float_t *val;
    DATA(const Int_t npoints){
        val = new Float_t[npoints];
    }
};

Where I can initialize the class with npoints as parameter.

Is there any way that I can read the branch setting the length of the array at the beginning of the code?

PS.: vector’s also worked giving the same error.
PS2.: If needed I can try to create a quick example to reproduce the problem. But in principal, the reading (and writing) methods are shown in tutorial/tree2.C

Best regards,
Henrique


ROOT Version: 26.04
Platform: Ubuntu 21.10 impish (x86-64)
Compiler: (gcc/g++ 11.2.0)

What you want is to have the initialization being a variation of:

Int_t npoints = max_of_npoints_over_all_files.
Data data(npoints);
....
tree->SetBranchAddress("npoints", &npoints);
tree->SetBranchAdddres("myval", &(myval->val));

and then reading at least those 2 branches in your loop.

const Int_t npoints = 10;

I am confused by the ‘const’. Didn’t you mentioned " I have different npoints for different root files"

Cheers,
Phiippe.

Dear Philippe,

Thanks for your reply!

  1. Using npoints as the maximum between the root files “works”. However, let’s say one of the values have 200,000 events and all the others have only 100 events. Would this solution consume much more memory than necessary?
  2. I couldn’t really implement your version as I don’t understand what “myval” is at “&(myval->val)”.

Yes, that is my main issue, it needs to be ‘const’, otherwise I get the error:

error: fields must have a constant size: 'variable length array in structure' extension will never be supported

I have added a small script to reproduce how the data was written and how I am reading it.

Best regards,
Henrique
tree_example.C (1.8 KB)

Actually I meant

tree->SetBranchAdddres("myval", &(myval->val[0]));

and using your code’s variable.

tree->SetBranchAdddres("myval", data.val);

Yes, that is my main issue, it needs to be ‘const’, otherwise I get the error:

Indeed, sorry for the confusion (I focused on the 2nd part of the code snippets).

However, let’s say one of the values have 200,000 events and all the others have only 100 events. Would this solution consume much more memory than necessary?

Yes, of course but it also reduce the number of memory allocations. So what to pick depends a bit of what you need to optimized for. The choice you have is to either stick with:

class DATA{
public:
  Int_t npoints = 0;
  Float_t val[MAXMEC];
  Float_t x[MAXMEC];
};

In which case, MAXREC must be the maximum possible number of values (no choice since the value is/must-be a compile time constant).
Or to go with something similar to:

class DATA{
public: 
    Int_t npoints_allocated = 0;
    Float_t *val;
    DATA(const Int_t npoints) : npoints_allocated(npoints) {
        val = new Float_t[npoints];
    }
    void resize(Int_t newsize) {
        if (newsize != npoints_allocated)  {
           delete [] val;
           val = new Float_t[newsize];
           npoints_allocated = newsize;
        }
     }

};
....
   TBranch *npoint_br =  nullptr;
   TBranch *myval_br =  nullptr;
   TBranch *x_br =  nullptr;
   Int_t npoints;
   t2->SetBranchAddress("npoints",&npoints, &npoint_br);
   t2->SetBranchAddress("myval",&data.val, &myval_br);
   t2->SetBranchAddress("x",&data.x, &x_br);
...
   for (Long64_t i=0;i<nentries;i++) {
     Long64_t localentry = t2->LoadTree(i);
     npoint_br->GetEntry(localentry);
     if ( npoints > npoints_allocated)  {
        data.resize(npoints);
        t2->SetBranchAddress("myval",&data.val, &myval_br);
     }
     myval_br->GetEntry(localentry);
     x_br->GetEntry(localentry);
     g[i] = new TGraph(data.npoints,data.x,data.val);  
   }

Or better yet, generate a dictionary for an updated Data class (by either using ACLiC to load the script or calling rootcling to generate a dictionary source file) and use simply:

class DATA{
public:
  Int_t npoints = 0;
  Float_t *val = nullptr; //[npoints]
  Float_t *x = nullptr; //[npoints]
    DATA(const Int_t npoints_) : npoints(npoints_) {
        val = new Float_t[npoints];
        x = new Float_t[npoints];
    }
};
...

   TTree t2("t2","t2");
   DATA data;
   t2.Branch("data",&data); // Will create the 3 branches.

...

   TTree *t2 = (TTree*)f->Get("t2");

   DATA *data = nullptr;

   t2->SetBranchAddress("data",&data); // Will set the address of all 3 branches.

...
   for (Long64_t i=0;i<nentries;i++) {
     t2->GetEntry(i);
     g[i] = new TGraph(data->npoints,data->x,data->val);  
   }

Cheers,
Philippe.

Hello again Philippe,

I am sorry to bother, but the proposed solution didn’t work in my case.
I do not control the tree creation, so I cannot modify it as in:

I’ve tried to apply the same concept the codes attached (one to create and another to read) but it gives me segmentation break.

If there is no solution for that, I will keep with the maximum value as previously suggested.

Best regards,
Henrique
tree_example.C (1.3 KB)
tree_create.C (1.2 KB)

In your last example you mean:

   t2->SetBranchAddress("myval",data->val);  // same as &(data->val[0])
   t2->SetBranchAddress("x",data->x); // same as &(data->x[0])

Dear Philippe,

That worked! Thank you very much :slight_smile:

Best regards,
Henrique