KAHIBARO
Discord Login Register

4.4 User Initialization Classes

Detector construction

In a minimal Geant4 application you must tell the toolkit what the world looks like and where your detector volumes are. This is the job of the detector construction class, which you implement by deriving from G4VUserDetectorConstruction.

The run manager will ask your class to build the geometry once at initialization. You provide this by overriding the Construct() method. Inside Construct() you usually do three things in order: define materials, build the world volume, and build and place all other volumes inside that world.

A very typical pattern is to implement a custom class such as DetectorConstruction that inherits from G4VUserDetectorConstruction and overrides

cpp
G4VPhysicalVolume* DetectorConstruction::Construct()
{
  // 1. Define materials (possibly using the NIST manager)
  // 2. Construct the world solid and logical volume
  // 3. Place the world physical volume and return it
  return worldPhys;
}

The returned G4VPhysicalVolume* must be the top level world volume. All other detector components are placed (directly or indirectly) as daughters of this world. You do not call Construct() yourself. Instead, in your main() you create an instance of your detector class and pass it to the run manager with

cpp
runManager->SetUserInitialization(new DetectorConstruction());

From that point, Geant4 owns the detector class object and will call Construct() at the appropriate time, usually during /run/initialize.

You can extend this basic structure to add more methods inside your detector construction class, for example separate helper functions to build subdetectors or to configure geometry based on user parameters. As long as the complete volume hierarchy is created inside Construct() and a valid world physical volume is returned, the run manager will be able to use your geometry in all later runs.

Geant4 also allows optional methods such as ConstructSDandField() in the same class. These are used to assign sensitive detectors and fields to your logical volumes, but the essential role of detector construction in a beginner application is to define the geometry and connect it to the run manager.

Physics list

The physics list describes which particles exist in your simulation and which physical processes act on them. Without a physics list, particles would be transported without interactions, or not transported at all. To configure physics you provide a class derived from G4VUserPhysicsList or, more commonly for beginners, you use an existing reference physics list through a helper base class such as G4VModularPhysicsList.

In your own application you rarely implement all physical processes from scratch. Instead, you typically select a predefined physics configuration, for example FTFP_BERT, that already contains appropriate electromagnetic and hadronic models. In C++ this usually looks like

cpp
#include "FTFP_BERT.hh"
int main()
{
  auto runManager = new G4RunManager;
  runManager->SetUserInitialization(new DetectorConstruction());
  auto physicsList = new FTFP_BERT;
  runManager->SetUserInitialization(physicsList);
  // ...
}

The second call to SetUserInitialization registers the physics list with the run manager. When you later call /run/initialize (or when the run manager initializes internally) it will build all particles and processes from that physics list and prepare the simulation.

If you want more control, you can implement a custom physics list class that derives from G4VModularPhysicsList and then register one or more physics constructors inside its constructor. For a beginner course, the important point is that the physics list is a separate user initialization object, independent of the geometry. The run manager only accepts one active physics list at a time.

In every Geant4 application you must provide exactly one physics list object to the run manager with SetUserInitialization, or the simulation will not have valid particle definitions and processes.

Because the physics list is provided once at initialization, changing to another physics configuration normally requires recompiling if you select it in C++. Later chapters introduce ways to choose between several physics lists or to configure them via macros, but at this stage you should understand the basic pattern: create or choose a physics list class, then give it to the run manager as a user initialization component.

Action initialization

While the detector construction and physics list describe the static aspects of the simulation, user actions describe what happens during a run, event, track, or step. In earlier versions of Geant4 you would register each user action class individually on the run manager. In multithreaded Geant4, and in modern examples, all user actions are collected and created through a single class derived from G4VUserActionInitialization.

Your action initialization class has two main responsibilities. First, it must define which action classes will be used in the simulation. Second, in multithreaded mode, it must ensure that appropriate action objects are created for the master thread and for each worker thread.

A typical implementation derives from G4VUserActionInitialization and overrides at least Build(). Inside Build() you create instances of your user action classes, for example PrimaryGeneratorAction, RunAction, EventAction, SteppingAction, and register them using the provided setter methods:

cpp
void ActionInitialization::Build() const
{
  auto primary = new PrimaryGeneratorAction();
  SetUserAction(primary);
  auto runAction = new RunAction();
  SetUserAction(runAction);
  auto eventAction = new EventAction();
  SetUserAction(eventAction);
  auto steppingAction = new SteppingAction(eventAction);
  SetUserAction(steppingAction);
}

You then pass an instance of ActionInitialization to the run manager as the third kind of user initialization object:

cpp
runManager->SetUserInitialization(new ActionInitialization());

In a multithreaded build, Geant4 will call Build() separately for each worker thread. This means every worker has its own instances of the action classes, which keeps per-event and per-track data thread local. If you need a special action only on the master thread, for example a RunAction that manages merging of analysis output, you can also override BuildForMaster() in your action initialization class and create the master-specific actions there.

From the perspective of a beginner, the key idea is that user actions are not created in main() directly. Instead, they are all encapsulated in one place, the action initialization class, and the run manager is told only about that one object. Geant4 then takes care of calling the appropriate user action methods at the right times during the simulation life cycle.

Views: 12

Comments

Please login to add a comment.

Don't have an account? Register now!