Root Tree Custom Objects

Hello All,

I have been struggling to get a TTree with custom objects working correctly.
I need to generate some custom Objects which I can then read in a Geant4 application.
I followed this advice, http://root.cern.ch/phpBB3//viewtopic.php?t=6172, and changed the JetEvent example from Root into the following file TMyRootEvent.hpp. Otherwise I was getting vtable and typeinfo errors when compiling my Geant4 application.

#ifndef TMyRootEvent_hpp
#define TMyRootEvent_hpp

#include "TObject.h"
#include "TString.h"
#include "TMath.h"   
#include "TRandom.h"   
#include "TClonesArray.h"
#include "TRefArray.h"
#include "TVector3.h"

class Hit : public TObject {

public:
  Float_t      fX;           //X of hit
  Float_t      fY;           //Y of hit
  Float_t      fZ;           //Z of hit

public:
  Hit(){ };
  ~Hit() { };
   
  ClassDef(Hit,1)  //A track hit
};

class Track : public TObject {

public:
  Float_t      fPx;           //X component of the momentum
  Float_t      fPy;           //Y component of the momentum
  Float_t      fPz;           //Z component of the momentum
  Int_t        fNhit;         //Number of hits for this track
  TRefArray    fHits;         //List of Hits for this track

public:
  Track() { };
  ~Track() { };
  Int_t         GetNhit() const { return fNhit; }
  TRefArray   &GetHits()  {return fHits; }
   
  ClassDef(Track,1)  //A track segment
};


class Jet : public TObject {

public:
  Double_t   fPt;       //Pt of jet
  Double_t   fPhi;      //Phi of jet
  TRefArray  fTracks;   //List of tracks in the jet

public:
  Jet() { };
  ~Jet() { };
  TRefArray   &GetTracks() {return fTracks; }

  ClassDef(Jet,1)  //Jet class
};

class Particle : public TObject {

private:
  Int_t      fPdgCode;
  
  Double_t   fPx;       //Px of jet
  Double_t   fPy;       //Py of jet
  Double_t   fPz;       //Pz of jet
  Double_t   fE;        //E of jet

public:
  Particle() { fPx = fPy = fPz = fE = 0;};
  ~Particle() { };

public:
  Int_t       GetPdgCode() const {return fPdgCode;};

  Double_t    Px() const {return fPx;};
  Double_t    Py() const {return fPy;};
  Double_t    Pz() const {return fPz;};
  Double_t    E()  const {return fE;};

  void    SetPdgCode(Int_t pdg)  {fPdgCode = pdg;};

  void    SetPx(Double_t px)  {fPx = px;};
  void    SetPy(Double_t py)  {fPy = py;};
  void    SetPz(Double_t pz)  {fPz = pz;};
  void    SetE(Double_t e)    {fE = e;};

  ClassDef(Particle,1)  //Particle class
};

class TMyRootEvent : public TObject
{      

private:
  TVector3       fVertex;            //vertex coordinates
  Int_t          fNparticle;              //Number of particles
  Int_t          fNjet;              //Number of jets
  Int_t          fNtrack;            //Number of tracks
  Int_t          fNhitA;             //Number of hist in detector A
  Int_t          fNhitB;             //Number of hist in detector B
  TClonesArray  *fParticles;         //->array with all Particles
  TClonesArray  *fJets;              //->array with all jets
  TClonesArray  *fTracks;            //->array with all tracks
  TClonesArray  *fHitsA;             //->array of hits in detector A
  TClonesArray  *fHitsB;             //->array of hits in detector B

public :
  
  TMyRootEvent()
  {
    // Create a TMyRootEvent object.
    // When the constructor is invoked for the first time, the class static
    // variables fgxxx are 0 and the TClonesArray fgxxx are created.
    
    fParticles = new TClonesArray("Particle", 100);
    fTracks = new TClonesArray("Track", 100);
    fJets   = new TClonesArray("Jet", 10);
    fHitsA  = new TClonesArray("Hit", 10000);
    fHitsB  = new TClonesArray("Hit", 1000);

    fNparticle =  fNjet   = fNtrack = fNhitA  = fNhitB  = 0;
  }
  
  ~TMyRootEvent ()
  {
    Reset(0);
  }
               
  void Build(Int_t jetm = 3, Int_t trackm = 10, Int_t hitam = 100, Int_t hitbm = 10) {
    //Build one event
    
    //Save current Object count
    Int_t ObjectNumber = TProcessID::GetObjectCount();
    Clear(0);
    
    Hit *hit;
    Track *track;
    Jet *jet;
    Particle *particle;
    fNparticle =  fNjet   = fNtrack = fNhitA  = fNhitB  = 0;
    
    fVertex.SetXYZ(gRandom->Gaus(0,0.1),
		   gRandom->Gaus(0,0.2),
		   gRandom->Gaus(0,10));
    
    Int_t njets = (Int_t)gRandom->Gaus(jetm,1); if (njets < 1) njets = 1;
    for (Int_t j=0;j<njets;j++) {
      particle = AddParticle();
      jet = AddJet();
      jet->fPt = gRandom->Gaus(0,10);
      jet->fPhi = 2*TMath::Pi()*gRandom->Rndm();
      Int_t ntracks = (Int_t)gRandom->Gaus(trackm,3); if (ntracks < 1) ntracks = 1;
      for (Int_t t=0;t<ntracks;t++) {
	track = AddTrack();
	track->fPx = gRandom->Gaus(0,1);
	track->fPy = gRandom->Gaus(0,1);
	track->fPz = gRandom->Gaus(0,5);
	jet->fTracks.Add(track);
	Int_t nhitsA = (Int_t)gRandom->Gaus(hitam,5);
	for (Int_t ha=0;ha<nhitsA;ha++) {
	  hit = AddHitA();
	  hit->fX = 10000*j + 100*t +ha;
	  hit->fY = 10000*j + 100*t +ha+0.1;
	  hit->fZ = 10000*j + 100*t +ha+0.2;
	  track->fHits.Add(hit);
	}
	Int_t nhitsB = (Int_t)gRandom->Gaus(hitbm,2);
	for (Int_t hb=0;hb<nhitsB;hb++) {
	  hit = AddHitB();
	  hit->fX = 20000*j + 100*t +hb+0.3;
	  hit->fY = 20000*j + 100*t +hb+0.4;
	  hit->fZ = 20000*j + 100*t +hb+0.5;
	  track->fHits.Add(hit);
	}
	track->fNhit = nhitsA + nhitsB;
      }
    }         
    //Restore Object count 
    //To save space in the table keeping track of all referenced objects
    //we assume that our events do not address each other. We reset the 
    //object count to what it was at the beginning of the event.
    TProcessID::SetObjectCount(ObjectNumber);
  }
  


  void Clear(Option_t *option)
  {
    fNparticle =  fNjet   = fNtrack = fNhitA  = fNhitB  = 0;
    fParticles->Clear(option);
    fJets->Clear(option);
    fTracks->Clear(option);
    fHitsA->Clear(option);
    fHitsB->Clear(option);
  }

  //______________________________________________________________________________
  void Reset(Option_t *)
  {
    // Static function to reset all static objects for this event

    delete fParticles; fParticles = 0;
    delete fJets;   fJets = 0;
    delete fTracks; fTracks = 0;
    delete fHitsA;  fHitsA = 0;
    delete fHitsB;  fHitsB = 0;
  }


  Int_t         GetNparticle()   const { return fNparticle; }
  Int_t         GetNjet()   const { return fNjet; }
  Int_t         GetNtrack() const { return fNtrack; }
  Int_t         GetNhitA()  const { return fNhitA; }
  Int_t         GetNhitB()  const { return fNhitB; }

  Particle *AddParticle()
  {
    // Add a new Jet to the list of tracks for this event.

    TClonesArray &particles = *fParticles;
    Particle *particle = new(particles[fNparticle++]) Particle();
    return particle;
  }

  Jet *AddJet()
  {
    // Add a new Jet to the list of tracks for this event.

    TClonesArray &jets = *fJets;
    Jet *jet = new(jets[fNjet++]) Jet();
    return jet;
  }

  Track *AddTrack()
  {
    // Add a new track to the list of tracks for this event.

    TClonesArray &tracks = *fTracks;
    Track *track = new(tracks[fNtrack++]) Track();
    return track;
  }

  Hit *AddHitA()
  {
    // Add a new hit to the list of hits in detector A

    TClonesArray &hitsA = *fHitsA;
    Hit *hit = new(hitsA[fNhitA++]) Hit();
    return hit;
  }

  Hit *AddHitB()
  {
    // Add a new hit to the list of hits in detector B

    TClonesArray &hitsB = *fHitsB;
    Hit *hit = new(hitsB[fNhitB++]) Hit();
    return hit;
  }

  TClonesArray *GetJets() const { return fJets; }

  ClassDef (TMyRootEvent,1) ;  
  
} ; 


#endif

I was able to write from of my Geant4 classes as follows (just fine!)

f = new TFile("JetEvent.root","recreate");
  T = new TTree("T","Event example with Jets");
  event = new TMyRootEvent;
  T->Branch("event","TMyRootEvent",&event,8000,2);
  
  for (Int_t ev=0;ev<250;ev++) {
    event->Build();
    T->Fill();
  }

  T->Print();
  T->Write();

In a simple root macro, I could only write the file as follows.

root>.L TMyRootEvent.hpp+
root>.x test.C

#include <stdio.h>
#include <vector>
#include "TMyRootEvent.hpp"
#include "TFile.h"
#include "TTree.h"
#include "TRandom.h"
#include "TROOT.h"
#include "TSystem.h"
#include "Riostream.h"
using namespace std;

void test(){

  //gROOT->ProcessLine(".L TMyRootEvent.hpp+");
  gSystem->Load("TMyRootEvent_hpp.so");
  TFile *f = TFile::Open("events.root","RECREATE");

  TTree *t1 = new TTree("t1","Tree With Particles");
  TMyRootEvent *event = new TMyRootEvent();

  t1->Branch("event",&event,8000,2);

  for(Int_t iev = 0; iev < 10; iev++){
    event->Build();
    t1->Fill();
  }
  
  t1->Write();
  t1->Print();
  
}

Uncommenting gROOT->ProcessLine(".L TMyRootEvent.hpp+") did not work!

If I would generate the library, leave my root session, and just load said library agian,
I would get something along these lines

Error in TTree::Branch: The actual class (TVector3) of the object provided for the definition of the branch “event” does not inherit from TMyRootEvent
*** glibc detected *** /home/yakov/FC/root/bin/root.exe: corrupted double-linked list: 0x0000000001ff7c20 ***
Memory Map, etc etc etc etc.

Could someone please help me with this?
I feel it may have to do with the ClassImp stuff because that was practically the only modification I did
in going from JetEvents.h/cpp ->TMyRootEvent.hpp

Best,

-Yakov

In your “test.C”, try to use: // ... class TMyRootEvent; #if !defined(__CINT__) #include "TMyRootEvent.hpp" #endif /* !defined(__CINT__) */ //... void test() { if (!TClass::GetDict("TMyRootEvent")) { gROOT->ProcessLine(".L TMyRootEvent.hpp++"); } // ... }
See also [url=https://root-forum.cern.ch/t/adding-classes-in-aclic/15817/2 post[/url] (it deals with exactly the same type of problems as you have now).

Hello Wile,

Thank you for the reply. I did this, as well as putting in ClassImp, and it is working.

The link regarding geant from my previous post seems to take care of that / do the same by invoking the rootcint command and generating a dictionary, so all my geant code did not need this.

For the root macros, this seems like the correct solution.

Cheers,

-Yakov