From 812b4620a7509850507c82b1fab914dd3b8f4033 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Fri, 19 Jul 2024 14:54:09 +0200 Subject: [PATCH 1/5] Refactored (base) classes from init.py into separate files --- simpa/core/__init__.py | 30 +--- simpa/core/device_digital_twins/__init__.py | 170 ++---------------- .../detection_geometries/__init__.py | 133 +------------- .../detection_geometry_base.py | 136 ++++++++++++++ .../digital_device_twin_base.py | 132 ++++++++++++++ .../illumination_geometries/__init__.py | 67 +------ .../illumination_geometry_base.py | 70 ++++++++ .../pa_devices/__init__.py | 158 +--------------- .../pa_devices/photoacoustic_device.py | 161 +++++++++++++++++ simpa/core/pipeline_module.py | 33 ++++ simpa/core/processing_components/__init__.py | 19 +- .../multispectral/__init__.py | 55 +----- .../multispectral_processing_algorithm.py | 57 ++++++ .../processing_component.py | 21 +++ simpa/core/simulation_modules/__init__.py | 28 +-- .../acoustic_forward_module/__init__.py | 82 +-------- .../acoustic_forward_model_base_adapter.py | 84 +++++++++ .../optical_simulation_module/__init__.py | 155 +--------------- .../optical_forward_model_base_adapter.py | 157 ++++++++++++++++ .../reconstruction_module/__init__.py | 93 +--------- .../reconstruction_module_base_adapter.py | 95 ++++++++++ .../simulation_modules/simulation_module.py | 32 ++++ .../volume_creation_module/__init__.py | 75 +------- .../volume_creator_base_adapter.py | 78 ++++++++ 24 files changed, 1087 insertions(+), 1034 deletions(-) create mode 100644 simpa/core/device_digital_twins/detection_geometries/detection_geometry_base.py create mode 100644 simpa/core/device_digital_twins/digital_device_twin_base.py create mode 100644 simpa/core/device_digital_twins/illumination_geometries/illumination_geometry_base.py create mode 100644 simpa/core/device_digital_twins/pa_devices/photoacoustic_device.py create mode 100644 simpa/core/pipeline_module.py create mode 100644 simpa/core/processing_components/multispectral/multispectral_processing_algorithm.py create mode 100644 simpa/core/processing_components/processing_component.py create mode 100644 simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_base_adapter.py create mode 100644 simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_base_adapter.py create mode 100644 simpa/core/simulation_modules/reconstruction_module/reconstruction_module_base_adapter.py create mode 100644 simpa/core/simulation_modules/simulation_module.py create mode 100644 simpa/core/simulation_modules/volume_creation_module/volume_creator_base_adapter.py diff --git a/simpa/core/__init__.py b/simpa/core/__init__.py index 8653966c..dd267601 100644 --- a/simpa/core/__init__.py +++ b/simpa/core/__init__.py @@ -1,32 +1,4 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from abc import abstractmethod - -from simpa.core.device_digital_twins import DigitalDeviceTwinBase -from simpa.log import Logger -from simpa.utils import Settings -from simpa.utils.processing_device import get_processing_device - -class PipelineModule: - """ - Defines a pipeline module (either simulation or processing module) that implements a run method and can be called by running the pipeline's simulate method. - """ - def __init__(self, global_settings: Settings): - """ - :param global_settings: The SIMPA settings dictionary - :type global_settings: Settings - """ - self.logger = Logger() - self.global_settings = global_settings - self.torch_device = get_processing_device(self.global_settings) - - @abstractmethod - def run(self, digital_device_twin: DigitalDeviceTwinBase): - """ - Executes the respective simulation module - - :param digital_device_twin: The digital twin that can be used by the digital device_twin. - """ - pass - \ No newline at end of file +from .pipeline_module import PipelineModule diff --git a/simpa/core/device_digital_twins/__init__.py b/simpa/core/device_digital_twins/__init__.py index 13bd5bcf..0c7de4d2 100644 --- a/simpa/core/device_digital_twins/__init__.py +++ b/simpa/core/device_digital_twins/__init__.py @@ -2,154 +2,22 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from abc import abstractmethod -from simpa.log import Logger -import numpy as np -import hashlib -import uuid -from simpa.utils.serializer import SerializableSIMPAClass -from simpa.utils.calculate import are_equal - - -class DigitalDeviceTwinBase(SerializableSIMPAClass): - """ - This class represents a device that can be used for illumination, detection or a combined photoacoustic device - which has representations of both. - """ - - def __init__(self, device_position_mm=None, field_of_view_extent_mm=None): - """ - :param device_position_mm: Each device has an internal position which serves as origin for internal \ - representations of e.g. detector element positions or illuminator positions. - :type device_position_mm: ndarray - :param field_of_view_extent_mm: Field of view which is defined as a numpy array of the shape \ - [xs, xe, ys, ye, zs, ze], where x, y, and z denote the coordinate axes and s and e denote the start and end \ - positions. - :type field_of_view_extent_mm: ndarray - """ - if device_position_mm is None: - self.device_position_mm = np.array([0, 0, 0]) - else: - self.device_position_mm = device_position_mm - - if field_of_view_extent_mm is None: - self.field_of_view_extent_mm = np.asarray([-10, 10, -10, 10, -10, 10]) - else: - self.field_of_view_extent_mm = field_of_view_extent_mm - - self.logger = Logger() - - def __eq__(self, other): - """ - Checks each key, value pair in the devices. - """ - if isinstance(other, DigitalDeviceTwinBase): - if self.__dict__.keys() != other.__dict__.keys(): - return False - for self_key, self_value in self.__dict__.items(): - other_value = other.__dict__[self_key] - if not are_equal(self_value, other_value): - return False - return True - return False - - @abstractmethod - def check_settings_prerequisites(self, global_settings) -> bool: - """ - It might be that certain device geometries need a certain dimensionality of the simulated PAI volume, or that - it requires the existence of certain Tags in the global global_settings. - To this end, a PAI device should use this method to inform the user about a mismatch of the desired device and - throw a ValueError if that is the case. - - :param global_settings: Settings for the entire simulation pipeline. - :type global_settings: Settings - - :raises ValueError: raises a value error if the prerequisites are not matched. - :returns: True if the prerequisites are met, False if they are not met, but no exception has been raised. - :rtype: bool - - """ - pass - - @abstractmethod - def update_settings_for_use_of_model_based_volume_creator(self, global_settings): - """ - This method can be overwritten by a PA device if the device poses special constraints to the - volume that should be considered by the model-based volume creator. - - :param global_settings: Settings for the entire simulation pipeline. - :type global_settings: Settings - """ - pass - - def get_field_of_view_mm(self) -> np.ndarray: - """ - Returns the absolute field of view in mm where the probe position is already - accounted for. - It is defined as a numpy array of the shape [xs, xe, ys, ye, zs, ze], - where x, y, and z denote the coordinate axes and s and e denote the start and end - positions. - - :return: Absolute field of view in mm where the probe position is already accounted for. - :rtype: ndarray - """ - position = self.device_position_mm - field_of_view_extent = self.field_of_view_extent_mm - - field_of_view = np.asarray([position[0] + field_of_view_extent[0], - position[0] + field_of_view_extent[1], - position[1] + field_of_view_extent[2], - position[1] + field_of_view_extent[3], - position[2] + field_of_view_extent[4], - position[2] + field_of_view_extent[5] - ]) - if min(field_of_view) < 0: - self.logger.warning(f"The field of view of the chosen device is not fully within the simulated volume, " - f"field of view is: {field_of_view}") - field_of_view[field_of_view < 0] = 0 - - return field_of_view - - def generate_uuid(self): - """ - Generates a universally unique identifier (uuid) for each device. - :return: - """ - class_dict = self.__dict__ - m = hashlib.md5() - m.update(str(class_dict).encode('utf-8')) - return str(uuid.UUID(m.hexdigest())) - - def serialize(self) -> dict: - serialized_device = self.__dict__ - return {"DigitalDeviceTwinBase": serialized_device} - - @staticmethod - def deserialize(dictionary_to_deserialize): - deserialized_device = DigitalDeviceTwinBase( - device_position_mm=dictionary_to_deserialize["device_position_mm"], - field_of_view_extent_mm=dictionary_to_deserialize["field_of_view_extent_mm"]) - return deserialized_device - - -""" -It is important to have these relative imports after the definition of the DigitalDeviceTwinBase class to avoid circular imports triggered by imported child classes -""" -from .pa_devices import PhotoacousticDevice # nopep8 -from simpa.core.device_digital_twins.detection_geometries import DetectionGeometryBase # nopep8 -from simpa.core.device_digital_twins.illumination_geometries import IlluminationGeometryBase # nopep8 -from .detection_geometries.curved_array import CurvedArrayDetectionGeometry # nopep8 -from .detection_geometries.linear_array import LinearArrayDetectionGeometry # nopep8 -from .detection_geometries.planar_array import PlanarArrayDetectionGeometry # nopep8 -from .illumination_geometries.slit_illumination import SlitIlluminationGeometry # nopep8 -from .illumination_geometries.gaussian_beam_illumination import GaussianBeamIlluminationGeometry # nopep8 -from .illumination_geometries.pencil_array_illumination import PencilArrayIlluminationGeometry # nopep8 -from .illumination_geometries.pencil_beam_illumination import PencilBeamIlluminationGeometry # nopep8 -from .illumination_geometries.disk_illumination import DiskIlluminationGeometry # nopep8 -from .illumination_geometries.rectangle_illumination import RectangleIlluminationGeometry # nopep8 -from .illumination_geometries.ring_illumination import RingIlluminationGeometry # nopep8 -from .illumination_geometries.ithera_msot_acuity_illumination import MSOTAcuityIlluminationGeometry # nopep8 -from .illumination_geometries.ithera_msot_invision_illumination import MSOTInVisionIlluminationGeometry # nopep8 -from .pa_devices.ithera_msot_invision import InVision256TF # nopep8 -from .pa_devices.ithera_msot_acuity import MSOTAcuityEcho # nopep8 -from .pa_devices.ithera_rsom import RSOMExplorerP50 # nopep8 +from .digital_device_twin_base import DigitalDeviceTwinBase +from .pa_devices import PhotoacousticDevice +from .detection_geometries import DetectionGeometryBase +from .detection_geometries.curved_array import CurvedArrayDetectionGeometry +from .detection_geometries.linear_array import LinearArrayDetectionGeometry +from .detection_geometries.planar_array import PlanarArrayDetectionGeometry +from .illumination_geometries import IlluminationGeometryBase +from .illumination_geometries.slit_illumination import SlitIlluminationGeometry +from .illumination_geometries.gaussian_beam_illumination import GaussianBeamIlluminationGeometry +from .illumination_geometries.pencil_array_illumination import PencilArrayIlluminationGeometry +from .illumination_geometries.pencil_beam_illumination import PencilBeamIlluminationGeometry +from .illumination_geometries.disk_illumination import DiskIlluminationGeometry +from .illumination_geometries.rectangle_illumination import RectangleIlluminationGeometry +from .illumination_geometries.ring_illumination import RingIlluminationGeometry +from .illumination_geometries.ithera_msot_acuity_illumination import MSOTAcuityIlluminationGeometry +from .illumination_geometries.ithera_msot_invision_illumination import MSOTInVisionIlluminationGeometry +from .pa_devices.ithera_msot_invision import InVision256TF +from .pa_devices.ithera_msot_acuity import MSOTAcuityEcho +from .pa_devices.ithera_rsom import RSOMExplorerP50 diff --git a/simpa/core/device_digital_twins/detection_geometries/__init__.py b/simpa/core/device_digital_twins/detection_geometries/__init__.py index eba6bdd5..e8d6c131 100644 --- a/simpa/core/device_digital_twins/detection_geometries/__init__.py +++ b/simpa/core/device_digital_twins/detection_geometries/__init__.py @@ -2,135 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from abc import abstractmethod -from simpa.core.device_digital_twins import DigitalDeviceTwinBase -import numpy as np - - -class DetectionGeometryBase(DigitalDeviceTwinBase): - """ - This class is the base class for representing all detector geometries. - """ - - def __init__(self, number_detector_elements, detector_element_width_mm, - detector_element_length_mm, center_frequency_hz, bandwidth_percent, - sampling_frequency_mhz, device_position_mm: np.ndarray = None, - field_of_view_extent_mm: np.ndarray = None): - """ - - :param number_detector_elements: Total number of detector elements. - :type number_detector_elements: int - :param detector_element_width_mm: In-plane width of one detector element (pitch - distance between two - elements) in mm. - :type detector_element_width_mm: int, float - :param detector_element_length_mm: Out-of-plane length of one detector element in mm. - :type detector_element_length_mm: int, float - :param center_frequency_hz: Center frequency of the detector with approximately gaussian frequency response in - Hz. - :type center_frequency_hz: int, float - :param bandwidth_percent: Full width at half maximum in percent of the center frequency. - :type bandwidth_percent: int, float - :param sampling_frequency_mhz: Sampling frequency of the detector in MHz. - :type sampling_frequency_mhz: int, float - :param device_position_mm: Each device has an internal position which serves as origin for internal \ - representations of detector positions. - :type device_position_mm: ndarray - :param field_of_view_extent_mm: Field of view which is defined as a numpy array of the shape \ - [xs, xe, ys, ye, zs, ze], where x, y, and z denote the coordinate axes and s and e denote the start and end \ - positions. - :type field_of_view_extent_mm: ndarray - """ - super(DetectionGeometryBase, self).__init__(device_position_mm=device_position_mm, - field_of_view_extent_mm=field_of_view_extent_mm) - self.number_detector_elements = number_detector_elements - self.detector_element_width_mm = detector_element_width_mm - self.detector_element_length_mm = detector_element_length_mm - self.center_frequency_Hz = center_frequency_hz - self.bandwidth_percent = bandwidth_percent - self.sampling_frequency_MHz = sampling_frequency_mhz - - @abstractmethod - def get_detector_element_positions_base_mm(self) -> np.ndarray: - """ - Defines the abstract positions of the detection elements in an arbitrary coordinate system. - Typically, the center of the field of view is defined as the origin. - - To obtain the positions in an interpretable coordinate system, please use the other method:: - - get_detector_element_positions_accounting_for_device_position_mm - - :returns: A numpy array containing the position vectors of the detection elements. - - """ - pass - - def get_detector_element_positions_accounting_for_device_position_mm(self) -> np.ndarray: - """ - Similar to:: - - get_detector_element_positions_base_mm - - This method returns the absolute positions of the detection elements relative to the device - position in the imaged volume, where the device position is defined by the following tag:: - - Tags.DIGITAL_DEVICE_POSITION - - :returns: A numpy array containing the coordinates of the detection elements - - """ - abstract_element_positions = self.get_detector_element_positions_base_mm() - device_position = self.device_position_mm - return np.add(abstract_element_positions, device_position) - - def get_detector_element_positions_accounting_for_field_of_view(self) -> np.ndarray: - """ - Similar to:: - - get_detector_element_positions_base_mm - - This method returns the absolute positions of the detection elements relative to the device - position in the imaged volume, where the device position is defined by the following tag:: - - Tags.DIGITAL_DEVICE_POSITION - - :returns: A numpy array containing the coordinates of the detection elements - - """ - abstract_element_positions = np.copy(self.get_detector_element_positions_base_mm()) - field_of_view = self.field_of_view_extent_mm - x_half = (field_of_view[1] - field_of_view[0]) / 2 - y_half = (field_of_view[3] - field_of_view[2]) / 2 - if np.abs(x_half) < 1e-10: - abstract_element_positions[:, 0] = 0 - if np.abs(y_half) < 1e-10: - abstract_element_positions[:, 1] = 0 - - abstract_element_positions[:, 0] += x_half - abstract_element_positions[:, 1] += y_half - abstract_element_positions[:, 2] += field_of_view[4] - return abstract_element_positions - - @abstractmethod - def get_detector_element_orientations(self) -> np.ndarray: - """ - This method yields a normalised orientation vector for each detection element. The length of - this vector is the same as the one obtained via the position methods:: - - get_detector_element_positions_base_mm - get_detector_element_positions_accounting_for_device_position_mm - - :returns: a numpy array that contains normalised orientation vectors for each detection element - - """ - pass - - def serialize(self) -> dict: - serialized_device = self.__dict__ - return {DetectionGeometryBase: serialized_device} - - @staticmethod - def deserialize(dictionary_to_deserialize): - deserialized_device = DetectionGeometryBase() - for key, value in dictionary_to_deserialize.items(): - deserialized_device.__dict__[key] = value - return deserialized_device +from .detection_geometry_base import DetectionGeometryBase diff --git a/simpa/core/device_digital_twins/detection_geometries/detection_geometry_base.py b/simpa/core/device_digital_twins/detection_geometries/detection_geometry_base.py new file mode 100644 index 00000000..eba6bdd5 --- /dev/null +++ b/simpa/core/device_digital_twins/detection_geometries/detection_geometry_base.py @@ -0,0 +1,136 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +from abc import abstractmethod +from simpa.core.device_digital_twins import DigitalDeviceTwinBase +import numpy as np + + +class DetectionGeometryBase(DigitalDeviceTwinBase): + """ + This class is the base class for representing all detector geometries. + """ + + def __init__(self, number_detector_elements, detector_element_width_mm, + detector_element_length_mm, center_frequency_hz, bandwidth_percent, + sampling_frequency_mhz, device_position_mm: np.ndarray = None, + field_of_view_extent_mm: np.ndarray = None): + """ + + :param number_detector_elements: Total number of detector elements. + :type number_detector_elements: int + :param detector_element_width_mm: In-plane width of one detector element (pitch - distance between two + elements) in mm. + :type detector_element_width_mm: int, float + :param detector_element_length_mm: Out-of-plane length of one detector element in mm. + :type detector_element_length_mm: int, float + :param center_frequency_hz: Center frequency of the detector with approximately gaussian frequency response in + Hz. + :type center_frequency_hz: int, float + :param bandwidth_percent: Full width at half maximum in percent of the center frequency. + :type bandwidth_percent: int, float + :param sampling_frequency_mhz: Sampling frequency of the detector in MHz. + :type sampling_frequency_mhz: int, float + :param device_position_mm: Each device has an internal position which serves as origin for internal \ + representations of detector positions. + :type device_position_mm: ndarray + :param field_of_view_extent_mm: Field of view which is defined as a numpy array of the shape \ + [xs, xe, ys, ye, zs, ze], where x, y, and z denote the coordinate axes and s and e denote the start and end \ + positions. + :type field_of_view_extent_mm: ndarray + """ + super(DetectionGeometryBase, self).__init__(device_position_mm=device_position_mm, + field_of_view_extent_mm=field_of_view_extent_mm) + self.number_detector_elements = number_detector_elements + self.detector_element_width_mm = detector_element_width_mm + self.detector_element_length_mm = detector_element_length_mm + self.center_frequency_Hz = center_frequency_hz + self.bandwidth_percent = bandwidth_percent + self.sampling_frequency_MHz = sampling_frequency_mhz + + @abstractmethod + def get_detector_element_positions_base_mm(self) -> np.ndarray: + """ + Defines the abstract positions of the detection elements in an arbitrary coordinate system. + Typically, the center of the field of view is defined as the origin. + + To obtain the positions in an interpretable coordinate system, please use the other method:: + + get_detector_element_positions_accounting_for_device_position_mm + + :returns: A numpy array containing the position vectors of the detection elements. + + """ + pass + + def get_detector_element_positions_accounting_for_device_position_mm(self) -> np.ndarray: + """ + Similar to:: + + get_detector_element_positions_base_mm + + This method returns the absolute positions of the detection elements relative to the device + position in the imaged volume, where the device position is defined by the following tag:: + + Tags.DIGITAL_DEVICE_POSITION + + :returns: A numpy array containing the coordinates of the detection elements + + """ + abstract_element_positions = self.get_detector_element_positions_base_mm() + device_position = self.device_position_mm + return np.add(abstract_element_positions, device_position) + + def get_detector_element_positions_accounting_for_field_of_view(self) -> np.ndarray: + """ + Similar to:: + + get_detector_element_positions_base_mm + + This method returns the absolute positions of the detection elements relative to the device + position in the imaged volume, where the device position is defined by the following tag:: + + Tags.DIGITAL_DEVICE_POSITION + + :returns: A numpy array containing the coordinates of the detection elements + + """ + abstract_element_positions = np.copy(self.get_detector_element_positions_base_mm()) + field_of_view = self.field_of_view_extent_mm + x_half = (field_of_view[1] - field_of_view[0]) / 2 + y_half = (field_of_view[3] - field_of_view[2]) / 2 + if np.abs(x_half) < 1e-10: + abstract_element_positions[:, 0] = 0 + if np.abs(y_half) < 1e-10: + abstract_element_positions[:, 1] = 0 + + abstract_element_positions[:, 0] += x_half + abstract_element_positions[:, 1] += y_half + abstract_element_positions[:, 2] += field_of_view[4] + return abstract_element_positions + + @abstractmethod + def get_detector_element_orientations(self) -> np.ndarray: + """ + This method yields a normalised orientation vector for each detection element. The length of + this vector is the same as the one obtained via the position methods:: + + get_detector_element_positions_base_mm + get_detector_element_positions_accounting_for_device_position_mm + + :returns: a numpy array that contains normalised orientation vectors for each detection element + + """ + pass + + def serialize(self) -> dict: + serialized_device = self.__dict__ + return {DetectionGeometryBase: serialized_device} + + @staticmethod + def deserialize(dictionary_to_deserialize): + deserialized_device = DetectionGeometryBase() + for key, value in dictionary_to_deserialize.items(): + deserialized_device.__dict__[key] = value + return deserialized_device diff --git a/simpa/core/device_digital_twins/digital_device_twin_base.py b/simpa/core/device_digital_twins/digital_device_twin_base.py new file mode 100644 index 00000000..478361a0 --- /dev/null +++ b/simpa/core/device_digital_twins/digital_device_twin_base.py @@ -0,0 +1,132 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +from abc import abstractmethod +from simpa.log import Logger +import numpy as np +import hashlib +import uuid +from simpa.utils.serializer import SerializableSIMPAClass +from simpa.utils.calculate import are_equal + + +class DigitalDeviceTwinBase(SerializableSIMPAClass): + """ + This class represents a device that can be used for illumination, detection or a combined photoacoustic device + which has representations of both. + """ + + def __init__(self, device_position_mm=None, field_of_view_extent_mm=None): + """ + :param device_position_mm: Each device has an internal position which serves as origin for internal \ + representations of e.g. detector element positions or illuminator positions. + :type device_position_mm: ndarray + :param field_of_view_extent_mm: Field of view which is defined as a numpy array of the shape \ + [xs, xe, ys, ye, zs, ze], where x, y, and z denote the coordinate axes and s and e denote the start and end \ + positions. + :type field_of_view_extent_mm: ndarray + """ + if device_position_mm is None: + self.device_position_mm = np.array([0, 0, 0]) + else: + self.device_position_mm = device_position_mm + + if field_of_view_extent_mm is None: + self.field_of_view_extent_mm = np.asarray([-10, 10, -10, 10, -10, 10]) + else: + self.field_of_view_extent_mm = field_of_view_extent_mm + + self.logger = Logger() + + def __eq__(self, other): + """ + Checks each key, value pair in the devices. + """ + if isinstance(other, DigitalDeviceTwinBase): + if self.__dict__.keys() != other.__dict__.keys(): + return False + for self_key, self_value in self.__dict__.items(): + other_value = other.__dict__[self_key] + if not are_equal(self_value, other_value): + return False + return True + return False + + @abstractmethod + def check_settings_prerequisites(self, global_settings) -> bool: + """ + It might be that certain device geometries need a certain dimensionality of the simulated PAI volume, or that + it requires the existence of certain Tags in the global global_settings. + To this end, a PAI device should use this method to inform the user about a mismatch of the desired device and + throw a ValueError if that is the case. + + :param global_settings: Settings for the entire simulation pipeline. + :type global_settings: Settings + + :raises ValueError: raises a value error if the prerequisites are not matched. + :returns: True if the prerequisites are met, False if they are not met, but no exception has been raised. + :rtype: bool + + """ + pass + + @abstractmethod + def update_settings_for_use_of_model_based_volume_creator(self, global_settings): + """ + This method can be overwritten by a PA device if the device poses special constraints to the + volume that should be considered by the model-based volume creator. + + :param global_settings: Settings for the entire simulation pipeline. + :type global_settings: Settings + """ + pass + + def get_field_of_view_mm(self) -> np.ndarray: + """ + Returns the absolute field of view in mm where the probe position is already + accounted for. + It is defined as a numpy array of the shape [xs, xe, ys, ye, zs, ze], + where x, y, and z denote the coordinate axes and s and e denote the start and end + positions. + + :return: Absolute field of view in mm where the probe position is already accounted for. + :rtype: ndarray + """ + position = self.device_position_mm + field_of_view_extent = self.field_of_view_extent_mm + + field_of_view = np.asarray([position[0] + field_of_view_extent[0], + position[0] + field_of_view_extent[1], + position[1] + field_of_view_extent[2], + position[1] + field_of_view_extent[3], + position[2] + field_of_view_extent[4], + position[2] + field_of_view_extent[5] + ]) + if min(field_of_view) < 0: + self.logger.warning(f"The field of view of the chosen device is not fully within the simulated volume, " + f"field of view is: {field_of_view}") + field_of_view[field_of_view < 0] = 0 + + return field_of_view + + def generate_uuid(self): + """ + Generates a universally unique identifier (uuid) for each device. + :return: + """ + class_dict = self.__dict__ + m = hashlib.md5() + m.update(str(class_dict).encode('utf-8')) + return str(uuid.UUID(m.hexdigest())) + + def serialize(self) -> dict: + serialized_device = self.__dict__ + return {"DigitalDeviceTwinBase": serialized_device} + + @staticmethod + def deserialize(dictionary_to_deserialize): + deserialized_device = DigitalDeviceTwinBase( + device_position_mm=dictionary_to_deserialize["device_position_mm"], + field_of_view_extent_mm=dictionary_to_deserialize["field_of_view_extent_mm"]) + return deserialized_device diff --git a/simpa/core/device_digital_twins/illumination_geometries/__init__.py b/simpa/core/device_digital_twins/illumination_geometries/__init__.py index 6c5780bf..cd10d903 100644 --- a/simpa/core/device_digital_twins/illumination_geometries/__init__.py +++ b/simpa/core/device_digital_twins/illumination_geometries/__init__.py @@ -2,69 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from abc import abstractmethod -from simpa.core.device_digital_twins import DigitalDeviceTwinBase -from simpa.utils import Settings -import numpy as np - - -class IlluminationGeometryBase(DigitalDeviceTwinBase): - """ - This class is the base class for representing all illumination geometries. - """ - - def __init__(self, device_position_mm=None, source_direction_vector=None, field_of_view_extent_mm=None): - """ - :param device_position_mm: Each device has an internal position which serves as origin for internal \ - representations of illuminator positions. - :type device_position_mm: ndarray - - :param source_direction_vector: Direction of the illumination source. - :type source_direction_vector: ndarray - - :param field_of_view_extent_mm: Field of view which is defined as a numpy array of the shape \ - [xs, xe, ys, ye, zs, ze], where x, y, and z denote the coordinate axes and s and e denote the start and end \ - positions. - :type field_of_view_extent_mm: ndarray - """ - super(IlluminationGeometryBase, self).__init__(device_position_mm=device_position_mm, - field_of_view_extent_mm=field_of_view_extent_mm) - - if source_direction_vector is None: - self.source_direction_vector = [0, 0, 1] - else: - self.source_direction_vector = source_direction_vector - self.normalized_source_direction_vector = self.source_direction_vector / np.linalg.norm( - self.source_direction_vector) - - @abstractmethod - def get_mcx_illuminator_definition(self, global_settings) -> dict: - """ - IMPORTANT: This method creates a dictionary that contains tags as they are expected for the - mcx simulation tool to represent the illumination geometry of this device. - - :param global_settings: The global_settings instance containing the simulation instructions. - :type global_settings: Settings - - :return: Dictionary that includes all parameters needed for mcx. - :rtype: dict - """ - pass - - def check_settings_prerequisites(self, global_settings) -> bool: - return True - - def update_settings_for_use_of_model_based_volume_creator(self, global_settings) -> Settings: - return global_settings - - def serialize(self) -> dict: - serialized_device = self.__dict__ - device_dict = {"IlluminationGeometryBase": serialized_device} - return device_dict - - @staticmethod - def deserialize(dictionary_to_deserialize): - deserialized_device = IlluminationGeometryBase() - for key, value in dictionary_to_deserialize.items(): - deserialized_device.__dict__[key] = value - return deserialized_device +from .illumination_geometry_base import IlluminationGeometryBase diff --git a/simpa/core/device_digital_twins/illumination_geometries/illumination_geometry_base.py b/simpa/core/device_digital_twins/illumination_geometries/illumination_geometry_base.py new file mode 100644 index 00000000..6c5780bf --- /dev/null +++ b/simpa/core/device_digital_twins/illumination_geometries/illumination_geometry_base.py @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +from abc import abstractmethod +from simpa.core.device_digital_twins import DigitalDeviceTwinBase +from simpa.utils import Settings +import numpy as np + + +class IlluminationGeometryBase(DigitalDeviceTwinBase): + """ + This class is the base class for representing all illumination geometries. + """ + + def __init__(self, device_position_mm=None, source_direction_vector=None, field_of_view_extent_mm=None): + """ + :param device_position_mm: Each device has an internal position which serves as origin for internal \ + representations of illuminator positions. + :type device_position_mm: ndarray + + :param source_direction_vector: Direction of the illumination source. + :type source_direction_vector: ndarray + + :param field_of_view_extent_mm: Field of view which is defined as a numpy array of the shape \ + [xs, xe, ys, ye, zs, ze], where x, y, and z denote the coordinate axes and s and e denote the start and end \ + positions. + :type field_of_view_extent_mm: ndarray + """ + super(IlluminationGeometryBase, self).__init__(device_position_mm=device_position_mm, + field_of_view_extent_mm=field_of_view_extent_mm) + + if source_direction_vector is None: + self.source_direction_vector = [0, 0, 1] + else: + self.source_direction_vector = source_direction_vector + self.normalized_source_direction_vector = self.source_direction_vector / np.linalg.norm( + self.source_direction_vector) + + @abstractmethod + def get_mcx_illuminator_definition(self, global_settings) -> dict: + """ + IMPORTANT: This method creates a dictionary that contains tags as they are expected for the + mcx simulation tool to represent the illumination geometry of this device. + + :param global_settings: The global_settings instance containing the simulation instructions. + :type global_settings: Settings + + :return: Dictionary that includes all parameters needed for mcx. + :rtype: dict + """ + pass + + def check_settings_prerequisites(self, global_settings) -> bool: + return True + + def update_settings_for_use_of_model_based_volume_creator(self, global_settings) -> Settings: + return global_settings + + def serialize(self) -> dict: + serialized_device = self.__dict__ + device_dict = {"IlluminationGeometryBase": serialized_device} + return device_dict + + @staticmethod + def deserialize(dictionary_to_deserialize): + deserialized_device = IlluminationGeometryBase() + for key, value in dictionary_to_deserialize.items(): + deserialized_device.__dict__[key] = value + return deserialized_device diff --git a/simpa/core/device_digital_twins/pa_devices/__init__.py b/simpa/core/device_digital_twins/pa_devices/__init__.py index 38b0d202..7b8845f7 100644 --- a/simpa/core/device_digital_twins/pa_devices/__init__.py +++ b/simpa/core/device_digital_twins/pa_devices/__init__.py @@ -2,160 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -import numpy as np -from abc import ABC -from simpa.core.device_digital_twins import DigitalDeviceTwinBase - - -class PhotoacousticDevice(DigitalDeviceTwinBase, ABC): - """Base class of a photoacoustic device. It consists of one detection geometry that describes the geometry of the - single detector elements and a list of illuminators. - - A Photoacoustic Device can be initialized as follows:: - - import simpa as sp - import numpy as np - - # Initialise a PhotoacousticDevice with its position and field of view - device = sp.PhotoacousticDevice(device_position_mm=np.array([10, 10, 0]), - field_of_view_extent_mm=np.array([-20, 20, 0, 0, 0, 20])) - - # Option 1) Set the detection geometry position relative to the PhotoacousticDevice - device.set_detection_geometry(sp.DetectionGeometry(), - detector_position_relative_to_pa_device=np.array([0, 0, -10])) - - # Option 2) Set the detection geometry position absolute - device.set_detection_geometry( - sp.DetectionGeometryBase(device_position_mm=np.array([10, 10, -10]))) - - # Option 1) Add the illumination geometry position relative to the PhotoacousticDevice - device.add_illumination_geometry(sp.IlluminationGeometry(), - illuminator_position_relative_to_pa_device=np.array([0, 0, 0])) - - # Option 2) Add the illumination geometry position absolute - device.add_illumination_geometry( - sp.IlluminationGeometryBase(device_position_mm=np.array([10, 10, 0])) - - Attributes: - detection_geometry (DetectionGeometryBase): Geometry of the detector elements. - illumination_geometries (list): List of illuminations defined by :py:class:`IlluminationGeometryBase`. - """ - - def __init__(self, device_position_mm=None, field_of_view_extent_mm=None): - """ - :param device_position_mm: Each device has an internal position which serves as origin for internal \ - representations of e.g. detector element positions or illuminator positions. - :type device_position_mm: ndarray - :param field_of_view_extent_mm: Field of view which is defined as a numpy array of the shape \ - [xs, xe, ys, ye, zs, ze], where x, y, and z denote the coordinate axes and s and e denote the start and end \ - positions. - :type field_of_view_extent_mm: ndarray - """ - super(PhotoacousticDevice, self).__init__(device_position_mm=device_position_mm, - field_of_view_extent_mm=field_of_view_extent_mm) - self.detection_geometry = None - self.illumination_geometries = [] - - def set_detection_geometry(self, detection_geometry, - detector_position_relative_to_pa_device=None): - """Sets the detection geometry for the PA device. The detection geometry can be instantiated with an absolute - position or it can be instantiated without the device_position_mm argument but a position relative to the - position of the PhotoacousticDevice. If both absolute and relative positions are given, the absolute position - is chosen as position of the detection geometry. - - :param detection_geometry: Detection geometry of the PA device. - :type detection_geometry: DetectionGeometryBase - :param detector_position_relative_to_pa_device: Position of the detection geometry relative to the PA device. - :type detector_position_relative_to_pa_device: ndarray - :raises ValueError: if the detection_geometry is None - - """ - if detection_geometry is None: - msg = "The given detection_geometry must not be None!" - self.logger.critical(msg) - raise ValueError(msg) - if np.linalg.norm(detection_geometry.device_position_mm) == 0 and \ - detector_position_relative_to_pa_device is not None: - detection_geometry.device_position_mm = np.add(self.device_position_mm, - detector_position_relative_to_pa_device) - self.detection_geometry = detection_geometry - - def add_illumination_geometry(self, illumination_geometry, illuminator_position_relative_to_pa_device=None): - """Adds an illuminator to the PA device. The illumination geometry can be instantiated with an absolute - position or it can be instantiated without the device_position_mm argument but a position relative to the - position of the PhotoacousticDevice. If both absolute and relative positions are given, the absolute position - is chosen as position of the illumination geometry. - - :param illumination_geometry: Geometry of the illuminator. - :type illumination_geometry: IlluminationGeometryBase - :param illuminator_position_relative_to_pa_device: Position of the illuminator relative to the PA device. - :type illuminator_position_relative_to_pa_device: ndarray - :raises ValueError: if the illumination_geometry is None - - """ - if illumination_geometry is None: - msg = "The given illumination_geometry must not be None!" - self.logger.critical(msg) - raise ValueError(msg) - if np.linalg.norm(illumination_geometry.device_position_mm) == 0: - if illuminator_position_relative_to_pa_device is not None: - illumination_geometry.device_position_mm = np.add(self.device_position_mm, - illuminator_position_relative_to_pa_device) - else: - illumination_geometry.device_position_mm = self.device_position_mm - self.illumination_geometries.append(illumination_geometry) - - def get_detection_geometry(self): - """ - :return: None if no detection geometry was set or an instance of DetectionGeometryBase. - :rtype: None, DetectionGeometryBase - """ - return self.detection_geometry - - def get_illumination_geometry(self): - """ - :return: None, if no illumination geometry was defined, - an instance of IlluminationGeometryBase if exactly one geometry was defined, - a list of IlluminationGeometryBase instances if more than one device was defined. - :rtype: None, IlluminationGeometryBase - """ - if len(self.illumination_geometries) == 0: - return None - - if len(self.illumination_geometries) == 1: - return self.illumination_geometries[0] - - return self.illumination_geometries - - def check_settings_prerequisites(self, global_settings) -> bool: - _result = True - if self.detection_geometry is not None \ - and not self.detection_geometry.check_settings_prerequisites(global_settings): - _result = False - for illumination_geometry in self.illumination_geometries: - if illumination_geometry is not None \ - and not illumination_geometry.check_settings_prerequisites(global_settings): - _result = False - return _result - - def update_settings_for_use_of_model_based_volume_creator(self, global_settings): - pass - - def serialize(self) -> dict: - serialized_device = self.__dict__ - device_dict = {"PhotoacousticDevice": serialized_device} - return device_dict - - @staticmethod - def deserialize(dictionary_to_deserialize): - deserialized_device = PhotoacousticDevice( - device_position_mm=dictionary_to_deserialize["device_position_mm"], - field_of_view_extent_mm=dictionary_to_deserialize["field_of_view_extent_mm"]) - det_geometry = dictionary_to_deserialize["detection_geometry"] - if det_geometry != "None": - deserialized_device.set_detection_geometry(dictionary_to_deserialize["detection_geometry"]) - if "illumination_geometries" in dictionary_to_deserialize: - for illumination_geometry in dictionary_to_deserialize["illumination_geometries"]: - deserialized_device.illumination_geometries.append(illumination_geometry) - - return deserialized_device +from .photoacoustic_device import PhotoacousticDevice diff --git a/simpa/core/device_digital_twins/pa_devices/photoacoustic_device.py b/simpa/core/device_digital_twins/pa_devices/photoacoustic_device.py new file mode 100644 index 00000000..38b0d202 --- /dev/null +++ b/simpa/core/device_digital_twins/pa_devices/photoacoustic_device.py @@ -0,0 +1,161 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import numpy as np +from abc import ABC +from simpa.core.device_digital_twins import DigitalDeviceTwinBase + + +class PhotoacousticDevice(DigitalDeviceTwinBase, ABC): + """Base class of a photoacoustic device. It consists of one detection geometry that describes the geometry of the + single detector elements and a list of illuminators. + + A Photoacoustic Device can be initialized as follows:: + + import simpa as sp + import numpy as np + + # Initialise a PhotoacousticDevice with its position and field of view + device = sp.PhotoacousticDevice(device_position_mm=np.array([10, 10, 0]), + field_of_view_extent_mm=np.array([-20, 20, 0, 0, 0, 20])) + + # Option 1) Set the detection geometry position relative to the PhotoacousticDevice + device.set_detection_geometry(sp.DetectionGeometry(), + detector_position_relative_to_pa_device=np.array([0, 0, -10])) + + # Option 2) Set the detection geometry position absolute + device.set_detection_geometry( + sp.DetectionGeometryBase(device_position_mm=np.array([10, 10, -10]))) + + # Option 1) Add the illumination geometry position relative to the PhotoacousticDevice + device.add_illumination_geometry(sp.IlluminationGeometry(), + illuminator_position_relative_to_pa_device=np.array([0, 0, 0])) + + # Option 2) Add the illumination geometry position absolute + device.add_illumination_geometry( + sp.IlluminationGeometryBase(device_position_mm=np.array([10, 10, 0])) + + Attributes: + detection_geometry (DetectionGeometryBase): Geometry of the detector elements. + illumination_geometries (list): List of illuminations defined by :py:class:`IlluminationGeometryBase`. + """ + + def __init__(self, device_position_mm=None, field_of_view_extent_mm=None): + """ + :param device_position_mm: Each device has an internal position which serves as origin for internal \ + representations of e.g. detector element positions or illuminator positions. + :type device_position_mm: ndarray + :param field_of_view_extent_mm: Field of view which is defined as a numpy array of the shape \ + [xs, xe, ys, ye, zs, ze], where x, y, and z denote the coordinate axes and s and e denote the start and end \ + positions. + :type field_of_view_extent_mm: ndarray + """ + super(PhotoacousticDevice, self).__init__(device_position_mm=device_position_mm, + field_of_view_extent_mm=field_of_view_extent_mm) + self.detection_geometry = None + self.illumination_geometries = [] + + def set_detection_geometry(self, detection_geometry, + detector_position_relative_to_pa_device=None): + """Sets the detection geometry for the PA device. The detection geometry can be instantiated with an absolute + position or it can be instantiated without the device_position_mm argument but a position relative to the + position of the PhotoacousticDevice. If both absolute and relative positions are given, the absolute position + is chosen as position of the detection geometry. + + :param detection_geometry: Detection geometry of the PA device. + :type detection_geometry: DetectionGeometryBase + :param detector_position_relative_to_pa_device: Position of the detection geometry relative to the PA device. + :type detector_position_relative_to_pa_device: ndarray + :raises ValueError: if the detection_geometry is None + + """ + if detection_geometry is None: + msg = "The given detection_geometry must not be None!" + self.logger.critical(msg) + raise ValueError(msg) + if np.linalg.norm(detection_geometry.device_position_mm) == 0 and \ + detector_position_relative_to_pa_device is not None: + detection_geometry.device_position_mm = np.add(self.device_position_mm, + detector_position_relative_to_pa_device) + self.detection_geometry = detection_geometry + + def add_illumination_geometry(self, illumination_geometry, illuminator_position_relative_to_pa_device=None): + """Adds an illuminator to the PA device. The illumination geometry can be instantiated with an absolute + position or it can be instantiated without the device_position_mm argument but a position relative to the + position of the PhotoacousticDevice. If both absolute and relative positions are given, the absolute position + is chosen as position of the illumination geometry. + + :param illumination_geometry: Geometry of the illuminator. + :type illumination_geometry: IlluminationGeometryBase + :param illuminator_position_relative_to_pa_device: Position of the illuminator relative to the PA device. + :type illuminator_position_relative_to_pa_device: ndarray + :raises ValueError: if the illumination_geometry is None + + """ + if illumination_geometry is None: + msg = "The given illumination_geometry must not be None!" + self.logger.critical(msg) + raise ValueError(msg) + if np.linalg.norm(illumination_geometry.device_position_mm) == 0: + if illuminator_position_relative_to_pa_device is not None: + illumination_geometry.device_position_mm = np.add(self.device_position_mm, + illuminator_position_relative_to_pa_device) + else: + illumination_geometry.device_position_mm = self.device_position_mm + self.illumination_geometries.append(illumination_geometry) + + def get_detection_geometry(self): + """ + :return: None if no detection geometry was set or an instance of DetectionGeometryBase. + :rtype: None, DetectionGeometryBase + """ + return self.detection_geometry + + def get_illumination_geometry(self): + """ + :return: None, if no illumination geometry was defined, + an instance of IlluminationGeometryBase if exactly one geometry was defined, + a list of IlluminationGeometryBase instances if more than one device was defined. + :rtype: None, IlluminationGeometryBase + """ + if len(self.illumination_geometries) == 0: + return None + + if len(self.illumination_geometries) == 1: + return self.illumination_geometries[0] + + return self.illumination_geometries + + def check_settings_prerequisites(self, global_settings) -> bool: + _result = True + if self.detection_geometry is not None \ + and not self.detection_geometry.check_settings_prerequisites(global_settings): + _result = False + for illumination_geometry in self.illumination_geometries: + if illumination_geometry is not None \ + and not illumination_geometry.check_settings_prerequisites(global_settings): + _result = False + return _result + + def update_settings_for_use_of_model_based_volume_creator(self, global_settings): + pass + + def serialize(self) -> dict: + serialized_device = self.__dict__ + device_dict = {"PhotoacousticDevice": serialized_device} + return device_dict + + @staticmethod + def deserialize(dictionary_to_deserialize): + deserialized_device = PhotoacousticDevice( + device_position_mm=dictionary_to_deserialize["device_position_mm"], + field_of_view_extent_mm=dictionary_to_deserialize["field_of_view_extent_mm"]) + det_geometry = dictionary_to_deserialize["detection_geometry"] + if det_geometry != "None": + deserialized_device.set_detection_geometry(dictionary_to_deserialize["detection_geometry"]) + if "illumination_geometries" in dictionary_to_deserialize: + for illumination_geometry in dictionary_to_deserialize["illumination_geometries"]: + deserialized_device.illumination_geometries.append(illumination_geometry) + + return deserialized_device diff --git a/simpa/core/pipeline_module.py b/simpa/core/pipeline_module.py new file mode 100644 index 00000000..9a49b941 --- /dev/null +++ b/simpa/core/pipeline_module.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT +from abc import abstractmethod + +from simpa.core.device_digital_twins import DigitalDeviceTwinBase +from simpa.log import Logger +from simpa.utils import Settings +from simpa.utils.processing_device import get_processing_device + + +class PipelineModule: + """ + Defines a pipeline module (either simulation or processing module) that implements a run method and can be called by running the pipeline's simulate method. + """ + + def __init__(self, global_settings: Settings): + """ + :param global_settings: The SIMPA settings dictionary + :type global_settings: Settings + """ + self.logger = Logger() + self.global_settings = global_settings + self.torch_device = get_processing_device(self.global_settings) + + @abstractmethod + def run(self, digital_device_twin: DigitalDeviceTwinBase): + """ + Executes the respective simulation module + + :param digital_device_twin: The digital twin that can be used by the digital device_twin. + """ + pass diff --git a/simpa/core/processing_components/__init__.py b/simpa/core/processing_components/__init__.py index c8306cbf..2cacf22a 100644 --- a/simpa/core/processing_components/__init__.py +++ b/simpa/core/processing_components/__init__.py @@ -2,21 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from abc import ABC -from simpa.core import PipelineModule - - -class ProcessingComponent(PipelineModule, ABC): - """ - Defines a pipeline processing component, which can be used to pre- or post-process simulation data. - """ - - def __init__(self, global_settings, component_settings_key: str): - """ - Initialises the ProcessingComponent. - - :param component_settings_key: The key where the component settings are stored in - """ - super(ProcessingComponent, self).__init__(global_settings=global_settings) - self.component_settings = global_settings[component_settings_key] - +from .processing_component import ProcessingComponent diff --git a/simpa/core/processing_components/multispectral/__init__.py b/simpa/core/processing_components/multispectral/__init__.py index d179c455..bf4ed09d 100644 --- a/simpa/core/processing_components/multispectral/__init__.py +++ b/simpa/core/processing_components/multispectral/__init__.py @@ -1,57 +1,4 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from simpa.io_handling import load_data_field -from simpa.utils import Tags -from simpa.log import Logger -import numpy as np -from abc import ABC, abstractmethod - - -class MultispectralProcessingAlgorithm(ABC): - """ - A MultispectralProcessingAlgorithm class represents an algorithm that works with multispectral input data. - """ - - def __init__(self, global_settings, component_settings_key: str): - """ - Instantiates a multispectral processing algorithm. - - Per default, this methods loads all data from a certain - Tags.DATA_FIELD into a data array for all - Tags.WAVELENGTHS. - - """ - if component_settings_key is None: - raise KeyError("The component settings must be set for a multispectral" - "processing algorithm!") - self.component_settings = global_settings[component_settings_key] - - if Tags.WAVELENGTHS not in self.component_settings: - raise KeyError("Tags.WAVELENGTHS must be in the component_settings of a multispectral processing algorithm") - - if Tags.DATA_FIELD not in self.component_settings: - raise KeyError("Tags.DATA_FIELD must be in the component_settings of a multispectral processing algorithm") - - self.logger = Logger() - self.global_settings = global_settings - self.wavelengths = self.component_settings[Tags.WAVELENGTHS] - self.data_field = self.component_settings[Tags.DATA_FIELD] - - self.data = list() - for i in range(len(self.wavelengths)): - self.data.append(load_data_field(self.global_settings[Tags.SIMPA_OUTPUT_PATH], - self.data_field, - self.wavelengths[i])) - - self.data = np.asarray(self.data) - if Tags.SIGNAL_THRESHOLD in self.component_settings: - self.data[self.data < self.component_settings[Tags.SIGNAL_THRESHOLD]*np.max(self.data)] = 0 - - @abstractmethod - def run(self): - """ - This method must be implemented by the multispectral algorithm, such that - any multispectral algorithm can be executed by invoking the run method. - """ - pass +from .multispectral_processing_algorithm import MultispectralProcessingAlgorithm diff --git a/simpa/core/processing_components/multispectral/multispectral_processing_algorithm.py b/simpa/core/processing_components/multispectral/multispectral_processing_algorithm.py new file mode 100644 index 00000000..d179c455 --- /dev/null +++ b/simpa/core/processing_components/multispectral/multispectral_processing_algorithm.py @@ -0,0 +1,57 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT +from simpa.io_handling import load_data_field +from simpa.utils import Tags +from simpa.log import Logger +import numpy as np +from abc import ABC, abstractmethod + + +class MultispectralProcessingAlgorithm(ABC): + """ + A MultispectralProcessingAlgorithm class represents an algorithm that works with multispectral input data. + """ + + def __init__(self, global_settings, component_settings_key: str): + """ + Instantiates a multispectral processing algorithm. + + Per default, this methods loads all data from a certain + Tags.DATA_FIELD into a data array for all + Tags.WAVELENGTHS. + + """ + if component_settings_key is None: + raise KeyError("The component settings must be set for a multispectral" + "processing algorithm!") + self.component_settings = global_settings[component_settings_key] + + if Tags.WAVELENGTHS not in self.component_settings: + raise KeyError("Tags.WAVELENGTHS must be in the component_settings of a multispectral processing algorithm") + + if Tags.DATA_FIELD not in self.component_settings: + raise KeyError("Tags.DATA_FIELD must be in the component_settings of a multispectral processing algorithm") + + self.logger = Logger() + self.global_settings = global_settings + self.wavelengths = self.component_settings[Tags.WAVELENGTHS] + self.data_field = self.component_settings[Tags.DATA_FIELD] + + self.data = list() + for i in range(len(self.wavelengths)): + self.data.append(load_data_field(self.global_settings[Tags.SIMPA_OUTPUT_PATH], + self.data_field, + self.wavelengths[i])) + + self.data = np.asarray(self.data) + if Tags.SIGNAL_THRESHOLD in self.component_settings: + self.data[self.data < self.component_settings[Tags.SIGNAL_THRESHOLD]*np.max(self.data)] = 0 + + @abstractmethod + def run(self): + """ + This method must be implemented by the multispectral algorithm, such that + any multispectral algorithm can be executed by invoking the run method. + """ + pass diff --git a/simpa/core/processing_components/processing_component.py b/simpa/core/processing_components/processing_component.py new file mode 100644 index 00000000..881a8f2d --- /dev/null +++ b/simpa/core/processing_components/processing_component.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +from abc import ABC +from simpa.core import PipelineModule + + +class ProcessingComponent(PipelineModule, ABC): + """ + Defines a pipeline processing component, which can be used to pre- or post-process simulation data. + """ + + def __init__(self, global_settings, component_settings_key: str): + """ + Initialises the ProcessingComponent. + + :param component_settings_key: The key where the component settings are stored in + """ + super(ProcessingComponent, self).__init__(global_settings=global_settings) + self.component_settings = global_settings[component_settings_key] diff --git a/simpa/core/simulation_modules/__init__.py b/simpa/core/simulation_modules/__init__.py index 872aa0c1..5344c009 100644 --- a/simpa/core/simulation_modules/__init__.py +++ b/simpa/core/simulation_modules/__init__.py @@ -2,30 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from abc import abstractmethod - -from simpa.core import PipelineModule -from simpa.utils import Settings - -class SimulationModule(PipelineModule): - """ - Defines a simulation module that is a step in the simulation pipeline. - Each simulation module can only be one of Volume Creation, Light Propagation Modeling, Acoustic Wave Propagation Modeling, Image Reconstruction. - """ - - def __init__(self, global_settings: Settings): - """ - :param global_settings: The SIMPA settings dictionary - :type global_settings: Settings - """ - super(SimulationModule, self).__init__(global_settings=global_settings) - self.component_settings = self.load_component_settings() - if self.component_settings is None: - raise ValueError("The component settings should not be None at this point") - - @abstractmethod - def load_component_settings(self) -> Settings: - """ - :return: Loads component settings corresponding to this simulation component - """ - pass +from .simulation_module import SimulationModule diff --git a/simpa/core/simulation_modules/acoustic_forward_module/__init__.py b/simpa/core/simulation_modules/acoustic_forward_module/__init__.py index 2beb0eb6..65d4898b 100644 --- a/simpa/core/simulation_modules/acoustic_forward_module/__init__.py +++ b/simpa/core/simulation_modules/acoustic_forward_module/__init__.py @@ -1,84 +1,4 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT - -from abc import abstractmethod -import numpy as np -from simpa.core.simulation_modules import SimulationModule -from simpa.utils import Tags, Settings -from simpa.io_handling.io_hdf5 import save_hdf5 -from simpa.utils.dict_path_manager import generate_dict_path -from simpa.core.device_digital_twins import PhotoacousticDevice, DetectionGeometryBase -from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined - - -class AcousticForwardModelBaseAdapter(SimulationModule): - """ - This method is the entry method for running an acoustic forward model. - It is invoked in the *simpa.core.simulation.simulate* method, but can also be called - individually for the purposes of performing acoustic forward modeling only or in a different context. - - The concrete will be chosen based on the:: - - Tags.ACOUSTIC_MODEL - - tag in the settings dictionary. - - :param settings: The settings dictionary containing key-value pairs that determine the simulation. - Here, it must contain the Tags.ACOUSTIC_MODEL tag and any tags that might be required by the specific - acoustic model. - :raises AssertionError: an assertion error is raised if the Tags.ACOUSTIC_MODEL tag is not given or - points to an unknown acoustic forward model. - """ - - def __init__(self, global_settings: Settings): - super(AcousticForwardModelBaseAdapter, self).__init__(global_settings=global_settings) - - def load_component_settings(self) -> Settings: - """Implements abstract method to serve acoustic settings as component settings - - :return: Settings: acoustic component settings - """ - return self.global_settings.get_acoustic_settings() - - @abstractmethod - def forward_model(self, detection_geometry) -> np.ndarray: - """ - This method performs the acoustic forward modeling given the initial pressure - distribution and the acoustic tissue properties contained in the settings file. - A deriving class needs to implement this method according to its model. - - :return: time series pressure data - """ - pass - - def run(self, digital_device_twin): - """ - Call this method to invoke the simulation process. - - :param digital_device_twin: - :return: a numpy array containing the time series pressure data per detection element - """ - - self.logger.info("Simulating the acoustic forward process...") - - _device = None - if isinstance(digital_device_twin, DetectionGeometryBase): - _device = digital_device_twin - elif isinstance(digital_device_twin, PhotoacousticDevice): - _device = digital_device_twin.get_detection_geometry() - else: - raise TypeError( - f"The optical forward modelling does not support devices of type {type(digital_device_twin)}") - - time_series_data = self.forward_model(_device) - - if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): - assert_array_well_defined(time_series_data, array_name="time_series_data") - - acoustic_output_path = generate_dict_path( - Tags.DATA_FIELD_TIME_SERIES_DATA, wavelength=self.global_settings[Tags.WAVELENGTH]) - - save_hdf5(time_series_data, self.global_settings[Tags.SIMPA_OUTPUT_PATH], acoustic_output_path) - - self.logger.info("Simulating the acoustic forward process...[Done]") +from .acoustic_forward_model_base_adapter import AcousticForwardModelBaseAdapter diff --git a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_base_adapter.py b/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_base_adapter.py new file mode 100644 index 00000000..e9f33d10 --- /dev/null +++ b/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_base_adapter.py @@ -0,0 +1,84 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +from abc import abstractmethod +import numpy as np +from simpa.core.simulation_modules import SimulationModule +from simpa.utils import Tags, Settings +from simpa.io_handling.io_hdf5 import save_hdf5 +from simpa.utils.dict_path_manager import generate_dict_path +from simpa.core.device_digital_twins import PhotoacousticDevice, DetectionGeometryBase +from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined + + +class AcousticForwardModelBaseAdapter(SimulationModule): + """ + This method is the entry method for running an acoustic forward model. + It is invoked in the *simpa.core.simulation.simulate* method, but can also be called + individually for the purposes of performing acoustic forward modeling only or in a different context. + + The concrete will be chosen based on the:: + + Tags.ACOUSTIC_MODEL + + tag in the settings dictionary. + + :param settings: The settings dictionary containing key-value pairs that determine the simulation. + Here, it must contain the Tags.ACOUSTIC_MODEL tag and any tags that might be required by the specific + acoustic model. + :raises AssertionError: an assertion error is raised if the Tags.ACOUSTIC_MODEL tag is not given or + points to an unknown acoustic forward model. + """ + + def __init__(self, global_settings: Settings): + super(AcousticForwardModelBaseAdapter, self).__init__(global_settings=global_settings) + + def load_component_settings(self) -> Settings: + """Implements abstract method to serve acoustic settings as component settings + + :return: Settings: acoustic component settings + """ + return self.global_settings.get_acoustic_settings() + + @abstractmethod + def forward_model(self, detection_geometry) -> np.ndarray: + """ + This method performs the acoustic forward modeling given the initial pressure + distribution and the acoustic tissue properties contained in the settings file. + A deriving class needs to implement this method according to its model. + + :return: time series pressure data + """ + pass + + def run(self, digital_device_twin): + """ + Call this method to invoke the simulation process. + + :param digital_device_twin: + :return: a numpy array containing the time series pressure data per detection element + """ + + self.logger.info("Simulating the acoustic forward process...") + + _device = None + if isinstance(digital_device_twin, DetectionGeometryBase): + _device = digital_device_twin + elif isinstance(digital_device_twin, PhotoacousticDevice): + _device = digital_device_twin.get_detection_geometry() + else: + raise TypeError( + f"The optical forward modelling does not support devices of type {type(digital_device_twin)}") + + time_series_data = self.forward_model(_device) + + if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): + assert_array_well_defined(time_series_data, array_name="time_series_data") + + acoustic_output_path = generate_dict_path( + Tags.DATA_FIELD_TIME_SERIES_DATA, wavelength=self.global_settings[Tags.WAVELENGTH]) + + save_hdf5(time_series_data, self.global_settings[Tags.SIMPA_OUTPUT_PATH], acoustic_output_path) + + self.logger.info("Simulating the acoustic forward process...[Done]") diff --git a/simpa/core/simulation_modules/optical_simulation_module/__init__.py b/simpa/core/simulation_modules/optical_simulation_module/__init__.py index 9e2a3b6a..cf03c952 100644 --- a/simpa/core/simulation_modules/optical_simulation_module/__init__.py +++ b/simpa/core/simulation_modules/optical_simulation_module/__init__.py @@ -1,157 +1,4 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from abc import abstractmethod -from typing import Dict, Union - -import numpy as np - -from simpa.core.simulation_modules import SimulationModule -from simpa.core.device_digital_twins import (IlluminationGeometryBase, - PhotoacousticDevice) -from simpa.io_handling.io_hdf5 import load_data_field, save_hdf5 -from simpa.utils import Settings, Tags -from simpa.utils.dict_path_manager import generate_dict_path -from simpa.utils.quality_assurance.data_sanity_testing import \ - assert_array_well_defined - - -class OpticalForwardModuleBase(SimulationModule): - """ - Use this class as a base for implementations of optical forward models. - This class has the attributes `self.temporary_output_files` which stores file paths that are temporarily created as - input to the optical simulator, e.g. MCX. The class attributes `nx, ny & nz` represent the volume dimensions - """ - - def __init__(self, global_settings: Settings): - super(OpticalForwardModuleBase, self).__init__(global_settings=global_settings) - self.nx = None - self.ny = None - self.nz = None - self.temporary_output_files = [] - - def load_component_settings(self) -> Settings: - """Implements abstract method to serve optical settings as component settings - - :return: Settings: optical component settings - """ - return self.global_settings.get_optical_settings() - - @abstractmethod - def forward_model(self, - absorption_cm: np.ndarray, - scattering_cm: np.ndarray, - anisotropy: np.ndarray, - illumination_geometry: IlluminationGeometryBase): - """ - A deriving class needs to implement this method according to its model. - - :param absorption_cm: Absorption in units of per centimeter - :param scattering_cm: Scattering in units of per centimeter - :param anisotropy: Dimensionless scattering anisotropy - :param illumination_geometry: A device that represents a detection geometry - :return: Fluence in units of J/cm^2 - """ - pass - - def run(self, device: Union[IlluminationGeometryBase, PhotoacousticDevice]) -> None: - """ - runs optical simulations. Volumes are first loaded from HDF5 file and parsed to `self.forward_model`, the output - is aggregated in case multiple illuminations are defined by `device` and stored in the same HDF5 file. - - :param device: Illumination or Photoacoustic device that defines the illumination geometry - :return: None - """ - - self.logger.info("Simulating the optical forward process...") - - file_path = self.global_settings[Tags.SIMPA_OUTPUT_PATH] - wl = str(self.global_settings[Tags.WAVELENGTH]) - - absorption = load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wl) - scattering = load_data_field(file_path, Tags.DATA_FIELD_SCATTERING_PER_CM, wl) - anisotropy = load_data_field(file_path, Tags.DATA_FIELD_ANISOTROPY, wl) - gruneisen_parameter = load_data_field(file_path, Tags.DATA_FIELD_GRUNEISEN_PARAMETER) - - _device = None - if isinstance(device, IlluminationGeometryBase): - _device = device - elif isinstance(device, PhotoacousticDevice): - _device = device.get_illumination_geometry() - else: - raise TypeError(f"The optical forward modelling does not support devices of type {type(device)}") - - results = self.run_forward_model(_device=_device, - device=device, - absorption=absorption, - scattering=scattering, - anisotropy=anisotropy) - fluence = results[Tags.DATA_FIELD_FLUENCE] - if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): - assert_array_well_defined(fluence, assume_non_negativity=True, array_name="fluence") - - if Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE in self.component_settings: - units = Tags.UNITS_PRESSURE - # Initial pressure should be given in units of Pascale - conversion_factor = 1e6 # 1 J/cm^3 = 10^6 N/m^2 = 10^6 Pa - initial_pressure = (absorption * fluence * gruneisen_parameter * - (self.component_settings[Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE] / 1000) - * conversion_factor) - else: - units = Tags.UNITS_ARBITRARY - initial_pressure = absorption * fluence - - if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): - assert_array_well_defined(initial_pressure, assume_non_negativity=True, array_name="initial_pressure") - - results[Tags.DATA_FIELD_FLUENCE] = fluence - results[Tags.OPTICAL_MODEL_UNITS] = units - results[Tags.DATA_FIELD_INITIAL_PRESSURE] = initial_pressure - optical_output = {} - for k, item in results.items(): - optical_output[k] = {self.global_settings[Tags.WAVELENGTH]: item} - - optical_output_path = generate_dict_path(Tags.OPTICAL_MODEL_OUTPUT_NAME) - save_hdf5(optical_output, self.global_settings[Tags.SIMPA_OUTPUT_PATH], optical_output_path) - self.logger.info("Simulating the optical forward process...[Done]") - - def run_forward_model(self, - _device, - device: Union[IlluminationGeometryBase, PhotoacousticDevice], - absorption: np.ndarray, - scattering: np.ndarray, - anisotropy: np.ndarray) -> Dict: - """ - runs `self.forward_model` as many times as defined by `device` and aggregates the results. - - :param _device: device illumination geometry - :param device: class defining illumination - :param absorption: Absorption volume - :param scattering: Scattering volume - :param anisotropy: Dimensionless scattering anisotropy - :return: - """ - if isinstance(_device, list): - # per convention this list has at least two elements - results = self.forward_model(absorption_cm=absorption, - scattering_cm=scattering, - anisotropy=anisotropy, - illumination_geometry=_device[0]) - fluence = results[Tags.DATA_FIELD_FLUENCE] - for idx in range(1, len(_device)): - # we already looked at the 0th element, so go from 1 to n-1 - results = self.forward_model(absorption_cm=absorption, - scattering_cm=scattering, - anisotropy=anisotropy, - illumination_geometry=_device[idx]) - fluence += results[Tags.DATA_FIELD_FLUENCE] - - fluence = fluence / len(_device) - - else: - results = self.forward_model(absorption_cm=absorption, - scattering_cm=scattering, - anisotropy=anisotropy, - illumination_geometry=_device) - fluence = results[Tags.DATA_FIELD_FLUENCE] - return {Tags.DATA_FIELD_FLUENCE: fluence} +from .optical_forward_model_base_adapter import OpticalForwardModuleBase diff --git a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_base_adapter.py b/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_base_adapter.py new file mode 100644 index 00000000..9e2a3b6a --- /dev/null +++ b/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_base_adapter.py @@ -0,0 +1,157 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT +from abc import abstractmethod +from typing import Dict, Union + +import numpy as np + +from simpa.core.simulation_modules import SimulationModule +from simpa.core.device_digital_twins import (IlluminationGeometryBase, + PhotoacousticDevice) +from simpa.io_handling.io_hdf5 import load_data_field, save_hdf5 +from simpa.utils import Settings, Tags +from simpa.utils.dict_path_manager import generate_dict_path +from simpa.utils.quality_assurance.data_sanity_testing import \ + assert_array_well_defined + + +class OpticalForwardModuleBase(SimulationModule): + """ + Use this class as a base for implementations of optical forward models. + This class has the attributes `self.temporary_output_files` which stores file paths that are temporarily created as + input to the optical simulator, e.g. MCX. The class attributes `nx, ny & nz` represent the volume dimensions + """ + + def __init__(self, global_settings: Settings): + super(OpticalForwardModuleBase, self).__init__(global_settings=global_settings) + self.nx = None + self.ny = None + self.nz = None + self.temporary_output_files = [] + + def load_component_settings(self) -> Settings: + """Implements abstract method to serve optical settings as component settings + + :return: Settings: optical component settings + """ + return self.global_settings.get_optical_settings() + + @abstractmethod + def forward_model(self, + absorption_cm: np.ndarray, + scattering_cm: np.ndarray, + anisotropy: np.ndarray, + illumination_geometry: IlluminationGeometryBase): + """ + A deriving class needs to implement this method according to its model. + + :param absorption_cm: Absorption in units of per centimeter + :param scattering_cm: Scattering in units of per centimeter + :param anisotropy: Dimensionless scattering anisotropy + :param illumination_geometry: A device that represents a detection geometry + :return: Fluence in units of J/cm^2 + """ + pass + + def run(self, device: Union[IlluminationGeometryBase, PhotoacousticDevice]) -> None: + """ + runs optical simulations. Volumes are first loaded from HDF5 file and parsed to `self.forward_model`, the output + is aggregated in case multiple illuminations are defined by `device` and stored in the same HDF5 file. + + :param device: Illumination or Photoacoustic device that defines the illumination geometry + :return: None + """ + + self.logger.info("Simulating the optical forward process...") + + file_path = self.global_settings[Tags.SIMPA_OUTPUT_PATH] + wl = str(self.global_settings[Tags.WAVELENGTH]) + + absorption = load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wl) + scattering = load_data_field(file_path, Tags.DATA_FIELD_SCATTERING_PER_CM, wl) + anisotropy = load_data_field(file_path, Tags.DATA_FIELD_ANISOTROPY, wl) + gruneisen_parameter = load_data_field(file_path, Tags.DATA_FIELD_GRUNEISEN_PARAMETER) + + _device = None + if isinstance(device, IlluminationGeometryBase): + _device = device + elif isinstance(device, PhotoacousticDevice): + _device = device.get_illumination_geometry() + else: + raise TypeError(f"The optical forward modelling does not support devices of type {type(device)}") + + results = self.run_forward_model(_device=_device, + device=device, + absorption=absorption, + scattering=scattering, + anisotropy=anisotropy) + fluence = results[Tags.DATA_FIELD_FLUENCE] + if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): + assert_array_well_defined(fluence, assume_non_negativity=True, array_name="fluence") + + if Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE in self.component_settings: + units = Tags.UNITS_PRESSURE + # Initial pressure should be given in units of Pascale + conversion_factor = 1e6 # 1 J/cm^3 = 10^6 N/m^2 = 10^6 Pa + initial_pressure = (absorption * fluence * gruneisen_parameter * + (self.component_settings[Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE] / 1000) + * conversion_factor) + else: + units = Tags.UNITS_ARBITRARY + initial_pressure = absorption * fluence + + if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): + assert_array_well_defined(initial_pressure, assume_non_negativity=True, array_name="initial_pressure") + + results[Tags.DATA_FIELD_FLUENCE] = fluence + results[Tags.OPTICAL_MODEL_UNITS] = units + results[Tags.DATA_FIELD_INITIAL_PRESSURE] = initial_pressure + optical_output = {} + for k, item in results.items(): + optical_output[k] = {self.global_settings[Tags.WAVELENGTH]: item} + + optical_output_path = generate_dict_path(Tags.OPTICAL_MODEL_OUTPUT_NAME) + save_hdf5(optical_output, self.global_settings[Tags.SIMPA_OUTPUT_PATH], optical_output_path) + self.logger.info("Simulating the optical forward process...[Done]") + + def run_forward_model(self, + _device, + device: Union[IlluminationGeometryBase, PhotoacousticDevice], + absorption: np.ndarray, + scattering: np.ndarray, + anisotropy: np.ndarray) -> Dict: + """ + runs `self.forward_model` as many times as defined by `device` and aggregates the results. + + :param _device: device illumination geometry + :param device: class defining illumination + :param absorption: Absorption volume + :param scattering: Scattering volume + :param anisotropy: Dimensionless scattering anisotropy + :return: + """ + if isinstance(_device, list): + # per convention this list has at least two elements + results = self.forward_model(absorption_cm=absorption, + scattering_cm=scattering, + anisotropy=anisotropy, + illumination_geometry=_device[0]) + fluence = results[Tags.DATA_FIELD_FLUENCE] + for idx in range(1, len(_device)): + # we already looked at the 0th element, so go from 1 to n-1 + results = self.forward_model(absorption_cm=absorption, + scattering_cm=scattering, + anisotropy=anisotropy, + illumination_geometry=_device[idx]) + fluence += results[Tags.DATA_FIELD_FLUENCE] + + fluence = fluence / len(_device) + + else: + results = self.forward_model(absorption_cm=absorption, + scattering_cm=scattering, + anisotropy=anisotropy, + illumination_geometry=_device) + fluence = results[Tags.DATA_FIELD_FLUENCE] + return {Tags.DATA_FIELD_FLUENCE: fluence} diff --git a/simpa/core/simulation_modules/reconstruction_module/__init__.py b/simpa/core/simulation_modules/reconstruction_module/__init__.py index 118874dc..bc5903bc 100644 --- a/simpa/core/simulation_modules/reconstruction_module/__init__.py +++ b/simpa/core/simulation_modules/reconstruction_module/__init__.py @@ -1,98 +1,9 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT +from .reconstruction_module_base_adapter import ReconstructionAdapterBase -from simpa.utils import Tags -from simpa.core.device_digital_twins import DetectionGeometryBase -from simpa.core.device_digital_twins import PhotoacousticDevice -from simpa.io_handling.io_hdf5 import load_data_field -from abc import abstractmethod -from simpa.core.simulation_modules import SimulationModule -from simpa.utils.dict_path_manager import generate_dict_path -from simpa.io_handling.io_hdf5 import save_hdf5 -import numpy as np -from simpa.utils import Settings -from simpa.core.simulation_modules.reconstruction_module.reconstruction_utils import bandpass_filter_with_settings, apply_b_mode -from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined - - -class ReconstructionAdapterBase(SimulationModule): - """ - This class is the main entry point to perform image reconstruction using the SIMPA toolkit. - All information necessary for the respective reconstruction method must be contained in the - respective settings dictionary. - """ - - def __init__(self, global_settings: Settings): - super(ReconstructionAdapterBase, self).__init__(global_settings=global_settings) - - def load_component_settings(self) -> Settings: - """Implements abstract method to serve reconstruction settings as component settings - - :return: Settings: reconstruction component settings - """ - return self.global_settings.get_reconstruction_settings() - - @abstractmethod - def reconstruction_algorithm(self, time_series_sensor_data, - detection_geometry: DetectionGeometryBase) -> np.ndarray: - """ - A deriving class needs to implement this method according to its model. - - :param time_series_sensor_data: the time series sensor data - :param detection_geometry: - :return: a reconstructed photoacoustic image - """ - pass - - def run(self, device): - self.logger.info("Performing reconstruction...") - - time_series_sensor_data = load_data_field(self.global_settings[Tags.SIMPA_OUTPUT_PATH], - Tags.DATA_FIELD_TIME_SERIES_DATA, self.global_settings[Tags.WAVELENGTH]) - - _device = None - if isinstance(device, DetectionGeometryBase): - _device = device - elif isinstance(device, PhotoacousticDevice): - _device = device.get_detection_geometry() - else: - raise TypeError(f"Type {type(device)} is not supported for performing image reconstruction.") - - if Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING in self.component_settings and \ - self.component_settings[Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING]: - - time_series_sensor_data = bandpass_filter_with_settings(time_series_sensor_data, - self.global_settings, - self.component_settings, - _device) - - # check for B-mode methods and perform envelope detection on time series data if specified - if Tags.RECONSTRUCTION_BMODE_BEFORE_RECONSTRUCTION in self.component_settings \ - and self.component_settings[Tags.RECONSTRUCTION_BMODE_BEFORE_RECONSTRUCTION] \ - and Tags.RECONSTRUCTION_BMODE_METHOD in self.component_settings: - time_series_sensor_data = apply_b_mode( - time_series_sensor_data, method=self.component_settings[Tags.RECONSTRUCTION_BMODE_METHOD]) - - reconstruction = self.reconstruction_algorithm(time_series_sensor_data, _device) - - # check for B-mode methods and perform envelope detection on time series data if specified - if Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION in self.component_settings \ - and self.component_settings[Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION] \ - and Tags.RECONSTRUCTION_BMODE_METHOD in self.component_settings: - reconstruction = apply_b_mode( - reconstruction, method=self.component_settings[Tags.RECONSTRUCTION_BMODE_METHOD]) - - if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): - assert_array_well_defined(reconstruction, array_name="reconstruction") - - reconstruction_output_path = generate_dict_path( - Tags.DATA_FIELD_RECONSTRUCTED_DATA, self.global_settings[Tags.WAVELENGTH]) - - save_hdf5(reconstruction, self.global_settings[Tags.SIMPA_OUTPUT_PATH], - reconstruction_output_path) - - self.logger.info("Performing reconstruction...[Done]") +from simpa.utils import Tags, Settings def create_reconstruction_settings(speed_of_sound_in_m_per_s: int = 1540, time_spacing_in_s: float = 2.5e-8, diff --git a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_base_adapter.py b/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_base_adapter.py new file mode 100644 index 00000000..2032960d --- /dev/null +++ b/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_base_adapter.py @@ -0,0 +1,95 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +from simpa.utils import Tags +from simpa.core.device_digital_twins import DetectionGeometryBase +from simpa.core.device_digital_twins import PhotoacousticDevice +from simpa.io_handling.io_hdf5 import load_data_field +from abc import abstractmethod +from simpa.core.simulation_modules import SimulationModule +from simpa.utils.dict_path_manager import generate_dict_path +from simpa.io_handling.io_hdf5 import save_hdf5 +import numpy as np +from simpa.utils import Settings +from simpa.core.simulation_modules.reconstruction_module.reconstruction_utils import bandpass_filter_with_settings, apply_b_mode +from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined + + +class ReconstructionAdapterBase(SimulationModule): + """ + This class is the main entry point to perform image reconstruction using the SIMPA toolkit. + All information necessary for the respective reconstruction method must be contained in the + respective settings dictionary. + """ + + def __init__(self, global_settings: Settings): + super(ReconstructionAdapterBase, self).__init__(global_settings=global_settings) + + def load_component_settings(self) -> Settings: + """Implements abstract method to serve reconstruction settings as component settings + + :return: Settings: reconstruction component settings + """ + return self.global_settings.get_reconstruction_settings() + + @abstractmethod + def reconstruction_algorithm(self, time_series_sensor_data, + detection_geometry: DetectionGeometryBase) -> np.ndarray: + """ + A deriving class needs to implement this method according to its model. + + :param time_series_sensor_data: the time series sensor data + :param detection_geometry: + :return: a reconstructed photoacoustic image + """ + pass + + def run(self, device): + self.logger.info("Performing reconstruction...") + + time_series_sensor_data = load_data_field(self.global_settings[Tags.SIMPA_OUTPUT_PATH], + Tags.DATA_FIELD_TIME_SERIES_DATA, self.global_settings[Tags.WAVELENGTH]) + + _device = None + if isinstance(device, DetectionGeometryBase): + _device = device + elif isinstance(device, PhotoacousticDevice): + _device = device.get_detection_geometry() + else: + raise TypeError(f"Type {type(device)} is not supported for performing image reconstruction.") + + if Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING in self.component_settings and \ + self.component_settings[Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING]: + + time_series_sensor_data = bandpass_filter_with_settings(time_series_sensor_data, + self.global_settings, + self.component_settings, + _device) + + # check for B-mode methods and perform envelope detection on time series data if specified + if Tags.RECONSTRUCTION_BMODE_BEFORE_RECONSTRUCTION in self.component_settings \ + and self.component_settings[Tags.RECONSTRUCTION_BMODE_BEFORE_RECONSTRUCTION] \ + and Tags.RECONSTRUCTION_BMODE_METHOD in self.component_settings: + time_series_sensor_data = apply_b_mode( + time_series_sensor_data, method=self.component_settings[Tags.RECONSTRUCTION_BMODE_METHOD]) + + reconstruction = self.reconstruction_algorithm(time_series_sensor_data, _device) + + # check for B-mode methods and perform envelope detection on time series data if specified + if Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION in self.component_settings \ + and self.component_settings[Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION] \ + and Tags.RECONSTRUCTION_BMODE_METHOD in self.component_settings: + reconstruction = apply_b_mode( + reconstruction, method=self.component_settings[Tags.RECONSTRUCTION_BMODE_METHOD]) + + if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): + assert_array_well_defined(reconstruction, array_name="reconstruction") + + reconstruction_output_path = generate_dict_path( + Tags.DATA_FIELD_RECONSTRUCTED_DATA, self.global_settings[Tags.WAVELENGTH]) + + save_hdf5(reconstruction, self.global_settings[Tags.SIMPA_OUTPUT_PATH], + reconstruction_output_path) + + self.logger.info("Performing reconstruction...[Done]") diff --git a/simpa/core/simulation_modules/simulation_module.py b/simpa/core/simulation_modules/simulation_module.py new file mode 100644 index 00000000..c87e1e26 --- /dev/null +++ b/simpa/core/simulation_modules/simulation_module.py @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +from abc import abstractmethod + +from simpa.core import PipelineModule +from simpa.utils import Settings + + +class SimulationModule(PipelineModule): + """ + Defines a simulation module that is a step in the simulation pipeline. + Each simulation module can only be one of Volume Creation, Light Propagation Modeling, Acoustic Wave Propagation Modeling, Image Reconstruction. + """ + + def __init__(self, global_settings: Settings): + """ + :param global_settings: The SIMPA settings dictionary + :type global_settings: Settings + """ + super(SimulationModule, self).__init__(global_settings=global_settings) + self.component_settings = self.load_component_settings() + if self.component_settings is None: + raise ValueError("The component settings should not be None at this point") + + @abstractmethod + def load_component_settings(self) -> Settings: + """ + :return: Loads component settings corresponding to this simulation component + """ + pass diff --git a/simpa/core/simulation_modules/volume_creation_module/__init__.py b/simpa/core/simulation_modules/volume_creation_module/__init__.py index 7f2b3ffa..bde3b59f 100644 --- a/simpa/core/simulation_modules/volume_creation_module/__init__.py +++ b/simpa/core/simulation_modules/volume_creation_module/__init__.py @@ -2,77 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from abc import abstractmethod -from simpa.utils.settings import Settings -from simpa.utils import Tags -from simpa.utils.constants import wavelength_independent_properties, property_tags -import torch -from simpa.core.simulation_modules import SimulationModule -from simpa.io_handling import save_data_field -from simpa.utils.quality_assurance.data_sanity_testing import assert_equal_shapes, assert_array_well_defined - - -class VolumeCreatorModuleBase(SimulationModule): - """ - Use this class to define your own volume creation adapter. - - """ - - def __init__(self, global_settings: Settings): - super(VolumeCreatorModuleBase, self).__init__(global_settings=global_settings) - - def load_component_settings(self) -> Settings: - """Implements abstract method to serve volume creation settings as component settings - - :return: Settings: volume creation component settings - """ - return self.global_settings.get_volume_creation_settings() - - def create_empty_volumes(self): - volumes = dict() - voxel_spacing = self.global_settings[Tags.SPACING_MM] - volume_x_dim = int(round(self.global_settings[Tags.DIM_VOLUME_X_MM] / voxel_spacing)) - volume_y_dim = int(round(self.global_settings[Tags.DIM_VOLUME_Y_MM] / voxel_spacing)) - volume_z_dim = int(round(self.global_settings[Tags.DIM_VOLUME_Z_MM] / voxel_spacing)) - sizes = (volume_x_dim, volume_y_dim, volume_z_dim) - - wavelength = self.global_settings[Tags.WAVELENGTH] - first_wavelength = self.global_settings[Tags.WAVELENGTHS][0] - - for key in property_tags: - # Create wavelength-independent properties only in the first wavelength run - if key in wavelength_independent_properties and wavelength != first_wavelength: - continue - volumes[key] = torch.zeros(sizes, dtype=torch.float, device=self.torch_device) - - return volumes, volume_x_dim, volume_y_dim, volume_z_dim - - @abstractmethod - def create_simulation_volume(self) -> dict: - """ - This method creates an in silico representation of a tissue as described in the settings file that is given. - - :return: A dictionary containing optical and acoustic properties as well as other characteristics of the - simulated volume such as oxygenation, and a segmentation mask. All of these are given as 3d numpy arrays. - :rtype: dict - """ - pass - - def run(self, device): - self.logger.info("VOLUME CREATION") - - volumes = self.create_simulation_volume() - # explicitly empty cache to free reserved GPU memory after volume creation - torch.cuda.empty_cache() - - if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): - assert_equal_shapes(list(volumes.values())) - for _volume_name in volumes.keys(): - if _volume_name == Tags.DATA_FIELD_OXYGENATION: - # oxygenation can have NaN by definition - continue - assert_array_well_defined(volumes[_volume_name], array_name=_volume_name) - - for key, value in volumes.items(): - save_data_field(value, self.global_settings[Tags.SIMPA_OUTPUT_PATH], - data_field=key, wavelength=self.global_settings[Tags.WAVELENGTH]) +from .volume_creator_base_adapter import VolumeCreatorModuleBase diff --git a/simpa/core/simulation_modules/volume_creation_module/volume_creator_base_adapter.py b/simpa/core/simulation_modules/volume_creation_module/volume_creator_base_adapter.py new file mode 100644 index 00000000..6b9f0753 --- /dev/null +++ b/simpa/core/simulation_modules/volume_creation_module/volume_creator_base_adapter.py @@ -0,0 +1,78 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +from abc import abstractmethod +from simpa.utils.settings import Settings +from simpa.utils import Tags +from simpa.utils.constants import wavelength_independent_properties, property_tags +import torch +from simpa.core.simulation_modules import SimulationModule +from simpa.io_handling import save_data_field +from simpa.utils.quality_assurance.data_sanity_testing import assert_equal_shapes, assert_array_well_defined + + +class VolumeCreatorModuleBase(SimulationModule): + """ + Use this class to define your own volume creation adapter. + + """ + + def __init__(self, global_settings: Settings): + super(VolumeCreatorModuleBase, self).__init__(global_settings=global_settings) + + def load_component_settings(self) -> Settings: + """Implements abstract method to serve volume creation settings as component settings + + :return: Settings: volume creation component settings + """ + return self.global_settings.get_volume_creation_settings() + + def create_empty_volumes(self): + volumes = dict() + voxel_spacing = self.global_settings[Tags.SPACING_MM] + volume_x_dim = int(round(self.global_settings[Tags.DIM_VOLUME_X_MM] / voxel_spacing)) + volume_y_dim = int(round(self.global_settings[Tags.DIM_VOLUME_Y_MM] / voxel_spacing)) + volume_z_dim = int(round(self.global_settings[Tags.DIM_VOLUME_Z_MM] / voxel_spacing)) + sizes = (volume_x_dim, volume_y_dim, volume_z_dim) + + wavelength = self.global_settings[Tags.WAVELENGTH] + first_wavelength = self.global_settings[Tags.WAVELENGTHS][0] + + for key in property_tags: + # Create wavelength-independent properties only in the first wavelength run + if key in wavelength_independent_properties and wavelength != first_wavelength: + continue + volumes[key] = torch.zeros(sizes, dtype=torch.float, device=self.torch_device) + + return volumes, volume_x_dim, volume_y_dim, volume_z_dim + + @abstractmethod + def create_simulation_volume(self) -> dict: + """ + This method creates an in silico representation of a tissue as described in the settings file that is given. + + :return: A dictionary containing optical and acoustic properties as well as other characteristics of the + simulated volume such as oxygenation, and a segmentation mask. All of these are given as 3d numpy arrays. + :rtype: dict + """ + pass + + def run(self, device): + self.logger.info("VOLUME CREATION") + + volumes = self.create_simulation_volume() + # explicitly empty cache to free reserved GPU memory after volume creation + torch.cuda.empty_cache() + + if not (Tags.IGNORE_QA_ASSERTIONS in self.global_settings and Tags.IGNORE_QA_ASSERTIONS): + assert_equal_shapes(list(volumes.values())) + for _volume_name in volumes.keys(): + if _volume_name == Tags.DATA_FIELD_OXYGENATION: + # oxygenation can have NaN by definition + continue + assert_array_well_defined(volumes[_volume_name], array_name=_volume_name) + + for key, value in volumes.items(): + save_data_field(value, self.global_settings[Tags.SIMPA_OUTPUT_PATH], + data_field=key, wavelength=self.global_settings[Tags.WAVELENGTH]) From f26ef46cdcb5deeeaadec4d6b121516fea3a27a8 Mon Sep 17 00:00:00 2001 From: Leonie Boland Date: Wed, 7 Aug 2024 10:29:13 +0200 Subject: [PATCH 2/5] renamed files and classes for consistency --- simpa/__init__.py | 32 +++++++++---------- simpa/core/__init__.py | 2 +- ...ine_module.py => pipeline_element_base.py} | 4 +-- simpa/core/processing_components/__init__.py | 2 +- .../monospectral/field_of_view_cropping.py | 4 +-- .../monospectral/iterative_qPAI_algorithm.py | 8 ++--- .../monospectral/noise/gamma_noise.py | 4 +-- .../monospectral/noise/gaussian_noise.py | 4 +-- .../monospectral/noise/poisson_noise.py | 4 +-- .../noise/salt_and_pepper_noise.py | 4 +-- .../monospectral/noise/uniform_noise.py | 4 +-- ...ponent.py => processing_component_base.py} | 8 ++--- simpa/core/simulation_modules/__init__.py | 2 +- .../__init__.py | 2 +- .../acoustic_adapter_base.py} | 6 ++-- .../acoustic_test_adapter.py} | 4 +-- .../k_wave_adapter.py} | 8 ++--- .../simulate_2D.m | 0 .../simulate_3D.m | 0 .../__init__.py | 2 +- .../mcx_adapter.py} | 4 +-- .../mcx_reflectance_adapter.py} | 6 ++-- .../optical_adapter_base.py} | 6 ++-- .../optical_test_adapter.py} | 4 +-- .../reconstruction_module/__init__.py | 2 +- ...um_adapter.py => delay_and_sum_adapter.py} | 0 ...r.py => delay_multiply_and_sum_adapter.py} | 0 ...pter.py => reconstruction_adapter_base.py} | 4 +-- ...pter.py => reconstruction_test_adapter.py} | 2 +- ... signed_delay_multiply_and_sum_adapter.py} | 0 ...al_adapter.py => time_reversal_adapter.py} | 0 ...on_module.py => simulation_module_base.py} | 6 ++-- .../volume_creation_module/__init__.py | 2 +- ...ased_adapter.py => model_based_adapter.py} | 4 +-- ...apter.py => segmentation_based_adapter.py} | 4 +-- ...ter.py => volume_creation_adapter_base.py} | 6 ++-- simpa/utils/tags.py | 4 +-- simpa_examples/linear_unmixing.py | 2 +- simpa_examples/minimal_optical_simulation.py | 6 ++-- ...minimal_optical_simulation_uniform_cube.py | 4 +-- simpa_examples/msot_invision_simulation.py | 2 +- .../optical_and_acoustic_simulation.py | 2 +- .../perform_iterative_qPAI_reconstruction.py | 2 +- simpa_examples/segmentation_loader.py | 2 +- .../automatic_tests/test_IPASC_export.py | 32 +++++++++---------- .../automatic_tests/test_create_a_volume.py | 4 +-- .../automatic_tests/test_linear_unmixing.py | 2 +- .../automatic_tests/test_noise_models.py | 4 +-- simpa_tests/automatic_tests/test_pipeline.py | 16 +++++----- ...KWaveAcousticForwardConvenienceFunction.py | 6 ++-- .../SimulationWithMSOTInvision.py | 4 +-- .../DelayAndSumReconstruction.py | 4 +-- .../DelayMultiplyAndSumReconstruction.py | 4 +-- .../PointSourceReconstruction.py | 4 +-- ...SignedDelayMultiplyAndSumReconstruction.py | 4 +-- .../TimeReversalReconstruction.py | 4 +-- ...tteringWithInifinitesimalSlabExperiment.py | 4 +-- ...tionAndScatteringWithinHomogenousMedium.py | 6 ++-- .../CompareMCXResultsWithDiffusionTheory.py | 4 +-- .../ComputeDiffuseReflectance.py | 6 ++-- .../QPAIReconstruction.py | 4 +-- .../TestLinearUnmixingVisual.py | 2 +- .../ReproduceDISMeasurements.py | 4 +-- .../volume_creation/SegmentationLoader.py | 2 +- 64 files changed, 149 insertions(+), 149 deletions(-) rename simpa/core/{pipeline_module.py => pipeline_element_base.py} (84%) rename simpa/core/processing_components/{processing_component.py => processing_component_base.py} (69%) rename simpa/core/simulation_modules/{optical_simulation_module => acoustic_module}/__init__.py (67%) rename simpa/core/simulation_modules/{acoustic_forward_module/acoustic_forward_model_base_adapter.py => acoustic_module/acoustic_adapter_base.py} (94%) rename simpa/core/simulation_modules/{acoustic_forward_module/acoustic_forward_model_test_adapter.py => acoustic_module/acoustic_test_adapter.py} (75%) rename simpa/core/simulation_modules/{acoustic_forward_module/acoustic_forward_module_k_wave_adapter.py => acoustic_module/k_wave_adapter.py} (98%) rename simpa/core/simulation_modules/{acoustic_forward_module => acoustic_module}/simulate_2D.m (100%) rename simpa/core/simulation_modules/{acoustic_forward_module => acoustic_module}/simulate_3D.m (100%) rename simpa/core/simulation_modules/{acoustic_forward_module => optical_module}/__init__.py (65%) rename simpa/core/simulation_modules/{optical_simulation_module/optical_forward_model_mcx_adapter.py => optical_module/mcx_adapter.py} (98%) rename simpa/core/simulation_modules/{optical_simulation_module/optical_forward_model_mcx_reflectance_adapter.py => optical_module/mcx_reflectance_adapter.py} (98%) rename simpa/core/simulation_modules/{optical_simulation_module/optical_forward_model_base_adapter.py => optical_module/optical_adapter_base.py} (97%) rename simpa/core/simulation_modules/{optical_simulation_module/optical_forward_model_test_adapter.py => optical_module/optical_test_adapter.py} (74%) rename simpa/core/simulation_modules/reconstruction_module/{reconstruction_module_delay_and_sum_adapter.py => delay_and_sum_adapter.py} (100%) rename simpa/core/simulation_modules/reconstruction_module/{reconstruction_module_delay_multiply_and_sum_adapter.py => delay_multiply_and_sum_adapter.py} (100%) rename simpa/core/simulation_modules/reconstruction_module/{reconstruction_module_base_adapter.py => reconstruction_adapter_base.py} (97%) rename simpa/core/simulation_modules/reconstruction_module/{reconstruction_module_test_adapter.py => reconstruction_test_adapter.py} (85%) rename simpa/core/simulation_modules/reconstruction_module/{reconstruction_module_signed_delay_multiply_and_sum_adapter.py => signed_delay_multiply_and_sum_adapter.py} (100%) rename simpa/core/simulation_modules/reconstruction_module/{reconstruction_module_time_reversal_adapter.py => time_reversal_adapter.py} (100%) rename simpa/core/simulation_modules/{simulation_module.py => simulation_module_base.py} (85%) rename simpa/core/simulation_modules/volume_creation_module/{volume_creation_module_model_based_adapter.py => model_based_adapter.py} (98%) rename simpa/core/simulation_modules/volume_creation_module/{volume_creation_module_segmentation_based_adapter.py => segmentation_based_adapter.py} (96%) rename simpa/core/simulation_modules/volume_creation_module/{volume_creator_base_adapter.py => volume_creation_adapter_base.py} (94%) diff --git a/simpa/__init__.py b/simpa/__init__.py index 58fff0a3..2981e6a7 100644 --- a/simpa/__init__.py +++ b/simpa/__init__.py @@ -5,32 +5,32 @@ from .utils import * from .log import Logger -from .core.simulation_modules.volume_creation_module.volume_creation_module_model_based_adapter import \ - ModelBasedVolumeCreationAdapter -from .core.simulation_modules.volume_creation_module.volume_creation_module_segmentation_based_adapter import \ - SegmentationBasedVolumeCreationAdapter -from .core.simulation_modules.optical_simulation_module.optical_forward_model_mcx_adapter import \ +from .core.simulation_modules.volume_creation_module.model_based_adapter import \ + ModelBasedAdapter +from .core.simulation_modules.volume_creation_module.segmentation_based_adapter import \ + SegmentationBasedAdapter +from .core.simulation_modules.optical_module.mcx_adapter import \ MCXAdapter -from .core.simulation_modules.optical_simulation_module.optical_forward_model_mcx_reflectance_adapter import \ - MCXAdapterReflectance -from .core.simulation_modules.acoustic_forward_module.acoustic_forward_module_k_wave_adapter import \ +from .core.simulation_modules.optical_module.mcx_reflectance_adapter import \ + MCXReflectanceAdapter +from .core.simulation_modules.acoustic_module.k_wave_adapter import \ KWaveAdapter -from .core.simulation_modules.reconstruction_module.reconstruction_module_delay_and_sum_adapter import \ +from .core.simulation_modules.reconstruction_module.delay_and_sum_adapter import \ DelayAndSumAdapter -from .core.simulation_modules.reconstruction_module.reconstruction_module_delay_multiply_and_sum_adapter import \ +from .core.simulation_modules.reconstruction_module.delay_multiply_and_sum_adapter import \ DelayMultiplyAndSumAdapter -from .core.simulation_modules.reconstruction_module.reconstruction_module_signed_delay_multiply_and_sum_adapter import \ +from .core.simulation_modules.reconstruction_module.signed_delay_multiply_and_sum_adapter import \ SignedDelayMultiplyAndSumAdapter -from .core.simulation_modules.reconstruction_module.reconstruction_module_time_reversal_adapter import \ +from .core.simulation_modules.reconstruction_module.time_reversal_adapter import \ TimeReversalAdapter -from .core.simulation_modules.reconstruction_module.reconstruction_module_delay_and_sum_adapter import \ +from .core.simulation_modules.reconstruction_module.delay_and_sum_adapter import \ reconstruct_delay_and_sum_pytorch -from .core.simulation_modules.reconstruction_module.reconstruction_module_delay_multiply_and_sum_adapter import \ +from .core.simulation_modules.reconstruction_module.delay_multiply_and_sum_adapter import \ reconstruct_delay_multiply_and_sum_pytorch -from .core.simulation_modules.reconstruction_module.reconstruction_module_signed_delay_multiply_and_sum_adapter import \ +from .core.simulation_modules.reconstruction_module.signed_delay_multiply_and_sum_adapter import \ reconstruct_signed_delay_multiply_and_sum_pytorch -from .core.simulation_modules.acoustic_forward_module.acoustic_forward_module_k_wave_adapter import \ +from .core.simulation_modules.acoustic_module.k_wave_adapter import \ perform_k_wave_acoustic_forward_simulation from simpa.core.processing_components.monospectral.noise import GaussianNoise diff --git a/simpa/core/__init__.py b/simpa/core/__init__.py index dd267601..b16f1abf 100644 --- a/simpa/core/__init__.py +++ b/simpa/core/__init__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from .pipeline_module import PipelineModule +from .pipeline_element_base import PipelineElementBase diff --git a/simpa/core/pipeline_module.py b/simpa/core/pipeline_element_base.py similarity index 84% rename from simpa/core/pipeline_module.py rename to simpa/core/pipeline_element_base.py index 9a49b941..2fdcfb10 100644 --- a/simpa/core/pipeline_module.py +++ b/simpa/core/pipeline_element_base.py @@ -9,9 +9,9 @@ from simpa.utils.processing_device import get_processing_device -class PipelineModule: +class PipelineElementBase: """ - Defines a pipeline module (either simulation or processing module) that implements a run method and can be called by running the pipeline's simulate method. + Defines a pipeline element (either simulation or processing module) that implements a run method and can be called by running the pipeline's simulate method. """ def __init__(self, global_settings: Settings): diff --git a/simpa/core/processing_components/__init__.py b/simpa/core/processing_components/__init__.py index 2cacf22a..2c0e2695 100644 --- a/simpa/core/processing_components/__init__.py +++ b/simpa/core/processing_components/__init__.py @@ -2,4 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from .processing_component import ProcessingComponent +from .processing_component_base import ProcessingComponentBase diff --git a/simpa/core/processing_components/monospectral/field_of_view_cropping.py b/simpa/core/processing_components/monospectral/field_of_view_cropping.py index b950dc8f..68ae16a2 100644 --- a/simpa/core/processing_components/monospectral/field_of_view_cropping.py +++ b/simpa/core/processing_components/monospectral/field_of_view_cropping.py @@ -5,12 +5,12 @@ from simpa.utils import Tags, Settings from simpa.utils.constants import property_tags, wavelength_independent_properties, toolkit_tags from simpa.io_handling import load_data_field, save_data_field -from simpa.core.processing_components import ProcessingComponent +from simpa.core.processing_components import ProcessingComponentBase from simpa.core.device_digital_twins import DigitalDeviceTwinBase, PhotoacousticDevice import numpy as np -class FieldOfViewCropping(ProcessingComponent): +class FieldOfViewCropping(ProcessingComponentBase): def __init__(self, global_settings, settings_key=None): if settings_key is None: diff --git a/simpa/core/processing_components/monospectral/iterative_qPAI_algorithm.py b/simpa/core/processing_components/monospectral/iterative_qPAI_algorithm.py index 3a942564..c929f7e0 100644 --- a/simpa/core/processing_components/monospectral/iterative_qPAI_algorithm.py +++ b/simpa/core/processing_components/monospectral/iterative_qPAI_algorithm.py @@ -11,16 +11,16 @@ from simpa.utils.libraries.literature_values import OpticalTissueProperties, StandardProperties from simpa.utils.libraries.molecule_library import MolecularComposition from simpa.utils.calculate import calculate_gruneisen_parameter_from_temperature -from simpa.core.simulation_modules.optical_simulation_module.optical_forward_model_mcx_adapter import \ +from simpa.core.simulation_modules.optical_module.mcx_adapter import \ MCXAdapter from simpa.utils import Settings from simpa.io_handling import save_data_field, load_data_field from simpa.utils import TISSUE_LIBRARY -from simpa.core.processing_components import ProcessingComponent +from simpa.core.processing_components import ProcessingComponentBase import os -class IterativeqPAI(ProcessingComponent): +class IterativeqPAI(ProcessingComponentBase): """ Applies iterative qPAI Algorithm [1] on simulated initial pressure map and saves the reconstruction result in the hdf5 output file. If a 2-d map of initial_pressure is passed the algorithm saves @@ -45,7 +45,7 @@ class IterativeqPAI(ProcessingComponent): """ def __init__(self, global_settings, component_settings_key: str): - super(ProcessingComponent, self).__init__(global_settings=global_settings) + super(ProcessingComponentBase, self).__init__(global_settings=global_settings) self.global_settings = global_settings self.optical_settings = global_settings.get_optical_settings() diff --git a/simpa/core/processing_components/monospectral/noise/gamma_noise.py b/simpa/core/processing_components/monospectral/noise/gamma_noise.py index 011ddffa..5d87ca6c 100644 --- a/simpa/core/processing_components/monospectral/noise/gamma_noise.py +++ b/simpa/core/processing_components/monospectral/noise/gamma_noise.py @@ -5,13 +5,13 @@ import numpy as np import torch -from simpa.core.processing_components import ProcessingComponent +from simpa.core.processing_components import ProcessingComponentBase from simpa.io_handling import load_data_field, save_data_field from simpa.utils import Tags from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined -class GammaNoise(ProcessingComponent): +class GammaNoise(ProcessingComponentBase): """ Applies Gamma noise to the defined data field. The noise will be applied to all wavelengths. diff --git a/simpa/core/processing_components/monospectral/noise/gaussian_noise.py b/simpa/core/processing_components/monospectral/noise/gaussian_noise.py index 5c31a50d..98dba54f 100644 --- a/simpa/core/processing_components/monospectral/noise/gaussian_noise.py +++ b/simpa/core/processing_components/monospectral/noise/gaussian_noise.py @@ -5,13 +5,13 @@ from simpa.utils import Tags from simpa.utils import EPS from simpa.io_handling import load_data_field, save_data_field -from simpa.core.processing_components import ProcessingComponent +from simpa.core.processing_components import ProcessingComponentBase from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined import numpy as np import torch -class GaussianNoise(ProcessingComponent): +class GaussianNoise(ProcessingComponentBase): """ Applies Gaussian noise to the defined data field. The noise will be applied to all wavelengths. diff --git a/simpa/core/processing_components/monospectral/noise/poisson_noise.py b/simpa/core/processing_components/monospectral/noise/poisson_noise.py index 0fd544e6..0f847f17 100644 --- a/simpa/core/processing_components/monospectral/noise/poisson_noise.py +++ b/simpa/core/processing_components/monospectral/noise/poisson_noise.py @@ -4,13 +4,13 @@ from simpa.utils import Tags from simpa.io_handling import load_data_field, save_data_field -from simpa.core.processing_components import ProcessingComponent +from simpa.core.processing_components import ProcessingComponentBase from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined import numpy as np import torch -class PoissonNoise(ProcessingComponent): +class PoissonNoise(ProcessingComponentBase): """ Applies Poisson noise to the defined data field. The noise will be applied to all wavelengths. diff --git a/simpa/core/processing_components/monospectral/noise/salt_and_pepper_noise.py b/simpa/core/processing_components/monospectral/noise/salt_and_pepper_noise.py index 4204b32e..b6c70fba 100644 --- a/simpa/core/processing_components/monospectral/noise/salt_and_pepper_noise.py +++ b/simpa/core/processing_components/monospectral/noise/salt_and_pepper_noise.py @@ -4,13 +4,13 @@ from simpa.utils import Tags from simpa.io_handling import load_data_field, save_data_field -from simpa.core.processing_components import ProcessingComponent +from simpa.core.processing_components import ProcessingComponentBase from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined import numpy as np import torch -class SaltAndPepperNoise(ProcessingComponent): +class SaltAndPepperNoise(ProcessingComponentBase): """ Applies salt and pepper noise to the defined data field. The noise will be applied to all wavelengths. diff --git a/simpa/core/processing_components/monospectral/noise/uniform_noise.py b/simpa/core/processing_components/monospectral/noise/uniform_noise.py index 5610dc43..befc0de4 100644 --- a/simpa/core/processing_components/monospectral/noise/uniform_noise.py +++ b/simpa/core/processing_components/monospectral/noise/uniform_noise.py @@ -4,13 +4,13 @@ from simpa.utils import Tags from simpa.io_handling import load_data_field, save_data_field -from simpa.core.processing_components import ProcessingComponent +from simpa.core.processing_components import ProcessingComponentBase from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined import numpy as np import torch -class UniformNoise(ProcessingComponent): +class UniformNoise(ProcessingComponentBase): """ Applies uniform noise to the defined data field. The noise will be applied to all wavelengths. diff --git a/simpa/core/processing_components/processing_component.py b/simpa/core/processing_components/processing_component_base.py similarity index 69% rename from simpa/core/processing_components/processing_component.py rename to simpa/core/processing_components/processing_component_base.py index 881a8f2d..e74f6686 100644 --- a/simpa/core/processing_components/processing_component.py +++ b/simpa/core/processing_components/processing_component_base.py @@ -3,19 +3,19 @@ # SPDX-License-Identifier: MIT from abc import ABC -from simpa.core import PipelineModule +from simpa.core import PipelineElementBase -class ProcessingComponent(PipelineModule, ABC): +class ProcessingComponentBase(PipelineElementBase, ABC): """ Defines a pipeline processing component, which can be used to pre- or post-process simulation data. """ def __init__(self, global_settings, component_settings_key: str): """ - Initialises the ProcessingComponent. + Initialises the ProcessingComponent object. :param component_settings_key: The key where the component settings are stored in """ - super(ProcessingComponent, self).__init__(global_settings=global_settings) + super(ProcessingComponentBase, self).__init__(global_settings=global_settings) self.component_settings = global_settings[component_settings_key] diff --git a/simpa/core/simulation_modules/__init__.py b/simpa/core/simulation_modules/__init__.py index 5344c009..2da4c6d2 100644 --- a/simpa/core/simulation_modules/__init__.py +++ b/simpa/core/simulation_modules/__init__.py @@ -2,4 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from .simulation_module import SimulationModule +from .simulation_module_base import SimulationModuleBase diff --git a/simpa/core/simulation_modules/optical_simulation_module/__init__.py b/simpa/core/simulation_modules/acoustic_module/__init__.py similarity index 67% rename from simpa/core/simulation_modules/optical_simulation_module/__init__.py rename to simpa/core/simulation_modules/acoustic_module/__init__.py index cf03c952..9ad0c1ea 100644 --- a/simpa/core/simulation_modules/optical_simulation_module/__init__.py +++ b/simpa/core/simulation_modules/acoustic_module/__init__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from .optical_forward_model_base_adapter import OpticalForwardModuleBase +from .acoustic_adapter_base import AcousticAdapterBase diff --git a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_base_adapter.py b/simpa/core/simulation_modules/acoustic_module/acoustic_adapter_base.py similarity index 94% rename from simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_base_adapter.py rename to simpa/core/simulation_modules/acoustic_module/acoustic_adapter_base.py index e9f33d10..85c4dd07 100644 --- a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_base_adapter.py +++ b/simpa/core/simulation_modules/acoustic_module/acoustic_adapter_base.py @@ -4,7 +4,7 @@ from abc import abstractmethod import numpy as np -from simpa.core.simulation_modules import SimulationModule +from simpa.core.simulation_modules import SimulationModuleBase from simpa.utils import Tags, Settings from simpa.io_handling.io_hdf5 import save_hdf5 from simpa.utils.dict_path_manager import generate_dict_path @@ -12,7 +12,7 @@ from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined -class AcousticForwardModelBaseAdapter(SimulationModule): +class AcousticAdapterBase(SimulationModuleBase): """ This method is the entry method for running an acoustic forward model. It is invoked in the *simpa.core.simulation.simulate* method, but can also be called @@ -32,7 +32,7 @@ class AcousticForwardModelBaseAdapter(SimulationModule): """ def __init__(self, global_settings: Settings): - super(AcousticForwardModelBaseAdapter, self).__init__(global_settings=global_settings) + super(AcousticAdapterBase, self).__init__(global_settings=global_settings) def load_component_settings(self) -> Settings: """Implements abstract method to serve acoustic settings as component settings diff --git a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_test_adapter.py b/simpa/core/simulation_modules/acoustic_module/acoustic_test_adapter.py similarity index 75% rename from simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_test_adapter.py rename to simpa/core/simulation_modules/acoustic_module/acoustic_test_adapter.py index 9350da60..9477cb3e 100644 --- a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_model_test_adapter.py +++ b/simpa/core/simulation_modules/acoustic_module/acoustic_test_adapter.py @@ -4,10 +4,10 @@ import numpy as np from simpa.utils import Tags -from simpa.core.simulation_modules.acoustic_forward_module import AcousticForwardModelBaseAdapter +from simpa.core.simulation_modules.acoustic_module import AcousticAdapterBase -class AcousticForwardModelTestAdapter(AcousticForwardModelBaseAdapter): +class AcousticTestAdapter(AcousticAdapterBase): def forward_model(self, device) -> np.ndarray: diff --git a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_module_k_wave_adapter.py b/simpa/core/simulation_modules/acoustic_module/k_wave_adapter.py similarity index 98% rename from simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_module_k_wave_adapter.py rename to simpa/core/simulation_modules/acoustic_module/k_wave_adapter.py index 7bb4e2b1..b72ceee5 100644 --- a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_module_k_wave_adapter.py +++ b/simpa/core/simulation_modules/acoustic_module/k_wave_adapter.py @@ -12,8 +12,8 @@ from simpa.core.device_digital_twins import (CurvedArrayDetectionGeometry, DetectionGeometryBase) -from simpa.core.simulation_modules.acoustic_forward_module import \ - AcousticForwardModelBaseAdapter +from simpa.core.simulation_modules.acoustic_module import \ + AcousticAdapterBase from simpa.io_handling.io_hdf5 import load_data_field, save_hdf5 from simpa.utils import Tags from simpa.utils.matlab import generate_matlab_cmd @@ -23,9 +23,9 @@ from simpa.utils.settings import Settings -class KWaveAdapter(AcousticForwardModelBaseAdapter): +class KWaveAdapter(AcousticAdapterBase): """ - The KwaveAcousticForwardModel adapter enables acoustic simulations to be run with the + The KwaveAdapter enables acoustic simulations to be run with the k-wave MATLAB toolbox. k-Wave is a free toolbox (http://www.k-wave.org/) developed by Bradley Treeby and Ben Cox (University College London) and Jiri Jaros (Brno University of Technology). diff --git a/simpa/core/simulation_modules/acoustic_forward_module/simulate_2D.m b/simpa/core/simulation_modules/acoustic_module/simulate_2D.m similarity index 100% rename from simpa/core/simulation_modules/acoustic_forward_module/simulate_2D.m rename to simpa/core/simulation_modules/acoustic_module/simulate_2D.m diff --git a/simpa/core/simulation_modules/acoustic_forward_module/simulate_3D.m b/simpa/core/simulation_modules/acoustic_module/simulate_3D.m similarity index 100% rename from simpa/core/simulation_modules/acoustic_forward_module/simulate_3D.m rename to simpa/core/simulation_modules/acoustic_module/simulate_3D.m diff --git a/simpa/core/simulation_modules/acoustic_forward_module/__init__.py b/simpa/core/simulation_modules/optical_module/__init__.py similarity index 65% rename from simpa/core/simulation_modules/acoustic_forward_module/__init__.py rename to simpa/core/simulation_modules/optical_module/__init__.py index 65d4898b..7666c9bf 100644 --- a/simpa/core/simulation_modules/acoustic_forward_module/__init__.py +++ b/simpa/core/simulation_modules/optical_module/__init__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from .acoustic_forward_model_base_adapter import AcousticForwardModelBaseAdapter +from .optical_adapter_base import OpticalAdapterBase diff --git a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_adapter.py b/simpa/core/simulation_modules/optical_module/mcx_adapter.py similarity index 98% rename from simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_adapter.py rename to simpa/core/simulation_modules/optical_module/mcx_adapter.py index da9b52d6..bd0ac7e1 100644 --- a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_adapter.py +++ b/simpa/core/simulation_modules/optical_module/mcx_adapter.py @@ -5,7 +5,7 @@ import numpy as np import subprocess from simpa.utils import Tags, Settings -from simpa.core.simulation_modules.optical_simulation_module import OpticalForwardModuleBase +from simpa.core.simulation_modules.optical_module import OpticalAdapterBase from simpa.core.device_digital_twins.illumination_geometries import IlluminationGeometryBase import json import jdata @@ -13,7 +13,7 @@ from typing import List, Dict, Tuple -class MCXAdapter(OpticalForwardModuleBase): +class MCXAdapter(OpticalAdapterBase): """ This class implements a bridge to the mcx framework to integrate mcx into SIMPA. This adapter only allows for computation of fluence, for computations of diffuse reflectance, take a look at `simpa.ReflectanceMcxAdapter` diff --git a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_reflectance_adapter.py b/simpa/core/simulation_modules/optical_module/mcx_reflectance_adapter.py similarity index 98% rename from simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_reflectance_adapter.py rename to simpa/core/simulation_modules/optical_module/mcx_reflectance_adapter.py index fa9a64a7..d336deef 100644 --- a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_reflectance_adapter.py +++ b/simpa/core/simulation_modules/optical_module/mcx_reflectance_adapter.py @@ -8,11 +8,11 @@ from typing import List, Tuple, Dict, Union from simpa.utils import Tags, Settings -from simpa.core.simulation_modules.optical_simulation_module.optical_forward_model_mcx_adapter import MCXAdapter +from simpa.core.simulation_modules.optical_module.mcx_adapter import MCXAdapter from simpa.core.device_digital_twins import IlluminationGeometryBase, PhotoacousticDevice -class MCXAdapterReflectance(MCXAdapter): +class MCXReflectanceAdapter(MCXAdapter): """ This class implements a bridge to the mcx framework to integrate mcx into SIMPA. This class targets specifically diffuse reflectance simulations. Specifically, it implements the capability to run diffuse reflectance simulations. @@ -35,7 +35,7 @@ def __init__(self, global_settings: Settings): :param global_settings: global settings used during simulations """ - super(MCXAdapterReflectance, self).__init__(global_settings=global_settings) + super(MCXReflectanceAdapter, self).__init__(global_settings=global_settings) self.mcx_photon_data_file = None self.padded = None self.mcx_output_suffixes = {'mcx_volumetric_data_file': '.jnii', diff --git a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_base_adapter.py b/simpa/core/simulation_modules/optical_module/optical_adapter_base.py similarity index 97% rename from simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_base_adapter.py rename to simpa/core/simulation_modules/optical_module/optical_adapter_base.py index 9e2a3b6a..fe7e3e78 100644 --- a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_base_adapter.py +++ b/simpa/core/simulation_modules/optical_module/optical_adapter_base.py @@ -6,7 +6,7 @@ import numpy as np -from simpa.core.simulation_modules import SimulationModule +from simpa.core.simulation_modules import SimulationModuleBase from simpa.core.device_digital_twins import (IlluminationGeometryBase, PhotoacousticDevice) from simpa.io_handling.io_hdf5 import load_data_field, save_hdf5 @@ -16,7 +16,7 @@ assert_array_well_defined -class OpticalForwardModuleBase(SimulationModule): +class OpticalAdapterBase(SimulationModuleBase): """ Use this class as a base for implementations of optical forward models. This class has the attributes `self.temporary_output_files` which stores file paths that are temporarily created as @@ -24,7 +24,7 @@ class OpticalForwardModuleBase(SimulationModule): """ def __init__(self, global_settings: Settings): - super(OpticalForwardModuleBase, self).__init__(global_settings=global_settings) + super(OpticalAdapterBase, self).__init__(global_settings=global_settings) self.nx = None self.ny = None self.nz = None diff --git a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_test_adapter.py b/simpa/core/simulation_modules/optical_module/optical_test_adapter.py similarity index 74% rename from simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_test_adapter.py rename to simpa/core/simulation_modules/optical_module/optical_test_adapter.py index fb891954..bb1a7b49 100644 --- a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_test_adapter.py +++ b/simpa/core/simulation_modules/optical_module/optical_test_adapter.py @@ -2,11 +2,11 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from simpa.core.simulation_modules.optical_simulation_module import OpticalForwardModuleBase +from simpa.core.simulation_modules.optical_module import OpticalAdapterBase from simpa import Tags -class OpticalForwardModelTestAdapter(OpticalForwardModuleBase): +class OpticalTestAdapter(OpticalAdapterBase): """ This Adapter was created for testing purposes and only """ diff --git a/simpa/core/simulation_modules/reconstruction_module/__init__.py b/simpa/core/simulation_modules/reconstruction_module/__init__.py index bc5903bc..7c084f66 100644 --- a/simpa/core/simulation_modules/reconstruction_module/__init__.py +++ b/simpa/core/simulation_modules/reconstruction_module/__init__.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from .reconstruction_module_base_adapter import ReconstructionAdapterBase +from .reconstruction_adapter_base import ReconstructionAdapterBase from simpa.utils import Tags, Settings diff --git a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_delay_and_sum_adapter.py b/simpa/core/simulation_modules/reconstruction_module/delay_and_sum_adapter.py similarity index 100% rename from simpa/core/simulation_modules/reconstruction_module/reconstruction_module_delay_and_sum_adapter.py rename to simpa/core/simulation_modules/reconstruction_module/delay_and_sum_adapter.py diff --git a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_delay_multiply_and_sum_adapter.py b/simpa/core/simulation_modules/reconstruction_module/delay_multiply_and_sum_adapter.py similarity index 100% rename from simpa/core/simulation_modules/reconstruction_module/reconstruction_module_delay_multiply_and_sum_adapter.py rename to simpa/core/simulation_modules/reconstruction_module/delay_multiply_and_sum_adapter.py diff --git a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_base_adapter.py b/simpa/core/simulation_modules/reconstruction_module/reconstruction_adapter_base.py similarity index 97% rename from simpa/core/simulation_modules/reconstruction_module/reconstruction_module_base_adapter.py rename to simpa/core/simulation_modules/reconstruction_module/reconstruction_adapter_base.py index 2032960d..90d17099 100644 --- a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_base_adapter.py +++ b/simpa/core/simulation_modules/reconstruction_module/reconstruction_adapter_base.py @@ -7,7 +7,7 @@ from simpa.core.device_digital_twins import PhotoacousticDevice from simpa.io_handling.io_hdf5 import load_data_field from abc import abstractmethod -from simpa.core.simulation_modules import SimulationModule +from simpa.core.simulation_modules import SimulationModuleBase from simpa.utils.dict_path_manager import generate_dict_path from simpa.io_handling.io_hdf5 import save_hdf5 import numpy as np @@ -16,7 +16,7 @@ from simpa.utils.quality_assurance.data_sanity_testing import assert_array_well_defined -class ReconstructionAdapterBase(SimulationModule): +class ReconstructionAdapterBase(SimulationModuleBase): """ This class is the main entry point to perform image reconstruction using the SIMPA toolkit. All information necessary for the respective reconstruction method must be contained in the diff --git a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_test_adapter.py b/simpa/core/simulation_modules/reconstruction_module/reconstruction_test_adapter.py similarity index 85% rename from simpa/core/simulation_modules/reconstruction_module/reconstruction_module_test_adapter.py rename to simpa/core/simulation_modules/reconstruction_module/reconstruction_test_adapter.py index e3456c4e..4e2a7076 100644 --- a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_test_adapter.py +++ b/simpa/core/simulation_modules/reconstruction_module/reconstruction_test_adapter.py @@ -5,7 +5,7 @@ from simpa.core.simulation_modules.reconstruction_module import ReconstructionAdapterBase -class ReconstructionModuleTestAdapter(ReconstructionAdapterBase): +class ReconstructionTestAdapter(ReconstructionAdapterBase): def reconstruction_algorithm(self, time_series_sensor_data, detection_geometry): return time_series_sensor_data / 10 + 5 diff --git a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_signed_delay_multiply_and_sum_adapter.py b/simpa/core/simulation_modules/reconstruction_module/signed_delay_multiply_and_sum_adapter.py similarity index 100% rename from simpa/core/simulation_modules/reconstruction_module/reconstruction_module_signed_delay_multiply_and_sum_adapter.py rename to simpa/core/simulation_modules/reconstruction_module/signed_delay_multiply_and_sum_adapter.py diff --git a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_time_reversal_adapter.py b/simpa/core/simulation_modules/reconstruction_module/time_reversal_adapter.py similarity index 100% rename from simpa/core/simulation_modules/reconstruction_module/reconstruction_module_time_reversal_adapter.py rename to simpa/core/simulation_modules/reconstruction_module/time_reversal_adapter.py diff --git a/simpa/core/simulation_modules/simulation_module.py b/simpa/core/simulation_modules/simulation_module_base.py similarity index 85% rename from simpa/core/simulation_modules/simulation_module.py rename to simpa/core/simulation_modules/simulation_module_base.py index c87e1e26..e20e2502 100644 --- a/simpa/core/simulation_modules/simulation_module.py +++ b/simpa/core/simulation_modules/simulation_module_base.py @@ -4,11 +4,11 @@ from abc import abstractmethod -from simpa.core import PipelineModule +from simpa.core import PipelineElementBase from simpa.utils import Settings -class SimulationModule(PipelineModule): +class SimulationModuleBase(PipelineElementBase): """ Defines a simulation module that is a step in the simulation pipeline. Each simulation module can only be one of Volume Creation, Light Propagation Modeling, Acoustic Wave Propagation Modeling, Image Reconstruction. @@ -19,7 +19,7 @@ def __init__(self, global_settings: Settings): :param global_settings: The SIMPA settings dictionary :type global_settings: Settings """ - super(SimulationModule, self).__init__(global_settings=global_settings) + super(SimulationModuleBase, self).__init__(global_settings=global_settings) self.component_settings = self.load_component_settings() if self.component_settings is None: raise ValueError("The component settings should not be None at this point") diff --git a/simpa/core/simulation_modules/volume_creation_module/__init__.py b/simpa/core/simulation_modules/volume_creation_module/__init__.py index bde3b59f..c39607ef 100644 --- a/simpa/core/simulation_modules/volume_creation_module/__init__.py +++ b/simpa/core/simulation_modules/volume_creation_module/__init__.py @@ -2,4 +2,4 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from .volume_creator_base_adapter import VolumeCreatorModuleBase +from .volume_creation_adapter_base import VolumeCreationAdapterBase diff --git a/simpa/core/simulation_modules/volume_creation_module/volume_creation_module_model_based_adapter.py b/simpa/core/simulation_modules/volume_creation_module/model_based_adapter.py similarity index 98% rename from simpa/core/simulation_modules/volume_creation_module/volume_creation_module_model_based_adapter.py rename to simpa/core/simulation_modules/volume_creation_module/model_based_adapter.py index 2379adf3..0061928f 100644 --- a/simpa/core/simulation_modules/volume_creation_module/volume_creation_module_model_based_adapter.py +++ b/simpa/core/simulation_modules/volume_creation_module/model_based_adapter.py @@ -2,7 +2,7 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from simpa.core.simulation_modules.volume_creation_module import VolumeCreatorModuleBase +from simpa.core.simulation_modules.volume_creation_module import VolumeCreationAdapterBase from simpa.utils.libraries.structure_library import priority_sorted_structures from simpa.utils import Tags import numpy as np @@ -10,7 +10,7 @@ import torch -class ModelBasedVolumeCreationAdapter(VolumeCreatorModuleBase): +class ModelBasedAdapter(VolumeCreationAdapterBase): """ The model-based volume creator uses a set of rules how to generate structures to create a simulation volume. diff --git a/simpa/core/simulation_modules/volume_creation_module/volume_creation_module_segmentation_based_adapter.py b/simpa/core/simulation_modules/volume_creation_module/segmentation_based_adapter.py similarity index 96% rename from simpa/core/simulation_modules/volume_creation_module/volume_creation_module_segmentation_based_adapter.py rename to simpa/core/simulation_modules/volume_creation_module/segmentation_based_adapter.py index 490ce2eb..260ea585 100644 --- a/simpa/core/simulation_modules/volume_creation_module/volume_creation_module_segmentation_based_adapter.py +++ b/simpa/core/simulation_modules/volume_creation_module/segmentation_based_adapter.py @@ -2,7 +2,7 @@ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -from simpa.core.simulation_modules.volume_creation_module import VolumeCreatorModuleBase +from simpa.core.simulation_modules.volume_creation_module import VolumeCreationAdapterBase from simpa.utils import Tags from simpa.utils.constants import property_tags from simpa.io_handling import save_hdf5 @@ -10,7 +10,7 @@ import torch -class SegmentationBasedVolumeCreationAdapter(VolumeCreatorModuleBase): +class SegmentationBasedAdapter(VolumeCreationAdapterBase): """ This volume creator expects a np.ndarray to be in the settigs under the Tags.INPUT_SEGMENTATION_VOLUME tag and uses this array diff --git a/simpa/core/simulation_modules/volume_creation_module/volume_creator_base_adapter.py b/simpa/core/simulation_modules/volume_creation_module/volume_creation_adapter_base.py similarity index 94% rename from simpa/core/simulation_modules/volume_creation_module/volume_creator_base_adapter.py rename to simpa/core/simulation_modules/volume_creation_module/volume_creation_adapter_base.py index 6b9f0753..af3f47d6 100644 --- a/simpa/core/simulation_modules/volume_creation_module/volume_creator_base_adapter.py +++ b/simpa/core/simulation_modules/volume_creation_module/volume_creation_adapter_base.py @@ -7,19 +7,19 @@ from simpa.utils import Tags from simpa.utils.constants import wavelength_independent_properties, property_tags import torch -from simpa.core.simulation_modules import SimulationModule +from simpa.core.simulation_modules import SimulationModuleBase from simpa.io_handling import save_data_field from simpa.utils.quality_assurance.data_sanity_testing import assert_equal_shapes, assert_array_well_defined -class VolumeCreatorModuleBase(SimulationModule): +class VolumeCreationAdapterBase(SimulationModuleBase): """ Use this class to define your own volume creation adapter. """ def __init__(self, global_settings: Settings): - super(VolumeCreatorModuleBase, self).__init__(global_settings=global_settings) + super(VolumeCreationAdapterBase, self).__init__(global_settings=global_settings) def load_component_settings(self) -> Settings: """Implements abstract method to serve volume creation settings as component settings diff --git a/simpa/utils/tags.py b/simpa/utils/tags.py index 39f3c4f4..3bb90d04 100644 --- a/simpa/utils/tags.py +++ b/simpa/utils/tags.py @@ -113,9 +113,9 @@ class Tags: Usage: module volume_creation_module, naming convention """ - VOLUME_CREATOR_SEGMENTATION_BASED = "volume_creator_segmentation_based" + VOLUME_CREATOR_SEGMENTATION_BASED = "segmentation_based_adapter" """ - Corresponds to the SegmentationBasedVolumeCreator.\n + Corresponds to the SegmentationBasedAdapter.\n Usage: module volume_creation_module, naming convention """ diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index b1ce4c70..e76e0045 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -144,7 +144,7 @@ def create_example_tissue(): # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), + sp.ModelBasedAdapter(settings), sp.MCXAdapter(settings), sp.FieldOfViewCropping(settings), ] diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index 73961f87..4e892ba0 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -117,14 +117,14 @@ def create_example_tissue(): if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), + sp.ModelBasedAdapter(settings), sp.MCXAdapter(settings), sp.GaussianNoise(settings, "noise_model_1") ] else: pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapterReflectance(settings), + sp.ModelBasedAdapter(settings), + sp.MCXReflectanceAdapter(settings), ] diff --git a/simpa_examples/minimal_optical_simulation_uniform_cube.py b/simpa_examples/minimal_optical_simulation_uniform_cube.py index 39f299e5..2b68da3f 100644 --- a/simpa_examples/minimal_optical_simulation_uniform_cube.py +++ b/simpa_examples/minimal_optical_simulation_uniform_cube.py @@ -76,8 +76,8 @@ def create_example_tissue(): }) pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapterReflectance(settings), + sp.ModelBasedAdapter(settings), + sp.MCXReflectanceAdapter(settings), ] device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 7c5de16d..2571d518 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -16,7 +16,7 @@ def create_pipeline(_settings: sp.Settings): return [ - sp.ModelBasedVolumeCreationAdapter(settings), + sp.ModelBasedAdapter(settings), sp.MCXAdapter(settings), sp.KWaveAdapter(settings), sp.FieldOfViewCropping(settings), diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 68bb856c..44e6b9fb 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -176,7 +176,7 @@ def create_example_tissue(): SIMULATION_PIPELINE = [ - sp.ModelBasedVolumeCreationAdapter(settings), + sp.ModelBasedAdapter(settings), sp.MCXAdapter(settings), sp.GaussianNoise(settings, "noise_initial_pressure"), sp.KWaveAdapter(settings), diff --git a/simpa_examples/perform_iterative_qPAI_reconstruction.py b/simpa_examples/perform_iterative_qPAI_reconstruction.py index 90d4ec93..1d553653 100644 --- a/simpa_examples/perform_iterative_qPAI_reconstruction.py +++ b/simpa_examples/perform_iterative_qPAI_reconstruction.py @@ -132,7 +132,7 @@ def create_example_tissue(): # run pipeline including iterative qPAI method pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), + sp.ModelBasedAdapter(settings), sp.MCXAdapter(settings), sp.GaussianNoise(settings, "noise_model"), sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") diff --git a/simpa_examples/segmentation_loader.py b/simpa_examples/segmentation_loader.py index 09fe493e..d52311b3 100644 --- a/simpa_examples/segmentation_loader.py +++ b/simpa_examples/segmentation_loader.py @@ -77,7 +77,7 @@ def segmentation_class_mapping(): }) pipeline = [ - sp.SegmentationBasedVolumeCreationAdapter(settings), + sp.SegmentationBasedAdapter(settings), sp.MCXAdapter(settings) ] diff --git a/simpa_tests/automatic_tests/test_IPASC_export.py b/simpa_tests/automatic_tests/test_IPASC_export.py index cc638ec7..29cbc91f 100644 --- a/simpa_tests/automatic_tests/test_IPASC_export.py +++ b/simpa_tests/automatic_tests/test_IPASC_export.py @@ -10,13 +10,13 @@ from simpa.core.device_digital_twins import RSOMExplorerP50 from simpa.utils import Tags, Settings from simpa_tests.test_utils import create_test_structure_parameters -from simpa import ModelBasedVolumeCreationAdapter -from simpa.core.simulation_modules.optical_simulation_module.optical_forward_model_test_adapter import \ - OpticalForwardModelTestAdapter -from simpa.core.simulation_modules.acoustic_forward_module.acoustic_forward_model_test_adapter import \ - AcousticForwardModelTestAdapter -from simpa.core.simulation_modules.reconstruction_module.reconstruction_module_test_adapter import \ - ReconstructionModuleTestAdapter +from simpa import ModelBasedAdapter +from simpa.core.simulation_modules.optical_module.optical_test_adapter import \ + OpticalTestAdapter +from simpa.core.simulation_modules.acoustic_module.acoustic_test_adapter import \ + AcousticTestAdapter +from simpa.core.simulation_modules.reconstruction_module.reconstruction_test_adapter import \ + ReconstructionTestAdapter from pacfish import load_data as load_ipasc from simpa.io_handling import load_hdf5 as load_simpa @@ -63,21 +63,21 @@ def setUp(self) -> None: }) self.acoustic_simulation_pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), - OpticalForwardModelTestAdapter(self.settings), - AcousticForwardModelTestAdapter(self.settings), + ModelBasedAdapter(self.settings), + OpticalTestAdapter(self.settings), + AcousticTestAdapter(self.settings), ] self.optical_simulation_pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), - OpticalForwardModelTestAdapter(self.settings) + ModelBasedAdapter(self.settings), + OpticalTestAdapter(self.settings) ] self.full_simulation_pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), - OpticalForwardModelTestAdapter(self.settings), - AcousticForwardModelTestAdapter(self.settings), - ReconstructionModuleTestAdapter(self.settings) + ModelBasedAdapter(self.settings), + OpticalTestAdapter(self.settings), + AcousticTestAdapter(self.settings), + ReconstructionTestAdapter(self.settings) ] self.device = RSOMExplorerP50(0.1, 12, 12) diff --git a/simpa_tests/automatic_tests/test_create_a_volume.py b/simpa_tests/automatic_tests/test_create_a_volume.py index c67a8fc8..05652702 100644 --- a/simpa_tests/automatic_tests/test_create_a_volume.py +++ b/simpa_tests/automatic_tests/test_create_a_volume.py @@ -8,7 +8,7 @@ from simpa.core.simulation import simulate import os from simpa_tests.test_utils import create_test_structure_parameters -from simpa import ModelBasedVolumeCreationAdapter +from simpa import ModelBasedAdapter from simpa.core.device_digital_twins import RSOMExplorerP50 @@ -32,7 +32,7 @@ def test_create_volume(self): settings.set_volume_creation_settings({Tags.STRUCTURES: create_test_structure_parameters()}) simulation_pipeline = [ - ModelBasedVolumeCreationAdapter(settings) + ModelBasedAdapter(settings) ] simulate(simulation_pipeline, settings, RSOMExplorerP50(0.1, 1, 1)) diff --git a/simpa_tests/automatic_tests/test_linear_unmixing.py b/simpa_tests/automatic_tests/test_linear_unmixing.py index fe61e885..dc6e955e 100644 --- a/simpa_tests/automatic_tests/test_linear_unmixing.py +++ b/simpa_tests/automatic_tests/test_linear_unmixing.py @@ -50,7 +50,7 @@ def setUp(self): self.device = sp.PencilBeamIlluminationGeometry() # Run empty pipeline simulation to "fill" hdf5 file following usual procedure - pipeline = [sp.ModelBasedVolumeCreationAdapter(self.settings)] + pipeline = [sp.ModelBasedAdapter(self.settings)] sp.simulate(pipeline, self.settings, self.device) def test(self): diff --git a/simpa_tests/automatic_tests/test_noise_models.py b/simpa_tests/automatic_tests/test_noise_models.py index b23ac50d..d4dd256f 100644 --- a/simpa_tests/automatic_tests/test_noise_models.py +++ b/simpa_tests/automatic_tests/test_noise_models.py @@ -13,7 +13,7 @@ from simpa.core.simulation import simulate from simpa.utils import TISSUE_LIBRARY from simpa.io_handling import load_data_field -from simpa import ModelBasedVolumeCreationAdapter +from simpa import ModelBasedAdapter class TestNoiseModels(unittest.TestCase): @@ -63,7 +63,7 @@ def validate_noise_model_results(self, noise_model, noise_model_settings, settings["noise_model_settings"] = noise_model_settings simulation_pipeline = [ - ModelBasedVolumeCreationAdapter(settings), + ModelBasedAdapter(settings), noise_model(settings, "noise_model_settings") ] diff --git a/simpa_tests/automatic_tests/test_pipeline.py b/simpa_tests/automatic_tests/test_pipeline.py index 1b290fe9..e251a178 100644 --- a/simpa_tests/automatic_tests/test_pipeline.py +++ b/simpa_tests/automatic_tests/test_pipeline.py @@ -9,11 +9,11 @@ import numpy as np from simpa_tests.test_utils import create_test_structure_parameters import os -from simpa import ModelBasedVolumeCreationAdapter -from simpa.core.simulation_modules.optical_simulation_module.optical_forward_model_test_adapter import \ - OpticalForwardModelTestAdapter -from simpa.core.simulation_modules.acoustic_forward_module.acoustic_forward_model_test_adapter import \ - AcousticForwardModelTestAdapter +from simpa import ModelBasedAdapter +from simpa.core.simulation_modules.optical_module.optical_test_adapter import \ + OpticalTestAdapter +from simpa.core.simulation_modules.acoustic_module.acoustic_test_adapter import \ + AcousticTestAdapter from simpa.core.device_digital_twins import RSOMExplorerP50 @@ -72,9 +72,9 @@ def test_pipeline(self): }) simulation_pipeline = [ - ModelBasedVolumeCreationAdapter(settings), - OpticalForwardModelTestAdapter(settings), - AcousticForwardModelTestAdapter(settings), + ModelBasedAdapter(settings), + OpticalTestAdapter(settings), + AcousticTestAdapter(settings), ] simulate(simulation_pipeline, settings, RSOMExplorerP50(0.1, 1, 1)) diff --git a/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py b/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py index 93b0a5e6..6d9023a7 100644 --- a/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py +++ b/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py @@ -5,9 +5,9 @@ from simpa.core.device_digital_twins import SlitIlluminationGeometry, LinearArrayDetectionGeometry, PhotoacousticDevice from simpa import perform_k_wave_acoustic_forward_simulation -from simpa.core.simulation_modules.reconstruction_module.reconstruction_module_delay_and_sum_adapter import \ +from simpa.core.simulation_modules.reconstruction_module.delay_and_sum_adapter import \ reconstruct_delay_and_sum_pytorch -from simpa import MCXAdapter, ModelBasedVolumeCreationAdapter, \ +from simpa import MCXAdapter, ModelBasedAdapter, \ GaussianNoise from simpa.utils import Tags, Settings, TISSUE_LIBRARY from simpa.core.simulation import simulate @@ -88,7 +88,7 @@ def setup(self): # run pipeline including volume creation and optical mcx simulation self.pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings), GaussianNoise(self.settings, "noise_model") ] diff --git a/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py b/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py index 397d2da5..81f5017b 100644 --- a/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py +++ b/simpa_tests/manual_tests/digital_device_twins/SimulationWithMSOTInvision.py @@ -12,7 +12,7 @@ from simpa.visualisation.matplotlib_data_visualisation import visualise_data import numpy as np from simpa.utils.path_manager import PathManager -from simpa import DelayAndSumAdapter, MCXAdapter, KWaveAdapter, ModelBasedVolumeCreationAdapter, FieldOfViewCropping +from simpa import DelayAndSumAdapter, MCXAdapter, KWaveAdapter, ModelBasedAdapter, FieldOfViewCropping from simpa.core.device_digital_twins import * from simpa_tests.manual_tests import ManualIntegrationTestClass import os @@ -26,7 +26,7 @@ def setup(self): def create_pipeline(self, _settings: Settings): return [ - ModelBasedVolumeCreationAdapter(_settings), + ModelBasedAdapter(_settings), MCXAdapter(_settings), KWaveAdapter(_settings), FieldOfViewCropping(_settings), diff --git a/simpa_tests/manual_tests/image_reconstruction/DelayAndSumReconstruction.py b/simpa_tests/manual_tests/image_reconstruction/DelayAndSumReconstruction.py index 0f29f65d..b135f16a 100644 --- a/simpa_tests/manual_tests/image_reconstruction/DelayAndSumReconstruction.py +++ b/simpa_tests/manual_tests/image_reconstruction/DelayAndSumReconstruction.py @@ -8,7 +8,7 @@ from simpa.io_handling import load_data_field from simpa.core.simulation import simulate from simpa import KWaveAdapter, MCXAdapter, \ - DelayAndSumAdapter, ModelBasedVolumeCreationAdapter, GaussianNoise + DelayAndSumAdapter, ModelBasedAdapter, GaussianNoise from simpa import reconstruct_delay_and_sum_pytorch from simpa_tests.manual_tests import ReconstructionAlgorithmTestBaseClass @@ -28,7 +28,7 @@ def test_reconstruction_of_simulation(self): self.device.update_settings_for_use_of_model_based_volume_creator(self.settings) SIMUATION_PIPELINE = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings), GaussianNoise(self.settings, "noise_initial_pressure"), KWaveAdapter(self.settings), diff --git a/simpa_tests/manual_tests/image_reconstruction/DelayMultiplyAndSumReconstruction.py b/simpa_tests/manual_tests/image_reconstruction/DelayMultiplyAndSumReconstruction.py index 7a0de0d5..6c0151f5 100644 --- a/simpa_tests/manual_tests/image_reconstruction/DelayMultiplyAndSumReconstruction.py +++ b/simpa_tests/manual_tests/image_reconstruction/DelayMultiplyAndSumReconstruction.py @@ -7,7 +7,7 @@ from simpa.io_handling import load_data_field from simpa.core.simulation import simulate from simpa import KWaveAdapter, MCXAdapter, \ - DelayMultiplyAndSumAdapter, ModelBasedVolumeCreationAdapter, GaussianNoise + DelayMultiplyAndSumAdapter, ModelBasedAdapter, GaussianNoise from simpa import reconstruct_delay_multiply_and_sum_pytorch from simpa_tests.manual_tests import ReconstructionAlgorithmTestBaseClass @@ -27,7 +27,7 @@ def test_reconstruction_of_simulation(self): self.device.update_settings_for_use_of_model_based_volume_creator(self.settings) SIMUATION_PIPELINE = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings), GaussianNoise(self.settings, "noise_initial_pressure"), KWaveAdapter(self.settings), diff --git a/simpa_tests/manual_tests/image_reconstruction/PointSourceReconstruction.py b/simpa_tests/manual_tests/image_reconstruction/PointSourceReconstruction.py index e59f35e3..60e39425 100644 --- a/simpa_tests/manual_tests/image_reconstruction/PointSourceReconstruction.py +++ b/simpa_tests/manual_tests/image_reconstruction/PointSourceReconstruction.py @@ -12,7 +12,7 @@ from simpa.visualisation.matplotlib_data_visualisation import visualise_data import numpy as np from simpa.utils.path_manager import PathManager -from simpa import DelayAndSumAdapter, MCXAdapter, KWaveAdapter, ModelBasedVolumeCreationAdapter, FieldOfViewCropping +from simpa import DelayAndSumAdapter, MCXAdapter, KWaveAdapter, ModelBasedAdapter, FieldOfViewCropping from simpa.core.device_digital_twins import * from simpa.io_handling import load_data_field @@ -155,7 +155,7 @@ def create_point_source(): }) SIMUATION_PIPELINE = [ - ModelBasedVolumeCreationAdapter(settings), + ModelBasedAdapter(settings), MCXAdapter(settings), KWaveAdapter(settings), FieldOfViewCropping(settings), diff --git a/simpa_tests/manual_tests/image_reconstruction/SignedDelayMultiplyAndSumReconstruction.py b/simpa_tests/manual_tests/image_reconstruction/SignedDelayMultiplyAndSumReconstruction.py index 754cdd01..ef15aab9 100644 --- a/simpa_tests/manual_tests/image_reconstruction/SignedDelayMultiplyAndSumReconstruction.py +++ b/simpa_tests/manual_tests/image_reconstruction/SignedDelayMultiplyAndSumReconstruction.py @@ -7,7 +7,7 @@ from simpa.io_handling import load_data_field from simpa.core.simulation import simulate from simpa import KWaveAdapter, MCXAdapter, \ - SignedDelayMultiplyAndSumAdapter, ModelBasedVolumeCreationAdapter + SignedDelayMultiplyAndSumAdapter, ModelBasedAdapter from simpa.core.processing_components.monospectral.noise import GaussianNoise from simpa import reconstruct_signed_delay_multiply_and_sum_pytorch from simpa_tests.manual_tests import ReconstructionAlgorithmTestBaseClass @@ -28,7 +28,7 @@ def test_reconstruction_of_simulation(self): self.device.update_settings_for_use_of_model_based_volume_creator(self.settings) SIMUATION_PIPELINE = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings), GaussianNoise(self.settings, "noise_initial_pressure"), KWaveAdapter(self.settings), diff --git a/simpa_tests/manual_tests/image_reconstruction/TimeReversalReconstruction.py b/simpa_tests/manual_tests/image_reconstruction/TimeReversalReconstruction.py index 72a8c49f..cad47201 100644 --- a/simpa_tests/manual_tests/image_reconstruction/TimeReversalReconstruction.py +++ b/simpa_tests/manual_tests/image_reconstruction/TimeReversalReconstruction.py @@ -7,7 +7,7 @@ from simpa.io_handling import load_data_field from simpa.core.simulation import simulate from simpa import KWaveAdapter, MCXAdapter, \ - TimeReversalAdapter, ModelBasedVolumeCreationAdapter, GaussianNoise + TimeReversalAdapter, ModelBasedAdapter, GaussianNoise from simpa import reconstruct_delay_and_sum_pytorch from simpa_tests.manual_tests import ReconstructionAlgorithmTestBaseClass @@ -27,7 +27,7 @@ def test_reconstruction_of_simulation(self): self.device.update_settings_for_use_of_model_based_volume_creator(self.settings) SIMULATION_PIPELINE = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings), GaussianNoise(self.settings, "noise_initial_pressure"), KWaveAdapter(self.settings), diff --git a/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithInifinitesimalSlabExperiment.py b/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithInifinitesimalSlabExperiment.py index 5ac62e06..04199a5e 100644 --- a/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithInifinitesimalSlabExperiment.py +++ b/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithInifinitesimalSlabExperiment.py @@ -28,7 +28,7 @@ import matplotlib.pyplot as plt import numpy as np -from simpa import MCXAdapter, ModelBasedVolumeCreationAdapter +from simpa import MCXAdapter, ModelBasedAdapter from simpa.core.device_digital_twins import PhotoacousticDevice, PencilBeamIlluminationGeometry from simpa.core.simulation import simulate from simpa.io_handling import load_data_field @@ -208,7 +208,7 @@ def test_simulation(self, distance=10, expected_decay_ratio=np.e, scattering_val self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = anisotropy_value pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings) ] diff --git a/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithinHomogenousMedium.py b/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithinHomogenousMedium.py index 806863bd..bd6ca054 100644 --- a/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithinHomogenousMedium.py +++ b/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithinHomogenousMedium.py @@ -28,7 +28,7 @@ import matplotlib.pyplot as plt import numpy as np -from simpa import MCXAdapter, ModelBasedVolumeCreationAdapter +from simpa import MCXAdapter, ModelBasedAdapter from simpa.core.device_digital_twins import PhotoacousticDevice, PencilBeamIlluminationGeometry from simpa.core.simulation import simulate from simpa.io_handling import load_data_field @@ -223,7 +223,7 @@ def test_simultion(self, scattering_value_1=1e-30, self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = anisotropy_value_1 pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings) ] @@ -247,7 +247,7 @@ def test_simultion(self, scattering_value_1=1e-30, self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = 0.9 pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings) ] diff --git a/simpa_tests/manual_tests/optical_forward_models/CompareMCXResultsWithDiffusionTheory.py b/simpa_tests/manual_tests/optical_forward_models/CompareMCXResultsWithDiffusionTheory.py index 898fd1f6..939fc870 100644 --- a/simpa_tests/manual_tests/optical_forward_models/CompareMCXResultsWithDiffusionTheory.py +++ b/simpa_tests/manual_tests/optical_forward_models/CompareMCXResultsWithDiffusionTheory.py @@ -4,7 +4,7 @@ from simpa.utils import Tags, PathManager, Settings, TISSUE_LIBRARY from simpa.core.simulation import simulate -from simpa import ModelBasedVolumeCreationAdapter, MCXAdapter +from simpa import ModelBasedAdapter, MCXAdapter from simpa.core.device_digital_twins import PhotoacousticDevice, PencilBeamIlluminationGeometry from simpa.io_handling import load_data_field import numpy as np @@ -130,7 +130,7 @@ def run_simulation(self, distance, spacing): # run pipeline including volume creation and optical mcx simulation pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings), ] simulate(pipeline, self.settings, self.device) diff --git a/simpa_tests/manual_tests/optical_forward_models/ComputeDiffuseReflectance.py b/simpa_tests/manual_tests/optical_forward_models/ComputeDiffuseReflectance.py index e8e77855..f24944f5 100644 --- a/simpa_tests/manual_tests/optical_forward_models/ComputeDiffuseReflectance.py +++ b/simpa_tests/manual_tests/optical_forward_models/ComputeDiffuseReflectance.py @@ -4,7 +4,7 @@ from simpa.utils import Tags, PathManager, Settings, TISSUE_LIBRARY from simpa.core.simulation import simulate -from simpa import ModelBasedVolumeCreationAdapter, MCXAdapterReflectance +from simpa import ModelBasedAdapter, MCXReflectanceAdapter from simpa.core.device_digital_twins import PhotoacousticDevice, PencilBeamIlluminationGeometry from simpa.io_handling import load_data_field import numpy as np @@ -136,8 +136,8 @@ def run_simulation(self, distance, spacing): # run pipeline including volume creation and optical mcx simulation pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), - MCXAdapterReflectance(self.settings), + ModelBasedAdapter(self.settings), + MCXReflectanceAdapter(self.settings), ] simulate(pipeline, self.settings, self.device) diff --git a/simpa_tests/manual_tests/processing_components/QPAIReconstruction.py b/simpa_tests/manual_tests/processing_components/QPAIReconstruction.py index c262bcbb..21bf4e56 100644 --- a/simpa_tests/manual_tests/processing_components/QPAIReconstruction.py +++ b/simpa_tests/manual_tests/processing_components/QPAIReconstruction.py @@ -7,7 +7,7 @@ from simpa_tests.manual_tests import ManualIntegrationTestClass from simpa.core.device_digital_twins import RSOMExplorerP50 from simpa.core.processing_components.monospectral.iterative_qPAI_algorithm import IterativeqPAI -from simpa import MCXAdapter, ModelBasedVolumeCreationAdapter, \ +from simpa import MCXAdapter, ModelBasedAdapter, \ GaussianNoise from simpa.utils import Tags, Settings, TISSUE_LIBRARY from simpa.core.simulation import simulate @@ -85,7 +85,7 @@ def setup(self): # run pipeline including volume creation and optical mcx simulation pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings), GaussianNoise(self.settings, "noise_model") ] diff --git a/simpa_tests/manual_tests/processing_components/TestLinearUnmixingVisual.py b/simpa_tests/manual_tests/processing_components/TestLinearUnmixingVisual.py index 38896d64..7f76e1ac 100644 --- a/simpa_tests/manual_tests/processing_components/TestLinearUnmixingVisual.py +++ b/simpa_tests/manual_tests/processing_components/TestLinearUnmixingVisual.py @@ -65,7 +65,7 @@ def setup(self): # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS self.pipeline = [ - sp.ModelBasedVolumeCreationAdapter(self.settings) + sp.ModelBasedAdapter(self.settings) ] def perform_test(self): diff --git a/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py b/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py index f36f1fe2..f2d3a9d9 100644 --- a/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py +++ b/simpa_tests/manual_tests/test_with_experimental_measurements/ReproduceDISMeasurements.py @@ -24,7 +24,7 @@ from simpa.core.device_digital_twins import * import numpy as np from simpa.visualisation.matplotlib_data_visualisation import visualise_data -from simpa import ModelBasedVolumeCreationAdapter, MCXAdapter +from simpa import ModelBasedAdapter, MCXAdapter from simpa_tests.manual_tests.test_with_experimental_measurements.utils import read_reference_spectra, read_rxt_file from simpa_tests.manual_tests import ManualIntegrationTestClass import inspect @@ -177,7 +177,7 @@ def create_measurement_setup(sample_tickness_mm): }) self.pipeline = [ - ModelBasedVolumeCreationAdapter(self.settings), + ModelBasedAdapter(self.settings), MCXAdapter(self.settings), ] diff --git a/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py b/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py index 780d92a7..230a8e3c 100644 --- a/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py +++ b/simpa_tests/manual_tests/volume_creation/SegmentationLoader.py @@ -72,7 +72,7 @@ def segmentation_class_mapping(): }) self.pipeline = [ - sp.SegmentationBasedVolumeCreationAdapter(self.settings), + sp.SegmentationBasedAdapter(self.settings), sp.MCXAdapter(self.settings) ] From e1799f89e32fa18ee7bb2eb9a217d10df5062cd9 Mon Sep 17 00:00:00 2001 From: Leonie Boland Date: Wed, 7 Aug 2024 11:28:46 +0200 Subject: [PATCH 3/5] fixed manual test of k-wave convenience function --- .../KWaveAcousticForwardConvenienceFunction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py b/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py index 6d9023a7..f99f2f3f 100644 --- a/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py +++ b/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py @@ -128,7 +128,7 @@ def test_convenience_function(self): self.reconstructed = reconstruct_delay_and_sum_pytorch( time_series_data.copy(), self.device.get_detection_geometry(), speed_of_sound_in_m_per_s=1540, - time_spacing_in_s=1/40_000_000_000, + time_spacing_in_s=1/40_000_000, sensor_spacing_in_mm=self.device.get_detection_geometry().pitch_mm, recon_mode=Tags.RECONSTRUCTION_MODE_PRESSURE, ) From 8f78914ab0d49f711162be2de7a57652ef2762bb Mon Sep 17 00:00:00 2001 From: Leonie Boland Date: Wed, 7 Aug 2024 11:38:18 +0200 Subject: [PATCH 4/5] fixed manual tests in reconstruction convenience functions --- .../image_reconstruction/DelayAndSumReconstruction.py | 2 +- .../image_reconstruction/DelayMultiplyAndSumReconstruction.py | 2 +- .../SignedDelayMultiplyAndSumReconstruction.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/simpa_tests/manual_tests/image_reconstruction/DelayAndSumReconstruction.py b/simpa_tests/manual_tests/image_reconstruction/DelayAndSumReconstruction.py index b135f16a..c20d2901 100644 --- a/simpa_tests/manual_tests/image_reconstruction/DelayAndSumReconstruction.py +++ b/simpa_tests/manual_tests/image_reconstruction/DelayAndSumReconstruction.py @@ -49,7 +49,7 @@ def test_convenience_function(self): # reconstruct via convenience function self.reconstructed_image_convenience = reconstruct_delay_and_sum_pytorch(time_series_sensor_data, self.device.get_detection_geometry(), reconstruction_settings[Tags.DATA_FIELD_SPEED_OF_SOUND], 1.0 / ( - self.device.get_detection_geometry().sampling_frequency_MHz * 1000), self.settings[Tags.SPACING_MM], reconstruction_settings[Tags.RECONSTRUCTION_MODE], reconstruction_settings[Tags.RECONSTRUCTION_APODIZATION_METHOD]) + self.device.get_detection_geometry().sampling_frequency_MHz * 1_000_000), self.settings[Tags.SPACING_MM], reconstruction_settings[Tags.RECONSTRUCTION_MODE], reconstruction_settings[Tags.RECONSTRUCTION_APODIZATION_METHOD]) # apply envelope detection method if set if reconstruction_settings[Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION]: diff --git a/simpa_tests/manual_tests/image_reconstruction/DelayMultiplyAndSumReconstruction.py b/simpa_tests/manual_tests/image_reconstruction/DelayMultiplyAndSumReconstruction.py index 6c0151f5..e9a458a6 100644 --- a/simpa_tests/manual_tests/image_reconstruction/DelayMultiplyAndSumReconstruction.py +++ b/simpa_tests/manual_tests/image_reconstruction/DelayMultiplyAndSumReconstruction.py @@ -48,7 +48,7 @@ def test_convenience_function(self): # reconstruct via convenience function self.reconstructed_image_convenience = reconstruct_delay_multiply_and_sum_pytorch(time_series_sensor_data, self.device.get_detection_geometry(), reconstruction_settings[Tags.DATA_FIELD_SPEED_OF_SOUND], 1.0 / ( - self.device.get_detection_geometry().sampling_frequency_MHz * 1000), self.settings[Tags.SPACING_MM], reconstruction_settings[Tags.RECONSTRUCTION_MODE], reconstruction_settings[Tags.RECONSTRUCTION_APODIZATION_METHOD]) + self.device.get_detection_geometry().sampling_frequency_MHz * 1_000_000), self.settings[Tags.SPACING_MM], reconstruction_settings[Tags.RECONSTRUCTION_MODE], reconstruction_settings[Tags.RECONSTRUCTION_APODIZATION_METHOD]) # apply envelope detection method if set if reconstruction_settings[Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION]: diff --git a/simpa_tests/manual_tests/image_reconstruction/SignedDelayMultiplyAndSumReconstruction.py b/simpa_tests/manual_tests/image_reconstruction/SignedDelayMultiplyAndSumReconstruction.py index ef15aab9..ff61cc4e 100644 --- a/simpa_tests/manual_tests/image_reconstruction/SignedDelayMultiplyAndSumReconstruction.py +++ b/simpa_tests/manual_tests/image_reconstruction/SignedDelayMultiplyAndSumReconstruction.py @@ -49,7 +49,7 @@ def test_convenience_function(self): # reconstruct via convenience function self.reconstructed_image_convenience = reconstruct_signed_delay_multiply_and_sum_pytorch(time_series_sensor_data, self.device.get_detection_geometry(), reconstruction_settings[Tags.DATA_FIELD_SPEED_OF_SOUND], 1.0 / ( - self.device.get_detection_geometry().sampling_frequency_MHz * 1000), self.settings[Tags.SPACING_MM], reconstruction_settings[Tags.RECONSTRUCTION_MODE], reconstruction_settings[Tags.RECONSTRUCTION_APODIZATION_METHOD]) + self.device.get_detection_geometry().sampling_frequency_MHz * 1_000_000), self.settings[Tags.SPACING_MM], reconstruction_settings[Tags.RECONSTRUCTION_MODE], reconstruction_settings[Tags.RECONSTRUCTION_APODIZATION_METHOD]) # apply envelope detection method if set if reconstruction_settings[Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION]: From 0c34e504aa514f0d4223e9f86d9c05499576b703 Mon Sep 17 00:00:00 2001 From: Janek Grohl Date: Tue, 13 Aug 2024 12:24:29 +0100 Subject: [PATCH 5/5] fix minimal kwave test --- .../manual_tests/acoustic_forward_models/MinimalKWaveTest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simpa_tests/manual_tests/acoustic_forward_models/MinimalKWaveTest.py b/simpa_tests/manual_tests/acoustic_forward_models/MinimalKWaveTest.py index 7f781d5b..35e966f8 100644 --- a/simpa_tests/manual_tests/acoustic_forward_models/MinimalKWaveTest.py +++ b/simpa_tests/manual_tests/acoustic_forward_models/MinimalKWaveTest.py @@ -27,7 +27,7 @@ def setup(self): if os.path.exists(p0_path): self.initial_pressure = np.load(p0_path)["initial_pressure"] else: - self.initial_pressure = np.zeros((100, 100, 100)) + self.initial_pressure = np.zeros((100, 30, 100)) self.initial_pressure[50, :, 50] = 1 self.speed_of_sound = np.ones((100, 30, 100)) * self.SPEED_OF_SOUND self.density = np.ones((100, 30, 100)) * 1000