Helpers#

Error handling. Use the following to fail with an exception and trace:

import opengate as gate

gate.raise_except('There is bug')
gate.exception.fatal('This is a fatal error')
gate.exception.warning('This is a warning')

There are several levels: WARNING INFO DEBUG. The last one prints more information. Logging is handled with logger in helpers_log.py.

Engines#

As explained above, the Engine classes drive the actual Geant4 simulation. This section explains the engines in detail.

SimulationEngine#

The SimulationEngine is the main engine class. Upon instantiation (i.e., in the __init__() method), all sub-engines are created which drive the different parts if the Geant4 simulation. The sub-engines keep a reference to the SimulationEngine which created them.

The three main methods of the SimulationEngine are SimulationEngine.run_engine(), SimulationEngine.initialize() and SimulationEngine.start_and_stop(), where the latter two are called by the former. The method SimulationEngine.run_engine() essentially takes the role of the main.cc in a pure Geant4 simulation. It first initializes the simulation with SimulationEngine.initialize() and then starts event loop with SimulationEngine.start_and_stop() via the SourceEngine.

The SimulationEngine uses the G4RunManager via a pybind11 wrapping and many Geant4 objects are created by the G4RunManager. In short: The G4RunManager is informed about the geometry, physics, and sources via its SetUserInitialization() method. The Geant4 initialization procedure is then triggered by g4_RunManager.Initialize(), just as a regular Geant4 simulation would. For details, please consult the Geant4 user guide.

Note that there are subtleties concerning the way the G4RunManager works in singlethread and multithread mode which we do not cover in this guide.

Complementary to the g4_RunManager.Initialize(), there are expicit calls to initialize() methods of the sub-engines, some of them before g4_RunManager.Initialize() and some afterwards.

The SimulationEngine is implemented as what is known in python as context manager. This means, in can be created in a with-clause: with SimulationEngine(self) as se: … (see SimulationEngine._run_simulation_engine()). What it does is that the SimulationEngine object only exists as long as the commands inside the with-clause are executed. At the end of the with-clause, python calls the method SimulationEngine.__exit__() (like an exit hook) which triggers the method SimulationEngine.close(). The purpose of the close() method is to prepare all python objects that are part of the GATE simulation for the deletion of the G4RunManager. In particular, it makes sure that references to those Geant4 objects which the destructor of the G4RunManager will delete are set to None. Otherwise, segmentation faults will occur. We do not go into further detail here, but it is useful to understand this background of the close() mechanism triggered by the with-clause because you, as a developer, might need to implement a close() method in your class or extend the close() in an existing class which your are enhancing (see the section on how a class is set up in GATE).

It is worth noting that you have probably already come across context managers in python elsewhere. For example, when opening a file, you typically do with open(‘my_file.txt’, ‘w’) as f: …. When the with-clause ends, python automatically calls f.close(), i.e. closes the IO pipeline.

VolumeEngine#

To be completed.

PhysicsEngine#

To be completed.

ActorEngine#

To be completed.

SourceEngine#

To be completed.

ActionEngine#

To be completed.

OPENGATE Simulation#

To be completed.

OPENGATE elements: volumes, physic, sources, actors#

A simulation is composed of several elements: some volumes, some sources, some actors, and some physics properties. The parameters that can be defined by the user (the person that develops the simulation) are managed by a simple dict-like structure. No Geant4 objects are built until the initialization phase. This allows (relative) simplicity in the development.

UserInfo (before initialization)#

An ‘element’ can be a Volume, a Source, or an Actor. There are several element types that can be defined and used several times by the user. For example, a BoxVolume, with element_type = Volume and type_name = Box. For all elements, the user information (user_info) is a single structure that contains all parameters to build/manage the element (the size of a BoxVolume, the radius of a SphereVolume, the activity of a GenericSource, etc.). User info is stored in a dict-like structure. This is performed through a UserInfo class inheriting from Box.

One single function is used to define the default keys of a given user info: set_default_user_info. This function must be defined as a static method in the class that defines the element type (BoxVolume in the previous example).

Examples:

vol = sim.add_volume('Type', 'name')        # -> vol is UserInfo
sol = sim.new_solid('Type', 'name')         # -> sol is UserInfo
src = sim.add_source('Type', 'name')        # -> src is UserInfo
act = sim.add_actor('Type', 'name')         # -> act is UserInfo
phys = sim.get_physics_user_info()          # -> phys is UserInfo
filter = sim.add_filter('Type', 'name')     # -> filter is UserInfo

OPENGATE Geometry#

To be completed.

OPENGATE Physics#

To be completed.

OPENGATE Source#

Consider the test056 and the “TemplateSource” as a starting example to create a new type of source.

Main files: SourceManager, SourceBase, helper_sources, all XXXSource.py.

  • [py] SourceManager

    • Manages all sources (GateSourceManager) and all threads.

    • run_timing_intervals: array of start/end time for all runs

    • sources: dict of SourceBase

    • g4_sources: array of GateVSource. Needed to avoid pointer deletion on py side

    • g4_thread_source_managers: array of all source managers for all threads

    • g4_master_source_manager: master thread source manager

  • [cpp] GateSourceManager

    • Manages a list of sources.

    • fSources: list of all managed GateVSource sources

    • initialize: set the time intervals

    • start_main_thread: start the simulation, only for the main thread

    • GeneratePrimaries: will be called by the G4 engine.

A source type is split into two parts: py and cpp. The py part inherits from SourceBase and manages the user info. The cpp part inherits from GateVSource and shoots the particles.

  • [py] SourceBase

    • Base class for all types of source (py side)

    • Used to store the user info of the source

    • Manages the start and end time of the source

    • The create_g4_source function must be overloaded

  • [cpp] GateVSource

    • Base class for all types of source (cpp side)

    • GeneratePrimaries: is the main function that will be called by the source manager

    • PrepareNextRun and PrepareNextTime must be implemented. Will be called by the SourceManager to determine when this source shoots particles.

The SourceManager class manages 1) all sources of particles and 2) the time associated with all runs. The sources are SourceBase objects that manage 1) the user properties stored in user_info and 2) the corresponding cpp object inheriting from GateVSource. The latter are created in the function build() by the create_g4_source() function and stored in the self.g4_sources array to avoid py pointer automatic deletion.

The GateSourceManager inherits from G4 G4VUserPrimaryGeneratorAction. It manages the generation of events from all sources. The G4 engine calls the method GeneratePrimaries every time an event should be simulated. The current active source and time of the event is determined at this moment, the source manager chooses the next source that will shoot events according to the current simulation time. There is one GateSourceManager per thread.

All sources must inherit from SourceBase class. It must implement the function create_g4_source that will build the corresponding cpp source (that inherits from GateVSource). The goal of the py SourceBase is to manage the user options of the source and pass them to the cpp side.

OPENGATE Actors#

TODO –> inheritance to allow callback ; warning cost trampoline

Actors encapsulate several Geant4 concepts. They are used as a callback from the Geant4 engine to score information or modify the default behavior of particles during a simulation. An Actor combines the Geant4 SensitiveDetector and Actions callbacks within a single concept that can perform tasks each time a Run, Event, Track or Step starts or ends in a given volume. Actors are mainly used to record parameters or information of interest calculated during the simulation, but they can also be used to act on the current particle, for example to stop tracking it.

How to develop a new Actor?#

Warning: this is a preliminary (short) documentation at an early stage of the code (July 2022). It could be outdated.

We recommend looking at an example (e.g., GateDoseActor). The main concept is to write a cpp class that will act during the simulation and manage all user options and parameters from the python side. Geant4 messengers are no longer used. There is a mechanism, explained below, to convert python options to cpp options. Writing a new actor (a kind of scorer) involves 4 steps.