Re: [ROOT] bug in TTree::Branch?

From: Rene Brun (brun@pcbrun.cern.ch)
Date: Mon Dec 06 2004 - 22:16:54 MET


Hi Sue,

With all my apologies for the wrong information in my previous mail.
You are right. When you specify Event* event=0; an internal object Event
is created to build its class dictionary. This object was not
deleted with the right procedure. I have fixed this problem in the CVS 
version.

Rene Brun

On 
Mon, 6 Dec 2004, Susan Kasahara wrote:

> Hi Rene,
> Thanks for your reply.  I understand that one approach is more efficient 
> than the
> other, but I still think there is a problem in the second case and this 
> has more general
> application than the Event example.   It appears to me that the 
> TTree::Branch method intention is
> to delete the internally constructed Event object and resets the event 
> ptr to 0 on output,
> and in fact I do see the event ptr reset to 0, but the Event destructor 
> is not invoked before this.
> For example, modifying the second test script to print the event ptrs 
> before & after:
> {
> 
> gSystem -> Load("libEvent.so");
> 
> TFile* file = new TFile("test.root","RECREATE");
> TTree* tree = new TTree("Event","Event tree");
> 
> Event* event = 0;
> cout << "Create Event tree branch, event = " << event << endl;
> TBranch *branch = tree->Branch("event", "Event", &event, 64000,99);
> cout << "Done with branch create, event =  " << event << endl;
> 
> }
> 
> yields this:
> root [0] .x testEvent2.C
> Create Event tree branch, event = 0
> Event constructor
> Done with branch create, event =  0
> root[1]
> 
> Thanks again for your help.
> -Sue
> 
> Rene Brun wrote:
> 
> >Hi Sue,
> >
> >The current behaviour is correct.
> >If you have : Event* event = new Event();
> >TTree::Branch uses the existing object to organize the sub branches.
> >If you have: Event* event = 0;
> >TTree::Branch must create an object via the default constructor.
> >This object is not deleted when exiting the branch constructor
> >  -because most of the time the default object created may be used for filling.
> >  -if it was deleted, you will have to create one and call SetBranchAddress.
> >For this reason, it is always more convenient
> >  -to create an object before calling TTree::Branch
> >  -to set the pointer to 0 when reading the Tree.
> >This is the recommended process in our Event example.
> >
> >Rene brun
> >
> >
> >Sue Kasahara wrote:
> >  
> >
> >>Hi root talk,
> >>I've discovered what I think is a bug in the TTree::Branch method:
> >>    virtual TBranch     *Branch(const char *name, const char *classname,
> >>void *addobj, Int_t bufsize=32000, Int_t splitlevel=99);
> >>which is that if I pass it the address of a null ptr, the internally
> >>constructed object of class "classname" is not properly destructed
> >>before exiting the method.
> >>I can illustrate the problem with the test/Event example, which I've
> >>altered only slightly to print two messages: one when the Event
> >>constructor is
> >>invoked, and one when the Event destructor is invoked.
> >>If I execute the following script in a root session:
> >>{
> >>
> >>gSystem -> Load("libEvent.so");
> >>
> >>TFile* file = new TFile("test.root","RECREATE");
> >>TTree* tree = new TTree("Event","Event tree");
> >>
> >>Event* event = new Event();
> >>cout << "Create Event tree branch " << endl;
> >>TBranch *branch = tree->Branch("event", "Event", &event, 64000,99);
> >>cout << "Done with branch create " << endl;
> >>if ( event ) delete event; event = 0;
> >>
> >>}
> >>
> >>I receive the expected result:
> >>root [0] .x testEvent.C
> >>Event constructor
> >>Create Event tree branch
> >>Done with branch create
> >>Event destructor
> >>root[1]
> >>
> >>but if I execute this script:
> >>{
> >>
> >>gSystem -> Load("libEvent.so");
> >>
> >>TFile* file = new TFile("test.root","RECREATE");
> >>TTree* tree = new TTree("Event","Event tree");
> >>
> >>Event* event = 0;
> >>cout << "Create Event tree branch " << endl;
> >>TBranch *branch = tree->Branch("event", "Event", &event, 64000,99);
> >>cout << "Done with branch create " << endl;
> >>
> >>}
> >>
> >>root [0] .x testEvent2.C
> >>Create Event tree branch
> >>Event constructor
> >>Done with branch create
> >>root[1]
> >>
> >>which is unexpected because the Event destructor is never invoked.
> >>I'm attaching the modified Event.cxx that I use in this example.  I'm using
> >>root cvs from today 5 Dec 2004,  (I also observed the problem with root from
> >>23 Oct 2004.), on linux with gcc 3.2.3.
> >>Thanks for your help,
> >>-Sue
> >>
> >>  --------------------------------------------------------------------------------
> >>// @(#)root/test:$Name:  $:$Id: Event.cxx,v 1.27 2004/01/25 20:33:32 brun Exp $
> >>// Author: Rene Brun   19/08/96
> >>
> >>////////////////////////////////////////////////////////////////////////
> >>//
> >>//                       Event and Track classes
> >>//                       =======================
> >>//
> >>//  The Event class is a naive/simple example of an event structure.
> >>//     public:
> >>//        char           fType[20];
> >>//        char          *fEventName;         //run+event number in character format
> >>//        Int_t          fNtrack;
> >>//        Int_t          fNseg;
> >>//        Int_t          fNvertex;
> >>//        UInt_t         fFlag;
> >>//        Double32_t     fTemperature;
> >>//        Int_t          fMeasures[10];
> >>//        Double32_t     fMatrix[4][4];
> >>//        Double32_t    *fClosestDistance; //[fNvertex] indexed array!
> >>//        EventHeader    fEvtHdr;
> >>//        TClonesArray  *fTracks;
> >>//        TRefArray     *fHighPt;            //array of High Pt tracks only
> >>//        TRefArray     *fMuons;             //array of Muon tracks only
> >>//        TRef           fLastTrack;         //pointer to last track
> >>//        TRef           fHistoWeb;          //EXEC:GetHistoWeb reference to an histogram in a TWebFile
> >>//        TH1F          *fH;
> >>//        TBits          fTriggerBits;       //Bits triggered by this event.
> >>//
> >>//   The EventHeader class has 3 data members (integers):
> >>//     public:
> >>//        Int_t          fEvtNum;
> >>//        Int_t          fRun;
> >>//        Int_t          fDate;
> >>//
> >>//
> >>//   The Event data member fTracks is a pointer to a TClonesArray.
> >>//   It is an array of a variable number of tracks per event.
> >>//   Each element of the array is an object of class Track with the members:
> >>//     private:
> >>//        Float_t      fPx;           //X component of the momentum
> >>//        Float_t      fPy;           //Y component of the momentum
> >>//        Float_t      fPz;           //Z component of the momentum
> >>//        Float_t      fRandom;       //A random track quantity
> >>//        Float_t      fMass2;        //The mass square of this particle
> >>//        Float_t      fBx;           //X intercept at the vertex
> >>//        Float_t      fBy;           //Y intercept at the vertex
> >>//        Float_t      fMeanCharge;   //Mean charge deposition of all hits of this track
> >>//        Float_t      fXfirst;       //X coordinate of the first point
> >>//        Float_t      fXlast;        //X coordinate of the last point
> >>//        Float_t      fYfirst;       //Y coordinate of the first point
> >>//        Float_t      fYlast;        //Y coordinate of the last point
> >>//        Float_t      fZfirst;       //Z coordinate of the first point
> >>//        Float_t      fZlast;        //Z coordinate of the last point
> >>//        Double32_t   fCharge;       //Charge of this track
> >>//        Double32_t   fVertex[3];    //Track vertex position
> >>//        Int_t        fNpoint;       //Number of points for this track
> >>//        Short_t      fValid;        //Validity criterion
> >>//        Int_t        fNsp;          //Number of points for this track with a special value
> >>//        Double32_t  *fPointValue;   //[fNsp] a special quantity for some point.
> >>//        TBits        fTriggerBits;  //Bits triggered by this track.
> >>//
> >>//   An example of a batch program to use the Event/Track classes is given
> >>//   in this directory: MainEvent.
> >>//   Look also in the same directory at the following macros:
> >>//     - eventa.C  an example how to read the tree
> >>//     - eventb.C  how to read events conditionally
> >>//
> >>//   During the processing of the event (optionally) also a large number
> >>//   of histograms can be filled. The creation and handling of the
> >>//   histograms is taken care of by the HistogramManager class.
> >>//
> >>////////////////////////////////////////////////////////////////////////
> >>#include <iostream>
> >>using namespace std;
> >>
> >>#include "TRandom.h"
> >>#include "TDirectory.h"
> >>#include "TProcessID.h"
> >>
> >>#include "Event.h"
> >>
> >>ClassImp(EventHeader)
> >>ClassImp(Event)
> >>ClassImp(Track)
> >>ClassImp(HistogramManager)
> >>
> >>TClonesArray *Event::fgTracks = 0;
> >>TH1F *Event::fgHist = 0;
> >>
> >>//______________________________________________________________________________
> >>Event::Event()
> >>{
> >>   // Create an Event object.
> >>   // When the constructor is invoked for the first time, the class static
> >>   // variable fgTracks is 0 and the TClonesArray fgTracks is created.
> >>
> >>  cout << "Event constructor " << endl;
> >>
> >>   if (!fgTracks) fgTracks = new TClonesArray("Track", 1000);
> >>   fTracks = fgTracks;
> >>   fHighPt = new TRefArray;
> >>   fMuons  = new TRefArray;
> >>   fNtrack = 0;
> >>   fH      = 0;
> >>   Int_t i0,i1;
> >>   for (i0 = 0; i0 < 4; i0++) {
> >>      for (i1 = 0; i1 < 4; i1++) {
> >>         fMatrix[i0][i1] = 0.0;
> >>      }
> >>   }
> >>   for (i0 = 0; i0 <10; i0++) fMeasures[i0] = 0;
> >>   for (i0 = 0; i0 <20; i0++) fType[i0] = 0;
> >>   fClosestDistance = 0;
> >>   fEventName = 0;
> >>   fWebHistogram.SetAction(this);
> >>}
> >>
> >>//______________________________________________________________________________
> >>Event::~Event()
> >>{
> >>  cout << "Event destructor " << endl;
> >>
> >>   Clear();
> >>   if (fH == fgHist) fgHist = 0;
> >>   delete fH; fH = 0;
> >>   delete fHighPt; fHighPt = 0;
> >>   delete fMuons;  fMuons = 0;
> >>   delete [] fClosestDistance;
> >>   if (fEventName) delete [] fEventName;
> >>}
> >>
> >>//______________________________________________________________________________
> >>void Event::Build(Int_t ev, Int_t arg5, Float_t ptmin) {
> >>  char etype[20];
> >>  Float_t sigmat, sigmas;
> >>  gRandom->Rannor(sigmat,sigmas);
> >>  Int_t ntrack   = Int_t(arg5 +arg5*sigmat/120.);
> >>  Float_t random = gRandom->Rndm(1);
> >>
> >>  //Save current Object count
> >>  Int_t ObjectNumber = TProcessID::GetObjectCount();
> >>  Clear();
> >>  fHighPt->Delete();
> >>  fMuons->Delete();
> >>
> >>  Int_t nch = 15;
> >>  if (ev >= 100)   nch += 3;
> >>  if (ev >= 10000) nch += 3;
> >>  if (fEventName) delete [] fEventName;
> >>  fEventName = new char[nch];
> >>  sprintf(fEventName,"Event%d_Run%d",ev,200);
> >>  sprintf(etype,"type%d",ev%5);
> >>  SetType(etype);
> >>  SetHeader(ev, 200, 960312, random);
> >>  SetNseg(Int_t(10*ntrack+20*sigmas));
> >>  SetNvertex(Int_t(1+20*gRandom->Rndm()));
> >>  SetFlag(UInt_t(random+0.5));
> >>  SetTemperature(random+20.);
> >>
> >>  for(UChar_t m = 0; m < 10; m++) {
> >>     SetMeasure(m, Int_t(gRandom->Gaus(m,m+1)));
> >>  }
> >>  for(UChar_t i0 = 0; i0 < 4; i0++) {
> >>    for(UChar_t i1 = 0; i1 < 4; i1++) {
> >>       SetMatrix(i0,i1,gRandom->Gaus(i0*i1,1));
> >>    }
> >>  }
> >>
> >>  fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1)));
> >>  fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1)));
> >>  fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1)));
> >>
> >>  //  Create and Fill the Track objects
> >>  for (Int_t t = 0; t < ntrack; t++) AddTrack(random,ptmin);
> >>
> >>  //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);
> >>}
> >>
> >>//______________________________________________________________________________
> >>Track *Event::AddTrack(Float_t random, Float_t ptmin)
> >>{
> >>   // Add a new track to the list of tracks for this event.
> >>   // To avoid calling the very time consuming operator new for each track,
> >>   // the standard but not well know C++ operator "new with placement"
> >>   // is called. If tracks[i] is 0, a new Track object will be created
> >>   // otherwise the previous Track[i] will be overwritten.
> >>
> >>   TClonesArray &tracks = *fTracks;
> >>   Track *track = new(tracks[fNtrack++]) Track(random);
> >>   //Save reference to last Track in the collection of Tracks
> >>   fLastTrack = track;
> >>   //Save reference in fHighPt if track is a high Pt track
> >>   if (track->GetPt() > ptmin)   fHighPt->Add(track);
> >>   //Save reference in fMuons if track is a muon candidate
> >>   if (track->GetMass2() < 0.11) fMuons->Add(track);
> >>   return track;
> >>}
> >>
> >>//______________________________________________________________________________
> >>void Event::Clear(Option_t * /*option*/)
> >>{
> >>   fTracks->Clear("C"); //will also call Track::Clear
> >>   fHighPt->Delete();
> >>   fMuons->Delete();
> >>}
> >>
> >>//______________________________________________________________________________
> >>void Event::Reset(Option_t * /*option*/)
> >>{
> >>// Static function to reset all static objects for this event
> >>//   fgTracks->Delete(option);
> >>
> >>   delete fgTracks; fgTracks = 0;
> >>   fgHist   = 0;
> >>}
> >>
> >>//______________________________________________________________________________
> >>void Event::SetHeader(Int_t i, Int_t run, Int_t date, Float_t random)
> >>{
> >>   fNtrack = 0;
> >>   fEvtHdr.Set(i, run, date);
> >>   if (!fgHist) fgHist = new TH1F("hstat","Event Histogram",100,0,1);
> >>   fH = fgHist;
> >>   fH->Fill(random);
> >>}
> >>
> >>//______________________________________________________________________________
> >>void Event::SetMeasure(UChar_t which, Int_t what) {
> >>   if (which<10) fMeasures[which] = what;
> >>}
> >>
> >>//______________________________________________________________________________
> >>void Event::SetRandomVertex() {
> >>   // This delete is to test the relocation of variable length array
> >>   if (fClosestDistance) delete [] fClosestDistance;
> >>   if (!fNvertex) {
> >>      fClosestDistance = 0;
> >>      return;
> >>   }
> >>   fClosestDistance = new Double32_t[fNvertex];
> >>   for (Int_t k = 0; k < fNvertex; k++ ) {
> >>      fClosestDistance[k] = gRandom->Gaus(1,1);
> >>   }
> >>}
> >>
> >>//______________________________________________________________________________
> >>Track::Track(const Track &orig) : TObject(orig)
> >>{
> >>   // Copy a track object
> >>
> >>   fPx = orig.fPx;
> >>   fPy = orig.fPy;
> >>   fPz = orig.fPx;
> >>   fRandom = orig.fRandom;
> >>   fMass2 = orig.fMass2;
> >>   fBx = orig.fBx;
> >>   fBy = orig.fBy;
> >>   fMeanCharge = orig.fMeanCharge;
> >>   fXfirst = orig.fXfirst;
> >>   fXlast  = orig.fXlast;
> >>   fYfirst = orig.fYfirst;
> >>   fYlast  = orig.fYlast;
> >>   fZfirst = orig.fZfirst;
> >>   fZlast  = orig.fZlast;
> >>   fCharge = orig.fCharge;
> >>
> >>   fVertex[0] = orig.fVertex[0];
> >>   fVertex[1] = orig.fVertex[1];
> >>   fVertex[2] = orig.fVertex[2];
> >>   fNpoint = orig.fNpoint;
> >>   fNsp = orig.fNsp;
> >>   if (fNsp) {
> >>      fPointValue = new Double32_t[fNsp];
> >>      for(int i=0; i<fNsp; i++) {
> >>         fPointValue[i] = orig.fPointValue[i];
> >>      }
> >>   } else {
> >>      fPointValue = 0;
> >>   }
> >>   fValid  = orig.fValid;
> >>
> >>   fTriggerBits = orig.fTriggerBits;
> >>
> >>}
> >>
> >>//______________________________________________________________________________
> >>Track::Track(Float_t random) : TObject(),fTriggerBits(64)
> >>{
> >>   // Create a track object.
> >>   // Note that in this example, data members do not have any physical meaning.
> >>
> >>   Float_t a,b,px,py;
> >>   gRandom->Rannor(px,py);
> >>   fPx = px;
> >>   fPy = py;
> >>   fPz = TMath::Sqrt(px*px+py*py);
> >>   fRandom = 1000*random;
> >>   if (fRandom < 10) fMass2 = 0.106;
> >>   else if (fRandom < 100) fMass2 = 0.8;
> >>   else if (fRandom < 500) fMass2 = 4.5;
> >>   else if (fRandom < 900) fMass2 = 8.9;
> >>   else  fMass2 = 9.8;
> >>   gRandom->Rannor(a,b);
> >>   fBx = 0.1*a;
> >>   fBy = 0.1*b;
> >>   fMeanCharge = 0.01*gRandom->Rndm(1);
> >>   gRandom->Rannor(a,b);
> >>   fXfirst = a*10;
> >>   fXlast  = b*10;
> >>   gRandom->Rannor(a,b);
> >>   fYfirst = a*12;
> >>   fYlast  = b*16;
> >>   gRandom->Rannor(a,b);
> >>   fZfirst = 50 + 5*a;
> >>   fZlast  = 200 + 10*b;
> >>   fCharge = Double32_t(Int_t(3*gRandom->Rndm(1)) - 1);
> >>
> >>   fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1)));
> >>   fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1)));
> >>   fTriggerBits.SetBitNumber((UInt_t)(64*gRandom->Rndm(1)));
> >>
> >>   fVertex[0] = gRandom->Gaus(0,0.1);
> >>   fVertex[1] = gRandom->Gaus(0,0.2);
> >>   fVertex[2] = gRandom->Gaus(0,10);
> >>   fNpoint = Int_t(60+10*gRandom->Rndm(1));
> >>   fNsp = Int_t(3*gRandom->Rndm(1));
> >>   if (fNsp) {
> >>      fPointValue = new Double32_t[fNsp];
> >>      for(int i=0; i<fNsp; i++) {
> >>         fPointValue[i] = i+1;
> >>      }
> >>   } else {
> >>      fPointValue = 0;
> >>   }
> >>   fValid  = Int_t(0.6+gRandom->Rndm(1));
> >>}
> >>
> >>//______________________________________________________________________________
> >>void Track::Clear(Option_t * /*option*/)
> >>{
> >>   fTriggerBits.Clear();
> >>   delete [] fPointValue;
> >>   fPointValue=0;
> >>}
> >>
> >>//______________________________________________________________________________
> >>HistogramManager::HistogramManager(TDirectory *dir)
> >>{
> >>   // Create histogram manager object. Histograms will be created
> >>   // in the "dir" directory.
> >>
> >>   // Save current directory and cd to "dir".
> >>   TDirectory *saved = gDirectory;
> >>   dir->cd();
> >>
> >>   fNtrack      = new TH1F("hNtrack",    "Ntrack",100,575,625);
> >>   fNseg        = new TH1F("hNseg",      "Nseg",100,5800,6200);
> >>   fTemperature = new TH1F("hTemperature","Temperature",100,19.5,20.5);
> >>   fPx          = new TH1F("hPx",        "Px",100,-4,4);
> >>   fPy          = new TH1F("hPy",        "Py",100,-4,4);
> >>   fPz          = new TH1F("hPz",        "Pz",100,0,5);
> >>   fRandom      = new TH1F("hRandom",    "Random",100,0,1000);
> >>   fMass2       = new TH1F("hMass2",     "Mass2",100,0,12);
> >>   fBx          = new TH1F("hBx",        "Bx",100,-0.5,0.5);
> >>   fBy          = new TH1F("hBy",        "By",100,-0.5,0.5);
> >>   fMeanCharge  = new TH1F("hMeanCharge","MeanCharge",100,0,0.01);
> >>   fXfirst      = new TH1F("hXfirst",    "Xfirst",100,-40,40);
> >>   fXlast       = new TH1F("hXlast",     "Xlast",100,-40,40);
> >>   fYfirst      = new TH1F("hYfirst",    "Yfirst",100,-40,40);
> >>   fYlast       = new TH1F("hYlast",     "Ylast",100,-40,40);
> >>   fZfirst      = new TH1F("hZfirst",    "Zfirst",100,0,80);
> >>   fZlast       = new TH1F("hZlast",     "Zlast",100,0,250);
> >>   fCharge      = new TH1F("hCharge",    "Charge",100,-1.5,1.5);
> >>   fNpoint      = new TH1F("hNpoint",    "Npoint",100,50,80);
> >>   fValid       = new TH1F("hValid",     "Valid",100,0,1.2);
> >>
> >>   // cd back to original directory
> >>   saved->cd();
> >>}
> >>
> >>//______________________________________________________________________________
> >>HistogramManager::~HistogramManager()
> >>{
> >>   // Clean up all histograms.
> >>
> >>   // Nothing to do. Histograms will be deleted when the directory
> >>   // in which tey are stored is closed.
> >>}
> >>
> >>//______________________________________________________________________________
> >>void HistogramManager::Hfill(Event *event)
> >>{
> >>   // Fill histograms.
> >>
> >>   fNtrack->Fill(event->GetNtrack());
> >>   fNseg->Fill(event->GetNseg());
> >>   fTemperature->Fill(event->GetTemperature());
> >>
> >>   for (Int_t itrack = 0; itrack < event->GetNtrack(); itrack++) {
> >>      Track *track = (Track*)event->GetTracks()->UncheckedAt(itrack);
> >>      fPx->Fill(track->GetPx());
> >>      fPy->Fill(track->GetPy());
> >>      fPz->Fill(track->GetPz());
> >>      fRandom->Fill(track->GetRandom());
> >>      fMass2->Fill(track->GetMass2());
> >>      fBx->Fill(track->GetBx());
> >>      fBy->Fill(track->GetBy());
> >>      fMeanCharge->Fill(track->GetMeanCharge());
> >>      fXfirst->Fill(track->GetXfirst());
> >>      fXlast->Fill(track->GetXlast());
> >>      fYfirst->Fill(track->GetYfirst());
> >>      fYlast->Fill(track->GetYlast());
> >>      fZfirst->Fill(track->GetZfirst());
> >>      fZlast->Fill(track->GetZlast());
> >>      fCharge->Fill(track->GetCharge());
> >>      fNpoint->Fill(track->GetNpoint());
> >>      fValid->Fill(track->GetValid());
> >>   }
> >>}
> >>    
> >>
> 



This archive was generated by hypermail 2b29 : Sun Jan 02 2005 - 05:50:10 MET