KAHIBARO
Discord Login Register

19.1 ROOT Output from Geant4

Generating ROOT files

In Geant4, ROOT output is produced through the analysis system, typically via G4AnalysisManager. From the ROOT point of view, your Geant4 application behaves like a small ROOT producer: you define histograms and ntuples, you fill them during the run, then you write them to a ROOT file at the end.

The essential steps to generate ROOT files are always the same. You create and configure the analysis manager once, usually in your RunAction constructor. You open the output file at the beginning of the run, normally in BeginOfRunAction. You fill histograms and ntuples inside event level or step level user actions, for example in EventAction or SteppingAction. Finally, you write and close the file in EndOfRunAction.

The choice of backend is controlled by CMake when you build Geant4. If Geant4 is configured with ROOT support and you select the ROOT analysis backend, the analysis manager will produce .root files instead of CSV or other formats. For absolute beginners, the main visible effect is that the file extension you pass to OpenFile() is typically just a name like "output" and the manager will actually create output.root.

It is important to understand that the analysis manager is a singleton. You usually access it through
G4AnalysisManager::Instance() from any user action class. In a multithreaded build there is one instance per thread, but the interface you call is the same.

A minimal pattern looks like this in pseudocode form, without going into implementation details that belong in the dedicated analysis chapter:

  1. In RunAction constructor, obtain auto analysisManager = G4AnalysisManager::Instance(); and set general options such as verbosity or activation of histograms.
  2. In BeginOfRunAction, call analysisManager->OpenFile("myOutput"); to prepare the ROOT file.
  3. In user actions for events, steps, or tracks, call analysisManager->FillH1(...) or analysisManager->FillNtuple...(...) to record data.
  4. In EndOfRunAction, call analysisManager->Write(); followed by analysisManager->CloseFile(); to actually create the ROOT file and flush all data.

To obtain a valid ROOT file you must call Write() and CloseFile() on G4AnalysisManager at the end of the run. Forgetting either call often results in an empty or corrupted ROOT file.

When multithreading is enabled, each worker thread creates its own internal histograms and ntuples and fills them independently. At the end of the run, Geant4 merges all thread local results into a single ROOT file. The merging is handled by the analysis manager backend, so beginners usually do not need to write any extra code for this, but it is important to remember that the data you see in the final file already includes all threads.

The ROOT output file typically contains a directory for histograms and one or more TTrees for ntuples. The exact structure depends on how you configure the analysis manager and how many ntuples you create. You can inspect this structure later in ROOT using commands such as .ls inside the ROOT interactive session.

Histograms

Histograms in Geant4 are 1D, 2D, or 3D containers that accumulate distributions of scalar or vector quantities during the simulation. In the ROOT output they become TH1, TH2, or TH3 objects stored inside the ROOT file and can be plotted or analyzed with ROOT.

From the Geant4 user point of view, the typical workflow is to define histograms before the run starts and to fill them every time you want to record a value. For example, to record an energy spectrum, you define a 1D histogram with an energy range and a number of bins, then fill it with deposited energy values event by event.

A simplified mental model for 1D histograms is:

ConceptGeant4 parameterROOT effect
Number of binsnbinsNumber of bins in TH1
Lower edgexMinMinimum histogram range
Upper edgexMaxMaximum histogram range
Bin contentFillH1(id, value, weight)Increments bin content in TH1
Histogram IDInteger index starting at zero or oneUsed to identify which histogram

You normally create histograms once, for example in the RunAction constructor, after obtaining the analysis manager. You specify a unique name, an internal title for later plotting, and parameters such as number of bins and axis limits. For ROOT output, these names become the names of the corresponding ROOT objects. It is good practice to choose short but descriptive names, for example "EdepCrystal" for an energy deposition spectrum in a crystal.

Histograms are filled at the level where you have access to the quantity you want to record. For an energy spectrum per event, you might accumulate total energy in EventAction and then, at the end of the event, fill the histogram with the event total. For dose as a function of depth, you might fill a histogram in SteppingAction with the step position and deposited energy.

A key point is that histogram filling is independent of the output format. As long as your Geant4 is built with ROOT analysis support and you open a ROOT file through the analysis manager, all histograms you define and fill will appear in that file as ROOT histograms.

Histogram binning strongly affects the usefulness of your ROOT output. If the bin range is too narrow, some data will fall outside the histogram. If the bin size is too coarse, narrow features such as peaks or sharp edges will be smeared out and hard to see in ROOT.

Once the run is finished and the ROOT file is written, you can open it with ROOT and access the histograms by name. ROOT commands such as h->Draw() will display the distributions that were filled in Geant4. From the simulation side you rarely need to handle any ROOT types directly. The analysis manager hides the details and takes care of creating appropriate TH1 or TH2 objects in the file.

Ntuples

Ntuples provide event by event or hit by hit storage of structured data. In the ROOT file an ntuple becomes a TTree that contains a set of branches, each branch corresponding to a column you define in Geant4. This is the main mechanism to perform detailed offline analysis of Geant4 simulations with ROOT.

Conceptually, an ntuple is like a table that grows during the run:

Row indexColumn 1 (e.g. energy)Column 2 (e.g. x)Column 3 (e.g. y)Column 4 (e.g. particle ID)
0$E_0$$x_0$$y_0$$id_0$
1$E_1$$x_1$$y_1$$id_1$
...............

Geant4 provides a simple interface to create such a structure. Early in the application, usually in RunAction, you define the ntuple itself with a name and title and then create columns for each quantity you want to store, such as energy, position coordinates, time, or detector ID. When you finish defining all columns, you finalize the ntuple structure. At this stage no data are written yet.

During the run, each time you decide to record an entry, you set the values of the ntuple columns and then tell the analysis manager to add a new row. For example, for each hit in a sensitive detector you might store the deposited energy, the global time, and the detector element index. In code, you would call the appropriate FillNtuple...Column methods for each column, then a single AddNtupleRow() call to append a new row to the TTree inside the ROOT file.

The mapping between Geant4 ntuple concepts and ROOT should be kept in mind:

Geant4 ntuple conceptROOT object concept
NtupleTTree
Column nameBranch name
Column type (double, int, etc)Branch data type
RowTTree entry
AddNtupleRow()TTree::Fill()

This mapping is handled automatically by the analysis manager, so your Geant4 code does not interact with TTree directly. However, understanding the relation is helpful when you open the ROOT file later. In ROOT you will see a TTree with the ntuple name, and branches with the same column names that you selected in your Geant4 code.

Ntuples are essential when you need to apply complex selection criteria or to combine many variables during offline analysis. For instance, you can record one row per event that contains total energy deposit, number of hits, primary particle energy, and event ID. Later in ROOT, you can apply cuts on any column, create histograms of any combination of quantities, or even compute new variables from the stored ones.

Ntuples can become very large. Recording one row per step or per optical photon can quickly produce ROOT files of many gigabytes. Always think carefully about which quantities you truly need for analysis and which can be reduced to histograms during the run.

When the run ends and Write() and CloseFile() are called, all accumulated ntuple rows are written into the ROOT file. In a multithreaded run, thread local ntuples are merged into a single TTree containing rows from all threads. From the ROOT side you see one combined TTree and can analyze it without worrying about threads.

For beginners, a good starting pattern is to define a single ntuple that stores one row per physics event. Once you are comfortable opening the resulting ROOT file and drawing distributions from that TTree, you can extend your Geant4 code to record hit level or step level information in additional ntuples if needed.

Views: 7

Comments

Please login to add a comment.

Don't have an account? Register now!