KAHIBARO
Discord Login Register

36.10. Recommended Geant4 Project Structure

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:

  1. Avoiding subtle bugs when you change geometry, physics, or analysis.
  2. Reusing components across different simulations.
  3. 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 / FilePurpose
CMakeLists.txtTop-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:

ComponentHeader (in include/)Source (in src/)
Main program(none, typically)main.cc
Detector constructionDetectorConstruction.hhDetectorConstruction.cc
Physics list (if user defined)PhysicsList.hhPhysicsList.cc
Primary generatorPrimaryGeneratorAction.hhPrimaryGeneratorAction.cc
Run actionRunAction.hhRunAction.cc
Event actionEventAction.hhEventAction.cc
Stepping actionSteppingAction.hhSteppingAction.cc
Tracking actionTrackingAction.hhTrackingAction.cc
Stacking actionStackingAction.hhStackingAction.cc
Sensitive detector(s)MyDetectorSD.hhMyDetectorSD.cc
Hit class(es)MyHit.hhMyHit.cc
Analysis manager wrapper (optional)MyAnalysis.hhMyAnalysis.cc
Configuration / parameter classConfig.hhConfig.cc
Utility functionsUtils.hhUtils.cc

Within include/, you can group related headers into subdirectories once the project grows, for example:

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:

Your user initialization and actions are then separated:

ResponsibilityRecommended class nameFile name
Detector geometryDetectorConstructionDetectorConstruction.cc / .hh
Physics configurationPhysicsList or reference listPhysicsList.cc / .hh (if custom)
Action initializationActionInitializationActionInitialization.cc / .hh
Primary particle sourcePrimaryGeneratorActionPrimaryGeneratorAction.cc / .hh
Run-level bookkeepingRunActionRunAction.cc / .hh
Event-level bookkeepingEventActionEventAction.cc / .hh
Step-level operationsSteppingActionSteppingAction.cc / .hh
Optional tracking controlTrackingActionTrackingAction.cc / .hh
Optional secondary managementStackingActionStackingAction.cc / .hh
Sensitive detector for a subdetectorMyDetectorSDMyDetectorSD.cc / .hh
Hit information for that detectorMyHitMyHit.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:

The vis/ directory can store visualization specific macros so that you do not mix them with physics and run settings:

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.mac

In 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:

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:

CategoryExample directoryExample contents
Geometrysrc/geometry/DetectorConstruction.cc, Calorimeter.cc
Physicssrc/physics/PhysicsList.cc, EMPhysics.cc
Actionssrc/actions/RunAction.cc, EventAction.cc
Detectorssrc/detectors/CalorimeterSD.cc, TrackerSD.cc
Hitssrc/hits/CalorimeterHit.cc, TrackerHit.cc
Analysissrc/analysis/MyAnalysis.cc
Utilssrc/utils/Config.cc, Utils.cc

Mirror these directories in include/:

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:

  1. Find Geant4 using its configuration.
  2. Collect your project sources.
  3. Define an executable and link it to Geant4 libraries.
  4. 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:

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:

PathPurpose
CMakeLists.txtTop-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:

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:

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

Comments

Please login to add a comment.

Don't have an account? Register now!