Filling a TTree Branch with a variable lenght array


Please read tips for efficient and successful posting and posting code

_ROOT 6.28
_Platform: Ubuntu 22.04.4 LTS
Compiler: Not Provided


Hello rooters,
I’m trying to fill a TTree with a variable length array. The issue is that the array length is saved in an array itself.

To be more clear, here is my code :
Variables and branches definition :

 Int_t FF_multi_n[2];
   double FF_n_E_labRef[2][fMAX_EvapNeutrons];

 simuTree->Branch ("FissionFragment_Multi_n" ,FF_multi_n, "FissionFragment_Multi_n[2]/I");
 simuTree->Branch ("FissionFragment_N_E_LabRef" , FF_n_E_labRef,  "FissionFragment_N_E_LabRef[2][FF_multi_n]/D");

The FissionFragment_N_E_LabRef is not correctly filled and is not correctly read. When I do “MakeSelector(“mycode”)” on the tree I generate, the branch is either not supported (with the new root versions) or read as a char (if I write “=legacy”).

UPDATE : I’ve modified the branch declaration from
simuTree->Branch ("FissionFragment_N_E_LabRef" , FF_n_E_labRef, "FissionFragment_N_E_LabRef[2][FF_multi_n]/D");
to
simuTree->Branch ("FissionFragment_N_E_LabRef" , FF_n_E_labRef, "FissionFragment_N_E_LabRef[2][fMAX_EvapNeutrons]/D");

The MakeSelector(“mycode”,“=legacy”) now reads correctly the type of variable, namely a double.
For you to fully understand what I try to do:

  • I read a txt file and fill a tree from it → This step seems to be right. Right before filling the TTree with Fill() the values that are stored in the FF_n_E_labRef[2][fMAX_EvapNeutrons], variable are correct (I’ve a cout before filling)
  • I’ve made a MakeSelector(“xx”,“=legacy”) on the generated tree → the values that are read are wrong (I only added a GetEntry(entry) in the loop and a cout. It seems that for each line of the FissionFragment_N_E_LabRef[2][fMAX_EvapNeutrons] matrix, I can only read 2 values rather than fMAX_EvapNeutrons values
    I don’t know if it helps :frowning:

Thank you in advance for your help
Cheers
Paola

Hi @marini,
thanks for reaching out!
I add in the loop our TTree expert @pcanal

Cheers,
Monica

1 Like

Thank you Monica
Paola

Hello,

I think I’ve managed to fill my tree with a variable length array.
I’ve got a major suggestion reading this post and did the same :

except that I want to write 10 events, so I loop over them (jj)

int essaie()
{
int nParticles;
vector<int> nTracks;
vector<vector<double>> trackPosition;

TFile file("test.root", "RECREATE");
TTree* tree=new TTree("test", "test");
tree->Branch("nParticles", &nParticles, "nParticles/I");
tree->Branch("nTracks", &nTracks);
tree->Branch("trackPosition", &trackPosition);

for(int jj=0; jj<10; jj++)
{
    //Assume 2 particles
    nParticles = 2;
    //1st has 2 traks
    nTracks.push_back(2+jj);
    //2nd has 3 tracks
    nTracks.push_back(3+jj);
    
    //Fill positions of each track for 1st particle
    vector<double> temp1;
    for (size_t i = 0; i < 2; i++) temp1.push_back((i+1+jj)*10);
    trackPosition.push_back(temp1);
    
    //Fill positions of each track for 2st particle
    vector<double> temp2;
    for (size_t i = 0; i < 3; i++) temp2.push_back((i+1+jj)*100);
    trackPosition.push_back(temp2);
    
    tree->Fill();
}
file.Write();
return 0;

The output tree is correctly filled (I can see the different values with StartViewer() for instance.
Now my problem is : how do I read the generated tree?
Below is what I did by generating the codes with MakeSelector(“mycode”,“=legacy”) :

The header :

  #ifndef monessai_h
  #define monessai_h

  #include <Riostream.h>
  #include <TROOT.h>
  #include <TChain.h>
  #include <TFile.h>
  #include <TSelector.h>

  // Header file for the classes stored in the TTree if any.
  #include "vector"
  #include "vector"

  class monessai : public TSelector {
  public :
     TTree          *fChain;   //!pointer to the analyzed TTree or TChain

  // Fixed size dimensions of array or collections stored in the TTree if any.
  
     // Declaration of leaf types
     Int_t           nParticles;
     vector<int>     *nTracks;
     vector<vector<double> > *trackPosition;

     // List of branches
     TBranch        *b_nParticles;   //!
     TBranch        *b_nTracks;   //!
     TBranch        *b_trackPosition;   //!

     monessai(TTree * /*tree*/ =0) : fChain(0) { }
     virtual ~monessai() { }
     virtual Int_t   Version() const { return 2; }
     virtual void    Begin(TTree *tree);
     virtual void    SlaveBegin(TTree *tree);
     virtual void    Init(TTree *tree);
     virtual Bool_t  Notify();
     virtual Bool_t  Process(Long64_t entry);
     virtual Int_t   GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; }
     virtual void    SetOption(const char *option) { fOption = option; }
     virtual void    SetObject(TObject *obj) { fObject = obj; }
     virtual void    SetInputList(TList *input) { fInput = input; }
     virtual TList  *GetOutputList() const { return fOutput; }
     virtual void    SlaveTerminate();
     virtual void    Terminate();
  
     ClassDef(monessai,0);
  };
  #endif

  #ifdef monessai_cxx
  void monessai::Init(TTree *tree)
  {
     // The Init() function is called when the selector needs to initialize
     // a new tree or chain. Typically here the branch addresses and branch
     // pointers of the tree will be set.
     // It is normally not necessary to make changes to the generated
     // code, but the routine can be extended by the user if needed.
     // Init() will be called many times when running on PROOF
     // (once per file to be processed).

     // Set object pointer
     nTracks = 0;
     trackPosition = 0;
     // Set branch addresses and branch pointers
     if (!tree) return;
     fChain = tree;
     fChain->SetMakeClass(1);

     fChain->SetBranchAddress("nParticles", &nParticles, &b_nParticles);
     fChain->SetBranchAddress("nTracks", &nTracks, &b_nTracks);
     fChain->SetBranchAddress("trackPosition", &trackPosition, &b_trackPosition);
  }
  
  Bool_t monessai::Notify()
  {   return kTRUE; }

  #endif // #ifdef monessai_cxx

And the code .C

  #define monessai_cxx
  #include "monessai.h"
  #include <TH2.h>
  #include <TStyle.h>

  void monessai::Begin(TTree * /*tree*/)
  { TString option = GetOption();}

  void monessai::SlaveBegin(TTree * /*tree*/)
  {  TString option = GetOption();}

  Bool_t monessai::Process(Long64_t entry)
  {
      GetEntry(entry);
      cout<<nParticles<<endl;
      for(int i=0; i<nParticles; i++)
      {
      cout<<"ntracks size : "<<nTracks->at(i)<<endl;
       for(int j=0; j<nTracks->at(i); j++)
       cout<<trackPosition->at(i).at(j) <<endl;
      }
  return kTRUE;
  }

  void monessai::SlaveTerminate()
  {}

  void monessai::Terminate()
  {}

However the output looks like I’m always reading only the first event :frowning:

  2
  ntracks size : 2
  10
  20
  ntracks size : 3
  100
  200
  300
  2
  ntracks size : 2
  10
  20
  ntracks size : 3
  100
  200
  300
  2
  .....

and so on, for 10 times as I’ve 10 entries in the Tree.

Do you have any idea on where I’m wrong?
Thank you in advance for your help
Cheers
Paola

May be you should clear your vectors at each iteration otherwise you will cumulate the events in the vectors. cumulating, that’s what push does

#include <vector>
#include "TFile.h"
#include "TTree.h"

int essaie()
{
    int nParticles;
    std::vector<int> nTracks;
    std::vector<std::vector<double>> trackPosition;

    TFile file("test.root", "RECREATE");
    TTree* tree = new TTree("test", "test");
    tree->Branch("nParticles", &nParticles, "nParticles/I");
    tree->Branch("nTracks", &nTracks);
    tree->Branch("trackPosition", &trackPosition);

    for (int jj = 0; jj < 10; jj++)
    {
        // Clear vectors at the start of each loop iteration
        nTracks.clear();
        trackPosition.clear();

        // Assume 2 particles
        nParticles = 2;

        // 1st particle has 2 tracks
        nTracks.push_back(2 + jj);

        // 2nd particle has 3 tracks
        nTracks.push_back(3 + jj);

        // Fill positions of each track for 1st particle
        std::vector<double> temp1;
        for (size_t i = 0; i < 2; i++) temp1.push_back((i + 1 + jj) * 10);
        trackPosition.push_back(temp1);

        // Fill positions of each track for 2nd particle
        std::vector<double> temp2;
        for (size_t i = 0; i < 3; i++) temp2.push_back((i + 1 + jj) * 100);
        trackPosition.push_back(temp2);

        tree->Fill();
    }

    file.Write();
    return 0;
}

1 Like

Hello Couet,
thank you very much!
Let me try and I’ll come back!
Thank you
Paola

Thank you so much!
It was such an “easy” mistake
Thank you very much
Best regards
Paola

1 Like