Storing/Reading custom class

Hi, I’ve written a custom class “Particle” like:

class Particle : public TObject {
  public:  
    Particle();
    ~Particle();
    Double_t x;
    Double_t y;
    Double_t z;
    ClassDef(Particle,1)
};

then, I create a TTree and store several values:

  TTree *tree = new TTree("target","data");
  Particle *p = new Particle;
  tree->Branch("particle",&p);
(...)
  tree->Fill();

Now I want to read the information from the rootfile. I tried doing MakeClass on the resulting file and instead of giving me access to the “Particle” objects, I get access to the variables inside:

   TBranch        *b_particle_x;   //!
   TBranch        *b_particle_y;   //!
   TBranch        *b_particle_z;   //!

if I try something like:

fChain->SetBranchAddress("particle", &particle );

The information returned on the “particle” object is incorrect. Which would be the correct way to retrieve the actual object?

tree->Branch(“particle”, &p, 32000, 0);

Search for “splitlevel” in http://root.cern.ch/root/html/TTree.html

Exactly what I needed! Many thanks!

I have a custom class that includes a TTree, kListData (see attached). I’ve stashed this class inside a folder inside my ROOT GIT repo and modified the CMakeList.txt files to build this class as part of ROOT.

The objective of the parsing function call is to selectively take data from one root tree and store it in another as a vector. The plan is to use the vector to make the class expandable for reconstructing coincident events later. The class successfully compiles and parses files but when I try to kListData.Scan() the data inside the tree ROOT just hangs.

Could someone provide me some pointers on how to use a TTree inside a custom class?

TListSpectrum.h (2.5 KB) TListSpectrum.cpp (7.5 KB)

Hi,

@pcanal can surely help. But please clarify one thing: You should be able to use a TTree inside a custom class in the same way that you can use a Tree in a macro etc. Is the problem that you want to save the custom class with a tree inside into another tree?

Most likely the kListData TTree object gets attached to some (random/arbitrary) TFile outside of TListSpectrum and is deleted when that file is deleted/closed.

If this is the case and you need/want a in-memory tree then use

TListSpectrum::TListSpectrum() : TNamed("ListSpectrum", "Unnamed Rawdata File"), kListData("kListData","kListData")
{
	kChannels = 0;
	kSampleNorm = 1.0;
	kStartofCounting = *(new TTimeStamp());
	kGeometry = "{EMPTY}";
	kSampleUnit = "sample";
	
        kListData.SetDirectory(nullptr);
	
	kEnergyCalibration = {0.0, 0.0, 0.0}; // this should be in TListSpectrumCal
}

If you mean for it to belong to a file, I recommend making it more explicit:

TListSpectrum::TListSpectrum(TFile *file) : TNamed("ListSpectrum", "Unnamed Rawdata File"), kListData("kListData","kListData")
{
	kChannels = 0;
	kSampleNorm = 1.0;
	kStartofCounting = *(new TTimeStamp());
	kGeometry = "{EMPTY}";
	kSampleUnit = "sample";
	
        kListData.SetDirectory(file);
	
	kEnergyCalibration = {0.0, 0.0, 0.0}; // this should be in TListSpectrumCal
}

and make sure the TFile is open until you delete the TListSpectrum object.

1 Like

As a side note since kStartofCounting is a TTimeStamp object, the constructor will of TListSpectrum will implicitly call the default constructor for it and thus:

is redundant … and is a memory leak (the object create by new TTimeStamp is never deleted.

1 Like

I want to save the custom class as itself inside a TFile much like you would declare TFile X then TTree Y and as you fill it populates the TFile with the data. I’d like to declare TFile X then TListSpectrum Y and as it is populated with the data it fills the TFile with the data.

Yes, thank you, good eye.

What is the exact sequence of code that leads to the crash?

I’m definitely doing something silly and ROOT is doing exactly what ROOT is supposed to do. The crash is the traditional user error of creating the TTree without a resident TFile.

  1. I populate a huge tree (~2-6Gb)
  2. I create a TFile pointer using the “recreate” option
  3. I ask the TListSpectrum (TNamed object) to Write itself.
  4. It grabs the TFile pointer and begins preparations to dump an object that far exceeds the buffer ROOT defined buffer limit and crashes.

My question isn’t so much why is ROOT crashing but rather how best to write a TListSpectrum object to a TFile for storage purposes. Alternatively, I’ve thought about just writing the private ROOT objects that makeup TListSpectrum to the TFile by overloading the TNamed Write call but that wouldn’t have fixed the issue identified above where I was trying to write large memory-resident TTrees to a TFile. I have modified the constructors in TListSpectrum as you suggested and the TTree is now getting stored correctly, thank you so much for your help!

The idea is to use this class structure to store binary list-mode data files in an archive and incorporate functionality that makes it easier for spectroscopists to analyze and re-analyze the data using canned analysis utilities associated with two separate classes TListSpectrumCal and TListSpectrumAnalysis.

I’m just guessing here, but give this a thought:
Could it be that ROOT doesn’t serialise an object larger than 1 Gb in a single I/O operation?
If TListSpectrum contains a tree, and you write the tree manually, you might get around the 1 Gb limit, and therefore it works.

The way we have it working right now is just to instantiate a TFile and write the TTree separate from the object. ROOT was crashing for me if I filled the TTree object from binary then ask it to write the TTree to a TFile using the TTree::Write() call. I believe the correct method of filling large (>1Gb) TTrees is to instantiate a TFile then the TTree such that the TDirectory the TTree is linked to is a real file. As you fill the tree it writes to file such that you don’t end up trying to write a multi-Gb TTree to a file.

That sounds correct.