Skip to content

Commit

Permalink
Merge branch 'develop' into T323_clean_logging
Browse files Browse the repository at this point in the history
  • Loading branch information
frisograce authored Aug 14, 2024
2 parents 41cb707 + 3a224c3 commit 4f35ab3
Show file tree
Hide file tree
Showing 108 changed files with 2,407 additions and 1,274 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ docs/objects.inv

simpa_tests/figures
simpa_tests/figures/*
simpa_tests/**/figures
simpa_tests/**/figures/*
simpa_tests/manual_tests/figures
simpa_tests/manual_tests/reference_figures
simpa_tests/manual_tests/manual_tests_*
Expand Down Expand Up @@ -154,4 +156,4 @@ dmypy.json

# numpy files
*npy
/.mailmap
/.mailmap
25 changes: 15 additions & 10 deletions docs/source/benchmarking.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ It allows customization of initial spacing, final spacing, step size, output fil
To check and amend which simulations are run, see the [performance check script](../../simpa_examples/benchmarking/performance_check.py).

## Usage
To use this script, run it from the command line with the desired options. Please ensure you check two things before
running the script: First, ensure that the device will not be in use for the duration - ideally restart before
benchmarking - of the benchmarking process,
In order to be able to use this script, please first ensure that you have the dependencies required for the benchmarking
scripts. To do this, please navigate to the simpa directory and execute `pip install .[profile]`.

Now, you can run [performance_check.py](../../simpa_examples/benchmarking/performance_check.py) and [run_benchmarking.sh](../../simpa_examples/benchmarking/run_benchmarking.sh) from the command line with the desired
options. Please ensure you check two things before running the script: First, ensure that the device will not be in use
for the duration - ideally restart before benchmarking - of the benchmarking process,
as this will create large uncertainties within the outcomes. Second, ensure that you don't accidentally write over any
existing file by saving the files created by this script after runtime to different location.

The script will create multiple text files (eg. benchmarking_data_TIME_0.2.txt), showing the line by line profiling of
the most recent runs, as well as two csv's with the data from all the runs (benchmarking_data_frame.csv) and the means
and standard deviations of all the runs (benchmarking_data_frame_mean.csv).
The both scripts create text files (eg. benchmarking_data_TIME_0.2.txt), showing the line by line profiling of
the most recent runs. The [run_benchmarking.sh](../../simpa_examples/benchmarking/run_benchmarking.sh) also creates two csv's: one with the data from all the runs
(benchmarking_data_frame.csv) and one with the means and standard deviations of all the runs
(benchmarking_data_frame_mean.csv). With both scripts, unless the user intentionally changes the save folder name,
the output files will be overwritten.

Below is a description of the available options and how to use them.

Expand All @@ -28,8 +33,8 @@ Please put this in the conversation of the pull request, and not add it to the f

## Options
- **`-i, --init`**: First spacing to benchmark (default = 0.2mm).
- **`-c, --cease`**: Final spacing to benchmark (default = 0.4mm).
- **`-s, --step`**: Step between spacings (default = 0.1mm).
- **`-c, --cease`**: Final spacing to benchmark (default = 0.25mm).
- **`-s, --step`**: Step between spacings (default = 0.05mm).
- **`-f, --file`**: Where to store the output files (default = save in current directory; 'print' prints it in console).
- **`-t, --time`**: Profile times taken (if no profile is specified, all are set).
- **`-g, --gpu`**: Profile GPU usage (if no profile is specified, all are set).
Expand All @@ -41,8 +46,8 @@ Please put this in the conversation of the pull request, and not add it to the f
If no options are provided for initial spacing, final spacing, or step size, the script uses the following default
values:
- **Initial Spacing**: 0.2mm
- **Final Spacing**: 0.4mm
- **Step Size**: 0.1mm
- **Final Spacing**: 0.25mm
- **Step Size**: 0.05mm

If no profiling options are specified, all three profilers (time, GPU memory, and memory) are used by default.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
minimal_optical_simulation_heterogeneous_tissue
=========================================

.. literalinclude:: ../../simpa_examples/minimal_optical_simulation_heterogeneous_tissue.py
:language: python
:lines: 1-

6 changes: 6 additions & 0 deletions docs/source/simpa.utils.libraries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ libraries
simpa.utils.libraries.scattering_spectra_data
simpa.utils.libraries.structure_library

.. automodule:: simpa.utils.libraries.heterogeneity_generator
:members:
:undoc-members:
:show-inheritance:


.. automodule:: simpa.utils.libraries.literature_values
:members:
:undoc-members:
Expand Down
1 change: 1 addition & 0 deletions docs/source/simpa_examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ simpa\_examples
create_custom_tissues
linear_unmixing
minimal_optical_simulation
minimal_optical_simulation_heterogeneous_tissue
minimal_optical_simulation_uniform_cube
msot_invision_simulation
optical_and_acoustic_simulation
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "simpa"
dynamic = ["version"]
authors = [
{name = "Division of Intelligent Medical Systems (IMSY), DKFZ", email = "k.dreher@dkfz-heidelberg.de"},
{name = "Janek Groehl"}
{name = "Janek Groehl <janekgroehl@live.de>"}
]
description = "Simulation and Image Processing for Photonics and Acoustics"
license = {text = "MIT"}
Expand All @@ -32,7 +32,8 @@ dependencies = [
"wget>=3.2", # Is Public Domain (MIT compatible)
"jdata>=0.5.2", # Uses Apache 2.0-License (MIT compatible)
"pre-commit>=3.2.2", # Uses MIT-License (MIT compatible)
"PyWavelets" # Uses MIT-License (MIT compatible)
"PyWavelets", # Uses MIT-License (MIT compatible)
"scikit-learn>=1.1.0", # Uses BSD-License (MIT compatible)
]

[project.optional-dependencies]
Expand Down
35 changes: 18 additions & 17 deletions simpa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,38 @@
from .log import Logger
from importlib.metadata import version, PackageNotFoundError


try:
__version__ = version("simpa")
except PackageNotFoundError:
__version__ = "unknown version"
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
Expand Down
31 changes: 1 addition & 30 deletions simpa/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,33 +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
from .pipeline_element_base import PipelineElementBase
170 changes: 19 additions & 151 deletions simpa/core/device_digital_twins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 4f35ab3

Please sign in to comment.