3 DDG4 Implementation

A basic design criteria of the a DDG4 simulation application was to process any user defined hook provided by Geant4 as a series of algorithmic procedures, which could be implemented either using inheritance or by a callback mechanism registering functions fulfilling a given signature. Such sequences are provided for all actions mentioned in the list in Section 2 as well as for the callbacks to sensitive detectors.

The callback mechanism was introduced to allow for weak coupling between the various actions. For example could an action performing monitoring using histograms at the event level initialize or reset its histograms at the start/end of each run. To do so, clearly a callback at the start/end of a run would be necessary.

In the following sections a flexible and extensible interface to hooks of Geant4 is discussed starting with the description of the basic components Geant4Kernel and Geant4Action followed by the implementation of the relevant specializations. The specializations exposed are sequences of such actions, which also call registered objects. In later section the configuration and the combination of these components forming a functional simulation application is presented.

3.1 The Application Core Object: Geant4Kernel

The kernel object is the central context of a DDG4 simulation application and gives all clients access to the user hooks (see Figure 2). All Geant4 callback structures are exposed so that clients can easily objects implementing the required interface or register callbacks with the correct signature. Each of these action sequences is connected to an instance of a Geant4 provided callback structure as it is shown in Figure 1.


PIC

Figure 2: The main application object gives access to all sequencing actions in a DDG4 4 application. Sequence actions are only container of user actions calling one user action after the other. Optionally single callbacks may be registered to a user action.


3.2 Action Sequences

As shown in

3.3 The Base Class of DDG4 Actions: Geant4Action

The class Geant4Action is a common component interface providing the basic interface to the framework to

  • configure the component using a property mechanism
  • provide an appropriate interface to Geant4 interactivity. The interactivity included a generic way to change and access properties from the Geant4 UI prompt as well as executing registered commands.
  • As shown in Figure 3, the base class also provides to its sub-class a reference to the Geant4Kernel objects through the Geant4Context.

The Geant4Action is a named entity and can be uniquely identified within a sequence attached to one Geant4 user callback.


PIC

Figure 3: The design of the common base class Geant4Action.


DDG4 knows two types of actions: global actions and anonymous actions. Global actions are accessible externally from the Geant4Kernel instance. Global actions are also re-usable and hence may be contribute to several action sequences (see the following chapters for details). Global actions are uniquely identified by their name. Anonymous actions are known only within one sequence and normally are not shared between sequences.

3.3.1 The Properties of Geant4Action Instances

Nearly any subclass of a Geant4Action needs a flexible configuration in order to be reused, modified etc. The implementation of the mechanism uses a very flexible value conversion mechanism using boost::spirit, which support also conversions between unrelated types provided a dictionary is present.

Properties are supposed to be member variables of a given action object. To publish a property it needs to be declared in the constructor as shown here:

  declareProperty("OutputLevel", m_outputLevel = INFO);  
  declareProperty("Control",     m_needsControl = false);

The internal setup of the Geant4Action objects then ensure that all declared properties will be set after the object construction to the values set in the setup file.

Note: Because the values can only be set after the object was constructed, the actual values may not be used in the constructor of any base or sub-class.

3.4 Geant4 Action Sequences

All Geant4 user hooks are realized as action sequences. As shown in Figure 2 these sequences are accessible to the user, who may attach specialized actions to the different action sequences. This allows a flexible handing of specialized user actions e.g. to dynamically add monitoring actions filling histograms or to implement alternative hit creation mechanism in a sensitive detector for detailed detector studies. Figure 4 shows the schematic call structure of an example Geant4TrackingActionSequence:


PIC

Figure 4: The design of the tracking action sequence. Specialized tracking action objects inherit from the Geant4TrackingAction object and must be attached to the sequence.


Geant4 calls the function from the virtual interface (G4UserTrackingAction), which is realised by the Geant4UserTrackingAction with the single purpose to propagate the call to the action sequence, which then calls all registered clients of type Geant4TrackingAction.

The main action sequences have a fixed name. These are

  • The RunAction attached to the G4UserRunAction, implemented by the Geant4RunActionSequence class and is called at the start and the end of every run (beamOn). Members of the Geant4RunActionSequence are of type Geant4RunAction and receive the callbacks by overloading the two routines: /// begin-of-run callback  
    virtual void begin(const G4Run* run);  
    /// End-of-run callback  
    virtual void end(const G4Run* run);

    or register a callback with the signature void (T::*)(const G4Run*) either to receive begin-of-run or end-or-calls using the methods: /// Register begin-of-run callback. Types Q and T must be polymorph!  
    template <typename Q, typename T> void callAtBegin(Q* p, void (T::*f)(const G4Run*));  
    /// Register end-of-run callback. Types Q and T must be polymorph!  
    template <typename Q, typename T> void callAtEnd(Q* p, void (T::*f)(const G4Run*));

    of the Geant4RunActionSequence from the Geant4Context object.

  • The EventAction attached to the G4UserEventAction, implemented by the EventActionSequence class and is called at the start and the end of every event. Members of the Geant4EventActionSequence are of type Geant4EventAction and receive the callbacks by overloading the two routines: /// Begin-of-event callback  
    virtual void begin(const G4Event* event);  
    /// End-of-event callback  
    virtual void end(const G4Event* event);

    or register a callback with the signature void (T::*)(const G4Event*) either to receive begin-of-run or end-or-calls using the methods: /// Register begin-of-event callback  
    template <typename Q, typename T> void callAtBegin(Q* p, void (T::*f)(const G4Event*));  
    /// Register end-of-event callback  
    template <typename Q, typename T> void callAtEnd(Q* p, void (T::*f)(const G4Event*));

    of the Geant4EventActionSequence from the Geant4Context object.

  • The GeneratorAction attached to the G4VUserPrimaryGeneratorAction, implemented by the Geant4GeneratorActionSequence class and is called at the start of every event and provided all initial tracks from the Monte-Carlo generator. Members of the Geant4GeneratorActionSequence are of type Geant4EventAction and receive the callbacks by overloading the member function: /// Callback to generate primary particles  
    virtual void operator()(G4Event* event);

    or register a callback with the signature void (T::*)(G4Event*) to receive calls using the method: /// Register primary particle generation callback.  
    template <typename Q, typename T> void call(Q* p, void (T::*f)(G4Event*));

    of the Geant4GeneratorActionSequence from the Geant4Context object.


PIC

Figure 5: The design of the tracking action sequence. Specialized tracking action objects inherit from the Geant4TrackingAction object and must be attached to the sequence.


  • The TrackingAction attached to the G4UserTrackingAction, implemented by the Geant4- TrackingActionSequence class and is called at the start and the end of tracking one single particle trace through the material of the detector. Members of the Geant4TrackingActionSequence are of type Geant4TrackingAction and receive the callbacks by overloading the member function: /// Pre-tracking action callback  
    virtual void begin(const G4Track* trk);  
    /// Post-tracking action callback  
    virtual void end(const G4Track* trk);

    or register a callback with the signature void (T::*)(const G4Step*, G4SteppingManager*) to receive calls using the method: /// Register Pre-track action callback  
    template <typename Q, typename T> void callAtBegin(Q* p, void (T::*f)(const G4Track*));  
    /// Register Post-track action callback  
    template <typename Q, typename T> void callAtEnd(Q* p, void (T::*f)(const G4Track*));

    Figure 5 show as an example the design (class-diagram) of the Geant4TrackingAction.

  • The SteppingAction attached to the G4UserSteppingAction, implemented by the Geant4- SteppingActionSequence class and is called for each step when tracking a particle. Members of the Geant4SteppingActionSequence are of type Geant4SteppingAction and receive the callbacks by overloading the member function: /// User stepping callback  
    virtual void operator()(const G4Step* step, G4SteppingManager* mgr);

    or register a callback with the signature void (T::*)(const G4Step*, G4SteppingManager*) to receive calls using the method: /// Register stepping action callback.  
    template <typename Q, typename T> void call(Q* p, void (T::*f)(const G4Step*,  
                                                                   G4SteppingManager*));

  • The StackingAction attached to the G4UserStackingAction, implemented by the Geant4-
    StackingActionSequence class. Members of the Geant4StackingActionSequence are of type
    Geant4StackingAction and receive the callbacks by overloading the member functions: /// New-stage callback  
    virtual void newStage();  
    /// Preparation callback  
    virtual void prepare();

    or register a callback with the signature void (T::*)() to receive calls using the method: /// Register begin-of-event callback. Types Q and T must be polymorph!  
    template <typename T> void callAtNewStage(T* p, void (T::*f)());  
    /// Register end-of-event callback. Types Q and T must be polymorph!  
    template <typename T> void callAtPrepare(T* p, void (T::*f)());

All sequence types support the method void adopt(T* member_reference) to add the members. Once adopted, the sequence takes ownership and manages the member. The design of all sequences is very similar.

3.5 Sensitive Detectors

Sensitive detectors are associated by the detector designers to all active materials, which would produce a signal which can be read out. In Geant4 this concept is realized by using a base class G4VSensitiveDetector. The mandate of a sensitive detector is the construction of hit objects using information from steps along a particle track. The G4VSensitiveDetector receives a callback at the begin and the end of the event processing and at each step inside the active material whenever an energy deposition occurred.


PIC

Figure 6: The sensitive detector design. The actual energy deposits are collected in user defined subclasses of the Geant4Sensitive. Here, as an example possible actions called TrackerHitCollector, TrackerDetailedHitCollector and TrackerHitMonitor are shown.


The sensitive actions do not necessarily deal only the collection of energy deposits, but could also be used to simply monitor the performance of the active element e.g. by producing histograms of the absolute value or the spacial distribution of the depositions.

Within DDG4 the concept of sensitive detectors is implemented as a configurable action sequence of type Geant4SensDetActionSequence calling members of the type Geant4Sensitive as shown in Figure 6. The actual processing part of such a sensitive action is only called if the and of a set of required filters of type Geant4Filter is positive (see also section 3.5.3). No filter is also positive. Possible filters are e.g. particle filters, which ignore the sensitive detector action if the particle is a geantino or if the energy deposit is below a given threshold.

Objects of type Geant4Sensitive receive the callbacks by overloading the member function:   /// Method invoked at the beginning of each event.  
  virtual void begin(G4HCofThisEvent* hce);  
  /// Method invoked at the end of each event.  
  virtual void end(G4HCofThisEvent* hce);  
  /// Method for generating hit(s) using the information of G4Step object.  
  virtual bool process(G4Step* step, G4TouchableHistory* history);  
  /// Method invoked if the event was aborted.  
  virtual void clear(G4HCofThisEvent* hce);

or register a callback with the signature void (T::*)(G4HCofThisEvent*) respectively void (T::*)(G4Step*, G4TouchableHistory*) to receive callbacks using the methods:   /// Register begin-of-event callback  
  template <typename T> void callAtBegin(T* p, void (T::*f)(G4HCofThisEvent*));  
  /// Register end-of-event callback  
  template <typename T> void callAtEnd(T* p, void (T::*f)(G4HCofThisEvent*));  
  /// Register process-hit callback  
  template <typename T> void callAtProcess(T* p, void (T::*f)(G4Step*, G4TouchableHistory*));  
  /// Register clear callback  
  template <typename T> void callAtClear(T* p, void (T::*f)(G4HCofThisEvent*));

Please refer to the Geant4 Applications manual from the Geant4 web page for further details about the concept of sensitive detectors.

3.5.1 Helpers of Sensitive Detectors: The Geant4VolumeManager

Sooner or later, when a hit is created in a sensitive placed volume, the hit must be associated with this volume. For this purpose DD4hep provides the concept of the VolumeManager, which identifies placed volumes uniquely by a 64-bit identifier, the CellID. This mechanism allows to quickly retrieve a given volume given the hit data containing the CellID. The CellID is a very compressed representation for any element in the hierarchy of placed volumes to the sensitive volume in question.

During the simulation the reverse mechanism must be applied: Geant4 provides the hierarchy of G4PhysicalVolumes to the hit location and the local coordinates of the hit within the sensitive volume. Hence to determine the volume identifier is essential to store hits so that they can be later accessed and processed efficiently. This mechanism is provided by the Geant4VolumeManager. Clients typically do not interact with this object, any access necessary is provided by the Geant4Sensitive action:

  /// Method for generating hit(s) using the information of G4Step object.  
  bool MySensitiveAction:process(G4Step* step,G4TouchableHistory* /*hist*/ ) {  
    ...  
    Hit* hit = new Hit();  
    // *** Retrieve the cellID  ***  
    hit->cellID = cellID(step);  
    ...  
  }

The call is realized using a member function provided by the Geant4Sensitive action:

  /// Returns the cellID of the sensitive volume corresponding to the step  
  /** The CellID is the VolumeID + the local coordinates of the sensitive area.  
   *  Calculated by combining the VolIDS of the complete geometry path (Geant4TouchableHistory)  
   *  from the current sensitive volume to the world volume  
   */  
  long long int cellID(G4Step* step);

Note:
The Geant4VolumeManager functionality is not for free! It requires that

– match Geant4 volume with TGeo volume

3.5.2 DDG4 Intrinsic Sensitive Detectors

Currently there are two generic sensitive detectors implemented in DDG4:

  • The Geant4TrackerAction, which may be used to handle tracking devices. This sensitive detector produces one hit for every energy deposition of Geant4 i.e. for every callback to

      /// Method for generating hit(s) using the information of G4Step object.  
      virtual bool process(G4Step* step, G4TouchableHistory* history);

    See the implementation file DDG4/plugins/Geant4SDAction.cpp for details. The produced hits are of type Geant4Tracker::Hit .

  • The Geant4CalorimeterAction, which may be used to handle generic calorimeter like devices. This sensitive detector produces at most one hit for every cell in the calorimeter. If several tracks contribute to the energy deposit of this cell, the contributions are added up. See the implementation file DDG4/plugins/Geant4SDAction.cpp for details. The produced hits are of type Geant4Calorimeter::Hit . propagate the MC truth information with respect to each track kept in the particle record.

Both sensitive detectors use the Geant4VolumeManager discussed in section 3.5.1 to identify the sensitive elements.

PLEASE NOTE:
The above palette of generic sensitive detectors only contains two very often used implementations. We hope, that this palette over time grows from external contributions of other generic sensitive detectors. We would be happy to extend this palette with other generic implementations. One example would be the handling of the simulation response for optical detectors like RICH-Cerenkov detectors.

3.5.3 Sensitive Detector Filters

The concept of filters allows to build more flexible sensitive detectors by restricting the hit processing of a given instance of a sensitive action.

  • Examples would be to demand a given particle type before a sensitive action is invoked: a sensitive action dealing with optical photons (RICH detectors, etc), would e.g. not be interested in energy depositions of other particles. A filter object restricting the particle type to optical photons would be appropriate.
  • Another example would be to implement a special action instance, which would be only called if the filter requires a minimum energy deposit.

There are plenty of possible applications, hence we would like to introduce this feature here.

Filters are called by Geant4 before the hit processing in the sensitive detectors start. The global filters may be shared between many sensitive detectors. Alternatively filters may be directly attached to the sensitive detector in question. Attributes are directly passed as properties to the filter action.

Technically do Geant4Filter objects inherit from the base class Geant4Filter (see Figure 7. Any filter inherits from the common base class Geant4Filter, then several specializations may be configured like filters to select/reject particles, to specify the minimal energy deposit to be processed etc. A sensitive detector is called if the filter callback with the signature returns a true result:

  /// Filter action. Return true if hits should be processed  
  virtual bool operator()(const G4Step* step) const;


PIC

Figure 7: The sensitive detector filters design. The shown class diagram is actually implemented.


3.6 The Geant4 Physics List

Geant4 provides the base class G4VUserPhysicsList, which allows users to implement customized physics according to the studies to be made. Any user defined physics list must provide this interface. DDG4 provides such an interface through the ROOT plugin mechanism using the class G4VModularPhysicsList. The flexibility of DDG4 allows for several possibilities to setup the Geant4 physics list. Instead of explicitly coding the physics list, DDG4 foresees the usage of the plugin mechanism to instantiate the necessary calls to Geant4 in a sequence of actions:

  • The physics list is realized as a sequence of actions of type Geant4PhysicsListActionSequence . Members of the Geant4PhysicsListActionSequence are of type Geant4PhysicsList and receive the callbacks by overloading the member functions:

      /// Callback to construct the physics constructors  
      virtual void constructProcess(Geant4UserPhysics* interface);  
      /// constructParticle callback  
      virtual void constructParticles(Geant4UserPhysics* particle);  
      /// constructPhysics callback  
      virtual void constructPhysics(Geant4UserPhysics* physics);

    or register a callback with the signature void (T::*)(Geant4UserPhysics*) to receive calls using the method:

      /// Register process construction callback t  
      template <typename Q, typename T> void constructProcess(Q* p, void (T::*f)(Geant4UserPhysics*));  
      /// Register particle construction callback  
      template <typename Q, typename T> void constructParticle(Q* p, void (T::*f)(Geant4UserPhysics*));

    The argument of type Geant4UserPhysics provides a basic interface to the original G4VModular- PhysicsList, which allows to register physics constructors etc.

  • In most of the cases the above approach is an overkill and often even too flexible. Hence, alternatively, the physics list may consist of a single entry of type Geant4PhysicsList .

The basic implementation of the Geant4PhysicsList supports the usage of various

  • particle constructors , such as single particle constructors like G4Gamma or G4Proton, or whole particle groups like G4BosonConstructor or G4IonConstrutor,
  • physics process constructors , such as e.g. G4GammaConversion, G4PhotoElectricEffect or
    G4ComptonScattering,
  • physics constructors combining particles and the corresponding interactions, such as
    e.g. G4OpticalPhysics, HadronPhysicsLHEP or G4HadronElasticPhysics and
  • predefined Geant4 physics lists , such as FTFP_BERT, CHIPS or QGSP_INCLXX. This option is triggered by the content of the string property ”extends” of the Geant4Kernel::physicsList() action.

These constructors are internally connected to the above callbacks to register themselves. The constructors are instantiated using the ROOT plugin mechanism.

The description of the above interface is only for completeness. The basic idea is, that the physics list with its particle and physics constructors is configured entirely data driven using the setup mechanism described in the following chapter. However, DDG4 is not limited to the data driven approach. Specialized physics lists may be supplied, but there should be no need. New physics lists could always be composed by actually providing new physics constructors and actually publishing these using the factory methods:

// Framework include files  
#include "DDG4/Factories.h"  
 
#include "My_Very_Own_Physics_Constructor.h"  
DECLARE_GEANT4_PHYSICS(My_Very_Own_Physics_Constructor)

where My_Very_Own_Physics_Constructor represents a sub-class of G4VPhysicsConstructor.

3.7 The Support of the Geant4 UI: Geant4UIMessenger

The support of interactivity in Geant4 is mandatory to debug detector setups in small steps. The Geant4 toolkit did provide for this reason a machinery of UI commands.


PIC

Figure 8: The design of the Geant4UIMessenger class responsible for the interaction between the user and the components of DDG4 and Geant4.


The UI control is enabled, as soon as the property ”Control” (boolean) is set to true. Be default all properties of the action are exported. Similar to the callback mechanism described above it is also feasible to register any object callback invoking a method of a Geant4Action-subclass.

The following (shortened) screen dump illustrates the usage of the generic interface any Geant4Action offers: Idle> ls  
Command directory path : /  
 Sub-directories :  
   /control/   UI control commands.  
   /units/   Available units.  
   /process/   Process Table control commands.  
   /ddg4/   Control for all named Geant4 actions  
   ...  
Idle> cd /ddg4  
Idle> ls  
...  
Control for all named Geant4 actions  
 
 Sub-directories :  
   /ddg4/RunInit/   Control hierarchy for Geant4 action:RunInit  
   /ddg4/RunAction/   Control hierarchy for Geant4 action:RunAction  
   /ddg4/EventAction/   Control hierarchy for Geant4 action:EventAction  
   /ddg4/GeneratorAction/   Control hierarchy for Geant4 action:GeneratorAction  
   /ddg4/LCIO1/   Control hierarchy for Geant4 action:LCIO1  
   /ddg4/Smear1/   Control hierarchy for Geant4 action:Smear1  
   /ddg4/PrimaryHandler/   Control hierarchy for Geant4 action:PrimaryHandler  
   /ddg4/TrackingAction/   Control hierarchy for Geant4 action:TrackingAction  
   /ddg4/SteppingAction/   Control hierarchy for Geant4 action:SteppingAction  
   /ddg4/ParticleHandler/   Control hierarchy for Geant4 action:ParticleHandler  
   /ddg4/UserParticleHandler/   Control hierarchy for Geant4 action:UserParticleHandler  
   ...  
Idle> ls Smear1  
Command directory path : /ddg4/Smear1/  
 ...  
 Commands :  
   show * Show all properties of Geant4 component:Smear1  
   Control * Property item of type bool  
   Mask * Property item of type int  
   Name * Property item of type std::string  
   Offset * Property item of type ROOT::Math::LorentzVector<ROOT::Math::PxPyPzE4D<double> >  
   OutputLevel * Property item of type int  
   Sigma * Property item of type ROOT::Math::LorentzVector<ROOT::Math::PxPyPzE4D<double> >  
   name * Property item of type std::string  
Idle> Smear1/show  
PropertyManager: Property Control = True  
PropertyManager: Property Mask = 1  
PropertyManager: Property Name = ’Smear1’  
PropertyManager: Property Offset = ( -20 , -10 , -10 , 0 )  
PropertyManager: Property OutputLevel = 4  
PropertyManager: Property Sigma = ( 12 , 8 , 8 , 0 )  
PropertyManager: Property name = ’Smear1’  
 
Idle> Smear1/Offset (200*mm, -3*mm, 15*mm, 10*ns)  
Geant4UIMessenger: +++ Smear1> Setting property value Offset = (200*mm, -3*mm, 15*mm, 10*ns)  
                               native:( 200 , -3 , 15 , 10 ).  
Idle> Smear1/show  
...  
PropertyManager: Property Offset = ( 200 , -3 , 15 , 10 )