Ntuples
Table of Contents
Creating ntuples
In the Geant4 analysis system, an ntuple is a table of numbers that you can fill event by event or step by step and then write to a file for later analysis. Each ntuple has rows and columns. A row usually corresponds to a logical unit such as one event, one hit, or one track, and each column stores a specific quantity like energy, position, or time.
Ntuples are managed through G4AnalysisManager. You usually create them in your analysis-related user class, most often in RunAction::BeginOfRunAction, after you have created the analysis manager and opened the output file. A minimal sequence looks like this in C++:
auto analysisManager = G4AnalysisManager::Instance();
analysisManager->OpenFile("output");
// create an ntuple and define its columns
analysisManager->CreateNtuple("Events", "Event level data");
// ... create columns here ...
analysisManager->FinishNtuple();You can create several ntuples in the same application, each with its own ID. Geant4 assigns ntuple IDs starting from 0 in the order you create them. Later, when you fill data, you refer to columns by their column IDs, which are also assigned in creation order starting from 0.
After you have created ntuples and their columns, you fill them whenever you have data available, for instance in EventAction::EndOfEventAction or SteppingAction::UserSteppingAction. A typical use pattern is:
analysisManager->FillNtupleDColumn(0, energy); // column 0
analysisManager->FillNtupleDColumn(1, time); // column 1
analysisManager->FillNtupleIColumn(2, eventId); // column 2
analysisManager->AddNtupleRow(); // finish this row
The call to AddNtupleRow is essential, because it actually stores the current set of column values as one row in the ntuple. Without it, the filled values are not written.
At the end of the run, you must write and close the analysis file, usually in RunAction::EndOfRunAction:
analysisManager->Write();
analysisManager->CloseFile();The file format, for example ROOT or CSV, is chosen when you create the analysis manager instance and configure it. Once the file is written, you can open it in an external tool and treat each ntuple as a standard table or tree of data.
Always call FinishNtuple() after defining all columns of one ntuple, and always call AddNtupleRow() after filling the columns for a row. Without these calls, the ntuple structure or data will not be stored correctly.
Creating columns
Each ntuple column has a name and a data type. Geant4 provides different creation functions for integer, double, and string columns. The most common are:
G4int CreateNtupleIColumn(const G4String& name);
G4int CreateNtupleDColumn(const G4String& name);
G4int CreateNtupleSColumn(const G4String& name);
You call these between CreateNtuple and FinishNtuple. A complete example for an ntuple that stores event ID, total deposited energy, and interaction time might look like this:
auto analysisManager = G4AnalysisManager::Instance();
analysisManager->OpenFile("output");
// Define an ntuple for event-level information
analysisManager->CreateNtuple("EventNtuple", "Event data");
analysisManager->CreateNtupleIColumn("eventID"); // column 0
analysisManager->CreateNtupleDColumn("Edep"); // column 1
analysisManager->CreateNtupleDColumn("time"); // column 2
analysisManager->FinishNtuple();The functions return the column ID, so you can store it and use it later if you prefer not to rely on the creation order:
auto colEventID = analysisManager->CreateNtupleIColumn("eventID");
auto colEdep = analysisManager->CreateNtupleDColumn("Edep");
auto colTime = analysisManager->CreateNtupleDColumn("time");Later, when you fill the ntuple, you can either use the numeric column indices directly or use the stored IDs:
analysisManager->FillNtupleIColumn(colEventID, eventID);
analysisManager->FillNtupleDColumn(colEdep, edep);
analysisManager->FillNtupleDColumn(colTime, time);
analysisManager->AddNtupleRow();The following table summarizes the correspondence between column type, creation function, and fill function:
| Data type | Column creation function | Fill function |
|---|---|---|
| Integer | CreateNtupleIColumn("name") | FillNtupleIColumn(id, value) |
| Double | CreateNtupleDColumn("name") | FillNtupleDColumn(id, value) |
| String | CreateNtupleSColumn("name") | FillNtupleSColumn(id, value) |
If you define more than one ntuple, column numbering restarts from 0 for each ntuple. The current ntuple is the last one for which you called CreateNtuple. You can also use overloads that specify the ntuple ID explicitly if you want to create or fill columns in a different order, but for beginner applications it is usually enough to work with a single ntuple and the default IDs.
When planning columns, it is helpful to think about the later analysis. Quantities that will be plotted or used in cuts, such as deposited energy, position coordinates, time of flight, or particle type, are good candidates for separate columns. You can keep derived quantities for analysis tools rather than putting every possible combination into the ntuple.
The sequence for one ntuple must always be:
CreateNtuple("name", "title")- One or more
CreateNtuple*Column("colName")calls FinishNtuple()
Do not attempt to add new columns after callingFinishNtuple(), and always match the column type with the correctFillNtuple*Columnfunction.
Views: 11
KAHIBARO