From GATE 9 to 10 - Example Proton CT#

This is an example of a proton beam with a spiral phantom placed in between two proton detectors. The spiral phantom was used in the article Filtered backprojection proton CT reconstruction along most likely paths by Rit et al. The data generated by this simulation can be processed by the PCT software.

../_images/proton_ct.png

Initialization#

GATE 9#

/gate/run/initialize

/gate/random/setEngineName MersenneTwister
/gate/random/setEngineSeed auto

/gate/application/setTimeSlice              1 s
/gate/application/setTimeStart              0 s
/gate/application/setTimeStop               360 s

/gate/application/setTotalNumberOfPrimaries 720000

GATE 10#

n = 360

# Units
nm = gate.g4_units.nm
mm = gate.g4_units.mm
cm = gate.g4_units.cm
m = gate.g4_units.m
sec = gate.g4_units.second
MeV = gate.g4_units.MeV
Bq = gate.g4_units.Bq

# Simulation
sim = gate.Simulation()

sim.random_engine = "MersenneTwister"
sim.random_seed = "auto"
sim.run_timing_intervals = [[i * sec, (i + 1) * sec] for i in range(n)]
sim.check_volumes_overlap = False
sim.visu = True
sim.visu_type = "qt"
sim.g4_verbose = False
sim.progress_bar = True
sim.number_of_threads = 1
sim.output_dir = "output"

# Misc
yellow = [1, 1, 0, 1]

Geometry#

GATE 9#

/gate/geometry/setMaterialDatabase  data/GateMaterials.db
/gate/world/setMaterial             Air
/gate/world/geometry/setXLength     4 m
/gate/world/geometry/setYLength     4 m
/gate/world/geometry/setZLength     4 m

GATE 10#

sim.volume_manager.add_material_database(path_to_gate_materials)
sim.world.material = "Air"
sim.world.size = [4 * m, 4 * m, 4 * m]

Phantom#

GATE 9#

/gate/world/daughters/name              Spiral
/gate/world/daughters/insert            cylinder
/gate/Spiral/geometry/setRmin           0 cm
/gate/Spiral/geometry/setRmax           10 cm
/gate/Spiral/geometry/setHeight         40 cm
/gate/Spiral/setMaterial                Water
/gate/Spiral/vis/setColor               yellow
/gate/Spiral/vis/setVisible             1

# Insert at radius 0.00 mm and angle 0.00 degree
/gate/Spiral/daughters/name             SpiralInsert01
/gate/Spiral/daughters/insert           cylinder
/gate/SpiralInsert01/geometry/setRmin         0 mm
/gate/SpiralInsert01/geometry/setRmax         1 mm
/gate/SpiralInsert01/geometry/setHeight       40 cm
/gate/SpiralInsert01/setMaterial              Aluminium
/gate/SpiralInsert01/placement/setTranslation 0.0000 0.0000 0 mm
/gate/SpiralInsert01/vis/setColor             yellow
/gate/SpiralInsert01/vis/setVisible           1

# ...
# 24 additional inserts, omitted for brevity
# ...

/gate/Spiral/moves/insert       rotation
/gate/Spiral/rotation/setSpeed  1 deg/s
/gate/Spiral/rotation/setAxis   0 0 0

GATE 10#

def add_spiral_insert(sim, mother, name, rmin=0 * mm, rmax=1 * mm, dz=40 * cm, material="Aluminium", translation=[0 * mm, 0 * mm, 0 * mm], color=yellow):
  spiral_insert = sim.add_volume("Tubs", name=name)
  spiral_insert.mother = mother.name
  spiral_insert.rmin = rmin
  spiral_insert.rmax = rmax
  spiral_insert.dz = dz
  spiral_insert.material = material
  spiral_insert.translation = translation
  spiral_insert.color = color

def add_spiral(sim):
  # Mother of all
  spiral = sim.add_volume("Tubs", name="Spiral")
  spiral.rmin = 0 * cm
  spiral.rmax = 10 * cm
  spiral.dz = 40 * cm
  spiral.material = "Water"
  spiral.color = yellow

  # Spiral inserts
  sradius = 4
  radius = list(range(0, 100 - sradius // 2, sradius))
  sangle = 139
  angles = [math.radians(a) for a in range(0, sangle * len(radius), sangle)]
  posx = [radius[i] * math.cos(angles[i]) for i in range(len(radius))]
  posy = [radius[i] * math.sin(angles[i]) for i in range(len(radius))]

  for i in range(len(radius)):
       add_spiral_insert(sim, spiral, f"SpiralInsert{i:02d}", translation=[posx[i] * mm, posy[i] * mm, 0])

  # Spiral rotation
  tr, rot = gate.geometry.utility.volume_orbiting_transform("z", 0, 360, n, spiral.translation, spiral.rotation)
  spiral.add_dynamic_parametrisation(translation=tr, rotation=rot)

add_spiral(sim)

Beam#

GATE 9#

/gate/source/addSource mybeam gps
/gate/source/mybeam/gps/particle       proton
/gate/source/mybeam/gps/ene/mono       200 MeV
/gate/source/mybeam/gps/ene/type       Mono
/gate/source/mybeam/gps/pos/halfx      8 mm
/gate/source/mybeam/gps/pos/halfy      1 mm
/gate/source/mybeam/gps/pos/centre     1060 0 0 mm
/gate/source/mybeam/gps/pos/rot1       0 1 0
/gate/source/mybeam/gps/pos/rot2       0 0 1
/gate/source/mybeam/gps/pos/type       Plane
/gate/source/mybeam/gps/pos/shape      Rectangle
/gate/source/mybeam/gps/direction      -1 0 0
/gate/source/mybeam/gps/ang/type       focused
/gate/source/mybeam/gps/ang/rot1       0 1 0
/gate/source/mybeam/gps/ang/rot2       0 0 1
/gate/source/mybeam/gps/ang/focuspoint 1000 0 0 mm

GATE 10#

source = sim.add_source("GenericSource", "mybeam")
source.particle = "proton"
source.energy.mono = 200 * MeV
source.energy.type = "mono"
source.position.type = "box"
source.position.size = [1 * nm, 16 * mm, 1 * nm]
source.position.translation = [1060 * mm, 0 * mm, 0 * mm]
source.direction.type = "focused"
source.direction.focus_point = [1000 * mm, 0 * mm, 0 * mm]
source.n = 720000 / sim.number_of_threads

Physics list#

GATE 9#

/control/execute mac/physicslist_EM_std.mac
/control/execute mac/physicslist_HAD_std.mac

GATE 10#

sim.physics_manager.physics_list_name = "QGSP_BIC_EMZ"

Phase spaces#

GATE 9#

/gate/world/daughters/name                          PlanePhaseSpaceIn
/gate/world/daughters/insert                        box
/gate/PlanePhaseSpaceIn/geometry/setXLength         1 nm
/gate/PlanePhaseSpaceIn/geometry/setYLength         400 mm
/gate/PlanePhaseSpaceIn/geometry/setZLength         400 mm
/gate/PlanePhaseSpaceIn/setMaterial                 Air
/gate/PlanePhaseSpaceIn/vis/setVisible              1
/gate/PlanePhaseSpaceIn/vis/setColor                yellow

/gate/actor/addActor PhaseSpaceActor                PhaseSpaceIn
/gate/actor/PhaseSpaceIn/save                       output/PhaseSpaceIn.root
/gate/actor/PhaseSpaceIn/attachTo                   PlanePhaseSpaceIn
/gate/actor/PhaseSpaceIn/enableEkine                true
/gate/actor/PhaseSpaceIn/enableXPosition            false
/gate/actor/PhaseSpaceIn/enableYPosition            true
/gate/actor/PhaseSpaceIn/enableZPosition            true
/gate/actor/PhaseSpaceIn/enableXDirection           true
/gate/actor/PhaseSpaceIn/enableYDirection           true
/gate/actor/PhaseSpaceIn/enableZDirection           true
/gate/actor/PhaseSpaceIn/enableProductionVolume     false
/gate/actor/PhaseSpaceIn/enableProductionProcess    false
/gate/actor/PhaseSpaceIn/enableParticleName         false
/gate/actor/PhaseSpaceIn/enableWeight               false
/gate/actor/PhaseSpaceIn/enableTime                 true
/gate/actor/PhaseSpaceIn/storeSecondaries           true
/gate/actor/PhaseSpaceIn/useVolumeFrame             false
/gate/actor/PhaseSpaceIn/storeOutgoingParticles     false               particleFilter
/gate/actor/PhaseSpaceIn/particleFilter/addParticle proton
0 0 mm

/gate/world/daughters/name                           PlanePhaseSpaceOut
/gate/world/daughters/insert                         box
/gate/PlanePhaseSpaceOut/geometry/setXLength         1 nm
/gate/PlanePhaseSpaceOut/geometry/setYLength         400 mm
/gate/PlanePhaseSpaceOut/geometry/setZLength         400 mm
/gate/PlanePhaseSpaceOut/setMaterial                 Air
/gate/PlanePhaseSpaceOut/vis/setVisible              1
/gate/PlanePhaseSpaceOut/vis/setColor                yellow

/gate/actor/addActor PhaseSpaceActor                 PhaseSpaceOut
/gate/actor/PhaseSpaceOut/save                       output/PhaseSpaceOut.root
/gate/actor/PhaseSpaceOut/attachTo                   PlanePhaseSpaceOut
/gate/actor/PhaseSpaceOut/enableEkine                true
/gate/actor/PhaseSpaceOut/enableXPosition            false
/gate/actor/PhaseSpaceOut/enableYPosition            true
/gate/actor/PhaseSpaceOut/enableZPosition            true
/gate/actor/PhaseSpaceOut/enableXDirection           true
/gate/actor/PhaseSpaceOut/enableYDirection           true
/gate/actor/PhaseSpaceOut/enableZDirection           true
/gate/actor/PhaseSpaceOut/enableProductionVolume     false
/gate/actor/PhaseSpaceOut/enableProductionProcess    false
/gate/actor/PhaseSpaceOut/enableParticleName         false
/gate/actor/PhaseSpaceOut/enableWeight               false
/gate/actor/PhaseSpaceOut/enableTime                 true
/gate/actor/PhaseSpaceOut/storeSecondaries           true
/gate/actor/PhaseSpaceOut/useVolumeFrame             false
/gate/actor/PhaseSpaceOut/storeOutgoingParticles     false
/gate/actor/PhaseSpaceOut/particleFilter/addParticle proton

GATE 10#

def add_detector(sim, name, translation):
    plane = sim.add_volume("Box", "PlanePhaseSpace" + name)
    plane.size = [1 * nm, 400 * mm, 400 * mm]
    plane.translation = translation
    plane.material = "Air"
    plane.color = yellow

    phase_space = sim.add_actor("PhaseSpaceActor", "PhaseSpace" + name)
    phase_space.attached_to = plane.name
    phase_space.attributes = [
        "RunID",
        "EventID",
        "TrackID",
        "TrackCreatorProcess",
        "KineticEnergy",
        "Position",
        "Direction",
        "GlobalTime"
    ]
    filter = sim.add_filter("ParticleFilter", "Filter" + name)
    filter.particle = "proton"
    phase_space.filters.append(filter)
    return phase_space

phsp_actor_in = add_detector(sim, "In", [110 * mm, 0 * mm, 0 * mm])
phsp_actor_out = add_detector(sim, "Out", [-110 * mm, 0 * mm, 0 * mm])
phsp_actor_in.output_filename = "PhaseSpaceIn.root"
phsp_actor_out.output_filename = "PhaseSpaceOut.root"

Particles stats#

GATE 9#

/gate/actor/addActor  SimulationStatisticActor stat
/gate/actor/stat/save output/protonct.txt

GATE 10#

stat = sim.add_actor("SimulationStatisticsActor", "stat")
stat.output_filename = "protonct.json"

Main#

GATE 9#

/gate/application/start

GATE 10#

sim.run()