-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathprotonct.py
155 lines (124 loc) · 5.09 KB
/
protonct.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import math
import argparse
import opengate as gate
from scipy.spatial.transform import Rotation
def protonct(output,
projections=720,
protons_per_projection=1000,
seed=None,
visu=False,
verbose=False
):
# 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' if seed is None else seed
sim.check_volumes_overlap = False
sim.visu = visu
sim.visu_type = 'vrml'
sim.g4_verbose = False
sim.progress_bar = verbose
sim.number_of_threads = 1
sim.run_timing_intervals = [[j * sec, (j + 1) * sec] for j in range(projections)]
# Misc
yellow = [1, 1, 0, .5]
blue = [0, 0, 1, .5]
# Geometry
sim.volume_manager.add_material_database(gate.utility.get_contrib_path() / 'GateMaterials.db')
sim.world.material = 'Vacuum'
sim.world.size = [4 * m, 4 * m, 4 * m]
sim.world.color = [0, 0, 0, 0]
# Phantom
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 = blue
spiral.rotation = Rotation.from_euler('y', 90, degrees=True).as_matrix()
# Spiral rotation
tr, rot = gate.geometry.utility.volume_orbiting_transform('x', 0, 360, projections, spiral.translation, spiral.rotation)
spiral.add_dynamic_parametrisation(translation=tr, rotation=rot)
# 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))]
def add_spiral_insert(sim, mother, name, rmin=0 * mm, rmax=1 * mm, dz=40 * cm, material='Aluminium', translation=None, color=None):
if translation is None:
translation = [0 * mm, 0 * mm, 0 * mm]
if color is None:
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
for i in range(len(radius)):
add_spiral_insert(sim, spiral, f'SpiralInsert{i:02d}', translation=[posx[i] * mm, posy[i] * mm, 0])
add_spiral(sim)
# Beam
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 = [16 * mm, 1 * nm, 1 * nm]
source.position.translation = [0, 0, -1060 * mm]
source.direction.type = 'focused'
source.direction.focus_point = [0, 0, -1000 * mm]
source.activity = protons_per_projection * Bq
# Physics list
sim.physics_manager.physics_list_name = 'QGSP_BIC_EMZ'
# Phase spaces
def add_detector(sim, name, translation):
plane = sim.add_volume('Box', 'PlanePhaseSpace' + name)
plane.size = [400 * mm, 400 * mm, 1 * nm]
plane.translation = translation
plane.material = 'Vacuum'
plane.color = yellow
phase_space = sim.add_actor('PhaseSpaceActor', 'PhaseSpace' + name)
phase_space.attached_to = plane.name
phase_space.attributes = [
'RunID',
'EventID',
'TrackID',
'KineticEnergy',
'Position',
'Direction'
]
phase_space.output_filename = f'{output}/PhaseSpace{name}.root'
ps_filter = sim.add_filter('ParticleFilter', 'Filter' + name)
ps_filter.particle = 'proton'
phase_space.filters.append(ps_filter)
add_detector(sim, 'In', [0, 0, -110 * mm])
add_detector(sim, 'Out', [0, 0, 110 * mm])
# Particle stats
stat = sim.add_actor('SimulationStatisticsActor', 'stat')
stat.output_filename = f'{output}/protonct.txt'
sim.run()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--output', help="Output folder", default='output')
parser.add_argument('-p', '--projections', help="Number of projections", default=720, type=int)
parser.add_argument('-n', '--protons-per-projection', help="Number of protons per projection", default=1000, type=int)
parser.add_argument('-s', '--seed', help="Random number generator seed", type=int, required=False)
parser.add_argument('--visu', help="Enable visualization", default=False, action='store_true')
parser.add_argument('--verbose', help="Verbose execution", default=False, action='store_true')
args = parser.parse_args()
protonct(**vars(args))