TString or char* as TTree branch?

Hi all,

I want to do something (hopefully) fairly simple: I’d like to have a string stored in my ntuple. I’d have thought that this was something that’s done fairly often, but I’m not having much luck finding examples to copy.

I have a struct like:

struct PRDNTupleData {
int   numPRDs;
float prdLocX[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS]     ;
TString stationNames[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS]     ;
}

but I wasn’t sure how to add this as a branch. Do I have to use a char* instead? Can anyone point me to an example I could look at? This doesn’t work (at least, I seem to get junk back):

tree->Branch("StationName",   &stationNames,"stationName[numPRDs]/C");

Cheers,

Ed

1 Like

Hi,

You have 2 choices. If you really need to use the leaf list method of creating branch (the one you are using in your example), you have to use only raw C++ type (int, float, double, char, char*, etc).

If you want to C++ object (TString, etc), you need to use the method where you pass an object to the branch. In order to do so, you will need to compile your code (so that your class as a dictionary); this is simple to do using ACLiC.

So your code would turn into:struct PRDNTupleData { int numPRDs; float prdLocX[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS] ; TString stationNames[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS] ; }; .... PRDNTupleData *data = new PRDNTupleData(); tree->Branch("PRDNT",&data);

Cheers,
Philippe.

Hi Philippe,

Thanks for the reply… the second option looks very hard for me to do (I’m trying to do this from within ATLAS’s Athena framework).

So I’m probably stuck using char* (yuck). What I’m not sure about is how I define the char* so that ROOT reads it correctly - is there an example of someone doing this that I can look at?

Specifically, I guess I should declare the char as

char stationNames[10*MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS]

… to allow for the correct number of strings (which are less than 10 characters long).

I have to terminate these with ‘\0’ correct? Does TString’s Data() method do this for me?

Cheers,

Ed

[quote]the second option looks very hard for me to do (I’m trying to do this from within ATLAS’s Athena framework).[/quote]I have to believe that the Athena framework has a mean for you to generate the dictionary for your own classes!

For the leaflist method, I think the following should work:char *stationNames[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS]

[quote]I have to terminate these with ‘\0’ correct? Does TString’s Data() method do this for me?[/quote]Yes and yes.

Cheers,
Philippe.

Hi Philippe,

Sorry to keep pestering, but it’s still not working for me yet.

[quote=“pcanal”][quote]the second option looks very hard for me to do (I’m trying to do this from within ATLAS’s Athena framework).[/quote]I have to believe that the Athena framework has a mean for you to generate the dictionary for your own classes!
[/quote]
Sure, but I don’t know how I’d get this to work within ROOT. Or at least, I do have an idea, but I’ve been struggling with this for about a week now and I need to get something working ASAP. I play some more later on, when I have a bit more time.

I’ve tried this, but when I get:

root [3] tv__tree->Scan("GlobalPhi:StationName");
***********************************************
*    Row   * Instance * GlobalPhi * StationNa *
***********************************************
*        0 *        0 *           * <¹
*        1 *        0 * -0.371850 * <¹
*        1 *        1 * -0.368061 * <¹
*        1 *        2 * -0.364273 * <¹
*        1 *        3 * -0.360485 * <¹
*        1 *        4 * -0.356698 * <¹
*        1 *        5 * -0.352913 * <¹

i.e. the station name is corrupt (but everything else looks okay).

I’m completely baffled! I’m sure I’m doing something very stupid but I really can’t work out what.

Just in case haven’t provided enough information, here is the complete struct I fill:

	struct PRDNTupleData {
	    TTree* tree;
	    int   numPRDs;

		int   detectorType[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS];
	    int   prdId[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS];

	    float prdLocX[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS];
	    float prdLocY[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS];
	    int   prdPdgId[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS];
	    float prdGlobPhi[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS]; 
	    float prdGlobR[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS];
	    float prdGlobZ[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS];
	    int isPhi[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS];  
	    char* stationNames[MUONRECSTATISTICS_MAKEPRDNTUPLES_MAXPRDS*10];    
	    /**constructor*/
	    PRDNTupleData(TString name, TString title) :
	    numPRDs(0),
	    tree(new TTree(name,title))
	    {
		        tree->Branch("NumPRDs",       &numPRDs,     "numPRDs/I");
	        tree->Branch("DetectorType",  &detectorType,"detectorType[numPRDs]/I");
	        tree->Branch("PrdIdentifier", &prdId,       "prdIdentifier[numPRDs]/I");
	        tree->Branch("LocalX",        &prdLocX,     "localX[numPRDs]/F");
	        tree->Branch("LocalY",        &prdLocY,     "localY[numPRDs]/F");
	        tree->Branch("TruthPdgId",    &prdPdgId,    "truthPdgId[numPRDs]/I");
	        tree->Branch("GlobalPhi",     &prdGlobPhi,  "globalPhi[numPRDs]/F");
	        tree->Branch("GlobalR",       &prdGlobR,    "globalR[numPRDs]/F");
	        tree->Branch("GlobalZ",       &prdGlobZ,    "globalZ[numPRDs]/F");
	        tree->Branch("PhiMeasurement",&isPhi,       "phiMeasurement[numPRDs]/I");
	        tree->Branch("StationName",   &stationNames,"stationName[numPRDs]/C");
	    }
	};

… and here is where I fill the char*

    int index = m_prdData[type]->numPRDs;
    TString stationName(m_idHelper->stationNameString(stationNameIndex));
    m_prdData[type]->stationNames[index]=stationName.Data();

If you’re really curious/keen to help :wink: , the complete code is here:
atlas-sw.cern.ch/cgi-bin/viewcvs … cvs-markup

Cheers,

Ed

Unfortunately array of string does not seem to work with the leaf list method.
You have to move to using a Class or you have to unrool your array into several branches.

Cheers,
Philippe.

Hi there,
With time I would have hoped this will work…
Could anyone try to fix this please :cry: ?
Thanks, Z

Hi,

The leaflist method is risky since it requires you to express the layout twice consistently. In general this is a useful method for simple case (aka int, float,etc). We do not recommend to use it for complex case and thus have not added any new feature to it in a while.

In addition it is really easy to switch to a class (essentially similar to what you do except that you have to compile your script (using ACLiC) and use a simpler :slight_smile: call to create your branch). Please give it a try :slight_smile:

Cheers,
Philippe.

Dear Philippe,
I totally agree with you. I usually use this ACLIC technique for my Root analysis and it’s great.
But here I need it for Geant4 purposes and it’s really a compilation nightmare! I’ve already spent ages on it.
So if you or anyone else are familiar with G4 and are game for a bit of sweat, I’m ok to spend a couple of hours more on it.
Thanks in advance,
Z

PS: BTW, it still looks like a basic case for me…

Char_t  plainString    [300] ; 
Char_t *arrayOfStrings [300] ; 

TFile *fout = new TFile ("test.root","RECREATE") ;
  TTree *tree = new TTree ("tree","tree") ; 
  tree->Branch("plainString   ",&plainString   ,"plainString      /C") ;
  tree->Branch("arrayOfStrings", arrayOfStrings,"arrayOfStrings[3]/C") ;
    sprintf(plainString,"tata") ; arrayOfStrings[0] = "toto" ; arrayOfStrings[1] = "titi" ; arrayOfStrings[2] = "tutu" ; 
    printf ("\n%s\n%s\t%s\t%s\n\n",plainString,arrayOfStrings[0],arrayOfStrings[1],arrayOfStrings[2]) ; 
  tree->Fill() ; 
  tree->Write() ;  
fout->Close() ; 

TFile *fin = new TFile ("test.root","READ") ;
  TTree *tree_ = (TTree*)fin->Get("tree") ;
  tree_->Show(0) ; 
fin->Close() ;

Output:

tata
toto    titi    tutu

======> EVENT:0
 plainString       = tata
 arrayOfStrings  = lF]F]ÌF]

Hi,

Simply try these small changes:

[code]struct MyClass {
Char_t plainString [300] ;
Char_t *arrayOfStrings [300] ;
};

TFile *fout = new TFile (“test.root”,“RECREATE”) ;
TTree *tree = new TTree (“tree”,“tree”) ;
MyClass *obj = new MyClass;
tree->Branch(“plainString “,&obj);
sprintf(obj->plainString,“tata”) ; obj->arrayOfStrings[0] = “toto” ; arrayOfStrings[1] = “titi” ; obj->arrayOfStrings[2] = “tutu” ;
printf (”\n%s\n%s\t%s\t%s\n\n”,obj->plainString,obj->arrayOfStrings[0],obj->arrayOfStrings[1],arrayOfStrings[2]) ;
tree->Fill() ;
tree->Write() ;
fout->Close() ;

TFile *fin = new TFile (“test.root”,“READ”) ;
TTree tree_ = (TTree)fin->Get(“tree”) ;
tree_->Show(0) ;
fin->Close() ; [/code]

Cheers,
Philippe

Hum… The only thing I managed to make work is the following, which is not quite dynamic. But I might do something stupid.

.L tring.c+
tring()

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

struct MyClass 
{
  Char_t  plainString       [300] ;
  //Char_t *arrayOfStrings    [300] ;
  Char_t  arrayOfStrings [3][300] ;
} ;

void tring ()
{
  TFile *fout = new TFile ("test.root","RECREATE") ;
  TTree *tree = new TTree ("tree","tree") ;
    MyClass *obj = new MyClass ;
    tree->Branch("MyClass",&obj) ;
    sprintf(obj->plainString,"tata") ; 
    sprintf(obj->arrayOfStrings[0],"toto") ; sprintf(obj->arrayOfStrings[1],"titi") ; sprintf(obj->arrayOfStrings[2],"tutu") ;
    printf ("\n%s\n%s\t%s\t%s\n\n",obj->plainString,obj->arrayOfStrings[0],obj->arrayOfStrings[1],obj->arrayOfStrings[2]) ;
    tree->Fill() ;
    tree->Write() ;
  fout->Close() ;
 
  TFile *fin = new TFile ("test.root","READ") ;
    TTree *tree_ = (TTree*)fin->Get("tree") ;
    MyClass *obj_ = new MyClass ;
    tree_->SetBranchAddress("MyClass",&obj_) ; 
    tree_->GetEntry(0) ;
    printf ("\n%s\n%s\t%s\t%s\n\n",obj_->plainString,obj_->arrayOfStrings[0],obj_->arrayOfStrings[1],obj_->arrayOfStrings[2]) ;
  fin->Close() ;

  return ; 
}

[quote] which is not quite dynamic[/quote]What do you mean?

You can also separate this in several files:// File header.h struct MyClass { Char_t plainString [300] ; //Char_t *arrayOfStrings [300] ; Char_t arrayOfStrings [3][300] ; } ;

[code]// File tring.C
#include <TFile.h>
#include <TTree.h>

void tring ()
{
TFile *fout = new TFile (“test.root”,“RECREATE”) ;
TTree *tree = new TTree (“tree”,“tree”) ;
MyClass *obj = new MyClass ;
tree->Branch(“MyClass”,&obj) ;
sprintf(obj->plainString,“tata”) ;
sprintf(obj->arrayOfStrings[0],“toto”) ; sprintf(obj->arrayOfStrings[1],“titi”) ; sprintf(obj->arrayOfStrings[2],“tutu”) ;
printf ("\n%s\n%s\t%s\t%s\n\n",obj->plainString,obj->arrayOfStrings[0],obj->arrayOfStrings[1],obj->arrayOfStrings[2]) ;
tree->Fill() ;
tree->Write() ;
fout->Close() ;

TFile *fin = new TFile (“test.root”,“READ”) ;
TTree tree_ = (TTree)fin->Get(“tree”) ;
MyClass *obj_ = new MyClass ;
tree_->SetBranchAddress(“MyClass”,&obj_) ;
tree_->GetEntry(0) ;
printf ("\n%s\n%s\t%s\t%s\n\n",obj_->plainString,obj_->arrayOfStrings[0],obj_->arrayOfStrings[1],obj_->arrayOfStrings[2]) ;
fin->Close() ;

return ;
} [/code]
and the only file you must compile is header.h:

root [] .L header.h+ root [] .x tring.C

Cheers,
Philippe.

If I don’t fix the number of strings in arrayOfStrings (which is precisely what I’d like to do)

Char_t *arrayOfStrings [300] ;
It does not work.
Any clue ?

PS: Thanks for the “.L *.h+” tip.

My Apologies, I did not see this difference. I’ll check into it.

Cheers,
Philippe.

Indeed, we do not support array of char* (and if we did there is/would be some potential ambiguity on ownership of the pointers).
However now that you have a class and are compiling the header file, you can use ‘better’ tools:

//header2.h #if defined(__MAKECINT__) || !defined(__CINT__) // make sure we are not used by the interpreter #ifndef HEADER2_H #define HEADER2_H #include <string> #include <vector> struct MyClass { std::string plainString; std::vector<std::string> arrayOfStrings; } ; #endif // HEADER2_H #endif // make sure we are not used by the interpreter
and

[code]//run.C
#include <TFile.h>
#include <TTree.h>
#include “header2.h”

void run()
{
TFile *fout = new TFile (“test.root”,“RECREATE”) ;
TTree *tree = new TTree (“tree”,“tree”) ;
MyClass *obj = new MyClass ;
tree->Branch(“MyClass”,&obj) ;
obj->plainString = “tata”;
obj->arrayOfStrings.push_back(“toto”);
obj->arrayOfStrings.push_back(“titi”);
obj->arrayOfStrings.push_back(“tutu”);
printf ("\n%s\n%s\t%s\t%s\n\n",obj->plainString.c_str(),obj->arrayOfStrings[0].c_str(),obj->arrayOfStrings[1].c_str(),obj->arrayOfStrings[2].c_str()) ;
tree->Fill() ;
tree->Write() ;
fout->Close() ;

TFile *fin = new TFile (“test.root”,“READ”) ;
TTree tree_ = (TTree)fin->Get(“tree”) ;
MyClass *obj_ = new MyClass ;
tree_->SetBranchAddress(“MyClass”,&obj_) ;
tree_->GetEntry(0) ;
printf ("\n%s\n%s\t%s\t%s\n\n",obj->plainString.c_str(),obj->arrayOfStrings[0].c_str(),obj->arrayOfStrings[1].c_str(),obj->arrayOfStrings[2].c_str()) ;
fin->Close() ;

return ;
}[/code]

Cheers,
Philippe

OK, but still the problem remains: How to make this work with G4?
What I wanted to do at the beginning was :

tree->Branch(“MyClass”,&obj) ;
with a traditional root class (class MyClass : public TObject ; ClassDef (…) ; Double_t* fX ; //[fNX] ; …)
instead of doing :
tree->Branch(“ParticleName”, &ParticleName, “ParticleName /C”) ;
tree->Branch(“TrackID” , &TrackID , “TrackID /I”) ;
tree->Branch(“Ekin_0_MeV” , &Ekin_0 , “Ekin_0_MeV /D”) ;
tree->Branch(“Edep_MeV” , Edep , TString(Form(“Edep_MeV[%d]/D”,numberOfSensitiveVolumes))) ;
which is the only way I managed to make things work.

So here what I propose : I’ll start where I stopped last time, sent you all my attempts, and if we manage to make it work put an end to this infinite post… :wink:
(Sorry everybody!)

In the meanwhile you may have got some feedback from :
root.cern.ch/phpBB2/viewtopic.ph … ght=geant4
Or maybe someone has already investigated the problem!!! Please wave!

What do you reckon ?

Thanks for your time and consideration,
M

[quote]OK, but still the problem remains: How to make this work with G4? [/quote]A priori, you need to get the info from G4, copy it into a nice little object of your own class and use tree->Branch(“MyClass”,&obj);

[quote]So here what I propose : I’ll start where I stopped last time, sent you all my attempts, and if we manage to make it work put an end to this infinite post…[/quote]Please do.

Cheers,
Philippe.

[quote]In the meanwhile you may have got some feedback from :
root.cern.ch/phpBB2/viewtopic.ph … ght=geant4 [/quote]As far as I can tell, the conclusion was that, in order for rootcint to parse Geant4 header files, you need to pass a -I for every single directory where there are include files (aka what ever is in the Makefile include that Geant4 provides).

Cheers,
Philippe.

Allright. I’ll/We’ll keep you posted…
Z

Hi there,

Here’s a way to couple Geant4 with a regular root class containing TString such as:

#ifndef TMyRootEvent_hpp
#define TMyRootEvent_hpp

#include "TObject.h"
#include "TString.h"


class TMyRootEvent : public TObject
{		

	protected :

		Int_t fNCells ; 

		TString   fParticleName ; 
		Double_t  fEkin_0       ;  // initial kinetic energy [MeV]

		TString  *fSensitiveCellName ;  //[fNCells] sensitive cell name
		Double_t *fEkin              ;  //[fNCells] kinetic energy at the entrance of each sensitive cell [MeV]


	public :
			
		TMyRootEvent ()
		{	
			fNCells = -1 ;
			fSensitiveCellName = NULL ; 
			fEkin              = NULL ; 
		}
			
		~TMyRootEvent ()
		{
			delete fSensitiveCellName ; fSensitiveCellName = NULL ; 
			delete fEkin              ; fEkin              = NULL ; 
		}
					
		void InitEvent ( Int_t aNCells )
		{
			fNCells            = aNCells ;
			fSensitiveCellName = new TString  [fNCells] ; 
			fEkin              = new Double_t [fNCells] ; 
			return ; 
		}

		// Getters...
		// Setters...

  ClassDef (TMyRootEvent,1) ;  
			
} ; 


#endif

The implementation has to be inside the class ! (before the end of the class “};”)
(Otherwise some “Multiple Definition” error messages appear…)

The GNUmakefile in that case looks like this:

name := exampleN01
G4TARGET := $(name)
G4EXLIB := true

.PHONY: all
all: dict.cxx lib bin

dict.cxx :
   rootcint -f dict.cxx -c include/TMyRootEvent.hpp include/linkdef.h

myclean :
   rm -rf dict.* $(G4TMP)/$(G4SYSTEM)/exampleN01

include $(G4INSTALL)/config/architecture.gmk

G4NOHIST := true
CPPFLAGS += $(shell root-config --cflags)
# Here comes dict.cxx
LDFLAGS  += $(shell root-config --libs) dict.cxx

include $(G4INSTALL)/config/binmake.gmk 

The include/linkdef.h file just contains:

#ifdef __CINT__
#pragma link C++ class TMyRootEvent ;
#endif

The root file & the tree are created in RunAction:

RunAction()

	fTMyRootEvent = new TMyRootEvent () ; 
	fTMyRootEvent->InitEvent(fNCells) ; 

BeginOfRunAction()

	fRootFout = new TFile ("test.root","RECREATE","My GEANT4 simulation") ;
	fRootTree = new TTree ("tree","My GEANT4 simulation") ; 
	fRootTree->Branch("evt","TMyRootEvent",&fTMyRootEvent) ; 

EndOfRunAction()

	fRootTree->Write() ;  
	fRootFout->Close() ;  

The tree can be filled (fRootTree->Fill()) wherever (EndOfEventAction, SteppingAction, …).

To read the root file generated, one can use:

{
	gROOT->ProcessLine(".L TMyRootEvent.hpp+") ; 
	TFile *fin = new TFile ("test.root") ; 
	TTree *tree = (TTree*)fin->Get("tree") ;
	TMyRootEvent* evt = new TMyRootEvent() ;
	tree->SetBranchAddress("evt",&evt)  ; 
	tree->Show(0) ;
	tree->Draw("evt->GetEkin_0()") ;
	tree->Draw("evt->GetEkin(1)","evt->GetParticleName()==\"alpha\"") ;
}

Thanks to Philippe Canal for his precious help.
Zesp