AddFriend when looping over entries


ROOT Version: 6.12/06
Platform: Fedora 27
Compiler: gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)


I am trying to perform this rather simple task of looping over a TTree (tree1) from a TFile (dummy.root) and getting the desired data based on applied gates on this particular tree and on another one (tree2) that is stored in the file.

Although this is fairly simple to be performed in interactive mode by using

root [0] new TFile("dummy.root")
root [1] tree1->AddFriend("tree2")
root [2] tree1->Draw("x", "Run==1 && y<19 && tree2.Run==2", "histo")

it doesn’t seem to be so straightforward when done inside a macro, assigning the trees to new trees and trying to loop over the entries in the tree.

A sample code is the following

#include "TFile.h"
#include "TTree.h"
#include "TRandom.h"
#include "TH1.h"

#include <iostream>
using namespace std;


int   Run, Event;
float x,y,z;


/**********************************************
*                                             *
*        Dummy function to create TTrees      *
*                                             *
**********************************************/
void CreateTTree() {

// (1) Create the file to store the TTrees
   TFile *f = new TFile("dummy.root","RECREATE");
   
// (2) Create and fill the 1st TTree
   TTree *tree1 = new TTree("tree1","tree1");
   tree1->Branch("Run",&Run,"Run/I");
   tree1->Branch("Event",&Event,"Event/I");
   tree1->Branch("x",&x,"x/F");
   tree1->Branch("y",&y,"y/F");
   tree1->Branch("z",&z,"z/F");
   TRandom r;
   for (int i=0; i<10000; ++i) {
      if (i < 5000) Run = 1;
      else          Run = 2;
      Event = i;
      x = r.Gaus(10,1);
      y = r.Gaus(20,2);
      z = r.Landau(2,1);
      tree1->Fill();
   }
   //tree1->Print();
   tree1->Write();
   
// (3) Create and fill the 2nd TTree
   TTree *tree2 = new TTree("tree2","tree2");
   tree2->Branch("Run",&Run,"Run/I");
   tree2->Branch("Event",&Event,"Event/I");
   tree2->Branch("x",&x,"x/F");
   tree2->Branch("y",&y,"y/F");
   tree2->Branch("z",&z,"z/F");
   //TRandom r;
   for (int i=0; i<2000; ++i) {
      if (i < 1000) Run = 1;
      else          Run = 2;
      Event = i;
      x = r.Gaus(100,1);
      y = r.Gaus(200,2);
      z = r.Landau(20,1);
      tree2->Fill();
   }
   //tree2->Print();
   tree2->Write();
   
// (4) Release TFile from memory
   delete f;
}


/**********************************************
*                                             *
*  Run this function to illustrate the issue  *
*    Load, compile and execute addFriend()    *
*                                             *
**********************************************/
void addFriend(){

	CreateTTree();
	TFile *fin   = new TFile("dummy.root", "OPEN");
	
	TTree *t1 = (TTree*) fin->Get("tree1");
	TTree *t2 = (TTree*) fin->Get("tree2");
	
	t1->AddFriend(t2);
	//t1->Draw("x", "Run==1 && y<19 && t2.Run==2", "histo");//<------- It doesn't work when compiled and executed
	
	//tree1->AddFriend("tree2");                                  //<------- It works in interactive mode
	//tree1->Draw("x", "Run==1 && y<19 && tree2.Run==2", "histo");//<------- It works in interactive mode
	
	t1->SetBranchAddress("Run", &Run);
	t1->SetBranchAddress("Event", &Event);
	t1->SetBranchAddress("x", &x);
	t1->SetBranchAddress("y", &y);
	t1->SetBranchAddress("z", &z);
	
	TH1F *h_x_1 = new TH1F("h_x_1", "x variable from tree1; x; Counts", 100, 5, 15);
	
	for (int i=0; i<t1->GetEntries(); ++i){
		t1->GetEntry(i);
		//if (Run==1 && t2.Run==2)//<------- It doesn't work when compiled and executed
		if (Run==1)
			h_x_1->Fill(x);
	}
	
	h_x_1->Draw("histo");

}

When trying to call t2.Run==2 for instance, I get an error

error: no member named ‘Run’ in ‘TTree’

which although I understand why I get it, I can’t seem to find a workaround it.

It goes without saying that I could always define new variables (i.e. x2, y2, z2, Run2, Event2), separately loop over the second tree, somehow find a connection between the cuts in both trees and keep what is desired, which is what I was doing so far, but I believe it’s not the most convenient solution for many reasons (i.e. it takes a lot of time if the file is big).

Any idea how to properly use AddFriend inside a macro when looping over the entries in one TTree?

Thank you very much in advance!

I guess I’ve found it!

I have to define new variables for the second TTree indeed, but I do not need to loop over again.
Simply adding new conditions seems fine.

For instance the code changes to

#include "TFile.h"
#include "TTree.h"
#include "TRandom.h"
#include "TH1.h"

#include <iostream>
using namespace std;


int   Run, Event, Run2, Event2;
float x, y, z, x2, y2, z2;

void CreateTTree() {

// (1) Create the file to store the TTrees
   TFile *f = new TFile("dummy.root","RECREATE");
   
// (2) Create and fill the 1st TTree
   TTree *tree1 = new TTree("tree1","tree1");
   tree1->Branch("Run",&Run,"Run/I");
   tree1->Branch("Event",&Event,"Event/I");
   tree1->Branch("x",&x,"x/F");
   tree1->Branch("y",&y,"y/F");
   tree1->Branch("z",&z,"z/F");
   TRandom r;
   for (int i=0; i<10000; ++i) {
      if (i < 5000) Run = 1;
      else          Run = 2;
      Event = i;
      x = r.Gaus(10,1);
      y = r.Gaus(20,2);
      z = r.Landau(2,1);
      tree1->Fill();
   }
   //tree1->Print();
   tree1->Write();
   
// (3) Create and fill the 2nd TTree
   TTree *tree2 = new TTree("tree2","tree2");
   tree2->Branch("Run",&Run,"Run/I");
   tree2->Branch("Event",&Event,"Event/I");
   tree2->Branch("x",&x,"x/F");
   tree2->Branch("y",&y,"y/F");
   tree2->Branch("z",&z,"z/F");
   //TRandom r;
   for (int i=0; i<2000; ++i) {
      if (i < 1000) Run = 1;
      else          Run = 2;
      Event = i;
      x = r.Gaus(100,1);
      y = r.Gaus(200,2);
      z = r.Landau(20,1);
      tree2->Fill();
   }
   //tree2->Print();
   tree2->Write();
   
// (4) Release TFile from memory
   delete f;
}


void addFriend(){

	CreateTTree();
	TFile *fin   = new TFile("dummy.root", "OPEN");
	
	TTree *t1 = (TTree*) fin->Get("tree1");
	TTree *t2 = (TTree*) fin->Get("tree2");
	
	t1->AddFriend(t2);
	//t1->Draw("x", "Run==1 && y<19 && t2.Run==2", "histo");//<------- It doesn't work when compiled and executed
	
	//tree1->AddFriend("tree2");                                  //<------- It works in interactive mode
	//tree1->Draw("x", "Run==1 && y<19 && tree2.Run==2", "histo");//<------- It works in interactive mode
	
	t1->SetBranchAddress("Run", &Run);
	t1->SetBranchAddress("Event", &Event);
	t1->SetBranchAddress("x", &x);
	t1->SetBranchAddress("y", &y);
	t1->SetBranchAddress("z", &z);
	//
	t2->SetBranchAddress("Run", &Run2);
	t2->SetBranchAddress("Event", &Event2);
	t2->SetBranchAddress("x", &x2);
	t2->SetBranchAddress("y", &y2);
	t2->SetBranchAddress("z", &z2);
	
	TH1F *h_x_1 = new TH1F("h_x_1", "", 100, 5, 15);
	
	for (int i=0; i<t1->GetEntries(); ++i){
		t1->GetEntry(i);
		//if (Run==1 && t2.Run==2)//<------- It doesn't work when compiled and executed
		if (Run==1)
			h_x_1->Fill(x);
	}
	
	h_x_1->Draw("histosame");

}

So the fix is to add the definition of new variables

int   Run, Event, Run2, Event2;
float x, y, z, x2, y2, z2;

Set the TBranch addresses

t2->SetBranchAddress("Run", &Run2);
t2->SetBranchAddress("Event", &Event2);
t2->SetBranchAddress("x", &x2);
t2->SetBranchAddress("y", &y2);
t2->SetBranchAddress("z", &z2);

and just use them in the conditions i.e.

for (int i=0; i<t1->GetEntries(); ++i){
		t1->GetEntry(i);
		if (Run==1 && x2<100)//<--------- This is how it's used (?)
			h_x_1->Fill(x);
	}

I believe it’s correct unless I am missing something.

Either this:

t1->AddFriend(t2); // t1 and t2 are pointers

or this:

t1->AddFriend("tree2"); // t1 is a pointer, "tree2" is a name

and then this:

t1->Draw("x", "Run==1 && y<19 && tree2.Run==2", "histo"); // t1 is a pointer, "tree2" is a name

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