Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiprocessing #486

Draft
wants to merge 169 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
169 commits
Select commit Hold shift + click to select a range
fe4c659
Correct typo in comment
nkrah Oct 9, 2024
b66c665
Implement DynamicGateObject.reassign_subset_of_dynamic_params()
nkrah Oct 9, 2024
0456d04
Add attribute process_index to SimulationEngine (not used yet)
nkrah Oct 9, 2024
30526c8
First steps towards multi processing
nkrah Oct 9, 2024
a7434e5
Add test080_multiprocessing_1.py
nkrah Oct 9, 2024
605192f
Add test030_dose_motion_dynamic_param_multiproc.py
nkrah Oct 9, 2024
e4bc3a7
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 9, 2024
523fdc9
Implement MultiProcessingHandler classes
nkrah Oct 11, 2024
cea24cf
create test080_multiprocessing_handler.py
nkrah Oct 11, 2024
ae37675
Implement import_data_from_actor_output()
nkrah Oct 13, 2024
9c74aa2
Implement import_user_output_from_actor()
nkrah Oct 13, 2024
87af53e
Implement import_user_output_from_actor in ActorBase
nkrah Oct 13, 2024
196d954
Change local variable name in reassign_dynamic_params_for_process()
nkrah Oct 13, 2024
138a44f
Implement FinalizeSimulation() in VoxelDepositActor
nkrah Oct 13, 2024
a796bec
Add simulation_id to SimulationOutput
nkrah Oct 13, 2024
1375e4f
Implement SimulationOutput.store_output_from_simulation_engine()
nkrah Oct 13, 2024
fc90573
Simplify code in run_engine()
nkrah Oct 13, 2024
91f6b7f
Implement SimulationMetaData class
nkrah Oct 13, 2024
722552a
Use SimulationMetaData in Simulation
nkrah Oct 13, 2024
c7a317b
Update MultiProcessingHandlerBase class
nkrah Oct 13, 2024
52bde4f
Remove obsolete generate_run_timing_interval_map() method
nkrah Oct 13, 2024
3d1448b
Update imports in managers.py
nkrah Oct 13, 2024
fc0d155
Update run_in_process()
nkrah Oct 13, 2024
8b43fb2
Store number_of_sub_processes and start_new_process in simulation_met…
nkrah Oct 13, 2024
a544da9
Introduce avoid_write_to_disk_in_subprocess kwarg in Simulation.run()
nkrah Oct 13, 2024
19c9b17
Use multi_proc_handler in Simulation.run()
nkrah Oct 13, 2024
23fb55e
Trigger import_user_output_from_actor() after multi_proc run
nkrah Oct 13, 2024
a1ce476
store meta_data after run
nkrah Oct 13, 2024
a120987
Get parameters from sources after multiproc run
nkrah Oct 13, 2024
78bcd34
Remove obsolete code
nkrah Oct 13, 2024
39a45c8
Trigger FinalizeSimulation() at the end of a run
nkrah Oct 13, 2024
7e47bec
remove obsolete code
nkrah Oct 13, 2024
b8581ce
Add test008_dose_actor_multiproc.py
nkrah Oct 13, 2024
3d4fe1e
Update test080_multiprocessing_1.py
nkrah Oct 13, 2024
dcebecc
Update GateObject to rename property 'warnings' in Simulation
nkrah Oct 13, 2024
c804709
Update run_engine to use Simulation.meta_data
nkrah Oct 13, 2024
27b49c6
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 13, 2024
0a25f0d
Rename FinalizeSimulation() to EndOfMultiProcessAction()
nkrah Oct 13, 2024
c4cf4a4
Implement explicit inplace_merge_with in ItkImageDataItem
nkrah Oct 14, 2024
d94c0e4
Adapt merge_data to try to accelerate it (WIP)
nkrah Oct 14, 2024
7d919d8
Implement sum_itk_images based on SimpleITK (workaround)
nkrah Oct 14, 2024
7bfa8b7
remove debug prints and clean code
nkrah Oct 14, 2024
11f6740
In BaseUserInterfaceToActorOutput.__getstate__: always use re…
nkrah Oct 14, 2024
1383057
Implement reset_user_output() and reset_data()
nkrah Oct 14, 2024
6bba1b7
remove debug print
nkrah Oct 14, 2024
56061bc
Update comment
nkrah Oct 14, 2024
aebcc40
Update test008_dose_actor_multiproc.py
nkrah Oct 14, 2024
557e679
Update test009_voxels_dynamic.py (not relevant for test result)
nkrah Oct 14, 2024
15d72cd
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 14, 2024
53f5ff0
Correct typo in comment
nkrah Oct 9, 2024
1463013
Implement DynamicGateObject.reassign_subset_of_dynamic_params()
nkrah Oct 9, 2024
b3e8f0c
Add attribute process_index to SimulationEngine (not used yet)
nkrah Oct 9, 2024
0b7569b
First steps towards multi processing
nkrah Oct 9, 2024
d17682f
Add test080_multiprocessing_1.py
nkrah Oct 9, 2024
fb3cb56
Add test030_dose_motion_dynamic_param_multiproc.py
nkrah Oct 9, 2024
aa5e710
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 9, 2024
76fbfba
Implement MultiProcessingHandler classes
nkrah Oct 11, 2024
6973f13
create test080_multiprocessing_handler.py
nkrah Oct 11, 2024
0260522
Implement import_data_from_actor_output()
nkrah Oct 13, 2024
3b93451
Implement import_user_output_from_actor()
nkrah Oct 13, 2024
4d141c3
Implement import_user_output_from_actor in ActorBase
nkrah Oct 13, 2024
f06e627
Change local variable name in reassign_dynamic_params_for_process()
nkrah Oct 13, 2024
3ea4bc7
Implement FinalizeSimulation() in VoxelDepositActor
nkrah Oct 13, 2024
504673f
Add simulation_id to SimulationOutput
nkrah Oct 13, 2024
76b0f86
Implement SimulationOutput.store_output_from_simulation_engine()
nkrah Oct 13, 2024
fd799dd
Simplify code in run_engine()
nkrah Oct 13, 2024
3b6d94d
Implement SimulationMetaData class
nkrah Oct 13, 2024
c4cf0f0
Use SimulationMetaData in Simulation
nkrah Oct 13, 2024
8b28b15
Update MultiProcessingHandlerBase class
nkrah Oct 13, 2024
b1f2d21
Remove obsolete generate_run_timing_interval_map() method
nkrah Oct 13, 2024
2a033b7
Update imports in managers.py
nkrah Oct 13, 2024
9a6f871
Update run_in_process()
nkrah Oct 13, 2024
18b49a0
Store number_of_sub_processes and start_new_process in simulation_met…
nkrah Oct 13, 2024
0a4b237
Introduce avoid_write_to_disk_in_subprocess kwarg in Simulation.run()
nkrah Oct 13, 2024
805468a
Use multi_proc_handler in Simulation.run()
nkrah Oct 13, 2024
fa5ece3
Trigger import_user_output_from_actor() after multi_proc run
nkrah Oct 13, 2024
af06335
store meta_data after run
nkrah Oct 13, 2024
002c000
Get parameters from sources after multiproc run
nkrah Oct 13, 2024
43ae7d2
Remove obsolete code
nkrah Oct 13, 2024
78eecbb
Trigger FinalizeSimulation() at the end of a run
nkrah Oct 13, 2024
c6b9ae1
remove obsolete code
nkrah Oct 13, 2024
d6d5b37
Add test008_dose_actor_multiproc.py
nkrah Oct 13, 2024
53ceb61
Update test080_multiprocessing_1.py
nkrah Oct 13, 2024
38896f0
Update GateObject to rename property 'warnings' in Simulation
nkrah Oct 13, 2024
0e9928d
Update run_engine to use Simulation.meta_data
nkrah Oct 13, 2024
0020307
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 13, 2024
2612857
Rename FinalizeSimulation() to EndOfMultiProcessAction()
nkrah Oct 13, 2024
956be1c
Implement explicit inplace_merge_with in ItkImageDataItem
nkrah Oct 14, 2024
7bb0000
Adapt merge_data to try to accelerate it (WIP)
nkrah Oct 14, 2024
d5c7c40
Implement sum_itk_images based on SimpleITK (workaround)
nkrah Oct 14, 2024
481dbc4
remove debug prints and clean code
nkrah Oct 14, 2024
bffb06c
In BaseUserInterfaceToActorOutput.__getstate__: always use re…
nkrah Oct 14, 2024
fecd1e1
Implement reset_user_output() and reset_data()
nkrah Oct 14, 2024
845544e
remove debug print
nkrah Oct 14, 2024
2450ef5
Update comment
nkrah Oct 14, 2024
ee5b1df
Update test008_dose_actor_multiproc.py
nkrah Oct 14, 2024
2f49d87
Update test009_voxels_dynamic.py (not relevant for test result)
nkrah Oct 14, 2024
5ccba4e
Increment random_seed in run_in_process
nkrah Oct 14, 2024
775ddb2
Extend inplace_merge_with() to accept *other, i.e. an unpackaged list
nkrah Oct 15, 2024
ccc141d
Fix ItkImageDataItem.inplace_merge_with()
nkrah Oct 15, 2024
2df161a
Fix typo in error message
nkrah Oct 15, 2024
5dcb80b
Update test008_dose_actor_multiproc.py
nkrah Oct 15, 2024
79788b8
Deprecate unused kwarg 'stat' in assert_images()
nkrah Oct 15, 2024
22411d4
Update multiproc logic in Simulation class
nkrah Oct 15, 2024
d21bcbf
Merge remote-tracking branch 'origin/multiproc' into multiproc
nkrah Oct 24, 2024
fb36286
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 24, 2024
27f4204
Implement reset_data() in DataItem base class
nkrah Oct 24, 2024
994fded
update imports in dataitems.py
nkrah Oct 24, 2024
1e706b4
Implement StatisticsDataItem
nkrah Oct 24, 2024
b17a98c
Implement StatisticsItemContainer
nkrah Oct 24, 2024
09a95c4
Let SimulationStatisticsActor use new ActorOutputStatisticsActor
nkrah Oct 24, 2024
a2cd755
Implement ActorOutputStatisticsActor
nkrah Oct 24, 2024
abdaacd
Implement UserInterfaceToActorOutputStatisticsActor
nkrah Oct 24, 2024
37b6bba
let default_suffix be an instance attribute of the ActorOutput classe…
nkrah Oct 25, 2024
3b0e499
Implement ActorOutputUsingDataItemContainer.reset_data()
nkrah Oct 25, 2024
6ce548c
Add process_cls(ActorOutputStatisticsActor)
nkrah Oct 25, 2024
fd4d7fc
Remove unused store_output_data() method
nkrah Oct 25, 2024
e1a6713
Update test008_dose_actor_multiproc.py
nkrah Oct 25, 2024
3c4e7b5
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 25, 2024
51733c1
Update physics and sources documentation
dsarrut Oct 25, 2024
d76b64f
Add test080_multiprocessing_1.py
nkrah Oct 9, 2024
98752bf
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 9, 2024
8217a0b
Implement MultiProcessingHandler classes
nkrah Oct 11, 2024
3193890
create test080_multiprocessing_handler.py
nkrah Oct 11, 2024
552fae4
Implement FinalizeSimulation() in VoxelDepositActor
nkrah Oct 13, 2024
47bfa06
Add test008_dose_actor_multiproc.py
nkrah Oct 13, 2024
352ac37
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 13, 2024
cb90f29
Rename FinalizeSimulation() to EndOfMultiProcessAction()
nkrah Oct 13, 2024
d4d0f4b
Update test008_dose_actor_multiproc.py
nkrah Oct 14, 2024
8cb80b8
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 14, 2024
092dd69
Merge remote-tracking branch 'origin/master' into multiproc
nkrah Oct 28, 2024
ad36e56
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 28, 2024
216209f
rename test080 on multi processing to test082_xxx
nkrah Oct 28, 2024
8063a4d
Add merge_root to processing.py
nkrah Oct 28, 2024
3729d25
rename import_data_from_actor_output to merge_data_from_actor_output
nkrah Oct 28, 2024
fafbb32
Extend signature of import_user_output_from_actor by **kwargs
nkrah Oct 28, 2024
69d9649
Add process_index attribute to SimulationEngine and SimulationOutput
nkrah Oct 28, 2024
33fdb50
Implement clear_output_dir_before_run
nkrah Oct 28, 2024
26d6c54
Implement sub_process_registry
nkrah Oct 28, 2024
9fe4915
Make multi_proc_handler an instance attribute
nkrah Oct 28, 2024
e1d58bf
Add output_dir as argument to run_in_process()
nkrah Oct 28, 2024
a70ba52
Let each process store a json in the subfolder
nkrah Oct 28, 2024
f5f0d44
Capsulate code into merge_simulations_from_multiprocessing()
nkrah Oct 28, 2024
c9b9b66
check global variable __spec__ in run() to avoid breaking when runnin…
nkrah Oct 28, 2024
43a8292
Remove merge_root() and unicity(): now in ActorOutputRoot class
nkrah Oct 28, 2024
facc182
Implement ActorOutputRootmerge_data_from_actor_output()
nkrah Oct 28, 2024
5d06d5b
Add test019_phsp_actor_multiproc.py
nkrah Oct 28, 2024
01d0ea5
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 28, 2024
245b73b
Remove obsolete imports in processing.py
nkrah Oct 28, 2024
aaed7a5
Add property counts to SimulationStatisticsActor
nkrah Oct 29, 2024
c70cfef
remove obsolete UserInterfaceToActorOutputStatisticsActor
nkrah Oct 29, 2024
f16c1ea
Extend DataItem.__getattr__ to get attributes from self.data if possible
nkrah Oct 29, 2024
3423476
Adapt read_stat_file_legacy() to updated stats actor
nkrah Oct 29, 2024
fc7322a
Adapt assert_stats() and assert_stats_json() to updated stats actor
nkrah Oct 29, 2024
a8f9add
Merge sim_start_time and sim_stop_time via min/max
nkrah Oct 30, 2024
2210788
Adapt assert_stats_json() to updated StatsActor
nkrah Oct 30, 2024
d482c62
Improve handling of start_time and stop_time in StatisticsDataItem
nkrah Oct 30, 2024
1473df0
Update GateSimulationStatisticsActor::GetCounts()
nkrah Oct 30, 2024
014e950
In GateSimulationStatisticsActor: save start_time and stop_time as ep…
nkrah Oct 30, 2024
451e7da
adapt read_stat_file_json() to updated StatsActor
nkrah Oct 31, 2024
f6f348b
remove redundant code in test008_dose_actor_multiproc.py
nkrah Oct 31, 2024
09b4830
Merge remote-tracking branch 'origin/master' into multiproc
nkrah Oct 31, 2024
9e69420
[pre-commit.ci] Automatic python and c++ formatting
pre-commit-ci[bot] Oct 31, 2024
0eb6b81
activate ssh session on github
nkrah Oct 31, 2024
bbec500
update test082_multiprocessing_1.py
nkrah Oct 31, 2024
ec56329
pick up simulation_id as meta_data after a simulation
nkrah Oct 31, 2024
039ef82
add explicit process_cls() for MultiProcessHandler classes
nkrah Oct 31, 2024
dd3068c
Update file names in test081_simulation_optigan_with_random_seed.py
nkrah Oct 31, 2024
0ac5519
Adapt test006_runs.py to updated StatsActorOutput
nkrah Oct 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion opengate/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ def process_dynamic_parametrisation(self, params):
extra_params = {}
extra_params["auto_changer"] = params.pop(
"auto_changer", True
) # True of key not found (default)
) # True if key not found (default)
if extra_params["auto_changer"] not in (False, True):
fatal(
f"Received wrong value type for 'auto_changer': got {type(extra_params['auto_changer'])}, "
Expand Down Expand Up @@ -750,6 +750,14 @@ def add_dynamic_parametrisation(self, name=None, **params):
s += f"{k}: {v}\n"
log.debug(s)

def reassign_subset_of_dynamic_params(self, subset):
# loop over all dynamic parametrisations of this object,
for param in self.user_info["dynamic_params"].values():
for k, v in param.items():
# extract the subset of entries to the list that are relevant to this process
if k in self.dynamic_user_info:
param[k] = [v[i] for i in subset]

def create_changers(self):
# this base class implementation is here to keep inheritance intact.
return []
Expand Down
1 change: 1 addition & 0 deletions opengate/engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,7 @@ def __init__(self, simulation, new_process=False):
# this is only for info.
# Process handling is done in Simulation class, not in SimulationEngine!
self.new_process = new_process
self.process_index = None

# LATER : option to wait the end of completion or not

Expand Down
107 changes: 105 additions & 2 deletions opengate/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
from pathlib import Path
import weakref
import multiprocessing

import opengate_core as g4

Expand Down Expand Up @@ -1460,6 +1461,7 @@ def __init__(self, name="simulation", **kwargs):
self._current_random_seed = None

self.expected_number_of_events = None
self.mapping_run_timing_intervals = {}

def __str__(self):
s = (
Expand Down Expand Up @@ -1653,7 +1655,7 @@ def add_filter(self, filter_type, name):
def multithreaded(self):
return self.number_of_threads > 1 or self.force_multithread_mode

def _run_simulation_engine(self, start_new_process):
def _run_simulation_engine(self, start_new_process, process_index=None):
"""Method that creates a simulation engine in a context (with ...) and runs a simulation.

Args:
Expand All @@ -1668,17 +1670,74 @@ def _run_simulation_engine(self, start_new_process):
with SimulationEngine(self) as se:
se.new_process = start_new_process
se.init_only = self.init_only
se.process_index = process_index
output = se.run_engine()
return output

def run(self, start_new_process=False):
def generate_run_timing_interval_map(self, number_of_processes):
if number_of_processes % len(self.run_timing_intervals) != 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why ? I thought we just divide ALL time_interval by the number_of_processes

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but letting the user define the total number of processes rather than the process per run is more intuitive and will not require an API change if we implement a more advanced splitting scheme in the future. So I think it's better this way.

fatal(
"number_of_sub_processes must be a multiple of the number of run_timing_intervals, \n"
f"but I received {number_of_processes}, while there are {len(self.run_timing_intervals)}."
)

number_of_processes_per_run = int(
number_of_processes / len(self.run_timing_intervals)
)
run_timing_interval_map = {}
process_index = 0
for i, rti in enumerate(self.run_timing_intervals):
t_start, t_end = rti
duration_original = t_end - t_start
duration_in_process = duration_original / number_of_processes_per_run
t_intermediate = [
t_start + (j + 1) * duration_in_process
for j in range(number_of_processes_per_run - 1)
]
t_all = [t_start] + t_intermediate + [t_end]
for t_s, t_e in zip(t_all[:-1], t_all[1:]):
run_timing_interval_map[process_index] = {
"run_timing_intervals": [[t_s, t_e]],
"lut_original_rti": [i],
}
process_index += 1
return run_timing_interval_map

def run_in_process(self, process_index, run_timing_intervals, lut_original_rti):
# Important: this method is intended to run in a processes spawned off the main process.
# Therefore, self is actually a separate instance from the original simulation
# and we can safely adapt it in this process.

# adapt the output_dir
self.output_dir = str(Path(self.output_dir) / f"process_{process_index}")
print("self.output_dir = ", self.output_dir)

# adapt the run timing intervals in
self.run_timing_intervals = run_timing_intervals
# adapt all dynamic volumes
for vol in self.volume_manager.dynamic_volumes:
vol.reassign_subset_of_dynamic_params(lut_original_rti)
print(process_index)
print(f"Volume {vol.name}:")
print(vol.user_info["dynamic_params"])

output = self._run_simulation_engine(False, process_index=process_index)
print(
process_index, os.getpid(), id(self), run_timing_intervals, lut_original_rti
)
return output

def run(self, start_new_process=False, number_of_sub_processes=0):
# if windows and MT -> fail
if os.name == "nt" and self.multithreaded:
fatal(
"Error, the multi-thread option is not available for Windows now. "
"Run the simulation with one thread."
)

if number_of_sub_processes == 1:
start_new_process = True

# prepare sub process
if start_new_process is True:
"""Important: put:
Expand All @@ -1705,6 +1764,50 @@ def run(self, start_new_process=False):
source.fTotalSkippedEvents = s.user_info.fTotalSkippedEvents
source.fTotalZeroEvents = s.user_info.fTotalZeroEvents

elif number_of_sub_processes > 1:
run_timing_interval_map = self.generate_run_timing_interval_map(
number_of_sub_processes
)
try:
multiprocessing.set_start_method("spawn")
except RuntimeError:
print("Could not set start method 'spawn'.")
pass
# q = multiprocessing.Queue()
with multiprocessing.Pool(len(run_timing_interval_map)) as pool:
print("pool._outqueue: ", pool._outqueue) # DEMO
results = [
pool.apply_async(
self.run_in_process,
(
k,
v["run_timing_intervals"],
v["lut_original_rti"],
),
)
for k, v in run_timing_interval_map.items()
]
# `.apply_async()` immediately returns AsyncResult (ApplyResult) object
print(results[0]) # DEMO
list_of_output = [res.get() for res in results]
print(f"list_of_output: {list_of_output}")
return list_of_output
# processes = []
# for k, v in run_timing_interval_map.items():
# p = multiprocessing.Process(
# target=target_func,
# args=(q, self.run_in_process, k, v['run_timing_intervals'], v['lut_original_rti'])
# )
# p.start()
# processes.append(p)
# for p in processes:
# p.join() # (timeout=10) # timeout might be needed
#
# try:
# output = q.get(block=False)
# except queue.Empty:
# fatal("The queue is empty. The spawned process probably died.")
# return output
else:
# Nothing special to do if the simulation engine ran in the native python process
# because everything is already in place.
Expand Down
133 changes: 133 additions & 0 deletions opengate/tests/src/test030_dose_motion_dynamic_param_multiproc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import opengate as gate
from scipy.spatial.transform import Rotation
from opengate.tests import utility

if __name__ == "__main__":
paths = utility.get_default_test_paths(
__file__, "gate_test029_volume_time_rotation", "test030"
)

# create the simulation
sim = gate.Simulation()

# main options
sim.g4_verbose = False
sim.visu = False
sim.random_seed = 983456
sim.output_dir = paths.output

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

# change world size
sim.world.size = [1 * m, 1 * m, 1 * m]

# add a simple fake volume to test hierarchy
# translation and rotation like in the Gate macro
fake = sim.add_volume("Box", "fake")
fake.size = [40 * cm, 40 * cm, 40 * cm]
fake.translation = [1 * cm, 2 * cm, 3 * cm]
fake.material = "G4_AIR"
fake.color = [1, 0, 1, 1]

# waterbox
waterbox = sim.add_volume("Box", "waterbox")
waterbox.mother = fake
waterbox.size = [20 * cm, 20 * cm, 20 * cm]
waterbox.translation = [-3 * cm, -2 * cm, -1 * cm]
waterbox.rotation = Rotation.from_euler("y", -20, degrees=True).as_matrix()
waterbox.material = "G4_WATER"
waterbox.color = [0, 0, 1, 1]

# physics
sim.physics_manager.set_production_cut("world", "all", 700 * um)

# default source for tests
# the source is fixed at the center, only the volume will move
source = sim.add_source("GenericSource", "mysource")
source.energy.mono = 150 * MeV
source.particle = "proton"
source.position.type = "disc"
source.position.radius = 5 * mm
source.direction.type = "momentum"
source.direction.momentum = [0, 0, 1]
source.activity = 30000 * Bq

# add dose actor
dose = sim.add_actor("DoseActor", "dose")
dose.output_filename = "test030.mhd"
dose.attached_to = waterbox
dose.size = [99, 99, 99]
mm = gate.g4_units.mm
dose.spacing = [2 * mm, 2 * mm, 2 * mm]
dose.translation = [2 * mm, 3 * mm, -2 * mm]
dose.edep.keep_data_per_run = True
dose.edep.auto_merge = True
dose.edep_uncertainty.active = True

# add stat actor
stats = sim.add_actor("SimulationStatisticsActor", "Stats")

# motion
n = 3
interval_length = 1 * sec / n
sim.run_timing_intervals = [
(i * interval_length, (i + 1) * interval_length) for i in range(n)
]
gantry_angles_deg = [i * 20 for i in range(n)]
(
dynamic_translations,
dynamic_rotations,
) = gate.geometry.utility.get_transform_orbiting(
initial_position=fake.translation, axis="Y", angle_deg=gantry_angles_deg
)
fake.add_dynamic_parametrisation(
translation=dynamic_translations, rotation=dynamic_rotations
)

# start simulation
sim.run(number_of_sub_processes=3 * len(sim.run_timing_intervals))

# # print results at the end
# print(stats)
#
# # tests
# stats_ref = utility.read_stat_file(paths.output_ref / "stats030.txt")
# is_ok = utility.assert_stats(stats, stats_ref, 0.11)
#
# print()
# gate.exception.warning("Difference for EDEP")
# is_ok = (
# utility.assert_images(
# paths.output_ref / "test030-edep.mhd",
# dose.edep.get_output_path(),
# stats,
# tolerance=30,
# ignore_value=0,
# )
# and is_ok
# )
#
# print("\nDifference for uncertainty")
# is_ok = (
# utility.assert_images(
# paths.output_ref / "test030-edep_uncertainty.mhd",
# dose.edep_uncertainty.get_output_path(),
# stats,
# tolerance=15,
# ignore_value=1,
# )
# and is_ok
# )
#
# utility.test_ok(is_ok)
35 changes: 35 additions & 0 deletions opengate/tests/src/test080_multiprocessing_1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from opengate.utility import g4_units
import opengate as gate
from opengate.tests.utility import get_default_test_paths


if __name__ == "__main__":
paths = get_default_test_paths(__file__, output_folder="test080")

s = g4_units.s

sim = gate.Simulation()
sim.run_timing_intervals = [[0 * s, 1 * s], [1 * s, 3 * s], [10 * s, 15 * s]]
sim.output_dir = paths.output

box1 = sim.add_volume("BoxVolume", "box1")
box1.add_dynamic_parametrisation(
translation=[[i, i, i] for i in range(len(sim.run_timing_intervals))]
)

n_proc = 4 * len(sim.run_timing_intervals)
run_timing_interval_map = sim.generate_run_timing_interval_map(n_proc)
print(run_timing_interval_map)

output = sim.run(number_of_sub_processes=n_proc)

print("*** output ***")
for o in output:
print(o)

print(f"ID of the main sim: {id(sim)}")

ids = [e[2] for e in output]
assert id(sim) not in ids
Loading