Retrieving TVector3 From a Tree#
the old way#
As far as I know, “the old way” should work, even for non-primitive types
(such asTVector3
objects). As for the rather curious syntax of using a pointer and passing
the address of a pointer (see example below), this is simply how things are done in C++/ROOT
when it comes to non-primitive types.
Below I include a simple script that creates a tree containing a TVector3
, which is
initialized to have
void write(){
TFile * f = new TFile("out.root","RECREATE");
TTree * t = new TTree("my_tree", "my_tree_title");
TVector3 myVec(1,2,3);
t->Branch("my_branch", &myVec);
t->Fill();
f->Write();
f->Close();
}
And here is the corresponding script that retrieves the TVector3
. We can print the
void read(){
TFile *inFile = TFile::Open("out.root");
TTree *inTree = (TTree*) inFile->Get("my_tree");
TVector3*inVec_ptr=nullptr;
inTree->SetBranchAddress("my_branch",&inVec_ptr);
inTree->GetEntry(0);
std::cout<< inVec_ptr->X() << " " << inVec_ptr->Y() << " " << inVec_ptr->Z() << std::endl;
}
Note the usage of a pointer on the third line:
TVector3 * inVec_ptr=nullptr
. Additionally, it seems like this pointer has to be
initialized to a nullptr
.
And in the fourth line when we SetBranchAddress
, we give the address of the pointer.
Again, this is just how C++/ROOT does things when it comes to non-primitive types, as
explained
on the ROOT forum.
the newer way#
The method introduced above worked for me when I create a simple tree containing a simple
vector. However, it did not work when I tried to retrieve the branch
“eventSummary.neutrino.path.direction”, which is a TVector3
. I don’t know why I keep
getting a segmentation fault, but here is another
more modern method
of dealing with trees in general by using TTreeReader
and TTreeReaderValue
.
The following script worked on macOS as well as Fedora Linux.
void dir_plotter2(){
TFile * f = TFile::Open("2023-11-09_nnt_100.0_energy_21/run1/IceFinal_1_allTree.root");
TTreeReader myReader("allTree", f);
// <this> has to match the type of whatever branch we are attaching my_Vec to
TTreeReaderValue<TVector3> my_Vec(myReader,"eventSummary.neutrino.path.direction");
// If we were to use this method for a primitive type, say flavor, we woud
// instead write TTreeReaderValue<Int_t> ... to specify the integer type.
TH1F * h = new TH1F("Run 1 x direction", "x direction", 400, -1 ,1);
// start reading
while( myReader.Next()){
h->Fill(my_Vec->X());
}
h->Draw();
}
And below is the result:

Fig. 14 A sample x direction plot using branch “eventSummary.neutrino.path.direction”, which is a
TVector3
type object.#
the newest way#
The newest way is of course to use RDataFrame
, which provides a high-level interface for
accessing data stored in trees.
There is a comparison between the syntax of TTreeReader
and RDataFrame
in the
introduction section
in the CERN ROOT Reference Guide.
Below is a script for accessing the branch “eventSummary.neutrino.path.direction” using this method. The script is tested on macOS as well as Fedora Linux.
void dir_printer(){
// "tree name", "file name"
RDataFrame my_df("allTree","2023-11-09_nnt_100.0_energy_21/run1/IceFinal_1_allTree.root");
// here is a user defined function. RDataFrame takes care of the interation over all entries
// stored in the trees, so no while loop is needed.
auto my_print_function=
[](const TVector3& v){
std::cout << v.X() << std::endl;
};
my_df.Foreach(my_print_function, {"eventSummary.neutrino.path.direction"});
}
The inline-function my_print_function
is known as a C++ lambda expression, and it doesn’t
have to start with auto
(see script below).
The script above simply prints out the
void dir_plotter(){
TH1F * h = new TH1F("Run1 X Direction", "X Direction", 400, -1, 1);
RDataFrame my_df("allTree","2023-11-09_nnt_100.0_energy_21/run1/IceFinal_1_allTree.root");
std::function<void(TVector3)> my_fill_function =
[&h](const TVector3& v){
h->Fill(v.X());
};
my_df.Foreach(my_fill_function, {"eventSummary.neutrino.path.direction"});
h->Draw();
}
In this case we swapped out the auto
type for something more explicit.
Also worth noting is the usage of the
capture clause
[&h]
. Without this, we would get an error message:
“variable cannot be implicitly captured in a lambda with no capture-default specified.”
This error message is explained here.
Here is the output of the above script:

Fig. 15 Another sample x direction plot, this time using RDataFrame
.#
which is the same as figure 3.