Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
87 changes: 72 additions & 15 deletions opengate/actors/dynamicactors.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,37 @@
from ..decorators import requires_fatal


class DynamicGeometryActor(ActorBase, g4.GateVActor):
class DynamicActorBase(ActorBase, g4.GateVActor):

def __init__(self, *args, **kwargs):
kwargs["attached_to"] = __world_name__
ActorBase.__init__(self, *args, **kwargs)
self.geometry_changers = []
self.changers = []
self.__initcpp__()

def __initcpp__(self):
g4.GateVActor.__init__(self, {"name": self.name})
self.AddActions({"BeginOfRunActionMasterThread"})

def close(self):
for c in self.geometry_changers:
for c in self.changers:
c.close()
self.geometry_changers = []
self.changers = []
super().close()

def to_dictionary(self):
return_dict = super().to_dictionary()
return_dict["geometry_changers"] = dict(
[(v.name, v.to_dictionary()) for v in self.geometry_changers]
return_dict["changers"] = dict(
[(v.name, v.to_dictionary()) for v in self.changers]
)
return return_dict

def initialize(self):
ActorBase.initialize(self)


class DynamicGeometryActor(DynamicActorBase, g4.GateVActor):

def initialize(self):
ActorBase.initialize(self)
for c in self.geometry_changers:
Expand All @@ -53,6 +59,21 @@ def BeginOfRunActionMasterThread(self, run_id):
gm.CloseGeometry(self.simulation.dyn_geom_optimise, False, None)


class DynamicSourceActor(DynamicActorBase, g4.GateVActor):

def initialize(self):
ActorBase.initialize(self)
for c in self.source_changers:
if c.source_manager is None:
c.source_manager = self.simulation.source_manager
c.initialize()

def BeginOfRunActionMasterThread(self, run_id):
# FIXME: check if source engine needs to be informed
for c in self.source_changers:
c.apply_change(run_id)


def _setter_hook_attached_to(self, value):
# try to pick up the simulation from the attached_to volume
try:
Expand All @@ -76,7 +97,7 @@ def _setter_hook_attached_to(self, value):
return value


class GeometryChanger(GateObject):
class ChangerBase(GateObject):

# hints for IDE
attached_to: Optional[str]
Expand Down Expand Up @@ -113,6 +134,19 @@ def __init__(self, *args, **kwargs):
if simulation is not None:
self.simulation = simulation

def initialize(self):
# dummy implementation - nothing to do in the general case
pass

def apply_change(self, run_id):
raise NotImplementedError(
f"You are trying to call the method in the base class {type(self)}, "
f"but it is only available in classes inheriting from it. "
)


class GeometryChanger(ChangerBase):

@property
def volume_manager(self):
if self.simulation is not None:
Expand All @@ -125,15 +159,20 @@ def volume_manager(self):
def attached_to_volume(self):
return self.volume_manager.get_volume(self.attached_to)

def initialize(self):
# dummy implementation - nothing to do in the general case
pass

def apply_change(self, run_id):
raise NotImplementedError(
f"You are trying to call the method in the base class {type(self)}, "
f"but it is only available in classes inheriting from it. "
)
class SourceChanger(ChangerBase):

@property
def source_manager(self):
if self.simulation is not None:
return self.simulation.source_manager
else:
return None

@property
@requires_fatal("volume_manager")
def attached_to_source(self):
return self.source_manager[self.attached_to]


class VolumeImageChanger(GeometryChanger):
Expand Down Expand Up @@ -267,6 +306,24 @@ def apply_change(self, run_id):
self.g4_physical_volume.SetRotationHepRep3x3(self.g4_rotations[run_id])


class SourceActivityImageChanger(SourceChanger):

# hints for IDE
activity_images: Optional[list]

user_info_defaults = {
"activity_images": (
None,
{
"doc": "List of activity map file names corresponding to the run timing intervals. ",
},
),
}

def apply_change(self, run_id):
self.attached_to_source.update_activity_image(self.activity_images[run_id])


process_cls(DynamicGeometryActor)
process_cls(GeometryChanger)
process_cls(VolumeImageChanger)
Expand Down
16 changes: 16 additions & 0 deletions opengate/engines.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ def __init__(self, simulation_engine):
# FIXME: Why is this separate dictionary needed? Would be better to access the source manager directly
self.source_manager_options = Box()

@property
def source_manager(self):
return self.simulation_engine.simulation.source_manager

def close(self):
if self.verbose_close:
warning("Closing SourceEngine")
Expand Down Expand Up @@ -113,6 +117,18 @@ def initialize_actors(self):
self.simulation_engine.simulation.actor_manager.sorted_actors
)

def initialize_dynamic_parametrisations(self):
dynamic_sources = self.source_manager.dynamic_sources
for s in self.source_manager.dynamic_sources:
s.check_if_dynamic_params_match_run_timing_intervals()
if len(dynamic_sources) > 0:
dynamic_source_actor = self.simulation_engine.simulation.add_actor(
"DynamicSourceActor", "dynamic_source_actor"
)
dynamic_source_actor.priority = 1
for s in self.source_manager.dynamic_sources:
dynamic_source_actor.source_changers.extend(s.create_changers())

def create_master_source_manager(self):
# create the master source for the masterThread
self.g4_master_source_manager = self.create_g4_thread_source_manager(
Expand Down
4 changes: 2 additions & 2 deletions opengate/sources/base.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import numpy as np

from ..actors.base import _setter_hook_attached_to
from ..base import GateObject, process_cls
from ..base import GateObject, DynamicGateObject, process_cls
from ..utility import g4_units
from ..definitions import __world_name__
from ..exception import fatal, warning


class SourceBase(GateObject):
class SourceBase(DynamicGateObject):
"""
Base class for all source types.
"""
Expand Down
28 changes: 26 additions & 2 deletions opengate/sources/voxelsources.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
update_image_py_to_cpp,
compute_image_3D_CDF,
)
from ..utility import ensure_filename_is_str
from ..utility import ensure_filename_is_str, warning
from ..base import process_cls
from ..actors.dynamicactors import SourceActivityImageChanger


class VoxelSource(GenericSource, g4.GateVoxelSource):
Expand All @@ -27,6 +28,7 @@ class VoxelSource(GenericSource, g4.GateVoxelSource):
"doc": "Filename of the image of the 3D activity distribution "
"(will be automatically normalized to sum=1)",
"is_input_file": True,
"dynamic": True,
},
)
}
Expand All @@ -40,6 +42,25 @@ def __init__(self, *args, **kwargs):
def __initcpp__(self):
g4.GateVoxelSource.__init__(self)

def create_changers(self):
changers = super().create_changers()
for dp in self.dynamic_params.values():
if dp["extra_params"]["auto_changer"] is True:
if "image" in dp:
new_changer = SourceActivityImageChanger(
name=f"{self.name}_source_activity_changer_{len(changers)}",
activity_images=dp["image"],
attached_to=self,
simulation=self.simulation,
)
changers.append(new_changer)
else:
self.warning(
f"You need to manually create a changer for dynamic parametrisation {dp} "
f"of source '{self.name}'."
)
return changers

def set_transform_from_user_info(self):
# get source image information
src_info = read_image_info(str(self.image))
Expand Down Expand Up @@ -68,7 +89,7 @@ def cumulative_distribution_functions(self):
pg = self.GetSPSVoxelPosDistribution()
pg.SetCumulativeDistributionFunction(cdf_z, cdf_y, cdf_x)

def initialize(self, run_timing_intervals):
def update_activity_image(self, filename):
# read source image
self.itk_image = itk.imread(ensure_filename_is_str(self.image))

Expand All @@ -78,6 +99,9 @@ def initialize(self, run_timing_intervals):
# create Cumulative Distribution Function
self.cumulative_distribution_functions()

def initialize(self, run_timing_intervals):
self.update_activity_image(self.image)

# FIXME -> check other option in position not used here

# initialise standard options (particle energy, etc.)
Expand Down
Loading