KAHIBARO
Discord Login Register

Configuration Files

Separating configuration from code

Configuration files let you describe how a simulation should run without touching the Python logic that implements it. This separation is extremely helpful for GATE, where you often repeat the same geometry or detector model but change parameters such as activity, beam energy, or dose grid size.

In practice, you keep your Python files focused on "how" to build and run a simulation, and you move most of the "what values" and "which options" into external files. This makes your simulations easier to modify, reproduce, and share with others.

A simple way to think about it is that your Python script becomes an engine that reads instructions from configuration files. When you want a new scanner geometry, a different phantom, or a new energy window, you update the configuration rather than rewriting the code.

Why use configuration files in GATE projects

In even a modest GATE project you quickly accumulate many numerical parameters. These include sizes, positions, materials, activities, energy windows, and digitizer thresholds. If you hard code all of them directly inside functions they tend to spread across the script, which makes it difficult to answer basic questions like "Which values did I use for this figure?" or "What changed between these two simulations?"

Configuration files address these problems. They centralize parameters in one or a few human readable files, so you can:

  1. See all key settings in one place.
  2. Compare two configurations side by side.
  3. Track configuration changes with version control.
  4. Reuse the same code with many different parameter sets.

Configuration also helps reduce errors. When configuration is centralized you are less likely to forget to change a value in one part of the script but not another. It is also easier to implement checks, for example verifying that required fields are present or that some values are in realistic ranges.

In collaborative work configuration files make it possible for a user who is less comfortable with Python to still adjust simulations by editing a text file instead of touching the code. This is especially helpful for students or experimental collaborators who want to control geometry or beam settings without learning the details of the GATE Python API.

Common configuration formats for GATE

For GATE projects written in Python the most practical configuration formats are plain text formats that are easy to edit, read, and parse. Three popular choices are JSON, YAML, and TOML. They all have slightly different syntax, but the role is the same. They hold nested structures with names and values.

JSON is widely supported, simple, and unambiguous. A small JSON configuration for a PET scanner might look like this:

json
{
  "scanner": {
    "inner_radius_mm": 420.0,
    "axial_length_mm": 250.0,
    "crystal": {
      "material": "LYSO",
      "size_mm": [3.0, 3.0, 20.0]
    }
  },
  "source": {
    "radionuclide": "F18",
    "activity_MBq": 10.0
  },
  "digitizer": {
    "energy_window_keV": [400.0, 650.0]
  }
}

YAML has a more compact syntax and is often more pleasant to read:

yaml
scanner:
  inner_radius_mm: 420.0
  axial_length_mm: 250.0
  crystal:
    material: LYSO
    size_mm: [3.0, 3.0, 20.0]
source:
  radionuclide: F18
  activity_MBq: 10.0
digitizer:
  energy_window_keV: [400.0, 650.0]

TOML is another common choice, especially in Python projects, with table style sections:

toml
[scanner]
inner_radius_mm = 420.0
axial_length_mm = 250.0
[scanner.crystal]
material = "LYSO"
size_mm = [3.0, 3.0, 20.0]
[source]
radionuclide = "F18"
activity_MBq = 10.0
[digitizer]
energy_window_keV = [400.0, 650.0]

For beginners JSON is a good starting point because it is supported directly in the Python standard library and many analysis tools know how to read it. YAML and TOML need external Python packages but are widely used in scientific projects.

Regardless of the specific format, you should aim for a structure that reflects your simulation: high level sections for geometry, sources, physics, actors, and digitizers, then specific parameters inside each section.

Loading configuration in Python

To use a configuration file you import a parser in your main simulation script, read the file, and obtain a nested Python dictionary. Your code then passes these values to functions that build geometry, configure sources, and set up actors.

Here is a minimal example with JSON:

python
import json
from pathlib import Path
def load_config(path):
    with open(path, "r") as f:
        cfg = json.load(f)
    return cfg
if __name__ == "__main__":
    config_path = Path("config") / "pet_config.json"
    cfg = load_config(config_path)
    # Example access
    inner_radius = cfg["scanner"]["inner_radius_mm"]
    radionuclide = cfg["source"]["radionuclide"]

YAML and TOML usage is similar if you install the corresponding package:

python
# YAML example (requires pyyaml)
import yaml
with open("config/pet_config.yaml", "r") as f:
    cfg = yaml.safe_load(f)

Once you have the dictionary you pass it into your geometry and source functions. These functions are discussed in other chapters, so here we only mention the pattern. For example:

python
def build_scanner(sim, scanner_cfg):
    # Use scanner_cfg values to create volumes
    pass
def configure_source(sim, source_cfg):
    # Use source_cfg values to define the particle source
    pass
cfg = load_config("config/pet_config.json")
build_scanner(sim, cfg["scanner"])
configure_source(sim, cfg["source"])

Whenever possible avoid reading the same configuration file from multiple modules. Load it once in the entry point of your application and pass the relevant parts as arguments. This makes the flow easier to understand and simplifies testing.

It is useful to perform small validation right after loading the configuration. You can check for missing sections, enforce units, or assert that some ranges are correct. For example:

python
scanner_cfg = cfg["scanner"]
assert scanner_cfg["inner_radius_mm"] > 0

You can also write helper functions that provide default values if a field is missing.

Designing configuration structure around your simulations

A configuration file is more than just a bag of numbers. Its structure should reflect how you think about your simulations. If the structure is clear, understanding and modifying a simulation becomes much easier.

At the top level you can mirror the high level components of a GATE simulation workflow, such as geometry, sources, physics, actors, digitizers, and output. A possible layout could be:

yaml
geometry:
  world:
    size_m: [2.0, 2.0, 2.0]
    material: "G4_AIR"
  scanner:
    type: "pet_ring"
    inner_radius_mm: 420.0
    axial_length_mm: 250.0
sources:
  - name: "f18_source"
    type: "radioactive"
    radionuclide: "F18"
    activity_MBq: 5.0
physics:
  list: "G4EmStandardPhysics_option4"
actors:
  dose:
    enabled: true
    voxel_size_mm: [2.0, 2.0, 2.0]
digitizer:
  singles:
    energy_window_keV: [400.0, 650.0]
output:
  directory: "results/pet_run01"
  save_root: true

This example is only schematic, but the idea is that each function in your script reads a specific part of this structure. For instance, the function that sets up physics receives the physics block, and the function that defines actors receives the actors block.

For long projects you can split configuration into multiple files. For example, one file defines the scanner, which rarely changes, and another file defines study specific settings such as activity, acquisition time, and output directory. In that case your main script can combine them:

python
scanner_cfg = load_config("config/scanner.json")
study_cfg = load_config("config/study_A.json")
cfg = {
    "geometry": scanner_cfg["geometry"],
    "sources": study_cfg["sources"],
    "digitizer": study_cfg["digitizer"],
    "output": study_cfg["output"],
}

This approach is useful when several studies share the same detector model or patient geometry but differ in irradiation conditions.

Be careful to include units in parameter names or in a top level "units" section. Since GATE uses explicit units such as mm, MeV, and s, you must know what each number represents when you read it from a configuration file. A clear naming pattern such as inner_radius_mm or activity_MBq helps prevent confusion.

Configuration in parameter studies and reproducibility

Configuration files are particularly powerful when you perform parameter studies or optimization. Instead of editing Python scripts repeatedly, you create sets of configuration files that only differ in one or a few values, such as shield thickness or source position. A simple external script or shell loop can then run a simulation for each configuration without changing the code.

To keep parameter studies organized, adopt a naming convention for configuration files that encodes the key difference. For example:

The simulation script does not need to know which case it is running. It simply loads whichever configuration file it receives as an argument. That also makes simulations easier to run on high performance computing systems where each job reads a different configuration.

For reproducibility you should store configuration files in version control together with the Python code. Whenever you produce results for a figure or a report, record the exact configuration file used. You can also include the configuration content in the simulation output, for example by copying the file into the output directory or by saving a JSON snapshot of the configuration in the ROOT or text output.

Always save the exact configuration used for each simulation. Results are not reproducible if you cannot reconstruct the values of geometry, sources, physics, and actors. Store configuration files together with code and output, and avoid manual edits that are not tracked.

By clearly separating configuration from code you gain flexibility, reduce mistakes, and make it much easier for others, and for your future self, to understand, reproduce, and extend your GATE simulations.

Views: 12

Comments

Please login to add a comment.

Don't have an account? Register now!