36.10. Recommended Geant4 Project Structure
Table of Contents
Overview
A clear and consistent project structure makes Geant4 applications easier to write, debug, extend, and share. This appendix proposes a practical directory and file layout that you can adapt to most small and medium simulations. The focus is on separating responsibilities, keeping build files clean, and preparing your project for future growth without unnecessary complexity.
A well organized project is not optional. It is essential for:
- Avoiding subtle bugs when you change geometry, physics, or analysis.
- Reusing components across different simulations.
- Allowing others (and your future self) to understand and extend your code.
Basic Directory Layout
A typical Geant4 project can be organized into a small number of top-level directories. A good starting point is:
| Directory / File | Purpose |
|---|---|
CMakeLists.txt | Top-level build configuration. |
src/ | C++ source (.cc) files for your application. |
include/ | C++ header (.hh) files with class declarations. |
macro/ | Geant4 macro (.mac) files. |
data/ | Extra input data specific to your project. |
vis/ | Visualization macro files and styles. |
build/ | Out-of-source build directory (created by you). |
output/ | Simulation outputs (ROOT, CSV, logs, plots). |
docs/ | Notes, diagrams, and project documentation. |
scripts/ | Helper scripts for running and postprocessing. |
Only a few of these are strictly required for a minimal project. However, adopting this structure from the start avoids mixing very different kinds of files and keeps build products out of your source tree.
The build directory is usually not committed to version control. You create it manually and run CMake inside it. This keeps compiler outputs, temporary files, and generated makefiles separate from your source code.
Organizing Source and Header Files
The heart of your project lives in src/ and include/. Every user-defined class should have a header file in include/ and a matching source file in src/ with the same base name.
A simple, yet scalable convention is:
| Component | Header (in include/) | Source (in src/) |
|---|---|---|
| Main program | (none, typically) | main.cc |
| Detector construction | DetectorConstruction.hh | DetectorConstruction.cc |
| Physics list (if user defined) | PhysicsList.hh | PhysicsList.cc |
| Primary generator | PrimaryGeneratorAction.hh | PrimaryGeneratorAction.cc |
| Run action | RunAction.hh | RunAction.cc |
| Event action | EventAction.hh | EventAction.cc |
| Stepping action | SteppingAction.hh | SteppingAction.cc |
| Tracking action | TrackingAction.hh | TrackingAction.cc |
| Stacking action | StackingAction.hh | StackingAction.cc |
| Sensitive detector(s) | MyDetectorSD.hh | MyDetectorSD.cc |
| Hit class(es) | MyHit.hh | MyHit.cc |
| Analysis manager wrapper (optional) | MyAnalysis.hh | MyAnalysis.cc |
| Configuration / parameter class | Config.hh | Config.cc |
| Utility functions | Utils.hh | Utils.cc |
Within include/, you can group related headers into subdirectories once the project grows, for example:
include/geometry/DetectorConstruction.hhinclude/physics/PhysicsList.hhinclude/actions/RunAction.hh
If you choose this, mirror the same structure in src/ so that file locations remain predictable.
Always follow this rule:
One class = one header file and one source file (with the same base name), unless there is a very strong reason to group them.
This keeps compilation dependencies clear and reduces build times.
Mapping Geant4 Components to Files
Geant4 encourages a specific set of user classes. A clean structure reflects this directly in the file names and locations.
The main entry point usually lives in src/main.cc. It should be short and limited to:
- Creating the
G4RunManagerorG4MTRunManager. - Registering your user initialization classes (detector, physics, actions).
- Starting interactive or batch mode.
Your user initialization and actions are then separated:
| Responsibility | Recommended class name | File name |
|---|---|---|
| Detector geometry | DetectorConstruction | DetectorConstruction.cc / .hh |
| Physics configuration | PhysicsList or reference list | PhysicsList.cc / .hh (if custom) |
| Action initialization | ActionInitialization | ActionInitialization.cc / .hh |
| Primary particle source | PrimaryGeneratorAction | PrimaryGeneratorAction.cc / .hh |
| Run-level bookkeeping | RunAction | RunAction.cc / .hh |
| Event-level bookkeeping | EventAction | EventAction.cc / .hh |
| Step-level operations | SteppingAction | SteppingAction.cc / .hh |
| Optional tracking control | TrackingAction | TrackingAction.cc / .hh |
| Optional secondary management | StackingAction | StackingAction.cc / .hh |
| Sensitive detector for a subdetector | MyDetectorSD | MyDetectorSD.cc / .hh |
| Hit information for that detector | MyHit | MyHit.cc / .hh |
You can have multiple sensitive detectors and hit classes if your geometry has several subdetector types. Use descriptive names such as CalorimeterSD, TrackerSD, CalorimeterHit, and TrackerHit.
Macros, Visualization, and Configuration Files
It is tempting to scatter macro files and configuration text files inside the source directory, but this quickly becomes confusing. Instead, dedicate a few clear directories.
The macro/ directory contains run control macros. Typical files include:
init.macfor initialization commands such as/run/initializeand physics or geometry checks.run1.mac,run2.macfor different simulation scenarios.test_geometry.macfor overlap checks and visualization commands.
The vis/ directory can store visualization specific macros so that you do not mix them with physics and run settings:
vis.macfor opening a viewer and drawing volumes.vis_openGL.mac,vis_Qt.macfor viewer specific macros if needed.- Style macros for colors, line widths, and views.
The data/ directory supports any extra information that your code reads at runtime, such as material property tables, spectrum files, or parameter lists. Keep such files separate from Geant4 data libraries, which are handled globally by your installation.
A small but useful pattern is to have a single macro that includes others, for example in macro/master.mac:
/control/execute init.mac
/control/execute vis/vis.mac
/control/execute run1.macIn this way, you can tweak visual settings or run conditions without touching C++ code.
Never hard-code absolute file paths like C:/User/... or /home/you/... inside macros or C++ code.
Use relative paths from the project root, for example macro/run1.mac or data/materials.txt.
Output and Analysis Files
Keeping output separate from input and source code is critical for reproducibility and cleanliness. A simple approach is:
- Write simulation results into
output/. - Use subdirectories inside
output/if you have several categories of results, for exampleoutput/root/,output/csv/,output/logs/.
You can enforce this in your application by setting output file names clearly in one place, for example in a central analysis or configuration class. Use paths such as output/spectrum.root or output/dose_depth.csv.
If you generate many files, consider using date or run identifiers in names, but avoid encoding too much information in file names. Keep the mapping between macro parameters and output files documented in docs/ or in a simple README.md.
Scaling Up: Subdirectories for Geometry, Physics, and Actions
As your simulation becomes more complex, placing all .cc files in a flat src/ directory may become hard to navigate. A gentle refactor is to introduce subdirectories while preserving the basic idea of one class per file.
A good pattern is:
| Category | Example directory | Example contents |
|---|---|---|
| Geometry | src/geometry/ | DetectorConstruction.cc, Calorimeter.cc |
| Physics | src/physics/ | PhysicsList.cc, EMPhysics.cc |
| Actions | src/actions/ | RunAction.cc, EventAction.cc |
| Detectors | src/detectors/ | CalorimeterSD.cc, TrackerSD.cc |
| Hits | src/hits/ | CalorimeterHit.cc, TrackerHit.cc |
| Analysis | src/analysis/ | MyAnalysis.cc |
| Utils | src/utils/ | Config.cc, Utils.cc |
Mirror these directories in include/:
include/geometry/DetectorConstruction.hhinclude/actions/RunAction.hh- and so on.
This makes it obvious where to look for each part of the code and avoids huge source files that contain unrelated functionality.
CMake Integration and Targets
Your CMakeLists.txt should reflect the chosen structure but not hard-code every file name. You can either list each source file explicitly or use CMake commands to gather files from subdirectories.
A robust pattern is to define a single executable target for the application and link it against Geant4. For example, in the top-level CMakeLists.txt:
- Find Geant4 using its configuration.
- Collect your project sources.
- Define an executable and link it to Geant4 libraries.
- Optionally, define an installation or run script.
Try to keep build logic separate from simulation logic. Avoid including CMake-specific definitions or macros inside your C++ code.
Keep the build system simple and transparent:
- One main executable.
- One top-level
CMakeLists.txt. - Avoid complex CMake logic unless strictly necessary.
This reduces confusion and makes the project long-term maintainable.
Project Naming and File Naming Conventions
Consistent naming is a small effort that pays off when the project grows. A common convention is:
- Use
CamelCasefor C++ class names:DetectorConstruction,PrimaryGeneratorAction. - Use lowercase words separated by underscores for file and directory names, except where Geant4 suggests class-based filenames:
detector_construction_tests.cc, butDetectorConstruction.ccfor the main class. - Keep Geant4 user classes with their standard class names, to make it immediately clear what they represent.
Namespaces can help avoid name clashes in larger projects, but for small beginner projects you can usually work without them. If you do use a namespace, keep its name short and meaningful, such as myproj or pet_sim.
Multiple Examples in One Repository
If you plan to keep several different Geant4 applications in a single repository, it is better to separate them as independent subprojects rather than mixing everything into one build target. A simple structure is:
| Path | Purpose |
|---|---|
CMakeLists.txt | Top-level controlling file. |
examples/gamma/ | Gamma-ray detector project. |
examples/shielding/ | Radiation shielding project. |
examples/pet/ | PET scanner project. |
cmake/ | Shared CMake modules or scripts. |
Each example can follow the recommended structure inside its own directory:
examples/gamma/src/examples/gamma/include/examples/gamma/macro/examples/gamma/output/
The top-level CMakeLists.txt can then add each example as a subdirectory and build a separate executable for each.
Summary of Recommended Practices
To conclude, a good Geant4 project structure for beginners should:
- Separate source code, headers, macros, data, outputs, and documentation into their own directories.
- Follow a one-class-per-file rule with matching
.ccand.hhfile names. - Reflect Geant4 user classes directly in file names, such as
DetectorConstruction,PrimaryGeneratorAction, andRunAction. - Keep the build configuration simple and in one place, without mixing build logic into simulation code.
- Use relative paths and clear naming for macros, visualization files, and output data.
- Be ready to scale by introducing subdirectories for geometry, physics, actions, detectors, hits, analysis, and utilities.
If you adopt this layout from the beginning of your work with Geant4, most future projects will feel familiar and easier to manage, even as they become more sophisticated.
Views: 8
KAHIBARO