Fill a TBranch with a multi-dimensional std::vector


_ROOT Version: ROOT 6.14/02
_Platform: linuxx8664gcc (Ubuntu 18.04 64 bit)
_Compiler: gcc/g++ (Ubuntu 7.3.0-27ubuntu1~18.04)


Hello!
I am trying to store in a TBranch the content of a multi-dimensional vector: vector<vector<float>>
The vector size is not known at compile time but, once it is set at running time, it is never changed afterward.

I managed to compile and run my program but I always and inevitably get histograms full of garbage. I think the problem is that I am passing the wrong pointer to the TBranch but I am not sure how to troubleshoot it.

I have already read this (and other) threads:

and randomly tried those solutions but I am stuck.

I have attached a minimal working example that shows what I am trying to achieve. Of course the example has no meaning per se, but it should be compilable and runnable. The executable creates a ROOT file on the Desktop called test.root.
You can compile and run the program like this:

cd $HOME/Desktop
g++ -o test test.cxx `root-config --cflags --libs --glibs`
./test
root -l test.root

PS: is there a way to get by NOT creating a pointer?
f2vector * test = new f2vector; —> f2vector test;

PPS: Here is the content of the attachment if you want to take a look without downloading it:

#include <vector>
#include <iostream>
#include "TROOT.h"
#include "TFile.h"
#include "TTree.h"
#include "TInterpreter.h"
#include "TRandom3.h"

using namespace std;

typedef vector<vector<float>> f2vector;

int main() {
  gRandom = new TRandom3();
  
  gROOT->ProcessLine("#include <vector>");
  gInterpreter->GenerateDictionary("vector<vector<float>>", "vector");
  
  TFile * outputTreeFile = new TFile("$HOME/Desktop/test.root", "recreate");

  outputTreeFile->cd();

  f2vector * test = new f2vector;
  (*test).resize(3);
  for (int i = 0; i < 3; i++) (*test)[i].resize(3);
			  
  TTree * tree = new TTree("tree", "ROOT tree");
  tree->Branch("test", &test, "test[3][3]/F");
 
  for (int i = 0; i < 3; i++) {
	for (int j = 0; j < 3; j++) {
	  (*test)[i][j] = gRandom->Gaus(3.0, 1.0);
	  cout << "test[" << i << "][" << j << "] = " << (*test)[i][j] << endl;
	}
  }
  tree->Fill();
		 
  tree->Write();
  outputTreeFile->Close();
}

test.cxx (935 Bytes)

1 Like

Hi,

here the correct code to achieve your goal:

int main() {
  TRandom3 rnd;
  
  gInterpreter->GenerateDictionary("vector<vector<float>>", "vector");
  
  TFile outputTreeFile("test.root", "recreate");

  f2vector test(3,std::vector<float>(3));
			  
  TTree tree("tree", "ROOT tree");
  tree.Branch("test", &test);
 
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
      test[i][j] = rnd.Gaus(3.0, 1.0);
      cout << "test[" << i << "][" << j << "] = " << test[i][j] << endl;
    }
  }
  tree.Fill();		 
  tree.Write();
}

The mistake was the branch definition.

Cheers,
D

tree->Branch("test", &test);

Thank you very much. Now the code is working. So much time wasted for such a trivial error.

Anyway, I need the vector of vectors to be declared and allocated in different moments (the resizes were intended). But your solution is working even in that case, so all’s well that ends well

Actually, I also have 3-dimensional vectors in my code. I tried to apply the solution above and it doesn’t work … (but as I said it works OK for 2-dimensional vectors)

As usual here a minimal compilable example:

#include <vector>
#include <iostream>
#include "TROOT.h"
#include "TFile.h"
#include "TTree.h"
#include "TInterpreter.h"
#include "TRandom3.h"

using namespace std;

typedef vector<vector<vector<float>>> f3vector;

int main() {
  gRandom = new TRandom3();
  
  gInterpreter->GenerateDictionary("vector<vector<vector<float>>>", "vector");
  
  TFile * outputTreeFile = new TFile("$HOME/Desktop/test.root", "recreate");

  outputTreeFile->cd();

  f3vector test(3, std::vector<std::vector<float>>(3, std::vector<float>(3)));
			  
  TTree * tree = new TTree("tree", "ROOT tree");
  tree->Branch("test", &test);


  for (int i = 0; i < 3; i++) {
	for (int j = 0; j < 3; j++) {
	  for (int k = 0; k < 3; k++) {
		test[i][j][k] = gRandom->Gaus(3.0, 1.0);
		cout << "test[" << i << "][" << j << "][" << k << "] = " << test[i][j][k] << endl;
	  }
	}
  }
  tree->Fill();
  tree->Write();
  outputTreeFile->Close();
} 

test_2.cxx (909 Bytes)

After many trials and errors, I reached the conclusion that there are some issues with the generation of a dictionary for triple-nested classes in ROOT.

Or at least I could not get them working … I tried many permutations of the classes types but without success. When dealing with doubly nested classes everything works as expected but as soon as I try to “triple-nest”, things go south.

I solved by defining a custom class to deal with dynamically allocated 3D arrays. Basically, the objects of my class are managing a dynamically-allocated 3D-array, while making sure that the data resides on contiguous memory.

This way, filling and reading TBranches is trivial and the dictionary generation step is not even needed while retaining all the advantages of dynamically allocated memory and automatic memory management (embedded in the class).

The downside is that you need to create a custom class for the sole purpose of filling a tree with 3D pseudo-vectors (by 3D pseudo-vector I mean dynamically allocated 3D array).

I have attached my solution if you want to take a look. My custom class is basically playing the same role as a

vector<vector<vector<type>>>

with the only constraint that the memory is contiguously allocated.

test_3.zip (1.5 KB)

PS if you know how to deal with triple-nested classes in ROOT, please let me know!!!

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

Hi,

Did you try to generate both the dictionary for both level (they are both needed):

gInterpreter->GenerateDictionary("vector<vector<float>>", "vector");
gInterpreter->GenerateDictionary("vector<vector<vector<float>>>", "vector");

Technically you also need the dictionary with the first level too but since the element type is ‘float’, it is already provide by ROOT.

Cheers,
Philippe.