19.2. Reading Geant4 Output with ROOT
Table of Contents
Opening ROOT files
After you have run a Geant4 simulation that writes ROOT output, the next step is to inspect that output with the ROOT framework. Typically, Geant4 uses G4AnalysisManager to create a ROOT file that contains histograms and ntuples. These are stored in objects such as TH1, TH2, and TTree inside a .root file.
To begin, start a ROOT session from the command line in the directory where your output file is located. If your Geant4 output file is called output.root, you can start ROOT and open the file interactively by typing:
root output.root
ROOT will open and automatically load the file as the current file, which is accessible through the global variable gFile. You can verify that the file is open by calling:
gFile->Print();This lists the objects stored in the file, such as histograms and trees, along with their names and classes. If you prefer to open the file explicitly, you can start ROOT with no arguments, then open the file from the ROOT prompt:
TFile *f = TFile::Open("output.root");
f->ls();
The ls() method of TFile prints a directory-like listing of the file contents. You will usually see one or more TTree objects, typically named something like ntuple, Hits, or another name you chose in your Geant4 code, and possibly some histograms such as h1, hEnergy, or similar.
In many Geant4 examples that use G4AnalysisManager, ntuples are stored in directories inside the ROOT file. For example, you might see an object path like /ntuple0 or /Histo. To access a specific object, use Get with the full path:
TTree *tree = (TTree*) f->Get("ntuple0");Once you have a pointer to the object, you can manipulate it directly.
You can also open ROOT files and inspect them from a C++ macro rather than interactively. A simple macro called inspect.C might contain:
void inspect() {
TFile *f = TFile::Open("output.root");
if (!f || f->IsZombie()) {
std::cout << "Error opening file" << std::endl;
return;
}
f->ls();
}You can run this macro from the ROOT prompt with:
root -l inspect.CThis approach is convenient when you want to repeatedly examine files with the same basic checks.
If you have several ROOT files from different simulation runs, you can open them sequentially in the same ROOT session by calling TFile::Open multiple times. The most recently opened file becomes the current file, but you can keep pointers to earlier files to compare their contents programmatically.
Important rule: Always check that a TFile is valid with if (!f || f->IsZombie()) before using it. Attempting to read from an invalid file pointer can cause crashes or misleading results.
Inspecting TTrees
Most Geant4 simulations that record detailed event information use ROOT TTree objects, which store data in a columnar format. A TTree in this context usually corresponds to an ntuple created with G4AnalysisManager. Each row often corresponds to one event, one hit, or one step, depending on how you configured your analysis. Each column corresponds to a variable, for example deposited energy, position coordinates, time, or particle type.
Once you have obtained a pointer to a TTree, for example:
TTree *tree = (TTree*) gFile->Get("ntuple0");you can inspect its structure interactively. The simplest way is to call:
tree->Print();This prints a human readable description of the tree, including the number of entries and all branch names, their types, and in some cases titles or units. This is the quickest way to learn what data your Geant4 simulation has saved.
A typical output might list branches such as EventID, Edep, x, y, z, and time. These names usually correspond directly to the column names you defined in your Geant4 analysis code. To see only the list of branches, you can use:
tree->GetListOfBranches()->Print();This is helpful when you just need names and do not need the full detailed description.
ROOT allows you to quickly visualize tree data without writing explicit loops using the Draw method. For example, if your tree has a branch called Edep, representing deposited energy in a detector, you can create and display a histogram of that branch with:
tree->Draw("Edep");
This command creates an automatic histogram and fills it from the Edep values in all entries of the tree. You can apply cuts to examine only a subset of the data. For instance, to plot energy deposition only for events with Edep > 1*MeV assuming the branch stores values in MeV, you might write:
tree->Draw("Edep", "Edep > 1.0");For two dimensional plots, such as deposited energy versus position, you can draw:
tree->Draw("Edep:x");
This fills a two dimensional histogram with x on the horizontal axis and Edep on the vertical axis.
To access data programmatically, you often want to read branches into C++ variables. This involves setting branch addresses. If your tree has a double precision branch Edep and a double precision branch x, you can read them as follows:
double Edep;
double x;
tree->SetBranchAddress("Edep", &Edep);
tree->SetBranchAddress("x", &x);
Long64_t nEntries = tree->GetEntries();
for (Long64_t i = 0; i < nEntries; ++i) {
tree->GetEntry(i);
// Now Edep and x contain the values for entry i
}This pattern lets you perform custom calculations, such as computing averages, creating new histograms with specific binning, or applying complex selection criteria. It is particularly useful when analyzing detailed Geant4 output like step level information or detector hit collections.
When your Geant4 output uses branches that are arrays or vectors, for example multiple hits in a single event, the branch type will typically be something like std::vector<double>. Inspecting the tree with Print() will show this. In ROOT, you can connect such branches to std::vector pointers:
std::vector<double> *EdepVec = nullptr;
tree->SetBranchAddress("Edep", &EdepVec);
for (Long64_t i = 0; i < tree->GetEntries(); ++i) {
tree->GetEntry(i);
// EdepVec now points to the vector of energy deposits for this event
for (size_t j = 0; j < EdepVec->size(); ++j) {
double edep = EdepVec->at(j);
// Process edep here
}
}This is commonly used when one Geant4 event produces multiple hits in a detector.
In many Geant4 based analyses, you will want to check that the number of entries in the tree matches your expectations. If you wrote out one ntuple row per event, the number of entries should equal the number of simulated events. Use:
std::cout << "Entries: " << tree->GetEntries() << std::endl;If the number is different from what you expect, it can indicate that the ntuple was filled conditionally, for example only for events where energy deposition occurred.
To quickly summarize numerical information from a branch, use the GetMinimum and GetMaximum methods:
double minE = tree->GetMinimum("Edep");
double maxE = tree->GetMaximum("Edep");
std::cout << "Edep range: " << minE << " to " << maxE << std::endl;This can provide a first check that your Geant4 simulation is producing results in a reasonable range, for example energies that match the particle source energy and detector design.
For more structured inspection, you can clone selections of a tree into a new smaller tree. For example, to create a new tree containing only entries where deposited energy exceeds a threshold, type:
TTree *tSel = tree->CopyTree("Edep > 0.1");This new tree can then be written to a separate ROOT file or analyzed further without carrying along all the original entries.
When you rely on ROOT graphics, it is useful to remember that ROOT automatically manages canvases. If you draw from different trees or branches during one session, pay attention to which canvas you are looking at and which histogram is being updated. You can assign names to histograms drawn from a tree with:
tree->Draw("Edep >> hEdep(100,0,2)");
Here, a histogram called hEdep with 100 bins between 0 and 2 will be filled from the Edep branch. You can later retrieve and manipulate it as a standard TH1 object:
TH1 *hEdep = (TH1*) gDirectory->Get("hEdep");
hEdep->SetXTitle("Deposited energy (MeV)");
hEdep->SetYTitle("Counts");
Important rule: Always confirm branch names and types with tree->Print() before using SetBranchAddress or Draw. A mismatch between branch type and C++ variable type can lead to incorrect results or crashes without obvious error messages.
By combining interactive inspection with simple C++ loops, you can efficiently explore and understand the ROOT TTrees produced by your Geant4 simulations and prepare for more advanced analysis tasks.
Views: 8
KAHIBARO