From bb294ef4ce064d5a0c0fb91ac631a0b4c0ded9f8 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 21 Jun 2024 09:45:32 +0200 Subject: [PATCH 01/34] Making profiling produce documents, rather than printing in the terminal --- simpa/utils/profiling.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/simpa/utils/profiling.py b/simpa/utils/profiling.py index 7eb1a44e..a3cf2212 100644 --- a/simpa/utils/profiling.py +++ b/simpa/utils/profiling.py @@ -5,6 +5,10 @@ import os profile_type = os.getenv("SIMPA_PROFILE") +if os.getenv("SIMPA_PROFILE_SAVE_FILE"): + stream = open(os.getenv("SIMPA_PROFILE_SAVE_FILE"), 'w') +else: + stream = None if profile_type is None: # define a no-op @profile decorator def profile(f): @@ -14,16 +18,33 @@ def profile(f): from line_profiler import LineProfiler profile = LineProfiler() - atexit.register(profile.print_stats) + + def print_stats_atexit(): + return profile.print_stats(stream=stream, output_unit=10**(-6)) + atexit.register(print_stats_atexit) elif profile_type == "MEMORY": from memory_profiler import profile + profile = profile(stream=stream) elif profile_type == "GPU_MEMORY": - from pytorch_memlab import profile - from torch.cuda import memory_summary + from typing import Tuple + from pytorch_memlab.line_profiler.line_profiler import LineProfiler, DEFAULT_COLUMNS import atexit - @atexit.register - def print_memory_summary(): - print(memory_summary()) + global_line_profiler = LineProfiler() + global_line_profiler.enable() + + def profile(func, columns: Tuple[str, ...] = DEFAULT_COLUMNS): + """ + Check plagiarism problems + """ + import atexit + global_line_profiler.add_function(func) + + def print_stats_atexit(): + global_line_profiler.print_stats(func, columns, stream=stream) + + atexit.register(print_stats_atexit) + return func + else: raise RuntimeError("SIMPA_PROFILE env var is defined but invalid: valid values are TIME, MEMORY, or GPU_MEMORY") From 781c37f053e99ac72c5825570d6b5ac2e61be765 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 21 Jun 2024 09:45:54 +0200 Subject: [PATCH 02/34] Making simpa examples functions --- pyproject.toml | 1 + simpa_examples/__init__.py | 8 + simpa_examples/linear_unmixing.py | 318 ++++++------- simpa_examples/minimal_optical_simulation.py | 292 ++++++------ ...minimal_optical_simulation_uniform_cube.py | 159 ++++--- simpa_examples/msot_invision_simulation.py | 387 ++++++++-------- .../optical_and_acoustic_simulation.py | 380 ++++++++-------- .../perform_image_reconstruction.py | 59 ++- .../perform_iterative_qPAI_reconstruction.py | 427 +++++++++--------- ...KWaveAcousticForwardConvenienceFunction.py | 2 +- 10 files changed, 1064 insertions(+), 969 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 62f732e3..b674ed1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ repository = "https://github.com/IMSY-DKFZ/simpa" packages = [ { include = "simpa" }, { include = "simpa_tests" }, + { include = "simpa_examples"} ] # Requirements diff --git a/simpa_examples/__init__.py b/simpa_examples/__init__.py index 89cc8954..92a0960b 100644 --- a/simpa_examples/__init__.py +++ b/simpa_examples/__init__.py @@ -1,3 +1,11 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT + +from simpa_examples.linear_unmixing import run_linear_unmixing +from simpa_examples.minimal_optical_simulation import run_minimal_optical_simulation +from simpa_examples.minimal_optical_simulation_uniform_cube import run_minimal_optical_simulation_uniform_cube +from simpa_examples.msot_invision_simulation import run_msot_invision_simulation +from simpa_examples.optical_and_acoustic_simulation import run_optical_and_acoustic_simulation +from simpa_examples.perform_image_reconstruction import run_perform_image_reconstruction +from simpa_examples.perform_iterative_qPAI_reconstruction import run_perform_iterative_qPAI_reconstruction diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index b1ce4c70..5469a2a1 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -9,162 +9,174 @@ import simpa as sp from simpa import Tags from simpa.visualisation.matplotlib_data_visualisation import visualise_data +from simpa.utils.profiling import profile +from typing import Union + # FIXME temporary workaround for newest Intel architectures os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" -# TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you -# point to the correct file in the PathManager(). -path_manager = sp.PathManager() - -# set global params characterizing the simulated volume -VOLUME_TRANSDUCER_DIM_IN_MM = 75 -VOLUME_PLANAR_DIM_IN_MM = 20 -VOLUME_HEIGHT_IN_MM = 25 -SPACING = 0.25 -RANDOM_SEED = 471 -VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) - -# since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths -WAVELENGTHS = [750, 800, 850] - -def create_example_tissue(): +@profile +def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): """ - This is a very simple example script of how to create a tissue definition. - It contains a muscular background, an epidermis layer on top of the muscles - and two blood vessels. + + :param SPACING: The simulation spacing between voxels + :param path_manager: the path manager to be used, typically sp.PathManager + :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + :return: a run through of the example """ - background_dictionary = sp.Settings() - background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - - muscle_dictionary = sp.Settings() - muscle_dictionary[Tags.PRIORITY] = 1 - muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - - vessel_1_dictionary = sp.Settings() - vessel_1_dictionary[Tags.PRIORITY] = 3 - vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 10, - 5] - vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 12, - 5] - vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) - vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - - vessel_2_dictionary = sp.Settings() - vessel_2_dictionary[Tags.PRIORITY] = 3 - vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 10, - 5] - vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 12, - 5] - vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) - vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - - epidermis_dictionary = sp.Settings() - epidermis_dictionary[Tags.PRIORITY] = 8 - epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] - epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - - tissue_dict = sp.Settings() - tissue_dict[Tags.BACKGROUND] = background_dictionary - tissue_dict["muscle"] = muscle_dictionary - tissue_dict["epidermis"] = epidermis_dictionary - tissue_dict["vessel_1"] = vessel_1_dictionary - tissue_dict["vessel_2"] = vessel_2_dictionary - return tissue_dict - - -# Seed the numpy random configuration prior to creating the global_settings file in -# order to ensure that the same volume is generated with the same random seed every time. -np.random.seed(RANDOM_SEED) - -# Initialize global settings and prepare for simulation pipeline including -# volume creation and optical forward simulation. -general_settings = { - # These parameters set the general properties of the simulated volume - Tags.RANDOM_SEED: RANDOM_SEED, - Tags.VOLUME_NAME: VOLUME_NAME, - Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, - Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - Tags.WAVELENGTHS: WAVELENGTHS, - Tags.GPU: True, - Tags.DO_FILE_COMPRESSION: True -} -settings = sp.Settings(general_settings) -settings.set_volume_creation_settings({ - Tags.SIMULATE_DEFORMED_LAYERS: True, - Tags.STRUCTURES: create_example_tissue() -}) -settings.set_optical_settings({ - Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 -}) - -# Set component settings for linear unmixing. -# In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the -# resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. -# Please take a look at the component for more information. -settings["linear_unmixing"] = { - Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - Tags.WAVELENGTHS: WAVELENGTHS, - Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( - [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] - ), - Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, - Tags.LINEAR_UNMIXING_NON_NEGATIVE: True -} - -# Get device for simulation -device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - VOLUME_PLANAR_DIM_IN_MM/2, - 0])) -device.update_settings_for_use_of_model_based_volume_creator(settings) - -# Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS -pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapter(settings), - sp.FieldOfViewCropping(settings), -] -sp.simulate(pipeline, settings, device) - -# Run linear unmixing component with above specified settings. -sp.LinearUnmixing(settings, "linear_unmixing").run() - -# Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. -file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" -lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) -sO2 = lu_results["sO2"] - -mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) -p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) -gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) - -# Visualize linear unmixing result -visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelength=WAVELENGTHS[0], - show_initial_pressure=True, - show_oxygenation=True, - show_linear_unmixing_sO2=True) + # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you + # set global params characterizing the simulated volume + VOLUME_TRANSDUCER_DIM_IN_MM = 75 + VOLUME_PLANAR_DIM_IN_MM = 20 + VOLUME_HEIGHT_IN_MM = 25 + RANDOM_SEED = 471 + VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) + + # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths + WAVELENGTHS = [750, 800, 850] + + def create_example_tissue(): + """ + This is a very simple example script of how to create a tissue definition. + It contains a muscular background, an epidermis layer on top of the muscles + and two blood vessels. + """ + background_dictionary = sp.Settings() + background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + + muscle_dictionary = sp.Settings() + muscle_dictionary[Tags.PRIORITY] = 1 + muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + + vessel_1_dictionary = sp.Settings() + vessel_1_dictionary[Tags.PRIORITY] = 3 + vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 10, + 5] + vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 12, + 5] + vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) + vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + + vessel_2_dictionary = sp.Settings() + vessel_2_dictionary[Tags.PRIORITY] = 3 + vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 10, + 5] + vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 12, + 5] + vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) + vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + + epidermis_dictionary = sp.Settings() + epidermis_dictionary[Tags.PRIORITY] = 8 + epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] + epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + + tissue_dict = sp.Settings() + tissue_dict[Tags.BACKGROUND] = background_dictionary + tissue_dict["muscle"] = muscle_dictionary + tissue_dict["epidermis"] = epidermis_dictionary + tissue_dict["vessel_1"] = vessel_1_dictionary + tissue_dict["vessel_2"] = vessel_2_dictionary + return tissue_dict + + # Seed the numpy random configuration prior to creating the global_settings file in + # order to ensure that the same volume is generated with the same random seed every time. + np.random.seed(RANDOM_SEED) + + # Initialize global settings and prepare for simulation pipeline including + # volume creation and optical forward simulation. + general_settings = { + # These parameters set the general properties of the simulated volume + Tags.RANDOM_SEED: RANDOM_SEED, + Tags.VOLUME_NAME: VOLUME_NAME, + Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + Tags.SPACING_MM: SPACING, + Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + Tags.WAVELENGTHS: WAVELENGTHS, + Tags.GPU: True, + Tags.DO_FILE_COMPRESSION: True + } + settings = sp.Settings(general_settings) + settings.set_volume_creation_settings({ + Tags.SIMULATE_DEFORMED_LAYERS: True, + Tags.STRUCTURES: create_example_tissue() + }) + settings.set_optical_settings({ + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + }) + + # Set component settings for linear unmixing. + # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the + # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. + # Please take a look at the component for more information. + settings["linear_unmixing"] = { + Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + Tags.WAVELENGTHS: WAVELENGTHS, + Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( + [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] + ), + Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, + Tags.LINEAR_UNMIXING_NON_NEGATIVE: True + } + + # Get device for simulation + device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + VOLUME_PLANAR_DIM_IN_MM/2, + 0])) + device.update_settings_for_use_of_model_based_volume_creator(settings) + + # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS + pipeline = [ + sp.ModelBasedVolumeCreationAdapter(settings), + sp.MCXAdapter(settings), + sp.FieldOfViewCropping(settings), + ] + sp.simulate(pipeline, settings, device) + + # Run linear unmixing component with above specified settings. + sp.LinearUnmixing(settings, "linear_unmixing").run() + + # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. + file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) + sO2 = lu_results["sO2"] + + mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) + p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) + gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) + + # Visualize linear unmixing result + if visualise: + visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelength=WAVELENGTHS[0], + show_initial_pressure=True, + show_oxygenation=True, + show_linear_unmixing_sO2=True) + + +if __name__ == "__main__": + run_linear_unmixing(SPACING=0.25, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index 73961f87..ecf555cb 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -5,6 +5,8 @@ from simpa import Tags import simpa as sp import numpy as np +from simpa.utils.profiling import profile +from typing import Union # FIXME temporary workaround for newest Intel architectures import os @@ -12,149 +14,157 @@ # TODO: Please make sure that you have set the correct path to MCX binary and SAVE_PATH in the file path_config.env # located in the simpa_examples directory -path_manager = sp.PathManager() -VOLUME_TRANSDUCER_DIM_IN_MM = 60 -VOLUME_PLANAR_DIM_IN_MM = 30 -VOLUME_HEIGHT_IN_MM = 60 -SPACING = 0.5 -RANDOM_SEED = 471 -VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) -SAVE_REFLECTANCE = False -SAVE_PHOTON_DIRECTION = False -# If VISUALIZE is set to True, the simulation result will be plotted -VISUALIZE = True - - -def create_example_tissue(): - """ - This is a very simple example script of how to create a tissue definition. - It contains a muscular background, an epidermis layer on top of the muscles - and a blood vessel. +@profile +def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): """ - background_dictionary = sp.Settings() - background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - - muscle_dictionary = sp.Settings() - muscle_dictionary[Tags.PRIORITY] = 1 - muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] - muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - - vessel_1_dictionary = sp.Settings() - vessel_1_dictionary[Tags.PRIORITY] = 3 - vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 10, - VOLUME_HEIGHT_IN_MM/2] - vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 12, - VOLUME_HEIGHT_IN_MM/2] - vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() - vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - - epidermis_dictionary = sp.Settings() - epidermis_dictionary[Tags.PRIORITY] = 8 - epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] - epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] - epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - - tissue_dict = sp.Settings() - tissue_dict[Tags.BACKGROUND] = background_dictionary - tissue_dict["muscle"] = muscle_dictionary - tissue_dict["epidermis"] = epidermis_dictionary - tissue_dict["vessel_1"] = vessel_1_dictionary - return tissue_dict - - -# Seed the numpy random configuration prior to creating the global_settings file in -# order to ensure that the same volume -# is generated with the same random seed every time. - -np.random.seed(RANDOM_SEED) - -general_settings = { - # These parameters set the general properties of the simulated volume - Tags.RANDOM_SEED: RANDOM_SEED, - Tags.VOLUME_NAME: VOLUME_NAME, - Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, - Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - Tags.WAVELENGTHS: [798], - Tags.DO_FILE_COMPRESSION: True -} - -settings = sp.Settings(general_settings) - -settings.set_volume_creation_settings({ - Tags.SIMULATE_DEFORMED_LAYERS: True, - Tags.STRUCTURES: create_example_tissue() -}) -settings.set_optical_settings({ - Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION -}) -settings["noise_model_1"] = { - Tags.NOISE_MEAN: 1.0, - Tags.NOISE_STD: 0.1, - Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True -} - -if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: - pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapter(settings), - sp.GaussianNoise(settings, "noise_model_1") - ] -else: - pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapterReflectance(settings), - ] - - -class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): - """ - This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. + :param SPACING: The simulation spacing between voxels + :param path_manager: the path manager to be used, typically sp.PathManager + :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + :return: a run through of the example """ - - def __init__(self): - super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - VOLUME_PLANAR_DIM_IN_MM/2, 0])) - self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) - self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], - direction_vector_mm=[0, 0, 1])) - - -device = ExampleDeviceSlitIlluminationLinearDetector() - -sp.simulate(pipeline, settings, device) - -if Tags.WAVELENGTH in settings: - WAVELENGTH = settings[Tags.WAVELENGTH] -else: - WAVELENGTH = 700 - -if VISUALIZE: - sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelength=WAVELENGTH, - show_initial_pressure=True, - show_absorption=True, - show_diffuse_reflectance=SAVE_REFLECTANCE, - log_scale=True) + VOLUME_TRANSDUCER_DIM_IN_MM = 60 + VOLUME_PLANAR_DIM_IN_MM = 30 + VOLUME_HEIGHT_IN_MM = 60 + RANDOM_SEED = 471 + VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + SAVE_REFLECTANCE = False + SAVE_PHOTON_DIRECTION = False + + # If VISUALIZE is set to True, the simulation result will be plotted + + def create_example_tissue(): + """ + This is a very simple example script of how to create a tissue definition. + It contains a muscular background, an epidermis layer on top of the muscles + and a blood vessel. + """ + background_dictionary = sp.Settings() + background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + + muscle_dictionary = sp.Settings() + muscle_dictionary[Tags.PRIORITY] = 1 + muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] + muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + + vessel_1_dictionary = sp.Settings() + vessel_1_dictionary[Tags.PRIORITY] = 3 + vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 10, + VOLUME_HEIGHT_IN_MM/2] + vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 12, + VOLUME_HEIGHT_IN_MM/2] + vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() + vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + + epidermis_dictionary = sp.Settings() + epidermis_dictionary[Tags.PRIORITY] = 8 + epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] + epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] + epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + + tissue_dict = sp.Settings() + tissue_dict[Tags.BACKGROUND] = background_dictionary + tissue_dict["muscle"] = muscle_dictionary + tissue_dict["epidermis"] = epidermis_dictionary + tissue_dict["vessel_1"] = vessel_1_dictionary + return tissue_dict + + # Seed the numpy random configuration prior to creating the global_settings file in + # order to ensure that the same volume + # is generated with the same random seed every time. + + np.random.seed(RANDOM_SEED) + + general_settings = { + # These parameters set the general properties of the simulated volume + Tags.RANDOM_SEED: RANDOM_SEED, + Tags.VOLUME_NAME: VOLUME_NAME, + Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + Tags.SPACING_MM: SPACING, + Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + Tags.WAVELENGTHS: [798], + Tags.DO_FILE_COMPRESSION: True, + Tags.GPU: True + } + + settings = sp.Settings(general_settings) + + settings.set_volume_creation_settings({ + Tags.SIMULATE_DEFORMED_LAYERS: True, + Tags.STRUCTURES: create_example_tissue() + }) + settings.set_optical_settings({ + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + }) + settings["noise_model_1"] = { + Tags.NOISE_MEAN: 1.0, + Tags.NOISE_STD: 0.1, + Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + } + + if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: + pipeline = [ + sp.ModelBasedVolumeCreationAdapter(settings), + sp.MCXAdapter(settings), + sp.GaussianNoise(settings, "noise_model_1") + ] + else: + pipeline = [ + sp.ModelBasedVolumeCreationAdapter(settings), + sp.MCXAdapterReflectance(settings), + ] + + class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): + """ + This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. + + """ + + def __init__(self): + super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + VOLUME_PLANAR_DIM_IN_MM/2, 0])) + self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) + self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], + direction_vector_mm=[0, 0, 1])) + + device = ExampleDeviceSlitIlluminationLinearDetector() + + sp.simulate(pipeline, settings, device) + + if Tags.WAVELENGTH in settings: + WAVELENGTH = settings[Tags.WAVELENGTH] + else: + WAVELENGTH = 700 + + if visualise: + sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelength=WAVELENGTH, + show_initial_pressure=True, + show_absorption=True, + show_diffuse_reflectance=SAVE_REFLECTANCE, + log_scale=True) + + +if __name__ == "__main__": + run_minimal_optical_example(SPACING=0.2, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/minimal_optical_simulation_uniform_cube.py b/simpa_examples/minimal_optical_simulation_uniform_cube.py index 39f299e5..bcbf9d01 100644 --- a/simpa_examples/minimal_optical_simulation_uniform_cube.py +++ b/simpa_examples/minimal_optical_simulation_uniform_cube.py @@ -9,86 +9,99 @@ from simpa import Tags import simpa as sp import numpy as np +from typing import Union # FIXME temporary workaround for newest Intel architectures import os +from simpa.utils.profiling import profile os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" # TODO: Please make sure that you have set the correct path to MCX binary as described in the README.md file. -path_manager = sp.PathManager() -VOLUME_TRANSDUCER_DIM_IN_MM = 60 -VOLUME_PLANAR_DIM_IN_MM = 30 -VOLUME_HEIGHT_IN_MM = 60 -SPACING = 0.5 -RANDOM_SEED = 471 -VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) -SAVE_REFLECTANCE = True -SAVE_PHOTON_DIRECTION = False -# If VISUALIZE is set to True, the simulation result will be plotted -VISUALIZE = True - - -def create_example_tissue(): +@profile +def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), + visualise: bool = True): """ - This is a very simple example script of how to create a tissue definition. - It contains only a generic background tissue material. + + :param SPACING: The simulation spacing between voxels + :param path_manager: the path manager to be used, typically sp.PathManager + :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + :return: a run through of the example """ - background_dictionary = sp.Settings() - background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - - tissue_dict = sp.Settings() - tissue_dict[Tags.BACKGROUND] = background_dictionary - return tissue_dict - - -# Seed the numpy random configuration prior to creating the global_settings file in -# order to ensure that the same volume -# is generated with the same random seed every time. - -np.random.seed(RANDOM_SEED) - -general_settings = { - # These parameters set the general properties of the simulated volume - Tags.RANDOM_SEED: RANDOM_SEED, - Tags.VOLUME_NAME: VOLUME_NAME, - Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, - Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - Tags.WAVELENGTHS: [500], - Tags.DO_FILE_COMPRESSION: True -} - -settings = sp.Settings(general_settings) - -settings.set_volume_creation_settings({ - Tags.STRUCTURES: create_example_tissue() -}) -settings.set_optical_settings({ - Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION -}) - -pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapterReflectance(settings), -] - -device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - VOLUME_PLANAR_DIM_IN_MM/2, 0])) - -sp.simulate(pipeline, settings, device) - -if VISUALIZE: - sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - wavelength=settings[Tags.WAVELENGTH], - show_initial_pressure=True, - show_absorption=True, - show_diffuse_reflectance=SAVE_REFLECTANCE, - log_scale=True) + VOLUME_TRANSDUCER_DIM_IN_MM = 60 + VOLUME_PLANAR_DIM_IN_MM = 30 + VOLUME_HEIGHT_IN_MM = 60 + RANDOM_SEED = 471 + VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + SAVE_REFLECTANCE = True + SAVE_PHOTON_DIRECTION = False + + # If VISUALIZE is set to True, the simulation result will be plotted + VISUALIZE = True + + def create_example_tissue(): + """ + This is a very simple example script of how to create a tissue definition. + It contains only a generic background tissue material. + """ + background_dictionary = sp.Settings() + background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + + tissue_dict = sp.Settings() + tissue_dict[Tags.BACKGROUND] = background_dictionary + return tissue_dict + + # Seed the numpy random configuration prior to creating the global_settings file in + # order to ensure that the same volume + # is generated with the same random seed every time. + + np.random.seed(RANDOM_SEED) + + general_settings = { + # These parameters set the general properties of the simulated volume + Tags.RANDOM_SEED: RANDOM_SEED, + Tags.VOLUME_NAME: VOLUME_NAME, + Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + Tags.SPACING_MM: SPACING, + Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + Tags.WAVELENGTHS: [500], + Tags.DO_FILE_COMPRESSION: True + } + + settings = sp.Settings(general_settings) + + settings.set_volume_creation_settings({ + Tags.STRUCTURES: create_example_tissue() + }) + settings.set_optical_settings({ + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + }) + + pipeline = [ + sp.ModelBasedVolumeCreationAdapter(settings), + sp.MCXAdapterReflectance(settings), + ] + + device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + VOLUME_PLANAR_DIM_IN_MM/2, 0])) + + sp.simulate(pipeline, settings, device) + + if visualise: + sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + wavelength=settings[Tags.WAVELENGTH], + show_initial_pressure=True, + show_absorption=True, + show_diffuse_reflectance=SAVE_REFLECTANCE, + log_scale=True) + + +if __name__ == "__main__": + run_optical_simulation_uniform_cube_example(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 7c5de16d..75f6ef73 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -5,193 +5,206 @@ from simpa import Tags import simpa as sp import numpy as np +from simpa.utils.profiling import profile +from typing import Union path_manager = sp.PathManager() -SPEED_OF_SOUND = 1500 -SPACING = 0.5 -XZ_DIM = 90 -Y_DIM = 40 - - -def create_pipeline(_settings: sp.Settings): - return [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapter(settings), - sp.KWaveAdapter(settings), - sp.FieldOfViewCropping(settings), - sp.TimeReversalAdapter(settings) - ] - - -def get_device(): - pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) - return pa_device - - -def create_volume(): - inclusion_material = sp.Molecule(volume_fraction=1.0, - anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 0.9), - scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 100.0), - absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 4.0), - speed_of_sound=SPEED_OF_SOUND, - alpha_coefficient=1e-4, - density=1000, - gruneisen_parameter=1.0, - name="Inclusion") - - phantom_material = sp.Molecule(volume_fraction=1.0, - anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), - scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 100.0), - absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), - speed_of_sound=SPEED_OF_SOUND, - alpha_coefficient=1e-4, - density=1000, - gruneisen_parameter=1.0, - name="Phantom") - - heavy_water = sp.Molecule(volume_fraction=1.0, - anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), - scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), - absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), - speed_of_sound=SPEED_OF_SOUND, - alpha_coefficient=1e-4, - density=1000, - gruneisen_parameter=1.0, - name="background_water") - - background_dictionary = sp.Settings() - background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - .append(heavy_water) - .get_molecular_composition(segmentation_type=-1)) - background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - - phantom_material_dictionary = sp.Settings() - phantom_material_dictionary[Tags.PRIORITY] = 3 - phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] - phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 - phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 - phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 - phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - .append(phantom_material) - .get_molecular_composition(segmentation_type=0)) - phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - - inclusion_1_dictionary = sp.Settings() - inclusion_1_dictionary[Tags.PRIORITY] = 8 - inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] - inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 - inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 - inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 - inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - .append(inclusion_material) - .get_molecular_composition(segmentation_type=1)) - inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - - inclusion_2_dictionary = sp.Settings() - inclusion_2_dictionary[Tags.PRIORITY] = 5 - inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] - inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] - inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - .append(inclusion_material) - .get_molecular_composition(segmentation_type=2)) - inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - - tissue_dict = sp.Settings() - tissue_dict[Tags.BACKGROUND] = background_dictionary - tissue_dict["phantom"] = phantom_material_dictionary - tissue_dict["inclusion_1"] = inclusion_1_dictionary - tissue_dict["inclusion_2"] = inclusion_2_dictionary - return { - Tags.STRUCTURES: tissue_dict, - Tags.SIMULATE_DEFORMED_LAYERS: False - } - - -def get_settings(): - general_settings = { - # These parameters set the general properties of the simulated volume - Tags.RANDOM_SEED: 4711, - Tags.VOLUME_NAME: "InVision Simulation Example", - Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, - Tags.DIM_VOLUME_Z_MM: XZ_DIM, - Tags.DIM_VOLUME_X_MM: XZ_DIM, - Tags.DIM_VOLUME_Y_MM: Y_DIM, - Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - Tags.GPU: True, - Tags.WAVELENGTHS: [700] - } - - volume_settings = create_volume() - - optical_settings = { - Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, - Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - } - - acoustic_settings = { - Tags.ACOUSTIC_SIMULATION_3D: True, - Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - Tags.KWAVE_PROPERTY_PMLInside: False, - Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - Tags.KWAVE_PROPERTY_PlotPML: False, - Tags.RECORDMOVIE: False, - Tags.MOVIENAME: "visualization_log", - Tags.ACOUSTIC_LOG_SCALE: True - } - - reconstruction_settings = { - Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - Tags.TUKEY_WINDOW_ALPHA: 0.5, - Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, - Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, - Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - Tags.KWAVE_PROPERTY_PMLInside: False, - Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - Tags.KWAVE_PROPERTY_PlotPML: False, - Tags.RECORDMOVIE: False, - Tags.MOVIENAME: "visualization_log", - Tags.ACOUSTIC_LOG_SCALE: True, - Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - Tags.SPACING_MM: 0.25, - } - - _settings = sp.Settings(general_settings) - _settings.set_volume_creation_settings(volume_settings) - _settings.set_optical_settings(optical_settings) - _settings.set_acoustic_settings(acoustic_settings) - _settings.set_reconstruction_settings(reconstruction_settings) - return _settings - - -device = get_device() -settings = get_settings() -pipeline = create_pipeline(settings) - -sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) - -sp.visualise_data(settings=settings, - path_manager=path_manager, - show_absorption=True, - show_initial_pressure=True, - show_reconstructed_data=True, - show_xz_only=True) + +@profile +def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): + """ + + :param SPACING: The simulation spacing between voxels + :param path_manager: the path manager to be used, typically sp.PathManager + :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + :return: a run through of the example + """ + SPEED_OF_SOUND = 1500 + XZ_DIM = 90 + Y_DIM = 40 + + def create_pipeline(_settings: sp.Settings): + return [ + sp.ModelBasedVolumeCreationAdapter(settings), + sp.MCXAdapter(settings), + sp.KWaveAdapter(settings), + sp.FieldOfViewCropping(settings), + sp.TimeReversalAdapter(settings) + ] + + def get_device(): + pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) + return pa_device + + def create_volume(): + inclusion_material = sp.Molecule(volume_fraction=1.0, + anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 0.9), + scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 100.0), + absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 4.0), + speed_of_sound=SPEED_OF_SOUND, + alpha_coefficient=1e-4, + density=1000, + gruneisen_parameter=1.0, + name="Inclusion") + + phantom_material = sp.Molecule(volume_fraction=1.0, + anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 0.9), + scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 100.0), + absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 0.05), + speed_of_sound=SPEED_OF_SOUND, + alpha_coefficient=1e-4, + density=1000, + gruneisen_parameter=1.0, + name="Phantom") + + heavy_water = sp.Molecule(volume_fraction=1.0, + anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), + scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), + absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), + speed_of_sound=SPEED_OF_SOUND, + alpha_coefficient=1e-4, + density=1000, + gruneisen_parameter=1.0, + name="background_water") + + background_dictionary = sp.Settings() + background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + .append(heavy_water) + .get_molecular_composition(segmentation_type=-1)) + background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + + phantom_material_dictionary = sp.Settings() + phantom_material_dictionary[Tags.PRIORITY] = 3 + phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] + phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 + phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 + phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 + phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + .append(phantom_material) + .get_molecular_composition(segmentation_type=0)) + phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + + inclusion_1_dictionary = sp.Settings() + inclusion_1_dictionary[Tags.PRIORITY] = 8 + inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] + inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 + inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 + inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 + inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + .append(inclusion_material) + .get_molecular_composition(segmentation_type=1)) + inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + + inclusion_2_dictionary = sp.Settings() + inclusion_2_dictionary[Tags.PRIORITY] = 5 + inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] + inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] + inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + .append(inclusion_material) + .get_molecular_composition(segmentation_type=2)) + inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + + tissue_dict = sp.Settings() + tissue_dict[Tags.BACKGROUND] = background_dictionary + tissue_dict["phantom"] = phantom_material_dictionary + tissue_dict["inclusion_1"] = inclusion_1_dictionary + tissue_dict["inclusion_2"] = inclusion_2_dictionary + return { + Tags.STRUCTURES: tissue_dict, + Tags.SIMULATE_DEFORMED_LAYERS: False + } + + def get_settings(): + general_settings = { + # These parameters set the general properties of the simulated volume + Tags.RANDOM_SEED: 4711, + Tags.VOLUME_NAME: "InVision Simulation Example", + Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + Tags.SPACING_MM: SPACING, + Tags.DIM_VOLUME_Z_MM: XZ_DIM, + Tags.DIM_VOLUME_X_MM: XZ_DIM, + Tags.DIM_VOLUME_Y_MM: Y_DIM, + Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + Tags.GPU: True, + Tags.WAVELENGTHS: [700] + } + + volume_settings = create_volume() + + optical_settings = { + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, + Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + } + + acoustic_settings = { + Tags.ACOUSTIC_SIMULATION_3D: True, + Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + Tags.KWAVE_PROPERTY_PMLInside: False, + Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + Tags.KWAVE_PROPERTY_PlotPML: False, + Tags.RECORDMOVIE: False, + Tags.MOVIENAME: "visualization_log", + Tags.ACOUSTIC_LOG_SCALE: True + } + + reconstruction_settings = { + Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + Tags.TUKEY_WINDOW_ALPHA: 0.5, + Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, + Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, + Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + Tags.KWAVE_PROPERTY_PMLInside: False, + Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + Tags.KWAVE_PROPERTY_PlotPML: False, + Tags.RECORDMOVIE: False, + Tags.MOVIENAME: "visualization_log", + Tags.ACOUSTIC_LOG_SCALE: True, + Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + Tags.SPACING_MM: 0.25, + } + + _settings = sp.Settings(general_settings) + _settings.set_volume_creation_settings(volume_settings) + _settings.set_optical_settings(optical_settings) + _settings.set_acoustic_settings(acoustic_settings) + _settings.set_reconstruction_settings(reconstruction_settings) + return _settings + + device = get_device() + settings = get_settings() + pipeline = create_pipeline(settings) + + sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) + + if visualise: + sp.visualise_data(settings=settings, + path_manager=path_manager, + show_absorption=True, + show_initial_pressure=True, + show_reconstructed_data=True, + show_xz_only=True) + + +if __name__ == "__main__": + run_msot_invision_example(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 68bb856c..67bd00a8 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -5,198 +5,210 @@ from simpa import Tags import simpa as sp import numpy as np +from simpa.utils.profiling import profile +from typing import Union # FIXME temporary workaround for newest Intel architectures import os os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" -VOLUME_TRANSDUCER_DIM_IN_MM = 75 -VOLUME_PLANAR_DIM_IN_MM = 20 -VOLUME_HEIGHT_IN_MM = 25 -SPACING = 0.2 -RANDOM_SEED = 4711 - # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # point to the correct file in the PathManager(). -path_manager = sp.PathManager() - -# If VISUALIZE is set to True, the simulation result will be plotted -VISUALIZE = True -def create_example_tissue(): +@profile +def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), + visualise: bool = True): """ - This is a very simple example script of how to create a tissue definition. - It contains a muscular background, an epidermis layer on top of the muscles - and a blood vessel. + + :param SPACING: The simulation spacing between voxels + :param path_manager: the path manager to be used, typically sp.PathManager + :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + :return: a run through of the example """ - background_dictionary = sp.Settings() - background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) - background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - - tissue_dict = sp.Settings() - tissue_dict[Tags.BACKGROUND] = background_dictionary - tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, - molecular_composition=sp.TISSUE_LIBRARY.constant( - 0.05, 100, 0.9), - priority=1, - consider_partial_volume=True, - adhere_to_deformation=True) - tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, - molecular_composition=sp.TISSUE_LIBRARY.epidermis(), - priority=8, - consider_partial_volume=True, - adhere_to_deformation=True) - tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( - tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], - tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], - molecular_composition=sp.TISSUE_LIBRARY.blood(), - radius_mm=2, priority=3, consider_partial_volume=True, - adhere_to_deformation=False - ) - tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( - tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], - tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], - molecular_composition=sp.TISSUE_LIBRARY.blood(), - radius_mm=3, priority=3, consider_partial_volume=True, - adhere_to_deformation=False - ) - return tissue_dict - - -# Seed the numpy random configuration prior to creating the global_settings file in -# order to ensure that the same volume -# is generated with the same random seed every time. - -np.random.seed(RANDOM_SEED) -VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) - -general_settings = { - # These parameters set the general properties of the simulated volume - Tags.RANDOM_SEED: RANDOM_SEED, - Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), - Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, - Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - Tags.GPU: True, - Tags.WAVELENGTHS: [700, 800], - Tags.DO_FILE_COMPRESSION: True, - Tags.DO_IPASC_EXPORT: True -} -settings = sp.Settings(general_settings) -np.random.seed(RANDOM_SEED) - -settings.set_volume_creation_settings({ - Tags.STRUCTURES: create_example_tissue(), - Tags.SIMULATE_DEFORMED_LAYERS: True -}) - -settings.set_optical_settings({ - Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, - Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - Tags.MCX_ASSUMED_ANISOTROPY: 0.9, -}) - -settings.set_acoustic_settings({ - Tags.ACOUSTIC_SIMULATION_3D: False, - Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - Tags.KWAVE_PROPERTY_PMLInside: False, - Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - Tags.KWAVE_PROPERTY_PlotPML: False, - Tags.RECORDMOVIE: False, - Tags.MOVIENAME: "visualization_log", - Tags.ACOUSTIC_LOG_SCALE: True -}) - -settings.set_reconstruction_settings({ - Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - Tags.ACOUSTIC_SIMULATION_3D: False, - Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - Tags.TUKEY_WINDOW_ALPHA: 0.5, - Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), - Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), - Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, - Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - Tags.KWAVE_PROPERTY_PMLInside: False, - Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - Tags.KWAVE_PROPERTY_PlotPML: False, - Tags.RECORDMOVIE: False, - Tags.MOVIENAME: "visualization_log", - Tags.ACOUSTIC_LOG_SCALE: True, - Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, - Tags.DATA_FIELD_ALPHA_COEFF: 0.01, - Tags.DATA_FIELD_DENSITY: 1000, - Tags.SPACING_MM: SPACING -}) - -settings["noise_initial_pressure"] = { - Tags.NOISE_MEAN: 1, - Tags.NOISE_STD: 0.01, - Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True -} - -settings["noise_time_series"] = { - Tags.NOISE_STD: 1, - Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, - Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA -} - -# TODO: For the device choice, uncomment the undesired device - -# device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, -# VOLUME_PLANAR_DIM_IN_MM/2, -# 0])) -# device.update_settings_for_use_of_model_based_volume_creator(settings) - -device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - VOLUME_PLANAR_DIM_IN_MM/2, - 0]), - field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) -device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, - pitch_mm=0.25, - number_detector_elements=100, - field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) -print(device.get_detection_geometry().get_detector_element_positions_base_mm()) -device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) - - -SIMULATION_PIPELINE = [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapter(settings), - sp.GaussianNoise(settings, "noise_initial_pressure"), - sp.KWaveAdapter(settings), - sp.GaussianNoise(settings, "noise_time_series"), - sp.TimeReversalAdapter(settings), - sp.FieldOfViewCropping(settings) -] - -sp.simulate(SIMULATION_PIPELINE, settings, device) - -if Tags.WAVELENGTH in settings: - WAVELENGTH = settings[Tags.WAVELENGTH] -else: - WAVELENGTH = 700 - -if VISUALIZE: - sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - wavelength=WAVELENGTH, - show_time_series_data=True, - show_initial_pressure=True, - show_reconstructed_data=True, - log_scale=False, - show_xz_only=False) + VOLUME_TRANSDUCER_DIM_IN_MM = 75 + VOLUME_PLANAR_DIM_IN_MM = 20 + VOLUME_HEIGHT_IN_MM = 25 + RANDOM_SEED = 4711 + + # If VISUALIZE is set to True, the simulation result will be plotted + VISUALIZE = True + + def create_example_tissue(): + """ + This is a very simple example script of how to create a tissue definition. + It contains a muscular background, an epidermis layer on top of the muscles + and a blood vessel. + """ + background_dictionary = sp.Settings() + background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) + background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + + tissue_dict = sp.Settings() + tissue_dict[Tags.BACKGROUND] = background_dictionary + tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, + molecular_composition=sp.TISSUE_LIBRARY.constant( + 0.05, 100, 0.9), + priority=1, + consider_partial_volume=True, + adhere_to_deformation=True) + tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, + molecular_composition=sp.TISSUE_LIBRARY.epidermis(), + priority=8, + consider_partial_volume=True, + adhere_to_deformation=True) + tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( + tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], + tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], + molecular_composition=sp.TISSUE_LIBRARY.blood(), + radius_mm=2, priority=3, consider_partial_volume=True, + adhere_to_deformation=False + ) + tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( + tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], + tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], + molecular_composition=sp.TISSUE_LIBRARY.blood(), + radius_mm=3, priority=3, consider_partial_volume=True, + adhere_to_deformation=False + ) + return tissue_dict + + # Seed the numpy random configuration prior to creating the global_settings file in + # order to ensure that the same volume + # is generated with the same random seed every time. + + np.random.seed(RANDOM_SEED) + VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) + + general_settings = { + # These parameters set the general properties of the simulated volume + Tags.RANDOM_SEED: RANDOM_SEED, + Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), + Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + Tags.SPACING_MM: SPACING, + Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + Tags.GPU: True, + Tags.WAVELENGTHS: [700, 800], + Tags.DO_FILE_COMPRESSION: True, + Tags.DO_IPASC_EXPORT: True + } + settings = sp.Settings(general_settings) + np.random.seed(RANDOM_SEED) + + settings.set_volume_creation_settings({ + Tags.STRUCTURES: create_example_tissue(), + Tags.SIMULATE_DEFORMED_LAYERS: True + }) + + settings.set_optical_settings({ + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, + Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + Tags.MCX_ASSUMED_ANISOTROPY: 0.9, + }) + + settings.set_acoustic_settings({ + Tags.ACOUSTIC_SIMULATION_3D: False, + Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + Tags.KWAVE_PROPERTY_PMLInside: False, + Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + Tags.KWAVE_PROPERTY_PlotPML: False, + Tags.RECORDMOVIE: False, + Tags.MOVIENAME: "visualization_log", + Tags.ACOUSTIC_LOG_SCALE: True + }) + + settings.set_reconstruction_settings({ + Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + Tags.ACOUSTIC_SIMULATION_3D: False, + Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + Tags.TUKEY_WINDOW_ALPHA: 0.5, + Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), + Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), + Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, + Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + Tags.KWAVE_PROPERTY_PMLInside: False, + Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + Tags.KWAVE_PROPERTY_PlotPML: False, + Tags.RECORDMOVIE: False, + Tags.MOVIENAME: "visualization_log", + Tags.ACOUSTIC_LOG_SCALE: True, + Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, + Tags.DATA_FIELD_ALPHA_COEFF: 0.01, + Tags.DATA_FIELD_DENSITY: 1000, + Tags.SPACING_MM: SPACING + }) + + settings["noise_initial_pressure"] = { + Tags.NOISE_MEAN: 1, + Tags.NOISE_STD: 0.01, + Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + } + + settings["noise_time_series"] = { + Tags.NOISE_STD: 1, + Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, + Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA + } + + # TODO: For the device choice, uncomment the undesired device + + # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + # VOLUME_PLANAR_DIM_IN_MM/2, + # 0])) + # device.update_settings_for_use_of_model_based_volume_creator(settings) + + device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + VOLUME_PLANAR_DIM_IN_MM/2, + 0]), + field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) + device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, + pitch_mm=0.25, + number_detector_elements=100, + field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) + print(device.get_detection_geometry().get_detector_element_positions_base_mm()) + device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) + + SIMULATION_PIPELINE = [ + sp.ModelBasedVolumeCreationAdapter(settings), + sp.MCXAdapter(settings), + sp.GaussianNoise(settings, "noise_initial_pressure"), + sp.KWaveAdapter(settings), + sp.GaussianNoise(settings, "noise_time_series"), + sp.TimeReversalAdapter(settings), + sp.FieldOfViewCropping(settings) + ] + + sp.simulate(SIMULATION_PIPELINE, settings, device) + + if Tags.WAVELENGTH in settings: + WAVELENGTH = settings[Tags.WAVELENGTH] + else: + WAVELENGTH = 700 + + if visualise: + sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + wavelength=WAVELENGTH, + show_time_series_data=True, + show_initial_pressure=True, + show_reconstructed_data=True, + log_scale=False, + show_xz_only=False) + + +if __name__ == "__main__": + run_optical_and_acoustic_example(SPACING=0.2, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/perform_image_reconstruction.py b/simpa_examples/perform_image_reconstruction.py index fb710dac..caa8f2d8 100644 --- a/simpa_examples/perform_image_reconstruction.py +++ b/simpa_examples/perform_image_reconstruction.py @@ -9,35 +9,52 @@ import simpa as sp from simpa import Tags +from simpa.utils.profiling import profile +from typing import Union # FIXME temporary workaround for newest Intel architectures os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" path_manager = sp.PathManager() PATH = path_manager.get_hdf5_file_save_path() + "/CompletePipelineExample_4711.hdf5" -settings = sp.load_data_field(PATH, Tags.SETTINGS) -settings[Tags.WAVELENGTH] = settings[Tags.WAVELENGTHS][0] -settings.set_reconstruction_settings({ - Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - Tags.TUKEY_WINDOW_ALPHA: 0.5, - Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), - Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e6), - Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, - Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - Tags.SPACING_MM: settings[Tags.SPACING_MM] -}) +def run_perform_image_reconstruction(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise=True): + """ -# TODO use the correct device definition here -device = sp.load_data_field(PATH, Tags.DIGITAL_DEVICE) + :param SPACING: The simulation spacing between voxels + :param path_manager: the path manager to be used, typically sp.PathManager + :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + :return: a run through of the example + """ + PATH = path_manager.get_hdf5_file_save_path() + "/CompletePipelineExample_4711.hdf5" + settings = sp.load_data_field(PATH, Tags.SETTINGS) + settings[Tags.WAVELENGTH] = settings[Tags.WAVELENGTHS][0] -sp.DelayAndSumAdapter(settings).run(device) + settings.set_reconstruction_settings({ + Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + Tags.TUKEY_WINDOW_ALPHA: 0.5, + Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), + Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e6), + Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, + Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + Tags.SPACING_MM: settings[Tags.SPACING_MM] + }) -reconstructed_image = sp.load_data_field(PATH, Tags.DATA_FIELD_RECONSTRUCTED_DATA, settings[Tags.WAVELENGTH]) -reconstructed_image = np.squeeze(reconstructed_image) + # TODO use the correct device definition here + device = sp.load_data_field(PATH, Tags.DIGITAL_DEVICE) -sp.visualise_data(path_to_hdf5_file=PATH, - wavelength=settings[Tags.WAVELENGTH], - show_reconstructed_data=True, - show_xz_only=True) + sp.DelayAndSumAdapter(settings).run(device) + + reconstructed_image = sp.load_data_field(PATH, Tags.DATA_FIELD_RECONSTRUCTED_DATA, settings[Tags.WAVELENGTH]) + reconstructed_image = np.squeeze(reconstructed_image) + + if visualise: + sp.visualise_data(path_to_hdf5_file=PATH, + wavelength=settings[Tags.WAVELENGTH], + show_reconstructed_data=True, + show_xz_only=True) + + +if __name__ == "__main__": + run_image_reconstruction_example(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/perform_iterative_qPAI_reconstruction.py b/simpa_examples/perform_iterative_qPAI_reconstruction.py index 90d4ec93..d9da14e9 100644 --- a/simpa_examples/perform_iterative_qPAI_reconstruction.py +++ b/simpa_examples/perform_iterative_qPAI_reconstruction.py @@ -11,6 +11,8 @@ import simpa as sp from simpa import Tags +from typing import Union +from simpa.utils.profiling import profile # FIXME temporary workaround for newest Intel architectures os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" @@ -18,218 +20,225 @@ # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # point to the correct file in the PathManager(). -path_manager = sp.PathManager() -VOLUME_TRANSDUCER_DIM_IN_MM = 30 -VOLUME_PLANAR_DIM_IN_MM = 30 -VOLUME_HEIGHT_IN_MM = 30 -SPACING = 0.2 -RANDOM_SEED = 471 -VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) -# If VISUALIZE is set to True, the reconstruction result will be plotted -VISUALIZE = True - - -def create_example_tissue(): +@profile +def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise: bool = True): """ - This is a very simple example script of how to create a tissue definition. - It contains a muscular background, an epidermis layer on top of the muscles - and a blood vessel. + + :param SPACING: The simulation spacing between voxels + :param path_manager: the path manager to be used, typically sp.PathManager + :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + :return: a run through of the example """ - background_dictionary = sp.Settings() - background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) - background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - - epidermis_structure = sp.Settings() - epidermis_structure[Tags.PRIORITY] = 1 - epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] - epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] - epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) - epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True - epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True - epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - - vessel_structure_1 = sp.Settings() - vessel_structure_1[Tags.PRIORITY] = 2 - vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, - VOLUME_HEIGHT_IN_MM / 2] - vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, - VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] - vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 - vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 - vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) - vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True - vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True - vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE - - vessel_structure_2 = sp.Settings() - vessel_structure_2[Tags.PRIORITY] = 3 - vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, - VOLUME_HEIGHT_IN_MM / 3] - vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, - VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] - vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 - vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) - vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True - vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - - tissue_dict = sp.Settings() - tissue_dict[Tags.BACKGROUND] = background_dictionary - tissue_dict["epidermis"] = epidermis_structure - tissue_dict["vessel_1"] = vessel_structure_1 - tissue_dict["vessel_2"] = vessel_structure_2 - return tissue_dict - - -# set settings for volume creation, optical simulation and iterative qPAI method -np.random.seed(RANDOM_SEED) - -general_settings = { - # These parameters set the general properties of the simulated volume - Tags.RANDOM_SEED: RANDOM_SEED, - Tags.VOLUME_NAME: VOLUME_NAME, - Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, - Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - Tags.WAVELENGTHS: [700] -} - -settings = sp.Settings(general_settings) - -settings.set_volume_creation_settings({ - # These parameters set the properties for the volume creation - Tags.SIMULATE_DEFORMED_LAYERS: True, - Tags.STRUCTURES: create_example_tissue() -}) -settings.set_optical_settings({ - # These parameters set the properties for the optical Monte Carlo simulation - Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 -}) -settings["noise_model"] = { - Tags.NOISE_MEAN: 1.0, - Tags.NOISE_STD: 0.01, - Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True -} -settings["iterative_qpai_reconstruction"] = { - # These parameters set the properties of the iterative reconstruction - Tags.DOWNSCALE_FACTOR: 0.75, - Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, - # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant - Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, - Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, - # for this example, we are not interested in all absorption updates - Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, - Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 -} - -# run pipeline including iterative qPAI method -pipeline = [ - sp.ModelBasedVolumeCreationAdapter(settings), - sp.MCXAdapter(settings), - sp.GaussianNoise(settings, "noise_model"), - sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") -] - - -class CustomDevice(sp.PhotoacousticDevice): - - def __init__(self): - super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, - general_settings[Tags.DIM_VOLUME_Y_MM] / 2, - 0])) - self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) - - -device = CustomDevice() - -device.update_settings_for_use_of_model_based_volume_creator(settings) - -sp.simulate(pipeline, settings, device) - -# visualize reconstruction results -if VISUALIZE: - # get simulation output - data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - settings = sp.load_data_field(data_path, Tags.SETTINGS) - wavelength = settings[Tags.WAVELENGTHS][0] - - # get reconstruction result - absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) - - # get ground truth absorption coefficients - absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) - - # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) - scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR - absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") - - # compute reconstruction error - difference = absorption_gt - absorption_reconstruction - - median_error = np.median(difference) - q3, q1 = np.percentile(difference, [75, 25]) - iqr = q3 - q1 - - # visualize results - x_pos = int(np.shape(absorption_gt)[0] / 2) - y_pos = int(np.shape(absorption_gt)[1] / 2) - - if np.min(absorption_gt) > np.min(absorption_reconstruction): - cmin = np.min(absorption_reconstruction) - else: - cmin = np.min(absorption_gt) - - if np.max(absorption_gt) > np.max(absorption_reconstruction): - cmax = np.max(absorption_gt) - else: - cmax = np.max(absorption_reconstruction) - - results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] - results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] - - label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", - "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] - - plt.figure(figsize=(20, 15)) - plt.subplots_adjust(hspace=0.5) - plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + - "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) - - for i, quantity in enumerate(results_x_z): - plt.subplot(2, len(results_x_z), i + 1) - if i == 0: - plt.ylabel("x-z", fontsize=10) - plt.title(label[i], fontsize=10) - plt.imshow(quantity.T) - plt.xticks(fontsize=6) - plt.yticks(fontsize=6) - plt.colorbar() - if i != 2: - plt.clim(cmin, cmax) + VOLUME_TRANSDUCER_DIM_IN_MM = 30 + VOLUME_PLANAR_DIM_IN_MM = 30 + VOLUME_HEIGHT_IN_MM = 30 + RANDOM_SEED = 471 + VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) + + # If VISUALIZE is set to True, the reconstruction result will be plotted + + def create_example_tissue(): + """ + This is a very simple example script of how to create a tissue definition. + It contains a muscular background, an epidermis layer on top of the muscles + and a blood vessel. + """ + background_dictionary = sp.Settings() + background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) + background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + + epidermis_structure = sp.Settings() + epidermis_structure[Tags.PRIORITY] = 1 + epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] + epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] + epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) + epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True + epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True + epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + + vessel_structure_1 = sp.Settings() + vessel_structure_1[Tags.PRIORITY] = 2 + vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, + VOLUME_HEIGHT_IN_MM / 2] + vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, + VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] + vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 + vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 + vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) + vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True + vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True + vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE + + vessel_structure_2 = sp.Settings() + vessel_structure_2[Tags.PRIORITY] = 3 + vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, + VOLUME_HEIGHT_IN_MM / 3] + vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, + VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] + vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 + vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) + vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True + vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + + tissue_dict = sp.Settings() + tissue_dict[Tags.BACKGROUND] = background_dictionary + tissue_dict["epidermis"] = epidermis_structure + tissue_dict["vessel_1"] = vessel_structure_1 + tissue_dict["vessel_2"] = vessel_structure_2 + return tissue_dict + + # set settings for volume creation, optical simulation and iterative qPAI method + np.random.seed(RANDOM_SEED) + + general_settings = { + # These parameters set the general properties of the simulated volume + Tags.RANDOM_SEED: RANDOM_SEED, + Tags.VOLUME_NAME: VOLUME_NAME, + Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + Tags.SPACING_MM: SPACING, + Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + Tags.WAVELENGTHS: [700] + } + + settings = sp.Settings(general_settings) + + settings.set_volume_creation_settings({ + # These parameters set the properties for the volume creation + Tags.SIMULATE_DEFORMED_LAYERS: True, + Tags.STRUCTURES: create_example_tissue() + }) + settings.set_optical_settings({ + # These parameters set the properties for the optical Monte Carlo simulation + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + }) + settings["noise_model"] = { + Tags.NOISE_MEAN: 1.0, + Tags.NOISE_STD: 0.01, + Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + } + settings["iterative_qpai_reconstruction"] = { + # These parameters set the properties of the iterative reconstruction + Tags.DOWNSCALE_FACTOR: 0.75, + Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, + # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant + Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, + Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, + # for this example, we are not interested in all absorption updates + Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, + Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 + } + + # run pipeline including iterative qPAI method + pipeline = [ + sp.ModelBasedVolumeCreationAdapter(settings), + sp.MCXAdapter(settings), + sp.GaussianNoise(settings, "noise_model"), + sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") + ] + + class CustomDevice(sp.PhotoacousticDevice): + + def __init__(self): + super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, + general_settings[Tags.DIM_VOLUME_Y_MM] / 2, + 0])) + self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) + + device = CustomDevice() + + device.update_settings_for_use_of_model_based_volume_creator(settings) + + sp.simulate(pipeline, settings, device) + + # visualize reconstruction results + if visualise: + # get simulation output + data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + settings = sp.load_data_field(data_path, Tags.SETTINGS) + wavelength = settings[Tags.WAVELENGTHS][0] + + # get reconstruction result + absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) + + # get ground truth absorption coefficients + absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) + + # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) + scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR + absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") + + # compute reconstruction error + difference = absorption_gt - absorption_reconstruction + + median_error = np.median(difference) + q3, q1 = np.percentile(difference, [75, 25]) + iqr = q3 - q1 + + # visualize results + x_pos = int(np.shape(absorption_gt)[0] / 2) + y_pos = int(np.shape(absorption_gt)[1] / 2) + + if np.min(absorption_gt) > np.min(absorption_reconstruction): + cmin = np.min(absorption_reconstruction) else: - plt.clim(np.min(difference), np.max(difference)) - - for i, quantity in enumerate(results_y_z): - plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) - if i == 0: - plt.ylabel("y-z", fontsize=10) - plt.title(label[i], fontsize=10) - plt.imshow(quantity.T) - plt.xticks(fontsize=6) - plt.yticks(fontsize=6) - plt.colorbar() - if i != 2: - plt.clim(cmin, cmax) - else: - plt.clim(np.min(difference), np.max(difference)) + cmin = np.min(absorption_gt) - plt.show() - plt.close() + if np.max(absorption_gt) > np.max(absorption_reconstruction): + cmax = np.max(absorption_gt) + else: + cmax = np.max(absorption_reconstruction) + + results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] + results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] + + label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", + "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] + + plt.figure(figsize=(20, 15)) + plt.subplots_adjust(hspace=0.5) + plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + + "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) + + for i, quantity in enumerate(results_x_z): + plt.subplot(2, len(results_x_z), i + 1) + if i == 0: + plt.ylabel("x-z", fontsize=10) + plt.title(label[i], fontsize=10) + plt.imshow(quantity.T) + plt.xticks(fontsize=6) + plt.yticks(fontsize=6) + plt.colorbar() + if i != 2: + plt.clim(cmin, cmax) + else: + plt.clim(np.min(difference), np.max(difference)) + + for i, quantity in enumerate(results_y_z): + plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) + if i == 0: + plt.ylabel("y-z", fontsize=10) + plt.title(label[i], fontsize=10) + plt.imshow(quantity.T) + plt.xticks(fontsize=6) + plt.yticks(fontsize=6) + plt.colorbar() + if i != 2: + plt.clim(cmin, cmax) + else: + plt.clim(np.min(difference), np.max(difference)) + + plt.show() + plt.close() + + +if __name__ == "__main__": + run_iterative_qPAI_example(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py b/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py index 8832878f..42798097 100644 --- a/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py +++ b/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py @@ -114,7 +114,7 @@ def test_convenience_function(self): get_detection_geometry(), speed_of_sound=1540, density=1000, alpha_coeff=0.0, spacing_mm=0.25) - + # reconstruct the time series data to compare it with initial pressure self.settings.set_reconstruction_settings({ Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, From 34226fab5762ec0a42e0ebe7d46bf93e89a8e7d6 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 21 Jun 2024 09:49:12 +0200 Subject: [PATCH 03/34] Scripts to run the examples --- simpa_examples/benchmarking/__init__.py | 3 + simpa_examples/benchmarking/open_tests.py | 121 ++++++++++++++++++ .../benchmarking/performance_check_gpu.py | 24 ++++ .../benchmarking/performance_check_memory.py | 24 ++++ .../benchmarking/performance_check_time.py | 24 ++++ 5 files changed, 196 insertions(+) create mode 100644 simpa_examples/benchmarking/__init__.py create mode 100644 simpa_examples/benchmarking/open_tests.py create mode 100644 simpa_examples/benchmarking/performance_check_gpu.py create mode 100644 simpa_examples/benchmarking/performance_check_memory.py create mode 100644 simpa_examples/benchmarking/performance_check_time.py diff --git a/simpa_examples/benchmarking/__init__.py b/simpa_examples/benchmarking/__init__.py new file mode 100644 index 00000000..89cc8954 --- /dev/null +++ b/simpa_examples/benchmarking/__init__.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT diff --git a/simpa_examples/benchmarking/open_tests.py b/simpa_examples/benchmarking/open_tests.py new file mode 100644 index 00000000..8107080c --- /dev/null +++ b/simpa_examples/benchmarking/open_tests.py @@ -0,0 +1,121 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import os +import numpy as np +from prettytable import PrettyTable + + +def lines_that_contain(string, fp): + return [line for line in fp if string in line] + + +examples = ['linear_unmixing', 'minimal_optical_simulation', 'minimal_optical_simulation_uniform_cube', + 'msot_invision_simulation', 'optical_and_acoustic_simulation', + 'perform_iterative_qPAI_reconstruction'] +profiles = ['time', "gpu", "memory"] +spacings = np.arange(0.2, 0.6, 0.2) +benchmarking_dict = {} +for example in examples: + benchmarking_dict[example] = {} + for spacing in spacings: + benchmarking_dict[example][spacing] = {} + +info_starts = {"memory": 19, "gpu": 12, "time": 16} +info_ends = {"memory": 29, "gpu": 26, "time": 29} + +for profile in profiles: + for spacing in spacings: + file_name = "./benchmarking/benchmarking_data/benchmarking_data_"+profile+"_"+str(spacing)+".txt" + benchmarking_file = open(file_name, 'r') + current_examples = [] + + if profile == 'time': + example_name_lines = lines_that_contain("File:", benchmarking_file) + + for enl in example_name_lines: + example = enl.rpartition("simpa_examples/")[2].rpartition(".py")[0] + if example not in current_examples: + current_examples.append(example) + else: + break + + elif profile == 'gpu': + example_name_lines = lines_that_contain("##", benchmarking_file) + + for enl in example_name_lines: + example = enl.rpartition("run_")[2].rpartition("\n")[0] + if example not in current_examples: + current_examples.append(example) + else: + break + + if profile == 'memory': + example_name_lines = lines_that_contain("Filename:", benchmarking_file) + + for enl in example_name_lines: + example = enl.rpartition("simpa_examples/")[2].rpartition(".py")[0] + if example not in current_examples: + current_examples.append(example) + else: + break + + benchmarking_file = open(file_name, 'r') + lines_with_sp_simulate = lines_that_contain("sp.simulate", benchmarking_file) + + values = [] + units = [] + examples_counter = 0 + + for lwss in lines_with_sp_simulate: + value = float(lwss[info_starts[profile]:info_ends[profile]]) + + unit = str(lwss[info_ends[profile]]) + if profile == "time": + unit = "" + + example = current_examples[examples_counter] + benchmarking_dict[example][spacing][profile] = str(value)+unit + examples_counter += 1 + if examples_counter == len(current_examples): + examples_counter = 0 + +# for line_with_sp_simulate in lines_with_sp_simulate: +# value = float(line_with_sp_simulate[19:29]) +# unit = line_with_sp_simulate[29] +# benchmarking_dict[file_names[examples_counter]][spacings[spacing_counter]][os.environ['SIMPA_PROFILE']+" value"] = value +# benchmarking_dict[file_names[examples_counter]][spacings[spacing_counter]][os.environ['SIMPA_PROFILE']+" unit"] = unit +# examples_counter += 1 +# if examples_counter == 5: +# spacing_counter += 1 +# examples_counter = 0 +# +# for line_with_sp_simulate in lines_with_sp_simulate: +# value = float(line_with_sp_simulate[16:29]) +# benchmarking_dict[file_names[examples_counter]][spacings[spacing_counter]]["time"] = value +# examples_counter += 1 +# if examples_counter == 6: +# spacing_counter += 1 +# examples_counter = 0 + + +table = PrettyTable() +table.field_names = ["Example", "Spacing mm", 'Time μs', "GPU", "MEMORY"] +divider = False +for example, spacing_dict in benchmarking_dict.items(): + subrow_counter = 0 + for spacing, p_dict in spacing_dict.items(): + try: + p_dict["memory"] + except KeyError: + p_dict["memory"] = "N/A" + if subrow_counter == len(spacing_dict)-1: + divider = True + if subrow_counter == 0: + table.add_row([example, spacing, p_dict['time'], p_dict["gpu"], p_dict["memory"]], divider=divider) + else: + table.add_row(["", spacing, p_dict['time'], p_dict["gpu"], p_dict["memory"]], divider=divider) + subrow_counter += 1 + divider = False +print(table) diff --git a/simpa_examples/benchmarking/performance_check_gpu.py b/simpa_examples/benchmarking/performance_check_gpu.py new file mode 100644 index 00000000..de35dca7 --- /dev/null +++ b/simpa_examples/benchmarking/performance_check_gpu.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import numpy as np +import simpa as sp +from simpa_examples import * +import os +spacing = 0.2 +os.environ["SIMPA_PROFILE"] = "GPU_MEMORY" +os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_gpu_"+str(spacing)+".txt" + + +path_manager = sp.PathManager() + +examples = [run_linear_unmixing, run_minimal_optical_simulation, run_minimal_optical_simulation_uniform_cube, + run_msot_invision_simulation, run_optical_and_acoustic_simulation, + run_perform_iterative_qPAI_reconstruction] + +for example in examples: + try: + example(SPACING=spacing, path_manager=sp.PathManager(), visualise=False) + except AttributeError: + print("simulation cannot be run on {} with spacing {}".format(example, spacing)) diff --git a/simpa_examples/benchmarking/performance_check_memory.py b/simpa_examples/benchmarking/performance_check_memory.py new file mode 100644 index 00000000..1114c5f4 --- /dev/null +++ b/simpa_examples/benchmarking/performance_check_memory.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import numpy as np +import simpa as sp +from simpa_examples import * +import os +spacing = 0.4 +os.environ["SIMPA_PROFILE"] = "MEMORY" +os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_memory_"+str(spacing)+".txt" + + +path_manager = sp.PathManager() + +examples = [run_linear_unmixing, run_minimal_optical_simulation, run_minimal_optical_simulation_uniform_cube, + run_msot_invision_simulation, run_optical_and_acoustic_simulation, + run_perform_iterative_qPAI_reconstruction] + +for example in examples: + try: + example(SPACING=spacing, path_manager=sp.PathManager(), visualise=False) + except AttributeError: + print("simulation cannot be run on {} with spacing {}".format(example, spacing)) diff --git a/simpa_examples/benchmarking/performance_check_time.py b/simpa_examples/benchmarking/performance_check_time.py new file mode 100644 index 00000000..38866063 --- /dev/null +++ b/simpa_examples/benchmarking/performance_check_time.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import numpy as np +import simpa as sp +from simpa_examples import * +import os +spacing = 0.4 +os.environ["SIMPA_PROFILE"] = "TIME" +os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_time_"+str(spacing)+".txt" + + +path_manager = sp.PathManager() + +examples = [run_linear_unmixing, run_minimal_optical_simulation, run_minimal_optical_simulation_uniform_cube, + run_msot_invision_simulation, run_optical_and_acoustic_simulation, + run_perform_iterative_qPAI_reconstruction] + +for example in examples: + try: + example(SPACING=spacing, path_manager=sp.PathManager(), visualise=False) + except AttributeError: + print("simulation cannot be run on {} with spacing {}".format(example, spacing)) From ac800ca70d5411a32810da337eb3934113343c0a Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 21 Jun 2024 09:51:28 +0200 Subject: [PATCH 04/34] rename to make it clear --- .../benchmarking/{open_tests.py => create_benchmarking_table.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename simpa_examples/benchmarking/{open_tests.py => create_benchmarking_table.py} (100%) diff --git a/simpa_examples/benchmarking/open_tests.py b/simpa_examples/benchmarking/create_benchmarking_table.py similarity index 100% rename from simpa_examples/benchmarking/open_tests.py rename to simpa_examples/benchmarking/create_benchmarking_table.py From 2cd066652a6455883eb261785710cf138838cbb4 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 21 Jun 2024 10:46:19 +0200 Subject: [PATCH 05/34] benchmark table to file not printed in terminal --- .../benchmarking/create_benchmarking_table.py | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/simpa_examples/benchmarking/create_benchmarking_table.py b/simpa_examples/benchmarking/create_benchmarking_table.py index 8107080c..fc80cd1f 100644 --- a/simpa_examples/benchmarking/create_benchmarking_table.py +++ b/simpa_examples/benchmarking/create_benchmarking_table.py @@ -81,24 +81,6 @@ def lines_that_contain(string, fp): if examples_counter == len(current_examples): examples_counter = 0 -# for line_with_sp_simulate in lines_with_sp_simulate: -# value = float(line_with_sp_simulate[19:29]) -# unit = line_with_sp_simulate[29] -# benchmarking_dict[file_names[examples_counter]][spacings[spacing_counter]][os.environ['SIMPA_PROFILE']+" value"] = value -# benchmarking_dict[file_names[examples_counter]][spacings[spacing_counter]][os.environ['SIMPA_PROFILE']+" unit"] = unit -# examples_counter += 1 -# if examples_counter == 5: -# spacing_counter += 1 -# examples_counter = 0 -# -# for line_with_sp_simulate in lines_with_sp_simulate: -# value = float(line_with_sp_simulate[16:29]) -# benchmarking_dict[file_names[examples_counter]][spacings[spacing_counter]]["time"] = value -# examples_counter += 1 -# if examples_counter == 6: -# spacing_counter += 1 -# examples_counter = 0 - table = PrettyTable() table.field_names = ["Example", "Spacing mm", 'Time μs', "GPU", "MEMORY"] @@ -118,4 +100,7 @@ def lines_that_contain(string, fp): table.add_row(["", spacing, p_dict['time'], p_dict["gpu"], p_dict["memory"]], divider=divider) subrow_counter += 1 divider = False -print(table) + +table_file_name = "./benchmarking/benchmarking_data/benchmarking_data_table.txt" +benchmarking_table_file = open(table_file_name, 'w') +benchmarking_table_file.write(table.get_string()) From 56174928a685cb4459e4b02ee9bad513149fcf36 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 21 Jun 2024 11:24:43 +0200 Subject: [PATCH 06/34] fix linear unmixing --- simpa_examples/linear_unmixing.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index 5469a2a1..2654569e 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -3,21 +3,20 @@ # SPDX-License-Identifier: MIT import os - import numpy as np +from typing import Union import simpa as sp from simpa import Tags from simpa.visualisation.matplotlib_data_visualisation import visualise_data from simpa.utils.profiling import profile -from typing import Union # FIXME temporary workaround for newest Intel architectures os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" @profile -def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): +def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager(), visualise: bool = True): """ :param SPACING: The simulation spacing between voxels From e4088f483dbc4f0c008d8028f216e7ef40081ebf Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 21 Jun 2024 11:59:18 +0200 Subject: [PATCH 07/34] rename of processes --- simpa_examples/minimal_optical_simulation.py | 2 +- simpa_examples/minimal_optical_simulation_uniform_cube.py | 2 +- simpa_examples/msot_invision_simulation.py | 2 +- simpa_examples/optical_and_acoustic_simulation.py | 2 +- simpa_examples/perform_image_reconstruction.py | 2 +- simpa_examples/perform_iterative_qPAI_reconstruction.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index ecf555cb..38f3ceef 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -167,4 +167,4 @@ def __init__(self): if __name__ == "__main__": - run_minimal_optical_example(SPACING=0.2, path_manager=sp.PathManager(), visualise=True) + run_minimal_optical_simulation(SPACING=0.2, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/minimal_optical_simulation_uniform_cube.py b/simpa_examples/minimal_optical_simulation_uniform_cube.py index bcbf9d01..826e02a1 100644 --- a/simpa_examples/minimal_optical_simulation_uniform_cube.py +++ b/simpa_examples/minimal_optical_simulation_uniform_cube.py @@ -104,4 +104,4 @@ def create_example_tissue(): if __name__ == "__main__": - run_optical_simulation_uniform_cube_example(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) + run_minimal_optical_simulation_uniform_cube(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 75f6ef73..33bd775c 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -207,4 +207,4 @@ def get_settings(): if __name__ == "__main__": - run_msot_invision_example(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) + run_msot_invision_simulation(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 67bd00a8..7e450f99 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -211,4 +211,4 @@ def create_example_tissue(): if __name__ == "__main__": - run_optical_and_acoustic_example(SPACING=0.2, path_manager=sp.PathManager(), visualise=True) + run_optical_and_acoustic_simulation(SPACING=0.2, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/perform_image_reconstruction.py b/simpa_examples/perform_image_reconstruction.py index caa8f2d8..2d1fa9b2 100644 --- a/simpa_examples/perform_image_reconstruction.py +++ b/simpa_examples/perform_image_reconstruction.py @@ -57,4 +57,4 @@ def run_perform_image_reconstruction(SPACING: Union[int, float] = 0.5, path_mana if __name__ == "__main__": - run_image_reconstruction_example(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) + run_perform_image_reconstruction(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) diff --git a/simpa_examples/perform_iterative_qPAI_reconstruction.py b/simpa_examples/perform_iterative_qPAI_reconstruction.py index d9da14e9..671585a0 100644 --- a/simpa_examples/perform_iterative_qPAI_reconstruction.py +++ b/simpa_examples/perform_iterative_qPAI_reconstruction.py @@ -241,4 +241,4 @@ def __init__(self): if __name__ == "__main__": - run_iterative_qPAI_example(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) + run_perform_iterative_qPAI_reconstruction(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) From 422c33c10c6ffdec49bac9a307e76637be154d5a Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 21 Jun 2024 16:18:40 +0200 Subject: [PATCH 08/34] Warning to get around autopep8 --- simpa_examples/benchmarking/performance_check_gpu.py | 8 +++++--- simpa_examples/benchmarking/performance_check_memory.py | 1 + simpa_examples/benchmarking/performance_check_time.py | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/simpa_examples/benchmarking/performance_check_gpu.py b/simpa_examples/benchmarking/performance_check_gpu.py index de35dca7..a41c6611 100644 --- a/simpa_examples/benchmarking/performance_check_gpu.py +++ b/simpa_examples/benchmarking/performance_check_gpu.py @@ -6,10 +6,12 @@ import simpa as sp from simpa_examples import * import os -spacing = 0.2 -os.environ["SIMPA_PROFILE"] = "GPU_MEMORY" -os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_gpu_"+str(spacing)+".txt" +# TODO: following text must be ABOVE importing simpa and simpa examples for benchmarking to work +spacing = 0.2 +os.environ[")SIMPA_PROFILE"] = "GPU_MEMORY" +os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_gpu_" + str( + spacing) + ".txt" path_manager = sp.PathManager() diff --git a/simpa_examples/benchmarking/performance_check_memory.py b/simpa_examples/benchmarking/performance_check_memory.py index 1114c5f4..8874f621 100644 --- a/simpa_examples/benchmarking/performance_check_memory.py +++ b/simpa_examples/benchmarking/performance_check_memory.py @@ -6,6 +6,7 @@ import simpa as sp from simpa_examples import * import os +# TODO: following text must be ABOVE importing simpa and simpa examples for benchmarking to work spacing = 0.4 os.environ["SIMPA_PROFILE"] = "MEMORY" os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_memory_"+str(spacing)+".txt" diff --git a/simpa_examples/benchmarking/performance_check_time.py b/simpa_examples/benchmarking/performance_check_time.py index 38866063..a969fa65 100644 --- a/simpa_examples/benchmarking/performance_check_time.py +++ b/simpa_examples/benchmarking/performance_check_time.py @@ -6,6 +6,7 @@ import simpa as sp from simpa_examples import * import os +# TODO: following text must be ABOVE importing simpa and simpa examples for benchmarking to work spacing = 0.4 os.environ["SIMPA_PROFILE"] = "TIME" os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_time_"+str(spacing)+".txt" From ed1505b6373529621d41a0bb7e7491f8f560a4a0 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Tue, 25 Jun 2024 14:19:50 +0200 Subject: [PATCH 09/34] adding bash scripts to perform the tests adding typeface to make it all look nice --- simpa_examples/__init__.py | 1 + .../benchmarking_data_gpu_0.2.txt | 1034 ++++++++++++++++ .../benchmarking_data_gpu_0.4.txt | 1034 ++++++++++++++++ .../benchmarking_data_memory_0.2.txt | 1027 ++++++++++++++++ .../benchmarking_data_memory_0.4.txt | 0 .../benchmarking_data_table.txt | 21 + .../benchmarking_data_time_0.2.txt | 1048 +++++++++++++++++ .../benchmarking_data_time_0.4.txt | 1048 +++++++++++++++++ .../benchmarking/create_benchmarking_table.py | 28 +- .../benchmarking/performance_check.py | 39 + .../benchmarking/performance_check_gpu.py | 26 - .../benchmarking/performance_check_memory.py | 25 - .../benchmarking/performance_check_time.py | 25 - .../benchmarking/run_benchmarking.sh | 72 ++ simpa_examples/linear_unmixing.py | 11 +- simpa_examples/minimal_optical_simulation.py | 10 +- ...minimal_optical_simulation_uniform_cube.py | 11 +- simpa_examples/msot_invision_simulation.py | 10 +- .../optical_and_acoustic_simulation.py | 10 +- .../perform_iterative_qPAI_reconstruction.py | 11 +- simpa_examples/segmentation_loader.py | 171 +-- 21 files changed, 5482 insertions(+), 180 deletions(-) create mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.2.txt create mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.4.txt create mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.2.txt create mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.4.txt create mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_table.txt create mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.2.txt create mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.4.txt create mode 100644 simpa_examples/benchmarking/performance_check.py delete mode 100644 simpa_examples/benchmarking/performance_check_gpu.py delete mode 100644 simpa_examples/benchmarking/performance_check_memory.py delete mode 100644 simpa_examples/benchmarking/performance_check_time.py create mode 100644 simpa_examples/benchmarking/run_benchmarking.sh diff --git a/simpa_examples/__init__.py b/simpa_examples/__init__.py index 92a0960b..49f13e4d 100644 --- a/simpa_examples/__init__.py +++ b/simpa_examples/__init__.py @@ -9,3 +9,4 @@ from simpa_examples.optical_and_acoustic_simulation import run_optical_and_acoustic_simulation from simpa_examples.perform_image_reconstruction import run_perform_image_reconstruction from simpa_examples.perform_iterative_qPAI_reconstruction import run_perform_iterative_qPAI_reconstruction +from simpa_examples.segmentation_loader import run_segmentation_loader diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.2.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.2.txt new file mode 100644 index 00000000..4e5ce73b --- /dev/null +++ b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.2.txt @@ -0,0 +1,1034 @@ +## run_perform_iterative_qPAI_reconstruction + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 152.00M 25 @profile + 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise:bool = True): + 27 """ + 28 + 29 :param SPACING: The simulation spacing between voxels + 30 :param path_manager: the path manager to be used, typically sp.PathManager + 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 32 :return: a run through of the example + 33 """ + 8.12M 152.00M 34 VOLUME_TRANSDUCER_DIM_IN_MM = 30 + 8.12M 152.00M 35 VOLUME_PLANAR_DIM_IN_MM = 30 + 8.12M 152.00M 36 VOLUME_HEIGHT_IN_MM = 30 + 8.12M 152.00M 37 RANDOM_SEED = 471 + 8.12M 152.00M 38 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) + 39 + 40 # If VISUALIZE is set to True, the reconstruction result will be plotted + 41 + 8.12M 152.00M 42 def create_example_tissue(): + 43 """ + 44 This is a very simple example script of how to create a tissue definition. + 45 It contains a muscular background, an epidermis layer on top of the muscles + 46 and a blood vessel. + 47 """ + 48 background_dictionary = sp.Settings() + 49 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) + 50 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 51 + 52 epidermis_structure = sp.Settings() + 53 epidermis_structure[Tags.PRIORITY] = 1 + 54 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] + 55 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] + 56 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) + 57 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True + 58 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True + 59 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 60 + 61 vessel_structure_1 = sp.Settings() + 62 vessel_structure_1[Tags.PRIORITY] = 2 + 63 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, + 64 VOLUME_HEIGHT_IN_MM / 2] + 65 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, + 66 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] + 67 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 + 68 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 + 69 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) + 70 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True + 71 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True + 72 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE + 73 + 74 vessel_structure_2 = sp.Settings() + 75 vessel_structure_2[Tags.PRIORITY] = 3 + 76 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, + 77 VOLUME_HEIGHT_IN_MM / 3] + 78 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, + 79 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] + 80 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 + 81 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) + 82 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True + 83 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 84 + 85 tissue_dict = sp.Settings() + 86 tissue_dict[Tags.BACKGROUND] = background_dictionary + 87 tissue_dict["epidermis"] = epidermis_structure + 88 tissue_dict["vessel_1"] = vessel_structure_1 + 89 tissue_dict["vessel_2"] = vessel_structure_2 + 90 return tissue_dict + 91 + 92 + 93 # set settings for volume creation, optical simulation and iterative qPAI method + 8.12M 152.00M 94 np.random.seed(RANDOM_SEED) + 95 + 8.12M 152.00M 96 general_settings = { + 97 # These parameters set the general properties of the simulated volume + 8.12M 152.00M 98 Tags.RANDOM_SEED: RANDOM_SEED, + 8.12M 152.00M 99 Tags.VOLUME_NAME: VOLUME_NAME, + 8.12M 152.00M 100 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 8.12M 152.00M 101 Tags.SPACING_MM: SPACING, + 8.12M 152.00M 102 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 8.12M 152.00M 103 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 8.12M 152.00M 104 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 8.12M 152.00M 105 Tags.WAVELENGTHS: [700] + 106 } + 107 + 8.12M 152.00M 108 settings = sp.Settings(general_settings) + 109 + 8.12M 152.00M 110 settings.set_volume_creation_settings({ + 111 # These parameters set the properties for the volume creation + 8.12M 152.00M 112 Tags.SIMULATE_DEFORMED_LAYERS: True, + 8.12M 152.00M 113 Tags.STRUCTURES: create_example_tissue() + 114 }) + 8.12M 152.00M 115 settings.set_optical_settings({ + 116 # These parameters set the properties for the optical Monte Carlo simulation + 8.12M 152.00M 117 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 8.12M 152.00M 118 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 8.12M 152.00M 119 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 8.12M 152.00M 120 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 121 }) + 8.12M 152.00M 122 settings["noise_model"] = { + 8.12M 152.00M 123 Tags.NOISE_MEAN: 1.0, + 8.12M 152.00M 124 Tags.NOISE_STD: 0.01, + 8.12M 152.00M 125 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 8.12M 152.00M 126 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 8.12M 152.00M 127 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 128 } + 8.12M 152.00M 129 settings["iterative_qpai_reconstruction"] = { + 130 # These parameters set the properties of the iterative reconstruction + 8.12M 152.00M 131 Tags.DOWNSCALE_FACTOR: 0.75, + 8.12M 152.00M 132 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, + 133 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant + 8.12M 152.00M 134 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, + 8.12M 152.00M 135 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, + 136 # for this example, we are not interested in all absorption updates + 8.12M 152.00M 137 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, + 8.12M 152.00M 138 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 + 139 } + 140 + 141 # run pipeline including iterative qPAI method + 8.12M 152.00M 142 pipeline = [ + 8.12M 152.00M 143 sp.ModelBasedVolumeCreationAdapter(settings), + 8.12M 152.00M 144 sp.MCXAdapter(settings), + 8.12M 152.00M 145 sp.GaussianNoise(settings, "noise_model"), + 8.12M 152.00M 146 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") + 147 ] + 148 + 149 + 8.12M 152.00M 150 class CustomDevice(sp.PhotoacousticDevice): + 151 + 152 def __init__(self): + 153 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, + 154 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, + 155 0])) + 156 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) + 157 + 158 + 8.12M 152.00M 159 device = CustomDevice() + 160 + 8.12M 152.00M 161 device.update_settings_for_use_of_model_based_volume_creator(settings) + 162 + 346.85M 376.00M 163 sp.simulate(pipeline, settings, device) + 164 + 165 # visualize reconstruction results + 8.12M 150.00M 166 if visualise: + 167 # get simulation output + 168 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 169 settings = sp.load_data_field(data_path, Tags.SETTINGS) + 170 wavelength = settings[Tags.WAVELENGTHS][0] + 171 + 172 # get reconstruction result + 173 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) + 174 + 175 # get ground truth absorption coefficients + 176 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) + 177 + 178 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) + 179 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR + 180 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") + 181 + 182 # compute reconstruction error + 183 difference = absorption_gt - absorption_reconstruction + 184 + 185 median_error = np.median(difference) + 186 q3, q1 = np.percentile(difference, [75, 25]) + 187 iqr = q3 - q1 + 188 + 189 # visualize results + 190 x_pos = int(np.shape(absorption_gt)[0] / 2) + 191 y_pos = int(np.shape(absorption_gt)[1] / 2) + 192 + 193 if np.min(absorption_gt) > np.min(absorption_reconstruction): + 194 cmin = np.min(absorption_reconstruction) + 195 else: + 196 cmin = np.min(absorption_gt) + 197 + 198 if np.max(absorption_gt) > np.max(absorption_reconstruction): + 199 cmax = np.max(absorption_gt) + 200 else: + 201 cmax = np.max(absorption_reconstruction) + 202 + 203 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] + 204 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] + 205 + 206 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", + 207 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] + 208 + 209 plt.figure(figsize=(20, 15)) + 210 plt.subplots_adjust(hspace=0.5) + 211 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + + 212 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) + 213 + 214 for i, quantity in enumerate(results_x_z): + 215 plt.subplot(2, len(results_x_z), i + 1) + 216 if i == 0: + 217 plt.ylabel("x-z", fontsize=10) + 218 plt.title(label[i], fontsize=10) + 219 plt.imshow(quantity.T) + 220 plt.xticks(fontsize=6) + 221 plt.yticks(fontsize=6) + 222 plt.colorbar() + 223 if i != 2: + 224 plt.clim(cmin, cmax) + 225 else: + 226 plt.clim(np.min(difference), np.max(difference)) + 227 + 228 for i, quantity in enumerate(results_y_z): + 229 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) + 230 if i == 0: + 231 plt.ylabel("y-z", fontsize=10) + 232 plt.title(label[i], fontsize=10) + 233 plt.imshow(quantity.T) + 234 plt.xticks(fontsize=6) + 235 plt.yticks(fontsize=6) + 236 plt.colorbar() + 237 if i != 2: + 238 plt.clim(cmin, cmax) + 239 else: + 240 plt.clim(np.min(difference), np.max(difference)) + 241 + 242 plt.show() + 243 plt.close() +## run_optical_and_acoustic_simulation + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 148.00M 18 @profile + 19 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), + 20 visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 8.12M 148.00M 28 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 8.12M 148.00M 29 VOLUME_PLANAR_DIM_IN_MM = 20 + 8.12M 148.00M 30 VOLUME_HEIGHT_IN_MM = 25 + 8.12M 148.00M 31 RANDOM_SEED = 4711 + 32 + 33 # If VISUALIZE is set to True, the simulation result will be plotted + 8.12M 148.00M 34 VISUALIZE = True + 35 + 8.12M 148.00M 36 def create_example_tissue(): + 37 """ + 38 This is a very simple example script of how to create a tissue definition. + 39 It contains a muscular background, an epidermis layer on top of the muscles + 40 and a blood vessel. + 41 """ + 42 background_dictionary = sp.Settings() + 43 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) + 44 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 45 + 46 tissue_dict = sp.Settings() + 47 tissue_dict[Tags.BACKGROUND] = background_dictionary + 48 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, + 49 molecular_composition=sp.TISSUE_LIBRARY.constant( + 50 0.05, 100, 0.9), + 51 priority=1, + 52 consider_partial_volume=True, + 53 adhere_to_deformation=True) + 54 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, + 55 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), + 56 priority=8, + 57 consider_partial_volume=True, + 58 adhere_to_deformation=True) + 59 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( + 60 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], + 61 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], + 62 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 63 radius_mm=2, priority=3, consider_partial_volume=True, + 64 adhere_to_deformation=False + 65 ) + 66 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( + 67 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], + 68 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], + 69 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 70 radius_mm=3, priority=3, consider_partial_volume=True, + 71 adhere_to_deformation=False + 72 ) + 73 return tissue_dict + 74 + 75 + 76 # Seed the numpy random configuration prior to creating the global_settings file in + 77 # order to ensure that the same volume + 78 # is generated with the same random seed every time. + 79 + 8.12M 148.00M 80 np.random.seed(RANDOM_SEED) + 8.12M 148.00M 81 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) + 82 + 8.12M 148.00M 83 general_settings = { + 84 # These parameters set the general properties of the simulated volume + 8.12M 148.00M 85 Tags.RANDOM_SEED: RANDOM_SEED, + 8.12M 148.00M 86 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), + 8.12M 148.00M 87 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 8.12M 148.00M 88 Tags.SPACING_MM: SPACING, + 8.12M 148.00M 89 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 8.12M 148.00M 90 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 8.12M 148.00M 91 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 8.12M 148.00M 92 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 8.12M 148.00M 93 Tags.GPU: True, + 8.12M 148.00M 94 Tags.WAVELENGTHS: [700, 800], + 8.12M 148.00M 95 Tags.DO_FILE_COMPRESSION: True, + 8.12M 148.00M 96 Tags.DO_IPASC_EXPORT: True + 97 } + 8.12M 148.00M 98 settings = sp.Settings(general_settings) + 8.12M 148.00M 99 np.random.seed(RANDOM_SEED) + 100 + 8.12M 148.00M 101 settings.set_volume_creation_settings({ + 8.12M 148.00M 102 Tags.STRUCTURES: create_example_tissue(), + 8.12M 148.00M 103 Tags.SIMULATE_DEFORMED_LAYERS: True + 104 }) + 105 + 8.12M 148.00M 106 settings.set_optical_settings({ + 8.12M 148.00M 107 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 8.12M 148.00M 108 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 8.12M 148.00M 109 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, + 8.12M 148.00M 110 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 8.12M 148.00M 111 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, + 112 }) + 113 + 8.12M 148.00M 114 settings.set_acoustic_settings({ + 8.12M 148.00M 115 Tags.ACOUSTIC_SIMULATION_3D: False, + 8.12M 148.00M 116 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 8.12M 148.00M 117 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 8.12M 148.00M 118 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 8.12M 148.00M 119 Tags.KWAVE_PROPERTY_PMLInside: False, + 8.12M 148.00M 120 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 8.12M 148.00M 121 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 8.12M 148.00M 122 Tags.KWAVE_PROPERTY_PlotPML: False, + 8.12M 148.00M 123 Tags.RECORDMOVIE: False, + 8.12M 148.00M 124 Tags.MOVIENAME: "visualization_log", + 8.12M 148.00M 125 Tags.ACOUSTIC_LOG_SCALE: True + 126 }) + 127 + 8.12M 148.00M 128 settings.set_reconstruction_settings({ + 8.12M 148.00M 129 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 8.12M 148.00M 130 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 8.12M 148.00M 131 Tags.ACOUSTIC_SIMULATION_3D: False, + 8.12M 148.00M 132 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 8.12M 148.00M 133 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 8.12M 148.00M 134 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), + 8.12M 148.00M 135 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), + 8.12M 148.00M 136 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 8.12M 148.00M 137 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 8.12M 148.00M 138 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, + 8.12M 148.00M 139 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 8.12M 148.00M 140 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 8.12M 148.00M 141 Tags.KWAVE_PROPERTY_PMLInside: False, + 8.12M 148.00M 142 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 8.12M 148.00M 143 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 8.12M 148.00M 144 Tags.KWAVE_PROPERTY_PlotPML: False, + 8.12M 148.00M 145 Tags.RECORDMOVIE: False, + 8.12M 148.00M 146 Tags.MOVIENAME: "visualization_log", + 8.12M 148.00M 147 Tags.ACOUSTIC_LOG_SCALE: True, + 8.12M 148.00M 148 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, + 8.12M 148.00M 149 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, + 8.12M 148.00M 150 Tags.DATA_FIELD_DENSITY: 1000, + 8.12M 148.00M 151 Tags.SPACING_MM: SPACING + 152 }) + 153 + 8.12M 148.00M 154 settings["noise_initial_pressure"] = { + 8.12M 148.00M 155 Tags.NOISE_MEAN: 1, + 8.12M 148.00M 156 Tags.NOISE_STD: 0.01, + 8.12M 148.00M 157 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 8.12M 148.00M 158 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 8.12M 148.00M 159 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 160 } + 161 + 8.12M 148.00M 162 settings["noise_time_series"] = { + 8.12M 148.00M 163 Tags.NOISE_STD: 1, + 8.12M 148.00M 164 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, + 8.12M 148.00M 165 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA + 166 } + 167 + 168 # TODO: For the device choice, uncomment the undesired device + 169 + 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 171 # VOLUME_PLANAR_DIM_IN_MM/2, + 172 # 0])) + 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) + 174 + 8.12M 148.00M 175 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 8.12M 148.00M 176 VOLUME_PLANAR_DIM_IN_MM/2, + 8.12M 148.00M 177 0]), + 8.12M 148.00M 178 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) + 8.12M 148.00M 179 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, + 8.12M 148.00M 180 pitch_mm=0.25, + 8.12M 148.00M 181 number_detector_elements=100, + 8.12M 148.00M 182 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) + 8.12M 148.00M 183 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) + 8.12M 148.00M 184 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) + 185 + 186 + 8.12M 148.00M 187 SIMULATION_PIPELINE = [ + 8.12M 148.00M 188 sp.ModelBasedVolumeCreationAdapter(settings), + 8.12M 148.00M 189 sp.MCXAdapter(settings), + 8.12M 148.00M 190 sp.GaussianNoise(settings, "noise_initial_pressure"), + 8.12M 148.00M 191 sp.KWaveAdapter(settings), + 8.12M 148.00M 192 sp.GaussianNoise(settings, "noise_time_series"), + 8.12M 148.00M 193 sp.TimeReversalAdapter(settings), + 8.12M 148.00M 194 sp.FieldOfViewCropping(settings) + 195 ] + 196 + 440.30M 510.00M 197 sp.simulate(SIMULATION_PIPELINE, settings, device) + 198 + 8.12M 152.00M 199 if Tags.WAVELENGTH in settings: + 8.12M 152.00M 200 WAVELENGTH = settings[Tags.WAVELENGTH] + 201 else: + 202 WAVELENGTH = 700 + 203 + 8.12M 152.00M 204 if visualise: + 205 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + 206 wavelength=WAVELENGTH, + 207 show_time_series_data=True, + 208 show_initial_pressure=True, + 209 show_reconstructed_data=True, + 210 log_scale=False, + 211 show_xz_only=False) +## run_msot_invision_simulation + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 148.00M 13 @profile + 14 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise:bool = True): + 15 """ + 16 + 17 :param SPACING: The simulation spacing between voxels + 18 :param path_manager: the path manager to be used, typically sp.PathManager + 19 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 20 :return: a run through of the example + 21 """ + 8.12M 148.00M 22 SPEED_OF_SOUND = 1500 + 8.12M 148.00M 23 XZ_DIM = 90 + 8.12M 148.00M 24 Y_DIM = 40 + 25 + 8.12M 148.00M 26 def create_pipeline(_settings: sp.Settings): + 27 return [ + 28 sp.ModelBasedVolumeCreationAdapter(settings), + 29 sp.MCXAdapter(settings), + 30 sp.KWaveAdapter(settings), + 31 sp.FieldOfViewCropping(settings), + 32 sp.TimeReversalAdapter(settings) + 33 ] + 34 + 35 + 8.12M 148.00M 36 def get_device(): + 37 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) + 38 return pa_device + 39 + 40 + 8.12M 148.00M 41 def create_volume(): + 42 inclusion_material = sp.Molecule(volume_fraction=1.0, + 43 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 44 0.9), + 45 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 46 100.0), + 47 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 48 4.0), + 49 speed_of_sound=SPEED_OF_SOUND, + 50 alpha_coefficient=1e-4, + 51 density=1000, + 52 gruneisen_parameter=1.0, + 53 name="Inclusion") + 54 + 55 phantom_material = sp.Molecule(volume_fraction=1.0, + 56 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), + 57 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 58 100.0), + 59 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), + 60 speed_of_sound=SPEED_OF_SOUND, + 61 alpha_coefficient=1e-4, + 62 density=1000, + 63 gruneisen_parameter=1.0, + 64 name="Phantom") + 65 + 66 heavy_water = sp.Molecule(volume_fraction=1.0, + 67 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), + 68 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), + 69 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), + 70 speed_of_sound=SPEED_OF_SOUND, + 71 alpha_coefficient=1e-4, + 72 density=1000, + 73 gruneisen_parameter=1.0, + 74 name="background_water") + 75 + 76 background_dictionary = sp.Settings() + 77 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 78 .append(heavy_water) + 79 .get_molecular_composition(segmentation_type=-1)) + 80 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 81 + 82 phantom_material_dictionary = sp.Settings() + 83 phantom_material_dictionary[Tags.PRIORITY] = 3 + 84 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] + 85 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 + 86 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 + 87 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 + 88 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 89 .append(phantom_material) + 90 .get_molecular_composition(segmentation_type=0)) + 91 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 92 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 93 + 94 inclusion_1_dictionary = sp.Settings() + 95 inclusion_1_dictionary[Tags.PRIORITY] = 8 + 96 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] + 97 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 + 98 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 + 99 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 + 100 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 101 .append(inclusion_material) + 102 .get_molecular_composition(segmentation_type=1)) + 103 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 104 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 105 + 106 inclusion_2_dictionary = sp.Settings() + 107 inclusion_2_dictionary[Tags.PRIORITY] = 5 + 108 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] + 109 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] + 110 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 111 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 112 .append(inclusion_material) + 113 .get_molecular_composition(segmentation_type=2)) + 114 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 115 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 116 + 117 tissue_dict = sp.Settings() + 118 tissue_dict[Tags.BACKGROUND] = background_dictionary + 119 tissue_dict["phantom"] = phantom_material_dictionary + 120 tissue_dict["inclusion_1"] = inclusion_1_dictionary + 121 tissue_dict["inclusion_2"] = inclusion_2_dictionary + 122 return { + 123 Tags.STRUCTURES: tissue_dict, + 124 Tags.SIMULATE_DEFORMED_LAYERS: False + 125 } + 126 + 127 + 8.12M 148.00M 128 def get_settings(): + 129 general_settings = { + 130 # These parameters set the general properties of the simulated volume + 131 Tags.RANDOM_SEED: 4711, + 132 Tags.VOLUME_NAME: "InVision Simulation Example", + 133 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 134 Tags.SPACING_MM: SPACING, + 135 Tags.DIM_VOLUME_Z_MM: XZ_DIM, + 136 Tags.DIM_VOLUME_X_MM: XZ_DIM, + 137 Tags.DIM_VOLUME_Y_MM: Y_DIM, + 138 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 139 Tags.GPU: True, + 140 Tags.WAVELENGTHS: [700] + 141 } + 142 + 143 volume_settings = create_volume() + 144 + 145 optical_settings = { + 146 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 147 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 148 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, + 149 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 150 } + 151 + 152 acoustic_settings = { + 153 Tags.ACOUSTIC_SIMULATION_3D: True, + 154 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 155 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 156 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 157 Tags.KWAVE_PROPERTY_PMLInside: False, + 158 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 159 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 160 Tags.KWAVE_PROPERTY_PlotPML: False, + 161 Tags.RECORDMOVIE: False, + 162 Tags.MOVIENAME: "visualization_log", + 163 Tags.ACOUSTIC_LOG_SCALE: True + 164 } + 165 + 166 reconstruction_settings = { + 167 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 168 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 169 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 170 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 171 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, + 172 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 173 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, + 174 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 175 Tags.KWAVE_PROPERTY_PMLInside: False, + 176 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 177 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 178 Tags.KWAVE_PROPERTY_PlotPML: False, + 179 Tags.RECORDMOVIE: False, + 180 Tags.MOVIENAME: "visualization_log", + 181 Tags.ACOUSTIC_LOG_SCALE: True, + 182 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 183 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 184 Tags.SPACING_MM: 0.25, + 185 } + 186 + 187 _settings = sp.Settings(general_settings) + 188 _settings.set_volume_creation_settings(volume_settings) + 189 _settings.set_optical_settings(optical_settings) + 190 _settings.set_acoustic_settings(acoustic_settings) + 191 _settings.set_reconstruction_settings(reconstruction_settings) + 192 return _settings + 193 + 194 + 8.12M 148.00M 195 device = get_device() + 8.12M 148.00M 196 settings = get_settings() + 8.12M 148.00M 197 pipeline = create_pipeline(settings) + 198 + 3.32G 4.25G 199 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) + 200 + 8.12M 148.00M 201 if visualise: + 202 sp.visualise_data(settings=settings, + 203 path_manager=path_manager, + 204 show_absorption=True, + 205 show_initial_pressure=True, + 206 show_reconstructed_data=True, + 207 show_xz_only=True) +## run_minimal_optical_simulation_uniform_cube + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 202.00M 22 @profile + 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), + 24 visualise:bool = True): + 25 """ + 26 + 27 :param SPACING: The simulation spacing between voxels + 28 :param path_manager: the path manager to be used, typically sp.PathManager + 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 30 :return: a run through of the example + 31 """ + 8.12M 202.00M 32 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 8.12M 202.00M 33 VOLUME_PLANAR_DIM_IN_MM = 30 + 8.12M 202.00M 34 VOLUME_HEIGHT_IN_MM = 60 + 8.12M 202.00M 35 RANDOM_SEED = 471 + 8.12M 202.00M 36 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 8.12M 202.00M 37 SAVE_REFLECTANCE = True + 8.12M 202.00M 38 SAVE_PHOTON_DIRECTION = False + 39 + 40 # If VISUALIZE is set to True, the simulation result will be plotted + 8.12M 202.00M 41 VISUALIZE = True + 42 + 43 + 8.12M 202.00M 44 def create_example_tissue(): + 45 """ + 46 This is a very simple example script of how to create a tissue definition. + 47 It contains only a generic background tissue material. + 48 """ + 49 background_dictionary = sp.Settings() + 50 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 51 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 52 + 53 tissue_dict = sp.Settings() + 54 tissue_dict[Tags.BACKGROUND] = background_dictionary + 55 return tissue_dict + 56 + 57 + 58 # Seed the numpy random configuration prior to creating the global_settings file in + 59 # order to ensure that the same volume + 60 # is generated with the same random seed every time. + 61 + 8.12M 202.00M 62 np.random.seed(RANDOM_SEED) + 63 + 8.12M 202.00M 64 general_settings = { + 65 # These parameters set the general properties of the simulated volume + 8.12M 202.00M 66 Tags.RANDOM_SEED: RANDOM_SEED, + 8.12M 202.00M 67 Tags.VOLUME_NAME: VOLUME_NAME, + 8.12M 202.00M 68 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 8.12M 202.00M 69 Tags.SPACING_MM: SPACING, + 8.12M 202.00M 70 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 8.12M 202.00M 71 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 8.12M 202.00M 72 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 8.12M 202.00M 73 Tags.WAVELENGTHS: [500], + 8.12M 202.00M 74 Tags.DO_FILE_COMPRESSION: True + 75 } + 76 + 8.12M 202.00M 77 settings = sp.Settings(general_settings) + 78 + 8.12M 202.00M 79 settings.set_volume_creation_settings({ + 8.12M 202.00M 80 Tags.STRUCTURES: create_example_tissue() + 81 }) + 8.12M 202.00M 82 settings.set_optical_settings({ + 8.12M 202.00M 83 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 8.12M 202.00M 84 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 8.12M 202.00M 85 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 8.12M 202.00M 86 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 87 }) + 88 + 8.12M 202.00M 89 pipeline = [ + 8.12M 202.00M 90 sp.ModelBasedVolumeCreationAdapter(settings), + 8.12M 202.00M 91 sp.MCXAdapterReflectance(settings), + 92 ] + 93 + 8.12M 202.00M 94 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 8.12M 202.00M 95 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 96 + 1.13G 1.19G 97 sp.simulate(pipeline, settings, device) + 98 + 8.12M 148.00M 99 if visualise: + 100 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 101 wavelength=settings[Tags.WAVELENGTH], + 102 show_initial_pressure=True, + 103 show_absorption=True, + 104 show_diffuse_reflectance=SAVE_REFLECTANCE, + 105 log_scale=True) +## run_minimal_optical_simulation + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 148.00M 19 @profile + 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 8.12M 148.00M 28 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 8.12M 148.00M 29 VOLUME_PLANAR_DIM_IN_MM = 30 + 8.12M 148.00M 30 VOLUME_HEIGHT_IN_MM = 60 + 8.12M 148.00M 31 RANDOM_SEED = 471 + 8.12M 148.00M 32 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 8.12M 148.00M 33 SAVE_REFLECTANCE = False + 8.12M 148.00M 34 SAVE_PHOTON_DIRECTION = False + 35 + 36 # If VISUALIZE is set to True, the simulation result will be plotted + 37 + 8.12M 148.00M 38 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and a blood vessel. + 43 """ + 44 background_dictionary = sp.Settings() + 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 muscle_dictionary = sp.Settings() + 49 muscle_dictionary[Tags.PRIORITY] = 1 + 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] + 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 vessel_1_dictionary = sp.Settings() + 58 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 10, + 61 VOLUME_HEIGHT_IN_MM/2] + 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 12, + 64 VOLUME_HEIGHT_IN_MM/2] + 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() + 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 epidermis_dictionary = sp.Settings() + 71 epidermis_dictionary[Tags.PRIORITY] = 8 + 72 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] + 73 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] + 74 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 75 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 76 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 77 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 78 + 79 tissue_dict = sp.Settings() + 80 tissue_dict[Tags.BACKGROUND] = background_dictionary + 81 tissue_dict["muscle"] = muscle_dictionary + 82 tissue_dict["epidermis"] = epidermis_dictionary + 83 tissue_dict["vessel_1"] = vessel_1_dictionary + 84 return tissue_dict + 85 + 86 + 87 # Seed the numpy random configuration prior to creating the global_settings file in + 88 # order to ensure that the same volume + 89 # is generated with the same random seed every time. + 90 + 8.12M 148.00M 91 np.random.seed(RANDOM_SEED) + 92 + 8.12M 148.00M 93 general_settings = { + 94 # These parameters set the general properties of the simulated volume + 8.12M 148.00M 95 Tags.RANDOM_SEED: RANDOM_SEED, + 8.12M 148.00M 96 Tags.VOLUME_NAME: VOLUME_NAME, + 8.12M 148.00M 97 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 8.12M 148.00M 98 Tags.SPACING_MM: SPACING, + 8.12M 148.00M 99 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 8.12M 148.00M 100 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 8.12M 148.00M 101 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 8.12M 148.00M 102 Tags.WAVELENGTHS: [798], + 8.12M 148.00M 103 Tags.DO_FILE_COMPRESSION: True, + 8.12M 148.00M 104 Tags.GPU: True + 105 } + 106 + 8.12M 148.00M 107 settings = sp.Settings(general_settings) + 108 + 8.12M 148.00M 109 settings.set_volume_creation_settings({ + 8.12M 148.00M 110 Tags.SIMULATE_DEFORMED_LAYERS: True, + 8.12M 148.00M 111 Tags.STRUCTURES: create_example_tissue() + 112 }) + 8.12M 148.00M 113 settings.set_optical_settings({ + 8.12M 148.00M 114 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 8.12M 148.00M 115 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 8.12M 148.00M 116 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 8.12M 148.00M 117 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 118 }) + 8.12M 148.00M 119 settings["noise_model_1"] = { + 8.12M 148.00M 120 Tags.NOISE_MEAN: 1.0, + 8.12M 148.00M 121 Tags.NOISE_STD: 0.1, + 8.12M 148.00M 122 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 8.12M 148.00M 123 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 8.12M 148.00M 124 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 125 } + 126 + 8.12M 148.00M 127 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: + 8.12M 148.00M 128 pipeline = [ + 8.12M 148.00M 129 sp.ModelBasedVolumeCreationAdapter(settings), + 8.12M 148.00M 130 sp.MCXAdapter(settings), + 8.12M 148.00M 131 sp.GaussianNoise(settings, "noise_model_1") + 132 ] + 133 else: + 134 pipeline = [ + 135 sp.ModelBasedVolumeCreationAdapter(settings), + 136 sp.MCXAdapterReflectance(settings), + 137 ] + 138 + 139 + 8.12M 148.00M 140 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): + 141 """ + 142 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. + 143 + 144 """ + 145 + 146 def __init__(self): + 147 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 148 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 149 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) + 150 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], + 151 direction_vector_mm=[0, 0, 1])) + 152 + 153 + 8.12M 148.00M 154 device = ExampleDeviceSlitIlluminationLinearDetector() + 155 + 1.17G 1.60G 156 sp.simulate(pipeline, settings, device) + 157 + 8.12M 202.00M 158 if Tags.WAVELENGTH in settings: + 8.12M 202.00M 159 WAVELENGTH = settings[Tags.WAVELENGTH] + 160 else: + 161 WAVELENGTH = 700 + 162 + 8.12M 202.00M 163 if visualise: + 164 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 165 wavelength=WAVELENGTH, + 166 show_initial_pressure=True, + 167 show_absorption=True, + 168 show_diffuse_reflectance=SAVE_REFLECTANCE, + 169 log_scale=True) +## run_linear_unmixing + +active_bytes reserved_bytes line code + all all + peak peak + 0.00B 0.00B 18 @profile + 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): + 20 """ + 21 + 22 :param SPACING: The simulation spacing between voxels + 23 :param path_manager: the path manager to be used, typically sp.PathManager + 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 25 :return: a run through of the example + 26 """ + 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you + 28 # set global params characterizing the simulated volume + 0.00B 0.00B 29 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 0.00B 0.00B 30 VOLUME_PLANAR_DIM_IN_MM = 20 + 0.00B 0.00B 31 VOLUME_HEIGHT_IN_MM = 25 + 0.00B 0.00B 32 RANDOM_SEED = 471 + 0.00B 0.00B 33 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) + 34 + 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths + 0.00B 0.00B 36 WAVELENGTHS = [750, 800, 850] + 37 + 0.00B 0.00B 38 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and two blood vessels. + 43 """ + 44 background_dictionary = sp.Settings() + 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 muscle_dictionary = sp.Settings() + 49 muscle_dictionary[Tags.PRIORITY] = 1 + 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 vessel_1_dictionary = sp.Settings() + 58 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 10, + 61 5] + 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 12, + 64 5] + 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) + 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 vessel_2_dictionary = sp.Settings() + 71 vessel_2_dictionary[Tags.PRIORITY] = 3 + 72 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 73 10, + 74 5] + 75 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 76 12, + 77 5] + 78 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 79 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) + 80 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 81 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 82 + 83 epidermis_dictionary = sp.Settings() + 84 epidermis_dictionary[Tags.PRIORITY] = 8 + 85 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 86 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] + 87 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 88 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 89 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 90 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 91 + 92 tissue_dict = sp.Settings() + 93 tissue_dict[Tags.BACKGROUND] = background_dictionary + 94 tissue_dict["muscle"] = muscle_dictionary + 95 tissue_dict["epidermis"] = epidermis_dictionary + 96 tissue_dict["vessel_1"] = vessel_1_dictionary + 97 tissue_dict["vessel_2"] = vessel_2_dictionary + 98 return tissue_dict + 99 + 100 + 101 # Seed the numpy random configuration prior to creating the global_settings file in + 102 # order to ensure that the same volume is generated with the same random seed every time. + 0.00B 0.00B 103 np.random.seed(RANDOM_SEED) + 104 + 105 # Initialize global settings and prepare for simulation pipeline including + 106 # volume creation and optical forward simulation. + 0.00B 0.00B 107 general_settings = { + 108 # These parameters set the general properties of the simulated volume + 0.00B 0.00B 109 Tags.RANDOM_SEED: RANDOM_SEED, + 0.00B 0.00B 110 Tags.VOLUME_NAME: VOLUME_NAME, + 0.00B 0.00B 111 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 0.00B 0.00B 112 Tags.SPACING_MM: SPACING, + 0.00B 0.00B 113 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 0.00B 0.00B 114 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 0.00B 0.00B 115 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 0.00B 0.00B 116 Tags.WAVELENGTHS: WAVELENGTHS, + 0.00B 0.00B 117 Tags.GPU: True, + 0.00B 0.00B 118 Tags.DO_FILE_COMPRESSION: True + 119 } + 0.00B 0.00B 120 settings = sp.Settings(general_settings) + 0.00B 0.00B 121 settings.set_volume_creation_settings({ + 0.00B 0.00B 122 Tags.SIMULATE_DEFORMED_LAYERS: True, + 0.00B 0.00B 123 Tags.STRUCTURES: create_example_tissue() + 124 }) + 0.00B 0.00B 125 settings.set_optical_settings({ + 0.00B 0.00B 126 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 0.00B 0.00B 127 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 0.00B 0.00B 128 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 0.00B 0.00B 129 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 130 }) + 131 + 132 # Set component settings for linear unmixing. + 133 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the + 134 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. + 135 # Please take a look at the component for more information. + 0.00B 0.00B 136 settings["linear_unmixing"] = { + 0.00B 0.00B 137 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 0.00B 0.00B 138 Tags.WAVELENGTHS: WAVELENGTHS, + 0.00B 0.00B 139 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( + 0.00B 0.00B 140 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] + 141 ), + 0.00B 0.00B 142 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, + 0.00B 0.00B 143 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True + 144 } + 145 + 146 # Get device for simulation + 0.00B 0.00B 147 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 0.00B 0.00B 148 VOLUME_PLANAR_DIM_IN_MM/2, + 0.00B 0.00B 149 0])) + 0.00B 0.00B 150 device.update_settings_for_use_of_model_based_volume_creator(settings) + 151 + 152 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS + 0.00B 0.00B 153 pipeline = [ + 0.00B 0.00B 154 sp.ModelBasedVolumeCreationAdapter(settings), + 0.00B 0.00B 155 sp.MCXAdapter(settings), + 0.00B 0.00B 156 sp.FieldOfViewCropping(settings), + 157 ] + 1.03G 1.45G 158 sp.simulate(pipeline, settings, device) + 159 + 160 # Run linear unmixing component with above specified settings. + 8.12M 148.00M 161 sp.LinearUnmixing(settings, "linear_unmixing").run() + 162 + 163 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. + 8.12M 148.00M 164 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 8.12M 148.00M 165 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) + 8.12M 148.00M 166 sO2 = lu_results["sO2"] + 167 + 8.12M 148.00M 168 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) + 8.12M 148.00M 169 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) + 8.12M 148.00M 170 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) + 171 + 172 # Visualize linear unmixing result + 8.12M 148.00M 173 if visualise: + 174 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 175 wavelength=WAVELENGTHS[0], + 176 show_initial_pressure=True, + 177 show_oxygenation=True, + 178 show_linear_unmixing_sO2=True) diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.4.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.4.txt new file mode 100644 index 00000000..f71e0070 --- /dev/null +++ b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.4.txt @@ -0,0 +1,1034 @@ +## run_perform_iterative_qPAI_reconstruction + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 24.00M 25 @profile + 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise:bool = True): + 27 """ + 28 + 29 :param SPACING: The simulation spacing between voxels + 30 :param path_manager: the path manager to be used, typically sp.PathManager + 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 32 :return: a run through of the example + 33 """ + 8.12M 24.00M 34 VOLUME_TRANSDUCER_DIM_IN_MM = 30 + 8.12M 24.00M 35 VOLUME_PLANAR_DIM_IN_MM = 30 + 8.12M 24.00M 36 VOLUME_HEIGHT_IN_MM = 30 + 8.12M 24.00M 37 RANDOM_SEED = 471 + 8.12M 24.00M 38 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) + 39 + 40 # If VISUALIZE is set to True, the reconstruction result will be plotted + 41 + 8.12M 24.00M 42 def create_example_tissue(): + 43 """ + 44 This is a very simple example script of how to create a tissue definition. + 45 It contains a muscular background, an epidermis layer on top of the muscles + 46 and a blood vessel. + 47 """ + 48 background_dictionary = sp.Settings() + 49 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) + 50 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 51 + 52 epidermis_structure = sp.Settings() + 53 epidermis_structure[Tags.PRIORITY] = 1 + 54 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] + 55 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] + 56 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) + 57 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True + 58 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True + 59 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 60 + 61 vessel_structure_1 = sp.Settings() + 62 vessel_structure_1[Tags.PRIORITY] = 2 + 63 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, + 64 VOLUME_HEIGHT_IN_MM / 2] + 65 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, + 66 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] + 67 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 + 68 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 + 69 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) + 70 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True + 71 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True + 72 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE + 73 + 74 vessel_structure_2 = sp.Settings() + 75 vessel_structure_2[Tags.PRIORITY] = 3 + 76 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, + 77 VOLUME_HEIGHT_IN_MM / 3] + 78 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, + 79 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] + 80 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 + 81 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) + 82 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True + 83 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 84 + 85 tissue_dict = sp.Settings() + 86 tissue_dict[Tags.BACKGROUND] = background_dictionary + 87 tissue_dict["epidermis"] = epidermis_structure + 88 tissue_dict["vessel_1"] = vessel_structure_1 + 89 tissue_dict["vessel_2"] = vessel_structure_2 + 90 return tissue_dict + 91 + 92 + 93 # set settings for volume creation, optical simulation and iterative qPAI method + 8.12M 24.00M 94 np.random.seed(RANDOM_SEED) + 95 + 8.12M 24.00M 96 general_settings = { + 97 # These parameters set the general properties of the simulated volume + 8.12M 24.00M 98 Tags.RANDOM_SEED: RANDOM_SEED, + 8.12M 24.00M 99 Tags.VOLUME_NAME: VOLUME_NAME, + 8.12M 24.00M 100 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 8.12M 24.00M 101 Tags.SPACING_MM: SPACING, + 8.12M 24.00M 102 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 8.12M 24.00M 103 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 8.12M 24.00M 104 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 8.12M 24.00M 105 Tags.WAVELENGTHS: [700] + 106 } + 107 + 8.12M 24.00M 108 settings = sp.Settings(general_settings) + 109 + 8.12M 24.00M 110 settings.set_volume_creation_settings({ + 111 # These parameters set the properties for the volume creation + 8.12M 24.00M 112 Tags.SIMULATE_DEFORMED_LAYERS: True, + 8.12M 24.00M 113 Tags.STRUCTURES: create_example_tissue() + 114 }) + 8.12M 24.00M 115 settings.set_optical_settings({ + 116 # These parameters set the properties for the optical Monte Carlo simulation + 8.12M 24.00M 117 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 8.12M 24.00M 118 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 8.12M 24.00M 119 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 8.12M 24.00M 120 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 121 }) + 8.12M 24.00M 122 settings["noise_model"] = { + 8.12M 24.00M 123 Tags.NOISE_MEAN: 1.0, + 8.12M 24.00M 124 Tags.NOISE_STD: 0.01, + 8.12M 24.00M 125 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 8.12M 24.00M 126 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 8.12M 24.00M 127 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 128 } + 8.12M 24.00M 129 settings["iterative_qpai_reconstruction"] = { + 130 # These parameters set the properties of the iterative reconstruction + 8.12M 24.00M 131 Tags.DOWNSCALE_FACTOR: 0.75, + 8.12M 24.00M 132 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, + 133 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant + 8.12M 24.00M 134 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, + 8.12M 24.00M 135 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, + 136 # for this example, we are not interested in all absorption updates + 8.12M 24.00M 137 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, + 8.12M 24.00M 138 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 + 139 } + 140 + 141 # run pipeline including iterative qPAI method + 8.12M 24.00M 142 pipeline = [ + 8.12M 24.00M 143 sp.ModelBasedVolumeCreationAdapter(settings), + 8.12M 24.00M 144 sp.MCXAdapter(settings), + 8.12M 24.00M 145 sp.GaussianNoise(settings, "noise_model"), + 8.12M 24.00M 146 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") + 147 ] + 148 + 149 + 8.12M 24.00M 150 class CustomDevice(sp.PhotoacousticDevice): + 151 + 152 def __init__(self): + 153 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, + 154 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, + 155 0])) + 156 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) + 157 + 158 + 8.12M 24.00M 159 device = CustomDevice() + 160 + 8.12M 24.00M 161 device.update_settings_for_use_of_model_based_volume_creator(settings) + 162 + 52.01M 66.00M 163 sp.simulate(pipeline, settings, device) + 164 + 165 # visualize reconstruction results + 8.12M 22.00M 166 if visualise: + 167 # get simulation output + 168 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 169 settings = sp.load_data_field(data_path, Tags.SETTINGS) + 170 wavelength = settings[Tags.WAVELENGTHS][0] + 171 + 172 # get reconstruction result + 173 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) + 174 + 175 # get ground truth absorption coefficients + 176 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) + 177 + 178 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) + 179 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR + 180 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") + 181 + 182 # compute reconstruction error + 183 difference = absorption_gt - absorption_reconstruction + 184 + 185 median_error = np.median(difference) + 186 q3, q1 = np.percentile(difference, [75, 25]) + 187 iqr = q3 - q1 + 188 + 189 # visualize results + 190 x_pos = int(np.shape(absorption_gt)[0] / 2) + 191 y_pos = int(np.shape(absorption_gt)[1] / 2) + 192 + 193 if np.min(absorption_gt) > np.min(absorption_reconstruction): + 194 cmin = np.min(absorption_reconstruction) + 195 else: + 196 cmin = np.min(absorption_gt) + 197 + 198 if np.max(absorption_gt) > np.max(absorption_reconstruction): + 199 cmax = np.max(absorption_gt) + 200 else: + 201 cmax = np.max(absorption_reconstruction) + 202 + 203 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] + 204 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] + 205 + 206 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", + 207 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] + 208 + 209 plt.figure(figsize=(20, 15)) + 210 plt.subplots_adjust(hspace=0.5) + 211 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + + 212 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) + 213 + 214 for i, quantity in enumerate(results_x_z): + 215 plt.subplot(2, len(results_x_z), i + 1) + 216 if i == 0: + 217 plt.ylabel("x-z", fontsize=10) + 218 plt.title(label[i], fontsize=10) + 219 plt.imshow(quantity.T) + 220 plt.xticks(fontsize=6) + 221 plt.yticks(fontsize=6) + 222 plt.colorbar() + 223 if i != 2: + 224 plt.clim(cmin, cmax) + 225 else: + 226 plt.clim(np.min(difference), np.max(difference)) + 227 + 228 for i, quantity in enumerate(results_y_z): + 229 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) + 230 if i == 0: + 231 plt.ylabel("y-z", fontsize=10) + 232 plt.title(label[i], fontsize=10) + 233 plt.imshow(quantity.T) + 234 plt.xticks(fontsize=6) + 235 plt.yticks(fontsize=6) + 236 plt.colorbar() + 237 if i != 2: + 238 plt.clim(cmin, cmax) + 239 else: + 240 plt.clim(np.min(difference), np.max(difference)) + 241 + 242 plt.show() + 243 plt.close() +## run_optical_and_acoustic_simulation + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 20.00M 18 @profile + 19 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), + 20 visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 8.12M 20.00M 28 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 8.12M 20.00M 29 VOLUME_PLANAR_DIM_IN_MM = 20 + 8.12M 20.00M 30 VOLUME_HEIGHT_IN_MM = 25 + 8.12M 20.00M 31 RANDOM_SEED = 4711 + 32 + 33 # If VISUALIZE is set to True, the simulation result will be plotted + 8.12M 20.00M 34 VISUALIZE = True + 35 + 8.12M 20.00M 36 def create_example_tissue(): + 37 """ + 38 This is a very simple example script of how to create a tissue definition. + 39 It contains a muscular background, an epidermis layer on top of the muscles + 40 and a blood vessel. + 41 """ + 42 background_dictionary = sp.Settings() + 43 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) + 44 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 45 + 46 tissue_dict = sp.Settings() + 47 tissue_dict[Tags.BACKGROUND] = background_dictionary + 48 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, + 49 molecular_composition=sp.TISSUE_LIBRARY.constant( + 50 0.05, 100, 0.9), + 51 priority=1, + 52 consider_partial_volume=True, + 53 adhere_to_deformation=True) + 54 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, + 55 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), + 56 priority=8, + 57 consider_partial_volume=True, + 58 adhere_to_deformation=True) + 59 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( + 60 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], + 61 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], + 62 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 63 radius_mm=2, priority=3, consider_partial_volume=True, + 64 adhere_to_deformation=False + 65 ) + 66 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( + 67 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], + 68 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], + 69 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 70 radius_mm=3, priority=3, consider_partial_volume=True, + 71 adhere_to_deformation=False + 72 ) + 73 return tissue_dict + 74 + 75 + 76 # Seed the numpy random configuration prior to creating the global_settings file in + 77 # order to ensure that the same volume + 78 # is generated with the same random seed every time. + 79 + 8.12M 20.00M 80 np.random.seed(RANDOM_SEED) + 8.12M 20.00M 81 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) + 82 + 8.12M 20.00M 83 general_settings = { + 84 # These parameters set the general properties of the simulated volume + 8.12M 20.00M 85 Tags.RANDOM_SEED: RANDOM_SEED, + 8.12M 20.00M 86 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), + 8.12M 20.00M 87 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 8.12M 20.00M 88 Tags.SPACING_MM: SPACING, + 8.12M 20.00M 89 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 8.12M 20.00M 90 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 8.12M 20.00M 91 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 8.12M 20.00M 92 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 8.12M 20.00M 93 Tags.GPU: True, + 8.12M 20.00M 94 Tags.WAVELENGTHS: [700, 800], + 8.12M 20.00M 95 Tags.DO_FILE_COMPRESSION: True, + 8.12M 20.00M 96 Tags.DO_IPASC_EXPORT: True + 97 } + 8.12M 20.00M 98 settings = sp.Settings(general_settings) + 8.12M 20.00M 99 np.random.seed(RANDOM_SEED) + 100 + 8.12M 20.00M 101 settings.set_volume_creation_settings({ + 8.12M 20.00M 102 Tags.STRUCTURES: create_example_tissue(), + 8.12M 20.00M 103 Tags.SIMULATE_DEFORMED_LAYERS: True + 104 }) + 105 + 8.12M 20.00M 106 settings.set_optical_settings({ + 8.12M 20.00M 107 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 8.12M 20.00M 108 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 8.12M 20.00M 109 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, + 8.12M 20.00M 110 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 8.12M 20.00M 111 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, + 112 }) + 113 + 8.12M 20.00M 114 settings.set_acoustic_settings({ + 8.12M 20.00M 115 Tags.ACOUSTIC_SIMULATION_3D: False, + 8.12M 20.00M 116 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 8.12M 20.00M 117 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 8.12M 20.00M 118 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 8.12M 20.00M 119 Tags.KWAVE_PROPERTY_PMLInside: False, + 8.12M 20.00M 120 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 8.12M 20.00M 121 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 8.12M 20.00M 122 Tags.KWAVE_PROPERTY_PlotPML: False, + 8.12M 20.00M 123 Tags.RECORDMOVIE: False, + 8.12M 20.00M 124 Tags.MOVIENAME: "visualization_log", + 8.12M 20.00M 125 Tags.ACOUSTIC_LOG_SCALE: True + 126 }) + 127 + 8.12M 20.00M 128 settings.set_reconstruction_settings({ + 8.12M 20.00M 129 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 8.12M 20.00M 130 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 8.12M 20.00M 131 Tags.ACOUSTIC_SIMULATION_3D: False, + 8.12M 20.00M 132 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 8.12M 20.00M 133 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 8.12M 20.00M 134 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), + 8.12M 20.00M 135 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), + 8.12M 20.00M 136 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 8.12M 20.00M 137 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 8.12M 20.00M 138 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, + 8.12M 20.00M 139 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 8.12M 20.00M 140 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 8.12M 20.00M 141 Tags.KWAVE_PROPERTY_PMLInside: False, + 8.12M 20.00M 142 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 8.12M 20.00M 143 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 8.12M 20.00M 144 Tags.KWAVE_PROPERTY_PlotPML: False, + 8.12M 20.00M 145 Tags.RECORDMOVIE: False, + 8.12M 20.00M 146 Tags.MOVIENAME: "visualization_log", + 8.12M 20.00M 147 Tags.ACOUSTIC_LOG_SCALE: True, + 8.12M 20.00M 148 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, + 8.12M 20.00M 149 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, + 8.12M 20.00M 150 Tags.DATA_FIELD_DENSITY: 1000, + 8.12M 20.00M 151 Tags.SPACING_MM: SPACING + 152 }) + 153 + 8.12M 20.00M 154 settings["noise_initial_pressure"] = { + 8.12M 20.00M 155 Tags.NOISE_MEAN: 1, + 8.12M 20.00M 156 Tags.NOISE_STD: 0.01, + 8.12M 20.00M 157 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 8.12M 20.00M 158 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 8.12M 20.00M 159 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 160 } + 161 + 8.12M 20.00M 162 settings["noise_time_series"] = { + 8.12M 20.00M 163 Tags.NOISE_STD: 1, + 8.12M 20.00M 164 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, + 8.12M 20.00M 165 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA + 166 } + 167 + 168 # TODO: For the device choice, uncomment the undesired device + 169 + 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 171 # VOLUME_PLANAR_DIM_IN_MM/2, + 172 # 0])) + 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) + 174 + 8.12M 20.00M 175 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 8.12M 20.00M 176 VOLUME_PLANAR_DIM_IN_MM/2, + 8.12M 20.00M 177 0]), + 8.12M 20.00M 178 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) + 8.12M 20.00M 179 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, + 8.12M 20.00M 180 pitch_mm=0.25, + 8.12M 20.00M 181 number_detector_elements=100, + 8.12M 20.00M 182 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) + 8.12M 20.00M 183 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) + 8.12M 20.00M 184 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) + 185 + 186 + 8.12M 20.00M 187 SIMULATION_PIPELINE = [ + 8.12M 20.00M 188 sp.ModelBasedVolumeCreationAdapter(settings), + 8.12M 20.00M 189 sp.MCXAdapter(settings), + 8.12M 20.00M 190 sp.GaussianNoise(settings, "noise_initial_pressure"), + 8.12M 20.00M 191 sp.KWaveAdapter(settings), + 8.12M 20.00M 192 sp.GaussianNoise(settings, "noise_time_series"), + 8.12M 20.00M 193 sp.TimeReversalAdapter(settings), + 8.12M 20.00M 194 sp.FieldOfViewCropping(settings) + 195 ] + 196 + 62.10M 80.00M 197 sp.simulate(SIMULATION_PIPELINE, settings, device) + 198 + 199 if Tags.WAVELENGTH in settings: + 200 WAVELENGTH = settings[Tags.WAVELENGTH] + 201 else: + 202 WAVELENGTH = 700 + 203 + 204 if visualise: + 205 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + 206 wavelength=WAVELENGTH, + 207 show_time_series_data=True, + 208 show_initial_pressure=True, + 209 show_reconstructed_data=True, + 210 log_scale=False, + 211 show_xz_only=False) +## run_msot_invision_simulation + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 20.00M 13 @profile + 14 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise:bool = True): + 15 """ + 16 + 17 :param SPACING: The simulation spacing between voxels + 18 :param path_manager: the path manager to be used, typically sp.PathManager + 19 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 20 :return: a run through of the example + 21 """ + 8.12M 20.00M 22 SPEED_OF_SOUND = 1500 + 8.12M 20.00M 23 XZ_DIM = 90 + 8.12M 20.00M 24 Y_DIM = 40 + 25 + 8.12M 20.00M 26 def create_pipeline(_settings: sp.Settings): + 27 return [ + 28 sp.ModelBasedVolumeCreationAdapter(settings), + 29 sp.MCXAdapter(settings), + 30 sp.KWaveAdapter(settings), + 31 sp.FieldOfViewCropping(settings), + 32 sp.TimeReversalAdapter(settings) + 33 ] + 34 + 35 + 8.12M 20.00M 36 def get_device(): + 37 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) + 38 return pa_device + 39 + 40 + 8.12M 20.00M 41 def create_volume(): + 42 inclusion_material = sp.Molecule(volume_fraction=1.0, + 43 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 44 0.9), + 45 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 46 100.0), + 47 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 48 4.0), + 49 speed_of_sound=SPEED_OF_SOUND, + 50 alpha_coefficient=1e-4, + 51 density=1000, + 52 gruneisen_parameter=1.0, + 53 name="Inclusion") + 54 + 55 phantom_material = sp.Molecule(volume_fraction=1.0, + 56 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), + 57 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 58 100.0), + 59 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), + 60 speed_of_sound=SPEED_OF_SOUND, + 61 alpha_coefficient=1e-4, + 62 density=1000, + 63 gruneisen_parameter=1.0, + 64 name="Phantom") + 65 + 66 heavy_water = sp.Molecule(volume_fraction=1.0, + 67 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), + 68 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), + 69 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), + 70 speed_of_sound=SPEED_OF_SOUND, + 71 alpha_coefficient=1e-4, + 72 density=1000, + 73 gruneisen_parameter=1.0, + 74 name="background_water") + 75 + 76 background_dictionary = sp.Settings() + 77 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 78 .append(heavy_water) + 79 .get_molecular_composition(segmentation_type=-1)) + 80 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 81 + 82 phantom_material_dictionary = sp.Settings() + 83 phantom_material_dictionary[Tags.PRIORITY] = 3 + 84 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] + 85 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 + 86 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 + 87 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 + 88 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 89 .append(phantom_material) + 90 .get_molecular_composition(segmentation_type=0)) + 91 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 92 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 93 + 94 inclusion_1_dictionary = sp.Settings() + 95 inclusion_1_dictionary[Tags.PRIORITY] = 8 + 96 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] + 97 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 + 98 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 + 99 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 + 100 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 101 .append(inclusion_material) + 102 .get_molecular_composition(segmentation_type=1)) + 103 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 104 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 105 + 106 inclusion_2_dictionary = sp.Settings() + 107 inclusion_2_dictionary[Tags.PRIORITY] = 5 + 108 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] + 109 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] + 110 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 111 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 112 .append(inclusion_material) + 113 .get_molecular_composition(segmentation_type=2)) + 114 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 115 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 116 + 117 tissue_dict = sp.Settings() + 118 tissue_dict[Tags.BACKGROUND] = background_dictionary + 119 tissue_dict["phantom"] = phantom_material_dictionary + 120 tissue_dict["inclusion_1"] = inclusion_1_dictionary + 121 tissue_dict["inclusion_2"] = inclusion_2_dictionary + 122 return { + 123 Tags.STRUCTURES: tissue_dict, + 124 Tags.SIMULATE_DEFORMED_LAYERS: False + 125 } + 126 + 127 + 8.12M 20.00M 128 def get_settings(): + 129 general_settings = { + 130 # These parameters set the general properties of the simulated volume + 131 Tags.RANDOM_SEED: 4711, + 132 Tags.VOLUME_NAME: "InVision Simulation Example", + 133 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 134 Tags.SPACING_MM: SPACING, + 135 Tags.DIM_VOLUME_Z_MM: XZ_DIM, + 136 Tags.DIM_VOLUME_X_MM: XZ_DIM, + 137 Tags.DIM_VOLUME_Y_MM: Y_DIM, + 138 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 139 Tags.GPU: True, + 140 Tags.WAVELENGTHS: [700] + 141 } + 142 + 143 volume_settings = create_volume() + 144 + 145 optical_settings = { + 146 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 147 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 148 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, + 149 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 150 } + 151 + 152 acoustic_settings = { + 153 Tags.ACOUSTIC_SIMULATION_3D: True, + 154 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 155 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 156 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 157 Tags.KWAVE_PROPERTY_PMLInside: False, + 158 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 159 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 160 Tags.KWAVE_PROPERTY_PlotPML: False, + 161 Tags.RECORDMOVIE: False, + 162 Tags.MOVIENAME: "visualization_log", + 163 Tags.ACOUSTIC_LOG_SCALE: True + 164 } + 165 + 166 reconstruction_settings = { + 167 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 168 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 169 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 170 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 171 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, + 172 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 173 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, + 174 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 175 Tags.KWAVE_PROPERTY_PMLInside: False, + 176 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 177 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 178 Tags.KWAVE_PROPERTY_PlotPML: False, + 179 Tags.RECORDMOVIE: False, + 180 Tags.MOVIENAME: "visualization_log", + 181 Tags.ACOUSTIC_LOG_SCALE: True, + 182 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 183 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 184 Tags.SPACING_MM: 0.25, + 185 } + 186 + 187 _settings = sp.Settings(general_settings) + 188 _settings.set_volume_creation_settings(volume_settings) + 189 _settings.set_optical_settings(optical_settings) + 190 _settings.set_acoustic_settings(acoustic_settings) + 191 _settings.set_reconstruction_settings(reconstruction_settings) + 192 return _settings + 193 + 194 + 8.12M 20.00M 195 device = get_device() + 8.12M 20.00M 196 settings = get_settings() + 8.12M 20.00M 197 pipeline = create_pipeline(settings) + 198 + 442.87M 556.00M 199 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) + 200 + 8.12M 20.00M 201 if visualise: + 202 sp.visualise_data(settings=settings, + 203 path_manager=path_manager, + 204 show_absorption=True, + 205 show_initial_pressure=True, + 206 show_reconstructed_data=True, + 207 show_xz_only=True) +## run_minimal_optical_simulation_uniform_cube + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 42.00M 22 @profile + 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), + 24 visualise:bool = True): + 25 """ + 26 + 27 :param SPACING: The simulation spacing between voxels + 28 :param path_manager: the path manager to be used, typically sp.PathManager + 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 30 :return: a run through of the example + 31 """ + 8.12M 42.00M 32 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 8.12M 42.00M 33 VOLUME_PLANAR_DIM_IN_MM = 30 + 8.12M 42.00M 34 VOLUME_HEIGHT_IN_MM = 60 + 8.12M 42.00M 35 RANDOM_SEED = 471 + 8.12M 42.00M 36 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 8.12M 42.00M 37 SAVE_REFLECTANCE = True + 8.12M 42.00M 38 SAVE_PHOTON_DIRECTION = False + 39 + 40 # If VISUALIZE is set to True, the simulation result will be plotted + 8.12M 42.00M 41 VISUALIZE = True + 42 + 43 + 8.12M 42.00M 44 def create_example_tissue(): + 45 """ + 46 This is a very simple example script of how to create a tissue definition. + 47 It contains only a generic background tissue material. + 48 """ + 49 background_dictionary = sp.Settings() + 50 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 51 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 52 + 53 tissue_dict = sp.Settings() + 54 tissue_dict[Tags.BACKGROUND] = background_dictionary + 55 return tissue_dict + 56 + 57 + 58 # Seed the numpy random configuration prior to creating the global_settings file in + 59 # order to ensure that the same volume + 60 # is generated with the same random seed every time. + 61 + 8.12M 42.00M 62 np.random.seed(RANDOM_SEED) + 63 + 8.12M 42.00M 64 general_settings = { + 65 # These parameters set the general properties of the simulated volume + 8.12M 42.00M 66 Tags.RANDOM_SEED: RANDOM_SEED, + 8.12M 42.00M 67 Tags.VOLUME_NAME: VOLUME_NAME, + 8.12M 42.00M 68 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 8.12M 42.00M 69 Tags.SPACING_MM: SPACING, + 8.12M 42.00M 70 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 8.12M 42.00M 71 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 8.12M 42.00M 72 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 8.12M 42.00M 73 Tags.WAVELENGTHS: [500], + 8.12M 42.00M 74 Tags.DO_FILE_COMPRESSION: True + 75 } + 76 + 8.12M 42.00M 77 settings = sp.Settings(general_settings) + 78 + 8.12M 42.00M 79 settings.set_volume_creation_settings({ + 8.12M 42.00M 80 Tags.STRUCTURES: create_example_tissue() + 81 }) + 8.12M 42.00M 82 settings.set_optical_settings({ + 8.12M 42.00M 83 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 8.12M 42.00M 84 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 8.12M 42.00M 85 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 8.12M 42.00M 86 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 87 }) + 88 + 8.12M 42.00M 89 pipeline = [ + 8.12M 42.00M 90 sp.ModelBasedVolumeCreationAdapter(settings), + 8.12M 42.00M 91 sp.MCXAdapterReflectance(settings), + 92 ] + 93 + 8.12M 42.00M 94 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 8.12M 42.00M 95 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 96 + 155.75M 162.00M 97 sp.simulate(pipeline, settings, device) + 98 + 8.12M 20.00M 99 if visualise: + 100 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 101 wavelength=settings[Tags.WAVELENGTH], + 102 show_initial_pressure=True, + 103 show_absorption=True, + 104 show_diffuse_reflectance=SAVE_REFLECTANCE, + 105 log_scale=True) +## run_minimal_optical_simulation + +active_bytes reserved_bytes line code + all all + peak peak + 8.12M 20.00M 19 @profile + 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 8.12M 20.00M 28 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 8.12M 20.00M 29 VOLUME_PLANAR_DIM_IN_MM = 30 + 8.12M 20.00M 30 VOLUME_HEIGHT_IN_MM = 60 + 8.12M 20.00M 31 RANDOM_SEED = 471 + 8.12M 20.00M 32 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 8.12M 20.00M 33 SAVE_REFLECTANCE = False + 8.12M 20.00M 34 SAVE_PHOTON_DIRECTION = False + 35 + 36 # If VISUALIZE is set to True, the simulation result will be plotted + 37 + 8.12M 20.00M 38 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and a blood vessel. + 43 """ + 44 background_dictionary = sp.Settings() + 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 muscle_dictionary = sp.Settings() + 49 muscle_dictionary[Tags.PRIORITY] = 1 + 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] + 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 vessel_1_dictionary = sp.Settings() + 58 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 10, + 61 VOLUME_HEIGHT_IN_MM/2] + 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 12, + 64 VOLUME_HEIGHT_IN_MM/2] + 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() + 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 epidermis_dictionary = sp.Settings() + 71 epidermis_dictionary[Tags.PRIORITY] = 8 + 72 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] + 73 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] + 74 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 75 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 76 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 77 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 78 + 79 tissue_dict = sp.Settings() + 80 tissue_dict[Tags.BACKGROUND] = background_dictionary + 81 tissue_dict["muscle"] = muscle_dictionary + 82 tissue_dict["epidermis"] = epidermis_dictionary + 83 tissue_dict["vessel_1"] = vessel_1_dictionary + 84 return tissue_dict + 85 + 86 + 87 # Seed the numpy random configuration prior to creating the global_settings file in + 88 # order to ensure that the same volume + 89 # is generated with the same random seed every time. + 90 + 8.12M 20.00M 91 np.random.seed(RANDOM_SEED) + 92 + 8.12M 20.00M 93 general_settings = { + 94 # These parameters set the general properties of the simulated volume + 8.12M 20.00M 95 Tags.RANDOM_SEED: RANDOM_SEED, + 8.12M 20.00M 96 Tags.VOLUME_NAME: VOLUME_NAME, + 8.12M 20.00M 97 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 8.12M 20.00M 98 Tags.SPACING_MM: SPACING, + 8.12M 20.00M 99 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 8.12M 20.00M 100 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 8.12M 20.00M 101 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 8.12M 20.00M 102 Tags.WAVELENGTHS: [798], + 8.12M 20.00M 103 Tags.DO_FILE_COMPRESSION: True, + 8.12M 20.00M 104 Tags.GPU: True + 105 } + 106 + 8.12M 20.00M 107 settings = sp.Settings(general_settings) + 108 + 8.12M 20.00M 109 settings.set_volume_creation_settings({ + 8.12M 20.00M 110 Tags.SIMULATE_DEFORMED_LAYERS: True, + 8.12M 20.00M 111 Tags.STRUCTURES: create_example_tissue() + 112 }) + 8.12M 20.00M 113 settings.set_optical_settings({ + 8.12M 20.00M 114 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 8.12M 20.00M 115 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 8.12M 20.00M 116 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 8.12M 20.00M 117 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 118 }) + 8.12M 20.00M 119 settings["noise_model_1"] = { + 8.12M 20.00M 120 Tags.NOISE_MEAN: 1.0, + 8.12M 20.00M 121 Tags.NOISE_STD: 0.1, + 8.12M 20.00M 122 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 8.12M 20.00M 123 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 8.12M 20.00M 124 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 125 } + 126 + 8.12M 20.00M 127 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: + 8.12M 20.00M 128 pipeline = [ + 8.12M 20.00M 129 sp.ModelBasedVolumeCreationAdapter(settings), + 8.12M 20.00M 130 sp.MCXAdapter(settings), + 8.12M 20.00M 131 sp.GaussianNoise(settings, "noise_model_1") + 132 ] + 133 else: + 134 pipeline = [ + 135 sp.ModelBasedVolumeCreationAdapter(settings), + 136 sp.MCXAdapterReflectance(settings), + 137 ] + 138 + 139 + 8.12M 20.00M 140 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): + 141 """ + 142 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. + 143 + 144 """ + 145 + 146 def __init__(self): + 147 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 148 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 149 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) + 150 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], + 151 direction_vector_mm=[0, 0, 1])) + 152 + 153 + 8.12M 20.00M 154 device = ExampleDeviceSlitIlluminationLinearDetector() + 155 + 160.20M 220.00M 156 sp.simulate(pipeline, settings, device) + 157 + 8.12M 42.00M 158 if Tags.WAVELENGTH in settings: + 8.12M 42.00M 159 WAVELENGTH = settings[Tags.WAVELENGTH] + 160 else: + 161 WAVELENGTH = 700 + 162 + 8.12M 42.00M 163 if visualise: + 164 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 165 wavelength=WAVELENGTH, + 166 show_initial_pressure=True, + 167 show_absorption=True, + 168 show_diffuse_reflectance=SAVE_REFLECTANCE, + 169 log_scale=True) +## run_linear_unmixing + +active_bytes reserved_bytes line code + all all + peak peak + 0.00B 0.00B 18 @profile + 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): + 20 """ + 21 + 22 :param SPACING: The simulation spacing between voxels + 23 :param path_manager: the path manager to be used, typically sp.PathManager + 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 25 :return: a run through of the example + 26 """ + 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you + 28 # set global params characterizing the simulated volume + 0.00B 0.00B 29 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 0.00B 0.00B 30 VOLUME_PLANAR_DIM_IN_MM = 20 + 0.00B 0.00B 31 VOLUME_HEIGHT_IN_MM = 25 + 0.00B 0.00B 32 RANDOM_SEED = 471 + 0.00B 0.00B 33 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) + 34 + 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths + 0.00B 0.00B 36 WAVELENGTHS = [750, 800, 850] + 37 + 0.00B 0.00B 38 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and two blood vessels. + 43 """ + 44 background_dictionary = sp.Settings() + 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 muscle_dictionary = sp.Settings() + 49 muscle_dictionary[Tags.PRIORITY] = 1 + 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 vessel_1_dictionary = sp.Settings() + 58 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 10, + 61 5] + 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 12, + 64 5] + 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) + 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 vessel_2_dictionary = sp.Settings() + 71 vessel_2_dictionary[Tags.PRIORITY] = 3 + 72 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 73 10, + 74 5] + 75 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 76 12, + 77 5] + 78 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 79 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) + 80 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 81 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 82 + 83 epidermis_dictionary = sp.Settings() + 84 epidermis_dictionary[Tags.PRIORITY] = 8 + 85 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 86 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] + 87 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 88 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 89 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 90 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 91 + 92 tissue_dict = sp.Settings() + 93 tissue_dict[Tags.BACKGROUND] = background_dictionary + 94 tissue_dict["muscle"] = muscle_dictionary + 95 tissue_dict["epidermis"] = epidermis_dictionary + 96 tissue_dict["vessel_1"] = vessel_1_dictionary + 97 tissue_dict["vessel_2"] = vessel_2_dictionary + 98 return tissue_dict + 99 + 100 + 101 # Seed the numpy random configuration prior to creating the global_settings file in + 102 # order to ensure that the same volume is generated with the same random seed every time. + 0.00B 0.00B 103 np.random.seed(RANDOM_SEED) + 104 + 105 # Initialize global settings and prepare for simulation pipeline including + 106 # volume creation and optical forward simulation. + 0.00B 0.00B 107 general_settings = { + 108 # These parameters set the general properties of the simulated volume + 0.00B 0.00B 109 Tags.RANDOM_SEED: RANDOM_SEED, + 0.00B 0.00B 110 Tags.VOLUME_NAME: VOLUME_NAME, + 0.00B 0.00B 111 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 0.00B 0.00B 112 Tags.SPACING_MM: SPACING, + 0.00B 0.00B 113 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 0.00B 0.00B 114 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 0.00B 0.00B 115 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 0.00B 0.00B 116 Tags.WAVELENGTHS: WAVELENGTHS, + 0.00B 0.00B 117 Tags.GPU: True, + 0.00B 0.00B 118 Tags.DO_FILE_COMPRESSION: True + 119 } + 0.00B 0.00B 120 settings = sp.Settings(general_settings) + 0.00B 0.00B 121 settings.set_volume_creation_settings({ + 0.00B 0.00B 122 Tags.SIMULATE_DEFORMED_LAYERS: True, + 0.00B 0.00B 123 Tags.STRUCTURES: create_example_tissue() + 124 }) + 0.00B 0.00B 125 settings.set_optical_settings({ + 0.00B 0.00B 126 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 0.00B 0.00B 127 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 0.00B 0.00B 128 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 0.00B 0.00B 129 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 130 }) + 131 + 132 # Set component settings for linear unmixing. + 133 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the + 134 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. + 135 # Please take a look at the component for more information. + 0.00B 0.00B 136 settings["linear_unmixing"] = { + 0.00B 0.00B 137 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 0.00B 0.00B 138 Tags.WAVELENGTHS: WAVELENGTHS, + 0.00B 0.00B 139 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( + 0.00B 0.00B 140 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] + 141 ), + 0.00B 0.00B 142 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, + 0.00B 0.00B 143 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True + 144 } + 145 + 146 # Get device for simulation + 0.00B 0.00B 147 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 0.00B 0.00B 148 VOLUME_PLANAR_DIM_IN_MM/2, + 0.00B 0.00B 149 0])) + 0.00B 0.00B 150 device.update_settings_for_use_of_model_based_volume_creator(settings) + 151 + 152 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS + 0.00B 0.00B 153 pipeline = [ + 0.00B 0.00B 154 sp.ModelBasedVolumeCreationAdapter(settings), + 0.00B 0.00B 155 sp.MCXAdapter(settings), + 0.00B 0.00B 156 sp.FieldOfViewCropping(settings), + 157 ] + 139.43M 192.00M 158 sp.simulate(pipeline, settings, device) + 159 + 160 # Run linear unmixing component with above specified settings. + 8.12M 20.00M 161 sp.LinearUnmixing(settings, "linear_unmixing").run() + 162 + 163 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. + 8.12M 20.00M 164 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 8.12M 20.00M 165 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) + 8.12M 20.00M 166 sO2 = lu_results["sO2"] + 167 + 8.12M 20.00M 168 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) + 8.12M 20.00M 169 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) + 8.12M 20.00M 170 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) + 171 + 172 # Visualize linear unmixing result + 8.12M 20.00M 173 if visualise: + 174 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 175 wavelength=WAVELENGTHS[0], + 176 show_initial_pressure=True, + 177 show_oxygenation=True, + 178 show_linear_unmixing_sO2=True) diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.2.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.2.txt new file mode 100644 index 00000000..39ea9177 --- /dev/null +++ b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.2.txt @@ -0,0 +1,1027 @@ +Filename: /home/f762e/Workspace/simpa/simpa_examples/linear_unmixing.py + +Line # Mem usage Increment Occurrences Line Contents +============================================================= + 18 445.0 MiB 445.0 MiB 1 @profile + 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager(), visualise: bool = True): + 20 """ + 21 + 22 :param SPACING: The simulation spacing between voxels + 23 :param path_manager: the path manager to be used, typically sp.PathManager + 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 25 :return: a run through of the example + 26 """ + 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you + 28 # set global params characterizing the simulated volume + 29 445.0 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 30 445.0 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 20 + 31 445.0 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 25 + 32 445.0 MiB 0.0 MiB 1 RANDOM_SEED = 471 + 33 445.0 MiB 0.0 MiB 1 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) + 34 + 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths + 36 445.0 MiB 0.0 MiB 1 WAVELENGTHS = [750, 800, 850] + 37 + 38 445.0 MiB 0.0 MiB 2 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and two blood vessels. + 43 """ + 44 445.0 MiB 0.0 MiB 1 background_dictionary = sp.Settings() + 45 445.4 MiB 0.3 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 445.4 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 445.4 MiB 0.0 MiB 1 muscle_dictionary = sp.Settings() + 49 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.PRIORITY] = 1 + 50 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 51 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 445.4 MiB 0.0 MiB 1 vessel_1_dictionary = sp.Settings() + 58 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 445.4 MiB 0.0 MiB 2 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 445.4 MiB 0.0 MiB 1 10, + 61 445.4 MiB 0.0 MiB 1 5] + 62 445.4 MiB 0.0 MiB 2 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 445.4 MiB 0.0 MiB 1 12, + 64 445.4 MiB 0.0 MiB 1 5] + 65 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) + 67 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 445.4 MiB 0.0 MiB 1 vessel_2_dictionary = sp.Settings() + 71 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.PRIORITY] = 3 + 72 445.4 MiB 0.0 MiB 2 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 73 445.4 MiB 0.0 MiB 1 10, + 74 445.4 MiB 0.0 MiB 1 5] + 75 445.4 MiB 0.0 MiB 2 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 76 445.4 MiB 0.0 MiB 1 12, + 77 445.4 MiB 0.0 MiB 1 5] + 78 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 79 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) + 80 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 81 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 82 + 83 445.4 MiB 0.0 MiB 1 epidermis_dictionary = sp.Settings() + 84 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.PRIORITY] = 8 + 85 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 86 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] + 87 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 88 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 89 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 90 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 91 + 92 445.4 MiB 0.0 MiB 1 tissue_dict = sp.Settings() + 93 445.4 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary + 94 445.4 MiB 0.0 MiB 1 tissue_dict["muscle"] = muscle_dictionary + 95 445.4 MiB 0.0 MiB 1 tissue_dict["epidermis"] = epidermis_dictionary + 96 445.4 MiB 0.0 MiB 1 tissue_dict["vessel_1"] = vessel_1_dictionary + 97 445.4 MiB 0.0 MiB 1 tissue_dict["vessel_2"] = vessel_2_dictionary + 98 445.4 MiB 0.0 MiB 1 return tissue_dict + 99 + 100 # Seed the numpy random configuration prior to creating the global_settings file in + 101 # order to ensure that the same volume is generated with the same random seed every time. + 102 445.0 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) + 103 + 104 # Initialize global settings and prepare for simulation pipeline including + 105 # volume creation and optical forward simulation. + 106 445.0 MiB 0.0 MiB 1 general_settings = { + 107 # These parameters set the general properties of the simulated volume + 108 445.0 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, + 109 445.0 MiB 0.0 MiB 1 Tags.VOLUME_NAME: VOLUME_NAME, + 110 445.0 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 111 445.0 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, + 112 445.0 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 113 445.0 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 114 445.0 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 115 445.0 MiB 0.0 MiB 1 Tags.WAVELENGTHS: WAVELENGTHS, + 116 445.0 MiB 0.0 MiB 1 Tags.GPU: True, + 117 445.0 MiB 0.0 MiB 1 Tags.DO_FILE_COMPRESSION: True + 118 } + 119 445.0 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) + 120 445.4 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ + 121 445.0 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: True, + 122 445.4 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue() + 123 }) + 124 445.4 MiB 0.0 MiB 2 settings.set_optical_settings({ + 125 445.4 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 126 445.4 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 127 445.4 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 128 445.4 MiB 0.0 MiB 1 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 129 }) + 130 + 131 # Set component settings for linear unmixing. + 132 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the + 133 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. + 134 # Please take a look at the component for more information. + 135 445.4 MiB 0.0 MiB 1 settings["linear_unmixing"] = { + 136 445.4 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 137 445.4 MiB 0.0 MiB 1 Tags.WAVELENGTHS: WAVELENGTHS, + 138 445.4 MiB 0.0 MiB 2 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( + 139 445.4 MiB 0.0 MiB 1 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] + 140 ), + 141 445.4 MiB 0.0 MiB 1 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, + 142 445.4 MiB 0.0 MiB 1 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True + 143 } + 144 + 145 # Get device for simulation + 146 445.4 MiB 0.0 MiB 2 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 147 445.4 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM/2, + 148 445.4 MiB 0.0 MiB 1 0])) + 149 445.4 MiB 0.0 MiB 1 device.update_settings_for_use_of_model_based_volume_creator(settings) + 150 + 151 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS + 152 446.9 MiB 0.0 MiB 1 pipeline = [ + 153 446.9 MiB 1.5 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), + 154 446.9 MiB 0.0 MiB 1 sp.MCXAdapter(settings), + 155 446.9 MiB 0.0 MiB 1 sp.FieldOfViewCropping(settings), + 156 ] + 157 973.5 MiB 526.6 MiB 1 sp.simulate(pipeline, settings, device) + 158 + 159 # Run linear unmixing component with above specified settings. + 160 979.7 MiB 6.2 MiB 1 sp.LinearUnmixing(settings, "linear_unmixing").run() + 161 + 162 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. + 163 979.7 MiB 0.0 MiB 1 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 164 979.7 MiB 0.0 MiB 1 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) + 165 979.7 MiB 0.0 MiB 1 sO2 = lu_results["sO2"] + 166 + 167 979.7 MiB 0.0 MiB 1 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) + 168 979.7 MiB 0.0 MiB 1 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) + 169 979.7 MiB 0.0 MiB 1 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) + 170 + 171 # Visualize linear unmixing result + 172 979.7 MiB 0.0 MiB 1 if visualise: + 173 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 174 wavelength=WAVELENGTHS[0], + 175 show_initial_pressure=True, + 176 show_oxygenation=True, + 177 show_linear_unmixing_sO2=True) + + +Filename: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation.py + +Line # Mem usage Increment Occurrences Line Contents +============================================================= + 19 979.7 MiB 979.7 MiB 1 @profile + 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 28 979.7 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 29 979.7 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 30 + 30 979.7 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 60 + 31 979.7 MiB 0.0 MiB 1 RANDOM_SEED = 471 + 32 979.7 MiB 0.0 MiB 1 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 33 979.7 MiB 0.0 MiB 1 SAVE_REFLECTANCE = False + 34 979.7 MiB 0.0 MiB 1 SAVE_PHOTON_DIRECTION = False + 35 + 36 # If VISUALIZE is set to True, the simulation result will be plotted + 37 + 38 979.7 MiB 0.0 MiB 2 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and a blood vessel. + 43 """ + 44 979.7 MiB 0.0 MiB 1 background_dictionary = sp.Settings() + 45 979.7 MiB 0.0 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 979.7 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 979.7 MiB 0.0 MiB 1 muscle_dictionary = sp.Settings() + 49 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.PRIORITY] = 1 + 50 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] + 51 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 979.7 MiB 0.0 MiB 1 vessel_1_dictionary = sp.Settings() + 58 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 979.7 MiB 0.0 MiB 2 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 979.7 MiB 0.0 MiB 1 10, + 61 979.7 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM/2] + 62 979.7 MiB 0.0 MiB 2 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 979.7 MiB 0.0 MiB 1 12, + 64 979.7 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM/2] + 65 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() + 67 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 979.7 MiB 0.0 MiB 1 epidermis_dictionary = sp.Settings() + 71 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.PRIORITY] = 8 + 72 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] + 73 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] + 74 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 75 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 76 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 77 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 78 + 79 979.7 MiB 0.0 MiB 1 tissue_dict = sp.Settings() + 80 979.7 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary + 81 979.7 MiB 0.0 MiB 1 tissue_dict["muscle"] = muscle_dictionary + 82 979.7 MiB 0.0 MiB 1 tissue_dict["epidermis"] = epidermis_dictionary + 83 979.7 MiB 0.0 MiB 1 tissue_dict["vessel_1"] = vessel_1_dictionary + 84 979.7 MiB 0.0 MiB 1 return tissue_dict + 85 + 86 # Seed the numpy random configuration prior to creating the global_settings file in + 87 # order to ensure that the same volume + 88 # is generated with the same random seed every time. + 89 + 90 979.7 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) + 91 + 92 979.7 MiB 0.0 MiB 1 general_settings = { + 93 # These parameters set the general properties of the simulated volume + 94 979.7 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, + 95 979.7 MiB 0.0 MiB 1 Tags.VOLUME_NAME: VOLUME_NAME, + 96 979.7 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 97 979.7 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, + 98 979.7 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 99 979.7 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 100 979.7 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 101 979.7 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [798], + 102 979.7 MiB 0.0 MiB 1 Tags.DO_FILE_COMPRESSION: True, + 103 979.7 MiB 0.0 MiB 1 Tags.GPU: True + 104 } + 105 + 106 979.7 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) + 107 + 108 979.7 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ + 109 979.7 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: True, + 110 979.7 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue() + 111 }) + 112 979.7 MiB 0.0 MiB 2 settings.set_optical_settings({ + 113 979.7 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 114 979.7 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 115 979.7 MiB 0.0 MiB 1 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 116 979.7 MiB 0.0 MiB 1 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 117 }) + 118 979.7 MiB 0.0 MiB 1 settings["noise_model_1"] = { + 119 979.7 MiB 0.0 MiB 1 Tags.NOISE_MEAN: 1.0, + 120 979.7 MiB 0.0 MiB 1 Tags.NOISE_STD: 0.1, + 121 979.7 MiB 0.0 MiB 1 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 122 979.7 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 123 979.7 MiB 0.0 MiB 1 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 124 } + 125 + 126 979.7 MiB 0.0 MiB 1 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: + 127 979.7 MiB 0.0 MiB 1 pipeline = [ + 128 979.7 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), + 129 979.7 MiB 0.0 MiB 1 sp.MCXAdapter(settings), + 130 979.7 MiB 0.0 MiB 1 sp.GaussianNoise(settings, "noise_model_1") + 131 ] + 132 else: + 133 pipeline = [ + 134 sp.ModelBasedVolumeCreationAdapter(settings), + 135 sp.MCXAdapterReflectance(settings), + 136 ] + 137 + 138 979.7 MiB 0.0 MiB 3 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): + 139 979.7 MiB 0.0 MiB 1 """ + 140 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. + 141 + 142 """ + 143 + 144 979.7 MiB 0.0 MiB 2 def __init__(self): + 145 979.7 MiB 0.0 MiB 2 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 146 979.7 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 147 979.7 MiB 0.0 MiB 1 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) + 148 979.7 MiB 0.0 MiB 2 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], + 149 979.7 MiB 0.0 MiB 1 direction_vector_mm=[0, 0, 1])) + 150 + 151 979.7 MiB 0.0 MiB 1 device = ExampleDeviceSlitIlluminationLinearDetector() + 152 + 153 996.1 MiB 16.4 MiB 1 sp.simulate(pipeline, settings, device) + 154 + 155 996.1 MiB 0.0 MiB 1 if Tags.WAVELENGTH in settings: + 156 996.1 MiB 0.0 MiB 1 WAVELENGTH = settings[Tags.WAVELENGTH] + 157 else: + 158 WAVELENGTH = 700 + 159 + 160 996.1 MiB 0.0 MiB 1 if visualise: + 161 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 162 wavelength=WAVELENGTH, + 163 show_initial_pressure=True, + 164 show_absorption=True, + 165 show_diffuse_reflectance=SAVE_REFLECTANCE, + 166 log_scale=True) + + +Filename: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation_uniform_cube.py + +Line # Mem usage Increment Occurrences Line Contents +============================================================= + 22 996.1 MiB 996.1 MiB 1 @profile + 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), + 24 visualise: bool = True): + 25 """ + 26 + 27 :param SPACING: The simulation spacing between voxels + 28 :param path_manager: the path manager to be used, typically sp.PathManager + 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 30 :return: a run through of the example + 31 """ + 32 996.1 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 33 996.1 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 30 + 34 996.1 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 60 + 35 996.1 MiB 0.0 MiB 1 RANDOM_SEED = 471 + 36 996.1 MiB 0.0 MiB 1 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 37 996.1 MiB 0.0 MiB 1 SAVE_REFLECTANCE = True + 38 996.1 MiB 0.0 MiB 1 SAVE_PHOTON_DIRECTION = False + 39 + 40 # If VISUALIZE is set to True, the simulation result will be plotted + 41 996.1 MiB 0.0 MiB 1 VISUALIZE = True + 42 + 43 996.1 MiB 0.0 MiB 2 def create_example_tissue(): + 44 """ + 45 This is a very simple example script of how to create a tissue definition. + 46 It contains only a generic background tissue material. + 47 """ + 48 996.1 MiB 0.0 MiB 1 background_dictionary = sp.Settings() + 49 996.1 MiB 0.0 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 50 996.1 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 51 + 52 996.1 MiB 0.0 MiB 1 tissue_dict = sp.Settings() + 53 996.1 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary + 54 996.1 MiB 0.0 MiB 1 return tissue_dict + 55 + 56 # Seed the numpy random configuration prior to creating the global_settings file in + 57 # order to ensure that the same volume + 58 # is generated with the same random seed every time. + 59 + 60 996.1 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) + 61 + 62 996.1 MiB 0.0 MiB 1 general_settings = { + 63 # These parameters set the general properties of the simulated volume + 64 996.1 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, + 65 996.1 MiB 0.0 MiB 1 Tags.VOLUME_NAME: VOLUME_NAME, + 66 996.1 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 67 996.1 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, + 68 996.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 69 996.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 70 996.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 71 996.1 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [500], + 72 996.1 MiB 0.0 MiB 1 Tags.DO_FILE_COMPRESSION: True + 73 } + 74 + 75 996.1 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) + 76 + 77 996.1 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ + 78 996.1 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue() + 79 }) + 80 996.1 MiB 0.0 MiB 2 settings.set_optical_settings({ + 81 996.1 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 82 996.1 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 83 996.1 MiB 0.0 MiB 1 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 84 996.1 MiB 0.0 MiB 1 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 85 }) + 86 + 87 996.1 MiB 0.0 MiB 1 pipeline = [ + 88 996.1 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), + 89 996.1 MiB 0.0 MiB 1 sp.MCXAdapterReflectance(settings), + 90 ] + 91 + 92 996.1 MiB 0.0 MiB 2 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 93 996.1 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 94 + 95 1015.1 MiB 19.0 MiB 1 sp.simulate(pipeline, settings, device) + 96 + 97 1015.1 MiB 0.0 MiB 1 if visualise: + 98 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 99 wavelength=settings[Tags.WAVELENGTH], + 100 show_initial_pressure=True, + 101 show_absorption=True, + 102 show_diffuse_reflectance=SAVE_REFLECTANCE, + 103 log_scale=True) + + +Filename: /home/f762e/Workspace/simpa/simpa_examples/msot_invision_simulation.py + +Line # Mem usage Increment Occurrences Line Contents +============================================================= + 14 1015.1 MiB 1015.1 MiB 1 @profile + 15 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): + 16 """ + 17 + 18 :param SPACING: The simulation spacing between voxels + 19 :param path_manager: the path manager to be used, typically sp.PathManager + 20 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 21 :return: a run through of the example + 22 """ + 23 1015.1 MiB 0.0 MiB 1 SPEED_OF_SOUND = 1500 + 24 1015.1 MiB 0.0 MiB 1 XZ_DIM = 90 + 25 1015.1 MiB 0.0 MiB 1 Y_DIM = 40 + 26 + 27 1015.1 MiB 0.0 MiB 2 def create_pipeline(_settings: sp.Settings): + 28 1015.1 MiB 0.0 MiB 1 return [ + 29 1015.1 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), + 30 1015.1 MiB 0.0 MiB 1 sp.MCXAdapter(settings), + 31 1015.1 MiB 0.0 MiB 1 sp.KWaveAdapter(settings), + 32 1015.1 MiB 0.0 MiB 1 sp.FieldOfViewCropping(settings), + 33 1015.1 MiB 0.0 MiB 1 sp.TimeReversalAdapter(settings) + 34 ] + 35 + 36 1015.1 MiB 0.0 MiB 2 def get_device(): + 37 1015.1 MiB 0.0 MiB 1 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) + 38 1015.1 MiB 0.0 MiB 1 return pa_device + 39 + 40 1015.1 MiB 0.0 MiB 2 def create_volume(): + 41 1015.1 MiB 0.0 MiB 2 inclusion_material = sp.Molecule(volume_fraction=1.0, + 42 1015.1 MiB 0.0 MiB 2 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 43 1015.1 MiB 0.0 MiB 1 0.9), + 44 1015.1 MiB 0.0 MiB 2 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 45 1015.1 MiB 0.0 MiB 1 100.0), + 46 1015.1 MiB 0.0 MiB 2 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 47 1015.1 MiB 0.0 MiB 1 4.0), + 48 1015.1 MiB 0.0 MiB 1 speed_of_sound=SPEED_OF_SOUND, + 49 1015.1 MiB 0.0 MiB 1 alpha_coefficient=1e-4, + 50 1015.1 MiB 0.0 MiB 1 density=1000, + 51 1015.1 MiB 0.0 MiB 1 gruneisen_parameter=1.0, + 52 1015.1 MiB 0.0 MiB 1 name="Inclusion") + 53 + 54 1015.1 MiB 0.0 MiB 2 phantom_material = sp.Molecule(volume_fraction=1.0, + 55 1015.1 MiB 0.0 MiB 2 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 56 1015.1 MiB 0.0 MiB 1 0.9), + 57 1015.1 MiB 0.0 MiB 2 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 58 1015.1 MiB 0.0 MiB 1 100.0), + 59 1015.1 MiB 0.0 MiB 2 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 60 1015.1 MiB 0.0 MiB 1 0.05), + 61 1015.1 MiB 0.0 MiB 1 speed_of_sound=SPEED_OF_SOUND, + 62 1015.1 MiB 0.0 MiB 1 alpha_coefficient=1e-4, + 63 1015.1 MiB 0.0 MiB 1 density=1000, + 64 1015.1 MiB 0.0 MiB 1 gruneisen_parameter=1.0, + 65 1015.1 MiB 0.0 MiB 1 name="Phantom") + 66 + 67 1015.1 MiB 0.0 MiB 2 heavy_water = sp.Molecule(volume_fraction=1.0, + 68 1015.1 MiB 0.0 MiB 1 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), + 69 1015.1 MiB 0.0 MiB 1 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), + 70 1015.1 MiB 0.0 MiB 1 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), + 71 1015.1 MiB 0.0 MiB 1 speed_of_sound=SPEED_OF_SOUND, + 72 1015.1 MiB 0.0 MiB 1 alpha_coefficient=1e-4, + 73 1015.1 MiB 0.0 MiB 1 density=1000, + 74 1015.1 MiB 0.0 MiB 1 gruneisen_parameter=1.0, + 75 1015.1 MiB 0.0 MiB 1 name="background_water") + 76 + 77 1015.1 MiB 0.0 MiB 1 background_dictionary = sp.Settings() + 78 1015.1 MiB 0.0 MiB 2 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 79 1015.1 MiB 0.0 MiB 1 .append(heavy_water) + 80 1015.1 MiB 0.0 MiB 1 .get_molecular_composition(segmentation_type=-1)) + 81 1015.1 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 82 + 83 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary = sp.Settings() + 84 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.PRIORITY] = 3 + 85 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] + 86 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 + 87 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 + 88 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 + 89 1015.1 MiB 0.0 MiB 2 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 90 1015.1 MiB 0.0 MiB 1 .append(phantom_material) + 91 1015.1 MiB 0.0 MiB 1 .get_molecular_composition(segmentation_type=0)) + 92 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 93 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 94 + 95 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary = sp.Settings() + 96 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.PRIORITY] = 8 + 97 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] + 98 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 + 99 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 + 100 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 + 101 1015.1 MiB 0.0 MiB 2 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 102 1015.1 MiB 0.0 MiB 1 .append(inclusion_material) + 103 1015.1 MiB 0.0 MiB 1 .get_molecular_composition(segmentation_type=1)) + 104 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 105 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 106 + 107 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary = sp.Settings() + 108 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.PRIORITY] = 5 + 109 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] + 110 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] + 111 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 112 1015.1 MiB 0.0 MiB 2 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 113 1015.1 MiB 0.0 MiB 1 .append(inclusion_material) + 114 1015.1 MiB 0.0 MiB 1 .get_molecular_composition(segmentation_type=2)) + 115 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 116 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 117 + 118 1015.1 MiB 0.0 MiB 1 tissue_dict = sp.Settings() + 119 1015.1 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary + 120 1015.1 MiB 0.0 MiB 1 tissue_dict["phantom"] = phantom_material_dictionary + 121 1015.1 MiB 0.0 MiB 1 tissue_dict["inclusion_1"] = inclusion_1_dictionary + 122 1015.1 MiB 0.0 MiB 1 tissue_dict["inclusion_2"] = inclusion_2_dictionary + 123 1015.1 MiB 0.0 MiB 1 return { + 124 1015.1 MiB 0.0 MiB 1 Tags.STRUCTURES: tissue_dict, + 125 1015.1 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: False + 126 } + 127 + 128 1015.1 MiB 0.0 MiB 2 def get_settings(): + 129 1015.1 MiB 0.0 MiB 1 general_settings = { + 130 # These parameters set the general properties of the simulated volume + 131 1015.1 MiB 0.0 MiB 1 Tags.RANDOM_SEED: 4711, + 132 1015.1 MiB 0.0 MiB 1 Tags.VOLUME_NAME: "InVision Simulation Example", + 133 1015.1 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 134 1015.1 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, + 135 1015.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: XZ_DIM, + 136 1015.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: XZ_DIM, + 137 1015.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: Y_DIM, + 138 1015.1 MiB 0.0 MiB 1 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 139 1015.1 MiB 0.0 MiB 1 Tags.GPU: True, + 140 1015.1 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [700] + 141 } + 142 + 143 1015.1 MiB 0.0 MiB 1 volume_settings = create_volume() + 144 + 145 1015.1 MiB 0.0 MiB 1 optical_settings = { + 146 1015.1 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 147 1015.1 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 148 1015.1 MiB 0.0 MiB 1 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, + 149 1015.1 MiB 0.0 MiB 1 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 150 } + 151 + 152 1015.1 MiB 0.0 MiB 1 acoustic_settings = { + 153 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_SIMULATION_3D: True, + 154 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 155 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 156 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 157 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLInside: False, + 158 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 159 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 160 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PlotPML: False, + 161 1015.1 MiB 0.0 MiB 1 Tags.RECORDMOVIE: False, + 162 1015.1 MiB 0.0 MiB 1 Tags.MOVIENAME: "visualization_log", + 163 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_LOG_SCALE: True + 164 } + 165 + 166 1015.1 MiB 0.0 MiB 19 reconstruction_settings = { + 167 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 168 1015.1 MiB 0.0 MiB 1 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 169 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 170 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 171 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, + 172 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 173 1015.1 MiB 0.0 MiB 1 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, + 174 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 175 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLInside: False, + 176 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 177 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 178 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PlotPML: False, + 179 1015.1 MiB 0.0 MiB 1 Tags.RECORDMOVIE: False, + 180 1015.1 MiB 0.0 MiB 1 Tags.MOVIENAME: "visualization_log", + 181 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_LOG_SCALE: True, + 182 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 183 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 184 1015.1 MiB 0.0 MiB 1 Tags.SPACING_MM: 0.25, + 185 } + 186 + 187 1015.1 MiB 0.0 MiB 1 _settings = sp.Settings(general_settings) + 188 1015.1 MiB 0.0 MiB 1 _settings.set_volume_creation_settings(volume_settings) + 189 1015.1 MiB 0.0 MiB 1 _settings.set_optical_settings(optical_settings) + 190 1015.1 MiB 0.0 MiB 1 _settings.set_acoustic_settings(acoustic_settings) + 191 1015.1 MiB 0.0 MiB 1 _settings.set_reconstruction_settings(reconstruction_settings) + 192 1015.1 MiB 0.0 MiB 1 return _settings + 193 + 194 1015.1 MiB 0.0 MiB 1 device = get_device() + 195 1015.1 MiB 0.0 MiB 1 settings = get_settings() + 196 1015.1 MiB 0.0 MiB 1 pipeline = create_pipeline(settings) + 197 + 198 1054.3 MiB 39.2 MiB 1 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) + 199 + 200 1054.3 MiB 0.0 MiB 1 if visualise: + 201 sp.visualise_data(settings=settings, + 202 path_manager=path_manager, + 203 show_absorption=True, + 204 show_initial_pressure=True, + 205 show_reconstructed_data=True, + 206 show_xz_only=True) + + +Filename: /home/f762e/Workspace/simpa/simpa_examples/optical_and_acoustic_simulation.py + +Line # Mem usage Increment Occurrences Line Contents +============================================================= + 19 1054.3 MiB 1054.3 MiB 1 @profile + 20 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), + 21 visualise: bool = True): + 22 """ + 23 + 24 :param SPACING: The simulation spacing between voxels + 25 :param path_manager: the path manager to be used, typically sp.PathManager + 26 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 27 :return: a run through of the example + 28 """ + 29 1054.3 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 30 1054.3 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 20 + 31 1054.3 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 25 + 32 1054.3 MiB 0.0 MiB 1 RANDOM_SEED = 4711 + 33 + 34 # If VISUALIZE is set to True, the simulation result will be plotted + 35 1054.3 MiB 0.0 MiB 1 VISUALIZE = True + 36 + 37 1054.3 MiB 0.0 MiB 2 def create_example_tissue(): + 38 """ + 39 This is a very simple example script of how to create a tissue definition. + 40 It contains a muscular background, an epidermis layer on top of the muscles + 41 and a blood vessel. + 42 """ + 43 1054.3 MiB 0.0 MiB 1 background_dictionary = sp.Settings() + 44 1054.3 MiB 0.0 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) + 45 1054.3 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 46 + 47 1054.3 MiB 0.0 MiB 1 tissue_dict = sp.Settings() + 48 1054.3 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary + 49 1054.3 MiB 0.0 MiB 2 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, + 50 1054.3 MiB 0.0 MiB 2 molecular_composition=sp.TISSUE_LIBRARY.constant( + 51 1054.3 MiB 0.0 MiB 1 0.05, 100, 0.9), + 52 1054.3 MiB 0.0 MiB 1 priority=1, + 53 1054.3 MiB 0.0 MiB 1 consider_partial_volume=True, + 54 1054.3 MiB 0.0 MiB 1 adhere_to_deformation=True) + 55 1054.3 MiB 0.0 MiB 2 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, + 56 1054.3 MiB 0.0 MiB 1 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), + 57 1054.3 MiB 0.0 MiB 1 priority=8, + 58 1054.3 MiB 0.0 MiB 1 consider_partial_volume=True, + 59 1054.3 MiB 0.0 MiB 1 adhere_to_deformation=True) + 60 1054.3 MiB 0.0 MiB 2 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( + 61 1054.3 MiB 0.0 MiB 1 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], + 62 1054.3 MiB 0.0 MiB 1 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], + 63 1054.3 MiB 0.0 MiB 1 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 64 1054.3 MiB 0.0 MiB 1 radius_mm=2, priority=3, consider_partial_volume=True, + 65 1054.3 MiB 0.0 MiB 1 adhere_to_deformation=False + 66 ) + 67 1054.3 MiB 0.0 MiB 2 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( + 68 1054.3 MiB 0.0 MiB 1 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], + 69 1054.3 MiB 0.0 MiB 1 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], + 70 1054.3 MiB 0.0 MiB 1 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 71 1054.3 MiB 0.0 MiB 1 radius_mm=3, priority=3, consider_partial_volume=True, + 72 1054.3 MiB 0.0 MiB 1 adhere_to_deformation=False + 73 ) + 74 1054.3 MiB 0.0 MiB 1 return tissue_dict + 75 + 76 # Seed the numpy random configuration prior to creating the global_settings file in + 77 # order to ensure that the same volume + 78 # is generated with the same random seed every time. + 79 + 80 1054.3 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) + 81 1054.3 MiB 0.0 MiB 1 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) + 82 + 83 1054.3 MiB 0.0 MiB 1 general_settings = { + 84 # These parameters set the general properties of the simulated volume + 85 1054.3 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, + 86 1054.3 MiB 0.0 MiB 1 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), + 87 1054.3 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 88 1054.3 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, + 89 1054.3 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 90 1054.3 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 91 1054.3 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 92 1054.3 MiB 0.0 MiB 1 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 93 1054.3 MiB 0.0 MiB 1 Tags.GPU: True, + 94 1054.3 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [700, 800], + 95 1054.3 MiB 0.0 MiB 1 Tags.DO_FILE_COMPRESSION: True, + 96 1054.3 MiB 0.0 MiB 1 Tags.DO_IPASC_EXPORT: True + 97 } + 98 1054.3 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) + 99 1054.3 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) + 100 + 101 1054.3 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ + 102 1054.3 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue(), + 103 1054.3 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: True + 104 }) + 105 + 106 1054.3 MiB 0.0 MiB 2 settings.set_optical_settings({ + 107 1054.3 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 108 1054.3 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 109 1054.3 MiB 0.0 MiB 1 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, + 110 1054.3 MiB 0.0 MiB 1 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 111 1054.3 MiB 0.0 MiB 1 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, + 112 }) + 113 + 114 1054.3 MiB 0.0 MiB 2 settings.set_acoustic_settings({ + 115 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_SIMULATION_3D: False, + 116 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 117 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 118 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 119 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLInside: False, + 120 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 121 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 122 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PlotPML: False, + 123 1054.3 MiB 0.0 MiB 1 Tags.RECORDMOVIE: False, + 124 1054.3 MiB 0.0 MiB 1 Tags.MOVIENAME: "visualization_log", + 125 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_LOG_SCALE: True + 126 }) + 127 + 128 1054.3 MiB 0.0 MiB 19 settings.set_reconstruction_settings({ + 129 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 130 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 131 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_SIMULATION_3D: False, + 132 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 133 1054.3 MiB 0.0 MiB 1 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 134 1054.3 MiB 0.0 MiB 1 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), + 135 1054.3 MiB 0.0 MiB 1 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), + 136 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 137 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 138 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, + 139 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 140 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 141 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLInside: False, + 142 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 143 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 144 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PlotPML: False, + 145 1054.3 MiB 0.0 MiB 1 Tags.RECORDMOVIE: False, + 146 1054.3 MiB 0.0 MiB 1 Tags.MOVIENAME: "visualization_log", + 147 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_LOG_SCALE: True, + 148 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, + 149 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, + 150 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD_DENSITY: 1000, + 151 1054.3 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING + 152 }) + 153 + 154 1054.3 MiB 0.0 MiB 1 settings["noise_initial_pressure"] = { + 155 1054.3 MiB 0.0 MiB 1 Tags.NOISE_MEAN: 1, + 156 1054.3 MiB 0.0 MiB 1 Tags.NOISE_STD: 0.01, + 157 1054.3 MiB 0.0 MiB 1 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 158 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 159 1054.3 MiB 0.0 MiB 1 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 160 } + 161 + 162 1054.3 MiB 0.0 MiB 1 settings["noise_time_series"] = { + 163 1054.3 MiB 0.0 MiB 1 Tags.NOISE_STD: 1, + 164 1054.3 MiB 0.0 MiB 1 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, + 165 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA + 166 } + 167 + 168 # TODO: For the device choice, uncomment the undesired device + 169 + 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 171 # VOLUME_PLANAR_DIM_IN_MM/2, + 172 # 0])) + 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) + 174 + 175 1054.3 MiB 0.0 MiB 3 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 176 1054.3 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM/2, + 177 1054.3 MiB 0.0 MiB 1 0]), + 178 1054.3 MiB 0.0 MiB 1 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) + 179 1054.3 MiB 0.0 MiB 2 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, + 180 1054.3 MiB 0.0 MiB 1 pitch_mm=0.25, + 181 1054.3 MiB 0.0 MiB 1 number_detector_elements=100, + 182 1054.3 MiB 0.0 MiB 1 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) + 183 1054.3 MiB 0.0 MiB 1 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) + 184 1054.3 MiB 0.0 MiB 1 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) + 185 + 186 1054.3 MiB 0.0 MiB 1 SIMULATION_PIPELINE = [ + 187 1054.3 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), + 188 1054.3 MiB 0.0 MiB 1 sp.MCXAdapter(settings), + 189 1054.3 MiB 0.0 MiB 1 sp.GaussianNoise(settings, "noise_initial_pressure"), + 190 1054.3 MiB 0.0 MiB 1 sp.KWaveAdapter(settings), + 191 1054.3 MiB 0.0 MiB 1 sp.GaussianNoise(settings, "noise_time_series"), + 192 1054.3 MiB 0.0 MiB 1 sp.TimeReversalAdapter(settings), + 193 1054.3 MiB 0.0 MiB 1 sp.FieldOfViewCropping(settings) + 194 ] + 195 + 196 1083.2 MiB 28.9 MiB 1 sp.simulate(SIMULATION_PIPELINE, settings, device) + 197 + 198 1083.2 MiB 0.0 MiB 1 if Tags.WAVELENGTH in settings: + 199 1083.2 MiB 0.0 MiB 1 WAVELENGTH = settings[Tags.WAVELENGTH] + 200 else: + 201 WAVELENGTH = 700 + 202 + 203 1083.2 MiB 0.0 MiB 1 if visualise: + 204 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + 205 wavelength=WAVELENGTH, + 206 show_time_series_data=True, + 207 show_initial_pressure=True, + 208 show_reconstructed_data=True, + 209 log_scale=False, + 210 show_xz_only=False) + + +Filename: /home/f762e/Workspace/simpa/simpa_examples/perform_iterative_qPAI_reconstruction.py + +Line # Mem usage Increment Occurrences Line Contents +============================================================= + 25 1083.2 MiB 1083.2 MiB 1 @profile + 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise: bool = True): + 27 """ + 28 + 29 :param SPACING: The simulation spacing between voxels + 30 :param path_manager: the path manager to be used, typically sp.PathManager + 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 32 :return: a run through of the example + 33 """ + 34 1083.2 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 30 + 35 1083.2 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 30 + 36 1083.2 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 30 + 37 1083.2 MiB 0.0 MiB 1 RANDOM_SEED = 471 + 38 1083.2 MiB 0.0 MiB 1 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) + 39 + 40 # If VISUALIZE is set to True, the reconstruction result will be plotted + 41 + 42 1083.2 MiB 0.0 MiB 2 def create_example_tissue(): + 43 """ + 44 This is a very simple example script of how to create a tissue definition. + 45 It contains a muscular background, an epidermis layer on top of the muscles + 46 and a blood vessel. + 47 """ + 48 1083.2 MiB 0.0 MiB 1 background_dictionary = sp.Settings() + 49 1083.2 MiB 0.0 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) + 50 1083.2 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 51 + 52 1083.2 MiB 0.0 MiB 1 epidermis_structure = sp.Settings() + 53 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.PRIORITY] = 1 + 54 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] + 55 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] + 56 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) + 57 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True + 58 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True + 59 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 60 + 61 1083.2 MiB 0.0 MiB 1 vessel_structure_1 = sp.Settings() + 62 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.PRIORITY] = 2 + 63 1083.2 MiB 0.0 MiB 2 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, + 64 1083.2 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM / 2] + 65 1083.2 MiB 0.0 MiB 2 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, + 66 1083.2 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] + 67 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 + 68 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 + 69 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) + 70 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True + 71 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True + 72 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE + 73 + 74 1083.2 MiB 0.0 MiB 1 vessel_structure_2 = sp.Settings() + 75 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.PRIORITY] = 3 + 76 1083.2 MiB 0.0 MiB 2 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, + 77 1083.2 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM / 3] + 78 1083.2 MiB 0.0 MiB 2 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, + 79 1083.2 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] + 80 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 + 81 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) + 82 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True + 83 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 84 + 85 1083.2 MiB 0.0 MiB 1 tissue_dict = sp.Settings() + 86 1083.2 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary + 87 1083.2 MiB 0.0 MiB 1 tissue_dict["epidermis"] = epidermis_structure + 88 1083.2 MiB 0.0 MiB 1 tissue_dict["vessel_1"] = vessel_structure_1 + 89 1083.2 MiB 0.0 MiB 1 tissue_dict["vessel_2"] = vessel_structure_2 + 90 1083.2 MiB 0.0 MiB 1 return tissue_dict + 91 + 92 # set settings for volume creation, optical simulation and iterative qPAI method + 93 1083.2 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) + 94 + 95 1083.2 MiB 0.0 MiB 1 general_settings = { + 96 # These parameters set the general properties of the simulated volume + 97 1083.2 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, + 98 1083.2 MiB 0.0 MiB 1 Tags.VOLUME_NAME: VOLUME_NAME, + 99 1083.2 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 100 1083.2 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, + 101 1083.2 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 102 1083.2 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 103 1083.2 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 104 1083.2 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [700] + 105 } + 106 + 107 1083.2 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) + 108 + 109 1083.2 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ + 110 # These parameters set the properties for the volume creation + 111 1083.2 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: True, + 112 1083.2 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue() + 113 }) + 114 1083.2 MiB 0.0 MiB 2 settings.set_optical_settings({ + 115 # These parameters set the properties for the optical Monte Carlo simulation + 116 1083.2 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 117 1083.2 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 118 1083.2 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 119 1083.2 MiB 0.0 MiB 1 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 120 }) + 121 1083.2 MiB 0.0 MiB 1 settings["noise_model"] = { + 122 1083.2 MiB 0.0 MiB 1 Tags.NOISE_MEAN: 1.0, + 123 1083.2 MiB 0.0 MiB 1 Tags.NOISE_STD: 0.01, + 124 1083.2 MiB 0.0 MiB 1 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 125 1083.2 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 126 1083.2 MiB 0.0 MiB 1 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 127 } + 128 1083.2 MiB 0.0 MiB 1 settings["iterative_qpai_reconstruction"] = { + 129 # These parameters set the properties of the iterative reconstruction + 130 1083.2 MiB 0.0 MiB 1 Tags.DOWNSCALE_FACTOR: 0.75, + 131 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, + 132 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant + 133 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, + 134 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, + 135 # for this example, we are not interested in all absorption updates + 136 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, + 137 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 + 138 } + 139 + 140 # run pipeline including iterative qPAI method + 141 1083.2 MiB 0.0 MiB 1 pipeline = [ + 142 1083.2 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), + 143 1083.2 MiB 0.0 MiB 1 sp.MCXAdapter(settings), + 144 1083.2 MiB 0.0 MiB 1 sp.GaussianNoise(settings, "noise_model"), + 145 1083.2 MiB 0.0 MiB 1 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") + 146 ] + 147 + 148 1083.2 MiB 0.0 MiB 3 class CustomDevice(sp.PhotoacousticDevice): + 149 + 150 1083.2 MiB 0.0 MiB 2 def __init__(self): + 151 1083.2 MiB 0.0 MiB 2 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, + 152 1083.2 MiB 0.0 MiB 1 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, + 153 1083.2 MiB 0.0 MiB 1 0])) + 154 1083.2 MiB 0.0 MiB 1 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) + 155 + 156 1083.2 MiB 0.0 MiB 1 device = CustomDevice() + 157 + 158 1083.2 MiB 0.0 MiB 1 device.update_settings_for_use_of_model_based_volume_creator(settings) + 159 + 160 1399.7 MiB 316.5 MiB 1 sp.simulate(pipeline, settings, device) + 161 + 162 # visualize reconstruction results + 163 1399.7 MiB 0.0 MiB 1 if visualise: + 164 # get simulation output + 165 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 166 settings = sp.load_data_field(data_path, Tags.SETTINGS) + 167 wavelength = settings[Tags.WAVELENGTHS][0] + 168 + 169 # get reconstruction result + 170 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) + 171 + 172 # get ground truth absorption coefficients + 173 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) + 174 + 175 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) + 176 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR + 177 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") + 178 + 179 # compute reconstruction error + 180 difference = absorption_gt - absorption_reconstruction + 181 + 182 median_error = np.median(difference) + 183 q3, q1 = np.percentile(difference, [75, 25]) + 184 iqr = q3 - q1 + 185 + 186 # visualize results + 187 x_pos = int(np.shape(absorption_gt)[0] / 2) + 188 y_pos = int(np.shape(absorption_gt)[1] / 2) + 189 + 190 if np.min(absorption_gt) > np.min(absorption_reconstruction): + 191 cmin = np.min(absorption_reconstruction) + 192 else: + 193 cmin = np.min(absorption_gt) + 194 + 195 if np.max(absorption_gt) > np.max(absorption_reconstruction): + 196 cmax = np.max(absorption_gt) + 197 else: + 198 cmax = np.max(absorption_reconstruction) + 199 + 200 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] + 201 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] + 202 + 203 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", + 204 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] + 205 + 206 plt.figure(figsize=(20, 15)) + 207 plt.subplots_adjust(hspace=0.5) + 208 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + + 209 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) + 210 + 211 for i, quantity in enumerate(results_x_z): + 212 plt.subplot(2, len(results_x_z), i + 1) + 213 if i == 0: + 214 plt.ylabel("x-z", fontsize=10) + 215 plt.title(label[i], fontsize=10) + 216 plt.imshow(quantity.T) + 217 plt.xticks(fontsize=6) + 218 plt.yticks(fontsize=6) + 219 plt.colorbar() + 220 if i != 2: + 221 plt.clim(cmin, cmax) + 222 else: + 223 plt.clim(np.min(difference), np.max(difference)) + 224 + 225 for i, quantity in enumerate(results_y_z): + 226 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) + 227 if i == 0: + 228 plt.ylabel("y-z", fontsize=10) + 229 plt.title(label[i], fontsize=10) + 230 plt.imshow(quantity.T) + 231 plt.xticks(fontsize=6) + 232 plt.yticks(fontsize=6) + 233 plt.colorbar() + 234 if i != 2: + 235 plt.clim(cmin, cmax) + 236 else: + 237 plt.clim(np.min(difference), np.max(difference)) + 238 + 239 plt.show() + 240 plt.close() + + diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.4.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.4.txt new file mode 100644 index 00000000..e69de29b diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_table.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_table.txt new file mode 100644 index 00000000..8bc23ef0 --- /dev/null +++ b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_table.txt @@ -0,0 +1,21 @@ ++-----------------------------------------+------------+------------+--------+--------+ +| Example | Spacing mm | Time μs | GPU | MEMORY | ++-----------------------------------------+------------+------------+--------+--------+ +| linear_unmixing | 0.2 | 12084177.3 | 1.45G | 526.6M | +| | 0.4 | 3958047.6 | 192.0M | 552.9M | ++-----------------------------------------+------------+------------+--------+--------+ +| minimal_optical_simulation | 0.2 | 10699994.3 | 1.6G | 16.4M | +| | 0.4 | 2844852.2 | 220.0M | -10.8M | ++-----------------------------------------+------------+------------+--------+--------+ +| minimal_optical_simulation_uniform_cube | 0.2 | 6502907.2 | 1.19G | 19.0M | +| | 0.4 | 1758689.2 | 162.0M | 0.0M | ++-----------------------------------------+------------+------------+--------+--------+ +| msot_invision_simulation | 0.2 | 28158333.9 | 4.25G | 39.2M | +| | 0.4 | 18450957.5 | 556.0M | 66.4M | ++-----------------------------------------+------------+------------+--------+--------+ +| optical_and_acoustic_simulation | 0.2 | 37078767.0 | 510.0M | 28.9M | +| | 0.4 | 9548734.8 | 80.0M | N/A | ++-----------------------------------------+------------+------------+--------+--------+ +| perform_iterative_qPAI_reconstruction | 0.2 | 8411215.6 | 376.0M | 316.5M | +| | 0.4 | 10481453.4 | 66.0M | 78.8M | ++-----------------------------------------+------------+------------+--------+--------+ \ No newline at end of file diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.2.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.2.txt new file mode 100644 index 00000000..30400b16 --- /dev/null +++ b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.2.txt @@ -0,0 +1,1048 @@ +Timer unit: 1e-06 s + +Total time: 15.7383 s +File: /home/f762e/Workspace/simpa/simpa_examples/linear_unmixing.py +Function: run_linear_unmixing at line 18 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 18 @profile + 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): + 20 """ + 21 + 22 :param SPACING: The simulation spacing between voxels + 23 :param path_manager: the path manager to be used, typically sp.PathManager + 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 25 :return: a run through of the example + 26 """ + 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you + 28 # set global params characterizing the simulated volume + 29 1 0.4 0.4 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 30 1 0.2 0.2 0.0 VOLUME_PLANAR_DIM_IN_MM = 20 + 31 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 25 + 32 1 0.1 0.1 0.0 RANDOM_SEED = 471 + 33 1 0.5 0.5 0.0 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) + 34 + 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths + 36 1 0.2 0.2 0.0 WAVELENGTHS = [750, 800, 850] + 37 + 38 1 0.2 0.2 0.0 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and two blood vessels. + 43 """ + 44 background_dictionary = sp.Settings() + 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 muscle_dictionary = sp.Settings() + 49 muscle_dictionary[Tags.PRIORITY] = 1 + 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 vessel_1_dictionary = sp.Settings() + 58 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 10, + 61 5] + 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 12, + 64 5] + 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) + 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 vessel_2_dictionary = sp.Settings() + 71 vessel_2_dictionary[Tags.PRIORITY] = 3 + 72 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 73 10, + 74 5] + 75 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 76 12, + 77 5] + 78 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 79 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) + 80 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 81 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 82 + 83 epidermis_dictionary = sp.Settings() + 84 epidermis_dictionary[Tags.PRIORITY] = 8 + 85 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 86 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] + 87 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 88 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 89 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 90 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 91 + 92 tissue_dict = sp.Settings() + 93 tissue_dict[Tags.BACKGROUND] = background_dictionary + 94 tissue_dict["muscle"] = muscle_dictionary + 95 tissue_dict["epidermis"] = epidermis_dictionary + 96 tissue_dict["vessel_1"] = vessel_1_dictionary + 97 tissue_dict["vessel_2"] = vessel_2_dictionary + 98 return tissue_dict + 99 + 100 + 101 # Seed the numpy random configuration prior to creating the global_settings file in + 102 # order to ensure that the same volume is generated with the same random seed every time. + 103 1 16.6 16.6 0.0 np.random.seed(RANDOM_SEED) + 104 + 105 # Initialize global settings and prepare for simulation pipeline including + 106 # volume creation and optical forward simulation. + 107 1 1.2 1.2 0.0 general_settings = { + 108 # These parameters set the general properties of the simulated volume + 109 1 0.9 0.9 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 110 1 0.5 0.5 0.0 Tags.VOLUME_NAME: VOLUME_NAME, + 111 1 54.3 54.3 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 112 1 0.7 0.7 0.0 Tags.SPACING_MM: SPACING, + 113 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 114 1 0.5 0.5 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 115 1 0.6 0.6 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 116 1 0.3 0.3 0.0 Tags.WAVELENGTHS: WAVELENGTHS, + 117 1 0.3 0.3 0.0 Tags.GPU: True, + 118 1 0.4 0.4 0.0 Tags.DO_FILE_COMPRESSION: True + 119 } + 120 1 25.2 25.2 0.0 settings = sp.Settings(general_settings) + 121 2 6.0 3.0 0.0 settings.set_volume_creation_settings({ + 122 1 0.3 0.3 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, + 123 1 30776.2 30776.2 0.2 Tags.STRUCTURES: create_example_tissue() + 124 }) + 125 2 7.1 3.5 0.0 settings.set_optical_settings({ + 126 1 0.4 0.4 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 127 1 36.7 36.7 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 128 1 0.5 0.5 0.0 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 129 1 0.3 0.3 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 130 }) + 131 + 132 # Set component settings for linear unmixing. + 133 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the + 134 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. + 135 # Please take a look at the component for more information. + 136 1 61.1 61.1 0.0 settings["linear_unmixing"] = { + 137 1 0.3 0.3 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 138 1 0.1 0.1 0.0 Tags.WAVELENGTHS: WAVELENGTHS, + 139 2 1980.0 990.0 0.0 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( + 140 1 0.8 0.8 0.0 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] + 141 ), + 142 1 0.5 0.5 0.0 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, + 143 1 0.6 0.6 0.0 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True + 144 } + 145 + 146 # Get device for simulation + 147 2 143.4 71.7 0.0 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 148 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM/2, + 149 1 0.1 0.1 0.0 0])) + 150 1 4490.2 4490.2 0.0 device.update_settings_for_use_of_model_based_volume_creator(settings) + 151 + 152 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS + 153 1 0.1 0.1 0.0 pipeline = [ + 154 1 14304.4 14304.4 0.1 sp.ModelBasedVolumeCreationAdapter(settings), + 155 1 14.3 14.3 0.0 sp.MCXAdapter(settings), + 156 1 136.0 136.0 0.0 sp.FieldOfViewCropping(settings), + 157 ] + 158 1 12084177.3 1e+07 76.8 sp.simulate(pipeline, settings, device) + 159 + 160 # Run linear unmixing component with above specified settings. + 161 1 3598835.0 4e+06 22.9 sp.LinearUnmixing(settings, "linear_unmixing").run() + 162 + 163 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. + 164 1 51.2 51.2 0.0 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 165 1 894.2 894.2 0.0 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) + 166 1 0.3 0.3 0.0 sO2 = lu_results["sO2"] + 167 + 168 1 567.7 567.7 0.0 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) + 169 1 1217.4 1217.4 0.0 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) + 170 1 530.7 530.7 0.0 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) + 171 + 172 # Visualize linear unmixing result + 173 1 0.2 0.2 0.0 if visualise: + 174 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 175 wavelength=WAVELENGTHS[0], + 176 show_initial_pressure=True, + 177 show_oxygenation=True, + 178 show_linear_unmixing_sO2=True) + +Total time: 10.7236 s +File: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation.py +Function: run_minimal_optical_simulation at line 19 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 19 @profile + 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 28 1 0.3 0.3 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 29 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 + 30 1 0.2 0.2 0.0 VOLUME_HEIGHT_IN_MM = 60 + 31 1 0.8 0.8 0.0 RANDOM_SEED = 471 + 32 1 0.6 0.6 0.0 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 33 1 0.1 0.1 0.0 SAVE_REFLECTANCE = False + 34 1 0.2 0.2 0.0 SAVE_PHOTON_DIRECTION = False + 35 + 36 # If VISUALIZE is set to True, the simulation result will be plotted + 37 + 38 1 1.5 1.5 0.0 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and a blood vessel. + 43 """ + 44 background_dictionary = sp.Settings() + 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 muscle_dictionary = sp.Settings() + 49 muscle_dictionary[Tags.PRIORITY] = 1 + 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] + 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 vessel_1_dictionary = sp.Settings() + 58 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 10, + 61 VOLUME_HEIGHT_IN_MM/2] + 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 12, + 64 VOLUME_HEIGHT_IN_MM/2] + 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() + 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 epidermis_dictionary = sp.Settings() + 71 epidermis_dictionary[Tags.PRIORITY] = 8 + 72 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] + 73 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] + 74 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 75 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 76 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 77 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 78 + 79 tissue_dict = sp.Settings() + 80 tissue_dict[Tags.BACKGROUND] = background_dictionary + 81 tissue_dict["muscle"] = muscle_dictionary + 82 tissue_dict["epidermis"] = epidermis_dictionary + 83 tissue_dict["vessel_1"] = vessel_1_dictionary + 84 return tissue_dict + 85 + 86 + 87 # Seed the numpy random configuration prior to creating the global_settings file in + 88 # order to ensure that the same volume + 89 # is generated with the same random seed every time. + 90 + 91 1 9.0 9.0 0.0 np.random.seed(RANDOM_SEED) + 92 + 93 1 0.9 0.9 0.0 general_settings = { + 94 # These parameters set the general properties of the simulated volume + 95 1 0.2 0.2 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 96 1 0.2 0.2 0.0 Tags.VOLUME_NAME: VOLUME_NAME, + 97 1 49.3 49.3 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 98 1 0.3 0.3 0.0 Tags.SPACING_MM: SPACING, + 99 1 0.1 0.1 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 100 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 101 1 0.1 0.1 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 102 1 0.9 0.9 0.0 Tags.WAVELENGTHS: [798], + 103 1 0.3 0.3 0.0 Tags.DO_FILE_COMPRESSION: True, + 104 1 0.2 0.2 0.0 Tags.GPU: True + 105 } + 106 + 107 1 15.1 15.1 0.0 settings = sp.Settings(general_settings) + 108 + 109 2 5.5 2.7 0.0 settings.set_volume_creation_settings({ + 110 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, + 111 1 23112.3 23112.3 0.2 Tags.STRUCTURES: create_example_tissue() + 112 }) + 113 2 69.9 35.0 0.0 settings.set_optical_settings({ + 114 1 0.2 0.2 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 115 1 35.5 35.5 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 116 1 1.4 1.4 0.0 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 117 1 0.3 0.3 0.0 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 118 }) + 119 1 34.6 34.6 0.0 settings["noise_model_1"] = { + 120 1 1.7 1.7 0.0 Tags.NOISE_MEAN: 1.0, + 121 1 0.3 0.3 0.0 Tags.NOISE_STD: 0.1, + 122 1 0.7 0.7 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 123 1 0.4 0.4 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 124 1 0.5 0.5 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 125 } + 126 + 127 1 0.1 0.1 0.0 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: + 128 1 0.1 0.1 0.0 pipeline = [ + 129 1 70.9 70.9 0.0 sp.ModelBasedVolumeCreationAdapter(settings), + 130 1 9.3 9.3 0.0 sp.MCXAdapter(settings), + 131 1 44.9 44.9 0.0 sp.GaussianNoise(settings, "noise_model_1") + 132 ] + 133 else: + 134 pipeline = [ + 135 sp.ModelBasedVolumeCreationAdapter(settings), + 136 sp.MCXAdapterReflectance(settings), + 137 ] + 138 + 139 + 140 1 24.4 24.4 0.0 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): + 141 """ + 142 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. + 143 + 144 """ + 145 + 146 def __init__(self): + 147 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 148 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 149 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) + 150 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], + 151 direction_vector_mm=[0, 0, 1])) + 152 + 153 + 154 1 67.0 67.0 0.0 device = ExampleDeviceSlitIlluminationLinearDetector() + 155 + 156 1 10699994.3 1e+07 99.8 sp.simulate(pipeline, settings, device) + 157 + 158 1 7.5 7.5 0.0 if Tags.WAVELENGTH in settings: + 159 1 4.1 4.1 0.0 WAVELENGTH = settings[Tags.WAVELENGTH] + 160 else: + 161 WAVELENGTH = 700 + 162 + 163 1 0.5 0.5 0.0 if visualise: + 164 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 165 wavelength=WAVELENGTH, + 166 show_initial_pressure=True, + 167 show_absorption=True, + 168 show_diffuse_reflectance=SAVE_REFLECTANCE, + 169 log_scale=True) + +Total time: 6.50607 s +File: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation_uniform_cube.py +Function: run_minimal_optical_simulation_uniform_cube at line 22 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 22 @profile + 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), + 24 visualise:bool = True): + 25 """ + 26 + 27 :param SPACING: The simulation spacing between voxels + 28 :param path_manager: the path manager to be used, typically sp.PathManager + 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 30 :return: a run through of the example + 31 """ + 32 1 0.5 0.5 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 33 1 0.3 0.3 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 + 34 1 0.2 0.2 0.0 VOLUME_HEIGHT_IN_MM = 60 + 35 1 0.4 0.4 0.0 RANDOM_SEED = 471 + 36 1 0.9 0.9 0.0 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 37 1 0.5 0.5 0.0 SAVE_REFLECTANCE = True + 38 1 0.5 0.5 0.0 SAVE_PHOTON_DIRECTION = False + 39 + 40 # If VISUALIZE is set to True, the simulation result will be plotted + 41 1 0.2 0.2 0.0 VISUALIZE = True + 42 + 43 + 44 1 0.8 0.8 0.0 def create_example_tissue(): + 45 """ + 46 This is a very simple example script of how to create a tissue definition. + 47 It contains only a generic background tissue material. + 48 """ + 49 background_dictionary = sp.Settings() + 50 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 51 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 52 + 53 tissue_dict = sp.Settings() + 54 tissue_dict[Tags.BACKGROUND] = background_dictionary + 55 return tissue_dict + 56 + 57 + 58 # Seed the numpy random configuration prior to creating the global_settings file in + 59 # order to ensure that the same volume + 60 # is generated with the same random seed every time. + 61 + 62 1 12.4 12.4 0.0 np.random.seed(RANDOM_SEED) + 63 + 64 1 1.2 1.2 0.0 general_settings = { + 65 # These parameters set the general properties of the simulated volume + 66 1 0.5 0.5 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 67 1 0.8 0.8 0.0 Tags.VOLUME_NAME: VOLUME_NAME, + 68 1 73.8 73.8 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 69 1 0.6 0.6 0.0 Tags.SPACING_MM: SPACING, + 70 1 0.4 0.4 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 71 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 72 1 0.5 0.5 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 73 1 0.5 0.5 0.0 Tags.WAVELENGTHS: [500], + 74 1 0.5 0.5 0.0 Tags.DO_FILE_COMPRESSION: True + 75 } + 76 + 77 1 18.7 18.7 0.0 settings = sp.Settings(general_settings) + 78 + 79 2 5.8 2.9 0.0 settings.set_volume_creation_settings({ + 80 1 2785.9 2785.9 0.0 Tags.STRUCTURES: create_example_tissue() + 81 }) + 82 2 84.6 42.3 0.0 settings.set_optical_settings({ + 83 1 0.4 0.4 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 84 1 47.8 47.8 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 85 1 0.4 0.4 0.0 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 86 1 0.2 0.2 0.0 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 87 }) + 88 + 89 1 0.2 0.2 0.0 pipeline = [ + 90 1 68.3 68.3 0.0 sp.ModelBasedVolumeCreationAdapter(settings), + 91 1 21.7 21.7 0.0 sp.MCXAdapterReflectance(settings), + 92 ] + 93 + 94 2 36.4 18.2 0.0 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 95 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 96 + 97 1 6502907.2 7e+06 100.0 sp.simulate(pipeline, settings, device) + 98 + 99 1 0.6 0.6 0.0 if visualise: + 100 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 101 wavelength=settings[Tags.WAVELENGTH], + 102 show_initial_pressure=True, + 103 show_absorption=True, + 104 show_diffuse_reflectance=SAVE_REFLECTANCE, + 105 log_scale=True) + +Total time: 28.1595 s +File: /home/f762e/Workspace/simpa/simpa_examples/msot_invision_simulation.py +Function: run_msot_invision_simulation at line 13 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 13 @profile + 14 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise:bool = True): + 15 """ + 16 + 17 :param SPACING: The simulation spacing between voxels + 18 :param path_manager: the path manager to be used, typically sp.PathManager + 19 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 20 :return: a run through of the example + 21 """ + 22 1 0.6 0.6 0.0 SPEED_OF_SOUND = 1500 + 23 1 0.4 0.4 0.0 XZ_DIM = 90 + 24 1 0.2 0.2 0.0 Y_DIM = 40 + 25 + 26 1 0.8 0.8 0.0 def create_pipeline(_settings: sp.Settings): + 27 return [ + 28 sp.ModelBasedVolumeCreationAdapter(settings), + 29 sp.MCXAdapter(settings), + 30 sp.KWaveAdapter(settings), + 31 sp.FieldOfViewCropping(settings), + 32 sp.TimeReversalAdapter(settings) + 33 ] + 34 + 35 + 36 1 0.4 0.4 0.0 def get_device(): + 37 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) + 38 return pa_device + 39 + 40 + 41 1 0.5 0.5 0.0 def create_volume(): + 42 inclusion_material = sp.Molecule(volume_fraction=1.0, + 43 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 44 0.9), + 45 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 46 100.0), + 47 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 48 4.0), + 49 speed_of_sound=SPEED_OF_SOUND, + 50 alpha_coefficient=1e-4, + 51 density=1000, + 52 gruneisen_parameter=1.0, + 53 name="Inclusion") + 54 + 55 phantom_material = sp.Molecule(volume_fraction=1.0, + 56 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), + 57 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 58 100.0), + 59 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), + 60 speed_of_sound=SPEED_OF_SOUND, + 61 alpha_coefficient=1e-4, + 62 density=1000, + 63 gruneisen_parameter=1.0, + 64 name="Phantom") + 65 + 66 heavy_water = sp.Molecule(volume_fraction=1.0, + 67 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), + 68 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), + 69 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), + 70 speed_of_sound=SPEED_OF_SOUND, + 71 alpha_coefficient=1e-4, + 72 density=1000, + 73 gruneisen_parameter=1.0, + 74 name="background_water") + 75 + 76 background_dictionary = sp.Settings() + 77 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 78 .append(heavy_water) + 79 .get_molecular_composition(segmentation_type=-1)) + 80 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 81 + 82 phantom_material_dictionary = sp.Settings() + 83 phantom_material_dictionary[Tags.PRIORITY] = 3 + 84 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] + 85 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 + 86 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 + 87 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 + 88 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 89 .append(phantom_material) + 90 .get_molecular_composition(segmentation_type=0)) + 91 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 92 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 93 + 94 inclusion_1_dictionary = sp.Settings() + 95 inclusion_1_dictionary[Tags.PRIORITY] = 8 + 96 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] + 97 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 + 98 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 + 99 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 + 100 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 101 .append(inclusion_material) + 102 .get_molecular_composition(segmentation_type=1)) + 103 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 104 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 105 + 106 inclusion_2_dictionary = sp.Settings() + 107 inclusion_2_dictionary[Tags.PRIORITY] = 5 + 108 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] + 109 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] + 110 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 111 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 112 .append(inclusion_material) + 113 .get_molecular_composition(segmentation_type=2)) + 114 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 115 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 116 + 117 tissue_dict = sp.Settings() + 118 tissue_dict[Tags.BACKGROUND] = background_dictionary + 119 tissue_dict["phantom"] = phantom_material_dictionary + 120 tissue_dict["inclusion_1"] = inclusion_1_dictionary + 121 tissue_dict["inclusion_2"] = inclusion_2_dictionary + 122 return { + 123 Tags.STRUCTURES: tissue_dict, + 124 Tags.SIMULATE_DEFORMED_LAYERS: False + 125 } + 126 + 127 + 128 1 0.5 0.5 0.0 def get_settings(): + 129 general_settings = { + 130 # These parameters set the general properties of the simulated volume + 131 Tags.RANDOM_SEED: 4711, + 132 Tags.VOLUME_NAME: "InVision Simulation Example", + 133 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 134 Tags.SPACING_MM: SPACING, + 135 Tags.DIM_VOLUME_Z_MM: XZ_DIM, + 136 Tags.DIM_VOLUME_X_MM: XZ_DIM, + 137 Tags.DIM_VOLUME_Y_MM: Y_DIM, + 138 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 139 Tags.GPU: True, + 140 Tags.WAVELENGTHS: [700] + 141 } + 142 + 143 volume_settings = create_volume() + 144 + 145 optical_settings = { + 146 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 147 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 148 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, + 149 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 150 } + 151 + 152 acoustic_settings = { + 153 Tags.ACOUSTIC_SIMULATION_3D: True, + 154 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 155 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 156 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 157 Tags.KWAVE_PROPERTY_PMLInside: False, + 158 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 159 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 160 Tags.KWAVE_PROPERTY_PlotPML: False, + 161 Tags.RECORDMOVIE: False, + 162 Tags.MOVIENAME: "visualization_log", + 163 Tags.ACOUSTIC_LOG_SCALE: True + 164 } + 165 + 166 reconstruction_settings = { + 167 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 168 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 169 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 170 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 171 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, + 172 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 173 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, + 174 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 175 Tags.KWAVE_PROPERTY_PMLInside: False, + 176 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 177 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 178 Tags.KWAVE_PROPERTY_PlotPML: False, + 179 Tags.RECORDMOVIE: False, + 180 Tags.MOVIENAME: "visualization_log", + 181 Tags.ACOUSTIC_LOG_SCALE: True, + 182 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 183 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 184 Tags.SPACING_MM: 0.25, + 185 } + 186 + 187 _settings = sp.Settings(general_settings) + 188 _settings.set_volume_creation_settings(volume_settings) + 189 _settings.set_optical_settings(optical_settings) + 190 _settings.set_acoustic_settings(acoustic_settings) + 191 _settings.set_reconstruction_settings(reconstruction_settings) + 192 return _settings + 193 + 194 + 195 1 291.4 291.4 0.0 device = get_device() + 196 1 628.8 628.8 0.0 settings = get_settings() + 197 1 216.4 216.4 0.0 pipeline = create_pipeline(settings) + 198 + 199 1 28158333.9 3e+07 100.0 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) + 200 + 201 1 0.1 0.1 0.0 if visualise: + 202 sp.visualise_data(settings=settings, + 203 path_manager=path_manager, + 204 show_absorption=True, + 205 show_initial_pressure=True, + 206 show_reconstructed_data=True, + 207 show_xz_only=True) + +Total time: 37.106 s +File: /home/f762e/Workspace/simpa/simpa_examples/optical_and_acoustic_simulation.py +Function: run_optical_and_acoustic_simulation at line 18 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 18 @profile + 19 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), + 20 visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 28 1 0.4 0.4 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 29 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 20 + 30 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 25 + 31 1 0.6 0.6 0.0 RANDOM_SEED = 4711 + 32 + 33 # If VISUALIZE is set to True, the simulation result will be plotted + 34 1 0.2 0.2 0.0 VISUALIZE = True + 35 + 36 1 0.5 0.5 0.0 def create_example_tissue(): + 37 """ + 38 This is a very simple example script of how to create a tissue definition. + 39 It contains a muscular background, an epidermis layer on top of the muscles + 40 and a blood vessel. + 41 """ + 42 background_dictionary = sp.Settings() + 43 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) + 44 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 45 + 46 tissue_dict = sp.Settings() + 47 tissue_dict[Tags.BACKGROUND] = background_dictionary + 48 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, + 49 molecular_composition=sp.TISSUE_LIBRARY.constant( + 50 0.05, 100, 0.9), + 51 priority=1, + 52 consider_partial_volume=True, + 53 adhere_to_deformation=True) + 54 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, + 55 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), + 56 priority=8, + 57 consider_partial_volume=True, + 58 adhere_to_deformation=True) + 59 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( + 60 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], + 61 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], + 62 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 63 radius_mm=2, priority=3, consider_partial_volume=True, + 64 adhere_to_deformation=False + 65 ) + 66 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( + 67 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], + 68 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], + 69 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 70 radius_mm=3, priority=3, consider_partial_volume=True, + 71 adhere_to_deformation=False + 72 ) + 73 return tissue_dict + 74 + 75 + 76 # Seed the numpy random configuration prior to creating the global_settings file in + 77 # order to ensure that the same volume + 78 # is generated with the same random seed every time. + 79 + 80 1 9.9 9.9 0.0 np.random.seed(RANDOM_SEED) + 81 1 0.6 0.6 0.0 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) + 82 + 83 1 1.5 1.5 0.0 general_settings = { + 84 # These parameters set the general properties of the simulated volume + 85 1 0.5 0.5 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 86 1 0.5 0.5 0.0 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), + 87 1 60.5 60.5 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 88 1 0.3 0.3 0.0 Tags.SPACING_MM: SPACING, + 89 1 0.4 0.4 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 90 1 0.3 0.3 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 91 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 92 1 0.4 0.4 0.0 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 93 1 0.3 0.3 0.0 Tags.GPU: True, + 94 1 0.3 0.3 0.0 Tags.WAVELENGTHS: [700, 800], + 95 1 0.4 0.4 0.0 Tags.DO_FILE_COMPRESSION: True, + 96 1 0.2 0.2 0.0 Tags.DO_IPASC_EXPORT: True + 97 } + 98 1 16.0 16.0 0.0 settings = sp.Settings(general_settings) + 99 1 2.2 2.2 0.0 np.random.seed(RANDOM_SEED) + 100 + 101 2 7.6 3.8 0.0 settings.set_volume_creation_settings({ + 102 1 24580.8 24580.8 0.1 Tags.STRUCTURES: create_example_tissue(), + 103 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True + 104 }) + 105 + 106 2 7.9 3.9 0.0 settings.set_optical_settings({ + 107 1 0.5 0.5 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 108 1 41.6 41.6 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 109 1 0.7 0.7 0.0 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, + 110 1 0.4 0.4 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 111 1 0.3 0.3 0.0 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, + 112 }) + 113 + 114 2 10.8 5.4 0.0 settings.set_acoustic_settings({ + 115 1 0.2 0.2 0.0 Tags.ACOUSTIC_SIMULATION_3D: False, + 116 1 33.4 33.4 0.0 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 117 1 0.4 0.4 0.0 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 118 1 0.7 0.7 0.0 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 119 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLInside: False, + 120 1 0.6 0.6 0.0 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 121 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 122 1 0.3 0.3 0.0 Tags.KWAVE_PROPERTY_PlotPML: False, + 123 1 0.2 0.2 0.0 Tags.RECORDMOVIE: False, + 124 1 0.2 0.2 0.0 Tags.MOVIENAME: "visualization_log", + 125 1 0.2 0.2 0.0 Tags.ACOUSTIC_LOG_SCALE: True + 126 }) + 127 + 128 19 130.4 6.9 0.0 settings.set_reconstruction_settings({ + 129 1 0.2 0.2 0.0 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 130 1 33.7 33.7 0.0 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 131 1 0.1 0.1 0.0 Tags.ACOUSTIC_SIMULATION_3D: False, + 132 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 133 1 0.4 0.4 0.0 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 134 1 0.5 0.5 0.0 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), + 135 1 0.4 0.4 0.0 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), + 136 1 0.2 0.2 0.0 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 137 1 0.4 0.4 0.0 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 138 1 0.5 0.5 0.0 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, + 139 1 0.3 0.3 0.0 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 140 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 141 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PMLInside: False, + 142 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 143 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 144 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PlotPML: False, + 145 1 0.1 0.1 0.0 Tags.RECORDMOVIE: False, + 146 1 0.1 0.1 0.0 Tags.MOVIENAME: "visualization_log", + 147 1 0.1 0.1 0.0 Tags.ACOUSTIC_LOG_SCALE: True, + 148 1 0.2 0.2 0.0 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, + 149 1 0.3 0.3 0.0 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, + 150 1 0.3 0.3 0.0 Tags.DATA_FIELD_DENSITY: 1000, + 151 1 0.3 0.3 0.0 Tags.SPACING_MM: SPACING + 152 }) + 153 + 154 1 33.8 33.8 0.0 settings["noise_initial_pressure"] = { + 155 1 0.4 0.4 0.0 Tags.NOISE_MEAN: 1, + 156 1 0.2 0.2 0.0 Tags.NOISE_STD: 0.01, + 157 1 0.5 0.5 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 158 1 0.3 0.3 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 159 1 0.3 0.3 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 160 } + 161 + 162 1 38.6 38.6 0.0 settings["noise_time_series"] = { + 163 1 0.1 0.1 0.0 Tags.NOISE_STD: 1, + 164 1 0.4 0.4 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, + 165 1 0.3 0.3 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA + 166 } + 167 + 168 # TODO: For the device choice, uncomment the undesired device + 169 + 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 171 # VOLUME_PLANAR_DIM_IN_MM/2, + 172 # 0])) + 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) + 174 + 175 3 10.6 3.5 0.0 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 176 1 0.2 0.2 0.0 VOLUME_PLANAR_DIM_IN_MM/2, + 177 1 0.1 0.1 0.0 0]), + 178 1 1.5 1.5 0.0 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) + 179 2 23.8 11.9 0.0 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, + 180 1 0.1 0.1 0.0 pitch_mm=0.25, + 181 1 0.1 0.1 0.0 number_detector_elements=100, + 182 1 1.0 1.0 0.0 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) + 183 1 1859.4 1859.4 0.0 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) + 184 1 40.5 40.5 0.0 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) + 185 + 186 + 187 1 0.1 0.1 0.0 SIMULATION_PIPELINE = [ + 188 1 91.1 91.1 0.0 sp.ModelBasedVolumeCreationAdapter(settings), + 189 1 8.5 8.5 0.0 sp.MCXAdapter(settings), + 190 1 50.5 50.5 0.0 sp.GaussianNoise(settings, "noise_initial_pressure"), + 191 1 4.4 4.4 0.0 sp.KWaveAdapter(settings), + 192 1 38.3 38.3 0.0 sp.GaussianNoise(settings, "noise_time_series"), + 193 1 5.1 5.1 0.0 sp.TimeReversalAdapter(settings), + 194 1 111.2 111.2 0.0 sp.FieldOfViewCropping(settings) + 195 ] + 196 + 197 1 37078767.0 4e+07 99.9 sp.simulate(SIMULATION_PIPELINE, settings, device) + 198 + 199 1 2.5 2.5 0.0 if Tags.WAVELENGTH in settings: + 200 1 1.3 1.3 0.0 WAVELENGTH = settings[Tags.WAVELENGTH] + 201 else: + 202 WAVELENGTH = 700 + 203 + 204 1 0.1 0.1 0.0 if visualise: + 205 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + 206 wavelength=WAVELENGTH, + 207 show_time_series_data=True, + 208 show_initial_pressure=True, + 209 show_reconstructed_data=True, + 210 log_scale=False, + 211 show_xz_only=False) + +Total time: 8.42102 s +File: /home/f762e/Workspace/simpa/simpa_examples/perform_iterative_qPAI_reconstruction.py +Function: run_perform_iterative_qPAI_reconstruction at line 25 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 25 @profile + 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise:bool = True): + 27 """ + 28 + 29 :param SPACING: The simulation spacing between voxels + 30 :param path_manager: the path manager to be used, typically sp.PathManager + 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 32 :return: a run through of the example + 33 """ + 34 1 0.5 0.5 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 30 + 35 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 + 36 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 30 + 37 1 0.1 0.1 0.0 RANDOM_SEED = 471 + 38 1 0.8 0.8 0.0 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) + 39 + 40 # If VISUALIZE is set to True, the reconstruction result will be plotted + 41 + 42 1 0.8 0.8 0.0 def create_example_tissue(): + 43 """ + 44 This is a very simple example script of how to create a tissue definition. + 45 It contains a muscular background, an epidermis layer on top of the muscles + 46 and a blood vessel. + 47 """ + 48 background_dictionary = sp.Settings() + 49 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) + 50 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 51 + 52 epidermis_structure = sp.Settings() + 53 epidermis_structure[Tags.PRIORITY] = 1 + 54 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] + 55 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] + 56 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) + 57 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True + 58 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True + 59 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 60 + 61 vessel_structure_1 = sp.Settings() + 62 vessel_structure_1[Tags.PRIORITY] = 2 + 63 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, + 64 VOLUME_HEIGHT_IN_MM / 2] + 65 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, + 66 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] + 67 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 + 68 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 + 69 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) + 70 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True + 71 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True + 72 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE + 73 + 74 vessel_structure_2 = sp.Settings() + 75 vessel_structure_2[Tags.PRIORITY] = 3 + 76 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, + 77 VOLUME_HEIGHT_IN_MM / 3] + 78 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, + 79 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] + 80 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 + 81 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) + 82 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True + 83 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 84 + 85 tissue_dict = sp.Settings() + 86 tissue_dict[Tags.BACKGROUND] = background_dictionary + 87 tissue_dict["epidermis"] = epidermis_structure + 88 tissue_dict["vessel_1"] = vessel_structure_1 + 89 tissue_dict["vessel_2"] = vessel_structure_2 + 90 return tissue_dict + 91 + 92 + 93 # set settings for volume creation, optical simulation and iterative qPAI method + 94 1 13.4 13.4 0.0 np.random.seed(RANDOM_SEED) + 95 + 96 1 1.0 1.0 0.0 general_settings = { + 97 # These parameters set the general properties of the simulated volume + 98 1 0.4 0.4 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 99 1 0.4 0.4 0.0 Tags.VOLUME_NAME: VOLUME_NAME, + 100 1 68.8 68.8 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 101 1 0.2 0.2 0.0 Tags.SPACING_MM: SPACING, + 102 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 103 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 104 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 105 1 0.2 0.2 0.0 Tags.WAVELENGTHS: [700] + 106 } + 107 + 108 1 14.8 14.8 0.0 settings = sp.Settings(general_settings) + 109 + 110 2 5.8 2.9 0.0 settings.set_volume_creation_settings({ + 111 # These parameters set the properties for the volume creation + 112 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, + 113 1 9341.6 9341.6 0.1 Tags.STRUCTURES: create_example_tissue() + 114 }) + 115 2 6.9 3.5 0.0 settings.set_optical_settings({ + 116 # These parameters set the properties for the optical Monte Carlo simulation + 117 1 0.8 0.8 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 118 1 36.8 36.8 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 119 1 0.8 0.8 0.0 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 120 1 0.7 0.7 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 121 }) + 122 1 39.3 39.3 0.0 settings["noise_model"] = { + 123 1 0.3 0.3 0.0 Tags.NOISE_MEAN: 1.0, + 124 1 0.2 0.2 0.0 Tags.NOISE_STD: 0.01, + 125 1 0.7 0.7 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 126 1 0.5 0.5 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 127 1 0.8 0.8 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 128 } + 129 1 42.2 42.2 0.0 settings["iterative_qpai_reconstruction"] = { + 130 # These parameters set the properties of the iterative reconstruction + 131 1 0.6 0.6 0.0 Tags.DOWNSCALE_FACTOR: 0.75, + 132 1 0.6 0.6 0.0 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, + 133 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant + 134 1 0.3 0.3 0.0 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, + 135 1 0.5 0.5 0.0 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, + 136 # for this example, we are not interested in all absorption updates + 137 1 0.5 0.5 0.0 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, + 138 1 3.7 3.7 0.0 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 + 139 } + 140 + 141 # run pipeline including iterative qPAI method + 142 1 0.1 0.1 0.0 pipeline = [ + 143 1 68.5 68.5 0.0 sp.ModelBasedVolumeCreationAdapter(settings), + 144 1 8.7 8.7 0.0 sp.MCXAdapter(settings), + 145 1 49.3 49.3 0.0 sp.GaussianNoise(settings, "noise_model"), + 146 1 15.4 15.4 0.0 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") + 147 ] + 148 + 149 + 150 1 23.4 23.4 0.0 class CustomDevice(sp.PhotoacousticDevice): + 151 + 152 def __init__(self): + 153 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, + 154 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, + 155 0])) + 156 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) + 157 + 158 + 159 1 46.7 46.7 0.0 device = CustomDevice() + 160 + 161 1 0.6 0.6 0.0 device.update_settings_for_use_of_model_based_volume_creator(settings) + 162 + 163 1 8411215.6 8e+06 99.9 sp.simulate(pipeline, settings, device) + 164 + 165 # visualize reconstruction results + 166 1 1.0 1.0 0.0 if visualise: + 167 # get simulation output + 168 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 169 settings = sp.load_data_field(data_path, Tags.SETTINGS) + 170 wavelength = settings[Tags.WAVELENGTHS][0] + 171 + 172 # get reconstruction result + 173 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) + 174 + 175 # get ground truth absorption coefficients + 176 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) + 177 + 178 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) + 179 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR + 180 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") + 181 + 182 # compute reconstruction error + 183 difference = absorption_gt - absorption_reconstruction + 184 + 185 median_error = np.median(difference) + 186 q3, q1 = np.percentile(difference, [75, 25]) + 187 iqr = q3 - q1 + 188 + 189 # visualize results + 190 x_pos = int(np.shape(absorption_gt)[0] / 2) + 191 y_pos = int(np.shape(absorption_gt)[1] / 2) + 192 + 193 if np.min(absorption_gt) > np.min(absorption_reconstruction): + 194 cmin = np.min(absorption_reconstruction) + 195 else: + 196 cmin = np.min(absorption_gt) + 197 + 198 if np.max(absorption_gt) > np.max(absorption_reconstruction): + 199 cmax = np.max(absorption_gt) + 200 else: + 201 cmax = np.max(absorption_reconstruction) + 202 + 203 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] + 204 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] + 205 + 206 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", + 207 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] + 208 + 209 plt.figure(figsize=(20, 15)) + 210 plt.subplots_adjust(hspace=0.5) + 211 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + + 212 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) + 213 + 214 for i, quantity in enumerate(results_x_z): + 215 plt.subplot(2, len(results_x_z), i + 1) + 216 if i == 0: + 217 plt.ylabel("x-z", fontsize=10) + 218 plt.title(label[i], fontsize=10) + 219 plt.imshow(quantity.T) + 220 plt.xticks(fontsize=6) + 221 plt.yticks(fontsize=6) + 222 plt.colorbar() + 223 if i != 2: + 224 plt.clim(cmin, cmax) + 225 else: + 226 plt.clim(np.min(difference), np.max(difference)) + 227 + 228 for i, quantity in enumerate(results_y_z): + 229 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) + 230 if i == 0: + 231 plt.ylabel("y-z", fontsize=10) + 232 plt.title(label[i], fontsize=10) + 233 plt.imshow(quantity.T) + 234 plt.xticks(fontsize=6) + 235 plt.yticks(fontsize=6) + 236 plt.colorbar() + 237 if i != 2: + 238 plt.clim(cmin, cmax) + 239 else: + 240 plt.clim(np.min(difference), np.max(difference)) + 241 + 242 plt.show() + 243 plt.close() + diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.4.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.4.txt new file mode 100644 index 00000000..a519b662 --- /dev/null +++ b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.4.txt @@ -0,0 +1,1048 @@ +Timer unit: 1e-06 s + +Total time: 4.93008 s +File: /home/f762e/Workspace/simpa/simpa_examples/linear_unmixing.py +Function: run_linear_unmixing at line 18 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 18 @profile + 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): + 20 """ + 21 + 22 :param SPACING: The simulation spacing between voxels + 23 :param path_manager: the path manager to be used, typically sp.PathManager + 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 25 :return: a run through of the example + 26 """ + 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you + 28 # set global params characterizing the simulated volume + 29 1 0.5 0.5 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 30 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 20 + 31 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 25 + 32 1 0.1 0.1 0.0 RANDOM_SEED = 471 + 33 1 0.6 0.6 0.0 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) + 34 + 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths + 36 1 0.2 0.2 0.0 WAVELENGTHS = [750, 800, 850] + 37 + 38 1 0.3 0.3 0.0 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and two blood vessels. + 43 """ + 44 background_dictionary = sp.Settings() + 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 muscle_dictionary = sp.Settings() + 49 muscle_dictionary[Tags.PRIORITY] = 1 + 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 vessel_1_dictionary = sp.Settings() + 58 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 10, + 61 5] + 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 12, + 64 5] + 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) + 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 vessel_2_dictionary = sp.Settings() + 71 vessel_2_dictionary[Tags.PRIORITY] = 3 + 72 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 73 10, + 74 5] + 75 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, + 76 12, + 77 5] + 78 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 79 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) + 80 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 81 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 82 + 83 epidermis_dictionary = sp.Settings() + 84 epidermis_dictionary[Tags.PRIORITY] = 8 + 85 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] + 86 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] + 87 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 88 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 89 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 90 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 91 + 92 tissue_dict = sp.Settings() + 93 tissue_dict[Tags.BACKGROUND] = background_dictionary + 94 tissue_dict["muscle"] = muscle_dictionary + 95 tissue_dict["epidermis"] = epidermis_dictionary + 96 tissue_dict["vessel_1"] = vessel_1_dictionary + 97 tissue_dict["vessel_2"] = vessel_2_dictionary + 98 return tissue_dict + 99 + 100 + 101 # Seed the numpy random configuration prior to creating the global_settings file in + 102 # order to ensure that the same volume is generated with the same random seed every time. + 103 1 16.0 16.0 0.0 np.random.seed(RANDOM_SEED) + 104 + 105 # Initialize global settings and prepare for simulation pipeline including + 106 # volume creation and optical forward simulation. + 107 1 1.5 1.5 0.0 general_settings = { + 108 # These parameters set the general properties of the simulated volume + 109 1 1.0 1.0 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 110 1 0.5 0.5 0.0 Tags.VOLUME_NAME: VOLUME_NAME, + 111 1 54.7 54.7 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 112 1 0.4 0.4 0.0 Tags.SPACING_MM: SPACING, + 113 1 0.5 0.5 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 114 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 115 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 116 1 0.3 0.3 0.0 Tags.WAVELENGTHS: WAVELENGTHS, + 117 1 0.5 0.5 0.0 Tags.GPU: True, + 118 1 0.3 0.3 0.0 Tags.DO_FILE_COMPRESSION: True + 119 } + 120 1 69.9 69.9 0.0 settings = sp.Settings(general_settings) + 121 2 6.5 3.2 0.0 settings.set_volume_creation_settings({ + 122 1 0.4 0.4 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, + 123 1 31044.7 31044.7 0.6 Tags.STRUCTURES: create_example_tissue() + 124 }) + 125 2 7.3 3.7 0.0 settings.set_optical_settings({ + 126 1 0.4 0.4 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 127 1 36.6 36.6 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 128 1 0.6 0.6 0.0 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 129 1 0.2 0.2 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 130 }) + 131 + 132 # Set component settings for linear unmixing. + 133 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the + 134 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. + 135 # Please take a look at the component for more information. + 136 1 60.4 60.4 0.0 settings["linear_unmixing"] = { + 137 1 0.7 0.7 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 138 1 0.2 0.2 0.0 Tags.WAVELENGTHS: WAVELENGTHS, + 139 2 1961.6 980.8 0.0 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( + 140 1 0.4 0.4 0.0 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] + 141 ), + 142 1 0.5 0.5 0.0 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, + 143 1 0.3 0.3 0.0 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True + 144 } + 145 + 146 # Get device for simulation + 147 2 141.7 70.9 0.0 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 148 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM/2, + 149 1 0.1 0.1 0.0 0])) + 150 1 4495.9 4495.9 0.1 device.update_settings_for_use_of_model_based_volume_creator(settings) + 151 + 152 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS + 153 1 0.2 0.2 0.0 pipeline = [ + 154 1 14382.3 14382.3 0.3 sp.ModelBasedVolumeCreationAdapter(settings), + 155 1 13.3 13.3 0.0 sp.MCXAdapter(settings), + 156 1 146.7 146.7 0.0 sp.FieldOfViewCropping(settings), + 157 ] + 158 1 3958047.6 4e+06 80.3 sp.simulate(pipeline, settings, device) + 159 + 160 # Run linear unmixing component with above specified settings. + 161 1 917597.9 917597.9 18.6 sp.LinearUnmixing(settings, "linear_unmixing").run() + 162 + 163 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. + 164 1 56.3 56.3 0.0 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 165 1 825.0 825.0 0.0 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) + 166 1 0.3 0.3 0.0 sO2 = lu_results["sO2"] + 167 + 168 1 328.3 328.3 0.0 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) + 169 1 477.8 477.8 0.0 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) + 170 1 298.7 298.7 0.0 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) + 171 + 172 # Visualize linear unmixing result + 173 1 0.2 0.2 0.0 if visualise: + 174 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 175 wavelength=WAVELENGTHS[0], + 176 show_initial_pressure=True, + 177 show_oxygenation=True, + 178 show_linear_unmixing_sO2=True) + +Total time: 2.86975 s +File: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation.py +Function: run_minimal_optical_simulation at line 19 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 19 @profile + 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 28 1 0.3 0.3 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 29 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 + 30 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 60 + 31 1 0.3 0.3 0.0 RANDOM_SEED = 471 + 32 1 0.6 0.6 0.0 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 33 1 0.1 0.1 0.0 SAVE_REFLECTANCE = False + 34 1 0.1 0.1 0.0 SAVE_PHOTON_DIRECTION = False + 35 + 36 # If VISUALIZE is set to True, the simulation result will be plotted + 37 + 38 1 2.3 2.3 0.0 def create_example_tissue(): + 39 """ + 40 This is a very simple example script of how to create a tissue definition. + 41 It contains a muscular background, an epidermis layer on top of the muscles + 42 and a blood vessel. + 43 """ + 44 background_dictionary = sp.Settings() + 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 47 + 48 muscle_dictionary = sp.Settings() + 49 muscle_dictionary[Tags.PRIORITY] = 1 + 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] + 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] + 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() + 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 56 + 57 vessel_1_dictionary = sp.Settings() + 58 vessel_1_dictionary[Tags.PRIORITY] = 3 + 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 60 10, + 61 VOLUME_HEIGHT_IN_MM/2] + 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, + 63 12, + 64 VOLUME_HEIGHT_IN_MM/2] + 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 + 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() + 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 69 + 70 epidermis_dictionary = sp.Settings() + 71 epidermis_dictionary[Tags.PRIORITY] = 8 + 72 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] + 73 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] + 74 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() + 75 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True + 76 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True + 77 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 78 + 79 tissue_dict = sp.Settings() + 80 tissue_dict[Tags.BACKGROUND] = background_dictionary + 81 tissue_dict["muscle"] = muscle_dictionary + 82 tissue_dict["epidermis"] = epidermis_dictionary + 83 tissue_dict["vessel_1"] = vessel_1_dictionary + 84 return tissue_dict + 85 + 86 + 87 # Seed the numpy random configuration prior to creating the global_settings file in + 88 # order to ensure that the same volume + 89 # is generated with the same random seed every time. + 90 + 91 1 12.0 12.0 0.0 np.random.seed(RANDOM_SEED) + 92 + 93 1 1.0 1.0 0.0 general_settings = { + 94 # These parameters set the general properties of the simulated volume + 95 1 0.3 0.3 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 96 1 0.3 0.3 0.0 Tags.VOLUME_NAME: VOLUME_NAME, + 97 1 52.2 52.2 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 98 1 0.4 0.4 0.0 Tags.SPACING_MM: SPACING, + 99 1 0.2 0.2 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 100 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 101 1 0.1 0.1 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 102 1 0.3 0.3 0.0 Tags.WAVELENGTHS: [798], + 103 1 0.3 0.3 0.0 Tags.DO_FILE_COMPRESSION: True, + 104 1 0.2 0.2 0.0 Tags.GPU: True + 105 } + 106 + 107 1 15.1 15.1 0.0 settings = sp.Settings(general_settings) + 108 + 109 2 6.1 3.0 0.0 settings.set_volume_creation_settings({ + 110 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, + 111 1 24443.6 24443.6 0.9 Tags.STRUCTURES: create_example_tissue() + 112 }) + 113 2 68.8 34.4 0.0 settings.set_optical_settings({ + 114 1 0.3 0.3 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 115 1 34.9 34.9 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 116 1 0.4 0.4 0.0 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 117 1 0.3 0.3 0.0 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 118 }) + 119 1 35.0 35.0 0.0 settings["noise_model_1"] = { + 120 1 0.4 0.4 0.0 Tags.NOISE_MEAN: 1.0, + 121 1 0.9 0.9 0.0 Tags.NOISE_STD: 0.1, + 122 1 0.6 0.6 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 123 1 0.5 0.5 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 124 1 0.3 0.3 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 125 } + 126 + 127 1 0.1 0.1 0.0 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: + 128 1 0.2 0.2 0.0 pipeline = [ + 129 1 69.8 69.8 0.0 sp.ModelBasedVolumeCreationAdapter(settings), + 130 1 10.0 10.0 0.0 sp.MCXAdapter(settings), + 131 1 46.4 46.4 0.0 sp.GaussianNoise(settings, "noise_model_1") + 132 ] + 133 else: + 134 pipeline = [ + 135 sp.ModelBasedVolumeCreationAdapter(settings), + 136 sp.MCXAdapterReflectance(settings), + 137 ] + 138 + 139 + 140 1 25.3 25.3 0.0 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): + 141 """ + 142 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. + 143 + 144 """ + 145 + 146 def __init__(self): + 147 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 148 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 149 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) + 150 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], + 151 direction_vector_mm=[0, 0, 1])) + 152 + 153 + 154 1 65.2 65.2 0.0 device = ExampleDeviceSlitIlluminationLinearDetector() + 155 + 156 1 2844852.2 3e+06 99.1 sp.simulate(pipeline, settings, device) + 157 + 158 1 3.7 3.7 0.0 if Tags.WAVELENGTH in settings: + 159 1 2.0 2.0 0.0 WAVELENGTH = settings[Tags.WAVELENGTH] + 160 else: + 161 WAVELENGTH = 700 + 162 + 163 1 0.2 0.2 0.0 if visualise: + 164 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 165 wavelength=WAVELENGTH, + 166 show_initial_pressure=True, + 167 show_absorption=True, + 168 show_diffuse_reflectance=SAVE_REFLECTANCE, + 169 log_scale=True) + +Total time: 1.76131 s +File: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation_uniform_cube.py +Function: run_minimal_optical_simulation_uniform_cube at line 22 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 22 @profile + 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), + 24 visualise:bool = True): + 25 """ + 26 + 27 :param SPACING: The simulation spacing between voxels + 28 :param path_manager: the path manager to be used, typically sp.PathManager + 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 30 :return: a run through of the example + 31 """ + 32 1 0.4 0.4 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 60 + 33 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 + 34 1 0.3 0.3 0.0 VOLUME_HEIGHT_IN_MM = 60 + 35 1 2.4 2.4 0.0 RANDOM_SEED = 471 + 36 1 0.6 0.6 0.0 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) + 37 1 0.2 0.2 0.0 SAVE_REFLECTANCE = True + 38 1 0.1 0.1 0.0 SAVE_PHOTON_DIRECTION = False + 39 + 40 # If VISUALIZE is set to True, the simulation result will be plotted + 41 1 0.1 0.1 0.0 VISUALIZE = True + 42 + 43 + 44 1 0.6 0.6 0.0 def create_example_tissue(): + 45 """ + 46 This is a very simple example script of how to create a tissue definition. + 47 It contains only a generic background tissue material. + 48 """ + 49 background_dictionary = sp.Settings() + 50 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) + 51 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 52 + 53 tissue_dict = sp.Settings() + 54 tissue_dict[Tags.BACKGROUND] = background_dictionary + 55 return tissue_dict + 56 + 57 + 58 # Seed the numpy random configuration prior to creating the global_settings file in + 59 # order to ensure that the same volume + 60 # is generated with the same random seed every time. + 61 + 62 1 11.4 11.4 0.0 np.random.seed(RANDOM_SEED) + 63 + 64 1 0.9 0.9 0.0 general_settings = { + 65 # These parameters set the general properties of the simulated volume + 66 1 0.8 0.8 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 67 1 0.3 0.3 0.0 Tags.VOLUME_NAME: VOLUME_NAME, + 68 1 52.4 52.4 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 69 1 1.0 1.0 0.0 Tags.SPACING_MM: SPACING, + 70 1 0.2 0.2 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 71 1 0.3 0.3 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 72 1 0.2 0.2 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 73 1 0.3 0.3 0.0 Tags.WAVELENGTHS: [500], + 74 1 0.4 0.4 0.0 Tags.DO_FILE_COMPRESSION: True + 75 } + 76 + 77 1 14.5 14.5 0.0 settings = sp.Settings(general_settings) + 78 + 79 2 4.9 2.5 0.0 settings.set_volume_creation_settings({ + 80 1 2299.6 2299.6 0.1 Tags.STRUCTURES: create_example_tissue() + 81 }) + 82 2 71.8 35.9 0.0 settings.set_optical_settings({ + 83 1 0.3 0.3 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, + 84 1 37.5 37.5 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 85 1 0.4 0.4 0.0 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, + 86 1 0.2 0.2 0.0 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION + 87 }) + 88 + 89 1 0.1 0.1 0.0 pipeline = [ + 90 1 68.3 68.3 0.0 sp.ModelBasedVolumeCreationAdapter(settings), + 91 1 21.3 21.3 0.0 sp.MCXAdapterReflectance(settings), + 92 ] + 93 + 94 2 30.9 15.5 0.0 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 95 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM/2, 0])) + 96 + 97 1 1758689.2 2e+06 99.9 sp.simulate(pipeline, settings, device) + 98 + 99 1 0.3 0.3 0.0 if visualise: + 100 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", + 101 wavelength=settings[Tags.WAVELENGTH], + 102 show_initial_pressure=True, + 103 show_absorption=True, + 104 show_diffuse_reflectance=SAVE_REFLECTANCE, + 105 log_scale=True) + +Total time: 18.452 s +File: /home/f762e/Workspace/simpa/simpa_examples/msot_invision_simulation.py +Function: run_msot_invision_simulation at line 13 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 13 @profile + 14 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise:bool = True): + 15 """ + 16 + 17 :param SPACING: The simulation spacing between voxels + 18 :param path_manager: the path manager to be used, typically sp.PathManager + 19 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 20 :return: a run through of the example + 21 """ + 22 1 0.3 0.3 0.0 SPEED_OF_SOUND = 1500 + 23 1 0.2 0.2 0.0 XZ_DIM = 90 + 24 1 0.2 0.2 0.0 Y_DIM = 40 + 25 + 26 1 0.8 0.8 0.0 def create_pipeline(_settings: sp.Settings): + 27 return [ + 28 sp.ModelBasedVolumeCreationAdapter(settings), + 29 sp.MCXAdapter(settings), + 30 sp.KWaveAdapter(settings), + 31 sp.FieldOfViewCropping(settings), + 32 sp.TimeReversalAdapter(settings) + 33 ] + 34 + 35 + 36 1 0.7 0.7 0.0 def get_device(): + 37 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) + 38 return pa_device + 39 + 40 + 41 1 0.3 0.3 0.0 def create_volume(): + 42 inclusion_material = sp.Molecule(volume_fraction=1.0, + 43 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 44 0.9), + 45 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 46 100.0), + 47 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 48 4.0), + 49 speed_of_sound=SPEED_OF_SOUND, + 50 alpha_coefficient=1e-4, + 51 density=1000, + 52 gruneisen_parameter=1.0, + 53 name="Inclusion") + 54 + 55 phantom_material = sp.Molecule(volume_fraction=1.0, + 56 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), + 57 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( + 58 100.0), + 59 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), + 60 speed_of_sound=SPEED_OF_SOUND, + 61 alpha_coefficient=1e-4, + 62 density=1000, + 63 gruneisen_parameter=1.0, + 64 name="Phantom") + 65 + 66 heavy_water = sp.Molecule(volume_fraction=1.0, + 67 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), + 68 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), + 69 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), + 70 speed_of_sound=SPEED_OF_SOUND, + 71 alpha_coefficient=1e-4, + 72 density=1000, + 73 gruneisen_parameter=1.0, + 74 name="background_water") + 75 + 76 background_dictionary = sp.Settings() + 77 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 78 .append(heavy_water) + 79 .get_molecular_composition(segmentation_type=-1)) + 80 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 81 + 82 phantom_material_dictionary = sp.Settings() + 83 phantom_material_dictionary[Tags.PRIORITY] = 3 + 84 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] + 85 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 + 86 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 + 87 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 + 88 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 89 .append(phantom_material) + 90 .get_molecular_composition(segmentation_type=0)) + 91 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 92 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 93 + 94 inclusion_1_dictionary = sp.Settings() + 95 inclusion_1_dictionary[Tags.PRIORITY] = 8 + 96 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] + 97 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 + 98 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 + 99 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 + 100 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 101 .append(inclusion_material) + 102 .get_molecular_composition(segmentation_type=1)) + 103 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 104 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE + 105 + 106 inclusion_2_dictionary = sp.Settings() + 107 inclusion_2_dictionary[Tags.PRIORITY] = 5 + 108 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] + 109 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] + 110 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 + 111 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() + 112 .append(inclusion_material) + 113 .get_molecular_composition(segmentation_type=2)) + 114 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False + 115 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 116 + 117 tissue_dict = sp.Settings() + 118 tissue_dict[Tags.BACKGROUND] = background_dictionary + 119 tissue_dict["phantom"] = phantom_material_dictionary + 120 tissue_dict["inclusion_1"] = inclusion_1_dictionary + 121 tissue_dict["inclusion_2"] = inclusion_2_dictionary + 122 return { + 123 Tags.STRUCTURES: tissue_dict, + 124 Tags.SIMULATE_DEFORMED_LAYERS: False + 125 } + 126 + 127 + 128 1 0.3 0.3 0.0 def get_settings(): + 129 general_settings = { + 130 # These parameters set the general properties of the simulated volume + 131 Tags.RANDOM_SEED: 4711, + 132 Tags.VOLUME_NAME: "InVision Simulation Example", + 133 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 134 Tags.SPACING_MM: SPACING, + 135 Tags.DIM_VOLUME_Z_MM: XZ_DIM, + 136 Tags.DIM_VOLUME_X_MM: XZ_DIM, + 137 Tags.DIM_VOLUME_Y_MM: Y_DIM, + 138 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 139 Tags.GPU: True, + 140 Tags.WAVELENGTHS: [700] + 141 } + 142 + 143 volume_settings = create_volume() + 144 + 145 optical_settings = { + 146 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 147 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 148 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, + 149 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 150 } + 151 + 152 acoustic_settings = { + 153 Tags.ACOUSTIC_SIMULATION_3D: True, + 154 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 155 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 156 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 157 Tags.KWAVE_PROPERTY_PMLInside: False, + 158 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 159 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 160 Tags.KWAVE_PROPERTY_PlotPML: False, + 161 Tags.RECORDMOVIE: False, + 162 Tags.MOVIENAME: "visualization_log", + 163 Tags.ACOUSTIC_LOG_SCALE: True + 164 } + 165 + 166 reconstruction_settings = { + 167 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 168 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 169 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 170 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 171 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, + 172 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 173 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, + 174 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 175 Tags.KWAVE_PROPERTY_PMLInside: False, + 176 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 177 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 178 Tags.KWAVE_PROPERTY_PlotPML: False, + 179 Tags.RECORDMOVIE: False, + 180 Tags.MOVIENAME: "visualization_log", + 181 Tags.ACOUSTIC_LOG_SCALE: True, + 182 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 183 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 184 Tags.SPACING_MM: 0.25, + 185 } + 186 + 187 _settings = sp.Settings(general_settings) + 188 _settings.set_volume_creation_settings(volume_settings) + 189 _settings.set_optical_settings(optical_settings) + 190 _settings.set_acoustic_settings(acoustic_settings) + 191 _settings.set_reconstruction_settings(reconstruction_settings) + 192 return _settings + 193 + 194 + 195 1 251.3 251.3 0.0 device = get_device() + 196 1 620.1 620.1 0.0 settings = get_settings() + 197 1 213.8 213.8 0.0 pipeline = create_pipeline(settings) + 198 + 199 1 18450957.5 2e+07 100.0 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) + 200 + 201 1 0.3 0.3 0.0 if visualise: + 202 sp.visualise_data(settings=settings, + 203 path_manager=path_manager, + 204 show_absorption=True, + 205 show_initial_pressure=True, + 206 show_reconstructed_data=True, + 207 show_xz_only=True) + +Total time: 9.57549 s +File: /home/f762e/Workspace/simpa/simpa_examples/optical_and_acoustic_simulation.py +Function: run_optical_and_acoustic_simulation at line 18 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 18 @profile + 19 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), + 20 visualise: bool = True): + 21 """ + 22 + 23 :param SPACING: The simulation spacing between voxels + 24 :param path_manager: the path manager to be used, typically sp.PathManager + 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 26 :return: a run through of the example + 27 """ + 28 1 0.3 0.3 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 75 + 29 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 20 + 30 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 25 + 31 1 0.1 0.1 0.0 RANDOM_SEED = 4711 + 32 + 33 # If VISUALIZE is set to True, the simulation result will be plotted + 34 1 0.1 0.1 0.0 VISUALIZE = True + 35 + 36 1 0.8 0.8 0.0 def create_example_tissue(): + 37 """ + 38 This is a very simple example script of how to create a tissue definition. + 39 It contains a muscular background, an epidermis layer on top of the muscles + 40 and a blood vessel. + 41 """ + 42 background_dictionary = sp.Settings() + 43 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) + 44 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 45 + 46 tissue_dict = sp.Settings() + 47 tissue_dict[Tags.BACKGROUND] = background_dictionary + 48 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, + 49 molecular_composition=sp.TISSUE_LIBRARY.constant( + 50 0.05, 100, 0.9), + 51 priority=1, + 52 consider_partial_volume=True, + 53 adhere_to_deformation=True) + 54 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, + 55 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), + 56 priority=8, + 57 consider_partial_volume=True, + 58 adhere_to_deformation=True) + 59 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( + 60 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], + 61 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], + 62 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 63 radius_mm=2, priority=3, consider_partial_volume=True, + 64 adhere_to_deformation=False + 65 ) + 66 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( + 67 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], + 68 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], + 69 molecular_composition=sp.TISSUE_LIBRARY.blood(), + 70 radius_mm=3, priority=3, consider_partial_volume=True, + 71 adhere_to_deformation=False + 72 ) + 73 return tissue_dict + 74 + 75 + 76 # Seed the numpy random configuration prior to creating the global_settings file in + 77 # order to ensure that the same volume + 78 # is generated with the same random seed every time. + 79 + 80 1 12.7 12.7 0.0 np.random.seed(RANDOM_SEED) + 81 1 0.6 0.6 0.0 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) + 82 + 83 1 1.5 1.5 0.0 general_settings = { + 84 # These parameters set the general properties of the simulated volume + 85 1 0.4 0.4 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 86 1 0.6 0.6 0.0 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), + 87 1 54.4 54.4 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 88 1 0.8 0.8 0.0 Tags.SPACING_MM: SPACING, + 89 1 0.2 0.2 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 90 1 0.3 0.3 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 91 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 92 1 0.9 0.9 0.0 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, + 93 1 0.4 0.4 0.0 Tags.GPU: True, + 94 1 0.4 0.4 0.0 Tags.WAVELENGTHS: [700, 800], + 95 1 0.2 0.2 0.0 Tags.DO_FILE_COMPRESSION: True, + 96 1 0.4 0.4 0.0 Tags.DO_IPASC_EXPORT: True + 97 } + 98 1 19.6 19.6 0.0 settings = sp.Settings(general_settings) + 99 1 2.4 2.4 0.0 np.random.seed(RANDOM_SEED) + 100 + 101 2 8.7 4.4 0.0 settings.set_volume_creation_settings({ + 102 1 24130.3 24130.3 0.3 Tags.STRUCTURES: create_example_tissue(), + 103 1 0.5 0.5 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True + 104 }) + 105 + 106 2 8.7 4.4 0.0 settings.set_optical_settings({ + 107 1 0.3 0.3 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 108 1 46.8 46.8 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 109 1 1.0 1.0 0.0 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, + 110 1 0.2 0.2 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + 111 1 0.3 0.3 0.0 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, + 112 }) + 113 + 114 2 14.3 7.1 0.0 settings.set_acoustic_settings({ + 115 1 0.2 0.2 0.0 Tags.ACOUSTIC_SIMULATION_3D: False, + 116 1 35.7 35.7 0.0 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 117 1 0.4 0.4 0.0 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 118 1 0.3 0.3 0.0 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 119 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLInside: False, + 120 1 0.4 0.4 0.0 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 121 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 122 1 0.3 0.3 0.0 Tags.KWAVE_PROPERTY_PlotPML: False, + 123 1 0.3 0.3 0.0 Tags.RECORDMOVIE: False, + 124 1 0.4 0.4 0.0 Tags.MOVIENAME: "visualization_log", + 125 1 0.3 0.3 0.0 Tags.ACOUSTIC_LOG_SCALE: True + 126 }) + 127 + 128 19 129.9 6.8 0.0 settings.set_reconstruction_settings({ + 129 1 0.7 0.7 0.0 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, + 130 1 37.8 37.8 0.0 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + 131 1 0.1 0.1 0.0 Tags.ACOUSTIC_SIMULATION_3D: False, + 132 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + 133 1 0.7 0.7 0.0 Tags.TUKEY_WINDOW_ALPHA: 0.5, + 134 1 0.4 0.4 0.0 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), + 135 1 0.4 0.4 0.0 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), + 136 1 0.1 0.1 0.0 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, + 137 1 0.5 0.5 0.0 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, + 138 1 1.0 1.0 0.0 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, + 139 1 0.5 0.5 0.0 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, + 140 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + 141 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PMLInside: False, + 142 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + 143 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + 144 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PlotPML: False, + 145 1 0.1 0.1 0.0 Tags.RECORDMOVIE: False, + 146 1 0.1 0.1 0.0 Tags.MOVIENAME: "visualization_log", + 147 1 0.1 0.1 0.0 Tags.ACOUSTIC_LOG_SCALE: True, + 148 1 0.3 0.3 0.0 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, + 149 1 0.2 0.2 0.0 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, + 150 1 0.2 0.2 0.0 Tags.DATA_FIELD_DENSITY: 1000, + 151 1 0.3 0.3 0.0 Tags.SPACING_MM: SPACING + 152 }) + 153 + 154 1 34.3 34.3 0.0 settings["noise_initial_pressure"] = { + 155 1 0.3 0.3 0.0 Tags.NOISE_MEAN: 1, + 156 1 0.2 0.2 0.0 Tags.NOISE_STD: 0.01, + 157 1 0.4 0.4 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 158 1 0.6 0.6 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 159 1 0.2 0.2 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 160 } + 161 + 162 1 33.1 33.1 0.0 settings["noise_time_series"] = { + 163 1 0.1 0.1 0.0 Tags.NOISE_STD: 1, + 164 1 0.5 0.5 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, + 165 1 0.2 0.2 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA + 166 } + 167 + 168 # TODO: For the device choice, uncomment the undesired device + 169 + 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 171 # VOLUME_PLANAR_DIM_IN_MM/2, + 172 # 0])) + 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) + 174 + 175 3 11.1 3.7 0.0 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, + 176 1 0.4 0.4 0.0 VOLUME_PLANAR_DIM_IN_MM/2, + 177 1 0.1 0.1 0.0 0]), + 178 1 1.3 1.3 0.0 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) + 179 2 26.6 13.3 0.0 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, + 180 1 0.1 0.1 0.0 pitch_mm=0.25, + 181 1 0.1 0.1 0.0 number_detector_elements=100, + 182 1 0.8 0.8 0.0 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) + 183 1 1748.6 1748.6 0.0 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) + 184 1 36.8 36.8 0.0 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) + 185 + 186 + 187 1 0.2 0.2 0.0 SIMULATION_PIPELINE = [ + 188 1 93.7 93.7 0.0 sp.ModelBasedVolumeCreationAdapter(settings), + 189 1 9.9 9.9 0.0 sp.MCXAdapter(settings), + 190 1 50.1 50.1 0.0 sp.GaussianNoise(settings, "noise_initial_pressure"), + 191 1 5.3 5.3 0.0 sp.KWaveAdapter(settings), + 192 1 46.5 46.5 0.0 sp.GaussianNoise(settings, "noise_time_series"), + 193 1 5.4 5.4 0.0 sp.TimeReversalAdapter(settings), + 194 1 125.6 125.6 0.0 sp.FieldOfViewCropping(settings) + 195 ] + 196 + 197 1 9548734.8 1e+07 99.7 sp.simulate(SIMULATION_PIPELINE, settings, device) + 198 + 199 if Tags.WAVELENGTH in settings: + 200 WAVELENGTH = settings[Tags.WAVELENGTH] + 201 else: + 202 WAVELENGTH = 700 + 203 + 204 if visualise: + 205 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], + 206 wavelength=WAVELENGTH, + 207 show_time_series_data=True, + 208 show_initial_pressure=True, + 209 show_reconstructed_data=True, + 210 log_scale=False, + 211 show_xz_only=False) + +Total time: 10.4913 s +File: /home/f762e/Workspace/simpa/simpa_examples/perform_iterative_qPAI_reconstruction.py +Function: run_perform_iterative_qPAI_reconstruction at line 25 + +Line # Hits Time Per Hit % Time Line Contents +============================================================== + 25 @profile + 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise:bool = True): + 27 """ + 28 + 29 :param SPACING: The simulation spacing between voxels + 30 :param path_manager: the path manager to be used, typically sp.PathManager + 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + 32 :return: a run through of the example + 33 """ + 34 1 0.3 0.3 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 30 + 35 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 + 36 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 30 + 37 1 0.1 0.1 0.0 RANDOM_SEED = 471 + 38 1 0.6 0.6 0.0 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) + 39 + 40 # If VISUALIZE is set to True, the reconstruction result will be plotted + 41 + 42 1 0.5 0.5 0.0 def create_example_tissue(): + 43 """ + 44 This is a very simple example script of how to create a tissue definition. + 45 It contains a muscular background, an epidermis layer on top of the muscles + 46 and a blood vessel. + 47 """ + 48 background_dictionary = sp.Settings() + 49 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) + 50 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + 51 + 52 epidermis_structure = sp.Settings() + 53 epidermis_structure[Tags.PRIORITY] = 1 + 54 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] + 55 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] + 56 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) + 57 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True + 58 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True + 59 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE + 60 + 61 vessel_structure_1 = sp.Settings() + 62 vessel_structure_1[Tags.PRIORITY] = 2 + 63 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, + 64 VOLUME_HEIGHT_IN_MM / 2] + 65 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, + 66 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] + 67 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 + 68 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 + 69 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) + 70 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True + 71 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True + 72 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE + 73 + 74 vessel_structure_2 = sp.Settings() + 75 vessel_structure_2[Tags.PRIORITY] = 3 + 76 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, + 77 VOLUME_HEIGHT_IN_MM / 3] + 78 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, + 79 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] + 80 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 + 81 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) + 82 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True + 83 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE + 84 + 85 tissue_dict = sp.Settings() + 86 tissue_dict[Tags.BACKGROUND] = background_dictionary + 87 tissue_dict["epidermis"] = epidermis_structure + 88 tissue_dict["vessel_1"] = vessel_structure_1 + 89 tissue_dict["vessel_2"] = vessel_structure_2 + 90 return tissue_dict + 91 + 92 + 93 # set settings for volume creation, optical simulation and iterative qPAI method + 94 1 11.9 11.9 0.0 np.random.seed(RANDOM_SEED) + 95 + 96 1 0.9 0.9 0.0 general_settings = { + 97 # These parameters set the general properties of the simulated volume + 98 1 0.4 0.4 0.0 Tags.RANDOM_SEED: RANDOM_SEED, + 99 1 0.2 0.2 0.0 Tags.VOLUME_NAME: VOLUME_NAME, + 100 1 65.3 65.3 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + 101 1 0.7 0.7 0.0 Tags.SPACING_MM: SPACING, + 102 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, + 103 1 0.3 0.3 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, + 104 1 0.1 0.1 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, + 105 1 0.2 0.2 0.0 Tags.WAVELENGTHS: [700] + 106 } + 107 + 108 1 15.7 15.7 0.0 settings = sp.Settings(general_settings) + 109 + 110 2 8.8 4.4 0.0 settings.set_volume_creation_settings({ + 111 # These parameters set the properties for the volume creation + 112 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, + 113 1 9337.6 9337.6 0.1 Tags.STRUCTURES: create_example_tissue() + 114 }) + 115 2 10.2 5.1 0.0 settings.set_optical_settings({ + 116 # These parameters set the properties for the optical Monte Carlo simulation + 117 1 0.6 0.6 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + 118 1 45.7 45.7 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + 119 1 1.0 1.0 0.0 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + 120 1 0.6 0.6 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 + 121 }) + 122 1 38.7 38.7 0.0 settings["noise_model"] = { + 123 1 0.3 0.3 0.0 Tags.NOISE_MEAN: 1.0, + 124 1 0.3 0.3 0.0 Tags.NOISE_STD: 0.01, + 125 1 0.5 0.5 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, + 126 1 0.5 0.5 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, + 127 1 0.3 0.3 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True + 128 } + 129 1 42.5 42.5 0.0 settings["iterative_qpai_reconstruction"] = { + 130 # These parameters set the properties of the iterative reconstruction + 131 1 0.6 0.6 0.0 Tags.DOWNSCALE_FACTOR: 0.75, + 132 1 0.5 0.5 0.0 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, + 133 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant + 134 1 0.3 0.3 0.0 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, + 135 1 0.4 0.4 0.0 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, + 136 # for this example, we are not interested in all absorption updates + 137 1 0.6 0.6 0.0 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, + 138 1 3.2 3.2 0.0 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 + 139 } + 140 + 141 # run pipeline including iterative qPAI method + 142 1 0.1 0.1 0.0 pipeline = [ + 143 1 68.3 68.3 0.0 sp.ModelBasedVolumeCreationAdapter(settings), + 144 1 10.1 10.1 0.0 sp.MCXAdapter(settings), + 145 1 42.2 42.2 0.0 sp.GaussianNoise(settings, "noise_model"), + 146 1 18.3 18.3 0.0 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") + 147 ] + 148 + 149 + 150 1 24.4 24.4 0.0 class CustomDevice(sp.PhotoacousticDevice): + 151 + 152 def __init__(self): + 153 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, + 154 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, + 155 0])) + 156 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) + 157 + 158 + 159 1 53.2 53.2 0.0 device = CustomDevice() + 160 + 161 1 0.8 0.8 0.0 device.update_settings_for_use_of_model_based_volume_creator(settings) + 162 + 163 1 10481453.4 1e+07 99.9 sp.simulate(pipeline, settings, device) + 164 + 165 # visualize reconstruction results + 166 1 0.6 0.6 0.0 if visualise: + 167 # get simulation output + 168 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" + 169 settings = sp.load_data_field(data_path, Tags.SETTINGS) + 170 wavelength = settings[Tags.WAVELENGTHS][0] + 171 + 172 # get reconstruction result + 173 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) + 174 + 175 # get ground truth absorption coefficients + 176 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) + 177 + 178 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) + 179 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR + 180 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") + 181 + 182 # compute reconstruction error + 183 difference = absorption_gt - absorption_reconstruction + 184 + 185 median_error = np.median(difference) + 186 q3, q1 = np.percentile(difference, [75, 25]) + 187 iqr = q3 - q1 + 188 + 189 # visualize results + 190 x_pos = int(np.shape(absorption_gt)[0] / 2) + 191 y_pos = int(np.shape(absorption_gt)[1] / 2) + 192 + 193 if np.min(absorption_gt) > np.min(absorption_reconstruction): + 194 cmin = np.min(absorption_reconstruction) + 195 else: + 196 cmin = np.min(absorption_gt) + 197 + 198 if np.max(absorption_gt) > np.max(absorption_reconstruction): + 199 cmax = np.max(absorption_gt) + 200 else: + 201 cmax = np.max(absorption_reconstruction) + 202 + 203 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] + 204 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] + 205 + 206 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", + 207 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] + 208 + 209 plt.figure(figsize=(20, 15)) + 210 plt.subplots_adjust(hspace=0.5) + 211 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + + 212 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) + 213 + 214 for i, quantity in enumerate(results_x_z): + 215 plt.subplot(2, len(results_x_z), i + 1) + 216 if i == 0: + 217 plt.ylabel("x-z", fontsize=10) + 218 plt.title(label[i], fontsize=10) + 219 plt.imshow(quantity.T) + 220 plt.xticks(fontsize=6) + 221 plt.yticks(fontsize=6) + 222 plt.colorbar() + 223 if i != 2: + 224 plt.clim(cmin, cmax) + 225 else: + 226 plt.clim(np.min(difference), np.max(difference)) + 227 + 228 for i, quantity in enumerate(results_y_z): + 229 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) + 230 if i == 0: + 231 plt.ylabel("y-z", fontsize=10) + 232 plt.title(label[i], fontsize=10) + 233 plt.imshow(quantity.T) + 234 plt.xticks(fontsize=6) + 235 plt.yticks(fontsize=6) + 236 plt.colorbar() + 237 if i != 2: + 238 plt.clim(cmin, cmax) + 239 else: + 240 plt.clim(np.min(difference), np.max(difference)) + 241 + 242 plt.show() + 243 plt.close() + diff --git a/simpa_examples/benchmarking/create_benchmarking_table.py b/simpa_examples/benchmarking/create_benchmarking_table.py index fc80cd1f..5bdb5722 100644 --- a/simpa_examples/benchmarking/create_benchmarking_table.py +++ b/simpa_examples/benchmarking/create_benchmarking_table.py @@ -13,8 +13,8 @@ def lines_that_contain(string, fp): examples = ['linear_unmixing', 'minimal_optical_simulation', 'minimal_optical_simulation_uniform_cube', 'msot_invision_simulation', 'optical_and_acoustic_simulation', - 'perform_iterative_qPAI_reconstruction'] -profiles = ['time', "gpu", "memory"] + 'perform_iterative_qPAI_reconstruction', 'segmentation_loader'] +profiles = ['TIME', "GPU_MEMORY", "MEMORY"] spacings = np.arange(0.2, 0.6, 0.2) benchmarking_dict = {} for example in examples: @@ -22,16 +22,16 @@ def lines_that_contain(string, fp): for spacing in spacings: benchmarking_dict[example][spacing] = {} -info_starts = {"memory": 19, "gpu": 12, "time": 16} -info_ends = {"memory": 29, "gpu": 26, "time": 29} +info_starts = {"MEMORY": 19, "GPU_MEMORY": 12, "TIME": 16} +info_ends = {"MEMORY": 29, "GPU_MEMORY": 26, "TIME": 29} for profile in profiles: for spacing in spacings: - file_name = "./benchmarking/benchmarking_data/benchmarking_data_"+profile+"_"+str(spacing)+".txt" + file_name = "./benchmarking_bash/benchmarking_data_"+profile+"_"+str(spacing)+".txt" benchmarking_file = open(file_name, 'r') current_examples = [] - if profile == 'time': + if profile == 'TIME': example_name_lines = lines_that_contain("File:", benchmarking_file) for enl in example_name_lines: @@ -41,7 +41,7 @@ def lines_that_contain(string, fp): else: break - elif profile == 'gpu': + elif profile == 'GPU_MEMORY': example_name_lines = lines_that_contain("##", benchmarking_file) for enl in example_name_lines: @@ -51,7 +51,7 @@ def lines_that_contain(string, fp): else: break - if profile == 'memory': + if profile == 'MEMORY': example_name_lines = lines_that_contain("Filename:", benchmarking_file) for enl in example_name_lines: @@ -72,7 +72,7 @@ def lines_that_contain(string, fp): value = float(lwss[info_starts[profile]:info_ends[profile]]) unit = str(lwss[info_ends[profile]]) - if profile == "time": + if profile == "TIME": unit = "" example = current_examples[examples_counter] @@ -89,18 +89,18 @@ def lines_that_contain(string, fp): subrow_counter = 0 for spacing, p_dict in spacing_dict.items(): try: - p_dict["memory"] + p_dict["MEMORY"] except KeyError: - p_dict["memory"] = "N/A" + p_dict["MEMORY"] = "N/A" if subrow_counter == len(spacing_dict)-1: divider = True if subrow_counter == 0: - table.add_row([example, spacing, p_dict['time'], p_dict["gpu"], p_dict["memory"]], divider=divider) + table.add_row([example, spacing, p_dict['TIME'], p_dict["GPU_MEMORY"], p_dict["MEMORY"]], divider=divider) else: - table.add_row(["", spacing, p_dict['time'], p_dict["gpu"], p_dict["memory"]], divider=divider) + table.add_row(["", spacing, p_dict['TIME'], p_dict["GPU_MEMORY"], p_dict["MEMORY"]], divider=divider) subrow_counter += 1 divider = False -table_file_name = "./benchmarking/benchmarking_data/benchmarking_data_table.txt" +table_file_name = "./benchmarking_bash/benchmarking_data_table.txt" benchmarking_table_file = open(table_file_name, 'w') benchmarking_table_file.write(table.get_string()) diff --git a/simpa_examples/benchmarking/performance_check.py b/simpa_examples/benchmarking/performance_check.py new file mode 100644 index 00000000..a3d2b476 --- /dev/null +++ b/simpa_examples/benchmarking/performance_check.py @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import pathlib +import os +import typing + +import typer +app = typer.Typer() + + +@app.command() +def run_benchmarking_tests(spacing=0.4, profile: str = "TIME", savefolder: str = None): + spacing = float(spacing) + os.environ["SIMPA_PROFILE"] = profile + if savefolder == 'default': + savefolder = pathlib.Path(__file__).resolve() / "/benchmarking_data_" / profile / "_" / str(spacing) / ".txt" + os.environ["SIMPA_SAVE_FOLDER"] = str(savefolder) + elif savefolder: + os.environ["SIMPA_PROFILE_SAVE_FILE"] = savefolder+"/benchmarking_data_"+profile+"_"+str(spacing)+".txt" + + import simpa_examples + import simpa as sp + + examples = [simpa_examples.run_linear_unmixing, simpa_examples.run_minimal_optical_simulation, + simpa_examples.run_minimal_optical_simulation_uniform_cube, simpa_examples.run_msot_invision_simulation, + simpa_examples.run_optical_and_acoustic_simulation, + simpa_examples.run_perform_iterative_qPAI_reconstruction] + + for example in examples: + try: + example(SPACING=spacing, path_manager=None, visualise=False) + except AttributeError: + print("simulation cannot be run on {} with spacing {}".format(example, spacing)) + + +if __name__ == "__main__": + app() diff --git a/simpa_examples/benchmarking/performance_check_gpu.py b/simpa_examples/benchmarking/performance_check_gpu.py deleted file mode 100644 index a41c6611..00000000 --- a/simpa_examples/benchmarking/performance_check_gpu.py +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ -# SPDX-FileCopyrightText: 2021 Janek Groehl -# SPDX-License-Identifier: MIT - -import numpy as np -import simpa as sp -from simpa_examples import * -import os - -# TODO: following text must be ABOVE importing simpa and simpa examples for benchmarking to work -spacing = 0.2 -os.environ[")SIMPA_PROFILE"] = "GPU_MEMORY" -os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_gpu_" + str( - spacing) + ".txt" - -path_manager = sp.PathManager() - -examples = [run_linear_unmixing, run_minimal_optical_simulation, run_minimal_optical_simulation_uniform_cube, - run_msot_invision_simulation, run_optical_and_acoustic_simulation, - run_perform_iterative_qPAI_reconstruction] - -for example in examples: - try: - example(SPACING=spacing, path_manager=sp.PathManager(), visualise=False) - except AttributeError: - print("simulation cannot be run on {} with spacing {}".format(example, spacing)) diff --git a/simpa_examples/benchmarking/performance_check_memory.py b/simpa_examples/benchmarking/performance_check_memory.py deleted file mode 100644 index 8874f621..00000000 --- a/simpa_examples/benchmarking/performance_check_memory.py +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ -# SPDX-FileCopyrightText: 2021 Janek Groehl -# SPDX-License-Identifier: MIT - -import numpy as np -import simpa as sp -from simpa_examples import * -import os -# TODO: following text must be ABOVE importing simpa and simpa examples for benchmarking to work -spacing = 0.4 -os.environ["SIMPA_PROFILE"] = "MEMORY" -os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_memory_"+str(spacing)+".txt" - - -path_manager = sp.PathManager() - -examples = [run_linear_unmixing, run_minimal_optical_simulation, run_minimal_optical_simulation_uniform_cube, - run_msot_invision_simulation, run_optical_and_acoustic_simulation, - run_perform_iterative_qPAI_reconstruction] - -for example in examples: - try: - example(SPACING=spacing, path_manager=sp.PathManager(), visualise=False) - except AttributeError: - print("simulation cannot be run on {} with spacing {}".format(example, spacing)) diff --git a/simpa_examples/benchmarking/performance_check_time.py b/simpa_examples/benchmarking/performance_check_time.py deleted file mode 100644 index a969fa65..00000000 --- a/simpa_examples/benchmarking/performance_check_time.py +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ -# SPDX-FileCopyrightText: 2021 Janek Groehl -# SPDX-License-Identifier: MIT - -import numpy as np -import simpa as sp -from simpa_examples import * -import os -# TODO: following text must be ABOVE importing simpa and simpa examples for benchmarking to work -spacing = 0.4 -os.environ["SIMPA_PROFILE"] = "TIME" -os.environ["SIMPA_PROFILE_SAVE_FILE"] = "./benchmarking/benchmarking_data/benchmarking_data_time_"+str(spacing)+".txt" - - -path_manager = sp.PathManager() - -examples = [run_linear_unmixing, run_minimal_optical_simulation, run_minimal_optical_simulation_uniform_cube, - run_msot_invision_simulation, run_optical_and_acoustic_simulation, - run_perform_iterative_qPAI_reconstruction] - -for example in examples: - try: - example(SPACING=spacing, path_manager=sp.PathManager(), visualise=False) - except AttributeError: - print("simulation cannot be run on {} with spacing {}".format(example, spacing)) diff --git a/simpa_examples/benchmarking/run_benchmarking.sh b/simpa_examples/benchmarking/run_benchmarking.sh new file mode 100644 index 00000000..79724a72 --- /dev/null +++ b/simpa_examples/benchmarking/run_benchmarking.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +help() { +echo "Usage: calculate benchmarking for [options]" +echo "For contributing, please use default" +echo "Options:" +echo " -i, --init First spacing to benchmark: default = 0.2mm" +echo " -c, --cease Final spacing to benchmark: default = 0.4mm" +echo " -s, --step Step between spacings: default = 0.1mm" +echo " -f, --file Where to store the output files: default - TODO specify where!" +echo " -t, --time Profile times taken: if no profile all are set" +echo " -g, --gpu Profile GPU usage: if no profile all are set" +echo " -m, --memory Profile memory usage: if no profile all are set" +echo " -b, --table Create pretty table with the primary results" +echo " -h, --help Display this help message" +exit 0 +} + +start=0.2 +stop=0.4 +step=0.1 +profiles=() +filename='default' + +while [ -n "$1" ]; do +case "$1" in + -i | --init) start=$2 + shift 1 + ;; + -c | --cease) stop=$2 + shift 1 + ;; + -s | --step) step=$2 + shift 1 + ;; + -f | --file) filename=$2 + shift 1 + ;; + -t | --time) profiles+=("TIME") + ;; + -g | --gpu) profiles+=("GPU_MEMORY") + ;; + -m | --memory) profiles+=("MEMORY") + ;; + -h | --help) help + ;; + *) echo "Option $1 not recognized" + ;; + -b | --table) write_table='True' + ;; +esac +shift 1 +done + +if [ ${#profiles[@]} -eq 0 ]; then + echo "WARNING: using all three profilers by default" + profiles=($"TIME") + profiles+=($"GPU_MEMORY") + profiles+=($"MEMORY") +fi + +for spacing in $(seq $start $step $stop) +do + for profile in "${profiles[@]}" + do + python3 performance_check.py --spacing $spacing --profile $profile --savefolder $filename + done +done + +if [ -$write_table == 'True' ]; then + python3 create_benchmarking_table.py +fi diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index 2654569e..aa6932d6 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -5,6 +5,7 @@ import os import numpy as np from typing import Union +import typer import simpa as sp from simpa import Tags @@ -14,9 +15,12 @@ # FIXME temporary workaround for newest Intel architectures os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" +app = typer.Typer() + +@app.command() @profile -def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager(), visualise: bool = True): +def run_linear_unmixing(SPACING: float = 0.25, path_manager=None, visualise: bool = True): """ :param SPACING: The simulation spacing between voxels @@ -24,6 +28,9 @@ def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathM :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example """ + print(SPACING) + if path_manager is None: + path_manager = sp.PathManager() # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # set global params characterizing the simulated volume VOLUME_TRANSDUCER_DIM_IN_MM = 75 @@ -178,4 +185,4 @@ def create_example_tissue(): if __name__ == "__main__": - run_linear_unmixing(SPACING=0.25, path_manager=sp.PathManager(), visualise=True) + app() diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index 38f3ceef..38b6bb22 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -7,6 +7,7 @@ import numpy as np from simpa.utils.profiling import profile from typing import Union +import typer # FIXME temporary workaround for newest Intel architectures import os @@ -15,9 +16,12 @@ # TODO: Please make sure that you have set the correct path to MCX binary and SAVE_PATH in the file path_config.env # located in the simpa_examples directory +app = typer.Typer() + +@app.command() @profile -def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): +def run_minimal_optical_simulation(SPACING: float = 0.5, path_manager=None, visualise: bool = True): """ :param SPACING: The simulation spacing between voxels @@ -25,6 +29,8 @@ def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manage :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example """ + if path_manager is None: + path_manager = sp.PathManager() VOLUME_TRANSDUCER_DIM_IN_MM = 60 VOLUME_PLANAR_DIM_IN_MM = 30 VOLUME_HEIGHT_IN_MM = 60 @@ -167,4 +173,4 @@ def __init__(self): if __name__ == "__main__": - run_minimal_optical_simulation(SPACING=0.2, path_manager=sp.PathManager(), visualise=True) + app() diff --git a/simpa_examples/minimal_optical_simulation_uniform_cube.py b/simpa_examples/minimal_optical_simulation_uniform_cube.py index 826e02a1..5c01775e 100644 --- a/simpa_examples/minimal_optical_simulation_uniform_cube.py +++ b/simpa_examples/minimal_optical_simulation_uniform_cube.py @@ -14,13 +14,18 @@ # FIXME temporary workaround for newest Intel architectures import os from simpa.utils.profiling import profile +import typer + os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" # TODO: Please make sure that you have set the correct path to MCX binary as described in the README.md file. +app = typer.Typer() + +@app.command() @profile -def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), +def run_minimal_optical_simulation_uniform_cube(SPACING: float = 0.5, path_manager=None, visualise: bool = True): """ @@ -29,6 +34,8 @@ def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example """ + if path_manager is None: + path_manager = sp.PathManager() VOLUME_TRANSDUCER_DIM_IN_MM = 60 VOLUME_PLANAR_DIM_IN_MM = 30 VOLUME_HEIGHT_IN_MM = 60 @@ -104,4 +111,4 @@ def create_example_tissue(): if __name__ == "__main__": - run_minimal_optical_simulation_uniform_cube(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) + app() diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 33bd775c..4a04231f 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -7,12 +7,16 @@ import numpy as np from simpa.utils.profiling import profile from typing import Union +import typer path_manager = sp.PathManager() +app = typer.Typer() + +@app.command() @profile -def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): +def run_msot_invision_simulation(SPACING: float = 0.5, path_manager=None, visualise: bool = True): """ :param SPACING: The simulation spacing between voxels @@ -20,6 +24,8 @@ def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager= :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example """ + if path_manager is None: + path_manager = sp.PathManager() SPEED_OF_SOUND = 1500 XZ_DIM = 90 Y_DIM = 40 @@ -207,4 +213,4 @@ def get_settings(): if __name__ == "__main__": - run_msot_invision_simulation(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) + app() diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 7e450f99..2c0b9d27 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -7,6 +7,7 @@ import numpy as np from simpa.utils.profiling import profile from typing import Union +import typer # FIXME temporary workaround for newest Intel architectures import os @@ -15,9 +16,12 @@ # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # point to the correct file in the PathManager(). +app = typer.Typer() + +@app.command() @profile -def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), +def run_optical_and_acoustic_simulation(SPACING: float = 0.2, path_manager=None, visualise: bool = True): """ @@ -26,6 +30,8 @@ def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_m :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example """ + if path_manager is None: + path_manager = sp.PathManager() VOLUME_TRANSDUCER_DIM_IN_MM = 75 VOLUME_PLANAR_DIM_IN_MM = 20 VOLUME_HEIGHT_IN_MM = 25 @@ -211,4 +217,4 @@ def create_example_tissue(): if __name__ == "__main__": - run_optical_and_acoustic_simulation(SPACING=0.2, path_manager=sp.PathManager(), visualise=True) + app() diff --git a/simpa_examples/perform_iterative_qPAI_reconstruction.py b/simpa_examples/perform_iterative_qPAI_reconstruction.py index 671585a0..ffe176ca 100644 --- a/simpa_examples/perform_iterative_qPAI_reconstruction.py +++ b/simpa_examples/perform_iterative_qPAI_reconstruction.py @@ -13,6 +13,7 @@ from simpa import Tags from typing import Union from simpa.utils.profiling import profile +import typer # FIXME temporary workaround for newest Intel architectures os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" @@ -21,9 +22,13 @@ # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # point to the correct file in the PathManager(). +app = typer.Typer() + +@app.command() @profile -def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise: bool = True): +def run_perform_iterative_qPAI_reconstruction(SPACING: float = 0.2, path_manager=None, + visualise: bool = True): """ :param SPACING: The simulation spacing between voxels @@ -31,6 +36,8 @@ def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example """ + if path_manager is None: + path_manager = sp.PathManager() VOLUME_TRANSDUCER_DIM_IN_MM = 30 VOLUME_PLANAR_DIM_IN_MM = 30 VOLUME_HEIGHT_IN_MM = 30 @@ -241,4 +248,4 @@ def __init__(self): if __name__ == "__main__": - run_perform_iterative_qPAI_reconstruction(SPACING=0.5, path_manager=sp.PathManager(), visualise=True) + app() diff --git a/simpa_examples/segmentation_loader.py b/simpa_examples/segmentation_loader.py index 09fe493e..8556a2a8 100644 --- a/simpa_examples/segmentation_loader.py +++ b/simpa_examples/segmentation_loader.py @@ -10,86 +10,101 @@ # FIXME temporary workaround for newest Intel architectures import os +import typer +from simpa.utils.profiling import profile os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" # If VISUALIZE is set to True, the simulation result will be plotted -VISUALIZE = True - +app = typer.Typer() # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # point to the correct file in the PathManager(). -path_manager = sp.PathManager() - -target_spacing = 1.0 - -label_mask = shepp_logan_phantom() - -label_mask = np.digitize(label_mask, bins=np.linspace(0.0, 1.0, 11), right=True) - -label_mask = np.reshape(label_mask, (400, 1, 400)) - -input_spacing = 0.2 -segmentation_volume_tiled = np.tile(label_mask, (1, 128, 1)) -segmentation_volume_mask = np.round(zoom(segmentation_volume_tiled, input_spacing/target_spacing, - order=0)).astype(int) - - -def segmentation_class_mapping(): - ret_dict = dict() - ret_dict[0] = sp.TISSUE_LIBRARY.heavy_water() - ret_dict[1] = sp.TISSUE_LIBRARY.blood() - ret_dict[2] = sp.TISSUE_LIBRARY.epidermis() - ret_dict[3] = sp.TISSUE_LIBRARY.muscle() - ret_dict[4] = sp.TISSUE_LIBRARY.mediprene() - ret_dict[5] = sp.TISSUE_LIBRARY.ultrasound_gel() - ret_dict[6] = sp.TISSUE_LIBRARY.heavy_water() - ret_dict[7] = (sp.MolecularCompositionGenerator() - .append(sp.MOLECULE_LIBRARY.oxyhemoglobin(0.01)) - .append(sp.MOLECULE_LIBRARY.deoxyhemoglobin(0.01)) - .append(sp.MOLECULE_LIBRARY.water(0.98)) - .get_molecular_composition(sp.SegmentationClasses.COUPLING_ARTIFACT)) - ret_dict[8] = sp.TISSUE_LIBRARY.heavy_water() - ret_dict[9] = sp.TISSUE_LIBRARY.heavy_water() - ret_dict[10] = sp.TISSUE_LIBRARY.heavy_water() - return ret_dict - - -settings = sp.Settings() -settings[Tags.SIMULATION_PATH] = path_manager.get_hdf5_file_save_path() -settings[Tags.VOLUME_NAME] = "SegmentationTest" -settings[Tags.RANDOM_SEED] = 1234 -settings[Tags.WAVELENGTHS] = [700] -settings[Tags.SPACING_MM] = target_spacing -settings[Tags.DIM_VOLUME_X_MM] = 400 / (target_spacing / input_spacing) -settings[Tags.DIM_VOLUME_Y_MM] = 128 / (target_spacing / input_spacing) -settings[Tags.DIM_VOLUME_Z_MM] = 400 / (target_spacing / input_spacing) - -settings.set_volume_creation_settings({ - Tags.INPUT_SEGMENTATION_VOLUME: segmentation_volume_mask, - Tags.SEGMENTATION_CLASS_MAPPING: segmentation_class_mapping(), - -}) - -settings.set_optical_settings({ - Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e8, - Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, - Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, -}) - -pipeline = [ - sp.SegmentationBasedVolumeCreationAdapter(settings), - sp.MCXAdapter(settings) -] - -sp.simulate(pipeline, settings, sp.RSOMExplorerP50(element_spacing_mm=1.0)) - -if Tags.WAVELENGTH in settings: - WAVELENGTH = settings[Tags.WAVELENGTH] -else: - WAVELENGTH = 700 - -if VISUALIZE: - sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", - wavelength=WAVELENGTH, - show_initial_pressure=True, - show_segmentation_map=True) + + +@app.command() +@profile +def run_segmentation_loader(SPACING: float = 0.1, path_manager=None, + visualise: bool = True): + """ + + :param SPACING: The simulation spacing between voxels + :param path_manager: the path manager to be used, typically sp.PathManager + :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted + :return: a run through of the example + """ + if path_manager is None: + path_manager = sp.PathManager() + + label_mask = shepp_logan_phantom() + + label_mask = np.digitize(label_mask, bins=np.linspace(0.0, 1.0, 11), right=True) + + label_mask = np.reshape(label_mask, (400, 1, 400)) + + input_spacing = 0.2 + segmentation_volume_tiled = np.tile(label_mask, (1, 128, 1)) + segmentation_volume_mask = np.round(zoom(segmentation_volume_tiled, input_spacing/SPACING, + order=0)).astype(int) + + def segmentation_class_mapping(): + ret_dict = dict() + ret_dict[0] = sp.TISSUE_LIBRARY.heavy_water() + ret_dict[1] = sp.TISSUE_LIBRARY.blood() + ret_dict[2] = sp.TISSUE_LIBRARY.epidermis() + ret_dict[3] = sp.TISSUE_LIBRARY.muscle() + ret_dict[4] = sp.TISSUE_LIBRARY.mediprene() + ret_dict[5] = sp.TISSUE_LIBRARY.ultrasound_gel() + ret_dict[6] = sp.TISSUE_LIBRARY.heavy_water() + ret_dict[7] = (sp.MolecularCompositionGenerator() + .append(sp.MOLECULE_LIBRARY.oxyhemoglobin(0.01)) + .append(sp.MOLECULE_LIBRARY.deoxyhemoglobin(0.01)) + .append(sp.MOLECULE_LIBRARY.water(0.98)) + .get_molecular_composition(sp.SegmentationClasses.COUPLING_ARTIFACT)) + ret_dict[8] = sp.TISSUE_LIBRARY.heavy_water() + ret_dict[9] = sp.TISSUE_LIBRARY.heavy_water() + ret_dict[10] = sp.TISSUE_LIBRARY.heavy_water() + return ret_dict + + settings = sp.Settings() + settings[Tags.SIMULATION_PATH] = path_manager.get_hdf5_file_save_path() + settings[Tags.VOLUME_NAME] = "SegmentationTest" + settings[Tags.RANDOM_SEED] = 1234 + settings[Tags.WAVELENGTHS] = [700] + settings[Tags.SPACING_MM] = SPACING + settings[Tags.DIM_VOLUME_X_MM] = 400 / (SPACING / input_spacing) + settings[Tags.DIM_VOLUME_Y_MM] = 128 / (SPACING / input_spacing) + settings[Tags.DIM_VOLUME_Z_MM] = 400 / (SPACING / input_spacing) + + settings.set_volume_creation_settings({ + Tags.INPUT_SEGMENTATION_VOLUME: segmentation_volume_mask, + Tags.SEGMENTATION_CLASS_MAPPING: segmentation_class_mapping(), + + }) + + settings.set_optical_settings({ + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e8, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, + Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + }) + + pipeline = [ + sp.SegmentationBasedVolumeCreationAdapter(settings), + sp.MCXAdapter(settings) + ] + + sp.simulate(pipeline, settings, sp.RSOMExplorerP50(element_spacing_mm=1.0)) + + if Tags.WAVELENGTH in settings: + WAVELENGTH = settings[Tags.WAVELENGTH] + else: + WAVELENGTH = 700 + + if visualise: + sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + "SegmentationTest" + ".hdf5", + wavelength=WAVELENGTH, + show_initial_pressure=True, + show_segmentation_map=True) + + +if __name__ == "__main__": + app() From 1f458cb72e67a2b8bacb72a4e7798815743cf0a1 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Tue, 25 Jun 2024 15:25:56 +0200 Subject: [PATCH 10/34] Adding documentation --- .../benchmarking/create_benchmarking_table.py | 6 ++++ .../benchmarking/performance_check.py | 29 +++++++++++++------ .../benchmarking/run_benchmarking.sh | 12 +++++--- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/simpa_examples/benchmarking/create_benchmarking_table.py b/simpa_examples/benchmarking/create_benchmarking_table.py index 5bdb5722..f228a545 100644 --- a/simpa_examples/benchmarking/create_benchmarking_table.py +++ b/simpa_examples/benchmarking/create_benchmarking_table.py @@ -8,6 +8,12 @@ def lines_that_contain(string, fp): + """ + Function to determine if a string contains a certain word/phrase + :param string: what to check + :param fp: + :return: + """ return [line for line in fp if string in line] diff --git a/simpa_examples/benchmarking/performance_check.py b/simpa_examples/benchmarking/performance_check.py index a3d2b476..a685c5ff 100644 --- a/simpa_examples/benchmarking/performance_check.py +++ b/simpa_examples/benchmarking/performance_check.py @@ -4,29 +4,40 @@ import pathlib import os -import typing import typer app = typer.Typer() @app.command() -def run_benchmarking_tests(spacing=0.4, profile: str = "TIME", savefolder: str = None): +def run_benchmarking_tests(spacing=0.4, profile: str = "TIME", savefolder: str = 'default'): + """ + + :param spacing: Simulation spacing in mm + :param profile: What tag to choose to benchmark the examples + :param savefolder: where to save the results + :return: file with benchmarking data line by line + """ spacing = float(spacing) os.environ["SIMPA_PROFILE"] = profile if savefolder == 'default': - savefolder = pathlib.Path(__file__).resolve() / "/benchmarking_data_" / profile / "_" / str(spacing) / ".txt" - os.environ["SIMPA_SAVE_FOLDER"] = str(savefolder) - elif savefolder: + savefolder = (str(pathlib.Path(__file__).parent.resolve()) + "/benchmarking_data_" + profile + "_" + str(spacing) + + ".txt") + os.environ["SIMPA_PROFILE_SAVE_FILE"] = savefolder + elif savefolder == 'print': + pass + elif len(savefolder) > 0: os.environ["SIMPA_PROFILE_SAVE_FILE"] = savefolder+"/benchmarking_data_"+profile+"_"+str(spacing)+".txt" import simpa_examples import simpa as sp - examples = [simpa_examples.run_linear_unmixing, simpa_examples.run_minimal_optical_simulation, - simpa_examples.run_minimal_optical_simulation_uniform_cube, simpa_examples.run_msot_invision_simulation, - simpa_examples.run_optical_and_acoustic_simulation, - simpa_examples.run_perform_iterative_qPAI_reconstruction] + # examples = [simpa_examples.run_linear_unmixing, simpa_examples.run_minimal_optical_simulation, + # simpa_examples.run_minimal_optical_simulation_uniform_cube, simpa_examples.run_msot_invision_simulation, + # simpa_examples.run_optical_and_acoustic_simulation, + # simpa_examples.run_perform_iterative_qPAI_reconstruction] + + examples = [simpa_examples.run_msot_invision_simulation] for example in examples: try: diff --git a/simpa_examples/benchmarking/run_benchmarking.sh b/simpa_examples/benchmarking/run_benchmarking.sh index 79724a72..83a265b6 100644 --- a/simpa_examples/benchmarking/run_benchmarking.sh +++ b/simpa_examples/benchmarking/run_benchmarking.sh @@ -7,7 +7,7 @@ echo "Options:" echo " -i, --init First spacing to benchmark: default = 0.2mm" echo " -c, --cease Final spacing to benchmark: default = 0.4mm" echo " -s, --step Step between spacings: default = 0.1mm" -echo " -f, --file Where to store the output files: default - TODO specify where!" +echo " -f, --file Where to store the output files: default save in curr; 'print' prints it in console" echo " -t, --time Profile times taken: if no profile all are set" echo " -g, --gpu Profile GPU usage: if no profile all are set" echo " -m, --memory Profile memory usage: if no profile all are set" @@ -16,9 +16,7 @@ echo " -h, --help Display this help message" exit 0 } -start=0.2 -stop=0.4 -step=0.1 +start=0 profiles=() filename='default' @@ -52,6 +50,12 @@ esac shift 1 done +if [ $start -eq 0 ]; then + start=0.2 + stop=0.4 + step=0.1 +fi + if [ ${#profiles[@]} -eq 0 ]; then echo "WARNING: using all three profilers by default" profiles=($"TIME") From 27b7901e524756671d7e2a429aa0e44814b1abd2 Mon Sep 17 00:00:00 2001 From: frisograce Date: Wed, 26 Jun 2024 09:19:43 +0200 Subject: [PATCH 11/34] Changing away from typer --- simpa/utils/profiling.py | 2 +- .../benchmarking_data_gpu_0.2.txt | 1034 ---------------- .../benchmarking_data_gpu_0.4.txt | 1034 ---------------- .../benchmarking_data_memory_0.2.txt | 1027 ---------------- .../benchmarking_data_memory_0.4.txt | 0 .../benchmarking_data_time_0.2.txt | 1048 ----------------- .../benchmarking_data_time_0.4.txt | 1048 ----------------- simpa_examples/linear_unmixing.py | 23 +- simpa_examples/minimal_optical_simulation.py | 22 +- ...minimal_optical_simulation_uniform_cube.py | 22 +- simpa_examples/msot_invision_simulation.py | 22 +- .../optical_and_acoustic_simulation.py | 24 +- .../perform_iterative_qPAI_reconstruction.py | 22 +- simpa_examples/segmentation_loader.py | 31 +- 14 files changed, 105 insertions(+), 5254 deletions(-) delete mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.2.txt delete mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.4.txt delete mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.2.txt delete mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.4.txt delete mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.2.txt delete mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.4.txt diff --git a/simpa/utils/profiling.py b/simpa/utils/profiling.py index a3cf2212..db95db2b 100644 --- a/simpa/utils/profiling.py +++ b/simpa/utils/profiling.py @@ -20,7 +20,7 @@ def profile(f): profile = LineProfiler() def print_stats_atexit(): - return profile.print_stats(stream=stream, output_unit=10**(-6)) + return profile.print_stats(stream=stream, output_unit=10**(-3)) atexit.register(print_stats_atexit) elif profile_type == "MEMORY": from memory_profiler import profile diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.2.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.2.txt deleted file mode 100644 index 4e5ce73b..00000000 --- a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.2.txt +++ /dev/null @@ -1,1034 +0,0 @@ -## run_perform_iterative_qPAI_reconstruction - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 152.00M 25 @profile - 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise:bool = True): - 27 """ - 28 - 29 :param SPACING: The simulation spacing between voxels - 30 :param path_manager: the path manager to be used, typically sp.PathManager - 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 32 :return: a run through of the example - 33 """ - 8.12M 152.00M 34 VOLUME_TRANSDUCER_DIM_IN_MM = 30 - 8.12M 152.00M 35 VOLUME_PLANAR_DIM_IN_MM = 30 - 8.12M 152.00M 36 VOLUME_HEIGHT_IN_MM = 30 - 8.12M 152.00M 37 RANDOM_SEED = 471 - 8.12M 152.00M 38 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) - 39 - 40 # If VISUALIZE is set to True, the reconstruction result will be plotted - 41 - 8.12M 152.00M 42 def create_example_tissue(): - 43 """ - 44 This is a very simple example script of how to create a tissue definition. - 45 It contains a muscular background, an epidermis layer on top of the muscles - 46 and a blood vessel. - 47 """ - 48 background_dictionary = sp.Settings() - 49 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) - 50 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 51 - 52 epidermis_structure = sp.Settings() - 53 epidermis_structure[Tags.PRIORITY] = 1 - 54 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] - 55 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] - 56 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) - 57 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True - 58 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True - 59 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 60 - 61 vessel_structure_1 = sp.Settings() - 62 vessel_structure_1[Tags.PRIORITY] = 2 - 63 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, - 64 VOLUME_HEIGHT_IN_MM / 2] - 65 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, - 66 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] - 67 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 - 68 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 - 69 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) - 70 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True - 71 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True - 72 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE - 73 - 74 vessel_structure_2 = sp.Settings() - 75 vessel_structure_2[Tags.PRIORITY] = 3 - 76 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, - 77 VOLUME_HEIGHT_IN_MM / 3] - 78 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, - 79 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] - 80 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 - 81 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) - 82 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True - 83 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 84 - 85 tissue_dict = sp.Settings() - 86 tissue_dict[Tags.BACKGROUND] = background_dictionary - 87 tissue_dict["epidermis"] = epidermis_structure - 88 tissue_dict["vessel_1"] = vessel_structure_1 - 89 tissue_dict["vessel_2"] = vessel_structure_2 - 90 return tissue_dict - 91 - 92 - 93 # set settings for volume creation, optical simulation and iterative qPAI method - 8.12M 152.00M 94 np.random.seed(RANDOM_SEED) - 95 - 8.12M 152.00M 96 general_settings = { - 97 # These parameters set the general properties of the simulated volume - 8.12M 152.00M 98 Tags.RANDOM_SEED: RANDOM_SEED, - 8.12M 152.00M 99 Tags.VOLUME_NAME: VOLUME_NAME, - 8.12M 152.00M 100 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 8.12M 152.00M 101 Tags.SPACING_MM: SPACING, - 8.12M 152.00M 102 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 8.12M 152.00M 103 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 8.12M 152.00M 104 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 8.12M 152.00M 105 Tags.WAVELENGTHS: [700] - 106 } - 107 - 8.12M 152.00M 108 settings = sp.Settings(general_settings) - 109 - 8.12M 152.00M 110 settings.set_volume_creation_settings({ - 111 # These parameters set the properties for the volume creation - 8.12M 152.00M 112 Tags.SIMULATE_DEFORMED_LAYERS: True, - 8.12M 152.00M 113 Tags.STRUCTURES: create_example_tissue() - 114 }) - 8.12M 152.00M 115 settings.set_optical_settings({ - 116 # These parameters set the properties for the optical Monte Carlo simulation - 8.12M 152.00M 117 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 8.12M 152.00M 118 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 8.12M 152.00M 119 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 8.12M 152.00M 120 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 121 }) - 8.12M 152.00M 122 settings["noise_model"] = { - 8.12M 152.00M 123 Tags.NOISE_MEAN: 1.0, - 8.12M 152.00M 124 Tags.NOISE_STD: 0.01, - 8.12M 152.00M 125 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 8.12M 152.00M 126 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 8.12M 152.00M 127 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 128 } - 8.12M 152.00M 129 settings["iterative_qpai_reconstruction"] = { - 130 # These parameters set the properties of the iterative reconstruction - 8.12M 152.00M 131 Tags.DOWNSCALE_FACTOR: 0.75, - 8.12M 152.00M 132 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, - 133 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant - 8.12M 152.00M 134 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, - 8.12M 152.00M 135 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, - 136 # for this example, we are not interested in all absorption updates - 8.12M 152.00M 137 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, - 8.12M 152.00M 138 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 - 139 } - 140 - 141 # run pipeline including iterative qPAI method - 8.12M 152.00M 142 pipeline = [ - 8.12M 152.00M 143 sp.ModelBasedVolumeCreationAdapter(settings), - 8.12M 152.00M 144 sp.MCXAdapter(settings), - 8.12M 152.00M 145 sp.GaussianNoise(settings, "noise_model"), - 8.12M 152.00M 146 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") - 147 ] - 148 - 149 - 8.12M 152.00M 150 class CustomDevice(sp.PhotoacousticDevice): - 151 - 152 def __init__(self): - 153 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, - 154 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, - 155 0])) - 156 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) - 157 - 158 - 8.12M 152.00M 159 device = CustomDevice() - 160 - 8.12M 152.00M 161 device.update_settings_for_use_of_model_based_volume_creator(settings) - 162 - 346.85M 376.00M 163 sp.simulate(pipeline, settings, device) - 164 - 165 # visualize reconstruction results - 8.12M 150.00M 166 if visualise: - 167 # get simulation output - 168 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 169 settings = sp.load_data_field(data_path, Tags.SETTINGS) - 170 wavelength = settings[Tags.WAVELENGTHS][0] - 171 - 172 # get reconstruction result - 173 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) - 174 - 175 # get ground truth absorption coefficients - 176 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) - 177 - 178 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) - 179 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR - 180 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") - 181 - 182 # compute reconstruction error - 183 difference = absorption_gt - absorption_reconstruction - 184 - 185 median_error = np.median(difference) - 186 q3, q1 = np.percentile(difference, [75, 25]) - 187 iqr = q3 - q1 - 188 - 189 # visualize results - 190 x_pos = int(np.shape(absorption_gt)[0] / 2) - 191 y_pos = int(np.shape(absorption_gt)[1] / 2) - 192 - 193 if np.min(absorption_gt) > np.min(absorption_reconstruction): - 194 cmin = np.min(absorption_reconstruction) - 195 else: - 196 cmin = np.min(absorption_gt) - 197 - 198 if np.max(absorption_gt) > np.max(absorption_reconstruction): - 199 cmax = np.max(absorption_gt) - 200 else: - 201 cmax = np.max(absorption_reconstruction) - 202 - 203 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] - 204 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] - 205 - 206 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", - 207 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] - 208 - 209 plt.figure(figsize=(20, 15)) - 210 plt.subplots_adjust(hspace=0.5) - 211 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + - 212 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) - 213 - 214 for i, quantity in enumerate(results_x_z): - 215 plt.subplot(2, len(results_x_z), i + 1) - 216 if i == 0: - 217 plt.ylabel("x-z", fontsize=10) - 218 plt.title(label[i], fontsize=10) - 219 plt.imshow(quantity.T) - 220 plt.xticks(fontsize=6) - 221 plt.yticks(fontsize=6) - 222 plt.colorbar() - 223 if i != 2: - 224 plt.clim(cmin, cmax) - 225 else: - 226 plt.clim(np.min(difference), np.max(difference)) - 227 - 228 for i, quantity in enumerate(results_y_z): - 229 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) - 230 if i == 0: - 231 plt.ylabel("y-z", fontsize=10) - 232 plt.title(label[i], fontsize=10) - 233 plt.imshow(quantity.T) - 234 plt.xticks(fontsize=6) - 235 plt.yticks(fontsize=6) - 236 plt.colorbar() - 237 if i != 2: - 238 plt.clim(cmin, cmax) - 239 else: - 240 plt.clim(np.min(difference), np.max(difference)) - 241 - 242 plt.show() - 243 plt.close() -## run_optical_and_acoustic_simulation - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 148.00M 18 @profile - 19 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), - 20 visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 8.12M 148.00M 28 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 8.12M 148.00M 29 VOLUME_PLANAR_DIM_IN_MM = 20 - 8.12M 148.00M 30 VOLUME_HEIGHT_IN_MM = 25 - 8.12M 148.00M 31 RANDOM_SEED = 4711 - 32 - 33 # If VISUALIZE is set to True, the simulation result will be plotted - 8.12M 148.00M 34 VISUALIZE = True - 35 - 8.12M 148.00M 36 def create_example_tissue(): - 37 """ - 38 This is a very simple example script of how to create a tissue definition. - 39 It contains a muscular background, an epidermis layer on top of the muscles - 40 and a blood vessel. - 41 """ - 42 background_dictionary = sp.Settings() - 43 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) - 44 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 45 - 46 tissue_dict = sp.Settings() - 47 tissue_dict[Tags.BACKGROUND] = background_dictionary - 48 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, - 49 molecular_composition=sp.TISSUE_LIBRARY.constant( - 50 0.05, 100, 0.9), - 51 priority=1, - 52 consider_partial_volume=True, - 53 adhere_to_deformation=True) - 54 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, - 55 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), - 56 priority=8, - 57 consider_partial_volume=True, - 58 adhere_to_deformation=True) - 59 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( - 60 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], - 61 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], - 62 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 63 radius_mm=2, priority=3, consider_partial_volume=True, - 64 adhere_to_deformation=False - 65 ) - 66 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( - 67 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], - 68 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], - 69 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 70 radius_mm=3, priority=3, consider_partial_volume=True, - 71 adhere_to_deformation=False - 72 ) - 73 return tissue_dict - 74 - 75 - 76 # Seed the numpy random configuration prior to creating the global_settings file in - 77 # order to ensure that the same volume - 78 # is generated with the same random seed every time. - 79 - 8.12M 148.00M 80 np.random.seed(RANDOM_SEED) - 8.12M 148.00M 81 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) - 82 - 8.12M 148.00M 83 general_settings = { - 84 # These parameters set the general properties of the simulated volume - 8.12M 148.00M 85 Tags.RANDOM_SEED: RANDOM_SEED, - 8.12M 148.00M 86 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), - 8.12M 148.00M 87 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 8.12M 148.00M 88 Tags.SPACING_MM: SPACING, - 8.12M 148.00M 89 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 8.12M 148.00M 90 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 8.12M 148.00M 91 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 8.12M 148.00M 92 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 8.12M 148.00M 93 Tags.GPU: True, - 8.12M 148.00M 94 Tags.WAVELENGTHS: [700, 800], - 8.12M 148.00M 95 Tags.DO_FILE_COMPRESSION: True, - 8.12M 148.00M 96 Tags.DO_IPASC_EXPORT: True - 97 } - 8.12M 148.00M 98 settings = sp.Settings(general_settings) - 8.12M 148.00M 99 np.random.seed(RANDOM_SEED) - 100 - 8.12M 148.00M 101 settings.set_volume_creation_settings({ - 8.12M 148.00M 102 Tags.STRUCTURES: create_example_tissue(), - 8.12M 148.00M 103 Tags.SIMULATE_DEFORMED_LAYERS: True - 104 }) - 105 - 8.12M 148.00M 106 settings.set_optical_settings({ - 8.12M 148.00M 107 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 8.12M 148.00M 108 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 8.12M 148.00M 109 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, - 8.12M 148.00M 110 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 8.12M 148.00M 111 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, - 112 }) - 113 - 8.12M 148.00M 114 settings.set_acoustic_settings({ - 8.12M 148.00M 115 Tags.ACOUSTIC_SIMULATION_3D: False, - 8.12M 148.00M 116 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 8.12M 148.00M 117 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 8.12M 148.00M 118 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 8.12M 148.00M 119 Tags.KWAVE_PROPERTY_PMLInside: False, - 8.12M 148.00M 120 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 8.12M 148.00M 121 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 8.12M 148.00M 122 Tags.KWAVE_PROPERTY_PlotPML: False, - 8.12M 148.00M 123 Tags.RECORDMOVIE: False, - 8.12M 148.00M 124 Tags.MOVIENAME: "visualization_log", - 8.12M 148.00M 125 Tags.ACOUSTIC_LOG_SCALE: True - 126 }) - 127 - 8.12M 148.00M 128 settings.set_reconstruction_settings({ - 8.12M 148.00M 129 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 8.12M 148.00M 130 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 8.12M 148.00M 131 Tags.ACOUSTIC_SIMULATION_3D: False, - 8.12M 148.00M 132 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 8.12M 148.00M 133 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 8.12M 148.00M 134 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), - 8.12M 148.00M 135 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), - 8.12M 148.00M 136 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 8.12M 148.00M 137 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 8.12M 148.00M 138 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, - 8.12M 148.00M 139 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 8.12M 148.00M 140 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 8.12M 148.00M 141 Tags.KWAVE_PROPERTY_PMLInside: False, - 8.12M 148.00M 142 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 8.12M 148.00M 143 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 8.12M 148.00M 144 Tags.KWAVE_PROPERTY_PlotPML: False, - 8.12M 148.00M 145 Tags.RECORDMOVIE: False, - 8.12M 148.00M 146 Tags.MOVIENAME: "visualization_log", - 8.12M 148.00M 147 Tags.ACOUSTIC_LOG_SCALE: True, - 8.12M 148.00M 148 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, - 8.12M 148.00M 149 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, - 8.12M 148.00M 150 Tags.DATA_FIELD_DENSITY: 1000, - 8.12M 148.00M 151 Tags.SPACING_MM: SPACING - 152 }) - 153 - 8.12M 148.00M 154 settings["noise_initial_pressure"] = { - 8.12M 148.00M 155 Tags.NOISE_MEAN: 1, - 8.12M 148.00M 156 Tags.NOISE_STD: 0.01, - 8.12M 148.00M 157 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 8.12M 148.00M 158 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 8.12M 148.00M 159 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 160 } - 161 - 8.12M 148.00M 162 settings["noise_time_series"] = { - 8.12M 148.00M 163 Tags.NOISE_STD: 1, - 8.12M 148.00M 164 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, - 8.12M 148.00M 165 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA - 166 } - 167 - 168 # TODO: For the device choice, uncomment the undesired device - 169 - 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 171 # VOLUME_PLANAR_DIM_IN_MM/2, - 172 # 0])) - 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) - 174 - 8.12M 148.00M 175 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 8.12M 148.00M 176 VOLUME_PLANAR_DIM_IN_MM/2, - 8.12M 148.00M 177 0]), - 8.12M 148.00M 178 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) - 8.12M 148.00M 179 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, - 8.12M 148.00M 180 pitch_mm=0.25, - 8.12M 148.00M 181 number_detector_elements=100, - 8.12M 148.00M 182 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) - 8.12M 148.00M 183 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) - 8.12M 148.00M 184 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) - 185 - 186 - 8.12M 148.00M 187 SIMULATION_PIPELINE = [ - 8.12M 148.00M 188 sp.ModelBasedVolumeCreationAdapter(settings), - 8.12M 148.00M 189 sp.MCXAdapter(settings), - 8.12M 148.00M 190 sp.GaussianNoise(settings, "noise_initial_pressure"), - 8.12M 148.00M 191 sp.KWaveAdapter(settings), - 8.12M 148.00M 192 sp.GaussianNoise(settings, "noise_time_series"), - 8.12M 148.00M 193 sp.TimeReversalAdapter(settings), - 8.12M 148.00M 194 sp.FieldOfViewCropping(settings) - 195 ] - 196 - 440.30M 510.00M 197 sp.simulate(SIMULATION_PIPELINE, settings, device) - 198 - 8.12M 152.00M 199 if Tags.WAVELENGTH in settings: - 8.12M 152.00M 200 WAVELENGTH = settings[Tags.WAVELENGTH] - 201 else: - 202 WAVELENGTH = 700 - 203 - 8.12M 152.00M 204 if visualise: - 205 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - 206 wavelength=WAVELENGTH, - 207 show_time_series_data=True, - 208 show_initial_pressure=True, - 209 show_reconstructed_data=True, - 210 log_scale=False, - 211 show_xz_only=False) -## run_msot_invision_simulation - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 148.00M 13 @profile - 14 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise:bool = True): - 15 """ - 16 - 17 :param SPACING: The simulation spacing between voxels - 18 :param path_manager: the path manager to be used, typically sp.PathManager - 19 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 20 :return: a run through of the example - 21 """ - 8.12M 148.00M 22 SPEED_OF_SOUND = 1500 - 8.12M 148.00M 23 XZ_DIM = 90 - 8.12M 148.00M 24 Y_DIM = 40 - 25 - 8.12M 148.00M 26 def create_pipeline(_settings: sp.Settings): - 27 return [ - 28 sp.ModelBasedVolumeCreationAdapter(settings), - 29 sp.MCXAdapter(settings), - 30 sp.KWaveAdapter(settings), - 31 sp.FieldOfViewCropping(settings), - 32 sp.TimeReversalAdapter(settings) - 33 ] - 34 - 35 - 8.12M 148.00M 36 def get_device(): - 37 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) - 38 return pa_device - 39 - 40 - 8.12M 148.00M 41 def create_volume(): - 42 inclusion_material = sp.Molecule(volume_fraction=1.0, - 43 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 44 0.9), - 45 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 46 100.0), - 47 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 48 4.0), - 49 speed_of_sound=SPEED_OF_SOUND, - 50 alpha_coefficient=1e-4, - 51 density=1000, - 52 gruneisen_parameter=1.0, - 53 name="Inclusion") - 54 - 55 phantom_material = sp.Molecule(volume_fraction=1.0, - 56 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), - 57 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 58 100.0), - 59 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), - 60 speed_of_sound=SPEED_OF_SOUND, - 61 alpha_coefficient=1e-4, - 62 density=1000, - 63 gruneisen_parameter=1.0, - 64 name="Phantom") - 65 - 66 heavy_water = sp.Molecule(volume_fraction=1.0, - 67 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), - 68 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), - 69 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), - 70 speed_of_sound=SPEED_OF_SOUND, - 71 alpha_coefficient=1e-4, - 72 density=1000, - 73 gruneisen_parameter=1.0, - 74 name="background_water") - 75 - 76 background_dictionary = sp.Settings() - 77 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 78 .append(heavy_water) - 79 .get_molecular_composition(segmentation_type=-1)) - 80 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 81 - 82 phantom_material_dictionary = sp.Settings() - 83 phantom_material_dictionary[Tags.PRIORITY] = 3 - 84 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] - 85 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 - 86 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 - 87 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 - 88 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 89 .append(phantom_material) - 90 .get_molecular_composition(segmentation_type=0)) - 91 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 92 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 93 - 94 inclusion_1_dictionary = sp.Settings() - 95 inclusion_1_dictionary[Tags.PRIORITY] = 8 - 96 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] - 97 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 - 98 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 - 99 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 - 100 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 101 .append(inclusion_material) - 102 .get_molecular_composition(segmentation_type=1)) - 103 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 104 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 105 - 106 inclusion_2_dictionary = sp.Settings() - 107 inclusion_2_dictionary[Tags.PRIORITY] = 5 - 108 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] - 109 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] - 110 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 111 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 112 .append(inclusion_material) - 113 .get_molecular_composition(segmentation_type=2)) - 114 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 115 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 116 - 117 tissue_dict = sp.Settings() - 118 tissue_dict[Tags.BACKGROUND] = background_dictionary - 119 tissue_dict["phantom"] = phantom_material_dictionary - 120 tissue_dict["inclusion_1"] = inclusion_1_dictionary - 121 tissue_dict["inclusion_2"] = inclusion_2_dictionary - 122 return { - 123 Tags.STRUCTURES: tissue_dict, - 124 Tags.SIMULATE_DEFORMED_LAYERS: False - 125 } - 126 - 127 - 8.12M 148.00M 128 def get_settings(): - 129 general_settings = { - 130 # These parameters set the general properties of the simulated volume - 131 Tags.RANDOM_SEED: 4711, - 132 Tags.VOLUME_NAME: "InVision Simulation Example", - 133 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 134 Tags.SPACING_MM: SPACING, - 135 Tags.DIM_VOLUME_Z_MM: XZ_DIM, - 136 Tags.DIM_VOLUME_X_MM: XZ_DIM, - 137 Tags.DIM_VOLUME_Y_MM: Y_DIM, - 138 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 139 Tags.GPU: True, - 140 Tags.WAVELENGTHS: [700] - 141 } - 142 - 143 volume_settings = create_volume() - 144 - 145 optical_settings = { - 146 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 147 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 148 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, - 149 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 150 } - 151 - 152 acoustic_settings = { - 153 Tags.ACOUSTIC_SIMULATION_3D: True, - 154 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 155 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 156 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 157 Tags.KWAVE_PROPERTY_PMLInside: False, - 158 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 159 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 160 Tags.KWAVE_PROPERTY_PlotPML: False, - 161 Tags.RECORDMOVIE: False, - 162 Tags.MOVIENAME: "visualization_log", - 163 Tags.ACOUSTIC_LOG_SCALE: True - 164 } - 165 - 166 reconstruction_settings = { - 167 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 168 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 169 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 170 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 171 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, - 172 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 173 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, - 174 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 175 Tags.KWAVE_PROPERTY_PMLInside: False, - 176 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 177 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 178 Tags.KWAVE_PROPERTY_PlotPML: False, - 179 Tags.RECORDMOVIE: False, - 180 Tags.MOVIENAME: "visualization_log", - 181 Tags.ACOUSTIC_LOG_SCALE: True, - 182 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 183 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 184 Tags.SPACING_MM: 0.25, - 185 } - 186 - 187 _settings = sp.Settings(general_settings) - 188 _settings.set_volume_creation_settings(volume_settings) - 189 _settings.set_optical_settings(optical_settings) - 190 _settings.set_acoustic_settings(acoustic_settings) - 191 _settings.set_reconstruction_settings(reconstruction_settings) - 192 return _settings - 193 - 194 - 8.12M 148.00M 195 device = get_device() - 8.12M 148.00M 196 settings = get_settings() - 8.12M 148.00M 197 pipeline = create_pipeline(settings) - 198 - 3.32G 4.25G 199 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) - 200 - 8.12M 148.00M 201 if visualise: - 202 sp.visualise_data(settings=settings, - 203 path_manager=path_manager, - 204 show_absorption=True, - 205 show_initial_pressure=True, - 206 show_reconstructed_data=True, - 207 show_xz_only=True) -## run_minimal_optical_simulation_uniform_cube - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 202.00M 22 @profile - 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), - 24 visualise:bool = True): - 25 """ - 26 - 27 :param SPACING: The simulation spacing between voxels - 28 :param path_manager: the path manager to be used, typically sp.PathManager - 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 30 :return: a run through of the example - 31 """ - 8.12M 202.00M 32 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 8.12M 202.00M 33 VOLUME_PLANAR_DIM_IN_MM = 30 - 8.12M 202.00M 34 VOLUME_HEIGHT_IN_MM = 60 - 8.12M 202.00M 35 RANDOM_SEED = 471 - 8.12M 202.00M 36 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 8.12M 202.00M 37 SAVE_REFLECTANCE = True - 8.12M 202.00M 38 SAVE_PHOTON_DIRECTION = False - 39 - 40 # If VISUALIZE is set to True, the simulation result will be plotted - 8.12M 202.00M 41 VISUALIZE = True - 42 - 43 - 8.12M 202.00M 44 def create_example_tissue(): - 45 """ - 46 This is a very simple example script of how to create a tissue definition. - 47 It contains only a generic background tissue material. - 48 """ - 49 background_dictionary = sp.Settings() - 50 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 51 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 52 - 53 tissue_dict = sp.Settings() - 54 tissue_dict[Tags.BACKGROUND] = background_dictionary - 55 return tissue_dict - 56 - 57 - 58 # Seed the numpy random configuration prior to creating the global_settings file in - 59 # order to ensure that the same volume - 60 # is generated with the same random seed every time. - 61 - 8.12M 202.00M 62 np.random.seed(RANDOM_SEED) - 63 - 8.12M 202.00M 64 general_settings = { - 65 # These parameters set the general properties of the simulated volume - 8.12M 202.00M 66 Tags.RANDOM_SEED: RANDOM_SEED, - 8.12M 202.00M 67 Tags.VOLUME_NAME: VOLUME_NAME, - 8.12M 202.00M 68 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 8.12M 202.00M 69 Tags.SPACING_MM: SPACING, - 8.12M 202.00M 70 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 8.12M 202.00M 71 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 8.12M 202.00M 72 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 8.12M 202.00M 73 Tags.WAVELENGTHS: [500], - 8.12M 202.00M 74 Tags.DO_FILE_COMPRESSION: True - 75 } - 76 - 8.12M 202.00M 77 settings = sp.Settings(general_settings) - 78 - 8.12M 202.00M 79 settings.set_volume_creation_settings({ - 8.12M 202.00M 80 Tags.STRUCTURES: create_example_tissue() - 81 }) - 8.12M 202.00M 82 settings.set_optical_settings({ - 8.12M 202.00M 83 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 8.12M 202.00M 84 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 8.12M 202.00M 85 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 8.12M 202.00M 86 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 87 }) - 88 - 8.12M 202.00M 89 pipeline = [ - 8.12M 202.00M 90 sp.ModelBasedVolumeCreationAdapter(settings), - 8.12M 202.00M 91 sp.MCXAdapterReflectance(settings), - 92 ] - 93 - 8.12M 202.00M 94 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 8.12M 202.00M 95 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 96 - 1.13G 1.19G 97 sp.simulate(pipeline, settings, device) - 98 - 8.12M 148.00M 99 if visualise: - 100 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 101 wavelength=settings[Tags.WAVELENGTH], - 102 show_initial_pressure=True, - 103 show_absorption=True, - 104 show_diffuse_reflectance=SAVE_REFLECTANCE, - 105 log_scale=True) -## run_minimal_optical_simulation - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 148.00M 19 @profile - 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 8.12M 148.00M 28 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 8.12M 148.00M 29 VOLUME_PLANAR_DIM_IN_MM = 30 - 8.12M 148.00M 30 VOLUME_HEIGHT_IN_MM = 60 - 8.12M 148.00M 31 RANDOM_SEED = 471 - 8.12M 148.00M 32 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 8.12M 148.00M 33 SAVE_REFLECTANCE = False - 8.12M 148.00M 34 SAVE_PHOTON_DIRECTION = False - 35 - 36 # If VISUALIZE is set to True, the simulation result will be plotted - 37 - 8.12M 148.00M 38 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and a blood vessel. - 43 """ - 44 background_dictionary = sp.Settings() - 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 muscle_dictionary = sp.Settings() - 49 muscle_dictionary[Tags.PRIORITY] = 1 - 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] - 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 vessel_1_dictionary = sp.Settings() - 58 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 10, - 61 VOLUME_HEIGHT_IN_MM/2] - 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 12, - 64 VOLUME_HEIGHT_IN_MM/2] - 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() - 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 epidermis_dictionary = sp.Settings() - 71 epidermis_dictionary[Tags.PRIORITY] = 8 - 72 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] - 73 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] - 74 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 75 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 76 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 77 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 78 - 79 tissue_dict = sp.Settings() - 80 tissue_dict[Tags.BACKGROUND] = background_dictionary - 81 tissue_dict["muscle"] = muscle_dictionary - 82 tissue_dict["epidermis"] = epidermis_dictionary - 83 tissue_dict["vessel_1"] = vessel_1_dictionary - 84 return tissue_dict - 85 - 86 - 87 # Seed the numpy random configuration prior to creating the global_settings file in - 88 # order to ensure that the same volume - 89 # is generated with the same random seed every time. - 90 - 8.12M 148.00M 91 np.random.seed(RANDOM_SEED) - 92 - 8.12M 148.00M 93 general_settings = { - 94 # These parameters set the general properties of the simulated volume - 8.12M 148.00M 95 Tags.RANDOM_SEED: RANDOM_SEED, - 8.12M 148.00M 96 Tags.VOLUME_NAME: VOLUME_NAME, - 8.12M 148.00M 97 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 8.12M 148.00M 98 Tags.SPACING_MM: SPACING, - 8.12M 148.00M 99 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 8.12M 148.00M 100 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 8.12M 148.00M 101 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 8.12M 148.00M 102 Tags.WAVELENGTHS: [798], - 8.12M 148.00M 103 Tags.DO_FILE_COMPRESSION: True, - 8.12M 148.00M 104 Tags.GPU: True - 105 } - 106 - 8.12M 148.00M 107 settings = sp.Settings(general_settings) - 108 - 8.12M 148.00M 109 settings.set_volume_creation_settings({ - 8.12M 148.00M 110 Tags.SIMULATE_DEFORMED_LAYERS: True, - 8.12M 148.00M 111 Tags.STRUCTURES: create_example_tissue() - 112 }) - 8.12M 148.00M 113 settings.set_optical_settings({ - 8.12M 148.00M 114 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 8.12M 148.00M 115 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 8.12M 148.00M 116 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 8.12M 148.00M 117 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 118 }) - 8.12M 148.00M 119 settings["noise_model_1"] = { - 8.12M 148.00M 120 Tags.NOISE_MEAN: 1.0, - 8.12M 148.00M 121 Tags.NOISE_STD: 0.1, - 8.12M 148.00M 122 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 8.12M 148.00M 123 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 8.12M 148.00M 124 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 125 } - 126 - 8.12M 148.00M 127 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: - 8.12M 148.00M 128 pipeline = [ - 8.12M 148.00M 129 sp.ModelBasedVolumeCreationAdapter(settings), - 8.12M 148.00M 130 sp.MCXAdapter(settings), - 8.12M 148.00M 131 sp.GaussianNoise(settings, "noise_model_1") - 132 ] - 133 else: - 134 pipeline = [ - 135 sp.ModelBasedVolumeCreationAdapter(settings), - 136 sp.MCXAdapterReflectance(settings), - 137 ] - 138 - 139 - 8.12M 148.00M 140 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): - 141 """ - 142 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. - 143 - 144 """ - 145 - 146 def __init__(self): - 147 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 148 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 149 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) - 150 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], - 151 direction_vector_mm=[0, 0, 1])) - 152 - 153 - 8.12M 148.00M 154 device = ExampleDeviceSlitIlluminationLinearDetector() - 155 - 1.17G 1.60G 156 sp.simulate(pipeline, settings, device) - 157 - 8.12M 202.00M 158 if Tags.WAVELENGTH in settings: - 8.12M 202.00M 159 WAVELENGTH = settings[Tags.WAVELENGTH] - 160 else: - 161 WAVELENGTH = 700 - 162 - 8.12M 202.00M 163 if visualise: - 164 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 165 wavelength=WAVELENGTH, - 166 show_initial_pressure=True, - 167 show_absorption=True, - 168 show_diffuse_reflectance=SAVE_REFLECTANCE, - 169 log_scale=True) -## run_linear_unmixing - -active_bytes reserved_bytes line code - all all - peak peak - 0.00B 0.00B 18 @profile - 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): - 20 """ - 21 - 22 :param SPACING: The simulation spacing between voxels - 23 :param path_manager: the path manager to be used, typically sp.PathManager - 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 25 :return: a run through of the example - 26 """ - 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you - 28 # set global params characterizing the simulated volume - 0.00B 0.00B 29 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 0.00B 0.00B 30 VOLUME_PLANAR_DIM_IN_MM = 20 - 0.00B 0.00B 31 VOLUME_HEIGHT_IN_MM = 25 - 0.00B 0.00B 32 RANDOM_SEED = 471 - 0.00B 0.00B 33 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) - 34 - 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths - 0.00B 0.00B 36 WAVELENGTHS = [750, 800, 850] - 37 - 0.00B 0.00B 38 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and two blood vessels. - 43 """ - 44 background_dictionary = sp.Settings() - 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 muscle_dictionary = sp.Settings() - 49 muscle_dictionary[Tags.PRIORITY] = 1 - 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 vessel_1_dictionary = sp.Settings() - 58 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 10, - 61 5] - 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 12, - 64 5] - 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) - 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 vessel_2_dictionary = sp.Settings() - 71 vessel_2_dictionary[Tags.PRIORITY] = 3 - 72 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 73 10, - 74 5] - 75 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 76 12, - 77 5] - 78 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 79 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) - 80 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 81 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 82 - 83 epidermis_dictionary = sp.Settings() - 84 epidermis_dictionary[Tags.PRIORITY] = 8 - 85 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 86 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] - 87 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 88 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 89 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 90 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 91 - 92 tissue_dict = sp.Settings() - 93 tissue_dict[Tags.BACKGROUND] = background_dictionary - 94 tissue_dict["muscle"] = muscle_dictionary - 95 tissue_dict["epidermis"] = epidermis_dictionary - 96 tissue_dict["vessel_1"] = vessel_1_dictionary - 97 tissue_dict["vessel_2"] = vessel_2_dictionary - 98 return tissue_dict - 99 - 100 - 101 # Seed the numpy random configuration prior to creating the global_settings file in - 102 # order to ensure that the same volume is generated with the same random seed every time. - 0.00B 0.00B 103 np.random.seed(RANDOM_SEED) - 104 - 105 # Initialize global settings and prepare for simulation pipeline including - 106 # volume creation and optical forward simulation. - 0.00B 0.00B 107 general_settings = { - 108 # These parameters set the general properties of the simulated volume - 0.00B 0.00B 109 Tags.RANDOM_SEED: RANDOM_SEED, - 0.00B 0.00B 110 Tags.VOLUME_NAME: VOLUME_NAME, - 0.00B 0.00B 111 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 0.00B 0.00B 112 Tags.SPACING_MM: SPACING, - 0.00B 0.00B 113 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 0.00B 0.00B 114 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 0.00B 0.00B 115 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 0.00B 0.00B 116 Tags.WAVELENGTHS: WAVELENGTHS, - 0.00B 0.00B 117 Tags.GPU: True, - 0.00B 0.00B 118 Tags.DO_FILE_COMPRESSION: True - 119 } - 0.00B 0.00B 120 settings = sp.Settings(general_settings) - 0.00B 0.00B 121 settings.set_volume_creation_settings({ - 0.00B 0.00B 122 Tags.SIMULATE_DEFORMED_LAYERS: True, - 0.00B 0.00B 123 Tags.STRUCTURES: create_example_tissue() - 124 }) - 0.00B 0.00B 125 settings.set_optical_settings({ - 0.00B 0.00B 126 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 0.00B 0.00B 127 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 0.00B 0.00B 128 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 0.00B 0.00B 129 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 130 }) - 131 - 132 # Set component settings for linear unmixing. - 133 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the - 134 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. - 135 # Please take a look at the component for more information. - 0.00B 0.00B 136 settings["linear_unmixing"] = { - 0.00B 0.00B 137 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 0.00B 0.00B 138 Tags.WAVELENGTHS: WAVELENGTHS, - 0.00B 0.00B 139 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( - 0.00B 0.00B 140 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] - 141 ), - 0.00B 0.00B 142 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, - 0.00B 0.00B 143 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True - 144 } - 145 - 146 # Get device for simulation - 0.00B 0.00B 147 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 0.00B 0.00B 148 VOLUME_PLANAR_DIM_IN_MM/2, - 0.00B 0.00B 149 0])) - 0.00B 0.00B 150 device.update_settings_for_use_of_model_based_volume_creator(settings) - 151 - 152 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS - 0.00B 0.00B 153 pipeline = [ - 0.00B 0.00B 154 sp.ModelBasedVolumeCreationAdapter(settings), - 0.00B 0.00B 155 sp.MCXAdapter(settings), - 0.00B 0.00B 156 sp.FieldOfViewCropping(settings), - 157 ] - 1.03G 1.45G 158 sp.simulate(pipeline, settings, device) - 159 - 160 # Run linear unmixing component with above specified settings. - 8.12M 148.00M 161 sp.LinearUnmixing(settings, "linear_unmixing").run() - 162 - 163 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. - 8.12M 148.00M 164 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 8.12M 148.00M 165 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) - 8.12M 148.00M 166 sO2 = lu_results["sO2"] - 167 - 8.12M 148.00M 168 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) - 8.12M 148.00M 169 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) - 8.12M 148.00M 170 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) - 171 - 172 # Visualize linear unmixing result - 8.12M 148.00M 173 if visualise: - 174 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 175 wavelength=WAVELENGTHS[0], - 176 show_initial_pressure=True, - 177 show_oxygenation=True, - 178 show_linear_unmixing_sO2=True) diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.4.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.4.txt deleted file mode 100644 index f71e0070..00000000 --- a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_gpu_0.4.txt +++ /dev/null @@ -1,1034 +0,0 @@ -## run_perform_iterative_qPAI_reconstruction - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 24.00M 25 @profile - 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise:bool = True): - 27 """ - 28 - 29 :param SPACING: The simulation spacing between voxels - 30 :param path_manager: the path manager to be used, typically sp.PathManager - 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 32 :return: a run through of the example - 33 """ - 8.12M 24.00M 34 VOLUME_TRANSDUCER_DIM_IN_MM = 30 - 8.12M 24.00M 35 VOLUME_PLANAR_DIM_IN_MM = 30 - 8.12M 24.00M 36 VOLUME_HEIGHT_IN_MM = 30 - 8.12M 24.00M 37 RANDOM_SEED = 471 - 8.12M 24.00M 38 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) - 39 - 40 # If VISUALIZE is set to True, the reconstruction result will be plotted - 41 - 8.12M 24.00M 42 def create_example_tissue(): - 43 """ - 44 This is a very simple example script of how to create a tissue definition. - 45 It contains a muscular background, an epidermis layer on top of the muscles - 46 and a blood vessel. - 47 """ - 48 background_dictionary = sp.Settings() - 49 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) - 50 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 51 - 52 epidermis_structure = sp.Settings() - 53 epidermis_structure[Tags.PRIORITY] = 1 - 54 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] - 55 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] - 56 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) - 57 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True - 58 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True - 59 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 60 - 61 vessel_structure_1 = sp.Settings() - 62 vessel_structure_1[Tags.PRIORITY] = 2 - 63 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, - 64 VOLUME_HEIGHT_IN_MM / 2] - 65 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, - 66 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] - 67 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 - 68 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 - 69 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) - 70 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True - 71 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True - 72 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE - 73 - 74 vessel_structure_2 = sp.Settings() - 75 vessel_structure_2[Tags.PRIORITY] = 3 - 76 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, - 77 VOLUME_HEIGHT_IN_MM / 3] - 78 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, - 79 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] - 80 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 - 81 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) - 82 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True - 83 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 84 - 85 tissue_dict = sp.Settings() - 86 tissue_dict[Tags.BACKGROUND] = background_dictionary - 87 tissue_dict["epidermis"] = epidermis_structure - 88 tissue_dict["vessel_1"] = vessel_structure_1 - 89 tissue_dict["vessel_2"] = vessel_structure_2 - 90 return tissue_dict - 91 - 92 - 93 # set settings for volume creation, optical simulation and iterative qPAI method - 8.12M 24.00M 94 np.random.seed(RANDOM_SEED) - 95 - 8.12M 24.00M 96 general_settings = { - 97 # These parameters set the general properties of the simulated volume - 8.12M 24.00M 98 Tags.RANDOM_SEED: RANDOM_SEED, - 8.12M 24.00M 99 Tags.VOLUME_NAME: VOLUME_NAME, - 8.12M 24.00M 100 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 8.12M 24.00M 101 Tags.SPACING_MM: SPACING, - 8.12M 24.00M 102 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 8.12M 24.00M 103 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 8.12M 24.00M 104 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 8.12M 24.00M 105 Tags.WAVELENGTHS: [700] - 106 } - 107 - 8.12M 24.00M 108 settings = sp.Settings(general_settings) - 109 - 8.12M 24.00M 110 settings.set_volume_creation_settings({ - 111 # These parameters set the properties for the volume creation - 8.12M 24.00M 112 Tags.SIMULATE_DEFORMED_LAYERS: True, - 8.12M 24.00M 113 Tags.STRUCTURES: create_example_tissue() - 114 }) - 8.12M 24.00M 115 settings.set_optical_settings({ - 116 # These parameters set the properties for the optical Monte Carlo simulation - 8.12M 24.00M 117 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 8.12M 24.00M 118 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 8.12M 24.00M 119 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 8.12M 24.00M 120 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 121 }) - 8.12M 24.00M 122 settings["noise_model"] = { - 8.12M 24.00M 123 Tags.NOISE_MEAN: 1.0, - 8.12M 24.00M 124 Tags.NOISE_STD: 0.01, - 8.12M 24.00M 125 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 8.12M 24.00M 126 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 8.12M 24.00M 127 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 128 } - 8.12M 24.00M 129 settings["iterative_qpai_reconstruction"] = { - 130 # These parameters set the properties of the iterative reconstruction - 8.12M 24.00M 131 Tags.DOWNSCALE_FACTOR: 0.75, - 8.12M 24.00M 132 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, - 133 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant - 8.12M 24.00M 134 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, - 8.12M 24.00M 135 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, - 136 # for this example, we are not interested in all absorption updates - 8.12M 24.00M 137 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, - 8.12M 24.00M 138 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 - 139 } - 140 - 141 # run pipeline including iterative qPAI method - 8.12M 24.00M 142 pipeline = [ - 8.12M 24.00M 143 sp.ModelBasedVolumeCreationAdapter(settings), - 8.12M 24.00M 144 sp.MCXAdapter(settings), - 8.12M 24.00M 145 sp.GaussianNoise(settings, "noise_model"), - 8.12M 24.00M 146 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") - 147 ] - 148 - 149 - 8.12M 24.00M 150 class CustomDevice(sp.PhotoacousticDevice): - 151 - 152 def __init__(self): - 153 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, - 154 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, - 155 0])) - 156 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) - 157 - 158 - 8.12M 24.00M 159 device = CustomDevice() - 160 - 8.12M 24.00M 161 device.update_settings_for_use_of_model_based_volume_creator(settings) - 162 - 52.01M 66.00M 163 sp.simulate(pipeline, settings, device) - 164 - 165 # visualize reconstruction results - 8.12M 22.00M 166 if visualise: - 167 # get simulation output - 168 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 169 settings = sp.load_data_field(data_path, Tags.SETTINGS) - 170 wavelength = settings[Tags.WAVELENGTHS][0] - 171 - 172 # get reconstruction result - 173 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) - 174 - 175 # get ground truth absorption coefficients - 176 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) - 177 - 178 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) - 179 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR - 180 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") - 181 - 182 # compute reconstruction error - 183 difference = absorption_gt - absorption_reconstruction - 184 - 185 median_error = np.median(difference) - 186 q3, q1 = np.percentile(difference, [75, 25]) - 187 iqr = q3 - q1 - 188 - 189 # visualize results - 190 x_pos = int(np.shape(absorption_gt)[0] / 2) - 191 y_pos = int(np.shape(absorption_gt)[1] / 2) - 192 - 193 if np.min(absorption_gt) > np.min(absorption_reconstruction): - 194 cmin = np.min(absorption_reconstruction) - 195 else: - 196 cmin = np.min(absorption_gt) - 197 - 198 if np.max(absorption_gt) > np.max(absorption_reconstruction): - 199 cmax = np.max(absorption_gt) - 200 else: - 201 cmax = np.max(absorption_reconstruction) - 202 - 203 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] - 204 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] - 205 - 206 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", - 207 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] - 208 - 209 plt.figure(figsize=(20, 15)) - 210 plt.subplots_adjust(hspace=0.5) - 211 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + - 212 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) - 213 - 214 for i, quantity in enumerate(results_x_z): - 215 plt.subplot(2, len(results_x_z), i + 1) - 216 if i == 0: - 217 plt.ylabel("x-z", fontsize=10) - 218 plt.title(label[i], fontsize=10) - 219 plt.imshow(quantity.T) - 220 plt.xticks(fontsize=6) - 221 plt.yticks(fontsize=6) - 222 plt.colorbar() - 223 if i != 2: - 224 plt.clim(cmin, cmax) - 225 else: - 226 plt.clim(np.min(difference), np.max(difference)) - 227 - 228 for i, quantity in enumerate(results_y_z): - 229 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) - 230 if i == 0: - 231 plt.ylabel("y-z", fontsize=10) - 232 plt.title(label[i], fontsize=10) - 233 plt.imshow(quantity.T) - 234 plt.xticks(fontsize=6) - 235 plt.yticks(fontsize=6) - 236 plt.colorbar() - 237 if i != 2: - 238 plt.clim(cmin, cmax) - 239 else: - 240 plt.clim(np.min(difference), np.max(difference)) - 241 - 242 plt.show() - 243 plt.close() -## run_optical_and_acoustic_simulation - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 20.00M 18 @profile - 19 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), - 20 visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 8.12M 20.00M 28 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 8.12M 20.00M 29 VOLUME_PLANAR_DIM_IN_MM = 20 - 8.12M 20.00M 30 VOLUME_HEIGHT_IN_MM = 25 - 8.12M 20.00M 31 RANDOM_SEED = 4711 - 32 - 33 # If VISUALIZE is set to True, the simulation result will be plotted - 8.12M 20.00M 34 VISUALIZE = True - 35 - 8.12M 20.00M 36 def create_example_tissue(): - 37 """ - 38 This is a very simple example script of how to create a tissue definition. - 39 It contains a muscular background, an epidermis layer on top of the muscles - 40 and a blood vessel. - 41 """ - 42 background_dictionary = sp.Settings() - 43 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) - 44 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 45 - 46 tissue_dict = sp.Settings() - 47 tissue_dict[Tags.BACKGROUND] = background_dictionary - 48 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, - 49 molecular_composition=sp.TISSUE_LIBRARY.constant( - 50 0.05, 100, 0.9), - 51 priority=1, - 52 consider_partial_volume=True, - 53 adhere_to_deformation=True) - 54 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, - 55 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), - 56 priority=8, - 57 consider_partial_volume=True, - 58 adhere_to_deformation=True) - 59 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( - 60 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], - 61 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], - 62 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 63 radius_mm=2, priority=3, consider_partial_volume=True, - 64 adhere_to_deformation=False - 65 ) - 66 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( - 67 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], - 68 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], - 69 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 70 radius_mm=3, priority=3, consider_partial_volume=True, - 71 adhere_to_deformation=False - 72 ) - 73 return tissue_dict - 74 - 75 - 76 # Seed the numpy random configuration prior to creating the global_settings file in - 77 # order to ensure that the same volume - 78 # is generated with the same random seed every time. - 79 - 8.12M 20.00M 80 np.random.seed(RANDOM_SEED) - 8.12M 20.00M 81 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) - 82 - 8.12M 20.00M 83 general_settings = { - 84 # These parameters set the general properties of the simulated volume - 8.12M 20.00M 85 Tags.RANDOM_SEED: RANDOM_SEED, - 8.12M 20.00M 86 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), - 8.12M 20.00M 87 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 8.12M 20.00M 88 Tags.SPACING_MM: SPACING, - 8.12M 20.00M 89 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 8.12M 20.00M 90 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 8.12M 20.00M 91 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 8.12M 20.00M 92 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 8.12M 20.00M 93 Tags.GPU: True, - 8.12M 20.00M 94 Tags.WAVELENGTHS: [700, 800], - 8.12M 20.00M 95 Tags.DO_FILE_COMPRESSION: True, - 8.12M 20.00M 96 Tags.DO_IPASC_EXPORT: True - 97 } - 8.12M 20.00M 98 settings = sp.Settings(general_settings) - 8.12M 20.00M 99 np.random.seed(RANDOM_SEED) - 100 - 8.12M 20.00M 101 settings.set_volume_creation_settings({ - 8.12M 20.00M 102 Tags.STRUCTURES: create_example_tissue(), - 8.12M 20.00M 103 Tags.SIMULATE_DEFORMED_LAYERS: True - 104 }) - 105 - 8.12M 20.00M 106 settings.set_optical_settings({ - 8.12M 20.00M 107 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 8.12M 20.00M 108 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 8.12M 20.00M 109 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, - 8.12M 20.00M 110 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 8.12M 20.00M 111 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, - 112 }) - 113 - 8.12M 20.00M 114 settings.set_acoustic_settings({ - 8.12M 20.00M 115 Tags.ACOUSTIC_SIMULATION_3D: False, - 8.12M 20.00M 116 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 8.12M 20.00M 117 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 8.12M 20.00M 118 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 8.12M 20.00M 119 Tags.KWAVE_PROPERTY_PMLInside: False, - 8.12M 20.00M 120 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 8.12M 20.00M 121 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 8.12M 20.00M 122 Tags.KWAVE_PROPERTY_PlotPML: False, - 8.12M 20.00M 123 Tags.RECORDMOVIE: False, - 8.12M 20.00M 124 Tags.MOVIENAME: "visualization_log", - 8.12M 20.00M 125 Tags.ACOUSTIC_LOG_SCALE: True - 126 }) - 127 - 8.12M 20.00M 128 settings.set_reconstruction_settings({ - 8.12M 20.00M 129 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 8.12M 20.00M 130 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 8.12M 20.00M 131 Tags.ACOUSTIC_SIMULATION_3D: False, - 8.12M 20.00M 132 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 8.12M 20.00M 133 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 8.12M 20.00M 134 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), - 8.12M 20.00M 135 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), - 8.12M 20.00M 136 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 8.12M 20.00M 137 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 8.12M 20.00M 138 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, - 8.12M 20.00M 139 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 8.12M 20.00M 140 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 8.12M 20.00M 141 Tags.KWAVE_PROPERTY_PMLInside: False, - 8.12M 20.00M 142 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 8.12M 20.00M 143 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 8.12M 20.00M 144 Tags.KWAVE_PROPERTY_PlotPML: False, - 8.12M 20.00M 145 Tags.RECORDMOVIE: False, - 8.12M 20.00M 146 Tags.MOVIENAME: "visualization_log", - 8.12M 20.00M 147 Tags.ACOUSTIC_LOG_SCALE: True, - 8.12M 20.00M 148 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, - 8.12M 20.00M 149 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, - 8.12M 20.00M 150 Tags.DATA_FIELD_DENSITY: 1000, - 8.12M 20.00M 151 Tags.SPACING_MM: SPACING - 152 }) - 153 - 8.12M 20.00M 154 settings["noise_initial_pressure"] = { - 8.12M 20.00M 155 Tags.NOISE_MEAN: 1, - 8.12M 20.00M 156 Tags.NOISE_STD: 0.01, - 8.12M 20.00M 157 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 8.12M 20.00M 158 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 8.12M 20.00M 159 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 160 } - 161 - 8.12M 20.00M 162 settings["noise_time_series"] = { - 8.12M 20.00M 163 Tags.NOISE_STD: 1, - 8.12M 20.00M 164 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, - 8.12M 20.00M 165 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA - 166 } - 167 - 168 # TODO: For the device choice, uncomment the undesired device - 169 - 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 171 # VOLUME_PLANAR_DIM_IN_MM/2, - 172 # 0])) - 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) - 174 - 8.12M 20.00M 175 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 8.12M 20.00M 176 VOLUME_PLANAR_DIM_IN_MM/2, - 8.12M 20.00M 177 0]), - 8.12M 20.00M 178 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) - 8.12M 20.00M 179 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, - 8.12M 20.00M 180 pitch_mm=0.25, - 8.12M 20.00M 181 number_detector_elements=100, - 8.12M 20.00M 182 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) - 8.12M 20.00M 183 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) - 8.12M 20.00M 184 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) - 185 - 186 - 8.12M 20.00M 187 SIMULATION_PIPELINE = [ - 8.12M 20.00M 188 sp.ModelBasedVolumeCreationAdapter(settings), - 8.12M 20.00M 189 sp.MCXAdapter(settings), - 8.12M 20.00M 190 sp.GaussianNoise(settings, "noise_initial_pressure"), - 8.12M 20.00M 191 sp.KWaveAdapter(settings), - 8.12M 20.00M 192 sp.GaussianNoise(settings, "noise_time_series"), - 8.12M 20.00M 193 sp.TimeReversalAdapter(settings), - 8.12M 20.00M 194 sp.FieldOfViewCropping(settings) - 195 ] - 196 - 62.10M 80.00M 197 sp.simulate(SIMULATION_PIPELINE, settings, device) - 198 - 199 if Tags.WAVELENGTH in settings: - 200 WAVELENGTH = settings[Tags.WAVELENGTH] - 201 else: - 202 WAVELENGTH = 700 - 203 - 204 if visualise: - 205 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - 206 wavelength=WAVELENGTH, - 207 show_time_series_data=True, - 208 show_initial_pressure=True, - 209 show_reconstructed_data=True, - 210 log_scale=False, - 211 show_xz_only=False) -## run_msot_invision_simulation - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 20.00M 13 @profile - 14 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise:bool = True): - 15 """ - 16 - 17 :param SPACING: The simulation spacing between voxels - 18 :param path_manager: the path manager to be used, typically sp.PathManager - 19 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 20 :return: a run through of the example - 21 """ - 8.12M 20.00M 22 SPEED_OF_SOUND = 1500 - 8.12M 20.00M 23 XZ_DIM = 90 - 8.12M 20.00M 24 Y_DIM = 40 - 25 - 8.12M 20.00M 26 def create_pipeline(_settings: sp.Settings): - 27 return [ - 28 sp.ModelBasedVolumeCreationAdapter(settings), - 29 sp.MCXAdapter(settings), - 30 sp.KWaveAdapter(settings), - 31 sp.FieldOfViewCropping(settings), - 32 sp.TimeReversalAdapter(settings) - 33 ] - 34 - 35 - 8.12M 20.00M 36 def get_device(): - 37 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) - 38 return pa_device - 39 - 40 - 8.12M 20.00M 41 def create_volume(): - 42 inclusion_material = sp.Molecule(volume_fraction=1.0, - 43 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 44 0.9), - 45 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 46 100.0), - 47 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 48 4.0), - 49 speed_of_sound=SPEED_OF_SOUND, - 50 alpha_coefficient=1e-4, - 51 density=1000, - 52 gruneisen_parameter=1.0, - 53 name="Inclusion") - 54 - 55 phantom_material = sp.Molecule(volume_fraction=1.0, - 56 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), - 57 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 58 100.0), - 59 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), - 60 speed_of_sound=SPEED_OF_SOUND, - 61 alpha_coefficient=1e-4, - 62 density=1000, - 63 gruneisen_parameter=1.0, - 64 name="Phantom") - 65 - 66 heavy_water = sp.Molecule(volume_fraction=1.0, - 67 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), - 68 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), - 69 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), - 70 speed_of_sound=SPEED_OF_SOUND, - 71 alpha_coefficient=1e-4, - 72 density=1000, - 73 gruneisen_parameter=1.0, - 74 name="background_water") - 75 - 76 background_dictionary = sp.Settings() - 77 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 78 .append(heavy_water) - 79 .get_molecular_composition(segmentation_type=-1)) - 80 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 81 - 82 phantom_material_dictionary = sp.Settings() - 83 phantom_material_dictionary[Tags.PRIORITY] = 3 - 84 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] - 85 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 - 86 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 - 87 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 - 88 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 89 .append(phantom_material) - 90 .get_molecular_composition(segmentation_type=0)) - 91 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 92 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 93 - 94 inclusion_1_dictionary = sp.Settings() - 95 inclusion_1_dictionary[Tags.PRIORITY] = 8 - 96 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] - 97 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 - 98 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 - 99 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 - 100 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 101 .append(inclusion_material) - 102 .get_molecular_composition(segmentation_type=1)) - 103 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 104 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 105 - 106 inclusion_2_dictionary = sp.Settings() - 107 inclusion_2_dictionary[Tags.PRIORITY] = 5 - 108 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] - 109 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] - 110 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 111 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 112 .append(inclusion_material) - 113 .get_molecular_composition(segmentation_type=2)) - 114 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 115 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 116 - 117 tissue_dict = sp.Settings() - 118 tissue_dict[Tags.BACKGROUND] = background_dictionary - 119 tissue_dict["phantom"] = phantom_material_dictionary - 120 tissue_dict["inclusion_1"] = inclusion_1_dictionary - 121 tissue_dict["inclusion_2"] = inclusion_2_dictionary - 122 return { - 123 Tags.STRUCTURES: tissue_dict, - 124 Tags.SIMULATE_DEFORMED_LAYERS: False - 125 } - 126 - 127 - 8.12M 20.00M 128 def get_settings(): - 129 general_settings = { - 130 # These parameters set the general properties of the simulated volume - 131 Tags.RANDOM_SEED: 4711, - 132 Tags.VOLUME_NAME: "InVision Simulation Example", - 133 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 134 Tags.SPACING_MM: SPACING, - 135 Tags.DIM_VOLUME_Z_MM: XZ_DIM, - 136 Tags.DIM_VOLUME_X_MM: XZ_DIM, - 137 Tags.DIM_VOLUME_Y_MM: Y_DIM, - 138 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 139 Tags.GPU: True, - 140 Tags.WAVELENGTHS: [700] - 141 } - 142 - 143 volume_settings = create_volume() - 144 - 145 optical_settings = { - 146 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 147 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 148 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, - 149 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 150 } - 151 - 152 acoustic_settings = { - 153 Tags.ACOUSTIC_SIMULATION_3D: True, - 154 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 155 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 156 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 157 Tags.KWAVE_PROPERTY_PMLInside: False, - 158 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 159 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 160 Tags.KWAVE_PROPERTY_PlotPML: False, - 161 Tags.RECORDMOVIE: False, - 162 Tags.MOVIENAME: "visualization_log", - 163 Tags.ACOUSTIC_LOG_SCALE: True - 164 } - 165 - 166 reconstruction_settings = { - 167 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 168 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 169 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 170 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 171 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, - 172 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 173 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, - 174 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 175 Tags.KWAVE_PROPERTY_PMLInside: False, - 176 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 177 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 178 Tags.KWAVE_PROPERTY_PlotPML: False, - 179 Tags.RECORDMOVIE: False, - 180 Tags.MOVIENAME: "visualization_log", - 181 Tags.ACOUSTIC_LOG_SCALE: True, - 182 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 183 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 184 Tags.SPACING_MM: 0.25, - 185 } - 186 - 187 _settings = sp.Settings(general_settings) - 188 _settings.set_volume_creation_settings(volume_settings) - 189 _settings.set_optical_settings(optical_settings) - 190 _settings.set_acoustic_settings(acoustic_settings) - 191 _settings.set_reconstruction_settings(reconstruction_settings) - 192 return _settings - 193 - 194 - 8.12M 20.00M 195 device = get_device() - 8.12M 20.00M 196 settings = get_settings() - 8.12M 20.00M 197 pipeline = create_pipeline(settings) - 198 - 442.87M 556.00M 199 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) - 200 - 8.12M 20.00M 201 if visualise: - 202 sp.visualise_data(settings=settings, - 203 path_manager=path_manager, - 204 show_absorption=True, - 205 show_initial_pressure=True, - 206 show_reconstructed_data=True, - 207 show_xz_only=True) -## run_minimal_optical_simulation_uniform_cube - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 42.00M 22 @profile - 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), - 24 visualise:bool = True): - 25 """ - 26 - 27 :param SPACING: The simulation spacing between voxels - 28 :param path_manager: the path manager to be used, typically sp.PathManager - 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 30 :return: a run through of the example - 31 """ - 8.12M 42.00M 32 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 8.12M 42.00M 33 VOLUME_PLANAR_DIM_IN_MM = 30 - 8.12M 42.00M 34 VOLUME_HEIGHT_IN_MM = 60 - 8.12M 42.00M 35 RANDOM_SEED = 471 - 8.12M 42.00M 36 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 8.12M 42.00M 37 SAVE_REFLECTANCE = True - 8.12M 42.00M 38 SAVE_PHOTON_DIRECTION = False - 39 - 40 # If VISUALIZE is set to True, the simulation result will be plotted - 8.12M 42.00M 41 VISUALIZE = True - 42 - 43 - 8.12M 42.00M 44 def create_example_tissue(): - 45 """ - 46 This is a very simple example script of how to create a tissue definition. - 47 It contains only a generic background tissue material. - 48 """ - 49 background_dictionary = sp.Settings() - 50 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 51 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 52 - 53 tissue_dict = sp.Settings() - 54 tissue_dict[Tags.BACKGROUND] = background_dictionary - 55 return tissue_dict - 56 - 57 - 58 # Seed the numpy random configuration prior to creating the global_settings file in - 59 # order to ensure that the same volume - 60 # is generated with the same random seed every time. - 61 - 8.12M 42.00M 62 np.random.seed(RANDOM_SEED) - 63 - 8.12M 42.00M 64 general_settings = { - 65 # These parameters set the general properties of the simulated volume - 8.12M 42.00M 66 Tags.RANDOM_SEED: RANDOM_SEED, - 8.12M 42.00M 67 Tags.VOLUME_NAME: VOLUME_NAME, - 8.12M 42.00M 68 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 8.12M 42.00M 69 Tags.SPACING_MM: SPACING, - 8.12M 42.00M 70 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 8.12M 42.00M 71 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 8.12M 42.00M 72 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 8.12M 42.00M 73 Tags.WAVELENGTHS: [500], - 8.12M 42.00M 74 Tags.DO_FILE_COMPRESSION: True - 75 } - 76 - 8.12M 42.00M 77 settings = sp.Settings(general_settings) - 78 - 8.12M 42.00M 79 settings.set_volume_creation_settings({ - 8.12M 42.00M 80 Tags.STRUCTURES: create_example_tissue() - 81 }) - 8.12M 42.00M 82 settings.set_optical_settings({ - 8.12M 42.00M 83 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 8.12M 42.00M 84 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 8.12M 42.00M 85 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 8.12M 42.00M 86 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 87 }) - 88 - 8.12M 42.00M 89 pipeline = [ - 8.12M 42.00M 90 sp.ModelBasedVolumeCreationAdapter(settings), - 8.12M 42.00M 91 sp.MCXAdapterReflectance(settings), - 92 ] - 93 - 8.12M 42.00M 94 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 8.12M 42.00M 95 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 96 - 155.75M 162.00M 97 sp.simulate(pipeline, settings, device) - 98 - 8.12M 20.00M 99 if visualise: - 100 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 101 wavelength=settings[Tags.WAVELENGTH], - 102 show_initial_pressure=True, - 103 show_absorption=True, - 104 show_diffuse_reflectance=SAVE_REFLECTANCE, - 105 log_scale=True) -## run_minimal_optical_simulation - -active_bytes reserved_bytes line code - all all - peak peak - 8.12M 20.00M 19 @profile - 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 8.12M 20.00M 28 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 8.12M 20.00M 29 VOLUME_PLANAR_DIM_IN_MM = 30 - 8.12M 20.00M 30 VOLUME_HEIGHT_IN_MM = 60 - 8.12M 20.00M 31 RANDOM_SEED = 471 - 8.12M 20.00M 32 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 8.12M 20.00M 33 SAVE_REFLECTANCE = False - 8.12M 20.00M 34 SAVE_PHOTON_DIRECTION = False - 35 - 36 # If VISUALIZE is set to True, the simulation result will be plotted - 37 - 8.12M 20.00M 38 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and a blood vessel. - 43 """ - 44 background_dictionary = sp.Settings() - 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 muscle_dictionary = sp.Settings() - 49 muscle_dictionary[Tags.PRIORITY] = 1 - 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] - 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 vessel_1_dictionary = sp.Settings() - 58 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 10, - 61 VOLUME_HEIGHT_IN_MM/2] - 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 12, - 64 VOLUME_HEIGHT_IN_MM/2] - 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() - 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 epidermis_dictionary = sp.Settings() - 71 epidermis_dictionary[Tags.PRIORITY] = 8 - 72 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] - 73 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] - 74 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 75 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 76 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 77 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 78 - 79 tissue_dict = sp.Settings() - 80 tissue_dict[Tags.BACKGROUND] = background_dictionary - 81 tissue_dict["muscle"] = muscle_dictionary - 82 tissue_dict["epidermis"] = epidermis_dictionary - 83 tissue_dict["vessel_1"] = vessel_1_dictionary - 84 return tissue_dict - 85 - 86 - 87 # Seed the numpy random configuration prior to creating the global_settings file in - 88 # order to ensure that the same volume - 89 # is generated with the same random seed every time. - 90 - 8.12M 20.00M 91 np.random.seed(RANDOM_SEED) - 92 - 8.12M 20.00M 93 general_settings = { - 94 # These parameters set the general properties of the simulated volume - 8.12M 20.00M 95 Tags.RANDOM_SEED: RANDOM_SEED, - 8.12M 20.00M 96 Tags.VOLUME_NAME: VOLUME_NAME, - 8.12M 20.00M 97 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 8.12M 20.00M 98 Tags.SPACING_MM: SPACING, - 8.12M 20.00M 99 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 8.12M 20.00M 100 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 8.12M 20.00M 101 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 8.12M 20.00M 102 Tags.WAVELENGTHS: [798], - 8.12M 20.00M 103 Tags.DO_FILE_COMPRESSION: True, - 8.12M 20.00M 104 Tags.GPU: True - 105 } - 106 - 8.12M 20.00M 107 settings = sp.Settings(general_settings) - 108 - 8.12M 20.00M 109 settings.set_volume_creation_settings({ - 8.12M 20.00M 110 Tags.SIMULATE_DEFORMED_LAYERS: True, - 8.12M 20.00M 111 Tags.STRUCTURES: create_example_tissue() - 112 }) - 8.12M 20.00M 113 settings.set_optical_settings({ - 8.12M 20.00M 114 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 8.12M 20.00M 115 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 8.12M 20.00M 116 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 8.12M 20.00M 117 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 118 }) - 8.12M 20.00M 119 settings["noise_model_1"] = { - 8.12M 20.00M 120 Tags.NOISE_MEAN: 1.0, - 8.12M 20.00M 121 Tags.NOISE_STD: 0.1, - 8.12M 20.00M 122 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 8.12M 20.00M 123 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 8.12M 20.00M 124 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 125 } - 126 - 8.12M 20.00M 127 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: - 8.12M 20.00M 128 pipeline = [ - 8.12M 20.00M 129 sp.ModelBasedVolumeCreationAdapter(settings), - 8.12M 20.00M 130 sp.MCXAdapter(settings), - 8.12M 20.00M 131 sp.GaussianNoise(settings, "noise_model_1") - 132 ] - 133 else: - 134 pipeline = [ - 135 sp.ModelBasedVolumeCreationAdapter(settings), - 136 sp.MCXAdapterReflectance(settings), - 137 ] - 138 - 139 - 8.12M 20.00M 140 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): - 141 """ - 142 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. - 143 - 144 """ - 145 - 146 def __init__(self): - 147 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 148 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 149 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) - 150 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], - 151 direction_vector_mm=[0, 0, 1])) - 152 - 153 - 8.12M 20.00M 154 device = ExampleDeviceSlitIlluminationLinearDetector() - 155 - 160.20M 220.00M 156 sp.simulate(pipeline, settings, device) - 157 - 8.12M 42.00M 158 if Tags.WAVELENGTH in settings: - 8.12M 42.00M 159 WAVELENGTH = settings[Tags.WAVELENGTH] - 160 else: - 161 WAVELENGTH = 700 - 162 - 8.12M 42.00M 163 if visualise: - 164 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 165 wavelength=WAVELENGTH, - 166 show_initial_pressure=True, - 167 show_absorption=True, - 168 show_diffuse_reflectance=SAVE_REFLECTANCE, - 169 log_scale=True) -## run_linear_unmixing - -active_bytes reserved_bytes line code - all all - peak peak - 0.00B 0.00B 18 @profile - 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): - 20 """ - 21 - 22 :param SPACING: The simulation spacing between voxels - 23 :param path_manager: the path manager to be used, typically sp.PathManager - 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 25 :return: a run through of the example - 26 """ - 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you - 28 # set global params characterizing the simulated volume - 0.00B 0.00B 29 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 0.00B 0.00B 30 VOLUME_PLANAR_DIM_IN_MM = 20 - 0.00B 0.00B 31 VOLUME_HEIGHT_IN_MM = 25 - 0.00B 0.00B 32 RANDOM_SEED = 471 - 0.00B 0.00B 33 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) - 34 - 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths - 0.00B 0.00B 36 WAVELENGTHS = [750, 800, 850] - 37 - 0.00B 0.00B 38 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and two blood vessels. - 43 """ - 44 background_dictionary = sp.Settings() - 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 muscle_dictionary = sp.Settings() - 49 muscle_dictionary[Tags.PRIORITY] = 1 - 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 vessel_1_dictionary = sp.Settings() - 58 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 10, - 61 5] - 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 12, - 64 5] - 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) - 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 vessel_2_dictionary = sp.Settings() - 71 vessel_2_dictionary[Tags.PRIORITY] = 3 - 72 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 73 10, - 74 5] - 75 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 76 12, - 77 5] - 78 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 79 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) - 80 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 81 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 82 - 83 epidermis_dictionary = sp.Settings() - 84 epidermis_dictionary[Tags.PRIORITY] = 8 - 85 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 86 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] - 87 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 88 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 89 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 90 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 91 - 92 tissue_dict = sp.Settings() - 93 tissue_dict[Tags.BACKGROUND] = background_dictionary - 94 tissue_dict["muscle"] = muscle_dictionary - 95 tissue_dict["epidermis"] = epidermis_dictionary - 96 tissue_dict["vessel_1"] = vessel_1_dictionary - 97 tissue_dict["vessel_2"] = vessel_2_dictionary - 98 return tissue_dict - 99 - 100 - 101 # Seed the numpy random configuration prior to creating the global_settings file in - 102 # order to ensure that the same volume is generated with the same random seed every time. - 0.00B 0.00B 103 np.random.seed(RANDOM_SEED) - 104 - 105 # Initialize global settings and prepare for simulation pipeline including - 106 # volume creation and optical forward simulation. - 0.00B 0.00B 107 general_settings = { - 108 # These parameters set the general properties of the simulated volume - 0.00B 0.00B 109 Tags.RANDOM_SEED: RANDOM_SEED, - 0.00B 0.00B 110 Tags.VOLUME_NAME: VOLUME_NAME, - 0.00B 0.00B 111 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 0.00B 0.00B 112 Tags.SPACING_MM: SPACING, - 0.00B 0.00B 113 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 0.00B 0.00B 114 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 0.00B 0.00B 115 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 0.00B 0.00B 116 Tags.WAVELENGTHS: WAVELENGTHS, - 0.00B 0.00B 117 Tags.GPU: True, - 0.00B 0.00B 118 Tags.DO_FILE_COMPRESSION: True - 119 } - 0.00B 0.00B 120 settings = sp.Settings(general_settings) - 0.00B 0.00B 121 settings.set_volume_creation_settings({ - 0.00B 0.00B 122 Tags.SIMULATE_DEFORMED_LAYERS: True, - 0.00B 0.00B 123 Tags.STRUCTURES: create_example_tissue() - 124 }) - 0.00B 0.00B 125 settings.set_optical_settings({ - 0.00B 0.00B 126 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 0.00B 0.00B 127 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 0.00B 0.00B 128 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 0.00B 0.00B 129 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 130 }) - 131 - 132 # Set component settings for linear unmixing. - 133 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the - 134 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. - 135 # Please take a look at the component for more information. - 0.00B 0.00B 136 settings["linear_unmixing"] = { - 0.00B 0.00B 137 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 0.00B 0.00B 138 Tags.WAVELENGTHS: WAVELENGTHS, - 0.00B 0.00B 139 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( - 0.00B 0.00B 140 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] - 141 ), - 0.00B 0.00B 142 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, - 0.00B 0.00B 143 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True - 144 } - 145 - 146 # Get device for simulation - 0.00B 0.00B 147 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 0.00B 0.00B 148 VOLUME_PLANAR_DIM_IN_MM/2, - 0.00B 0.00B 149 0])) - 0.00B 0.00B 150 device.update_settings_for_use_of_model_based_volume_creator(settings) - 151 - 152 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS - 0.00B 0.00B 153 pipeline = [ - 0.00B 0.00B 154 sp.ModelBasedVolumeCreationAdapter(settings), - 0.00B 0.00B 155 sp.MCXAdapter(settings), - 0.00B 0.00B 156 sp.FieldOfViewCropping(settings), - 157 ] - 139.43M 192.00M 158 sp.simulate(pipeline, settings, device) - 159 - 160 # Run linear unmixing component with above specified settings. - 8.12M 20.00M 161 sp.LinearUnmixing(settings, "linear_unmixing").run() - 162 - 163 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. - 8.12M 20.00M 164 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 8.12M 20.00M 165 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) - 8.12M 20.00M 166 sO2 = lu_results["sO2"] - 167 - 8.12M 20.00M 168 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) - 8.12M 20.00M 169 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) - 8.12M 20.00M 170 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) - 171 - 172 # Visualize linear unmixing result - 8.12M 20.00M 173 if visualise: - 174 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 175 wavelength=WAVELENGTHS[0], - 176 show_initial_pressure=True, - 177 show_oxygenation=True, - 178 show_linear_unmixing_sO2=True) diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.2.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.2.txt deleted file mode 100644 index 39ea9177..00000000 --- a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.2.txt +++ /dev/null @@ -1,1027 +0,0 @@ -Filename: /home/f762e/Workspace/simpa/simpa_examples/linear_unmixing.py - -Line # Mem usage Increment Occurrences Line Contents -============================================================= - 18 445.0 MiB 445.0 MiB 1 @profile - 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager(), visualise: bool = True): - 20 """ - 21 - 22 :param SPACING: The simulation spacing between voxels - 23 :param path_manager: the path manager to be used, typically sp.PathManager - 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 25 :return: a run through of the example - 26 """ - 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you - 28 # set global params characterizing the simulated volume - 29 445.0 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 30 445.0 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 20 - 31 445.0 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 25 - 32 445.0 MiB 0.0 MiB 1 RANDOM_SEED = 471 - 33 445.0 MiB 0.0 MiB 1 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) - 34 - 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths - 36 445.0 MiB 0.0 MiB 1 WAVELENGTHS = [750, 800, 850] - 37 - 38 445.0 MiB 0.0 MiB 2 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and two blood vessels. - 43 """ - 44 445.0 MiB 0.0 MiB 1 background_dictionary = sp.Settings() - 45 445.4 MiB 0.3 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 445.4 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 445.4 MiB 0.0 MiB 1 muscle_dictionary = sp.Settings() - 49 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.PRIORITY] = 1 - 50 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 51 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 445.4 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 445.4 MiB 0.0 MiB 1 vessel_1_dictionary = sp.Settings() - 58 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 445.4 MiB 0.0 MiB 2 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 445.4 MiB 0.0 MiB 1 10, - 61 445.4 MiB 0.0 MiB 1 5] - 62 445.4 MiB 0.0 MiB 2 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 445.4 MiB 0.0 MiB 1 12, - 64 445.4 MiB 0.0 MiB 1 5] - 65 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) - 67 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 445.4 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 445.4 MiB 0.0 MiB 1 vessel_2_dictionary = sp.Settings() - 71 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.PRIORITY] = 3 - 72 445.4 MiB 0.0 MiB 2 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 73 445.4 MiB 0.0 MiB 1 10, - 74 445.4 MiB 0.0 MiB 1 5] - 75 445.4 MiB 0.0 MiB 2 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 76 445.4 MiB 0.0 MiB 1 12, - 77 445.4 MiB 0.0 MiB 1 5] - 78 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 79 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) - 80 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 81 445.4 MiB 0.0 MiB 1 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 82 - 83 445.4 MiB 0.0 MiB 1 epidermis_dictionary = sp.Settings() - 84 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.PRIORITY] = 8 - 85 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 86 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] - 87 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 88 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 89 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 90 445.4 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 91 - 92 445.4 MiB 0.0 MiB 1 tissue_dict = sp.Settings() - 93 445.4 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary - 94 445.4 MiB 0.0 MiB 1 tissue_dict["muscle"] = muscle_dictionary - 95 445.4 MiB 0.0 MiB 1 tissue_dict["epidermis"] = epidermis_dictionary - 96 445.4 MiB 0.0 MiB 1 tissue_dict["vessel_1"] = vessel_1_dictionary - 97 445.4 MiB 0.0 MiB 1 tissue_dict["vessel_2"] = vessel_2_dictionary - 98 445.4 MiB 0.0 MiB 1 return tissue_dict - 99 - 100 # Seed the numpy random configuration prior to creating the global_settings file in - 101 # order to ensure that the same volume is generated with the same random seed every time. - 102 445.0 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) - 103 - 104 # Initialize global settings and prepare for simulation pipeline including - 105 # volume creation and optical forward simulation. - 106 445.0 MiB 0.0 MiB 1 general_settings = { - 107 # These parameters set the general properties of the simulated volume - 108 445.0 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, - 109 445.0 MiB 0.0 MiB 1 Tags.VOLUME_NAME: VOLUME_NAME, - 110 445.0 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 111 445.0 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, - 112 445.0 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 113 445.0 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 114 445.0 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 115 445.0 MiB 0.0 MiB 1 Tags.WAVELENGTHS: WAVELENGTHS, - 116 445.0 MiB 0.0 MiB 1 Tags.GPU: True, - 117 445.0 MiB 0.0 MiB 1 Tags.DO_FILE_COMPRESSION: True - 118 } - 119 445.0 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) - 120 445.4 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ - 121 445.0 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: True, - 122 445.4 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue() - 123 }) - 124 445.4 MiB 0.0 MiB 2 settings.set_optical_settings({ - 125 445.4 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 126 445.4 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 127 445.4 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 128 445.4 MiB 0.0 MiB 1 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 129 }) - 130 - 131 # Set component settings for linear unmixing. - 132 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the - 133 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. - 134 # Please take a look at the component for more information. - 135 445.4 MiB 0.0 MiB 1 settings["linear_unmixing"] = { - 136 445.4 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 137 445.4 MiB 0.0 MiB 1 Tags.WAVELENGTHS: WAVELENGTHS, - 138 445.4 MiB 0.0 MiB 2 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( - 139 445.4 MiB 0.0 MiB 1 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] - 140 ), - 141 445.4 MiB 0.0 MiB 1 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, - 142 445.4 MiB 0.0 MiB 1 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True - 143 } - 144 - 145 # Get device for simulation - 146 445.4 MiB 0.0 MiB 2 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 147 445.4 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM/2, - 148 445.4 MiB 0.0 MiB 1 0])) - 149 445.4 MiB 0.0 MiB 1 device.update_settings_for_use_of_model_based_volume_creator(settings) - 150 - 151 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS - 152 446.9 MiB 0.0 MiB 1 pipeline = [ - 153 446.9 MiB 1.5 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), - 154 446.9 MiB 0.0 MiB 1 sp.MCXAdapter(settings), - 155 446.9 MiB 0.0 MiB 1 sp.FieldOfViewCropping(settings), - 156 ] - 157 973.5 MiB 526.6 MiB 1 sp.simulate(pipeline, settings, device) - 158 - 159 # Run linear unmixing component with above specified settings. - 160 979.7 MiB 6.2 MiB 1 sp.LinearUnmixing(settings, "linear_unmixing").run() - 161 - 162 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. - 163 979.7 MiB 0.0 MiB 1 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 164 979.7 MiB 0.0 MiB 1 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) - 165 979.7 MiB 0.0 MiB 1 sO2 = lu_results["sO2"] - 166 - 167 979.7 MiB 0.0 MiB 1 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) - 168 979.7 MiB 0.0 MiB 1 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) - 169 979.7 MiB 0.0 MiB 1 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) - 170 - 171 # Visualize linear unmixing result - 172 979.7 MiB 0.0 MiB 1 if visualise: - 173 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 174 wavelength=WAVELENGTHS[0], - 175 show_initial_pressure=True, - 176 show_oxygenation=True, - 177 show_linear_unmixing_sO2=True) - - -Filename: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation.py - -Line # Mem usage Increment Occurrences Line Contents -============================================================= - 19 979.7 MiB 979.7 MiB 1 @profile - 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 28 979.7 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 29 979.7 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 30 - 30 979.7 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 60 - 31 979.7 MiB 0.0 MiB 1 RANDOM_SEED = 471 - 32 979.7 MiB 0.0 MiB 1 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 33 979.7 MiB 0.0 MiB 1 SAVE_REFLECTANCE = False - 34 979.7 MiB 0.0 MiB 1 SAVE_PHOTON_DIRECTION = False - 35 - 36 # If VISUALIZE is set to True, the simulation result will be plotted - 37 - 38 979.7 MiB 0.0 MiB 2 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and a blood vessel. - 43 """ - 44 979.7 MiB 0.0 MiB 1 background_dictionary = sp.Settings() - 45 979.7 MiB 0.0 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 979.7 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 979.7 MiB 0.0 MiB 1 muscle_dictionary = sp.Settings() - 49 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.PRIORITY] = 1 - 50 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] - 51 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 979.7 MiB 0.0 MiB 1 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 979.7 MiB 0.0 MiB 1 vessel_1_dictionary = sp.Settings() - 58 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 979.7 MiB 0.0 MiB 2 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 979.7 MiB 0.0 MiB 1 10, - 61 979.7 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM/2] - 62 979.7 MiB 0.0 MiB 2 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 979.7 MiB 0.0 MiB 1 12, - 64 979.7 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM/2] - 65 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() - 67 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 979.7 MiB 0.0 MiB 1 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 979.7 MiB 0.0 MiB 1 epidermis_dictionary = sp.Settings() - 71 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.PRIORITY] = 8 - 72 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] - 73 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] - 74 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 75 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 76 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 77 979.7 MiB 0.0 MiB 1 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 78 - 79 979.7 MiB 0.0 MiB 1 tissue_dict = sp.Settings() - 80 979.7 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary - 81 979.7 MiB 0.0 MiB 1 tissue_dict["muscle"] = muscle_dictionary - 82 979.7 MiB 0.0 MiB 1 tissue_dict["epidermis"] = epidermis_dictionary - 83 979.7 MiB 0.0 MiB 1 tissue_dict["vessel_1"] = vessel_1_dictionary - 84 979.7 MiB 0.0 MiB 1 return tissue_dict - 85 - 86 # Seed the numpy random configuration prior to creating the global_settings file in - 87 # order to ensure that the same volume - 88 # is generated with the same random seed every time. - 89 - 90 979.7 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) - 91 - 92 979.7 MiB 0.0 MiB 1 general_settings = { - 93 # These parameters set the general properties of the simulated volume - 94 979.7 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, - 95 979.7 MiB 0.0 MiB 1 Tags.VOLUME_NAME: VOLUME_NAME, - 96 979.7 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 97 979.7 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, - 98 979.7 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 99 979.7 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 100 979.7 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 101 979.7 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [798], - 102 979.7 MiB 0.0 MiB 1 Tags.DO_FILE_COMPRESSION: True, - 103 979.7 MiB 0.0 MiB 1 Tags.GPU: True - 104 } - 105 - 106 979.7 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) - 107 - 108 979.7 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ - 109 979.7 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: True, - 110 979.7 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue() - 111 }) - 112 979.7 MiB 0.0 MiB 2 settings.set_optical_settings({ - 113 979.7 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 114 979.7 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 115 979.7 MiB 0.0 MiB 1 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 116 979.7 MiB 0.0 MiB 1 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 117 }) - 118 979.7 MiB 0.0 MiB 1 settings["noise_model_1"] = { - 119 979.7 MiB 0.0 MiB 1 Tags.NOISE_MEAN: 1.0, - 120 979.7 MiB 0.0 MiB 1 Tags.NOISE_STD: 0.1, - 121 979.7 MiB 0.0 MiB 1 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 122 979.7 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 123 979.7 MiB 0.0 MiB 1 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 124 } - 125 - 126 979.7 MiB 0.0 MiB 1 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: - 127 979.7 MiB 0.0 MiB 1 pipeline = [ - 128 979.7 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), - 129 979.7 MiB 0.0 MiB 1 sp.MCXAdapter(settings), - 130 979.7 MiB 0.0 MiB 1 sp.GaussianNoise(settings, "noise_model_1") - 131 ] - 132 else: - 133 pipeline = [ - 134 sp.ModelBasedVolumeCreationAdapter(settings), - 135 sp.MCXAdapterReflectance(settings), - 136 ] - 137 - 138 979.7 MiB 0.0 MiB 3 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): - 139 979.7 MiB 0.0 MiB 1 """ - 140 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. - 141 - 142 """ - 143 - 144 979.7 MiB 0.0 MiB 2 def __init__(self): - 145 979.7 MiB 0.0 MiB 2 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 146 979.7 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 147 979.7 MiB 0.0 MiB 1 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) - 148 979.7 MiB 0.0 MiB 2 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], - 149 979.7 MiB 0.0 MiB 1 direction_vector_mm=[0, 0, 1])) - 150 - 151 979.7 MiB 0.0 MiB 1 device = ExampleDeviceSlitIlluminationLinearDetector() - 152 - 153 996.1 MiB 16.4 MiB 1 sp.simulate(pipeline, settings, device) - 154 - 155 996.1 MiB 0.0 MiB 1 if Tags.WAVELENGTH in settings: - 156 996.1 MiB 0.0 MiB 1 WAVELENGTH = settings[Tags.WAVELENGTH] - 157 else: - 158 WAVELENGTH = 700 - 159 - 160 996.1 MiB 0.0 MiB 1 if visualise: - 161 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 162 wavelength=WAVELENGTH, - 163 show_initial_pressure=True, - 164 show_absorption=True, - 165 show_diffuse_reflectance=SAVE_REFLECTANCE, - 166 log_scale=True) - - -Filename: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation_uniform_cube.py - -Line # Mem usage Increment Occurrences Line Contents -============================================================= - 22 996.1 MiB 996.1 MiB 1 @profile - 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), - 24 visualise: bool = True): - 25 """ - 26 - 27 :param SPACING: The simulation spacing between voxels - 28 :param path_manager: the path manager to be used, typically sp.PathManager - 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 30 :return: a run through of the example - 31 """ - 32 996.1 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 33 996.1 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 30 - 34 996.1 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 60 - 35 996.1 MiB 0.0 MiB 1 RANDOM_SEED = 471 - 36 996.1 MiB 0.0 MiB 1 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 37 996.1 MiB 0.0 MiB 1 SAVE_REFLECTANCE = True - 38 996.1 MiB 0.0 MiB 1 SAVE_PHOTON_DIRECTION = False - 39 - 40 # If VISUALIZE is set to True, the simulation result will be plotted - 41 996.1 MiB 0.0 MiB 1 VISUALIZE = True - 42 - 43 996.1 MiB 0.0 MiB 2 def create_example_tissue(): - 44 """ - 45 This is a very simple example script of how to create a tissue definition. - 46 It contains only a generic background tissue material. - 47 """ - 48 996.1 MiB 0.0 MiB 1 background_dictionary = sp.Settings() - 49 996.1 MiB 0.0 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 50 996.1 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 51 - 52 996.1 MiB 0.0 MiB 1 tissue_dict = sp.Settings() - 53 996.1 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary - 54 996.1 MiB 0.0 MiB 1 return tissue_dict - 55 - 56 # Seed the numpy random configuration prior to creating the global_settings file in - 57 # order to ensure that the same volume - 58 # is generated with the same random seed every time. - 59 - 60 996.1 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) - 61 - 62 996.1 MiB 0.0 MiB 1 general_settings = { - 63 # These parameters set the general properties of the simulated volume - 64 996.1 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, - 65 996.1 MiB 0.0 MiB 1 Tags.VOLUME_NAME: VOLUME_NAME, - 66 996.1 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 67 996.1 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, - 68 996.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 69 996.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 70 996.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 71 996.1 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [500], - 72 996.1 MiB 0.0 MiB 1 Tags.DO_FILE_COMPRESSION: True - 73 } - 74 - 75 996.1 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) - 76 - 77 996.1 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ - 78 996.1 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue() - 79 }) - 80 996.1 MiB 0.0 MiB 2 settings.set_optical_settings({ - 81 996.1 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 82 996.1 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 83 996.1 MiB 0.0 MiB 1 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 84 996.1 MiB 0.0 MiB 1 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 85 }) - 86 - 87 996.1 MiB 0.0 MiB 1 pipeline = [ - 88 996.1 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), - 89 996.1 MiB 0.0 MiB 1 sp.MCXAdapterReflectance(settings), - 90 ] - 91 - 92 996.1 MiB 0.0 MiB 2 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 93 996.1 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 94 - 95 1015.1 MiB 19.0 MiB 1 sp.simulate(pipeline, settings, device) - 96 - 97 1015.1 MiB 0.0 MiB 1 if visualise: - 98 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 99 wavelength=settings[Tags.WAVELENGTH], - 100 show_initial_pressure=True, - 101 show_absorption=True, - 102 show_diffuse_reflectance=SAVE_REFLECTANCE, - 103 log_scale=True) - - -Filename: /home/f762e/Workspace/simpa/simpa_examples/msot_invision_simulation.py - -Line # Mem usage Increment Occurrences Line Contents -============================================================= - 14 1015.1 MiB 1015.1 MiB 1 @profile - 15 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): - 16 """ - 17 - 18 :param SPACING: The simulation spacing between voxels - 19 :param path_manager: the path manager to be used, typically sp.PathManager - 20 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 21 :return: a run through of the example - 22 """ - 23 1015.1 MiB 0.0 MiB 1 SPEED_OF_SOUND = 1500 - 24 1015.1 MiB 0.0 MiB 1 XZ_DIM = 90 - 25 1015.1 MiB 0.0 MiB 1 Y_DIM = 40 - 26 - 27 1015.1 MiB 0.0 MiB 2 def create_pipeline(_settings: sp.Settings): - 28 1015.1 MiB 0.0 MiB 1 return [ - 29 1015.1 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), - 30 1015.1 MiB 0.0 MiB 1 sp.MCXAdapter(settings), - 31 1015.1 MiB 0.0 MiB 1 sp.KWaveAdapter(settings), - 32 1015.1 MiB 0.0 MiB 1 sp.FieldOfViewCropping(settings), - 33 1015.1 MiB 0.0 MiB 1 sp.TimeReversalAdapter(settings) - 34 ] - 35 - 36 1015.1 MiB 0.0 MiB 2 def get_device(): - 37 1015.1 MiB 0.0 MiB 1 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) - 38 1015.1 MiB 0.0 MiB 1 return pa_device - 39 - 40 1015.1 MiB 0.0 MiB 2 def create_volume(): - 41 1015.1 MiB 0.0 MiB 2 inclusion_material = sp.Molecule(volume_fraction=1.0, - 42 1015.1 MiB 0.0 MiB 2 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 43 1015.1 MiB 0.0 MiB 1 0.9), - 44 1015.1 MiB 0.0 MiB 2 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 45 1015.1 MiB 0.0 MiB 1 100.0), - 46 1015.1 MiB 0.0 MiB 2 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 47 1015.1 MiB 0.0 MiB 1 4.0), - 48 1015.1 MiB 0.0 MiB 1 speed_of_sound=SPEED_OF_SOUND, - 49 1015.1 MiB 0.0 MiB 1 alpha_coefficient=1e-4, - 50 1015.1 MiB 0.0 MiB 1 density=1000, - 51 1015.1 MiB 0.0 MiB 1 gruneisen_parameter=1.0, - 52 1015.1 MiB 0.0 MiB 1 name="Inclusion") - 53 - 54 1015.1 MiB 0.0 MiB 2 phantom_material = sp.Molecule(volume_fraction=1.0, - 55 1015.1 MiB 0.0 MiB 2 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 56 1015.1 MiB 0.0 MiB 1 0.9), - 57 1015.1 MiB 0.0 MiB 2 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 58 1015.1 MiB 0.0 MiB 1 100.0), - 59 1015.1 MiB 0.0 MiB 2 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 60 1015.1 MiB 0.0 MiB 1 0.05), - 61 1015.1 MiB 0.0 MiB 1 speed_of_sound=SPEED_OF_SOUND, - 62 1015.1 MiB 0.0 MiB 1 alpha_coefficient=1e-4, - 63 1015.1 MiB 0.0 MiB 1 density=1000, - 64 1015.1 MiB 0.0 MiB 1 gruneisen_parameter=1.0, - 65 1015.1 MiB 0.0 MiB 1 name="Phantom") - 66 - 67 1015.1 MiB 0.0 MiB 2 heavy_water = sp.Molecule(volume_fraction=1.0, - 68 1015.1 MiB 0.0 MiB 1 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), - 69 1015.1 MiB 0.0 MiB 1 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), - 70 1015.1 MiB 0.0 MiB 1 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), - 71 1015.1 MiB 0.0 MiB 1 speed_of_sound=SPEED_OF_SOUND, - 72 1015.1 MiB 0.0 MiB 1 alpha_coefficient=1e-4, - 73 1015.1 MiB 0.0 MiB 1 density=1000, - 74 1015.1 MiB 0.0 MiB 1 gruneisen_parameter=1.0, - 75 1015.1 MiB 0.0 MiB 1 name="background_water") - 76 - 77 1015.1 MiB 0.0 MiB 1 background_dictionary = sp.Settings() - 78 1015.1 MiB 0.0 MiB 2 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 79 1015.1 MiB 0.0 MiB 1 .append(heavy_water) - 80 1015.1 MiB 0.0 MiB 1 .get_molecular_composition(segmentation_type=-1)) - 81 1015.1 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 82 - 83 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary = sp.Settings() - 84 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.PRIORITY] = 3 - 85 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] - 86 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 - 87 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 - 88 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 - 89 1015.1 MiB 0.0 MiB 2 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 90 1015.1 MiB 0.0 MiB 1 .append(phantom_material) - 91 1015.1 MiB 0.0 MiB 1 .get_molecular_composition(segmentation_type=0)) - 92 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 93 1015.1 MiB 0.0 MiB 1 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 94 - 95 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary = sp.Settings() - 96 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.PRIORITY] = 8 - 97 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] - 98 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 - 99 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 - 100 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 - 101 1015.1 MiB 0.0 MiB 2 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 102 1015.1 MiB 0.0 MiB 1 .append(inclusion_material) - 103 1015.1 MiB 0.0 MiB 1 .get_molecular_composition(segmentation_type=1)) - 104 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 105 1015.1 MiB 0.0 MiB 1 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 106 - 107 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary = sp.Settings() - 108 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.PRIORITY] = 5 - 109 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] - 110 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] - 111 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 112 1015.1 MiB 0.0 MiB 2 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 113 1015.1 MiB 0.0 MiB 1 .append(inclusion_material) - 114 1015.1 MiB 0.0 MiB 1 .get_molecular_composition(segmentation_type=2)) - 115 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 116 1015.1 MiB 0.0 MiB 1 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 117 - 118 1015.1 MiB 0.0 MiB 1 tissue_dict = sp.Settings() - 119 1015.1 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary - 120 1015.1 MiB 0.0 MiB 1 tissue_dict["phantom"] = phantom_material_dictionary - 121 1015.1 MiB 0.0 MiB 1 tissue_dict["inclusion_1"] = inclusion_1_dictionary - 122 1015.1 MiB 0.0 MiB 1 tissue_dict["inclusion_2"] = inclusion_2_dictionary - 123 1015.1 MiB 0.0 MiB 1 return { - 124 1015.1 MiB 0.0 MiB 1 Tags.STRUCTURES: tissue_dict, - 125 1015.1 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: False - 126 } - 127 - 128 1015.1 MiB 0.0 MiB 2 def get_settings(): - 129 1015.1 MiB 0.0 MiB 1 general_settings = { - 130 # These parameters set the general properties of the simulated volume - 131 1015.1 MiB 0.0 MiB 1 Tags.RANDOM_SEED: 4711, - 132 1015.1 MiB 0.0 MiB 1 Tags.VOLUME_NAME: "InVision Simulation Example", - 133 1015.1 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 134 1015.1 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, - 135 1015.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: XZ_DIM, - 136 1015.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: XZ_DIM, - 137 1015.1 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: Y_DIM, - 138 1015.1 MiB 0.0 MiB 1 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 139 1015.1 MiB 0.0 MiB 1 Tags.GPU: True, - 140 1015.1 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [700] - 141 } - 142 - 143 1015.1 MiB 0.0 MiB 1 volume_settings = create_volume() - 144 - 145 1015.1 MiB 0.0 MiB 1 optical_settings = { - 146 1015.1 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 147 1015.1 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 148 1015.1 MiB 0.0 MiB 1 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, - 149 1015.1 MiB 0.0 MiB 1 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 150 } - 151 - 152 1015.1 MiB 0.0 MiB 1 acoustic_settings = { - 153 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_SIMULATION_3D: True, - 154 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 155 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 156 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 157 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLInside: False, - 158 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 159 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 160 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PlotPML: False, - 161 1015.1 MiB 0.0 MiB 1 Tags.RECORDMOVIE: False, - 162 1015.1 MiB 0.0 MiB 1 Tags.MOVIENAME: "visualization_log", - 163 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_LOG_SCALE: True - 164 } - 165 - 166 1015.1 MiB 0.0 MiB 19 reconstruction_settings = { - 167 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 168 1015.1 MiB 0.0 MiB 1 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 169 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 170 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 171 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, - 172 1015.1 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 173 1015.1 MiB 0.0 MiB 1 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, - 174 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 175 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLInside: False, - 176 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 177 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 178 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PlotPML: False, - 179 1015.1 MiB 0.0 MiB 1 Tags.RECORDMOVIE: False, - 180 1015.1 MiB 0.0 MiB 1 Tags.MOVIENAME: "visualization_log", - 181 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_LOG_SCALE: True, - 182 1015.1 MiB 0.0 MiB 1 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 183 1015.1 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 184 1015.1 MiB 0.0 MiB 1 Tags.SPACING_MM: 0.25, - 185 } - 186 - 187 1015.1 MiB 0.0 MiB 1 _settings = sp.Settings(general_settings) - 188 1015.1 MiB 0.0 MiB 1 _settings.set_volume_creation_settings(volume_settings) - 189 1015.1 MiB 0.0 MiB 1 _settings.set_optical_settings(optical_settings) - 190 1015.1 MiB 0.0 MiB 1 _settings.set_acoustic_settings(acoustic_settings) - 191 1015.1 MiB 0.0 MiB 1 _settings.set_reconstruction_settings(reconstruction_settings) - 192 1015.1 MiB 0.0 MiB 1 return _settings - 193 - 194 1015.1 MiB 0.0 MiB 1 device = get_device() - 195 1015.1 MiB 0.0 MiB 1 settings = get_settings() - 196 1015.1 MiB 0.0 MiB 1 pipeline = create_pipeline(settings) - 197 - 198 1054.3 MiB 39.2 MiB 1 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) - 199 - 200 1054.3 MiB 0.0 MiB 1 if visualise: - 201 sp.visualise_data(settings=settings, - 202 path_manager=path_manager, - 203 show_absorption=True, - 204 show_initial_pressure=True, - 205 show_reconstructed_data=True, - 206 show_xz_only=True) - - -Filename: /home/f762e/Workspace/simpa/simpa_examples/optical_and_acoustic_simulation.py - -Line # Mem usage Increment Occurrences Line Contents -============================================================= - 19 1054.3 MiB 1054.3 MiB 1 @profile - 20 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), - 21 visualise: bool = True): - 22 """ - 23 - 24 :param SPACING: The simulation spacing between voxels - 25 :param path_manager: the path manager to be used, typically sp.PathManager - 26 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 27 :return: a run through of the example - 28 """ - 29 1054.3 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 30 1054.3 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 20 - 31 1054.3 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 25 - 32 1054.3 MiB 0.0 MiB 1 RANDOM_SEED = 4711 - 33 - 34 # If VISUALIZE is set to True, the simulation result will be plotted - 35 1054.3 MiB 0.0 MiB 1 VISUALIZE = True - 36 - 37 1054.3 MiB 0.0 MiB 2 def create_example_tissue(): - 38 """ - 39 This is a very simple example script of how to create a tissue definition. - 40 It contains a muscular background, an epidermis layer on top of the muscles - 41 and a blood vessel. - 42 """ - 43 1054.3 MiB 0.0 MiB 1 background_dictionary = sp.Settings() - 44 1054.3 MiB 0.0 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) - 45 1054.3 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 46 - 47 1054.3 MiB 0.0 MiB 1 tissue_dict = sp.Settings() - 48 1054.3 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary - 49 1054.3 MiB 0.0 MiB 2 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, - 50 1054.3 MiB 0.0 MiB 2 molecular_composition=sp.TISSUE_LIBRARY.constant( - 51 1054.3 MiB 0.0 MiB 1 0.05, 100, 0.9), - 52 1054.3 MiB 0.0 MiB 1 priority=1, - 53 1054.3 MiB 0.0 MiB 1 consider_partial_volume=True, - 54 1054.3 MiB 0.0 MiB 1 adhere_to_deformation=True) - 55 1054.3 MiB 0.0 MiB 2 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, - 56 1054.3 MiB 0.0 MiB 1 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), - 57 1054.3 MiB 0.0 MiB 1 priority=8, - 58 1054.3 MiB 0.0 MiB 1 consider_partial_volume=True, - 59 1054.3 MiB 0.0 MiB 1 adhere_to_deformation=True) - 60 1054.3 MiB 0.0 MiB 2 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( - 61 1054.3 MiB 0.0 MiB 1 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], - 62 1054.3 MiB 0.0 MiB 1 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], - 63 1054.3 MiB 0.0 MiB 1 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 64 1054.3 MiB 0.0 MiB 1 radius_mm=2, priority=3, consider_partial_volume=True, - 65 1054.3 MiB 0.0 MiB 1 adhere_to_deformation=False - 66 ) - 67 1054.3 MiB 0.0 MiB 2 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( - 68 1054.3 MiB 0.0 MiB 1 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], - 69 1054.3 MiB 0.0 MiB 1 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], - 70 1054.3 MiB 0.0 MiB 1 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 71 1054.3 MiB 0.0 MiB 1 radius_mm=3, priority=3, consider_partial_volume=True, - 72 1054.3 MiB 0.0 MiB 1 adhere_to_deformation=False - 73 ) - 74 1054.3 MiB 0.0 MiB 1 return tissue_dict - 75 - 76 # Seed the numpy random configuration prior to creating the global_settings file in - 77 # order to ensure that the same volume - 78 # is generated with the same random seed every time. - 79 - 80 1054.3 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) - 81 1054.3 MiB 0.0 MiB 1 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) - 82 - 83 1054.3 MiB 0.0 MiB 1 general_settings = { - 84 # These parameters set the general properties of the simulated volume - 85 1054.3 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, - 86 1054.3 MiB 0.0 MiB 1 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), - 87 1054.3 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 88 1054.3 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, - 89 1054.3 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 90 1054.3 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 91 1054.3 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 92 1054.3 MiB 0.0 MiB 1 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 93 1054.3 MiB 0.0 MiB 1 Tags.GPU: True, - 94 1054.3 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [700, 800], - 95 1054.3 MiB 0.0 MiB 1 Tags.DO_FILE_COMPRESSION: True, - 96 1054.3 MiB 0.0 MiB 1 Tags.DO_IPASC_EXPORT: True - 97 } - 98 1054.3 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) - 99 1054.3 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) - 100 - 101 1054.3 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ - 102 1054.3 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue(), - 103 1054.3 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: True - 104 }) - 105 - 106 1054.3 MiB 0.0 MiB 2 settings.set_optical_settings({ - 107 1054.3 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 108 1054.3 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 109 1054.3 MiB 0.0 MiB 1 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, - 110 1054.3 MiB 0.0 MiB 1 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 111 1054.3 MiB 0.0 MiB 1 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, - 112 }) - 113 - 114 1054.3 MiB 0.0 MiB 2 settings.set_acoustic_settings({ - 115 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_SIMULATION_3D: False, - 116 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 117 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 118 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 119 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLInside: False, - 120 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 121 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 122 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PlotPML: False, - 123 1054.3 MiB 0.0 MiB 1 Tags.RECORDMOVIE: False, - 124 1054.3 MiB 0.0 MiB 1 Tags.MOVIENAME: "visualization_log", - 125 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_LOG_SCALE: True - 126 }) - 127 - 128 1054.3 MiB 0.0 MiB 19 settings.set_reconstruction_settings({ - 129 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 130 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 131 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_SIMULATION_3D: False, - 132 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 133 1054.3 MiB 0.0 MiB 1 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 134 1054.3 MiB 0.0 MiB 1 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), - 135 1054.3 MiB 0.0 MiB 1 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), - 136 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 137 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 138 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, - 139 1054.3 MiB 0.0 MiB 1 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 140 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 141 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLInside: False, - 142 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 143 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 144 1054.3 MiB 0.0 MiB 1 Tags.KWAVE_PROPERTY_PlotPML: False, - 145 1054.3 MiB 0.0 MiB 1 Tags.RECORDMOVIE: False, - 146 1054.3 MiB 0.0 MiB 1 Tags.MOVIENAME: "visualization_log", - 147 1054.3 MiB 0.0 MiB 1 Tags.ACOUSTIC_LOG_SCALE: True, - 148 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, - 149 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, - 150 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD_DENSITY: 1000, - 151 1054.3 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING - 152 }) - 153 - 154 1054.3 MiB 0.0 MiB 1 settings["noise_initial_pressure"] = { - 155 1054.3 MiB 0.0 MiB 1 Tags.NOISE_MEAN: 1, - 156 1054.3 MiB 0.0 MiB 1 Tags.NOISE_STD: 0.01, - 157 1054.3 MiB 0.0 MiB 1 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 158 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 159 1054.3 MiB 0.0 MiB 1 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 160 } - 161 - 162 1054.3 MiB 0.0 MiB 1 settings["noise_time_series"] = { - 163 1054.3 MiB 0.0 MiB 1 Tags.NOISE_STD: 1, - 164 1054.3 MiB 0.0 MiB 1 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, - 165 1054.3 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA - 166 } - 167 - 168 # TODO: For the device choice, uncomment the undesired device - 169 - 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 171 # VOLUME_PLANAR_DIM_IN_MM/2, - 172 # 0])) - 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) - 174 - 175 1054.3 MiB 0.0 MiB 3 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 176 1054.3 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM/2, - 177 1054.3 MiB 0.0 MiB 1 0]), - 178 1054.3 MiB 0.0 MiB 1 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) - 179 1054.3 MiB 0.0 MiB 2 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, - 180 1054.3 MiB 0.0 MiB 1 pitch_mm=0.25, - 181 1054.3 MiB 0.0 MiB 1 number_detector_elements=100, - 182 1054.3 MiB 0.0 MiB 1 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) - 183 1054.3 MiB 0.0 MiB 1 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) - 184 1054.3 MiB 0.0 MiB 1 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) - 185 - 186 1054.3 MiB 0.0 MiB 1 SIMULATION_PIPELINE = [ - 187 1054.3 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), - 188 1054.3 MiB 0.0 MiB 1 sp.MCXAdapter(settings), - 189 1054.3 MiB 0.0 MiB 1 sp.GaussianNoise(settings, "noise_initial_pressure"), - 190 1054.3 MiB 0.0 MiB 1 sp.KWaveAdapter(settings), - 191 1054.3 MiB 0.0 MiB 1 sp.GaussianNoise(settings, "noise_time_series"), - 192 1054.3 MiB 0.0 MiB 1 sp.TimeReversalAdapter(settings), - 193 1054.3 MiB 0.0 MiB 1 sp.FieldOfViewCropping(settings) - 194 ] - 195 - 196 1083.2 MiB 28.9 MiB 1 sp.simulate(SIMULATION_PIPELINE, settings, device) - 197 - 198 1083.2 MiB 0.0 MiB 1 if Tags.WAVELENGTH in settings: - 199 1083.2 MiB 0.0 MiB 1 WAVELENGTH = settings[Tags.WAVELENGTH] - 200 else: - 201 WAVELENGTH = 700 - 202 - 203 1083.2 MiB 0.0 MiB 1 if visualise: - 204 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - 205 wavelength=WAVELENGTH, - 206 show_time_series_data=True, - 207 show_initial_pressure=True, - 208 show_reconstructed_data=True, - 209 log_scale=False, - 210 show_xz_only=False) - - -Filename: /home/f762e/Workspace/simpa/simpa_examples/perform_iterative_qPAI_reconstruction.py - -Line # Mem usage Increment Occurrences Line Contents -============================================================= - 25 1083.2 MiB 1083.2 MiB 1 @profile - 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise: bool = True): - 27 """ - 28 - 29 :param SPACING: The simulation spacing between voxels - 30 :param path_manager: the path manager to be used, typically sp.PathManager - 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 32 :return: a run through of the example - 33 """ - 34 1083.2 MiB 0.0 MiB 1 VOLUME_TRANSDUCER_DIM_IN_MM = 30 - 35 1083.2 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM = 30 - 36 1083.2 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM = 30 - 37 1083.2 MiB 0.0 MiB 1 RANDOM_SEED = 471 - 38 1083.2 MiB 0.0 MiB 1 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) - 39 - 40 # If VISUALIZE is set to True, the reconstruction result will be plotted - 41 - 42 1083.2 MiB 0.0 MiB 2 def create_example_tissue(): - 43 """ - 44 This is a very simple example script of how to create a tissue definition. - 45 It contains a muscular background, an epidermis layer on top of the muscles - 46 and a blood vessel. - 47 """ - 48 1083.2 MiB 0.0 MiB 1 background_dictionary = sp.Settings() - 49 1083.2 MiB 0.0 MiB 1 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) - 50 1083.2 MiB 0.0 MiB 1 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 51 - 52 1083.2 MiB 0.0 MiB 1 epidermis_structure = sp.Settings() - 53 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.PRIORITY] = 1 - 54 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] - 55 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] - 56 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) - 57 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True - 58 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True - 59 1083.2 MiB 0.0 MiB 1 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 60 - 61 1083.2 MiB 0.0 MiB 1 vessel_structure_1 = sp.Settings() - 62 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.PRIORITY] = 2 - 63 1083.2 MiB 0.0 MiB 2 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, - 64 1083.2 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM / 2] - 65 1083.2 MiB 0.0 MiB 2 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, - 66 1083.2 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] - 67 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 - 68 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 - 69 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) - 70 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True - 71 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True - 72 1083.2 MiB 0.0 MiB 1 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE - 73 - 74 1083.2 MiB 0.0 MiB 1 vessel_structure_2 = sp.Settings() - 75 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.PRIORITY] = 3 - 76 1083.2 MiB 0.0 MiB 2 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, - 77 1083.2 MiB 0.0 MiB 1 VOLUME_HEIGHT_IN_MM / 3] - 78 1083.2 MiB 0.0 MiB 2 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, - 79 1083.2 MiB 0.0 MiB 1 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] - 80 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 - 81 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) - 82 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True - 83 1083.2 MiB 0.0 MiB 1 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 84 - 85 1083.2 MiB 0.0 MiB 1 tissue_dict = sp.Settings() - 86 1083.2 MiB 0.0 MiB 1 tissue_dict[Tags.BACKGROUND] = background_dictionary - 87 1083.2 MiB 0.0 MiB 1 tissue_dict["epidermis"] = epidermis_structure - 88 1083.2 MiB 0.0 MiB 1 tissue_dict["vessel_1"] = vessel_structure_1 - 89 1083.2 MiB 0.0 MiB 1 tissue_dict["vessel_2"] = vessel_structure_2 - 90 1083.2 MiB 0.0 MiB 1 return tissue_dict - 91 - 92 # set settings for volume creation, optical simulation and iterative qPAI method - 93 1083.2 MiB 0.0 MiB 1 np.random.seed(RANDOM_SEED) - 94 - 95 1083.2 MiB 0.0 MiB 1 general_settings = { - 96 # These parameters set the general properties of the simulated volume - 97 1083.2 MiB 0.0 MiB 1 Tags.RANDOM_SEED: RANDOM_SEED, - 98 1083.2 MiB 0.0 MiB 1 Tags.VOLUME_NAME: VOLUME_NAME, - 99 1083.2 MiB 0.0 MiB 1 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 100 1083.2 MiB 0.0 MiB 1 Tags.SPACING_MM: SPACING, - 101 1083.2 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 102 1083.2 MiB 0.0 MiB 1 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 103 1083.2 MiB 0.0 MiB 1 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 104 1083.2 MiB 0.0 MiB 1 Tags.WAVELENGTHS: [700] - 105 } - 106 - 107 1083.2 MiB 0.0 MiB 1 settings = sp.Settings(general_settings) - 108 - 109 1083.2 MiB 0.0 MiB 2 settings.set_volume_creation_settings({ - 110 # These parameters set the properties for the volume creation - 111 1083.2 MiB 0.0 MiB 1 Tags.SIMULATE_DEFORMED_LAYERS: True, - 112 1083.2 MiB 0.0 MiB 1 Tags.STRUCTURES: create_example_tissue() - 113 }) - 114 1083.2 MiB 0.0 MiB 2 settings.set_optical_settings({ - 115 # These parameters set the properties for the optical Monte Carlo simulation - 116 1083.2 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 117 1083.2 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 118 1083.2 MiB 0.0 MiB 1 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 119 1083.2 MiB 0.0 MiB 1 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 120 }) - 121 1083.2 MiB 0.0 MiB 1 settings["noise_model"] = { - 122 1083.2 MiB 0.0 MiB 1 Tags.NOISE_MEAN: 1.0, - 123 1083.2 MiB 0.0 MiB 1 Tags.NOISE_STD: 0.01, - 124 1083.2 MiB 0.0 MiB 1 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 125 1083.2 MiB 0.0 MiB 1 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 126 1083.2 MiB 0.0 MiB 1 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 127 } - 128 1083.2 MiB 0.0 MiB 1 settings["iterative_qpai_reconstruction"] = { - 129 # These parameters set the properties of the iterative reconstruction - 130 1083.2 MiB 0.0 MiB 1 Tags.DOWNSCALE_FACTOR: 0.75, - 131 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, - 132 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant - 133 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, - 134 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, - 135 # for this example, we are not interested in all absorption updates - 136 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, - 137 1083.2 MiB 0.0 MiB 1 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 - 138 } - 139 - 140 # run pipeline including iterative qPAI method - 141 1083.2 MiB 0.0 MiB 1 pipeline = [ - 142 1083.2 MiB 0.0 MiB 1 sp.ModelBasedVolumeCreationAdapter(settings), - 143 1083.2 MiB 0.0 MiB 1 sp.MCXAdapter(settings), - 144 1083.2 MiB 0.0 MiB 1 sp.GaussianNoise(settings, "noise_model"), - 145 1083.2 MiB 0.0 MiB 1 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") - 146 ] - 147 - 148 1083.2 MiB 0.0 MiB 3 class CustomDevice(sp.PhotoacousticDevice): - 149 - 150 1083.2 MiB 0.0 MiB 2 def __init__(self): - 151 1083.2 MiB 0.0 MiB 2 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, - 152 1083.2 MiB 0.0 MiB 1 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, - 153 1083.2 MiB 0.0 MiB 1 0])) - 154 1083.2 MiB 0.0 MiB 1 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) - 155 - 156 1083.2 MiB 0.0 MiB 1 device = CustomDevice() - 157 - 158 1083.2 MiB 0.0 MiB 1 device.update_settings_for_use_of_model_based_volume_creator(settings) - 159 - 160 1399.7 MiB 316.5 MiB 1 sp.simulate(pipeline, settings, device) - 161 - 162 # visualize reconstruction results - 163 1399.7 MiB 0.0 MiB 1 if visualise: - 164 # get simulation output - 165 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 166 settings = sp.load_data_field(data_path, Tags.SETTINGS) - 167 wavelength = settings[Tags.WAVELENGTHS][0] - 168 - 169 # get reconstruction result - 170 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) - 171 - 172 # get ground truth absorption coefficients - 173 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) - 174 - 175 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) - 176 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR - 177 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") - 178 - 179 # compute reconstruction error - 180 difference = absorption_gt - absorption_reconstruction - 181 - 182 median_error = np.median(difference) - 183 q3, q1 = np.percentile(difference, [75, 25]) - 184 iqr = q3 - q1 - 185 - 186 # visualize results - 187 x_pos = int(np.shape(absorption_gt)[0] / 2) - 188 y_pos = int(np.shape(absorption_gt)[1] / 2) - 189 - 190 if np.min(absorption_gt) > np.min(absorption_reconstruction): - 191 cmin = np.min(absorption_reconstruction) - 192 else: - 193 cmin = np.min(absorption_gt) - 194 - 195 if np.max(absorption_gt) > np.max(absorption_reconstruction): - 196 cmax = np.max(absorption_gt) - 197 else: - 198 cmax = np.max(absorption_reconstruction) - 199 - 200 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] - 201 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] - 202 - 203 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", - 204 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] - 205 - 206 plt.figure(figsize=(20, 15)) - 207 plt.subplots_adjust(hspace=0.5) - 208 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + - 209 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) - 210 - 211 for i, quantity in enumerate(results_x_z): - 212 plt.subplot(2, len(results_x_z), i + 1) - 213 if i == 0: - 214 plt.ylabel("x-z", fontsize=10) - 215 plt.title(label[i], fontsize=10) - 216 plt.imshow(quantity.T) - 217 plt.xticks(fontsize=6) - 218 plt.yticks(fontsize=6) - 219 plt.colorbar() - 220 if i != 2: - 221 plt.clim(cmin, cmax) - 222 else: - 223 plt.clim(np.min(difference), np.max(difference)) - 224 - 225 for i, quantity in enumerate(results_y_z): - 226 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) - 227 if i == 0: - 228 plt.ylabel("y-z", fontsize=10) - 229 plt.title(label[i], fontsize=10) - 230 plt.imshow(quantity.T) - 231 plt.xticks(fontsize=6) - 232 plt.yticks(fontsize=6) - 233 plt.colorbar() - 234 if i != 2: - 235 plt.clim(cmin, cmax) - 236 else: - 237 plt.clim(np.min(difference), np.max(difference)) - 238 - 239 plt.show() - 240 plt.close() - - diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.4.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_memory_0.4.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.2.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.2.txt deleted file mode 100644 index 30400b16..00000000 --- a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.2.txt +++ /dev/null @@ -1,1048 +0,0 @@ -Timer unit: 1e-06 s - -Total time: 15.7383 s -File: /home/f762e/Workspace/simpa/simpa_examples/linear_unmixing.py -Function: run_linear_unmixing at line 18 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 18 @profile - 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): - 20 """ - 21 - 22 :param SPACING: The simulation spacing between voxels - 23 :param path_manager: the path manager to be used, typically sp.PathManager - 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 25 :return: a run through of the example - 26 """ - 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you - 28 # set global params characterizing the simulated volume - 29 1 0.4 0.4 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 30 1 0.2 0.2 0.0 VOLUME_PLANAR_DIM_IN_MM = 20 - 31 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 25 - 32 1 0.1 0.1 0.0 RANDOM_SEED = 471 - 33 1 0.5 0.5 0.0 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) - 34 - 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths - 36 1 0.2 0.2 0.0 WAVELENGTHS = [750, 800, 850] - 37 - 38 1 0.2 0.2 0.0 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and two blood vessels. - 43 """ - 44 background_dictionary = sp.Settings() - 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 muscle_dictionary = sp.Settings() - 49 muscle_dictionary[Tags.PRIORITY] = 1 - 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 vessel_1_dictionary = sp.Settings() - 58 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 10, - 61 5] - 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 12, - 64 5] - 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) - 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 vessel_2_dictionary = sp.Settings() - 71 vessel_2_dictionary[Tags.PRIORITY] = 3 - 72 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 73 10, - 74 5] - 75 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 76 12, - 77 5] - 78 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 79 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) - 80 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 81 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 82 - 83 epidermis_dictionary = sp.Settings() - 84 epidermis_dictionary[Tags.PRIORITY] = 8 - 85 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 86 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] - 87 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 88 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 89 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 90 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 91 - 92 tissue_dict = sp.Settings() - 93 tissue_dict[Tags.BACKGROUND] = background_dictionary - 94 tissue_dict["muscle"] = muscle_dictionary - 95 tissue_dict["epidermis"] = epidermis_dictionary - 96 tissue_dict["vessel_1"] = vessel_1_dictionary - 97 tissue_dict["vessel_2"] = vessel_2_dictionary - 98 return tissue_dict - 99 - 100 - 101 # Seed the numpy random configuration prior to creating the global_settings file in - 102 # order to ensure that the same volume is generated with the same random seed every time. - 103 1 16.6 16.6 0.0 np.random.seed(RANDOM_SEED) - 104 - 105 # Initialize global settings and prepare for simulation pipeline including - 106 # volume creation and optical forward simulation. - 107 1 1.2 1.2 0.0 general_settings = { - 108 # These parameters set the general properties of the simulated volume - 109 1 0.9 0.9 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 110 1 0.5 0.5 0.0 Tags.VOLUME_NAME: VOLUME_NAME, - 111 1 54.3 54.3 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 112 1 0.7 0.7 0.0 Tags.SPACING_MM: SPACING, - 113 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 114 1 0.5 0.5 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 115 1 0.6 0.6 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 116 1 0.3 0.3 0.0 Tags.WAVELENGTHS: WAVELENGTHS, - 117 1 0.3 0.3 0.0 Tags.GPU: True, - 118 1 0.4 0.4 0.0 Tags.DO_FILE_COMPRESSION: True - 119 } - 120 1 25.2 25.2 0.0 settings = sp.Settings(general_settings) - 121 2 6.0 3.0 0.0 settings.set_volume_creation_settings({ - 122 1 0.3 0.3 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, - 123 1 30776.2 30776.2 0.2 Tags.STRUCTURES: create_example_tissue() - 124 }) - 125 2 7.1 3.5 0.0 settings.set_optical_settings({ - 126 1 0.4 0.4 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 127 1 36.7 36.7 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 128 1 0.5 0.5 0.0 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 129 1 0.3 0.3 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 130 }) - 131 - 132 # Set component settings for linear unmixing. - 133 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the - 134 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. - 135 # Please take a look at the component for more information. - 136 1 61.1 61.1 0.0 settings["linear_unmixing"] = { - 137 1 0.3 0.3 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 138 1 0.1 0.1 0.0 Tags.WAVELENGTHS: WAVELENGTHS, - 139 2 1980.0 990.0 0.0 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( - 140 1 0.8 0.8 0.0 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] - 141 ), - 142 1 0.5 0.5 0.0 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, - 143 1 0.6 0.6 0.0 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True - 144 } - 145 - 146 # Get device for simulation - 147 2 143.4 71.7 0.0 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 148 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM/2, - 149 1 0.1 0.1 0.0 0])) - 150 1 4490.2 4490.2 0.0 device.update_settings_for_use_of_model_based_volume_creator(settings) - 151 - 152 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS - 153 1 0.1 0.1 0.0 pipeline = [ - 154 1 14304.4 14304.4 0.1 sp.ModelBasedVolumeCreationAdapter(settings), - 155 1 14.3 14.3 0.0 sp.MCXAdapter(settings), - 156 1 136.0 136.0 0.0 sp.FieldOfViewCropping(settings), - 157 ] - 158 1 12084177.3 1e+07 76.8 sp.simulate(pipeline, settings, device) - 159 - 160 # Run linear unmixing component with above specified settings. - 161 1 3598835.0 4e+06 22.9 sp.LinearUnmixing(settings, "linear_unmixing").run() - 162 - 163 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. - 164 1 51.2 51.2 0.0 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 165 1 894.2 894.2 0.0 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) - 166 1 0.3 0.3 0.0 sO2 = lu_results["sO2"] - 167 - 168 1 567.7 567.7 0.0 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) - 169 1 1217.4 1217.4 0.0 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) - 170 1 530.7 530.7 0.0 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) - 171 - 172 # Visualize linear unmixing result - 173 1 0.2 0.2 0.0 if visualise: - 174 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 175 wavelength=WAVELENGTHS[0], - 176 show_initial_pressure=True, - 177 show_oxygenation=True, - 178 show_linear_unmixing_sO2=True) - -Total time: 10.7236 s -File: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation.py -Function: run_minimal_optical_simulation at line 19 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 19 @profile - 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 28 1 0.3 0.3 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 29 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 - 30 1 0.2 0.2 0.0 VOLUME_HEIGHT_IN_MM = 60 - 31 1 0.8 0.8 0.0 RANDOM_SEED = 471 - 32 1 0.6 0.6 0.0 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 33 1 0.1 0.1 0.0 SAVE_REFLECTANCE = False - 34 1 0.2 0.2 0.0 SAVE_PHOTON_DIRECTION = False - 35 - 36 # If VISUALIZE is set to True, the simulation result will be plotted - 37 - 38 1 1.5 1.5 0.0 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and a blood vessel. - 43 """ - 44 background_dictionary = sp.Settings() - 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 muscle_dictionary = sp.Settings() - 49 muscle_dictionary[Tags.PRIORITY] = 1 - 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] - 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 vessel_1_dictionary = sp.Settings() - 58 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 10, - 61 VOLUME_HEIGHT_IN_MM/2] - 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 12, - 64 VOLUME_HEIGHT_IN_MM/2] - 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() - 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 epidermis_dictionary = sp.Settings() - 71 epidermis_dictionary[Tags.PRIORITY] = 8 - 72 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] - 73 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] - 74 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 75 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 76 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 77 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 78 - 79 tissue_dict = sp.Settings() - 80 tissue_dict[Tags.BACKGROUND] = background_dictionary - 81 tissue_dict["muscle"] = muscle_dictionary - 82 tissue_dict["epidermis"] = epidermis_dictionary - 83 tissue_dict["vessel_1"] = vessel_1_dictionary - 84 return tissue_dict - 85 - 86 - 87 # Seed the numpy random configuration prior to creating the global_settings file in - 88 # order to ensure that the same volume - 89 # is generated with the same random seed every time. - 90 - 91 1 9.0 9.0 0.0 np.random.seed(RANDOM_SEED) - 92 - 93 1 0.9 0.9 0.0 general_settings = { - 94 # These parameters set the general properties of the simulated volume - 95 1 0.2 0.2 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 96 1 0.2 0.2 0.0 Tags.VOLUME_NAME: VOLUME_NAME, - 97 1 49.3 49.3 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 98 1 0.3 0.3 0.0 Tags.SPACING_MM: SPACING, - 99 1 0.1 0.1 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 100 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 101 1 0.1 0.1 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 102 1 0.9 0.9 0.0 Tags.WAVELENGTHS: [798], - 103 1 0.3 0.3 0.0 Tags.DO_FILE_COMPRESSION: True, - 104 1 0.2 0.2 0.0 Tags.GPU: True - 105 } - 106 - 107 1 15.1 15.1 0.0 settings = sp.Settings(general_settings) - 108 - 109 2 5.5 2.7 0.0 settings.set_volume_creation_settings({ - 110 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, - 111 1 23112.3 23112.3 0.2 Tags.STRUCTURES: create_example_tissue() - 112 }) - 113 2 69.9 35.0 0.0 settings.set_optical_settings({ - 114 1 0.2 0.2 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 115 1 35.5 35.5 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 116 1 1.4 1.4 0.0 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 117 1 0.3 0.3 0.0 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 118 }) - 119 1 34.6 34.6 0.0 settings["noise_model_1"] = { - 120 1 1.7 1.7 0.0 Tags.NOISE_MEAN: 1.0, - 121 1 0.3 0.3 0.0 Tags.NOISE_STD: 0.1, - 122 1 0.7 0.7 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 123 1 0.4 0.4 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 124 1 0.5 0.5 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 125 } - 126 - 127 1 0.1 0.1 0.0 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: - 128 1 0.1 0.1 0.0 pipeline = [ - 129 1 70.9 70.9 0.0 sp.ModelBasedVolumeCreationAdapter(settings), - 130 1 9.3 9.3 0.0 sp.MCXAdapter(settings), - 131 1 44.9 44.9 0.0 sp.GaussianNoise(settings, "noise_model_1") - 132 ] - 133 else: - 134 pipeline = [ - 135 sp.ModelBasedVolumeCreationAdapter(settings), - 136 sp.MCXAdapterReflectance(settings), - 137 ] - 138 - 139 - 140 1 24.4 24.4 0.0 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): - 141 """ - 142 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. - 143 - 144 """ - 145 - 146 def __init__(self): - 147 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 148 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 149 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) - 150 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], - 151 direction_vector_mm=[0, 0, 1])) - 152 - 153 - 154 1 67.0 67.0 0.0 device = ExampleDeviceSlitIlluminationLinearDetector() - 155 - 156 1 10699994.3 1e+07 99.8 sp.simulate(pipeline, settings, device) - 157 - 158 1 7.5 7.5 0.0 if Tags.WAVELENGTH in settings: - 159 1 4.1 4.1 0.0 WAVELENGTH = settings[Tags.WAVELENGTH] - 160 else: - 161 WAVELENGTH = 700 - 162 - 163 1 0.5 0.5 0.0 if visualise: - 164 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 165 wavelength=WAVELENGTH, - 166 show_initial_pressure=True, - 167 show_absorption=True, - 168 show_diffuse_reflectance=SAVE_REFLECTANCE, - 169 log_scale=True) - -Total time: 6.50607 s -File: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation_uniform_cube.py -Function: run_minimal_optical_simulation_uniform_cube at line 22 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 22 @profile - 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), - 24 visualise:bool = True): - 25 """ - 26 - 27 :param SPACING: The simulation spacing between voxels - 28 :param path_manager: the path manager to be used, typically sp.PathManager - 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 30 :return: a run through of the example - 31 """ - 32 1 0.5 0.5 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 33 1 0.3 0.3 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 - 34 1 0.2 0.2 0.0 VOLUME_HEIGHT_IN_MM = 60 - 35 1 0.4 0.4 0.0 RANDOM_SEED = 471 - 36 1 0.9 0.9 0.0 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 37 1 0.5 0.5 0.0 SAVE_REFLECTANCE = True - 38 1 0.5 0.5 0.0 SAVE_PHOTON_DIRECTION = False - 39 - 40 # If VISUALIZE is set to True, the simulation result will be plotted - 41 1 0.2 0.2 0.0 VISUALIZE = True - 42 - 43 - 44 1 0.8 0.8 0.0 def create_example_tissue(): - 45 """ - 46 This is a very simple example script of how to create a tissue definition. - 47 It contains only a generic background tissue material. - 48 """ - 49 background_dictionary = sp.Settings() - 50 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 51 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 52 - 53 tissue_dict = sp.Settings() - 54 tissue_dict[Tags.BACKGROUND] = background_dictionary - 55 return tissue_dict - 56 - 57 - 58 # Seed the numpy random configuration prior to creating the global_settings file in - 59 # order to ensure that the same volume - 60 # is generated with the same random seed every time. - 61 - 62 1 12.4 12.4 0.0 np.random.seed(RANDOM_SEED) - 63 - 64 1 1.2 1.2 0.0 general_settings = { - 65 # These parameters set the general properties of the simulated volume - 66 1 0.5 0.5 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 67 1 0.8 0.8 0.0 Tags.VOLUME_NAME: VOLUME_NAME, - 68 1 73.8 73.8 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 69 1 0.6 0.6 0.0 Tags.SPACING_MM: SPACING, - 70 1 0.4 0.4 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 71 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 72 1 0.5 0.5 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 73 1 0.5 0.5 0.0 Tags.WAVELENGTHS: [500], - 74 1 0.5 0.5 0.0 Tags.DO_FILE_COMPRESSION: True - 75 } - 76 - 77 1 18.7 18.7 0.0 settings = sp.Settings(general_settings) - 78 - 79 2 5.8 2.9 0.0 settings.set_volume_creation_settings({ - 80 1 2785.9 2785.9 0.0 Tags.STRUCTURES: create_example_tissue() - 81 }) - 82 2 84.6 42.3 0.0 settings.set_optical_settings({ - 83 1 0.4 0.4 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 84 1 47.8 47.8 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 85 1 0.4 0.4 0.0 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 86 1 0.2 0.2 0.0 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 87 }) - 88 - 89 1 0.2 0.2 0.0 pipeline = [ - 90 1 68.3 68.3 0.0 sp.ModelBasedVolumeCreationAdapter(settings), - 91 1 21.7 21.7 0.0 sp.MCXAdapterReflectance(settings), - 92 ] - 93 - 94 2 36.4 18.2 0.0 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 95 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 96 - 97 1 6502907.2 7e+06 100.0 sp.simulate(pipeline, settings, device) - 98 - 99 1 0.6 0.6 0.0 if visualise: - 100 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 101 wavelength=settings[Tags.WAVELENGTH], - 102 show_initial_pressure=True, - 103 show_absorption=True, - 104 show_diffuse_reflectance=SAVE_REFLECTANCE, - 105 log_scale=True) - -Total time: 28.1595 s -File: /home/f762e/Workspace/simpa/simpa_examples/msot_invision_simulation.py -Function: run_msot_invision_simulation at line 13 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 13 @profile - 14 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise:bool = True): - 15 """ - 16 - 17 :param SPACING: The simulation spacing between voxels - 18 :param path_manager: the path manager to be used, typically sp.PathManager - 19 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 20 :return: a run through of the example - 21 """ - 22 1 0.6 0.6 0.0 SPEED_OF_SOUND = 1500 - 23 1 0.4 0.4 0.0 XZ_DIM = 90 - 24 1 0.2 0.2 0.0 Y_DIM = 40 - 25 - 26 1 0.8 0.8 0.0 def create_pipeline(_settings: sp.Settings): - 27 return [ - 28 sp.ModelBasedVolumeCreationAdapter(settings), - 29 sp.MCXAdapter(settings), - 30 sp.KWaveAdapter(settings), - 31 sp.FieldOfViewCropping(settings), - 32 sp.TimeReversalAdapter(settings) - 33 ] - 34 - 35 - 36 1 0.4 0.4 0.0 def get_device(): - 37 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) - 38 return pa_device - 39 - 40 - 41 1 0.5 0.5 0.0 def create_volume(): - 42 inclusion_material = sp.Molecule(volume_fraction=1.0, - 43 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 44 0.9), - 45 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 46 100.0), - 47 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 48 4.0), - 49 speed_of_sound=SPEED_OF_SOUND, - 50 alpha_coefficient=1e-4, - 51 density=1000, - 52 gruneisen_parameter=1.0, - 53 name="Inclusion") - 54 - 55 phantom_material = sp.Molecule(volume_fraction=1.0, - 56 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), - 57 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 58 100.0), - 59 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), - 60 speed_of_sound=SPEED_OF_SOUND, - 61 alpha_coefficient=1e-4, - 62 density=1000, - 63 gruneisen_parameter=1.0, - 64 name="Phantom") - 65 - 66 heavy_water = sp.Molecule(volume_fraction=1.0, - 67 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), - 68 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), - 69 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), - 70 speed_of_sound=SPEED_OF_SOUND, - 71 alpha_coefficient=1e-4, - 72 density=1000, - 73 gruneisen_parameter=1.0, - 74 name="background_water") - 75 - 76 background_dictionary = sp.Settings() - 77 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 78 .append(heavy_water) - 79 .get_molecular_composition(segmentation_type=-1)) - 80 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 81 - 82 phantom_material_dictionary = sp.Settings() - 83 phantom_material_dictionary[Tags.PRIORITY] = 3 - 84 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] - 85 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 - 86 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 - 87 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 - 88 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 89 .append(phantom_material) - 90 .get_molecular_composition(segmentation_type=0)) - 91 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 92 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 93 - 94 inclusion_1_dictionary = sp.Settings() - 95 inclusion_1_dictionary[Tags.PRIORITY] = 8 - 96 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] - 97 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 - 98 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 - 99 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 - 100 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 101 .append(inclusion_material) - 102 .get_molecular_composition(segmentation_type=1)) - 103 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 104 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 105 - 106 inclusion_2_dictionary = sp.Settings() - 107 inclusion_2_dictionary[Tags.PRIORITY] = 5 - 108 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] - 109 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] - 110 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 111 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 112 .append(inclusion_material) - 113 .get_molecular_composition(segmentation_type=2)) - 114 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 115 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 116 - 117 tissue_dict = sp.Settings() - 118 tissue_dict[Tags.BACKGROUND] = background_dictionary - 119 tissue_dict["phantom"] = phantom_material_dictionary - 120 tissue_dict["inclusion_1"] = inclusion_1_dictionary - 121 tissue_dict["inclusion_2"] = inclusion_2_dictionary - 122 return { - 123 Tags.STRUCTURES: tissue_dict, - 124 Tags.SIMULATE_DEFORMED_LAYERS: False - 125 } - 126 - 127 - 128 1 0.5 0.5 0.0 def get_settings(): - 129 general_settings = { - 130 # These parameters set the general properties of the simulated volume - 131 Tags.RANDOM_SEED: 4711, - 132 Tags.VOLUME_NAME: "InVision Simulation Example", - 133 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 134 Tags.SPACING_MM: SPACING, - 135 Tags.DIM_VOLUME_Z_MM: XZ_DIM, - 136 Tags.DIM_VOLUME_X_MM: XZ_DIM, - 137 Tags.DIM_VOLUME_Y_MM: Y_DIM, - 138 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 139 Tags.GPU: True, - 140 Tags.WAVELENGTHS: [700] - 141 } - 142 - 143 volume_settings = create_volume() - 144 - 145 optical_settings = { - 146 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 147 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 148 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, - 149 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 150 } - 151 - 152 acoustic_settings = { - 153 Tags.ACOUSTIC_SIMULATION_3D: True, - 154 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 155 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 156 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 157 Tags.KWAVE_PROPERTY_PMLInside: False, - 158 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 159 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 160 Tags.KWAVE_PROPERTY_PlotPML: False, - 161 Tags.RECORDMOVIE: False, - 162 Tags.MOVIENAME: "visualization_log", - 163 Tags.ACOUSTIC_LOG_SCALE: True - 164 } - 165 - 166 reconstruction_settings = { - 167 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 168 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 169 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 170 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 171 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, - 172 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 173 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, - 174 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 175 Tags.KWAVE_PROPERTY_PMLInside: False, - 176 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 177 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 178 Tags.KWAVE_PROPERTY_PlotPML: False, - 179 Tags.RECORDMOVIE: False, - 180 Tags.MOVIENAME: "visualization_log", - 181 Tags.ACOUSTIC_LOG_SCALE: True, - 182 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 183 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 184 Tags.SPACING_MM: 0.25, - 185 } - 186 - 187 _settings = sp.Settings(general_settings) - 188 _settings.set_volume_creation_settings(volume_settings) - 189 _settings.set_optical_settings(optical_settings) - 190 _settings.set_acoustic_settings(acoustic_settings) - 191 _settings.set_reconstruction_settings(reconstruction_settings) - 192 return _settings - 193 - 194 - 195 1 291.4 291.4 0.0 device = get_device() - 196 1 628.8 628.8 0.0 settings = get_settings() - 197 1 216.4 216.4 0.0 pipeline = create_pipeline(settings) - 198 - 199 1 28158333.9 3e+07 100.0 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) - 200 - 201 1 0.1 0.1 0.0 if visualise: - 202 sp.visualise_data(settings=settings, - 203 path_manager=path_manager, - 204 show_absorption=True, - 205 show_initial_pressure=True, - 206 show_reconstructed_data=True, - 207 show_xz_only=True) - -Total time: 37.106 s -File: /home/f762e/Workspace/simpa/simpa_examples/optical_and_acoustic_simulation.py -Function: run_optical_and_acoustic_simulation at line 18 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 18 @profile - 19 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), - 20 visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 28 1 0.4 0.4 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 29 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 20 - 30 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 25 - 31 1 0.6 0.6 0.0 RANDOM_SEED = 4711 - 32 - 33 # If VISUALIZE is set to True, the simulation result will be plotted - 34 1 0.2 0.2 0.0 VISUALIZE = True - 35 - 36 1 0.5 0.5 0.0 def create_example_tissue(): - 37 """ - 38 This is a very simple example script of how to create a tissue definition. - 39 It contains a muscular background, an epidermis layer on top of the muscles - 40 and a blood vessel. - 41 """ - 42 background_dictionary = sp.Settings() - 43 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) - 44 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 45 - 46 tissue_dict = sp.Settings() - 47 tissue_dict[Tags.BACKGROUND] = background_dictionary - 48 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, - 49 molecular_composition=sp.TISSUE_LIBRARY.constant( - 50 0.05, 100, 0.9), - 51 priority=1, - 52 consider_partial_volume=True, - 53 adhere_to_deformation=True) - 54 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, - 55 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), - 56 priority=8, - 57 consider_partial_volume=True, - 58 adhere_to_deformation=True) - 59 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( - 60 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], - 61 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], - 62 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 63 radius_mm=2, priority=3, consider_partial_volume=True, - 64 adhere_to_deformation=False - 65 ) - 66 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( - 67 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], - 68 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], - 69 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 70 radius_mm=3, priority=3, consider_partial_volume=True, - 71 adhere_to_deformation=False - 72 ) - 73 return tissue_dict - 74 - 75 - 76 # Seed the numpy random configuration prior to creating the global_settings file in - 77 # order to ensure that the same volume - 78 # is generated with the same random seed every time. - 79 - 80 1 9.9 9.9 0.0 np.random.seed(RANDOM_SEED) - 81 1 0.6 0.6 0.0 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) - 82 - 83 1 1.5 1.5 0.0 general_settings = { - 84 # These parameters set the general properties of the simulated volume - 85 1 0.5 0.5 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 86 1 0.5 0.5 0.0 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), - 87 1 60.5 60.5 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 88 1 0.3 0.3 0.0 Tags.SPACING_MM: SPACING, - 89 1 0.4 0.4 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 90 1 0.3 0.3 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 91 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 92 1 0.4 0.4 0.0 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 93 1 0.3 0.3 0.0 Tags.GPU: True, - 94 1 0.3 0.3 0.0 Tags.WAVELENGTHS: [700, 800], - 95 1 0.4 0.4 0.0 Tags.DO_FILE_COMPRESSION: True, - 96 1 0.2 0.2 0.0 Tags.DO_IPASC_EXPORT: True - 97 } - 98 1 16.0 16.0 0.0 settings = sp.Settings(general_settings) - 99 1 2.2 2.2 0.0 np.random.seed(RANDOM_SEED) - 100 - 101 2 7.6 3.8 0.0 settings.set_volume_creation_settings({ - 102 1 24580.8 24580.8 0.1 Tags.STRUCTURES: create_example_tissue(), - 103 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True - 104 }) - 105 - 106 2 7.9 3.9 0.0 settings.set_optical_settings({ - 107 1 0.5 0.5 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 108 1 41.6 41.6 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 109 1 0.7 0.7 0.0 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, - 110 1 0.4 0.4 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 111 1 0.3 0.3 0.0 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, - 112 }) - 113 - 114 2 10.8 5.4 0.0 settings.set_acoustic_settings({ - 115 1 0.2 0.2 0.0 Tags.ACOUSTIC_SIMULATION_3D: False, - 116 1 33.4 33.4 0.0 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 117 1 0.4 0.4 0.0 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 118 1 0.7 0.7 0.0 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 119 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLInside: False, - 120 1 0.6 0.6 0.0 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 121 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 122 1 0.3 0.3 0.0 Tags.KWAVE_PROPERTY_PlotPML: False, - 123 1 0.2 0.2 0.0 Tags.RECORDMOVIE: False, - 124 1 0.2 0.2 0.0 Tags.MOVIENAME: "visualization_log", - 125 1 0.2 0.2 0.0 Tags.ACOUSTIC_LOG_SCALE: True - 126 }) - 127 - 128 19 130.4 6.9 0.0 settings.set_reconstruction_settings({ - 129 1 0.2 0.2 0.0 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 130 1 33.7 33.7 0.0 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 131 1 0.1 0.1 0.0 Tags.ACOUSTIC_SIMULATION_3D: False, - 132 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 133 1 0.4 0.4 0.0 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 134 1 0.5 0.5 0.0 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), - 135 1 0.4 0.4 0.0 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), - 136 1 0.2 0.2 0.0 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 137 1 0.4 0.4 0.0 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 138 1 0.5 0.5 0.0 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, - 139 1 0.3 0.3 0.0 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 140 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 141 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PMLInside: False, - 142 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 143 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 144 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PlotPML: False, - 145 1 0.1 0.1 0.0 Tags.RECORDMOVIE: False, - 146 1 0.1 0.1 0.0 Tags.MOVIENAME: "visualization_log", - 147 1 0.1 0.1 0.0 Tags.ACOUSTIC_LOG_SCALE: True, - 148 1 0.2 0.2 0.0 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, - 149 1 0.3 0.3 0.0 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, - 150 1 0.3 0.3 0.0 Tags.DATA_FIELD_DENSITY: 1000, - 151 1 0.3 0.3 0.0 Tags.SPACING_MM: SPACING - 152 }) - 153 - 154 1 33.8 33.8 0.0 settings["noise_initial_pressure"] = { - 155 1 0.4 0.4 0.0 Tags.NOISE_MEAN: 1, - 156 1 0.2 0.2 0.0 Tags.NOISE_STD: 0.01, - 157 1 0.5 0.5 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 158 1 0.3 0.3 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 159 1 0.3 0.3 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 160 } - 161 - 162 1 38.6 38.6 0.0 settings["noise_time_series"] = { - 163 1 0.1 0.1 0.0 Tags.NOISE_STD: 1, - 164 1 0.4 0.4 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, - 165 1 0.3 0.3 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA - 166 } - 167 - 168 # TODO: For the device choice, uncomment the undesired device - 169 - 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 171 # VOLUME_PLANAR_DIM_IN_MM/2, - 172 # 0])) - 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) - 174 - 175 3 10.6 3.5 0.0 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 176 1 0.2 0.2 0.0 VOLUME_PLANAR_DIM_IN_MM/2, - 177 1 0.1 0.1 0.0 0]), - 178 1 1.5 1.5 0.0 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) - 179 2 23.8 11.9 0.0 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, - 180 1 0.1 0.1 0.0 pitch_mm=0.25, - 181 1 0.1 0.1 0.0 number_detector_elements=100, - 182 1 1.0 1.0 0.0 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) - 183 1 1859.4 1859.4 0.0 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) - 184 1 40.5 40.5 0.0 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) - 185 - 186 - 187 1 0.1 0.1 0.0 SIMULATION_PIPELINE = [ - 188 1 91.1 91.1 0.0 sp.ModelBasedVolumeCreationAdapter(settings), - 189 1 8.5 8.5 0.0 sp.MCXAdapter(settings), - 190 1 50.5 50.5 0.0 sp.GaussianNoise(settings, "noise_initial_pressure"), - 191 1 4.4 4.4 0.0 sp.KWaveAdapter(settings), - 192 1 38.3 38.3 0.0 sp.GaussianNoise(settings, "noise_time_series"), - 193 1 5.1 5.1 0.0 sp.TimeReversalAdapter(settings), - 194 1 111.2 111.2 0.0 sp.FieldOfViewCropping(settings) - 195 ] - 196 - 197 1 37078767.0 4e+07 99.9 sp.simulate(SIMULATION_PIPELINE, settings, device) - 198 - 199 1 2.5 2.5 0.0 if Tags.WAVELENGTH in settings: - 200 1 1.3 1.3 0.0 WAVELENGTH = settings[Tags.WAVELENGTH] - 201 else: - 202 WAVELENGTH = 700 - 203 - 204 1 0.1 0.1 0.0 if visualise: - 205 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - 206 wavelength=WAVELENGTH, - 207 show_time_series_data=True, - 208 show_initial_pressure=True, - 209 show_reconstructed_data=True, - 210 log_scale=False, - 211 show_xz_only=False) - -Total time: 8.42102 s -File: /home/f762e/Workspace/simpa/simpa_examples/perform_iterative_qPAI_reconstruction.py -Function: run_perform_iterative_qPAI_reconstruction at line 25 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 25 @profile - 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise:bool = True): - 27 """ - 28 - 29 :param SPACING: The simulation spacing between voxels - 30 :param path_manager: the path manager to be used, typically sp.PathManager - 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 32 :return: a run through of the example - 33 """ - 34 1 0.5 0.5 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 30 - 35 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 - 36 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 30 - 37 1 0.1 0.1 0.0 RANDOM_SEED = 471 - 38 1 0.8 0.8 0.0 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) - 39 - 40 # If VISUALIZE is set to True, the reconstruction result will be plotted - 41 - 42 1 0.8 0.8 0.0 def create_example_tissue(): - 43 """ - 44 This is a very simple example script of how to create a tissue definition. - 45 It contains a muscular background, an epidermis layer on top of the muscles - 46 and a blood vessel. - 47 """ - 48 background_dictionary = sp.Settings() - 49 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) - 50 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 51 - 52 epidermis_structure = sp.Settings() - 53 epidermis_structure[Tags.PRIORITY] = 1 - 54 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] - 55 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] - 56 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) - 57 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True - 58 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True - 59 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 60 - 61 vessel_structure_1 = sp.Settings() - 62 vessel_structure_1[Tags.PRIORITY] = 2 - 63 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, - 64 VOLUME_HEIGHT_IN_MM / 2] - 65 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, - 66 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] - 67 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 - 68 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 - 69 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) - 70 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True - 71 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True - 72 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE - 73 - 74 vessel_structure_2 = sp.Settings() - 75 vessel_structure_2[Tags.PRIORITY] = 3 - 76 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, - 77 VOLUME_HEIGHT_IN_MM / 3] - 78 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, - 79 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] - 80 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 - 81 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) - 82 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True - 83 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 84 - 85 tissue_dict = sp.Settings() - 86 tissue_dict[Tags.BACKGROUND] = background_dictionary - 87 tissue_dict["epidermis"] = epidermis_structure - 88 tissue_dict["vessel_1"] = vessel_structure_1 - 89 tissue_dict["vessel_2"] = vessel_structure_2 - 90 return tissue_dict - 91 - 92 - 93 # set settings for volume creation, optical simulation and iterative qPAI method - 94 1 13.4 13.4 0.0 np.random.seed(RANDOM_SEED) - 95 - 96 1 1.0 1.0 0.0 general_settings = { - 97 # These parameters set the general properties of the simulated volume - 98 1 0.4 0.4 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 99 1 0.4 0.4 0.0 Tags.VOLUME_NAME: VOLUME_NAME, - 100 1 68.8 68.8 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 101 1 0.2 0.2 0.0 Tags.SPACING_MM: SPACING, - 102 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 103 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 104 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 105 1 0.2 0.2 0.0 Tags.WAVELENGTHS: [700] - 106 } - 107 - 108 1 14.8 14.8 0.0 settings = sp.Settings(general_settings) - 109 - 110 2 5.8 2.9 0.0 settings.set_volume_creation_settings({ - 111 # These parameters set the properties for the volume creation - 112 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, - 113 1 9341.6 9341.6 0.1 Tags.STRUCTURES: create_example_tissue() - 114 }) - 115 2 6.9 3.5 0.0 settings.set_optical_settings({ - 116 # These parameters set the properties for the optical Monte Carlo simulation - 117 1 0.8 0.8 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 118 1 36.8 36.8 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 119 1 0.8 0.8 0.0 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 120 1 0.7 0.7 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 121 }) - 122 1 39.3 39.3 0.0 settings["noise_model"] = { - 123 1 0.3 0.3 0.0 Tags.NOISE_MEAN: 1.0, - 124 1 0.2 0.2 0.0 Tags.NOISE_STD: 0.01, - 125 1 0.7 0.7 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 126 1 0.5 0.5 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 127 1 0.8 0.8 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 128 } - 129 1 42.2 42.2 0.0 settings["iterative_qpai_reconstruction"] = { - 130 # These parameters set the properties of the iterative reconstruction - 131 1 0.6 0.6 0.0 Tags.DOWNSCALE_FACTOR: 0.75, - 132 1 0.6 0.6 0.0 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, - 133 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant - 134 1 0.3 0.3 0.0 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, - 135 1 0.5 0.5 0.0 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, - 136 # for this example, we are not interested in all absorption updates - 137 1 0.5 0.5 0.0 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, - 138 1 3.7 3.7 0.0 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 - 139 } - 140 - 141 # run pipeline including iterative qPAI method - 142 1 0.1 0.1 0.0 pipeline = [ - 143 1 68.5 68.5 0.0 sp.ModelBasedVolumeCreationAdapter(settings), - 144 1 8.7 8.7 0.0 sp.MCXAdapter(settings), - 145 1 49.3 49.3 0.0 sp.GaussianNoise(settings, "noise_model"), - 146 1 15.4 15.4 0.0 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") - 147 ] - 148 - 149 - 150 1 23.4 23.4 0.0 class CustomDevice(sp.PhotoacousticDevice): - 151 - 152 def __init__(self): - 153 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, - 154 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, - 155 0])) - 156 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) - 157 - 158 - 159 1 46.7 46.7 0.0 device = CustomDevice() - 160 - 161 1 0.6 0.6 0.0 device.update_settings_for_use_of_model_based_volume_creator(settings) - 162 - 163 1 8411215.6 8e+06 99.9 sp.simulate(pipeline, settings, device) - 164 - 165 # visualize reconstruction results - 166 1 1.0 1.0 0.0 if visualise: - 167 # get simulation output - 168 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 169 settings = sp.load_data_field(data_path, Tags.SETTINGS) - 170 wavelength = settings[Tags.WAVELENGTHS][0] - 171 - 172 # get reconstruction result - 173 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) - 174 - 175 # get ground truth absorption coefficients - 176 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) - 177 - 178 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) - 179 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR - 180 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") - 181 - 182 # compute reconstruction error - 183 difference = absorption_gt - absorption_reconstruction - 184 - 185 median_error = np.median(difference) - 186 q3, q1 = np.percentile(difference, [75, 25]) - 187 iqr = q3 - q1 - 188 - 189 # visualize results - 190 x_pos = int(np.shape(absorption_gt)[0] / 2) - 191 y_pos = int(np.shape(absorption_gt)[1] / 2) - 192 - 193 if np.min(absorption_gt) > np.min(absorption_reconstruction): - 194 cmin = np.min(absorption_reconstruction) - 195 else: - 196 cmin = np.min(absorption_gt) - 197 - 198 if np.max(absorption_gt) > np.max(absorption_reconstruction): - 199 cmax = np.max(absorption_gt) - 200 else: - 201 cmax = np.max(absorption_reconstruction) - 202 - 203 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] - 204 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] - 205 - 206 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", - 207 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] - 208 - 209 plt.figure(figsize=(20, 15)) - 210 plt.subplots_adjust(hspace=0.5) - 211 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + - 212 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) - 213 - 214 for i, quantity in enumerate(results_x_z): - 215 plt.subplot(2, len(results_x_z), i + 1) - 216 if i == 0: - 217 plt.ylabel("x-z", fontsize=10) - 218 plt.title(label[i], fontsize=10) - 219 plt.imshow(quantity.T) - 220 plt.xticks(fontsize=6) - 221 plt.yticks(fontsize=6) - 222 plt.colorbar() - 223 if i != 2: - 224 plt.clim(cmin, cmax) - 225 else: - 226 plt.clim(np.min(difference), np.max(difference)) - 227 - 228 for i, quantity in enumerate(results_y_z): - 229 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) - 230 if i == 0: - 231 plt.ylabel("y-z", fontsize=10) - 232 plt.title(label[i], fontsize=10) - 233 plt.imshow(quantity.T) - 234 plt.xticks(fontsize=6) - 235 plt.yticks(fontsize=6) - 236 plt.colorbar() - 237 if i != 2: - 238 plt.clim(cmin, cmax) - 239 else: - 240 plt.clim(np.min(difference), np.max(difference)) - 241 - 242 plt.show() - 243 plt.close() - diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.4.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.4.txt deleted file mode 100644 index a519b662..00000000 --- a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_time_0.4.txt +++ /dev/null @@ -1,1048 +0,0 @@ -Timer unit: 1e-06 s - -Total time: 4.93008 s -File: /home/f762e/Workspace/simpa/simpa_examples/linear_unmixing.py -Function: run_linear_unmixing at line 18 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 18 @profile - 19 def run_linear_unmixing(SPACING: Union[int, float] = 0.25, path_manager=sp.PathManager, visualise: bool = True): - 20 """ - 21 - 22 :param SPACING: The simulation spacing between voxels - 23 :param path_manager: the path manager to be used, typically sp.PathManager - 24 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 25 :return: a run through of the example - 26 """ - 27 # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you - 28 # set global params characterizing the simulated volume - 29 1 0.5 0.5 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 30 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 20 - 31 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 25 - 32 1 0.1 0.1 0.0 RANDOM_SEED = 471 - 33 1 0.6 0.6 0.0 VOLUME_NAME = "LinearUnmixingExample_" + str(RANDOM_SEED) - 34 - 35 # since we want to perform linear unmixing, the simulation pipeline should be execute for at least two wavelengths - 36 1 0.2 0.2 0.0 WAVELENGTHS = [750, 800, 850] - 37 - 38 1 0.3 0.3 0.0 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and two blood vessels. - 43 """ - 44 background_dictionary = sp.Settings() - 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 muscle_dictionary = sp.Settings() - 49 muscle_dictionary[Tags.PRIORITY] = 1 - 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 vessel_1_dictionary = sp.Settings() - 58 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 10, - 61 5] - 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 12, - 64 5] - 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.99) - 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 vessel_2_dictionary = sp.Settings() - 71 vessel_2_dictionary[Tags.PRIORITY] = 3 - 72 vessel_2_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 73 10, - 74 5] - 75 vessel_2_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/3, - 76 12, - 77 5] - 78 vessel_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 79 vessel_2_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood(oxygenation=0.75) - 80 vessel_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 81 vessel_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 82 - 83 epidermis_dictionary = sp.Settings() - 84 epidermis_dictionary[Tags.PRIORITY] = 8 - 85 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 0] - 86 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 0.1] - 87 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 88 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 89 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 90 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 91 - 92 tissue_dict = sp.Settings() - 93 tissue_dict[Tags.BACKGROUND] = background_dictionary - 94 tissue_dict["muscle"] = muscle_dictionary - 95 tissue_dict["epidermis"] = epidermis_dictionary - 96 tissue_dict["vessel_1"] = vessel_1_dictionary - 97 tissue_dict["vessel_2"] = vessel_2_dictionary - 98 return tissue_dict - 99 - 100 - 101 # Seed the numpy random configuration prior to creating the global_settings file in - 102 # order to ensure that the same volume is generated with the same random seed every time. - 103 1 16.0 16.0 0.0 np.random.seed(RANDOM_SEED) - 104 - 105 # Initialize global settings and prepare for simulation pipeline including - 106 # volume creation and optical forward simulation. - 107 1 1.5 1.5 0.0 general_settings = { - 108 # These parameters set the general properties of the simulated volume - 109 1 1.0 1.0 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 110 1 0.5 0.5 0.0 Tags.VOLUME_NAME: VOLUME_NAME, - 111 1 54.7 54.7 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 112 1 0.4 0.4 0.0 Tags.SPACING_MM: SPACING, - 113 1 0.5 0.5 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 114 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 115 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 116 1 0.3 0.3 0.0 Tags.WAVELENGTHS: WAVELENGTHS, - 117 1 0.5 0.5 0.0 Tags.GPU: True, - 118 1 0.3 0.3 0.0 Tags.DO_FILE_COMPRESSION: True - 119 } - 120 1 69.9 69.9 0.0 settings = sp.Settings(general_settings) - 121 2 6.5 3.2 0.0 settings.set_volume_creation_settings({ - 122 1 0.4 0.4 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, - 123 1 31044.7 31044.7 0.6 Tags.STRUCTURES: create_example_tissue() - 124 }) - 125 2 7.3 3.7 0.0 settings.set_optical_settings({ - 126 1 0.4 0.4 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 127 1 36.6 36.6 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 128 1 0.6 0.6 0.0 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 129 1 0.2 0.2 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 130 }) - 131 - 132 # Set component settings for linear unmixing. - 133 # In this example we are only interested in the chromophore concentration of oxy- and deoxyhemoglobin and the - 134 # resulting blood oxygen saturation. We want to perform the algorithm using all three wavelengths defined above. - 135 # Please take a look at the component for more information. - 136 1 60.4 60.4 0.0 settings["linear_unmixing"] = { - 137 1 0.7 0.7 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 138 1 0.2 0.2 0.0 Tags.WAVELENGTHS: WAVELENGTHS, - 139 2 1961.6 980.8 0.0 Tags.LINEAR_UNMIXING_SPECTRA: sp.get_simpa_internal_absorption_spectra_by_names( - 140 1 0.4 0.4 0.0 [Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_OXYHEMOGLOBIN, Tags.SIMPA_NAMED_ABSORPTION_SPECTRUM_DEOXYHEMOGLOBIN] - 141 ), - 142 1 0.5 0.5 0.0 Tags.LINEAR_UNMIXING_COMPUTE_SO2: True, - 143 1 0.3 0.3 0.0 Tags.LINEAR_UNMIXING_NON_NEGATIVE: True - 144 } - 145 - 146 # Get device for simulation - 147 2 141.7 70.9 0.0 device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 148 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM/2, - 149 1 0.1 0.1 0.0 0])) - 150 1 4495.9 4495.9 0.1 device.update_settings_for_use_of_model_based_volume_creator(settings) - 151 - 152 # Run simulation pipeline for all wavelengths in Tag.WAVELENGTHS - 153 1 0.2 0.2 0.0 pipeline = [ - 154 1 14382.3 14382.3 0.3 sp.ModelBasedVolumeCreationAdapter(settings), - 155 1 13.3 13.3 0.0 sp.MCXAdapter(settings), - 156 1 146.7 146.7 0.0 sp.FieldOfViewCropping(settings), - 157 ] - 158 1 3958047.6 4e+06 80.3 sp.simulate(pipeline, settings, device) - 159 - 160 # Run linear unmixing component with above specified settings. - 161 1 917597.9 917597.9 18.6 sp.LinearUnmixing(settings, "linear_unmixing").run() - 162 - 163 # Load linear unmixing result (blood oxygen saturation) and reference absorption for first wavelength. - 164 1 56.3 56.3 0.0 file_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 165 1 825.0 825.0 0.0 lu_results = sp.load_data_field(file_path, Tags.LINEAR_UNMIXING_RESULT) - 166 1 0.3 0.3 0.0 sO2 = lu_results["sO2"] - 167 - 168 1 328.3 328.3 0.0 mua = sp.load_data_field(file_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength=WAVELENGTHS[0]) - 169 1 477.8 477.8 0.0 p0 = sp.load_data_field(file_path, Tags.DATA_FIELD_INITIAL_PRESSURE, wavelength=WAVELENGTHS[0]) - 170 1 298.7 298.7 0.0 gt_oxy = sp.load_data_field(file_path, Tags.DATA_FIELD_OXYGENATION, wavelength=WAVELENGTHS[0]) - 171 - 172 # Visualize linear unmixing result - 173 1 0.2 0.2 0.0 if visualise: - 174 visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 175 wavelength=WAVELENGTHS[0], - 176 show_initial_pressure=True, - 177 show_oxygenation=True, - 178 show_linear_unmixing_sO2=True) - -Total time: 2.86975 s -File: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation.py -Function: run_minimal_optical_simulation at line 19 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 19 @profile - 20 def run_minimal_optical_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 28 1 0.3 0.3 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 29 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 - 30 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 60 - 31 1 0.3 0.3 0.0 RANDOM_SEED = 471 - 32 1 0.6 0.6 0.0 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 33 1 0.1 0.1 0.0 SAVE_REFLECTANCE = False - 34 1 0.1 0.1 0.0 SAVE_PHOTON_DIRECTION = False - 35 - 36 # If VISUALIZE is set to True, the simulation result will be plotted - 37 - 38 1 2.3 2.3 0.0 def create_example_tissue(): - 39 """ - 40 This is a very simple example script of how to create a tissue definition. - 41 It contains a muscular background, an epidermis layer on top of the muscles - 42 and a blood vessel. - 43 """ - 44 background_dictionary = sp.Settings() - 45 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 46 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 47 - 48 muscle_dictionary = sp.Settings() - 49 muscle_dictionary[Tags.PRIORITY] = 1 - 50 muscle_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 10] - 51 muscle_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 100] - 52 muscle_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.muscle() - 53 muscle_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 54 muscle_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 55 muscle_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 56 - 57 vessel_1_dictionary = sp.Settings() - 58 vessel_1_dictionary[Tags.PRIORITY] = 3 - 59 vessel_1_dictionary[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 60 10, - 61 VOLUME_HEIGHT_IN_MM/2] - 62 vessel_1_dictionary[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM/2, - 63 12, - 64 VOLUME_HEIGHT_IN_MM/2] - 65 vessel_1_dictionary[Tags.STRUCTURE_RADIUS_MM] = 3 - 66 vessel_1_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.blood() - 67 vessel_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 68 vessel_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 69 - 70 epidermis_dictionary = sp.Settings() - 71 epidermis_dictionary[Tags.PRIORITY] = 8 - 72 epidermis_dictionary[Tags.STRUCTURE_START_MM] = [0, 0, 9] - 73 epidermis_dictionary[Tags.STRUCTURE_END_MM] = [0, 0, 10] - 74 epidermis_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.epidermis() - 75 epidermis_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = True - 76 epidermis_dictionary[Tags.ADHERE_TO_DEFORMATION] = True - 77 epidermis_dictionary[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 78 - 79 tissue_dict = sp.Settings() - 80 tissue_dict[Tags.BACKGROUND] = background_dictionary - 81 tissue_dict["muscle"] = muscle_dictionary - 82 tissue_dict["epidermis"] = epidermis_dictionary - 83 tissue_dict["vessel_1"] = vessel_1_dictionary - 84 return tissue_dict - 85 - 86 - 87 # Seed the numpy random configuration prior to creating the global_settings file in - 88 # order to ensure that the same volume - 89 # is generated with the same random seed every time. - 90 - 91 1 12.0 12.0 0.0 np.random.seed(RANDOM_SEED) - 92 - 93 1 1.0 1.0 0.0 general_settings = { - 94 # These parameters set the general properties of the simulated volume - 95 1 0.3 0.3 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 96 1 0.3 0.3 0.0 Tags.VOLUME_NAME: VOLUME_NAME, - 97 1 52.2 52.2 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 98 1 0.4 0.4 0.0 Tags.SPACING_MM: SPACING, - 99 1 0.2 0.2 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 100 1 0.2 0.2 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 101 1 0.1 0.1 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 102 1 0.3 0.3 0.0 Tags.WAVELENGTHS: [798], - 103 1 0.3 0.3 0.0 Tags.DO_FILE_COMPRESSION: True, - 104 1 0.2 0.2 0.0 Tags.GPU: True - 105 } - 106 - 107 1 15.1 15.1 0.0 settings = sp.Settings(general_settings) - 108 - 109 2 6.1 3.0 0.0 settings.set_volume_creation_settings({ - 110 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, - 111 1 24443.6 24443.6 0.9 Tags.STRUCTURES: create_example_tissue() - 112 }) - 113 2 68.8 34.4 0.0 settings.set_optical_settings({ - 114 1 0.3 0.3 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 115 1 34.9 34.9 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 116 1 0.4 0.4 0.0 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 117 1 0.3 0.3 0.0 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 118 }) - 119 1 35.0 35.0 0.0 settings["noise_model_1"] = { - 120 1 0.4 0.4 0.0 Tags.NOISE_MEAN: 1.0, - 121 1 0.9 0.9 0.0 Tags.NOISE_STD: 0.1, - 122 1 0.6 0.6 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 123 1 0.5 0.5 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 124 1 0.3 0.3 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 125 } - 126 - 127 1 0.1 0.1 0.0 if not SAVE_REFLECTANCE and not SAVE_PHOTON_DIRECTION: - 128 1 0.2 0.2 0.0 pipeline = [ - 129 1 69.8 69.8 0.0 sp.ModelBasedVolumeCreationAdapter(settings), - 130 1 10.0 10.0 0.0 sp.MCXAdapter(settings), - 131 1 46.4 46.4 0.0 sp.GaussianNoise(settings, "noise_model_1") - 132 ] - 133 else: - 134 pipeline = [ - 135 sp.ModelBasedVolumeCreationAdapter(settings), - 136 sp.MCXAdapterReflectance(settings), - 137 ] - 138 - 139 - 140 1 25.3 25.3 0.0 class ExampleDeviceSlitIlluminationLinearDetector(sp.PhotoacousticDevice): - 141 """ - 142 This class represents a digital twin of a PA device with a slit as illumination next to a linear detection geometry. - 143 - 144 """ - 145 - 146 def __init__(self): - 147 super().__init__(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 148 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 149 self.set_detection_geometry(sp.LinearArrayDetectionGeometry()) - 150 self.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[20, 0, 0], - 151 direction_vector_mm=[0, 0, 1])) - 152 - 153 - 154 1 65.2 65.2 0.0 device = ExampleDeviceSlitIlluminationLinearDetector() - 155 - 156 1 2844852.2 3e+06 99.1 sp.simulate(pipeline, settings, device) - 157 - 158 1 3.7 3.7 0.0 if Tags.WAVELENGTH in settings: - 159 1 2.0 2.0 0.0 WAVELENGTH = settings[Tags.WAVELENGTH] - 160 else: - 161 WAVELENGTH = 700 - 162 - 163 1 0.2 0.2 0.0 if visualise: - 164 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 165 wavelength=WAVELENGTH, - 166 show_initial_pressure=True, - 167 show_absorption=True, - 168 show_diffuse_reflectance=SAVE_REFLECTANCE, - 169 log_scale=True) - -Total time: 1.76131 s -File: /home/f762e/Workspace/simpa/simpa_examples/minimal_optical_simulation_uniform_cube.py -Function: run_minimal_optical_simulation_uniform_cube at line 22 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 22 @profile - 23 def run_minimal_optical_simulation_uniform_cube(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), - 24 visualise:bool = True): - 25 """ - 26 - 27 :param SPACING: The simulation spacing between voxels - 28 :param path_manager: the path manager to be used, typically sp.PathManager - 29 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 30 :return: a run through of the example - 31 """ - 32 1 0.4 0.4 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 60 - 33 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 - 34 1 0.3 0.3 0.0 VOLUME_HEIGHT_IN_MM = 60 - 35 1 2.4 2.4 0.0 RANDOM_SEED = 471 - 36 1 0.6 0.6 0.0 VOLUME_NAME = "MyVolumeName_"+str(RANDOM_SEED) - 37 1 0.2 0.2 0.0 SAVE_REFLECTANCE = True - 38 1 0.1 0.1 0.0 SAVE_PHOTON_DIRECTION = False - 39 - 40 # If VISUALIZE is set to True, the simulation result will be plotted - 41 1 0.1 0.1 0.0 VISUALIZE = True - 42 - 43 - 44 1 0.6 0.6 0.0 def create_example_tissue(): - 45 """ - 46 This is a very simple example script of how to create a tissue definition. - 47 It contains only a generic background tissue material. - 48 """ - 49 background_dictionary = sp.Settings() - 50 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-4, 1e-4, 0.9) - 51 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 52 - 53 tissue_dict = sp.Settings() - 54 tissue_dict[Tags.BACKGROUND] = background_dictionary - 55 return tissue_dict - 56 - 57 - 58 # Seed the numpy random configuration prior to creating the global_settings file in - 59 # order to ensure that the same volume - 60 # is generated with the same random seed every time. - 61 - 62 1 11.4 11.4 0.0 np.random.seed(RANDOM_SEED) - 63 - 64 1 0.9 0.9 0.0 general_settings = { - 65 # These parameters set the general properties of the simulated volume - 66 1 0.8 0.8 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 67 1 0.3 0.3 0.0 Tags.VOLUME_NAME: VOLUME_NAME, - 68 1 52.4 52.4 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 69 1 1.0 1.0 0.0 Tags.SPACING_MM: SPACING, - 70 1 0.2 0.2 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 71 1 0.3 0.3 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 72 1 0.2 0.2 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 73 1 0.3 0.3 0.0 Tags.WAVELENGTHS: [500], - 74 1 0.4 0.4 0.0 Tags.DO_FILE_COMPRESSION: True - 75 } - 76 - 77 1 14.5 14.5 0.0 settings = sp.Settings(general_settings) - 78 - 79 2 4.9 2.5 0.0 settings.set_volume_creation_settings({ - 80 1 2299.6 2299.6 0.1 Tags.STRUCTURES: create_example_tissue() - 81 }) - 82 2 71.8 35.9 0.0 settings.set_optical_settings({ - 83 1 0.3 0.3 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 5e7, - 84 1 37.5 37.5 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 85 1 0.4 0.4 0.0 Tags.COMPUTE_DIFFUSE_REFLECTANCE: SAVE_REFLECTANCE, - 86 1 0.2 0.2 0.0 Tags.COMPUTE_PHOTON_DIRECTION_AT_EXIT: SAVE_PHOTON_DIRECTION - 87 }) - 88 - 89 1 0.1 0.1 0.0 pipeline = [ - 90 1 68.3 68.3 0.0 sp.ModelBasedVolumeCreationAdapter(settings), - 91 1 21.3 21.3 0.0 sp.MCXAdapterReflectance(settings), - 92 ] - 93 - 94 2 30.9 15.5 0.0 device = sp.PencilBeamIlluminationGeometry(device_position_mm=np.asarray([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 95 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM/2, 0])) - 96 - 97 1 1758689.2 2e+06 99.9 sp.simulate(pipeline, settings, device) - 98 - 99 1 0.3 0.3 0.0 if visualise: - 100 sp.visualise_data(path_to_hdf5_file=path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5", - 101 wavelength=settings[Tags.WAVELENGTH], - 102 show_initial_pressure=True, - 103 show_absorption=True, - 104 show_diffuse_reflectance=SAVE_REFLECTANCE, - 105 log_scale=True) - -Total time: 18.452 s -File: /home/f762e/Workspace/simpa/simpa_examples/msot_invision_simulation.py -Function: run_msot_invision_simulation at line 13 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 13 @profile - 14 def run_msot_invision_simulation(SPACING: Union[int, float] = 0.5, path_manager=sp.PathManager(), visualise:bool = True): - 15 """ - 16 - 17 :param SPACING: The simulation spacing between voxels - 18 :param path_manager: the path manager to be used, typically sp.PathManager - 19 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 20 :return: a run through of the example - 21 """ - 22 1 0.3 0.3 0.0 SPEED_OF_SOUND = 1500 - 23 1 0.2 0.2 0.0 XZ_DIM = 90 - 24 1 0.2 0.2 0.0 Y_DIM = 40 - 25 - 26 1 0.8 0.8 0.0 def create_pipeline(_settings: sp.Settings): - 27 return [ - 28 sp.ModelBasedVolumeCreationAdapter(settings), - 29 sp.MCXAdapter(settings), - 30 sp.KWaveAdapter(settings), - 31 sp.FieldOfViewCropping(settings), - 32 sp.TimeReversalAdapter(settings) - 33 ] - 34 - 35 - 36 1 0.7 0.7 0.0 def get_device(): - 37 pa_device = sp.InVision256TF(device_position_mm=np.asarray([XZ_DIM/2, Y_DIM/2, XZ_DIM/2])) - 38 return pa_device - 39 - 40 - 41 1 0.3 0.3 0.0 def create_volume(): - 42 inclusion_material = sp.Molecule(volume_fraction=1.0, - 43 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 44 0.9), - 45 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 46 100.0), - 47 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 48 4.0), - 49 speed_of_sound=SPEED_OF_SOUND, - 50 alpha_coefficient=1e-4, - 51 density=1000, - 52 gruneisen_parameter=1.0, - 53 name="Inclusion") - 54 - 55 phantom_material = sp.Molecule(volume_fraction=1.0, - 56 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.9), - 57 scattering_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY( - 58 100.0), - 59 absorption_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(0.05), - 60 speed_of_sound=SPEED_OF_SOUND, - 61 alpha_coefficient=1e-4, - 62 density=1000, - 63 gruneisen_parameter=1.0, - 64 name="Phantom") - 65 - 66 heavy_water = sp.Molecule(volume_fraction=1.0, - 67 anisotropy_spectrum=sp.AnisotropySpectrumLibrary.CONSTANT_ANISOTROPY_ARBITRARY(1.0), - 68 scattering_spectrum=sp.ScatteringSpectrumLibrary.CONSTANT_SCATTERING_ARBITRARY(0.1), - 69 absorption_spectrum=sp.AbsorptionSpectrumLibrary.CONSTANT_ABSORBER_ARBITRARY(1e-30), - 70 speed_of_sound=SPEED_OF_SOUND, - 71 alpha_coefficient=1e-4, - 72 density=1000, - 73 gruneisen_parameter=1.0, - 74 name="background_water") - 75 - 76 background_dictionary = sp.Settings() - 77 background_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 78 .append(heavy_water) - 79 .get_molecular_composition(segmentation_type=-1)) - 80 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 81 - 82 phantom_material_dictionary = sp.Settings() - 83 phantom_material_dictionary[Tags.PRIORITY] = 3 - 84 phantom_material_dictionary[Tags.STRUCTURE_START_MM] = [31, 0, 38] - 85 phantom_material_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 28 - 86 phantom_material_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 40 - 87 phantom_material_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 14 - 88 phantom_material_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 89 .append(phantom_material) - 90 .get_molecular_composition(segmentation_type=0)) - 91 phantom_material_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 92 phantom_material_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 93 - 94 inclusion_1_dictionary = sp.Settings() - 95 inclusion_1_dictionary[Tags.PRIORITY] = 8 - 96 inclusion_1_dictionary[Tags.STRUCTURE_START_MM] = [38, 10, 40] - 97 inclusion_1_dictionary[Tags.STRUCTURE_X_EXTENT_MM] = 2 - 98 inclusion_1_dictionary[Tags.STRUCTURE_Y_EXTENT_MM] = 20 - 99 inclusion_1_dictionary[Tags.STRUCTURE_Z_EXTENT_MM] = 10 - 100 inclusion_1_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 101 .append(inclusion_material) - 102 .get_molecular_composition(segmentation_type=1)) - 103 inclusion_1_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 104 inclusion_1_dictionary[Tags.STRUCTURE_TYPE] = Tags.RECTANGULAR_CUBOID_STRUCTURE - 105 - 106 inclusion_2_dictionary = sp.Settings() - 107 inclusion_2_dictionary[Tags.PRIORITY] = 5 - 108 inclusion_2_dictionary[Tags.STRUCTURE_START_MM] = [50, 0, 43] - 109 inclusion_2_dictionary[Tags.STRUCTURE_END_MM] = [50, 40, 43] - 110 inclusion_2_dictionary[Tags.STRUCTURE_RADIUS_MM] = 2 - 111 inclusion_2_dictionary[Tags.MOLECULE_COMPOSITION] = (sp.MolecularCompositionGenerator() - 112 .append(inclusion_material) - 113 .get_molecular_composition(segmentation_type=2)) - 114 inclusion_2_dictionary[Tags.CONSIDER_PARTIAL_VOLUME] = False - 115 inclusion_2_dictionary[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 116 - 117 tissue_dict = sp.Settings() - 118 tissue_dict[Tags.BACKGROUND] = background_dictionary - 119 tissue_dict["phantom"] = phantom_material_dictionary - 120 tissue_dict["inclusion_1"] = inclusion_1_dictionary - 121 tissue_dict["inclusion_2"] = inclusion_2_dictionary - 122 return { - 123 Tags.STRUCTURES: tissue_dict, - 124 Tags.SIMULATE_DEFORMED_LAYERS: False - 125 } - 126 - 127 - 128 1 0.3 0.3 0.0 def get_settings(): - 129 general_settings = { - 130 # These parameters set the general properties of the simulated volume - 131 Tags.RANDOM_SEED: 4711, - 132 Tags.VOLUME_NAME: "InVision Simulation Example", - 133 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 134 Tags.SPACING_MM: SPACING, - 135 Tags.DIM_VOLUME_Z_MM: XZ_DIM, - 136 Tags.DIM_VOLUME_X_MM: XZ_DIM, - 137 Tags.DIM_VOLUME_Y_MM: Y_DIM, - 138 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 139 Tags.GPU: True, - 140 Tags.WAVELENGTHS: [700] - 141 } - 142 - 143 volume_settings = create_volume() - 144 - 145 optical_settings = { - 146 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 147 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 148 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_INVISION, - 149 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 150 } - 151 - 152 acoustic_settings = { - 153 Tags.ACOUSTIC_SIMULATION_3D: True, - 154 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 155 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 156 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 157 Tags.KWAVE_PROPERTY_PMLInside: False, - 158 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 159 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 160 Tags.KWAVE_PROPERTY_PlotPML: False, - 161 Tags.RECORDMOVIE: False, - 162 Tags.MOVIENAME: "visualization_log", - 163 Tags.ACOUSTIC_LOG_SCALE: True - 164 } - 165 - 166 reconstruction_settings = { - 167 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 168 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 169 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 170 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 171 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_HAMMING, - 172 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 173 Tags.DATA_FIELD_SPEED_OF_SOUND: SPEED_OF_SOUND, - 174 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 175 Tags.KWAVE_PROPERTY_PMLInside: False, - 176 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 177 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 178 Tags.KWAVE_PROPERTY_PlotPML: False, - 179 Tags.RECORDMOVIE: False, - 180 Tags.MOVIENAME: "visualization_log", - 181 Tags.ACOUSTIC_LOG_SCALE: True, - 182 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 183 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 184 Tags.SPACING_MM: 0.25, - 185 } - 186 - 187 _settings = sp.Settings(general_settings) - 188 _settings.set_volume_creation_settings(volume_settings) - 189 _settings.set_optical_settings(optical_settings) - 190 _settings.set_acoustic_settings(acoustic_settings) - 191 _settings.set_reconstruction_settings(reconstruction_settings) - 192 return _settings - 193 - 194 - 195 1 251.3 251.3 0.0 device = get_device() - 196 1 620.1 620.1 0.0 settings = get_settings() - 197 1 213.8 213.8 0.0 pipeline = create_pipeline(settings) - 198 - 199 1 18450957.5 2e+07 100.0 sp.simulate(simulation_pipeline=pipeline, digital_device_twin=device, settings=settings) - 200 - 201 1 0.3 0.3 0.0 if visualise: - 202 sp.visualise_data(settings=settings, - 203 path_manager=path_manager, - 204 show_absorption=True, - 205 show_initial_pressure=True, - 206 show_reconstructed_data=True, - 207 show_xz_only=True) - -Total time: 9.57549 s -File: /home/f762e/Workspace/simpa/simpa_examples/optical_and_acoustic_simulation.py -Function: run_optical_and_acoustic_simulation at line 18 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 18 @profile - 19 def run_optical_and_acoustic_simulation(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), - 20 visualise: bool = True): - 21 """ - 22 - 23 :param SPACING: The simulation spacing between voxels - 24 :param path_manager: the path manager to be used, typically sp.PathManager - 25 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 26 :return: a run through of the example - 27 """ - 28 1 0.3 0.3 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 75 - 29 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 20 - 30 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 25 - 31 1 0.1 0.1 0.0 RANDOM_SEED = 4711 - 32 - 33 # If VISUALIZE is set to True, the simulation result will be plotted - 34 1 0.1 0.1 0.0 VISUALIZE = True - 35 - 36 1 0.8 0.8 0.0 def create_example_tissue(): - 37 """ - 38 This is a very simple example script of how to create a tissue definition. - 39 It contains a muscular background, an epidermis layer on top of the muscles - 40 and a blood vessel. - 41 """ - 42 background_dictionary = sp.Settings() - 43 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(1e-10, 1e-10, 1.0) - 44 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 45 - 46 tissue_dict = sp.Settings() - 47 tissue_dict[Tags.BACKGROUND] = background_dictionary - 48 tissue_dict["muscle"] = sp.define_horizontal_layer_structure_settings(z_start_mm=0, thickness_mm=100, - 49 molecular_composition=sp.TISSUE_LIBRARY.constant( - 50 0.05, 100, 0.9), - 51 priority=1, - 52 consider_partial_volume=True, - 53 adhere_to_deformation=True) - 54 tissue_dict["epidermis"] = sp.define_horizontal_layer_structure_settings(z_start_mm=1, thickness_mm=0.1, - 55 molecular_composition=sp.TISSUE_LIBRARY.epidermis(), - 56 priority=8, - 57 consider_partial_volume=True, - 58 adhere_to_deformation=True) - 59 tissue_dict["vessel_1"] = sp.define_circular_tubular_structure_settings( - 60 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, 0, 5], - 61 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2 - 10, VOLUME_PLANAR_DIM_IN_MM, 5], - 62 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 63 radius_mm=2, priority=3, consider_partial_volume=True, - 64 adhere_to_deformation=False - 65 ) - 66 tissue_dict["vessel_2"] = sp.define_circular_tubular_structure_settings( - 67 tube_start_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, 0, 10], - 68 tube_end_mm=[VOLUME_TRANSDUCER_DIM_IN_MM/2, VOLUME_PLANAR_DIM_IN_MM, 10], - 69 molecular_composition=sp.TISSUE_LIBRARY.blood(), - 70 radius_mm=3, priority=3, consider_partial_volume=True, - 71 adhere_to_deformation=False - 72 ) - 73 return tissue_dict - 74 - 75 - 76 # Seed the numpy random configuration prior to creating the global_settings file in - 77 # order to ensure that the same volume - 78 # is generated with the same random seed every time. - 79 - 80 1 12.7 12.7 0.0 np.random.seed(RANDOM_SEED) - 81 1 0.6 0.6 0.0 VOLUME_NAME = "CompletePipelineTestMSOT_"+str(RANDOM_SEED) - 82 - 83 1 1.5 1.5 0.0 general_settings = { - 84 # These parameters set the general properties of the simulated volume - 85 1 0.4 0.4 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 86 1 0.6 0.6 0.0 Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), - 87 1 54.4 54.4 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 88 1 0.8 0.8 0.0 Tags.SPACING_MM: SPACING, - 89 1 0.2 0.2 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 90 1 0.3 0.3 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 91 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 92 1 0.9 0.9 0.0 Tags.VOLUME_CREATOR: Tags.VOLUME_CREATOR_VERSATILE, - 93 1 0.4 0.4 0.0 Tags.GPU: True, - 94 1 0.4 0.4 0.0 Tags.WAVELENGTHS: [700, 800], - 95 1 0.2 0.2 0.0 Tags.DO_FILE_COMPRESSION: True, - 96 1 0.4 0.4 0.0 Tags.DO_IPASC_EXPORT: True - 97 } - 98 1 19.6 19.6 0.0 settings = sp.Settings(general_settings) - 99 1 2.4 2.4 0.0 np.random.seed(RANDOM_SEED) - 100 - 101 2 8.7 4.4 0.0 settings.set_volume_creation_settings({ - 102 1 24130.3 24130.3 0.3 Tags.STRUCTURES: create_example_tissue(), - 103 1 0.5 0.5 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True - 104 }) - 105 - 106 2 8.7 4.4 0.0 settings.set_optical_settings({ - 107 1 0.3 0.3 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 108 1 46.8 46.8 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 109 1 1.0 1.0 0.0 Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, - 110 1 0.2 0.2 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, - 111 1 0.3 0.3 0.0 Tags.MCX_ASSUMED_ANISOTROPY: 0.9, - 112 }) - 113 - 114 2 14.3 7.1 0.0 settings.set_acoustic_settings({ - 115 1 0.2 0.2 0.0 Tags.ACOUSTIC_SIMULATION_3D: False, - 116 1 35.7 35.7 0.0 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 117 1 0.4 0.4 0.0 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 118 1 0.3 0.3 0.0 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 119 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLInside: False, - 120 1 0.4 0.4 0.0 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 121 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 122 1 0.3 0.3 0.0 Tags.KWAVE_PROPERTY_PlotPML: False, - 123 1 0.3 0.3 0.0 Tags.RECORDMOVIE: False, - 124 1 0.4 0.4 0.0 Tags.MOVIENAME: "visualization_log", - 125 1 0.3 0.3 0.0 Tags.ACOUSTIC_LOG_SCALE: True - 126 }) - 127 - 128 19 129.9 6.8 0.0 settings.set_reconstruction_settings({ - 129 1 0.7 0.7 0.0 Tags.RECONSTRUCTION_PERFORM_BANDPASS_FILTERING: False, - 130 1 37.8 37.8 0.0 Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), - 131 1 0.1 0.1 0.0 Tags.ACOUSTIC_SIMULATION_3D: False, - 132 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, - 133 1 0.7 0.7 0.0 Tags.TUKEY_WINDOW_ALPHA: 0.5, - 134 1 0.4 0.4 0.0 Tags.BANDPASS_CUTOFF_LOWPASS_IN_HZ: int(8e6), - 135 1 0.4 0.4 0.0 Tags.BANDPASS_CUTOFF_HIGHPASS_IN_HZ: int(0.1e4), - 136 1 0.1 0.1 0.0 Tags.RECONSTRUCTION_BMODE_AFTER_RECONSTRUCTION: False, - 137 1 0.5 0.5 0.0 Tags.RECONSTRUCTION_BMODE_METHOD: Tags.RECONSTRUCTION_BMODE_METHOD_HILBERT_TRANSFORM, - 138 1 1.0 1.0 0.0 Tags.RECONSTRUCTION_APODIZATION_METHOD: Tags.RECONSTRUCTION_APODIZATION_BOX, - 139 1 0.5 0.5 0.0 Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, - 140 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", - 141 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PMLInside: False, - 142 1 0.2 0.2 0.0 Tags.KWAVE_PROPERTY_PMLSize: [31, 32], - 143 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, - 144 1 0.1 0.1 0.0 Tags.KWAVE_PROPERTY_PlotPML: False, - 145 1 0.1 0.1 0.0 Tags.RECORDMOVIE: False, - 146 1 0.1 0.1 0.0 Tags.MOVIENAME: "visualization_log", - 147 1 0.1 0.1 0.0 Tags.ACOUSTIC_LOG_SCALE: True, - 148 1 0.3 0.3 0.0 Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, - 149 1 0.2 0.2 0.0 Tags.DATA_FIELD_ALPHA_COEFF: 0.01, - 150 1 0.2 0.2 0.0 Tags.DATA_FIELD_DENSITY: 1000, - 151 1 0.3 0.3 0.0 Tags.SPACING_MM: SPACING - 152 }) - 153 - 154 1 34.3 34.3 0.0 settings["noise_initial_pressure"] = { - 155 1 0.3 0.3 0.0 Tags.NOISE_MEAN: 1, - 156 1 0.2 0.2 0.0 Tags.NOISE_STD: 0.01, - 157 1 0.4 0.4 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 158 1 0.6 0.6 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 159 1 0.2 0.2 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 160 } - 161 - 162 1 33.1 33.1 0.0 settings["noise_time_series"] = { - 163 1 0.1 0.1 0.0 Tags.NOISE_STD: 1, - 164 1 0.5 0.5 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_ADDITIVE, - 165 1 0.2 0.2 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_TIME_SERIES_DATA - 166 } - 167 - 168 # TODO: For the device choice, uncomment the undesired device - 169 - 170 # device = sp.MSOTAcuityEcho(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 171 # VOLUME_PLANAR_DIM_IN_MM/2, - 172 # 0])) - 173 # device.update_settings_for_use_of_model_based_volume_creator(settings) - 174 - 175 3 11.1 3.7 0.0 device = sp.PhotoacousticDevice(device_position_mm=np.array([VOLUME_TRANSDUCER_DIM_IN_MM/2, - 176 1 0.4 0.4 0.0 VOLUME_PLANAR_DIM_IN_MM/2, - 177 1 0.1 0.1 0.0 0]), - 178 1 1.3 1.3 0.0 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20])) - 179 2 26.6 13.3 0.0 device.set_detection_geometry(sp.LinearArrayDetectionGeometry(device_position_mm=device.device_position_mm, - 180 1 0.1 0.1 0.0 pitch_mm=0.25, - 181 1 0.1 0.1 0.0 number_detector_elements=100, - 182 1 0.8 0.8 0.0 field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) - 183 1 1748.6 1748.6 0.0 print(device.get_detection_geometry().get_detector_element_positions_base_mm()) - 184 1 36.8 36.8 0.0 device.add_illumination_geometry(sp.SlitIlluminationGeometry(slit_vector_mm=[100, 0, 0])) - 185 - 186 - 187 1 0.2 0.2 0.0 SIMULATION_PIPELINE = [ - 188 1 93.7 93.7 0.0 sp.ModelBasedVolumeCreationAdapter(settings), - 189 1 9.9 9.9 0.0 sp.MCXAdapter(settings), - 190 1 50.1 50.1 0.0 sp.GaussianNoise(settings, "noise_initial_pressure"), - 191 1 5.3 5.3 0.0 sp.KWaveAdapter(settings), - 192 1 46.5 46.5 0.0 sp.GaussianNoise(settings, "noise_time_series"), - 193 1 5.4 5.4 0.0 sp.TimeReversalAdapter(settings), - 194 1 125.6 125.6 0.0 sp.FieldOfViewCropping(settings) - 195 ] - 196 - 197 1 9548734.8 1e+07 99.7 sp.simulate(SIMULATION_PIPELINE, settings, device) - 198 - 199 if Tags.WAVELENGTH in settings: - 200 WAVELENGTH = settings[Tags.WAVELENGTH] - 201 else: - 202 WAVELENGTH = 700 - 203 - 204 if visualise: - 205 sp.visualise_data(path_to_hdf5_file=settings[Tags.SIMPA_OUTPUT_PATH], - 206 wavelength=WAVELENGTH, - 207 show_time_series_data=True, - 208 show_initial_pressure=True, - 209 show_reconstructed_data=True, - 210 log_scale=False, - 211 show_xz_only=False) - -Total time: 10.4913 s -File: /home/f762e/Workspace/simpa/simpa_examples/perform_iterative_qPAI_reconstruction.py -Function: run_perform_iterative_qPAI_reconstruction at line 25 - -Line # Hits Time Per Hit % Time Line Contents -============================================================== - 25 @profile - 26 def run_perform_iterative_qPAI_reconstruction(SPACING: Union[int, float] = 0.2, path_manager=sp.PathManager(), visualise:bool = True): - 27 """ - 28 - 29 :param SPACING: The simulation spacing between voxels - 30 :param path_manager: the path manager to be used, typically sp.PathManager - 31 :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted - 32 :return: a run through of the example - 33 """ - 34 1 0.3 0.3 0.0 VOLUME_TRANSDUCER_DIM_IN_MM = 30 - 35 1 0.1 0.1 0.0 VOLUME_PLANAR_DIM_IN_MM = 30 - 36 1 0.1 0.1 0.0 VOLUME_HEIGHT_IN_MM = 30 - 37 1 0.1 0.1 0.0 RANDOM_SEED = 471 - 38 1 0.6 0.6 0.0 VOLUME_NAME = "MyqPAIReconstruction_" + str(RANDOM_SEED) - 39 - 40 # If VISUALIZE is set to True, the reconstruction result will be plotted - 41 - 42 1 0.5 0.5 0.0 def create_example_tissue(): - 43 """ - 44 This is a very simple example script of how to create a tissue definition. - 45 It contains a muscular background, an epidermis layer on top of the muscles - 46 and a blood vessel. - 47 """ - 48 background_dictionary = sp.Settings() - 49 background_dictionary[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(0.05, 30, 0.9) - 50 background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND - 51 - 52 epidermis_structure = sp.Settings() - 53 epidermis_structure[Tags.PRIORITY] = 1 - 54 epidermis_structure[Tags.STRUCTURE_START_MM] = [0, 0, 2] - 55 epidermis_structure[Tags.STRUCTURE_END_MM] = [0, 0, 2.5] - 56 epidermis_structure[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(2.2, 100.0, 0.9) - 57 epidermis_structure[Tags.CONSIDER_PARTIAL_VOLUME] = True - 58 epidermis_structure[Tags.ADHERE_TO_DEFORMATION] = True - 59 epidermis_structure[Tags.STRUCTURE_TYPE] = Tags.HORIZONTAL_LAYER_STRUCTURE - 60 - 61 vessel_structure_1 = sp.Settings() - 62 vessel_structure_1[Tags.PRIORITY] = 2 - 63 vessel_structure_1[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, 0, - 64 VOLUME_HEIGHT_IN_MM / 2] - 65 vessel_structure_1[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2.5, - 66 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 2] - 67 vessel_structure_1[Tags.STRUCTURE_RADIUS_MM] = 1.75 - 68 vessel_structure_1[Tags.STRUCTURE_ECCENTRICITY] = 0.85 - 69 vessel_structure_1[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(5.2, 100.0, 0.9) - 70 vessel_structure_1[Tags.CONSIDER_PARTIAL_VOLUME] = True - 71 vessel_structure_1[Tags.ADHERE_TO_DEFORMATION] = True - 72 vessel_structure_1[Tags.STRUCTURE_TYPE] = Tags.ELLIPTICAL_TUBULAR_STRUCTURE - 73 - 74 vessel_structure_2 = sp.Settings() - 75 vessel_structure_2[Tags.PRIORITY] = 3 - 76 vessel_structure_2[Tags.STRUCTURE_START_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, 0, - 77 VOLUME_HEIGHT_IN_MM / 3] - 78 vessel_structure_2[Tags.STRUCTURE_END_MM] = [VOLUME_TRANSDUCER_DIM_IN_MM / 2, - 79 VOLUME_PLANAR_DIM_IN_MM, VOLUME_HEIGHT_IN_MM / 3] - 80 vessel_structure_2[Tags.STRUCTURE_RADIUS_MM] = 0.75 - 81 vessel_structure_2[Tags.MOLECULE_COMPOSITION] = sp.TISSUE_LIBRARY.constant(3.0, 100.0, 0.9) - 82 vessel_structure_2[Tags.CONSIDER_PARTIAL_VOLUME] = True - 83 vessel_structure_2[Tags.STRUCTURE_TYPE] = Tags.CIRCULAR_TUBULAR_STRUCTURE - 84 - 85 tissue_dict = sp.Settings() - 86 tissue_dict[Tags.BACKGROUND] = background_dictionary - 87 tissue_dict["epidermis"] = epidermis_structure - 88 tissue_dict["vessel_1"] = vessel_structure_1 - 89 tissue_dict["vessel_2"] = vessel_structure_2 - 90 return tissue_dict - 91 - 92 - 93 # set settings for volume creation, optical simulation and iterative qPAI method - 94 1 11.9 11.9 0.0 np.random.seed(RANDOM_SEED) - 95 - 96 1 0.9 0.9 0.0 general_settings = { - 97 # These parameters set the general properties of the simulated volume - 98 1 0.4 0.4 0.0 Tags.RANDOM_SEED: RANDOM_SEED, - 99 1 0.2 0.2 0.0 Tags.VOLUME_NAME: VOLUME_NAME, - 100 1 65.3 65.3 0.0 Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - 101 1 0.7 0.7 0.0 Tags.SPACING_MM: SPACING, - 102 1 0.3 0.3 0.0 Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, - 103 1 0.3 0.3 0.0 Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, - 104 1 0.1 0.1 0.0 Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, - 105 1 0.2 0.2 0.0 Tags.WAVELENGTHS: [700] - 106 } - 107 - 108 1 15.7 15.7 0.0 settings = sp.Settings(general_settings) - 109 - 110 2 8.8 4.4 0.0 settings.set_volume_creation_settings({ - 111 # These parameters set the properties for the volume creation - 112 1 0.2 0.2 0.0 Tags.SIMULATE_DEFORMED_LAYERS: True, - 113 1 9337.6 9337.6 0.1 Tags.STRUCTURES: create_example_tissue() - 114 }) - 115 2 10.2 5.1 0.0 settings.set_optical_settings({ - 116 # These parameters set the properties for the optical Monte Carlo simulation - 117 1 0.6 0.6 0.0 Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, - 118 1 45.7 45.7 0.0 Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), - 119 1 1.0 1.0 0.0 Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, - 120 1 0.6 0.6 0.0 Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50 - 121 }) - 122 1 38.7 38.7 0.0 settings["noise_model"] = { - 123 1 0.3 0.3 0.0 Tags.NOISE_MEAN: 1.0, - 124 1 0.3 0.3 0.0 Tags.NOISE_STD: 0.01, - 125 1 0.5 0.5 0.0 Tags.NOISE_MODE: Tags.NOISE_MODE_MULTIPLICATIVE, - 126 1 0.5 0.5 0.0 Tags.DATA_FIELD: Tags.DATA_FIELD_INITIAL_PRESSURE, - 127 1 0.3 0.3 0.0 Tags.NOISE_NON_NEGATIVITY_CONSTRAINT: True - 128 } - 129 1 42.5 42.5 0.0 settings["iterative_qpai_reconstruction"] = { - 130 # These parameters set the properties of the iterative reconstruction - 131 1 0.6 0.6 0.0 Tags.DOWNSCALE_FACTOR: 0.75, - 132 1 0.5 0.5 0.0 Tags.ITERATIVE_RECONSTRUCTION_CONSTANT_REGULARIZATION: False, - 133 # the following tag has no effect, since the regularization is chosen to be SNR dependent, not constant - 134 1 0.3 0.3 0.0 Tags.ITERATIVE_RECONSTRUCTION_REGULARIZATION_SIGMA: 0.01, - 135 1 0.4 0.4 0.0 Tags.ITERATIVE_RECONSTRUCTION_MAX_ITERATION_NUMBER: 20, - 136 # for this example, we are not interested in all absorption updates - 137 1 0.6 0.6 0.0 Tags.ITERATIVE_RECONSTRUCTION_SAVE_INTERMEDIATE_RESULTS: False, - 138 1 3.2 3.2 0.0 Tags.ITERATIVE_RECONSTRUCTION_STOPPING_LEVEL: 1e-3 - 139 } - 140 - 141 # run pipeline including iterative qPAI method - 142 1 0.1 0.1 0.0 pipeline = [ - 143 1 68.3 68.3 0.0 sp.ModelBasedVolumeCreationAdapter(settings), - 144 1 10.1 10.1 0.0 sp.MCXAdapter(settings), - 145 1 42.2 42.2 0.0 sp.GaussianNoise(settings, "noise_model"), - 146 1 18.3 18.3 0.0 sp.IterativeqPAI(settings, "iterative_qpai_reconstruction") - 147 ] - 148 - 149 - 150 1 24.4 24.4 0.0 class CustomDevice(sp.PhotoacousticDevice): - 151 - 152 def __init__(self): - 153 super(CustomDevice, self).__init__(device_position_mm=np.asarray([general_settings[Tags.DIM_VOLUME_X_MM] / 2, - 154 general_settings[Tags.DIM_VOLUME_Y_MM] / 2, - 155 0])) - 156 self.add_illumination_geometry(sp.DiskIlluminationGeometry(beam_radius_mm=20)) - 157 - 158 - 159 1 53.2 53.2 0.0 device = CustomDevice() - 160 - 161 1 0.8 0.8 0.0 device.update_settings_for_use_of_model_based_volume_creator(settings) - 162 - 163 1 10481453.4 1e+07 99.9 sp.simulate(pipeline, settings, device) - 164 - 165 # visualize reconstruction results - 166 1 0.6 0.6 0.0 if visualise: - 167 # get simulation output - 168 data_path = path_manager.get_hdf5_file_save_path() + "/" + VOLUME_NAME + ".hdf5" - 169 settings = sp.load_data_field(data_path, Tags.SETTINGS) - 170 wavelength = settings[Tags.WAVELENGTHS][0] - 171 - 172 # get reconstruction result - 173 absorption_reconstruction = sp.load_data_field(data_path, Tags.ITERATIVE_qPAI_RESULT, wavelength) - 174 - 175 # get ground truth absorption coefficients - 176 absorption_gt = sp.load_data_field(data_path, Tags.DATA_FIELD_ABSORPTION_PER_CM, wavelength) - 177 - 178 # rescale ground truth to same dimension as reconstruction (necessary due to resampling in iterative algorithm) - 179 scale = np.shape(absorption_reconstruction)[0] / np.shape(absorption_gt)[0] # same as Tags.DOWNSCALE_FACTOR - 180 absorption_gt = zoom(absorption_gt, scale, order=1, mode="nearest") - 181 - 182 # compute reconstruction error - 183 difference = absorption_gt - absorption_reconstruction - 184 - 185 median_error = np.median(difference) - 186 q3, q1 = np.percentile(difference, [75, 25]) - 187 iqr = q3 - q1 - 188 - 189 # visualize results - 190 x_pos = int(np.shape(absorption_gt)[0] / 2) - 191 y_pos = int(np.shape(absorption_gt)[1] / 2) - 192 - 193 if np.min(absorption_gt) > np.min(absorption_reconstruction): - 194 cmin = np.min(absorption_reconstruction) - 195 else: - 196 cmin = np.min(absorption_gt) - 197 - 198 if np.max(absorption_gt) > np.max(absorption_reconstruction): - 199 cmax = np.max(absorption_gt) - 200 else: - 201 cmax = np.max(absorption_reconstruction) - 202 - 203 results_x_z = [absorption_gt[:, y_pos, :], absorption_reconstruction[:, y_pos, :], difference[:, y_pos, :]] - 204 results_y_z = [absorption_gt[x_pos, :, :], absorption_reconstruction[x_pos, :, :], difference[x_pos, :, :]] - 205 - 206 label = ["Absorption coefficients: ${\mu_a}^{gt}$", "Reconstruction: ${\mu_a}^{reconstr.}$", - 207 "Difference: ${\mu_a}^{gt} - {\mu_a}^{reconstr.}$"] - 208 - 209 plt.figure(figsize=(20, 15)) - 210 plt.subplots_adjust(hspace=0.5) - 211 plt.suptitle("Iterative qPAI Reconstruction \n median error = " + str(np.round(median_error, 4)) + - 212 "\n IQR = " + str(np.round(iqr, 4)), fontsize=10) - 213 - 214 for i, quantity in enumerate(results_x_z): - 215 plt.subplot(2, len(results_x_z), i + 1) - 216 if i == 0: - 217 plt.ylabel("x-z", fontsize=10) - 218 plt.title(label[i], fontsize=10) - 219 plt.imshow(quantity.T) - 220 plt.xticks(fontsize=6) - 221 plt.yticks(fontsize=6) - 222 plt.colorbar() - 223 if i != 2: - 224 plt.clim(cmin, cmax) - 225 else: - 226 plt.clim(np.min(difference), np.max(difference)) - 227 - 228 for i, quantity in enumerate(results_y_z): - 229 plt.subplot(2, len(results_x_z), i + len(results_x_z) + 1) - 230 if i == 0: - 231 plt.ylabel("y-z", fontsize=10) - 232 plt.title(label[i], fontsize=10) - 233 plt.imshow(quantity.T) - 234 plt.xticks(fontsize=6) - 235 plt.yticks(fontsize=6) - 236 plt.colorbar() - 237 if i != 2: - 238 plt.clim(cmin, cmax) - 239 else: - 240 plt.clim(np.min(difference), np.max(difference)) - 241 - 242 plt.show() - 243 plt.close() - diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index aa6932d6..e40c7609 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -5,7 +5,7 @@ import os import numpy as np from typing import Union -import typer +from argparse import ArgumentParser import simpa as sp from simpa import Tags @@ -15,20 +15,16 @@ # FIXME temporary workaround for newest Intel architectures os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" -app = typer.Typer() - -@app.command() @profile -def run_linear_unmixing(SPACING: float = 0.25, path_manager=None, visualise: bool = True): +def run_linear_unmixing(spacing: Union[float, int] = 0.25, path_manager=None, visualise: bool = True): """ - :param SPACING: The simulation spacing between voxels + :param spacing: The simulation spacing between voxels :param path_manager: the path manager to be used, typically sp.PathManager :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example """ - print(SPACING) if path_manager is None: path_manager = sp.PathManager() # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you @@ -115,7 +111,7 @@ def create_example_tissue(): Tags.RANDOM_SEED: RANDOM_SEED, Tags.VOLUME_NAME: VOLUME_NAME, Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, + Tags.SPACING_MM: spacing, Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, @@ -185,4 +181,13 @@ def create_example_tissue(): if __name__ == "__main__": - app() + parser = ArgumentParser(description='Run the linear unmixing example') + parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') + parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') + config = parser.parse_args() + + spacing = config.spacing + path_manager = config.path_manager + visualise = config.visualise + run_linear_unmixing(spacing=spacing, path_manager=path_manager, visualise=visualise) diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index 38b6bb22..a2fcb23c 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -7,7 +7,7 @@ import numpy as np from simpa.utils.profiling import profile from typing import Union -import typer +from argparse import ArgumentParser # FIXME temporary workaround for newest Intel architectures import os @@ -16,15 +16,12 @@ # TODO: Please make sure that you have set the correct path to MCX binary and SAVE_PATH in the file path_config.env # located in the simpa_examples directory -app = typer.Typer() - -@app.command() @profile -def run_minimal_optical_simulation(SPACING: float = 0.5, path_manager=None, visualise: bool = True): +def run_minimal_optical_simulation(spacing: Union[float, int] = 0.5, path_manager=None, visualise: bool = True): """ - :param SPACING: The simulation spacing between voxels + :param spacing: The simulation spacing between voxels :param path_manager: the path manager to be used, typically sp.PathManager :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example @@ -100,7 +97,7 @@ def create_example_tissue(): Tags.RANDOM_SEED: RANDOM_SEED, Tags.VOLUME_NAME: VOLUME_NAME, Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, + Tags.SPACING_MM: spacing, Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, @@ -173,4 +170,13 @@ def __init__(self): if __name__ == "__main__": - app() + parser = ArgumentParser(description='Run the minimal optical simulation example') + parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') + parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') + config = parser.parse_args() + + spacing = config.spacing + path_manager = config.path_manager + visualise = config.visualise + run_minimal_optical_simulation(spacing=spacing, path_manager=path_manager, visualise=visualise) diff --git a/simpa_examples/minimal_optical_simulation_uniform_cube.py b/simpa_examples/minimal_optical_simulation_uniform_cube.py index 5c01775e..b6bd26ff 100644 --- a/simpa_examples/minimal_optical_simulation_uniform_cube.py +++ b/simpa_examples/minimal_optical_simulation_uniform_cube.py @@ -14,22 +14,19 @@ # FIXME temporary workaround for newest Intel architectures import os from simpa.utils.profiling import profile -import typer +from argparse import ArgumentParser os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" # TODO: Please make sure that you have set the correct path to MCX binary as described in the README.md file. -app = typer.Typer() - -@app.command() @profile -def run_minimal_optical_simulation_uniform_cube(SPACING: float = 0.5, path_manager=None, +def run_minimal_optical_simulation_uniform_cube(spacing: Union[float, int] = 0.5, path_manager=None, visualise: bool = True): """ - :param SPACING: The simulation spacing between voxels + :param spacing: The simulation spacing between voxels :param path_manager: the path manager to be used, typically sp.PathManager :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example @@ -71,7 +68,7 @@ def create_example_tissue(): Tags.RANDOM_SEED: RANDOM_SEED, Tags.VOLUME_NAME: VOLUME_NAME, Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, + Tags.SPACING_MM: spacing, Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, @@ -111,4 +108,13 @@ def create_example_tissue(): if __name__ == "__main__": - app() + parser = ArgumentParser(description='Run the minimal optical simulation uniform cube example') + parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') + parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') + config = parser.parse_args() + + spacing = config.spacing + path_manager = config.path_manager + visualise = config.visualise + run_minimal_optical_simulation_uniform_cube(spacing=spacing, path_manager=path_manager, visualise=visualise) diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 4a04231f..9ccb2cd9 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -7,19 +7,16 @@ import numpy as np from simpa.utils.profiling import profile from typing import Union -import typer +from argparse import ArgumentParser path_manager = sp.PathManager() -app = typer.Typer() - -@app.command() @profile -def run_msot_invision_simulation(SPACING: float = 0.5, path_manager=None, visualise: bool = True): +def run_msot_invision_simulation(spacing: Union[float, int] = 0.5, path_manager=None, visualise: bool = True): """ - :param SPACING: The simulation spacing between voxels + :param spacing: The simulation spacing between voxels :param path_manager: the path manager to be used, typically sp.PathManager :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example @@ -137,7 +134,7 @@ def get_settings(): Tags.RANDOM_SEED: 4711, Tags.VOLUME_NAME: "InVision Simulation Example", Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, + Tags.SPACING_MM: spacing, Tags.DIM_VOLUME_Z_MM: XZ_DIM, Tags.DIM_VOLUME_X_MM: XZ_DIM, Tags.DIM_VOLUME_Y_MM: Y_DIM, @@ -213,4 +210,13 @@ def get_settings(): if __name__ == "__main__": - app() + parser = ArgumentParser(description='Run the msot invision simulation example') + parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') + parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') + config = parser.parse_args() + + spacing = config.spacing + path_manager = config.path_manager + visualise = config.visualise + run_msot_invision_simulation(spacing=spacing, path_manager=path_manager, visualise=visualise) \ No newline at end of file diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 2c0b9d27..50bd71df 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -7,7 +7,7 @@ import numpy as np from simpa.utils.profiling import profile from typing import Union -import typer +from argparse import ArgumentParser # FIXME temporary workaround for newest Intel architectures import os @@ -16,16 +16,13 @@ # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # point to the correct file in the PathManager(). -app = typer.Typer() - -@app.command() @profile -def run_optical_and_acoustic_simulation(SPACING: float = 0.2, path_manager=None, +def run_optical_and_acoustic_simulation(spacing: Union[float, int] = 0.2, path_manager=None, visualise: bool = True): """ - :param SPACING: The simulation spacing between voxels + :param spacing: The simulation spacing between voxels :param path_manager: the path manager to be used, typically sp.PathManager :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example @@ -91,7 +88,7 @@ def create_example_tissue(): Tags.RANDOM_SEED: RANDOM_SEED, Tags.VOLUME_NAME: "CompletePipelineExample_" + str(RANDOM_SEED), Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, + Tags.SPACING_MM: spacing, Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, @@ -154,7 +151,7 @@ def create_example_tissue(): Tags.DATA_FIELD_SPEED_OF_SOUND: 1540, Tags.DATA_FIELD_ALPHA_COEFF: 0.01, Tags.DATA_FIELD_DENSITY: 1000, - Tags.SPACING_MM: SPACING + Tags.SPACING_MM: spacing }) settings["noise_initial_pressure"] = { @@ -217,4 +214,13 @@ def create_example_tissue(): if __name__ == "__main__": - app() + parser = ArgumentParser(description='Run the optical and acoustic simulation example') + parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') + parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') + config = parser.parse_args() + + spacing = config.spacing + path_manager = config.path_manager + visualise = config.visualise + run_optical_and_acoustic_simulation(spacing=spacing, path_manager=path_manager, visualise=visualise) diff --git a/simpa_examples/perform_iterative_qPAI_reconstruction.py b/simpa_examples/perform_iterative_qPAI_reconstruction.py index ffe176ca..29a8ace4 100644 --- a/simpa_examples/perform_iterative_qPAI_reconstruction.py +++ b/simpa_examples/perform_iterative_qPAI_reconstruction.py @@ -13,7 +13,7 @@ from simpa import Tags from typing import Union from simpa.utils.profiling import profile -import typer +from argparse import ArgumentParser # FIXME temporary workaround for newest Intel architectures os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" @@ -22,16 +22,13 @@ # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # point to the correct file in the PathManager(). -app = typer.Typer() - -@app.command() @profile -def run_perform_iterative_qPAI_reconstruction(SPACING: float = 0.2, path_manager=None, +def run_perform_iterative_qPAI_reconstruction(spacing: Union[float, int] = 0.2, path_manager=None, visualise: bool = True): """ - :param SPACING: The simulation spacing between voxels + :param spacing: The simulation spacing between voxels :param path_manager: the path manager to be used, typically sp.PathManager :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example @@ -104,7 +101,7 @@ def create_example_tissue(): Tags.RANDOM_SEED: RANDOM_SEED, Tags.VOLUME_NAME: VOLUME_NAME, Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), - Tags.SPACING_MM: SPACING, + Tags.SPACING_MM: spacing, Tags.DIM_VOLUME_Z_MM: VOLUME_HEIGHT_IN_MM, Tags.DIM_VOLUME_X_MM: VOLUME_TRANSDUCER_DIM_IN_MM, Tags.DIM_VOLUME_Y_MM: VOLUME_PLANAR_DIM_IN_MM, @@ -248,4 +245,13 @@ def __init__(self): if __name__ == "__main__": - app() + parser = ArgumentParser(description='Run the iterative qPAI reconstruction example') + parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') + parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') + config = parser.parse_args() + + spacing = config.spacing + path_manager = config.path_manager + visualise = config.visualise + run_perform_iterative_qPAI_reconstruction(spacing=spacing, path_manager=path_manager, visualise=visualise) diff --git a/simpa_examples/segmentation_loader.py b/simpa_examples/segmentation_loader.py index 8556a2a8..bef3091c 100644 --- a/simpa_examples/segmentation_loader.py +++ b/simpa_examples/segmentation_loader.py @@ -5,28 +5,26 @@ from simpa import Tags import simpa as sp import numpy as np +from typing import Union from skimage.data import shepp_logan_phantom from scipy.ndimage import zoom # FIXME temporary workaround for newest Intel architectures import os -import typer +from argparse import ArgumentParser from simpa.utils.profiling import profile os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" -# If VISUALIZE is set to True, the simulation result will be plotted -app = typer.Typer() # TODO: Please make sure that a valid path_config.env file is located in your home directory, or that you # point to the correct file in the PathManager(). -@app.command() @profile -def run_segmentation_loader(SPACING: float = 0.1, path_manager=None, +def run_segmentation_loader(spacing: Union[float, int] = 0.1, path_manager=None, visualise: bool = True): """ - :param SPACING: The simulation spacing between voxels + :param spacing: The simulation spacing between voxels :param path_manager: the path manager to be used, typically sp.PathManager :param visualise: If VISUALIZE is set to True, the reconstruction result will be plotted :return: a run through of the example @@ -42,7 +40,7 @@ def run_segmentation_loader(SPACING: float = 0.1, path_manager=None, input_spacing = 0.2 segmentation_volume_tiled = np.tile(label_mask, (1, 128, 1)) - segmentation_volume_mask = np.round(zoom(segmentation_volume_tiled, input_spacing/SPACING, + segmentation_volume_mask = np.round(zoom(segmentation_volume_tiled, input_spacing/spacing, order=0)).astype(int) def segmentation_class_mapping(): @@ -69,10 +67,10 @@ def segmentation_class_mapping(): settings[Tags.VOLUME_NAME] = "SegmentationTest" settings[Tags.RANDOM_SEED] = 1234 settings[Tags.WAVELENGTHS] = [700] - settings[Tags.SPACING_MM] = SPACING - settings[Tags.DIM_VOLUME_X_MM] = 400 / (SPACING / input_spacing) - settings[Tags.DIM_VOLUME_Y_MM] = 128 / (SPACING / input_spacing) - settings[Tags.DIM_VOLUME_Z_MM] = 400 / (SPACING / input_spacing) + settings[Tags.SPACING_MM] = spacing + settings[Tags.DIM_VOLUME_X_MM] = 400 / (spacing / input_spacing) + settings[Tags.DIM_VOLUME_Y_MM] = 128 / (spacing / input_spacing) + settings[Tags.DIM_VOLUME_Z_MM] = 400 / (spacing / input_spacing) settings.set_volume_creation_settings({ Tags.INPUT_SEGMENTATION_VOLUME: segmentation_volume_mask, @@ -107,4 +105,13 @@ def segmentation_class_mapping(): if __name__ == "__main__": - app() + parser = ArgumentParser(description='Run the segmentation loader example') + parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') + parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') + config = parser.parse_args() + + spacing = config.spacing + path_manager = config.path_manager + visualise = config.visualise + run_segmentation_loader(spacing=spacing, path_manager=path_manager, visualise=visualise) From 53e9e900d854f14c662ef4e96c762aa4c4ea834a Mon Sep 17 00:00:00 2001 From: frisograce Date: Wed, 26 Jun 2024 09:20:09 +0200 Subject: [PATCH 12/34] Adding documentation to the readme and contributing --- CONTRIBUTING.md | 7 ++++--- README.md | 16 +++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a8fb28f6..a7e5bc8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,10 +36,11 @@ In general the following steps are involved during a contribution: A new implemented feature / a bug fix should be accompanied by a test. Additionally, all previously existing tests must still pass after the contribution. 6. Run pre-commit hooks and make sure all hooks are passing. -7. Once development is finished, create a pull request including your changes. +7. If changing core simpa modules/refactoring/optimisation, run benchmarking bash script on develop before and after the changes (see readme). +8. Once development is finished, create a pull request including your changes. For more information on how to create pull request, see GitHub's [about pull requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). -8. If there are conflicts between the simpa develop branch and your branch, you should update your feature branch with the simpa develop branch using a "merge" strategy instead of "rebase". -9. A member of the core development team will review your pull request and potentially require further changes +9. If there are conflicts between the simpa develop branch and your branch, you should update your feature branch with the simpa develop branch using a "merge" strategy instead of "rebase". +10. member of the core development team will review your pull request and potentially require further changes (see [Contribution review and integration](#contribution-review-and-integration)). Once all remarks have been resolved, your changes will be merged into the develop branch. diff --git a/README.md b/README.md index 33fa76e3..224ddda3 100755 --- a/README.md +++ b/README.md @@ -183,13 +183,15 @@ Please see the github guidelines for creating pull requests: [https://docs.githu # Performance profiling -Do you wish to know which parts of the simulation pipeline cost the most amount of time? -If that is the case then you can use the following commands to profile the execution of your simulation script. -You simply need to replace the `myscript` name with your script name. - -`python -m cProfile -o myscript.cprof myscript.py` - -`pyprof2calltree -k -i myscript.cprof` +When changing the SIMPA core, e.g., by refactoring/optimizing, or if you are curious about how fast your machine runs +SIMPA, you can run the SIMPA benchmarking scripts. It is recommended to run it once for checking if it works and then +change line (TODO) to run it at least 100 times for actual benchmarking. This has to be done with a clean +setup, no browser or other applications running (weekend/at night/...). To see if your changes are actually better than +the current implementation, you simply have to run the script on the current develop and additionally with your changes +and compare the resulting benchmarking/benchmarking_data_table.txt files (they will look like +benchmarking/benchmarking_data/benchmarking_data_table.txt). Make sure to back up the table after your first run, since +it otherwise will be overwritten. We recommend using the default settings (besides -file gives the location where the +table is saved). # Troubleshooting From 269796c65e7aabfc45660b26a266ce9c6e3e64d6 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Wed, 26 Jun 2024 14:06:58 +0200 Subject: [PATCH 13/34] Add possibility to add additional flags to executable, fixes #9 Co-authored-by: Alexander Seitel --- simpa/core/simulation_modules/__init__.py | 15 +++++- .../acoustic_forward_module_k_wave_adapter.py | 3 +- .../optical_forward_model_mcx_adapter.py | 2 + ...l_forward_model_mcx_reflectance_adapter.py | 2 + ...nstruction_module_time_reversal_adapter.py | 4 +- simpa/utils/matlab.py | 17 +++++- simpa/utils/tags.py | 6 +++ .../optical_and_acoustic_simulation.py | 1 + .../automatic_tests/test_additional_flags.py | 53 +++++++++++++++++++ 9 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 simpa_tests/automatic_tests/test_additional_flags.py diff --git a/simpa/core/simulation_modules/__init__.py b/simpa/core/simulation_modules/__init__.py index 2b9ec838..49b8af28 100644 --- a/simpa/core/simulation_modules/__init__.py +++ b/simpa/core/simulation_modules/__init__.py @@ -5,7 +5,8 @@ from abc import abstractmethod from simpa.core import PipelineModule -from simpa.utils import Settings +from simpa.utils import Settings, Tags +from typing import List class SimulationModule(PipelineModule): """ @@ -28,6 +29,18 @@ def load_component_settings(self) -> Settings: :return: Loads component settings corresponding to this simulation component """ pass + + def get_additional_flags(self) -> List[str]: + """Reads the list of additional flags from the corresponding component settings Tags.ADDITIONAL_FLAGS + + :return: List[str]: list of additional flags + """ + cmd = [] + if Tags.ADDITIONAL_FLAGS in self.component_settings: + for flag in self.component_settings[Tags.ADDITIONAL_FLAGS]: + if flag not in cmd: + cmd.append(str(flag)) + return cmd def before_running(self): """ diff --git a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_module_k_wave_adapter.py b/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_module_k_wave_adapter.py index 7bb4e2b1..17114651 100644 --- a/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_module_k_wave_adapter.py +++ b/simpa/core/simulation_modules/acoustic_forward_module/acoustic_forward_module_k_wave_adapter.py @@ -5,6 +5,7 @@ import gc import os import subprocess +from typing import List import numpy as np import scipy.io as sio @@ -238,7 +239,7 @@ def k_wave_acoustic_forward_model(self, detection_geometry: DetectionGeometryBas simulation_script_path = "simulate_2D" matlab_binary_path = self.component_settings[Tags.ACOUSTIC_MODEL_BINARY_PATH] - cmd = generate_matlab_cmd(matlab_binary_path, simulation_script_path, optical_path) + cmd = generate_matlab_cmd(matlab_binary_path, simulation_script_path, optical_path, self.get_additional_flags()) cur_dir = os.getcwd() self.logger.info(cmd) diff --git a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_adapter.py b/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_adapter.py index ed4617a4..881bc2b3 100644 --- a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_adapter.py +++ b/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_adapter.py @@ -73,6 +73,7 @@ def forward_model(self, self.generate_mcx_json_input(settings_dict=settings_dict) # run the simulation cmd = self.get_command() + self.logger.info(cmd) self.run_mcx(cmd) # Read output @@ -184,6 +185,7 @@ def get_command(self) -> List: cmd.append("1") cmd.append("-F") cmd.append("jnii") + cmd += self.get_additional_flags() return cmd @staticmethod diff --git a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_reflectance_adapter.py b/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_reflectance_adapter.py index fa9a64a7..73c92b7a 100644 --- a/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_reflectance_adapter.py +++ b/simpa/core/simulation_modules/optical_simulation_module/optical_forward_model_mcx_reflectance_adapter.py @@ -79,6 +79,7 @@ def forward_model(self, self.generate_mcx_json_input(settings_dict=settings_dict) # run the simulation cmd = self.get_command() + self.logger.info(cmd) self.run_mcx(cmd) # Read output @@ -117,6 +118,7 @@ def get_command(self) -> List: if Tags.COMPUTE_DIFFUSE_REFLECTANCE in self.component_settings and \ self.component_settings[Tags.COMPUTE_DIFFUSE_REFLECTANCE]: cmd.append("--saveref") # save diffuse reflectance at 0 filled voxels outside of domain + cmd += self.get_additional_flags() return cmd def read_mcx_output(self, **kwargs) -> Dict: diff --git a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_time_reversal_adapter.py b/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_time_reversal_adapter.py index 6b4bd43f..a67116e6 100644 --- a/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_time_reversal_adapter.py +++ b/simpa/core/simulation_modules/reconstruction_module/reconstruction_module_time_reversal_adapter.py @@ -169,8 +169,8 @@ def reconstruction_algorithm(self, time_series_sensor_data, detection_geometry): axes = (0, 1) matlab_binary_path = self.component_settings[Tags.ACOUSTIC_MODEL_BINARY_PATH] - cmd = generate_matlab_cmd(matlab_binary_path, time_reversal_script, acoustic_path) - + cmd = generate_matlab_cmd(matlab_binary_path, time_reversal_script, acoustic_path, self.get_additional_flags()) + cur_dir = os.getcwd() os.chdir(self.global_settings[Tags.SIMULATION_PATH]) self.logger.info(cmd) diff --git a/simpa/utils/matlab.py b/simpa/utils/matlab.py index e2ce4838..62dc1f22 100644 --- a/simpa/utils/matlab.py +++ b/simpa/utils/matlab.py @@ -7,7 +7,21 @@ from typing import List -def generate_matlab_cmd(matlab_binary_path: str, simulation_script_path: str, data_path: str) -> List[str]: +def generate_matlab_cmd(matlab_binary_path: str, simulation_script_path: str, data_path: str, additional_flags: List[str] = []) -> List[str]: + """Generates the MATLAB execution command from the given paths + + :param matlab_binary_path: path to the MATLAB binary file as defined by PathManager + :type matlab_binary_path: str + :param simulation_script_path: path to the MATLAB script that should be run (either simulate_2D.m or simulate_3D.m) + :type simulation_script_path: str + :param data_path: path to the .mat file used for simulating + :type data_path: str + :param additional_flags: list of optional additional flags for MATLAB + :type additional_flags: List[str] + :return: list of command parts + :rtype: List[str] + """ + # get path of calling script to add to matlab path base_script_path = os.path.dirname(os.path.abspath(inspect.stack()[1].filename)) # ensure data path is an absolute path @@ -19,6 +33,7 @@ def generate_matlab_cmd(matlab_binary_path: str, simulation_script_path: str, da cmd.append("-nosplash") cmd.append("-automation") cmd.append("-wait") + cmd += additional_flags cmd.append("-r") cmd.append(f"addpath('{base_script_path}');{simulation_script_path}('{data_path}');exit;") return cmd diff --git a/simpa/utils/tags.py b/simpa/utils/tags.py index 3dd7c259..49350846 100644 --- a/simpa/utils/tags.py +++ b/simpa/utils/tags.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT from numbers import Number +from typing import Iterable import numpy as np @@ -1490,3 +1491,8 @@ class Tags: """ Identifier for the environment varibale that defines the path the the matlab executable. """ + + ADDITIONAL_FLAGS = ("additional_flags", Iterable) + """ + Defines a sequence of extra flags to be parsed to executables for simulation modules + """ diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 68bb856c..c51a70e2 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -100,6 +100,7 @@ def create_example_tissue(): Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_MSOT_ACUITY_ECHO, Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, Tags.MCX_ASSUMED_ANISOTROPY: 0.9, + Tags.ADDITIONAL_FLAGS: ['--printgpu'] # to print MCX GPU information }) settings.set_acoustic_settings({ diff --git a/simpa_tests/automatic_tests/test_additional_flags.py b/simpa_tests/automatic_tests/test_additional_flags.py new file mode 100644 index 00000000..ca1c4731 --- /dev/null +++ b/simpa_tests/automatic_tests/test_additional_flags.py @@ -0,0 +1,53 @@ +import unittest +import numpy as np + +from simpa import MCXAdapterReflectance, MCXAdapter, KWaveAdapter, TimeReversalAdapter, Tags, Settings +from simpa.utils.matlab import generate_matlab_cmd + +class TestAdditionalFlags(unittest.TestCase): + def setUp(self) -> None: + self.additional_flags = ('-l', '-a') + self.settings = Settings() + + def test_get_cmd_mcx_reflectance_adapter(self): + self.settings.set_optical_settings({ + Tags.OPTICAL_MODEL_BINARY_PATH: '.', + Tags.ADDITIONAL_FLAGS: self.additional_flags + }) + mcx_reflectance_adapter = MCXAdapterReflectance(global_settings=self.settings) + cmd = mcx_reflectance_adapter.get_command() + for flag in self.additional_flags: + self.assertIn(flag, cmd, f"{flag} was not in command returned by mcx reflectance adapter but was defined as additional flag") + + def test_get_cmd_mcx_adapter(self): + self.settings.set_optical_settings({ + Tags.OPTICAL_MODEL_BINARY_PATH: '.', + Tags.ADDITIONAL_FLAGS: self.additional_flags + }) + mcx_adapter = MCXAdapter(global_settings=self.settings) + cmd = mcx_adapter.get_command() + for flag in self.additional_flags: + self.assertIn(flag, cmd, f"{flag} was not in command returned by mcx adapter but was defined as additional flag") + + def test_get_cmd_kwave_adapter(self): + self.settings.set_acoustic_settings({ + Tags.ADDITIONAL_FLAGS: self.additional_flags + }) + kwave_adapter = KWaveAdapter(global_settings=self.settings) + cmd = generate_matlab_cmd("./matlab.exe", "simulate_2D.m", "my_hdf5.mat", kwave_adapter.get_additional_flags()) + for flag in self.additional_flags: + self.assertIn(flag, cmd, f"{flag} was not in command returned by kwave adapter but was defined as additional flag") + + def test_get_cmd_time_reversal_adapter(self): + self.settings.set_reconstruction_settings({ + Tags.ADDITIONAL_FLAGS: self.additional_flags + }) + time_reversal_adapter = TimeReversalAdapter(global_settings=self.settings) + cmd = generate_matlab_cmd("./matlab.exe", "time_reversal_2D.m", "my_hdf5.mat", time_reversal_adapter.get_additional_flags()) + for flag in self.additional_flags: + self.assertIn(flag, cmd, f"{flag} was not in command returned by time reversal adapter but was defined as additional flag") + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 77660a905908b4ca8331519fe51082b8cdc2f124 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Wed, 26 Jun 2024 16:02:55 +0200 Subject: [PATCH 14/34] update --- .../benchmarking_data_frame_mean.csv | 19 +++ .../benchmarking/create_benchmarking_table.py | 112 ------------- .../benchmarking/extract_benchmarking_data.py | 152 ++++++++++++++++++ .../benchmarking/get_final_table.py | 64 ++++++++ .../benchmarking/performance_check.py | 34 ++-- .../benchmarking/run_benchmarking.sh | 35 ++-- 6 files changed, 278 insertions(+), 138 deletions(-) create mode 100644 simpa_examples/benchmarking/benchmarking_data_frame_mean.csv delete mode 100644 simpa_examples/benchmarking/create_benchmarking_table.py create mode 100644 simpa_examples/benchmarking/extract_benchmarking_data.py create mode 100644 simpa_examples/benchmarking/get_final_table.py diff --git a/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv b/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv new file mode 100644 index 00000000..107bc618 --- /dev/null +++ b/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv @@ -0,0 +1,19 @@ +Example,Spacing,GPU_MEMORY_mean,GPU_MEMORY_std,MEMORY_mean,MEMORY_std,TIME_mean,TIME_std +linear_unmixing,0.2,1450.0,0.0,507.3,5.09,11107.3,127.14 +linear_unmixing,0.3,458.0,0.0,684.55,0.78,4853.55,419.1 +linear_unmixing,0.4000000000000001,206.0,0.0,558.2,17.11,3326.95,23.83 +minimal_optical_simulation,0.2,1600.0,,-8.6,,10426.8, +minimal_optical_simulation,0.3,514.0,,1.2,,4273.6, +minimal_optical_simulation,0.4000000000000001,226.0,,-10.3,,2676.4, +minimal_optical_simulation_uniform_cube,0.2,1190.0,,50.4,,6281.9, +minimal_optical_simulation_uniform_cube,0.3,368.0,,-0.2,,3258.7, +minimal_optical_simulation_uniform_cube,0.4000000000000001,176.0,,0.2,,1714.0, +msot_invision_simulation,0.2,4250.0,,31.1,,28513.7, +msot_invision_simulation,0.3,1330.0,,70.1,,19706.4, +msot_invision_simulation,0.4000000000000001,556.0,,65.8,,17596.6, +optical_and_acoustic_simulation,0.2,510.0,,11.2,,33123.5, +optical_and_acoustic_simulation,0.3,180.0,,,,8789.8, +optical_and_acoustic_simulation,0.4000000000000001,80.0,,,,8930.5, +perform_iterative_qPAI_reconstruction,0.2,376.0,,73.1,,7583.6, +perform_iterative_qPAI_reconstruction,0.3,136.0,,-0.4,,9815.4, +perform_iterative_qPAI_reconstruction,0.4000000000000001,66.0,,73.1,,8253.1, diff --git a/simpa_examples/benchmarking/create_benchmarking_table.py b/simpa_examples/benchmarking/create_benchmarking_table.py deleted file mode 100644 index f228a545..00000000 --- a/simpa_examples/benchmarking/create_benchmarking_table.py +++ /dev/null @@ -1,112 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ -# SPDX-FileCopyrightText: 2021 Janek Groehl -# SPDX-License-Identifier: MIT - -import os -import numpy as np -from prettytable import PrettyTable - - -def lines_that_contain(string, fp): - """ - Function to determine if a string contains a certain word/phrase - :param string: what to check - :param fp: - :return: - """ - return [line for line in fp if string in line] - - -examples = ['linear_unmixing', 'minimal_optical_simulation', 'minimal_optical_simulation_uniform_cube', - 'msot_invision_simulation', 'optical_and_acoustic_simulation', - 'perform_iterative_qPAI_reconstruction', 'segmentation_loader'] -profiles = ['TIME', "GPU_MEMORY", "MEMORY"] -spacings = np.arange(0.2, 0.6, 0.2) -benchmarking_dict = {} -for example in examples: - benchmarking_dict[example] = {} - for spacing in spacings: - benchmarking_dict[example][spacing] = {} - -info_starts = {"MEMORY": 19, "GPU_MEMORY": 12, "TIME": 16} -info_ends = {"MEMORY": 29, "GPU_MEMORY": 26, "TIME": 29} - -for profile in profiles: - for spacing in spacings: - file_name = "./benchmarking_bash/benchmarking_data_"+profile+"_"+str(spacing)+".txt" - benchmarking_file = open(file_name, 'r') - current_examples = [] - - if profile == 'TIME': - example_name_lines = lines_that_contain("File:", benchmarking_file) - - for enl in example_name_lines: - example = enl.rpartition("simpa_examples/")[2].rpartition(".py")[0] - if example not in current_examples: - current_examples.append(example) - else: - break - - elif profile == 'GPU_MEMORY': - example_name_lines = lines_that_contain("##", benchmarking_file) - - for enl in example_name_lines: - example = enl.rpartition("run_")[2].rpartition("\n")[0] - if example not in current_examples: - current_examples.append(example) - else: - break - - if profile == 'MEMORY': - example_name_lines = lines_that_contain("Filename:", benchmarking_file) - - for enl in example_name_lines: - example = enl.rpartition("simpa_examples/")[2].rpartition(".py")[0] - if example not in current_examples: - current_examples.append(example) - else: - break - - benchmarking_file = open(file_name, 'r') - lines_with_sp_simulate = lines_that_contain("sp.simulate", benchmarking_file) - - values = [] - units = [] - examples_counter = 0 - - for lwss in lines_with_sp_simulate: - value = float(lwss[info_starts[profile]:info_ends[profile]]) - - unit = str(lwss[info_ends[profile]]) - if profile == "TIME": - unit = "" - - example = current_examples[examples_counter] - benchmarking_dict[example][spacing][profile] = str(value)+unit - examples_counter += 1 - if examples_counter == len(current_examples): - examples_counter = 0 - - -table = PrettyTable() -table.field_names = ["Example", "Spacing mm", 'Time μs', "GPU", "MEMORY"] -divider = False -for example, spacing_dict in benchmarking_dict.items(): - subrow_counter = 0 - for spacing, p_dict in spacing_dict.items(): - try: - p_dict["MEMORY"] - except KeyError: - p_dict["MEMORY"] = "N/A" - if subrow_counter == len(spacing_dict)-1: - divider = True - if subrow_counter == 0: - table.add_row([example, spacing, p_dict['TIME'], p_dict["GPU_MEMORY"], p_dict["MEMORY"]], divider=divider) - else: - table.add_row(["", spacing, p_dict['TIME'], p_dict["GPU_MEMORY"], p_dict["MEMORY"]], divider=divider) - subrow_counter += 1 - divider = False - -table_file_name = "./benchmarking_bash/benchmarking_data_table.txt" -benchmarking_table_file = open(table_file_name, 'w') -benchmarking_table_file.write(table.get_string()) diff --git a/simpa_examples/benchmarking/extract_benchmarking_data.py b/simpa_examples/benchmarking/extract_benchmarking_data.py new file mode 100644 index 00000000..6ce5c972 --- /dev/null +++ b/simpa_examples/benchmarking/extract_benchmarking_data.py @@ -0,0 +1,152 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT +import os +import sys + +import numpy as np +import pandas as pd +from pathlib import Path +from argparse import ArgumentParser +from typing import Union + + +def lines_that_contain(string, fp): + """ + Function to determine if a string contains a certain word/phrase + :param string: what to check + :param fp: + :return: + """ + return [line for line in fp if string in line] + + +def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: float = .4, step: float = .1, + savefolder: str = None) -> None: + """ Reads benchmarking data and creates a pandas dataframe + :param profiles: list with profiles ['TIME', "GPU_MEMORY", "MEMORY"] + :param start: start spacing default .2 + :param stop: stop spacing default .4 + :param step: step size default .1 + :param savefolder: PATH TO benchmarking data txt --> assumes txt: savefolder + "/benchmarking_data_" + + profile + "_" + str(spacing) + ".txt" + :return: None + :raises: ImportError (unknown units from input data) + """ + + # init defaults + if savefolder is None or savefolder == "default": + savefolder = str(Path(__file__).parent.resolve()) + + if profiles is None: + profiles = ['TIME', "GPU_MEMORY", "MEMORY"] + + spacings = np.arange(start, stop+1e-6, step).tolist() + + # specific information for the location of the data in the line profiler files + info_starts = {"MEMORY": 19, "GPU_MEMORY": 12, "TIME": 16} + info_ends = {"MEMORY": 29, "GPU_MEMORY": 26, "TIME": 29} + + benchmarking_lists = [] # init result + for profile in profiles: + for spacing in spacings: + file_name = savefolder + "/benchmarking_data_" + profile + "_" + str(np.round(spacing, 4)) + ".txt" + benchmarking_file = open(file_name, 'r') + current_examples = [] + + # where to find which files have successfully run + if profile == 'TIME': + example_name_lines = lines_that_contain("File:", benchmarking_file) + + for enl in example_name_lines: + example = enl.rpartition("simpa_examples/")[2].rpartition(".py")[0] + if example not in current_examples: + current_examples.append(example) + else: + break + + elif profile == 'GPU_MEMORY': + example_name_lines = lines_that_contain("##", benchmarking_file) + + for enl in example_name_lines: + example = enl.rpartition("run_")[2].rpartition("\n")[0] + if example not in current_examples: + current_examples.append(example) + else: + break + + if profile == 'MEMORY': + example_name_lines = lines_that_contain("Filename:", benchmarking_file) + + for enl in example_name_lines: + example = enl.rpartition("simpa_examples/")[2].rpartition(".py")[0] + if example not in current_examples: + current_examples.append(example) + else: + break + + benchmarking_file = open(file_name, 'r') + lines_with_sp_simulate = lines_that_contain("sp.simulate", benchmarking_file) + + examples_counter = 0 + for lwss in lines_with_sp_simulate: + try: + value = float(lwss[info_starts[profile]:info_ends[profile]]) + except ValueError: + continue + + unit = str(lwss[info_ends[profile]]) + if profile == "TIME": + value_with_unit = value + else: + if unit == 'K': + value_with_unit = value / 1000 + elif unit == 'M': + value_with_unit = value + elif unit == 'G': + value_with_unit = value * 1000 + else: + raise ImportError(f'Unit {unit} not supported') + + example = current_examples[examples_counter] + benchmarking_lists.append([example, spacing, profile, value_with_unit]) + + # lets you know which example you are on + examples_counter += 1 + if examples_counter == len(current_examples): + examples_counter = 0 + + # creating data frame + new_df = pd.DataFrame(benchmarking_lists, columns=['Example', 'Spacing', 'Profile', 'Value']) + new_df.astype(dtype={"Example": "str", "Spacing": "float64", "Profile": "str", "Value": "float64"}) + + # if exists: load old dataframe and append OR just save df + df_file = Path(savefolder + '/benchmarking_data_frame.csv') + if df_file.is_file(): + old_df = pd.read_csv(df_file) + new_df = pd.concat([old_df, new_df]) + new_df.to_csv(df_file, index=False) + + +if __name__ == "__main__": + parser = ArgumentParser(description='Run benchmarking tests') + parser.add_argument("--start", default=.2, + help='start spacing default .2mm') + parser.add_argument("--stop", default=.4, + help='stop spacing default .4mm') + parser.add_argument("--step", default=.1, + help='step size mm') + parser.add_argument("--profiles", default=None, type=str, + help='the profile to run') + parser.add_argument("--savefolder", default=None, type=str, help='where to save the results') + config = parser.parse_args() + + start = config.start + stop = config.stop + step = config.step + profiles = config.profiles + savefolder = config.savefolder + + profiles = profiles.split('%')[:-1] + read_out_benchmarking_data(start=float(start), stop=float(stop), step=float(step), profiles=profiles, + savefolder=savefolder) diff --git a/simpa_examples/benchmarking/get_final_table.py b/simpa_examples/benchmarking/get_final_table.py new file mode 100644 index 00000000..4c1256de --- /dev/null +++ b/simpa_examples/benchmarking/get_final_table.py @@ -0,0 +1,64 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import pandas as pd +import numpy as np +from pathlib import Path +from argparse import ArgumentParser + + +def get_final_table(savefolder): + """ Function to get the final table from a benchmarking file. Just call it after the last iteration of the + benchmarking. Saves csv in same location as savefolder! + :param savefolder: str to csv file containing benchmarking data frame + :return: None + :raise: None + """ + if savefolder is None or savefolder == "default": + savefolder = str(Path(__file__).parent.resolve()) + + # read the csv file + df_file = Path(savefolder + '/benchmarking_data_frame.csv') + new_df = pd.read_csv(df_file) + + # init result + mean_list = [] + # loop over every example, spacing, profile and saves mean and std for the sub dataframe + for expl in np.unique(new_df['Example']): + for space in np.unique(new_df['Spacing']): + expl_list = [expl, space] # init row of result + for prof in np.unique(new_df['Profile']): + # get the sub data frame for a specific profile and spacing. + sub_df = new_df.loc[ + (new_df['Spacing'] == space) & (new_df['Example'] == expl) & (new_df['Profile'] == prof)] + _mean = sub_df.mean(numeric_only=True)['Value'] + _std = sub_df.std(numeric_only=True)['Value'] + expl_list.append(np.round(_mean, 2)) # append to output row + expl_list.append(np.round(_std, 2)) + mean_list.append(expl_list) + + # init naming and format of output data frame columns + my_profiles = [] + type_dict = {"Example": "str", "Spacing": "float64"} + for entry in np.unique(new_df['Profile']).tolist(): + my_profiles.append(entry + '_mean') + my_profiles.append(entry + '_std') + type_dict[entry + '_mean'] = 'float64' + type_dict[entry + '_std'] = 'float64' + + # concat rows (list of lists) to a single data frame + cols = ['Example', 'Spacing'] + my_profiles + mean_df = pd.DataFrame(mean_list, columns=cols) + mean_df.astype(dtype=type_dict) + # save to csv at input location + mean_df.to_csv(str(df_file).replace('.csv', '_mean.csv'), index=False) + + +if __name__ == "__main__": + parser = ArgumentParser(description='Run benchmarking tests') + parser.add_argument("--savefolder", default=None, type=str, help='where to save the results') + config = parser.parse_args() + + savefolder = config.savefolder + get_final_table(savefolder=savefolder) diff --git a/simpa_examples/benchmarking/performance_check.py b/simpa_examples/benchmarking/performance_check.py index a685c5ff..37be5d08 100644 --- a/simpa_examples/benchmarking/performance_check.py +++ b/simpa_examples/benchmarking/performance_check.py @@ -4,12 +4,9 @@ import pathlib import os +from argparse import ArgumentParser -import typer -app = typer.Typer() - -@app.command() def run_benchmarking_tests(spacing=0.4, profile: str = "TIME", savefolder: str = 'default'): """ @@ -21,8 +18,8 @@ def run_benchmarking_tests(spacing=0.4, profile: str = "TIME", savefolder: str = spacing = float(spacing) os.environ["SIMPA_PROFILE"] = profile if savefolder == 'default': - savefolder = (str(pathlib.Path(__file__).parent.resolve()) + "/benchmarking_data_" + profile + "_" + str(spacing) - + ".txt") + savefolder = (str(pathlib.Path(__file__).parent.resolve()) + "/benchmarking_data_" + profile + "_" + + str(spacing) + ".txt") os.environ["SIMPA_PROFILE_SAVE_FILE"] = savefolder elif savefolder == 'print': pass @@ -30,21 +27,28 @@ def run_benchmarking_tests(spacing=0.4, profile: str = "TIME", savefolder: str = os.environ["SIMPA_PROFILE_SAVE_FILE"] = savefolder+"/benchmarking_data_"+profile+"_"+str(spacing)+".txt" import simpa_examples - import simpa as sp - - # examples = [simpa_examples.run_linear_unmixing, simpa_examples.run_minimal_optical_simulation, - # simpa_examples.run_minimal_optical_simulation_uniform_cube, simpa_examples.run_msot_invision_simulation, - # simpa_examples.run_optical_and_acoustic_simulation, - # simpa_examples.run_perform_iterative_qPAI_reconstruction] - examples = [simpa_examples.run_msot_invision_simulation] + examples = [simpa_examples.run_linear_unmixing, simpa_examples.run_minimal_optical_simulation, + simpa_examples.run_minimal_optical_simulation_uniform_cube, simpa_examples.run_msot_invision_simulation, + simpa_examples.run_optical_and_acoustic_simulation, + simpa_examples.run_perform_iterative_qPAI_reconstruction, simpa_examples.segmentation_loader] for example in examples: try: - example(SPACING=spacing, path_manager=None, visualise=False) + example(spacing=spacing, path_manager=None, visualise=False) except AttributeError: print("simulation cannot be run on {} with spacing {}".format(example, spacing)) if __name__ == "__main__": - app() + parser = ArgumentParser(description='Run benchmarking tests') + parser.add_argument("--spacing", default=0.2, help='the voxel spacing in mm') + parser.add_argument("--profile", default="TIME", type=str, + help='the profile to run') + parser.add_argument("--savefolder", default='default', type=str, help='where to save the results') + config = parser.parse_args() + + spacing = config.spacing + profile = config.profile + savefolder = config.savefolder + run_benchmarking_tests(spacing=float(spacing), profile=config.profile, savefolder=config.savefolder) diff --git a/simpa_examples/benchmarking/run_benchmarking.sh b/simpa_examples/benchmarking/run_benchmarking.sh index 83a265b6..a98fc2b3 100644 --- a/simpa_examples/benchmarking/run_benchmarking.sh +++ b/simpa_examples/benchmarking/run_benchmarking.sh @@ -2,6 +2,8 @@ help() { echo "Usage: calculate benchmarking for [options]" +echo "For further details see readme" +echo "Number of examples can be selected in performance_check.py" echo "For contributing, please use default" echo "Options:" echo " -i, --init First spacing to benchmark: default = 0.2mm" @@ -11,12 +13,13 @@ echo " -f, --file Where to store the output files: default save in cu echo " -t, --time Profile times taken: if no profile all are set" echo " -g, --gpu Profile GPU usage: if no profile all are set" echo " -m, --memory Profile memory usage: if no profile all are set" -echo " -b, --table Create pretty table with the primary results" +echo " -n, --number Number of simulations: default = 1)" echo " -h, --help Display this help message" exit 0 } start=0 +number=1 profiles=() filename='default' @@ -39,13 +42,14 @@ case "$1" in -g | --gpu) profiles+=("GPU_MEMORY") ;; -m | --memory) profiles+=("MEMORY") + ;; + -n | --number) number=$2 + shift 1 ;; -h | --help) help ;; *) echo "Option $1 not recognized" ;; - -b | --table) write_table='True' - ;; esac shift 1 done @@ -63,14 +67,23 @@ if [ ${#profiles[@]} -eq 0 ]; then profiles+=($"MEMORY") fi -for spacing in $(seq $start $step $stop) +prfs='' +for profile in "${profiles[@]}" do - for profile in "${profiles[@]}" - do - python3 performance_check.py --spacing $spacing --profile $profile --savefolder $filename - done + prfs+="$profile" + prfs+="%" done -if [ -$write_table == 'True' ]; then - python3 create_benchmarking_table.py -fi +for ((i=0; i < number; i++)) +do + for spacing in $(seq $start $step $stop) + do + for profile in "${profiles[@]}" + do + python3 performance_check.py --spacing $spacing --profile $profile --savefolder $filename + done + done + python3 extract_benchmarking_data.py --start $start --stop $stop --step $step --profiles "$prfs" --savefolder $filename +done + +python3 get_final_table.py --savefolder $filename From 728ca09009d0bfee2ba5a61a1cb8ed8d8ea3bac4 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Wed, 26 Jun 2024 16:05:40 +0200 Subject: [PATCH 15/34] update --- README.md | 3 +++ simpa_examples/msot_invision_simulation.py | 2 +- simpa_examples/segmentation_loader.py | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 224ddda3..6df1d5b4 100755 --- a/README.md +++ b/README.md @@ -193,6 +193,9 @@ benchmarking/benchmarking_data/benchmarking_data_table.txt). Make sure to back u it otherwise will be overwritten. We recommend using the default settings (besides -file gives the location where the table is saved). +You can add or remove simpa examples from the benchmarking in the performance_check.py (examples= ...). If examples +do not run on your device (eg to small GPU RAM), the skript should work either way. #300 + # Troubleshooting In this section, known problems are listed with their solutions (if available): diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 9ccb2cd9..1cc8a518 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -219,4 +219,4 @@ def get_settings(): spacing = config.spacing path_manager = config.path_manager visualise = config.visualise - run_msot_invision_simulation(spacing=spacing, path_manager=path_manager, visualise=visualise) \ No newline at end of file + run_msot_invision_simulation(spacing=spacing, path_manager=path_manager, visualise=visualise) diff --git a/simpa_examples/segmentation_loader.py b/simpa_examples/segmentation_loader.py index bef3091c..e86607eb 100644 --- a/simpa_examples/segmentation_loader.py +++ b/simpa_examples/segmentation_loader.py @@ -20,7 +20,7 @@ @profile -def run_segmentation_loader(spacing: Union[float, int] = 0.1, path_manager=None, +def run_segmentation_loader(spacing: Union[float, int] = .1, path_manager=None, visualise: bool = True): """ From 57bd737c1a4e1eb86f6c5584083d420af5950a07 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 27 Jun 2024 09:38:51 +0200 Subject: [PATCH 16/34] Data using an overnight run --- .../benchmarking_data_frame_mean.csv | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv b/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv index 107bc618..a36e6530 100644 --- a/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv +++ b/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv @@ -1,19 +1,19 @@ Example,Spacing,GPU_MEMORY_mean,GPU_MEMORY_std,MEMORY_mean,MEMORY_std,TIME_mean,TIME_std -linear_unmixing,0.2,1450.0,0.0,507.3,5.09,11107.3,127.14 -linear_unmixing,0.3,458.0,0.0,684.55,0.78,4853.55,419.1 -linear_unmixing,0.4000000000000001,206.0,0.0,558.2,17.11,3326.95,23.83 -minimal_optical_simulation,0.2,1600.0,,-8.6,,10426.8, -minimal_optical_simulation,0.3,514.0,,1.2,,4273.6, -minimal_optical_simulation,0.4000000000000001,226.0,,-10.3,,2676.4, -minimal_optical_simulation_uniform_cube,0.2,1190.0,,50.4,,6281.9, -minimal_optical_simulation_uniform_cube,0.3,368.0,,-0.2,,3258.7, -minimal_optical_simulation_uniform_cube,0.4000000000000001,176.0,,0.2,,1714.0, -msot_invision_simulation,0.2,4250.0,,31.1,,28513.7, -msot_invision_simulation,0.3,1330.0,,70.1,,19706.4, -msot_invision_simulation,0.4000000000000001,556.0,,65.8,,17596.6, -optical_and_acoustic_simulation,0.2,510.0,,11.2,,33123.5, -optical_and_acoustic_simulation,0.3,180.0,,,,8789.8, -optical_and_acoustic_simulation,0.4000000000000001,80.0,,,,8930.5, -perform_iterative_qPAI_reconstruction,0.2,376.0,,73.1,,7583.6, -perform_iterative_qPAI_reconstruction,0.3,136.0,,-0.4,,9815.4, -perform_iterative_qPAI_reconstruction,0.4000000000000001,66.0,,73.1,,8253.1, +linear_unmixing,0.2,1450.0,0.0,497.92,22.45,11601.65,339.33 +linear_unmixing,0.3,458.0,0.0,604.1,85.63,4800.38,233.09 +linear_unmixing,0.4000000000000001,199.28,7.03,554.04,26.63,3264.97,122.91 +minimal_optical_simulation,0.2,1600.0,0.0,20.15,20.52,10842.97,226.6 +minimal_optical_simulation,0.3,514.0,0.0,25.13,67.42,4081.68,127.35 +minimal_optical_simulation,0.4000000000000001,223.12,3.01,0.4,19.99,2681.59,63.79 +minimal_optical_simulation_uniform_cube,0.2,1190.0,0.0,30.61,17.58,6453.55,132.51 +minimal_optical_simulation_uniform_cube,0.3,368.0,0.0,33.52,59.16,3243.28,117.88 +minimal_optical_simulation_uniform_cube,0.4000000000000001,169.28,7.03,1.52,9.6,1669.26,41.78 +msot_invision_simulation,0.2,4250.0,0.0,36.9,8.74,31478.65,5637.21 +msot_invision_simulation,0.3,1330.0,0.0,55.14,16.3,23634.48,2221.21 +msot_invision_simulation,0.4000000000000001,556.0,0.0,64.69,5.81,21680.22,3377.43 +optical_and_acoustic_simulation,0.2,510.0,0.0,22.29,19.39,36481.83,9463.13 +optical_and_acoustic_simulation,0.3,180.0,0.0,,,9674.44,2259.11 +optical_and_acoustic_simulation,0.4000000000000001,80.0,0.0,,,9849.76,3164.97 +perform_iterative_qPAI_reconstruction,0.2,376.0,0.0,161.59,110.96,7249.38,275.35 +perform_iterative_qPAI_reconstruction,0.3,136.0,0.0,29.24,34.84,9763.71,108.95 +perform_iterative_qPAI_reconstruction,0.4000000000000001,66.0,0.0,76.08,3.27,8348.39,93.2 From cc5affd369dba4b71fe7c4c998d6ade109601183 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 27 Jun 2024 09:42:08 +0200 Subject: [PATCH 17/34] remove old data --- .../benchmarking_data_table.txt | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 simpa_examples/benchmarking/benchmarking_data/benchmarking_data_table.txt diff --git a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_table.txt b/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_table.txt deleted file mode 100644 index 8bc23ef0..00000000 --- a/simpa_examples/benchmarking/benchmarking_data/benchmarking_data_table.txt +++ /dev/null @@ -1,21 +0,0 @@ -+-----------------------------------------+------------+------------+--------+--------+ -| Example | Spacing mm | Time μs | GPU | MEMORY | -+-----------------------------------------+------------+------------+--------+--------+ -| linear_unmixing | 0.2 | 12084177.3 | 1.45G | 526.6M | -| | 0.4 | 3958047.6 | 192.0M | 552.9M | -+-----------------------------------------+------------+------------+--------+--------+ -| minimal_optical_simulation | 0.2 | 10699994.3 | 1.6G | 16.4M | -| | 0.4 | 2844852.2 | 220.0M | -10.8M | -+-----------------------------------------+------------+------------+--------+--------+ -| minimal_optical_simulation_uniform_cube | 0.2 | 6502907.2 | 1.19G | 19.0M | -| | 0.4 | 1758689.2 | 162.0M | 0.0M | -+-----------------------------------------+------------+------------+--------+--------+ -| msot_invision_simulation | 0.2 | 28158333.9 | 4.25G | 39.2M | -| | 0.4 | 18450957.5 | 556.0M | 66.4M | -+-----------------------------------------+------------+------------+--------+--------+ -| optical_and_acoustic_simulation | 0.2 | 37078767.0 | 510.0M | 28.9M | -| | 0.4 | 9548734.8 | 80.0M | N/A | -+-----------------------------------------+------------+------------+--------+--------+ -| perform_iterative_qPAI_reconstruction | 0.2 | 8411215.6 | 376.0M | 316.5M | -| | 0.4 | 10481453.4 | 66.0M | 78.8M | -+-----------------------------------------+------------+------------+--------+--------+ \ No newline at end of file From 57de937e7f34fe53022e634fa0317101029474d4 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Thu, 27 Jun 2024 11:18:15 +0200 Subject: [PATCH 18/34] Specified documentation of how to override flags. Last given flags are used --- simpa/utils/tags.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/simpa/utils/tags.py b/simpa/utils/tags.py index 49350846..074a8a91 100644 --- a/simpa/utils/tags.py +++ b/simpa/utils/tags.py @@ -1494,5 +1494,8 @@ class Tags: ADDITIONAL_FLAGS = ("additional_flags", Iterable) """ - Defines a sequence of extra flags to be parsed to executables for simulation modules + Defines a sequence of extra flags to be parsed to executables for simulation modules. + Caution: The user is responsible for checking if these flags exist and don't break the predefined flags' behaviour. + It is assumed that if flags are specified multiple times the flag provided last is considered. + This can for example be used to override predefined flags. """ From 275d21822a2951537023b97c318261a0bfc3df89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Hinrich=20N=C3=B6lke?= Date: Thu, 27 Jun 2024 15:10:03 +0200 Subject: [PATCH 19/34] add simpa version to hdf5 file Co-authored-by: Jonas Burian --- simpa/core/simulation.py | 2 ++ simpa/utils/tags.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/simpa/core/simulation.py b/simpa/core/simulation.py index ce53cc45..f84a7fd1 100644 --- a/simpa/core/simulation.py +++ b/simpa/core/simulation.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT from simpa.utils import Tags +from simpa import __version__ from simpa.io_handling.io_hdf5 import save_hdf5, load_hdf5, save_data_field, load_data_field from simpa.io_handling.ipasc import export_to_ipasc from simpa.utils.settings import Settings @@ -56,6 +57,7 @@ def simulate(simulation_pipeline: list, settings: Settings, digital_device_twin: settings[Tags.SIMPA_OUTPUT_PATH] = simpa_output_path + ".hdf5" + simpa_output[Tags.SIMPA_VERSION] = __version__ simpa_output[Tags.SETTINGS] = settings simpa_output[Tags.DIGITAL_DEVICE] = digital_device_twin simpa_output[Tags.SIMULATION_PIPELINE] = [type(x).__name__ for x in simulation_pipeline] diff --git a/simpa/utils/tags.py b/simpa/utils/tags.py index 3dd7c259..b4ee980d 100644 --- a/simpa/utils/tags.py +++ b/simpa/utils/tags.py @@ -1250,7 +1250,7 @@ class Tags: Default filename of the SIMPA output if not specified otherwise.\n Usage: SIMPA package, naming convention """ - SIMPA_VERSION = ("simpa_version", str) + SIMPA_VERSION = "simpa_version" """ Version number of the currently installed simpa package Usage: SIMPA package From fa63e7a1f6c0fe82c401b94438a0b4158d88ccef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Hinrich=20N=C3=B6lke?= Date: Thu, 27 Jun 2024 15:52:47 +0200 Subject: [PATCH 20/34] revert changes in tags.py --- simpa/utils/tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simpa/utils/tags.py b/simpa/utils/tags.py index b4ee980d..3dd7c259 100644 --- a/simpa/utils/tags.py +++ b/simpa/utils/tags.py @@ -1250,7 +1250,7 @@ class Tags: Default filename of the SIMPA output if not specified otherwise.\n Usage: SIMPA package, naming convention """ - SIMPA_VERSION = "simpa_version" + SIMPA_VERSION = ("simpa_version", str) """ Version number of the currently installed simpa package Usage: SIMPA package From cd3b8217f403014203537e98a89be9b2902d2be6 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 27 Jun 2024 16:46:40 +0200 Subject: [PATCH 21/34] remove typing --- simpa_examples/linear_unmixing.py | 8 ++------ simpa_examples/minimal_optical_simulation.py | 10 +++------- .../minimal_optical_simulation_uniform_cube.py | 11 ++++------- simpa_examples/msot_invision_simulation.py | 10 +++------- simpa_examples/optical_and_acoustic_simulation.py | 11 ++++------- .../perform_iterative_qPAI_reconstruction.py | 11 ++++------- simpa_examples/segmentation_loader.py | 10 +++------- 7 files changed, 23 insertions(+), 48 deletions(-) diff --git a/simpa_examples/linear_unmixing.py b/simpa_examples/linear_unmixing.py index e40c7609..e1b78f7f 100644 --- a/simpa_examples/linear_unmixing.py +++ b/simpa_examples/linear_unmixing.py @@ -4,7 +4,6 @@ import os import numpy as np -from typing import Union from argparse import ArgumentParser import simpa as sp @@ -17,7 +16,7 @@ @profile -def run_linear_unmixing(spacing: Union[float, int] = 0.25, path_manager=None, visualise: bool = True): +def run_linear_unmixing(spacing: float | int = 0.25, path_manager=None, visualise: bool = True): """ :param spacing: The simulation spacing between voxels @@ -187,7 +186,4 @@ def create_example_tissue(): parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() - spacing = config.spacing - path_manager = config.path_manager - visualise = config.visualise - run_linear_unmixing(spacing=spacing, path_manager=path_manager, visualise=visualise) + run_linear_unmixing(spacing=config.spacing, path_manager=config.path_manager, visualise=config.visualise) diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index a2fcb23c..c3646f22 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -6,7 +6,6 @@ import simpa as sp import numpy as np from simpa.utils.profiling import profile -from typing import Union from argparse import ArgumentParser # FIXME temporary workaround for newest Intel architectures @@ -18,7 +17,7 @@ @profile -def run_minimal_optical_simulation(spacing: Union[float, int] = 0.5, path_manager=None, visualise: bool = True): +def run_minimal_optical_simulation(spacing: float | int = 0.5, path_manager=None, visualise: bool = True): """ :param spacing: The simulation spacing between voxels @@ -171,12 +170,9 @@ def __init__(self): if __name__ == "__main__": parser = ArgumentParser(description='Run the minimal optical simulation example') - parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--spacing", default=0.2, type=float, help='the voxel spacing in mm') parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() - spacing = config.spacing - path_manager = config.path_manager - visualise = config.visualise - run_minimal_optical_simulation(spacing=spacing, path_manager=path_manager, visualise=visualise) + run_minimal_optical_simulation(spacing=config.spacing, path_manager=config.path_manager, visualise=config.visualise) diff --git a/simpa_examples/minimal_optical_simulation_uniform_cube.py b/simpa_examples/minimal_optical_simulation_uniform_cube.py index b6bd26ff..675addd5 100644 --- a/simpa_examples/minimal_optical_simulation_uniform_cube.py +++ b/simpa_examples/minimal_optical_simulation_uniform_cube.py @@ -9,7 +9,6 @@ from simpa import Tags import simpa as sp import numpy as np -from typing import Union # FIXME temporary workaround for newest Intel architectures import os @@ -22,7 +21,7 @@ @profile -def run_minimal_optical_simulation_uniform_cube(spacing: Union[float, int] = 0.5, path_manager=None, +def run_minimal_optical_simulation_uniform_cube(spacing: float | int = 0.5, path_manager=None, visualise: bool = True): """ @@ -109,12 +108,10 @@ def create_example_tissue(): if __name__ == "__main__": parser = ArgumentParser(description='Run the minimal optical simulation uniform cube example') - parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--spacing", default=0.2, type=float, help='the voxel spacing in mm') parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() - spacing = config.spacing - path_manager = config.path_manager - visualise = config.visualise - run_minimal_optical_simulation_uniform_cube(spacing=spacing, path_manager=path_manager, visualise=visualise) + run_minimal_optical_simulation_uniform_cube(spacing=config.spacing, path_manager=config.path_manager, + visualise=config.visualise) diff --git a/simpa_examples/msot_invision_simulation.py b/simpa_examples/msot_invision_simulation.py index 1cc8a518..210daf4b 100644 --- a/simpa_examples/msot_invision_simulation.py +++ b/simpa_examples/msot_invision_simulation.py @@ -6,14 +6,13 @@ import simpa as sp import numpy as np from simpa.utils.profiling import profile -from typing import Union from argparse import ArgumentParser path_manager = sp.PathManager() @profile -def run_msot_invision_simulation(spacing: Union[float, int] = 0.5, path_manager=None, visualise: bool = True): +def run_msot_invision_simulation(spacing: float | int = 0.5, path_manager=None, visualise: bool = True): """ :param spacing: The simulation spacing between voxels @@ -211,12 +210,9 @@ def get_settings(): if __name__ == "__main__": parser = ArgumentParser(description='Run the msot invision simulation example') - parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--spacing", default=0.2, type=float, help='the voxel spacing in mm') parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() - spacing = config.spacing - path_manager = config.path_manager - visualise = config.visualise - run_msot_invision_simulation(spacing=spacing, path_manager=path_manager, visualise=visualise) + run_msot_invision_simulation(spacing=config.spacing, path_manager=config.path_manager, visualise=config.visualise) diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 50bd71df..c6142b8f 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -6,7 +6,6 @@ import simpa as sp import numpy as np from simpa.utils.profiling import profile -from typing import Union from argparse import ArgumentParser # FIXME temporary workaround for newest Intel architectures @@ -18,7 +17,7 @@ @profile -def run_optical_and_acoustic_simulation(spacing: Union[float, int] = 0.2, path_manager=None, +def run_optical_and_acoustic_simulation(spacing: float | int = 0.2, path_manager=None, visualise: bool = True): """ @@ -215,12 +214,10 @@ def create_example_tissue(): if __name__ == "__main__": parser = ArgumentParser(description='Run the optical and acoustic simulation example') - parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--spacing", default=0.2, type=float, help='the voxel spacing in mm') parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() - spacing = config.spacing - path_manager = config.path_manager - visualise = config.visualise - run_optical_and_acoustic_simulation(spacing=spacing, path_manager=path_manager, visualise=visualise) + run_optical_and_acoustic_simulation(spacing=config.spacing, path_manager=config.path_manager, + visualise=config.visualise) diff --git a/simpa_examples/perform_iterative_qPAI_reconstruction.py b/simpa_examples/perform_iterative_qPAI_reconstruction.py index 29a8ace4..f46ccfb7 100644 --- a/simpa_examples/perform_iterative_qPAI_reconstruction.py +++ b/simpa_examples/perform_iterative_qPAI_reconstruction.py @@ -11,7 +11,6 @@ import simpa as sp from simpa import Tags -from typing import Union from simpa.utils.profiling import profile from argparse import ArgumentParser @@ -24,7 +23,7 @@ @profile -def run_perform_iterative_qPAI_reconstruction(spacing: Union[float, int] = 0.2, path_manager=None, +def run_perform_iterative_qPAI_reconstruction(spacing: float | int = 0.2, path_manager=None, visualise: bool = True): """ @@ -246,12 +245,10 @@ def __init__(self): if __name__ == "__main__": parser = ArgumentParser(description='Run the iterative qPAI reconstruction example') - parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--spacing", default=0.2, type=float, help='the voxel spacing in mm') parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() - spacing = config.spacing - path_manager = config.path_manager - visualise = config.visualise - run_perform_iterative_qPAI_reconstruction(spacing=spacing, path_manager=path_manager, visualise=visualise) + run_perform_iterative_qPAI_reconstruction(spacing=config.spacing, path_manager=config.path_manager, + visualise=config.visualise) diff --git a/simpa_examples/segmentation_loader.py b/simpa_examples/segmentation_loader.py index e86607eb..8114ce0f 100644 --- a/simpa_examples/segmentation_loader.py +++ b/simpa_examples/segmentation_loader.py @@ -5,7 +5,6 @@ from simpa import Tags import simpa as sp import numpy as np -from typing import Union from skimage.data import shepp_logan_phantom from scipy.ndimage import zoom @@ -20,7 +19,7 @@ @profile -def run_segmentation_loader(spacing: Union[float, int] = .1, path_manager=None, +def run_segmentation_loader(spacing: float | int = .1, path_manager=None, visualise: bool = True): """ @@ -106,12 +105,9 @@ def segmentation_class_mapping(): if __name__ == "__main__": parser = ArgumentParser(description='Run the segmentation loader example') - parser.add_argument("--spacing", default=0.2, type=Union[float, int], help='the voxel spacing in mm') + parser.add_argument("--spacing", default=0.2, type=float, help='the voxel spacing in mm') parser.add_argument("--path_manager", default=None, help='the path manager, None uses sp.PathManager') parser.add_argument("--visualise", default=True, type=bool, help='whether to visualise the result') config = parser.parse_args() - spacing = config.spacing - path_manager = config.path_manager - visualise = config.visualise - run_segmentation_loader(spacing=spacing, path_manager=path_manager, visualise=visualise) + run_segmentation_loader(spacing=config.spacing, path_manager=config.path_manager, visualise=config.visualise) From e1482640d9efd38dc84d932259d68c889637f576 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Thu, 27 Jun 2024 17:54:57 +0200 Subject: [PATCH 22/34] adding docs and updating profiler --- .gitignore | 3 + CONTRIBUTING.md | 6 +- README.md | 21 ++- docs/source/bench_link.md | 2 + docs/source/benchmarking.md | 132 ++++++++++++++++++ docs/source/images/benchmarking_table.png | Bin 0 -> 96297 bytes docs/source/index.rst | 1 + simpa/utils/profiling.py | 19 +-- .../benchmarking_data_frame_mean.csv | 19 --- .../benchmarking/extract_benchmarking_data.py | 27 ++-- .../benchmarking/get_final_table.py | 4 +- .../benchmarking/performance_check.py | 5 +- .../benchmarking/run_benchmarking.sh | 12 +- 13 files changed, 180 insertions(+), 71 deletions(-) create mode 100644 docs/source/bench_link.md create mode 100644 docs/source/benchmarking.md create mode 100644 docs/source/images/benchmarking_table.png delete mode 100644 simpa_examples/benchmarking/benchmarking_data_frame_mean.csv diff --git a/.gitignore b/.gitignore index d65fe579..8d83a059 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ docs/objects.inv simpa_tests/figures simpa_tests/figures/* +simpa_examples/benchmarking/*.csv +simpa_examples/benchmarking/*.txt + path_config.env *.orig diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a7e5bc8c..d108c76d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,12 +35,12 @@ In general the following steps are involved during a contribution: 5. Perform test driven development on feature branch. A new implemented feature / a bug fix should be accompanied by a test. Additionally, all previously existing tests must still pass after the contribution. -6. Run pre-commit hooks and make sure all hooks are passing. -7. If changing core simpa modules/refactoring/optimisation, run benchmarking bash script on develop before and after the changes (see readme). +6. Run pre-commit hooks and make sure all hooks are passing. +7. If you want to benchmark your contributions please use the benchmarking bash script (see [benchmarking.md](docs/source/benchmarking.md) for more details). 8. Once development is finished, create a pull request including your changes. For more information on how to create pull request, see GitHub's [about pull requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). 9. If there are conflicts between the simpa develop branch and your branch, you should update your feature branch with the simpa develop branch using a "merge" strategy instead of "rebase". -10. member of the core development team will review your pull request and potentially require further changes +10. A member of the core development team will review your pull request and potentially require further changes (see [Contribution review and integration](#contribution-review-and-integration)). Once all remarks have been resolved, your changes will be merged into the develop branch. diff --git a/README.md b/README.md index 61e5e108..964f8f03 100755 --- a/README.md +++ b/README.md @@ -184,17 +184,16 @@ Please see the github guidelines for creating pull requests: [https://docs.githu # Performance profiling When changing the SIMPA core, e.g., by refactoring/optimizing, or if you are curious about how fast your machine runs -SIMPA, you can run the SIMPA benchmarking scripts. It is recommended to run it once for checking if it works and then -change line (TODO) to run it at least 100 times for actual benchmarking. This has to be done with a clean -setup, no browser or other applications running (weekend/at night/...). To see if your changes are actually better than -the current implementation, you simply have to run the script on the current develop and additionally with your changes -and compare the resulting benchmarking/benchmarking_data_table.txt files (they will look like -benchmarking/benchmarking_data/benchmarking_data_table.txt). Make sure to back up the table after your first run, since -it otherwise will be overwritten. We recommend using the default settings (besides -file gives the location where the -table is saved). - -You can add or remove simpa examples from the benchmarking in the performance_check.py (examples= ...). If examples -do not run on your device (eg to small GPU RAM), the skript should work either way. #300 +SIMPA, you can run the SIMPA [benchmarking scripts](simpa_examples/benchmarking/run_benchmarking.sh). It is recommended +to run: + +```bash +bash ./run_benchmark.sh +``` + +once for checking if it works and then parse [--number 100] to run it at eg 100 times for actual benchmarking. +Please see [benchmarking.md](docs/source/benchmarking.md) for a complete explanation. + # Troubleshooting diff --git a/docs/source/bench_link.md b/docs/source/bench_link.md new file mode 100644 index 00000000..e53ff117 --- /dev/null +++ b/docs/source/bench_link.md @@ -0,0 +1,2 @@ +```{include} benchmarking.md +``` \ No newline at end of file diff --git a/docs/source/benchmarking.md b/docs/source/benchmarking.md new file mode 100644 index 00000000..12d87d63 --- /dev/null +++ b/docs/source/benchmarking.md @@ -0,0 +1,132 @@ +# Benchmarking SIMPA + +## Overview +The [run_benchmarking.sh](../../simpa_examples/benchmarking/run_benchmarking.sh) bash script helps you benchmark +SIMPA simulations with various profiling options such as time, GPU memory, and memory usage. +It allows customization of initial spacing, final spacing, step size, output file location, and number of simulations. +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, +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). + +Below is a description of the available options and how to use them. + +### Benchmarking for contributions +When contributing, you may be asked by the development team to benchmarking the changes you've made to help them +understand how your changes have effected the performance of SIMPA. Therefore, we ask that you run this script with +**`-n, --number`** as 100 before AND after your changes, on a clean setup with no browser or other applications running. +Please put this in the conversation of the pull request, and not add it to the files of the pull request itself. + + +## 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). +- **`-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). +- **`-m, --memory`**: Profile memory usage (if no profile is specified, all are set). +- **`-n, --number`**: Number of simulations (default = 1). +- **`-h, --help`**: Display this help message. + +## Default Values +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 + +If no profiling options are specified, all three profilers (time, GPU memory, and memory) are used by default. + +## Examples +Here are some examples of how to use the script: + +1. **Default Usage**: + ```bash + bash ./run_benchmark.sh + ``` + +2. **Custom Spacing and File Output**: + ```bash + bash ./run_benchmark.sh -i 0.1 -c 0.5 -s 0.05 -f results + ``` + +3. **Profile Time and GPU Memory for 3 Simulations**: + ```bash + bash ./run_benchmark.sh -t -g -n 3 + ``` + +To read the csv you can use the following code: +```python +import pandas as pd +my_simpa_dir = '/home/user/workspace/...' +benchmarking_results = pd.read_csv(my_simpa_dir + 'simpa/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv') +display(benchmarking_results) # display works for ipynb - for py files use print(benchmarking_results) +``` + +The expected outcome should look something similar to the below: + +![img.png](images/benchmarking_table.png) + +# Line Profiler (more advanced - for specific function profiling) + +Within SIMPA we have an [inbuilt python script](../../simpa/utils/profiling.py) to help benchmark specific functions and +understand the holdups in our code. This script is designed to set up a profiling environment based on an environment +variable named `SIMPA_PROFILE`. The `@profile` decorator can then be added to functions to see line-by-line statistics +of code performance. + +Here is a breakdown of the script's functionality: + +1. **Determine Profile Type and Stream:** + - `profile_type` is fetched from the environment variable `SIMPA_PROFILE`. + - `stream` is set to an open file object if the environment variable `SIMPA_PROFILE_SAVE_FILE` is set; otherwise, it is `None`. + +2. **No Profiling:** + - If `profile_type` is `None` + +3. **Time Profiling:** + - If `profile_type` is `"TIME"` + +4. **Memory Profiling:** + - If `profile_type` is `"MEMORY"` + +5. **GPU Memory Profiling:** + - If `profile_type` is `"GPU_MEMORY"` + +6. **Profile Decorator** + - The `profile` decorator is defined to register functions to be profiled and to print statistics upon program exit. + +7. **Invalid Profile Type:** + - If `profile_type` does not match any of the expected values (`"TIME"`, `"MEMORY"`, or `"GPU_MEMORY"`), a `RuntimeError` is raised. + +### Example Usage + +To use this script, you need to set the `SIMPA_PROFILE` environment variable to one of the supported values +(`TIME`, `MEMORY`, or `GPU_MEMORY`) and optionally set the `SIMPA_PROFILE_SAVE_FILE` to specify where to save the +profiling results. + +The environment variables must be set before importing simpa in your python script like below. +```python +import os +os.environ("SIMPA_PROFILE")="TIME" +os.environ("SIMPA_PROFILE_SAVE_FILE")=profile_results.txt +``` + +To use the `@profile` decorator, simply apply it to the functions you want to profile within your script: + +```python +@profile +def some_function(): + # function implementation +``` + +Make sure the necessary profiling modules (`line_profiler`, `memory_profiler`, `pytorch_memlab`) are installed in your +environment. \ No newline at end of file diff --git a/docs/source/images/benchmarking_table.png b/docs/source/images/benchmarking_table.png new file mode 100644 index 0000000000000000000000000000000000000000..03d3d8f808cc4983e66a7b1de4a93436276ef77f GIT binary patch literal 96297 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 E7Zr9n`~Uy| literal 0 HcmV?d00001 diff --git a/docs/source/index.rst b/docs/source/index.rst index 041f3cfc..4539b02a 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,6 +21,7 @@ pipeline according to their specific use cases and tool requirements. intro_link contributing_link + bench_link.md =================================== diff --git a/simpa/utils/profiling.py b/simpa/utils/profiling.py index db95db2b..896f879b 100644 --- a/simpa/utils/profiling.py +++ b/simpa/utils/profiling.py @@ -4,13 +4,17 @@ import os +# Determine the type of profiling from the environment variable profile_type = os.getenv("SIMPA_PROFILE") + +# Determine if a save file for profiling results is specified if os.getenv("SIMPA_PROFILE_SAVE_FILE"): stream = open(os.getenv("SIMPA_PROFILE_SAVE_FILE"), 'w') else: stream = None + if profile_type is None: - # define a no-op @profile decorator + # Define a no-op @profile decorator if no profiling is specified def profile(f): return f elif profile_type == "TIME": @@ -18,26 +22,22 @@ def profile(f): from line_profiler import LineProfiler profile = LineProfiler() - - def print_stats_atexit(): - return profile.print_stats(stream=stream, output_unit=10**(-3)) - atexit.register(print_stats_atexit) + # Register to print stats on program exit + atexit.register(lambda: profile.print_stats(stream=stream, output_unit=10**(-3))) elif profile_type == "MEMORY": from memory_profiler import profile profile = profile(stream=stream) elif profile_type == "GPU_MEMORY": - from typing import Tuple from pytorch_memlab.line_profiler.line_profiler import LineProfiler, DEFAULT_COLUMNS import atexit global_line_profiler = LineProfiler() global_line_profiler.enable() - def profile(func, columns: Tuple[str, ...] = DEFAULT_COLUMNS): + def profile(func, columns: tuple[str, ...] = DEFAULT_COLUMNS): """ - Check plagiarism problems + Profile the function for GPU memory usage """ - import atexit global_line_profiler.add_function(func) def print_stats_atexit(): @@ -47,4 +47,5 @@ def print_stats_atexit(): return func else: + # Raise an error if the SIMPA_PROFILE value is invalid raise RuntimeError("SIMPA_PROFILE env var is defined but invalid: valid values are TIME, MEMORY, or GPU_MEMORY") diff --git a/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv b/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv deleted file mode 100644 index a36e6530..00000000 --- a/simpa_examples/benchmarking/benchmarking_data_frame_mean.csv +++ /dev/null @@ -1,19 +0,0 @@ -Example,Spacing,GPU_MEMORY_mean,GPU_MEMORY_std,MEMORY_mean,MEMORY_std,TIME_mean,TIME_std -linear_unmixing,0.2,1450.0,0.0,497.92,22.45,11601.65,339.33 -linear_unmixing,0.3,458.0,0.0,604.1,85.63,4800.38,233.09 -linear_unmixing,0.4000000000000001,199.28,7.03,554.04,26.63,3264.97,122.91 -minimal_optical_simulation,0.2,1600.0,0.0,20.15,20.52,10842.97,226.6 -minimal_optical_simulation,0.3,514.0,0.0,25.13,67.42,4081.68,127.35 -minimal_optical_simulation,0.4000000000000001,223.12,3.01,0.4,19.99,2681.59,63.79 -minimal_optical_simulation_uniform_cube,0.2,1190.0,0.0,30.61,17.58,6453.55,132.51 -minimal_optical_simulation_uniform_cube,0.3,368.0,0.0,33.52,59.16,3243.28,117.88 -minimal_optical_simulation_uniform_cube,0.4000000000000001,169.28,7.03,1.52,9.6,1669.26,41.78 -msot_invision_simulation,0.2,4250.0,0.0,36.9,8.74,31478.65,5637.21 -msot_invision_simulation,0.3,1330.0,0.0,55.14,16.3,23634.48,2221.21 -msot_invision_simulation,0.4000000000000001,556.0,0.0,64.69,5.81,21680.22,3377.43 -optical_and_acoustic_simulation,0.2,510.0,0.0,22.29,19.39,36481.83,9463.13 -optical_and_acoustic_simulation,0.3,180.0,0.0,,,9674.44,2259.11 -optical_and_acoustic_simulation,0.4000000000000001,80.0,0.0,,,9849.76,3164.97 -perform_iterative_qPAI_reconstruction,0.2,376.0,0.0,161.59,110.96,7249.38,275.35 -perform_iterative_qPAI_reconstruction,0.3,136.0,0.0,29.24,34.84,9763.71,108.95 -perform_iterative_qPAI_reconstruction,0.4000000000000001,66.0,0.0,76.08,3.27,8348.39,93.2 diff --git a/simpa_examples/benchmarking/extract_benchmarking_data.py b/simpa_examples/benchmarking/extract_benchmarking_data.py index 6ce5c972..69cf1c1d 100644 --- a/simpa_examples/benchmarking/extract_benchmarking_data.py +++ b/simpa_examples/benchmarking/extract_benchmarking_data.py @@ -1,22 +1,19 @@ # SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ # SPDX-FileCopyrightText: 2021 Janek Groehl # SPDX-License-Identifier: MIT -import os -import sys import numpy as np import pandas as pd from pathlib import Path from argparse import ArgumentParser -from typing import Union def lines_that_contain(string, fp): """ - Function to determine if a string contains a certain word/phrase - :param string: what to check - :param fp: - :return: + Function to determine if a string contains a certain word/phrase and the returns the full line + :param string: string the line in the fp is compared to + :param fp: full page of text from profiler output + :return: lines in the full page that contain 'string' """ return [line for line in fp if string in line] @@ -36,7 +33,7 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f # init defaults if savefolder is None or savefolder == "default": - savefolder = str(Path(__file__).parent.resolve()) + savefolder = Path(__file__).parent.resolve() if profiles is None: profiles = ['TIME', "GPU_MEMORY", "MEMORY"] @@ -50,7 +47,8 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f benchmarking_lists = [] # init result for profile in profiles: for spacing in spacings: - file_name = savefolder + "/benchmarking_data_" + profile + "_" + str(np.round(spacing, 4)) + ".txt" + txt_file = "benchmarking_data_" + profile + "_" + str(np.round(spacing, 4)) + ".txt" + file_name = savefolder / txt_file benchmarking_file = open(file_name, 'r') current_examples = [] @@ -75,7 +73,7 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f else: break - if profile == 'MEMORY': + elif profile == 'MEMORY': example_name_lines = lines_that_contain("Filename:", benchmarking_file) for enl in example_name_lines: @@ -141,12 +139,7 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f parser.add_argument("--savefolder", default=None, type=str, help='where to save the results') config = parser.parse_args() - start = config.start - stop = config.stop - step = config.step profiles = config.profiles - savefolder = config.savefolder - profiles = profiles.split('%')[:-1] - read_out_benchmarking_data(start=float(start), stop=float(stop), step=float(step), profiles=profiles, - savefolder=savefolder) + read_out_benchmarking_data(start=float(config.start), stop=float(config.stop), step=float(config.step), + profiles=profiles, savefolder=config.savefolder) diff --git a/simpa_examples/benchmarking/get_final_table.py b/simpa_examples/benchmarking/get_final_table.py index 4c1256de..5b0a0c35 100644 --- a/simpa_examples/benchmarking/get_final_table.py +++ b/simpa_examples/benchmarking/get_final_table.py @@ -16,10 +16,10 @@ def get_final_table(savefolder): :raise: None """ if savefolder is None or savefolder == "default": - savefolder = str(Path(__file__).parent.resolve()) + savefolder = Path(__file__).parent.resolve() # read the csv file - df_file = Path(savefolder + '/benchmarking_data_frame.csv') + df_file = savefolder / 'benchmarking_data_frame.csv' new_df = pd.read_csv(df_file) # init result diff --git a/simpa_examples/benchmarking/performance_check.py b/simpa_examples/benchmarking/performance_check.py index 37be5d08..7a1128bd 100644 --- a/simpa_examples/benchmarking/performance_check.py +++ b/simpa_examples/benchmarking/performance_check.py @@ -48,7 +48,4 @@ def run_benchmarking_tests(spacing=0.4, profile: str = "TIME", savefolder: str = parser.add_argument("--savefolder", default='default', type=str, help='where to save the results') config = parser.parse_args() - spacing = config.spacing - profile = config.profile - savefolder = config.savefolder - run_benchmarking_tests(spacing=float(spacing), profile=config.profile, savefolder=config.savefolder) + run_benchmarking_tests(spacing=float(config.spacing), profile=config.profile, savefolder=config.savefolder) diff --git a/simpa_examples/benchmarking/run_benchmarking.sh b/simpa_examples/benchmarking/run_benchmarking.sh index a98fc2b3..f5af01b1 100644 --- a/simpa_examples/benchmarking/run_benchmarking.sh +++ b/simpa_examples/benchmarking/run_benchmarking.sh @@ -4,16 +4,16 @@ help() { echo "Usage: calculate benchmarking for [options]" echo "For further details see readme" echo "Number of examples can be selected in performance_check.py" -echo "For contributing, please use default" +echo "For comparable benchmarks, please use default" echo "Options:" echo " -i, --init First spacing to benchmark: default = 0.2mm" echo " -c, --cease Final spacing to benchmark: default = 0.4mm" echo " -s, --step Step between spacings: default = 0.1mm" -echo " -f, --file Where to store the output files: default save in curr; 'print' prints it in console" -echo " -t, --time Profile times taken: if no profile all are set" -echo " -g, --gpu Profile GPU usage: if no profile all are set" -echo " -m, --memory Profile memory usage: if no profile all are set" -echo " -n, --number Number of simulations: default = 1)" +echo " -f, --file Where to store the output files: default save in current directory; 'print' prints it in console" +echo " -t, --time Profile times taken: if no profile, all are set" +echo " -g, --gpu Profile GPU usage: if no profile, all are set" +echo " -m, --memory Profile memory usage: if no profile, all are set" +echo " -n, --number Number of simulations: default = 1" echo " -h, --help Display this help message" exit 0 } From 99e0df6070339b7c748ee74ddc0c25c0fc40fa01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Hinrich=20N=C3=B6lke?= Date: Fri, 28 Jun 2024 13:54:10 +0200 Subject: [PATCH 23/34] Fix issue with circular imports (i.e. overwrite of __init__.py) Co-authored-by: Jonas Burian --- simpa/__init__.py | 6 ++++++ simpa/core/simulation.py | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/simpa/__init__.py b/simpa/__init__.py index 58fff0a3..b336a76f 100644 --- a/simpa/__init__.py +++ b/simpa/__init__.py @@ -4,7 +4,13 @@ from .utils import * 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 \ diff --git a/simpa/core/simulation.py b/simpa/core/simulation.py index f84a7fd1..41c549fa 100644 --- a/simpa/core/simulation.py +++ b/simpa/core/simulation.py @@ -4,6 +4,7 @@ from simpa.utils import Tags from simpa import __version__ + from simpa.io_handling.io_hdf5 import save_hdf5, load_hdf5, save_data_field, load_data_field from simpa.io_handling.ipasc import export_to_ipasc from simpa.utils.settings import Settings @@ -56,7 +57,7 @@ def simulate(simulation_pipeline: list, settings: Settings, digital_device_twin: simpa_output_path = path + settings[Tags.VOLUME_NAME] settings[Tags.SIMPA_OUTPUT_PATH] = simpa_output_path + ".hdf5" - + simpa_output[Tags.SIMPA_VERSION] = __version__ simpa_output[Tags.SETTINGS] = settings simpa_output[Tags.DIGITAL_DEVICE] = digital_device_twin From 21ddf443aa3d6e951b860ce9d6f619c43d6513b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Hinrich=20N=C3=B6lke?= Date: Fri, 28 Jun 2024 14:21:42 +0200 Subject: [PATCH 24/34] Change folder name to .pre_commit_configs Co-authored-by: Jonas Burian --- {pre_commit_configs => .pre_commit_configs}/license_header.txt | 0 {pre_commit_configs => .pre_commit_configs}/link-config.json | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {pre_commit_configs => .pre_commit_configs}/license_header.txt (100%) rename {pre_commit_configs => .pre_commit_configs}/link-config.json (100%) diff --git a/pre_commit_configs/license_header.txt b/.pre_commit_configs/license_header.txt similarity index 100% rename from pre_commit_configs/license_header.txt rename to .pre_commit_configs/license_header.txt diff --git a/pre_commit_configs/link-config.json b/.pre_commit_configs/link-config.json similarity index 100% rename from pre_commit_configs/link-config.json rename to .pre_commit_configs/link-config.json From 2b913692be825588e0cee91daefa4466cab93769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Hinrich=20N=C3=B6lke?= Date: Fri, 28 Jun 2024 14:25:31 +0200 Subject: [PATCH 25/34] Change folder name in config yaml Co-authored-by: Jonas Burian --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 98f439c3..44cd7c16 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: types: [python] args: - --license-filepath - - pre_commit_configs/license_header.txt # defaults to: LICENSE.txt + - .pre_commit_configs/license_header.txt # defaults to: LICENSE.txt - repo: https://github.com/jorisroovers/gitlint # Uses MIT License (MIT compatible) rev: v0.19.1 @@ -38,7 +38,7 @@ repos: types: [markdown] args: - -c - - pre_commit_configs/link-config.json + - .pre_commit_configs/link-config.json always_run: true # toggle comment to perform git config user email check. Note that you have to place the check-email.sh script in your .git/hooks/ folder From 0d98d7fb4e57502e4b98a29b21b0a27b195e26e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Hinrich=20N=C3=B6lke?= Date: Fri, 28 Jun 2024 15:48:22 +0200 Subject: [PATCH 26/34] make simpa version accessible with sp.get_data_field(path, Tags.SIMPA_VERSION Co-authored-by: Jonas Burian --- simpa/io_handling/io_hdf5.py | 2 ++ simpa/utils/dict_path_manager.py | 2 +- simpa/utils/tags.py | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/simpa/io_handling/io_hdf5.py b/simpa/io_handling/io_hdf5.py index 2464827b..eb10ae97 100644 --- a/simpa/io_handling/io_hdf5.py +++ b/simpa/io_handling/io_hdf5.py @@ -123,6 +123,8 @@ def data_grabber(file, path): """ if isinstance(h5file[path], h5py._hl.dataset.Dataset): + if isinstance(h5file[path][()], bytes): + return h5file[path][()].decode("utf-8") return h5file[path][()] dictionary = {} diff --git a/simpa/utils/dict_path_manager.py b/simpa/utils/dict_path_manager.py index c4e5e105..7a47127d 100644 --- a/simpa/utils/dict_path_manager.py +++ b/simpa/utils/dict_path_manager.py @@ -16,7 +16,7 @@ def generate_dict_path(data_field, wavelength: (int, float) = None) -> str: :return: String which defines the path to the data_field. """ - if data_field in [Tags.SIMULATIONS, Tags.SETTINGS, Tags.DIGITAL_DEVICE, Tags.SIMULATION_PIPELINE]: + if data_field in [Tags.SIMPA_VERSION, Tags.SIMULATIONS, Tags.SETTINGS, Tags.DIGITAL_DEVICE, Tags.SIMULATION_PIPELINE]: return "/" + data_field + "/" all_wl_independent_properties = wavelength_independent_properties + toolkit_tags diff --git a/simpa/utils/tags.py b/simpa/utils/tags.py index 84bd1e61..b23e2e6b 100644 --- a/simpa/utils/tags.py +++ b/simpa/utils/tags.py @@ -1250,7 +1250,7 @@ class Tags: Default filename of the SIMPA output if not specified otherwise.\n Usage: SIMPA package, naming convention """ - SIMPA_VERSION = ("simpa_version", str) + SIMPA_VERSION = "simpa_version" """ Version number of the currently installed simpa package Usage: SIMPA package From 4b812e86de6eb7b169513e21d0e09d4ad6ebaf0e Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Fri, 28 Jun 2024 17:28:23 +0200 Subject: [PATCH 27/34] Added manual execution test for MCX additional flags --- simpa/core/simulation_modules/__init__.py | 3 +- .../executables/MCXAdditionalFlags.py | 122 ++++++++++++++++++ 2 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 simpa_tests/manual_tests/executables/MCXAdditionalFlags.py diff --git a/simpa/core/simulation_modules/__init__.py b/simpa/core/simulation_modules/__init__.py index 34792abf..24c25062 100644 --- a/simpa/core/simulation_modules/__init__.py +++ b/simpa/core/simulation_modules/__init__.py @@ -39,6 +39,5 @@ def get_additional_flags(self) -> List[str]: cmd = [] if Tags.ADDITIONAL_FLAGS in self.component_settings: for flag in self.component_settings[Tags.ADDITIONAL_FLAGS]: - if flag not in cmd: - cmd.append(str(flag)) + cmd.append(str(flag)) return cmd \ No newline at end of file diff --git a/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py b/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py new file mode 100644 index 00000000..6a29c979 --- /dev/null +++ b/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py @@ -0,0 +1,122 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import os +import numpy as np +from simpa import MCXAdapter, ModelBasedVolumeCreationAdapter, simulate +from simpa.core.device_digital_twins import PhotoacousticDevice, PencilBeamIlluminationGeometry +from simpa.utils import Settings, Tags, TISSUE_LIBRARY, PathManager +from simpa_tests.manual_tests import ManualIntegrationTestClass + + +class MCXAdditionalFlags(ManualIntegrationTestClass): + + def create_example_tissue(self): + """ + This is a very simple example script of how to create a tissue definition. + It contains a muscular background, an epidermis layer on top of the muscles + and two blood vessels. It is used for volume creation. + """ + + background_dictionary = Settings() + background_dictionary[Tags.MOLECULE_COMPOSITION] = TISSUE_LIBRARY.constant(0.1, 100, 0.9) + background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + tissue_dict = Settings() + tissue_dict[Tags.BACKGROUND] = background_dictionary + + return tissue_dict + + def setup(self): + """ + This is not a completely autonomous simpa_tests case yet. + If run on another pc, please adjust the SIMULATION_PATH and MODEL_BINARY_PATH fields. + :return: + """ + + path_manager = PathManager() + + self.settings = Settings({ + Tags.WAVELENGTHS: [800], + Tags.WAVELENGTH: 800, + Tags.VOLUME_NAME: "AdditionalFlagsTest", + Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + Tags.SPACING_MM: 1, + Tags.DIM_VOLUME_X_MM: 100, + Tags.DIM_VOLUME_Y_MM: 100, + Tags.DIM_VOLUME_Z_MM: 100, + Tags.RANDOM_SEED: 4711 + }) + + self.settings.set_volume_creation_settings({ + Tags.SIMULATE_DEFORMED_LAYERS: True, + Tags.STRUCTURES: self.create_example_tissue() + }) + self.settings.set_optical_settings({ + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_PENCIL, + Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + Tags.MCX_ASSUMED_ANISOTROPY: 0.9 + }) + + self.device = PhotoacousticDevice(device_position_mm=np.asarray([self.settings[Tags.DIM_VOLUME_X_MM] / 2 - 0.5, + self.settings[Tags.DIM_VOLUME_Y_MM] / 2 - 0.5, + 0])) + self.device.add_illumination_geometry(PencilBeamIlluminationGeometry()) + + def run_simulation(self): + # run pipeline including volume creation and optical mcx simulation + pipeline = [ + ModelBasedVolumeCreationAdapter(self.settings), + MCXAdapter(self.settings), + ] + simulate(pipeline, self.settings, self.device) + + def test_execution_of_additional_flag(self): + """Tests if log file is created by setting additional parameters + + :raises FileNotFoundError: if log file does not exist at expected location + """ + output_name = f'{os.path.join(self.settings[Tags.SIMULATION_PATH], self.settings[Tags.VOLUME_NAME])}_output' + output_file_name = f'{output_name}.log' + + # perform cleaning before test and checking if file exists afterwards + if os.path.exists(output_file_name): + os.remove(output_file_name) + + self.settings.get_optical_settings()[Tags.ADDITIONAL_FLAGS] = ['-l', 1, '-s', output_name] + self.run_simulation() + + if not os.path.exists(output_file_name): + raise FileNotFoundError(f"Log file wasn't created at expected path {output_file_name}") + + def test_if_additional_flag_overrides_existing_flag(self): + """Tests if log file is created with correct last given name by setting multiple additional parameters + + :raises FileNotFoundError: if correct log file does not exist at expected location + """ + output_name = f'{os.path.join(self.settings[Tags.SIMULATION_PATH], self.settings[Tags.VOLUME_NAME])}_output' + output_file_name = f'{output_name}.log' + + # perform cleaning before test and checking if file exists afterwards + if os.path.exists(output_file_name): + os.remove(output_file_name) + + self.settings.get_optical_settings()[Tags.ADDITIONAL_FLAGS] = ['-l', 1, '-s', 'temp_name', '-s', output_name] + self.run_simulation() + + if not os.path.exists(output_file_name): + raise FileNotFoundError(f"Log file wasn't created with correct last given name at expected path {output_file_name}") + + def perform_test(self): + self.test_execution_of_additional_flag() + self.test_if_additional_flag_overrides_existing_flag() + + def visualise_result(self, show_figure_on_screen=True, save_path=None): + pass + +if __name__ == '__main__': + test = MCXAdditionalFlags() + test.run_test(show_figure_on_screen=False) From 293d69b82e958358c743e7466fea338041de2fa1 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Mon, 1 Jul 2024 10:32:51 +0200 Subject: [PATCH 28/34] cleaner extraction of data --- docs/source/simpa_examples.rst | 11 ++--- .../benchmarking/extract_benchmarking_data.py | 47 +++++++------------ .../benchmarking/get_final_table.py | 4 +- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/docs/source/simpa_examples.rst b/docs/source/simpa_examples.rst index 5630fd02..359fc45f 100644 --- a/docs/source/simpa_examples.rst +++ b/docs/source/simpa_examples.rst @@ -5,13 +5,12 @@ simpa\_examples :maxdepth: 2 create_a_custom_digital_device_twin - linear_unmixing - perform_image_reconstruction - minimal_optical_simulation_heterogeneous_tissue - msot_invision_simulation - perform_iterative_qPAI_reconstruction create_custom_tissues + linear_unmixing minimal_optical_simulation minimal_optical_simulation_uniform_cube - segmentation_loader optical_and_acoustic_simulation + perform_iterative_qPAI_reconstruction + msot_invision_simulation + perform_image_reconstruction + segmentation_loader diff --git a/simpa_examples/benchmarking/extract_benchmarking_data.py b/simpa_examples/benchmarking/extract_benchmarking_data.py index 69cf1c1d..452b92e9 100644 --- a/simpa_examples/benchmarking/extract_benchmarking_data.py +++ b/simpa_examples/benchmarking/extract_benchmarking_data.py @@ -34,6 +34,8 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f # init defaults if savefolder is None or savefolder == "default": savefolder = Path(__file__).parent.resolve() + else: + savefolder = Path(savefolder) if profiles is None: profiles = ['TIME', "GPU_MEMORY", "MEMORY"] @@ -44,6 +46,10 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f info_starts = {"MEMORY": 19, "GPU_MEMORY": 12, "TIME": 16} info_ends = {"MEMORY": 29, "GPU_MEMORY": 26, "TIME": 29} + profiling_strings = {"TIME": ("File:", "simpa_examples/", ".py"), + "GPU_MEMORY": ("##", "run_", "\n"), + "MEMORY": ("Filename:", "simpa_examples/", ".py"), } + benchmarking_lists = [] # init result for profile in profiles: for spacing in spacings: @@ -53,35 +59,13 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f current_examples = [] # where to find which files have successfully run - if profile == 'TIME': - example_name_lines = lines_that_contain("File:", benchmarking_file) - - for enl in example_name_lines: - example = enl.rpartition("simpa_examples/")[2].rpartition(".py")[0] - if example not in current_examples: - current_examples.append(example) - else: - break - - elif profile == 'GPU_MEMORY': - example_name_lines = lines_that_contain("##", benchmarking_file) - - for enl in example_name_lines: - example = enl.rpartition("run_")[2].rpartition("\n")[0] - if example not in current_examples: - current_examples.append(example) - else: - break - - elif profile == 'MEMORY': - example_name_lines = lines_that_contain("Filename:", benchmarking_file) - - for enl in example_name_lines: - example = enl.rpartition("simpa_examples/")[2].rpartition(".py")[0] - if example not in current_examples: - current_examples.append(example) - else: - break + example_name_lines = lines_that_contain(profiling_strings[profile][0], benchmarking_file) + for enl in example_name_lines: + example = enl.rpartition(profiling_strings[profile][1])[2].rpartition(profiling_strings[profile][2])[0] + if example not in current_examples: + current_examples.append(example) + else: + break benchmarking_file = open(file_name, 'r') lines_with_sp_simulate = lines_that_contain("sp.simulate", benchmarking_file) @@ -119,7 +103,7 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f new_df.astype(dtype={"Example": "str", "Spacing": "float64", "Profile": "str", "Value": "float64"}) # if exists: load old dataframe and append OR just save df - df_file = Path(savefolder + '/benchmarking_data_frame.csv') + df_file = savefolder / 'benchmarking_data_frame.csv' if df_file.is_file(): old_df = pd.read_csv(df_file) new_df = pd.concat([old_df, new_df]) @@ -140,6 +124,7 @@ def read_out_benchmarking_data(profiles: list = None, start: float = .2, stop: f config = parser.parse_args() profiles = config.profiles - profiles = profiles.split('%')[:-1] + if profiles: + profiles = profiles.split('%')[:-1] read_out_benchmarking_data(start=float(config.start), stop=float(config.stop), step=float(config.step), profiles=profiles, savefolder=config.savefolder) diff --git a/simpa_examples/benchmarking/get_final_table.py b/simpa_examples/benchmarking/get_final_table.py index 5b0a0c35..8bc84484 100644 --- a/simpa_examples/benchmarking/get_final_table.py +++ b/simpa_examples/benchmarking/get_final_table.py @@ -8,7 +8,7 @@ from argparse import ArgumentParser -def get_final_table(savefolder): +def get_final_table(savefolder: str = None): """ Function to get the final table from a benchmarking file. Just call it after the last iteration of the benchmarking. Saves csv in same location as savefolder! :param savefolder: str to csv file containing benchmarking data frame @@ -17,6 +17,8 @@ def get_final_table(savefolder): """ if savefolder is None or savefolder == "default": savefolder = Path(__file__).parent.resolve() + else: + savefolder = Path(savefolder) # read the csv file df_file = savefolder / 'benchmarking_data_frame.csv' From dd34b3659143f515c06c23559df107f04fa8b762 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Thu, 4 Jul 2024 11:27:25 +0200 Subject: [PATCH 29/34] Added additional flags manual test for MATLAB --- .../executables/MATLABAdditionalFlags.py | 146 ++++++++++++++++++ .../executables/MCXAdditionalFlags.py | 42 ++--- 2 files changed, 169 insertions(+), 19 deletions(-) create mode 100644 simpa_tests/manual_tests/executables/MATLABAdditionalFlags.py diff --git a/simpa_tests/manual_tests/executables/MATLABAdditionalFlags.py b/simpa_tests/manual_tests/executables/MATLABAdditionalFlags.py new file mode 100644 index 00000000..4e63a889 --- /dev/null +++ b/simpa_tests/manual_tests/executables/MATLABAdditionalFlags.py @@ -0,0 +1,146 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + +import os +import numpy as np +from simpa import MCXAdapter, ModelBasedVolumeCreationAdapter, simulate, KWaveAdapter +from simpa.core.device_digital_twins import PhotoacousticDevice, PencilBeamIlluminationGeometry, LinearArrayDetectionGeometry +from simpa.utils import Settings, Tags, TISSUE_LIBRARY, PathManager +from simpa_tests.manual_tests import ManualIntegrationTestClass + + +class MATLABAdditionalFlags(ManualIntegrationTestClass): + """ + Tests if using Tags.ADDITIONAL_FLAGS to set additional flags for MATLAB works in the KWaveAdapter. + """ + + def create_example_tissue(self): + """ + Creates a very simple example tissue with only background tissue. + """ + + background_dictionary = Settings() + background_dictionary[Tags.MOLECULE_COMPOSITION] = TISSUE_LIBRARY.constant(0.1, 100, 0.9) + background_dictionary[Tags.STRUCTURE_TYPE] = Tags.BACKGROUND + tissue_dict = Settings() + tissue_dict[Tags.BACKGROUND] = background_dictionary + + return tissue_dict + + def setup(self): + """ + Creates basic simulation settings and a simulation device. + """ + + path_manager = PathManager() + + self.settings = Settings({ + Tags.WAVELENGTHS: [800], + Tags.WAVELENGTH: 800, + Tags.VOLUME_NAME: "AdditionalFlagsTest", + Tags.SIMULATION_PATH: path_manager.get_hdf5_file_save_path(), + Tags.SPACING_MM: 1, + Tags.DIM_VOLUME_X_MM: 100, + Tags.DIM_VOLUME_Y_MM: 100, + Tags.DIM_VOLUME_Z_MM: 100, + Tags.RANDOM_SEED: 4711, + Tags.GPU: True + }) + + self.settings.set_volume_creation_settings({ + Tags.SIMULATE_DEFORMED_LAYERS: True, + Tags.STRUCTURES: self.create_example_tissue() + }) + self.settings.set_optical_settings({ + Tags.OPTICAL_MODEL_NUMBER_PHOTONS: 1e7, + Tags.OPTICAL_MODEL_BINARY_PATH: path_manager.get_mcx_binary_path(), + Tags.OPTICAL_MODEL: Tags.OPTICAL_MODEL_MCX, + Tags.ILLUMINATION_TYPE: Tags.ILLUMINATION_TYPE_PENCIL, + Tags.LASER_PULSE_ENERGY_IN_MILLIJOULE: 50, + Tags.MCX_ASSUMED_ANISOTROPY: 0.9 + }) + self.settings.set_acoustic_settings({ + Tags.ACOUSTIC_SIMULATION_3D: False, + Tags.ACOUSTIC_MODEL_BINARY_PATH: path_manager.get_matlab_binary_path(), + Tags.KWAVE_PROPERTY_ALPHA_POWER: 0.00, + Tags.KWAVE_PROPERTY_SENSOR_RECORD: "p", + Tags.KWAVE_PROPERTY_PMLInside: False, + Tags.KWAVE_PROPERTY_PMLSize: [31, 32], + Tags.KWAVE_PROPERTY_PMLAlpha: 1.5, + Tags.KWAVE_PROPERTY_PlotPML: False, + Tags.RECORDMOVIE: False, + Tags.MOVIENAME: "visualization_log", + Tags.ACOUSTIC_LOG_SCALE: True + }) + + self.device = PhotoacousticDevice(device_position_mm=np.asarray([self.settings[Tags.DIM_VOLUME_X_MM] / 2 - 0.5, + self.settings[Tags.DIM_VOLUME_Y_MM] / 2 - 0.5, + 0])) + self.device.add_illumination_geometry(PencilBeamIlluminationGeometry()) + self.device.set_detection_geometry(LinearArrayDetectionGeometry(device_position_mm=self.device.device_position_mm, + pitch_mm=0.25, + number_detector_elements=100, + field_of_view_extent_mm=np.asarray([-15, 15, 0, 0, 0, 20]))) + + output_name = f'{os.path.join(self.settings[Tags.SIMULATION_PATH], self.settings[Tags.VOLUME_NAME])}' + self.output_file_name = f'{output_name}.log' + + def run_simulation(self): + # run pipeline including volume creation and optical mcx simulation and acoustic matlab kwave simulation + pipeline = [ + ModelBasedVolumeCreationAdapter(self.settings), + MCXAdapter(self.settings), + KWaveAdapter(self.settings) + ] + simulate(pipeline, self.settings, self.device) + + def test_execution_of_additional_flag(self): + """Tests if log file is created by setting additional parameters + + :raises FileNotFoundError: if log file does not exist at expected location + """ + + # perform cleaning before test + if os.path.exists(self.output_file_name): + os.remove(self.output_file_name) + + # run simulation + self.settings.get_acoustic_settings()[Tags.ADDITIONAL_FLAGS] = ['-logfile', self.output_file_name] + self.run_simulation() + + # checking if file exists afterwards + if not os.path.exists(self.output_file_name): + raise FileNotFoundError(f"Log file wasn't created at expected path {self.output_file_name}") + + def test_if_last_flag_is_used(self): + """Tests if log file is created with correct last given name by setting multiple additional parameters + + :raises FileNotFoundError: if correct log file does not exist at expected location + """ + + # perform cleaning before test + if os.path.exists(self.output_file_name): + os.remove(self.output_file_name) + + # run simulation + self.settings.get_acoustic_settings()[Tags.ADDITIONAL_FLAGS] = ['-logfile', 'temp_name', '-logfile', self.output_file_name] + self.run_simulation() + + # checking if file exists afterwards + if not os.path.exists(self.output_file_name): + raise FileNotFoundError(f"Log file wasn't created with correct last given name at expected path {self.output_file_name}") + + def perform_test(self): + """ + Calls all individual tests of this class + """ + self.test_execution_of_additional_flag() + self.test_if_last_flag_is_used() + + def visualise_result(self, show_figure_on_screen=True, save_path=None): + pass # no figures are created that could be visualized + +if __name__ == '__main__': + test = MATLABAdditionalFlags() + test.run_test(show_figure_on_screen=False) diff --git a/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py b/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py index 6a29c979..4bf7da1d 100644 --- a/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py +++ b/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py @@ -14,9 +14,7 @@ class MCXAdditionalFlags(ManualIntegrationTestClass): def create_example_tissue(self): """ - This is a very simple example script of how to create a tissue definition. - It contains a muscular background, an epidermis layer on top of the muscles - and two blood vessels. It is used for volume creation. + Creates a very simple example tissue with only background tissue. """ background_dictionary = Settings() @@ -29,9 +27,7 @@ def create_example_tissue(self): def setup(self): """ - This is not a completely autonomous simpa_tests case yet. - If run on another pc, please adjust the SIMULATION_PATH and MODEL_BINARY_PATH fields. - :return: + Creates basic simulation settings and a simulation device. """ path_manager = PathManager() @@ -66,6 +62,9 @@ def setup(self): 0])) self.device.add_illumination_geometry(PencilBeamIlluminationGeometry()) + self.output_name = f'{os.path.join(self.settings[Tags.SIMULATION_PATH], self.settings[Tags.VOLUME_NAME])}_output' + self.output_file_name = f'{self.output_name}.log' + def run_simulation(self): # run pipeline including volume creation and optical mcx simulation pipeline = [ @@ -79,20 +78,20 @@ def test_execution_of_additional_flag(self): :raises FileNotFoundError: if log file does not exist at expected location """ - output_name = f'{os.path.join(self.settings[Tags.SIMULATION_PATH], self.settings[Tags.VOLUME_NAME])}_output' - output_file_name = f'{output_name}.log' - - # perform cleaning before test and checking if file exists afterwards - if os.path.exists(output_file_name): - os.remove(output_file_name) - self.settings.get_optical_settings()[Tags.ADDITIONAL_FLAGS] = ['-l', 1, '-s', output_name] + # perform cleaning before test + if os.path.exists(self. output_file_name): + os.remove(self.output_file_name) + + # run simulation + self.settings.get_optical_settings()[Tags.ADDITIONAL_FLAGS] = ['-l', 1, '-s', self.output_name] self.run_simulation() - if not os.path.exists(output_file_name): - raise FileNotFoundError(f"Log file wasn't created at expected path {output_file_name}") + # checking if file exists afterwards + if not os.path.exists(self.output_file_name): + raise FileNotFoundError(f"Log file wasn't created at expected path {self.output_file_name}") - def test_if_additional_flag_overrides_existing_flag(self): + def test_if_last_flag_is_used(self): """Tests if log file is created with correct last given name by setting multiple additional parameters :raises FileNotFoundError: if correct log file does not exist at expected location @@ -100,22 +99,27 @@ def test_if_additional_flag_overrides_existing_flag(self): output_name = f'{os.path.join(self.settings[Tags.SIMULATION_PATH], self.settings[Tags.VOLUME_NAME])}_output' output_file_name = f'{output_name}.log' - # perform cleaning before test and checking if file exists afterwards + # perform cleaning before test if os.path.exists(output_file_name): os.remove(output_file_name) + # run simulation self.settings.get_optical_settings()[Tags.ADDITIONAL_FLAGS] = ['-l', 1, '-s', 'temp_name', '-s', output_name] self.run_simulation() + # checking if file exists afterwards if not os.path.exists(output_file_name): raise FileNotFoundError(f"Log file wasn't created with correct last given name at expected path {output_file_name}") def perform_test(self): + """ + Calls all individual tests of this class + """ self.test_execution_of_additional_flag() - self.test_if_additional_flag_overrides_existing_flag() + self.test_if_last_flag_is_used() def visualise_result(self, show_figure_on_screen=True, save_path=None): - pass + pass # no figures are created that could be visualized if __name__ == '__main__': test = MCXAdditionalFlags() From 6fa020ff786f9a92ed4e589e8f5250c87dc4ea22 Mon Sep 17 00:00:00 2001 From: Friso Grace Date: Fri, 5 Jul 2024 09:32:23 +0200 Subject: [PATCH 30/34] resolve conflict docs/source/simpa_examples.rst --- docs/source/simpa_examples.rst | 2 +- simpa/core/__init__.py | 3 ++- simpa/core/processing_components/__init__.py | 1 - simpa/core/simulation_modules/__init__.py | 1 + .../simulation_modules/acoustic_forward_module/__init__.py | 2 +- .../simulation_modules/reconstruction_module/__init__.py | 2 +- .../simulation_modules/volume_creation_module/__init__.py | 2 +- simpa/utils/deformation_manager.py | 6 ++++-- .../automatic_tests/device_tests/test_field_of_view.py | 1 - simpa_tests/automatic_tests/test_bandpass_filter.py | 1 - simpa_tests/do_coverage.py | 2 +- 11 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/source/simpa_examples.rst b/docs/source/simpa_examples.rst index f6147c8d..e8a54665 100644 --- a/docs/source/simpa_examples.rst +++ b/docs/source/simpa_examples.rst @@ -7,8 +7,8 @@ simpa\_examples create_a_custom_digital_device_twin create_custom_tissues linear_unmixing - minimal_optical_simulation_uniform_cube minimal_optical_simulation + minimal_optical_simulation_uniform_cube msot_invision_simulation optical_and_acoustic_simulation perform_image_reconstruction diff --git a/simpa/core/__init__.py b/simpa/core/__init__.py index 8653966c..9a49b941 100644 --- a/simpa/core/__init__.py +++ b/simpa/core/__init__.py @@ -8,10 +8,12 @@ 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 @@ -29,4 +31,3 @@ def run(self, digital_device_twin: DigitalDeviceTwinBase): :param digital_device_twin: The digital twin that can be used by the digital device_twin. """ pass - \ No newline at end of file diff --git a/simpa/core/processing_components/__init__.py b/simpa/core/processing_components/__init__.py index c8306cbf..881a8f2d 100644 --- a/simpa/core/processing_components/__init__.py +++ b/simpa/core/processing_components/__init__.py @@ -19,4 +19,3 @@ def __init__(self, global_settings, component_settings_key: str): """ 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..c87e1e26 100644 --- a/simpa/core/simulation_modules/__init__.py +++ b/simpa/core/simulation_modules/__init__.py @@ -7,6 +7,7 @@ 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. diff --git a/simpa/core/simulation_modules/acoustic_forward_module/__init__.py b/simpa/core/simulation_modules/acoustic_forward_module/__init__.py index 2beb0eb6..e9f33d10 100644 --- a/simpa/core/simulation_modules/acoustic_forward_module/__init__.py +++ b/simpa/core/simulation_modules/acoustic_forward_module/__init__.py @@ -33,7 +33,7 @@ class AcousticForwardModelBaseAdapter(SimulationModule): 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 diff --git a/simpa/core/simulation_modules/reconstruction_module/__init__.py b/simpa/core/simulation_modules/reconstruction_module/__init__.py index 118874dc..05f8b3ef 100644 --- a/simpa/core/simulation_modules/reconstruction_module/__init__.py +++ b/simpa/core/simulation_modules/reconstruction_module/__init__.py @@ -25,7 +25,7 @@ class ReconstructionAdapterBase(SimulationModule): 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 diff --git a/simpa/core/simulation_modules/volume_creation_module/__init__.py b/simpa/core/simulation_modules/volume_creation_module/__init__.py index 7f2b3ffa..6b9f0753 100644 --- a/simpa/core/simulation_modules/volume_creation_module/__init__.py +++ b/simpa/core/simulation_modules/volume_creation_module/__init__.py @@ -20,7 +20,7 @@ class VolumeCreatorModuleBase(SimulationModule): 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 diff --git a/simpa/utils/deformation_manager.py b/simpa/utils/deformation_manager.py index 78d0a1ed..0c786a0d 100644 --- a/simpa/utils/deformation_manager.py +++ b/simpa/utils/deformation_manager.py @@ -26,7 +26,8 @@ def create_deformation_settings(bounds_mm, maximum_z_elevation_mm=1, filter_sigm # Add random permutations to the y-axis of the division knots all_scaling_value = np.multiply.outer( - np.cos(x_positions_vector / (bounds_mm[0][1] * (cosine_scaling_factor / np.pi)) - np.pi / (cosine_scaling_factor * 2)) ** 2, + np.cos(x_positions_vector / (bounds_mm[0][1] * (cosine_scaling_factor / + np.pi)) - np.pi / (cosine_scaling_factor * 2)) ** 2, np.cos(y_positions_vector / (bounds_mm[1][1] * (cosine_scaling_factor / np.pi)) - np.pi / (cosine_scaling_factor * 2)) ** 2) surface_elevations *= all_scaling_value @@ -60,7 +61,8 @@ def get_functional_from_deformation_settings(deformation_settings: dict): z_elevations_mm = deformation_settings[Tags.DEFORMATION_Z_ELEVATIONS_MM] order = "cubic" - functional_mm = RegularGridInterpolator(points=[x_coordinates_mm, y_coordinates_mm], values=z_elevations_mm, method=order) + functional_mm = RegularGridInterpolator( + points=[x_coordinates_mm, y_coordinates_mm], values=z_elevations_mm, method=order) return functional_mm diff --git a/simpa_tests/automatic_tests/device_tests/test_field_of_view.py b/simpa_tests/automatic_tests/device_tests/test_field_of_view.py index c1c79d87..cc63680c 100644 --- a/simpa_tests/automatic_tests/device_tests/test_field_of_view.py +++ b/simpa_tests/automatic_tests/device_tests/test_field_of_view.py @@ -101,4 +101,3 @@ def test_symmetric_with_odd_number_of_elements(self): self.assertAlmostEqual(ydim_end, 40) self.assertAlmostEqual(zdim_start, 0) self.assertAlmostEqual(zdim_end, 0) - diff --git a/simpa_tests/automatic_tests/test_bandpass_filter.py b/simpa_tests/automatic_tests/test_bandpass_filter.py index c499e2fd..404527d1 100644 --- a/simpa_tests/automatic_tests/test_bandpass_filter.py +++ b/simpa_tests/automatic_tests/test_bandpass_filter.py @@ -260,4 +260,3 @@ def test_butter_filter_with_random_signal(self, show_figure_on_screen=False): if show_figure_on_screen: self.visualize_filtered_spectrum(FILTERED_SIGNAL) - diff --git a/simpa_tests/do_coverage.py b/simpa_tests/do_coverage.py index cf9c4f69..88490637 100644 --- a/simpa_tests/do_coverage.py +++ b/simpa_tests/do_coverage.py @@ -25,4 +25,4 @@ cov.html_report(directory="../docs/test_coverage") # Exit with an appropriate code based on the test results -sys.exit(not result.wasSuccessful()) \ No newline at end of file +sys.exit(not result.wasSuccessful()) From d43809bb76e2f948946fa5c8c94ccb6cbfb330e0 Mon Sep 17 00:00:00 2001 From: Kris Dreher Date: Mon, 29 Jul 2024 15:41:37 +0200 Subject: [PATCH 31/34] Fixed initialization of benchmarking shell script variables stop and step --- simpa_examples/benchmarking/run_benchmarking.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/simpa_examples/benchmarking/run_benchmarking.sh b/simpa_examples/benchmarking/run_benchmarking.sh index f5af01b1..96a1e904 100644 --- a/simpa_examples/benchmarking/run_benchmarking.sh +++ b/simpa_examples/benchmarking/run_benchmarking.sh @@ -19,6 +19,8 @@ exit 0 } start=0 +stop=0 +step=0 number=1 profiles=() filename='default' @@ -54,9 +56,15 @@ esac shift 1 done -if [ $start -eq 0 ]; then +if [ "$start" == 0 ]; then start=0.2 +fi + +if [ "$stop" == 0 ]; then stop=0.4 +fi + +if [ "$step" == 0 ]; then step=0.1 fi From 0cc4d65cce8f397978e39428dd425fbc7761d38c Mon Sep 17 00:00:00 2001 From: Kris Dreher Date: Mon, 29 Jul 2024 15:42:00 +0200 Subject: [PATCH 32/34] Removed segmentation loader from benchmarking --- simpa_examples/__init__.py | 1 - simpa_examples/benchmarking/performance_check.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/simpa_examples/__init__.py b/simpa_examples/__init__.py index 49f13e4d..92a0960b 100644 --- a/simpa_examples/__init__.py +++ b/simpa_examples/__init__.py @@ -9,4 +9,3 @@ from simpa_examples.optical_and_acoustic_simulation import run_optical_and_acoustic_simulation from simpa_examples.perform_image_reconstruction import run_perform_image_reconstruction from simpa_examples.perform_iterative_qPAI_reconstruction import run_perform_iterative_qPAI_reconstruction -from simpa_examples.segmentation_loader import run_segmentation_loader diff --git a/simpa_examples/benchmarking/performance_check.py b/simpa_examples/benchmarking/performance_check.py index 7a1128bd..880b0d36 100644 --- a/simpa_examples/benchmarking/performance_check.py +++ b/simpa_examples/benchmarking/performance_check.py @@ -31,7 +31,7 @@ def run_benchmarking_tests(spacing=0.4, profile: str = "TIME", savefolder: str = examples = [simpa_examples.run_linear_unmixing, simpa_examples.run_minimal_optical_simulation, simpa_examples.run_minimal_optical_simulation_uniform_cube, simpa_examples.run_msot_invision_simulation, simpa_examples.run_optical_and_acoustic_simulation, - simpa_examples.run_perform_iterative_qPAI_reconstruction, simpa_examples.segmentation_loader] + simpa_examples.run_perform_iterative_qPAI_reconstruction] for example in examples: try: From e10d9380682a6a9b520a769d09319f80ad97cfac Mon Sep 17 00:00:00 2001 From: Kris Dreher Date: Mon, 29 Jul 2024 15:51:05 +0200 Subject: [PATCH 33/34] Updated recommended python version to 3.10 and removed deprecated 3.8 in favour of 3.12 --- .github/workflows/automatic_testing.yml | 2 +- README.md | 2 +- docs/source/introduction.md | 2 +- simpa/core/__init__.py | 3 ++- simpa/core/processing_components/__init__.py | 1 - simpa/core/simulation_modules/__init__.py | 1 + .../simulation_modules/acoustic_forward_module/__init__.py | 2 +- .../simulation_modules/reconstruction_module/__init__.py | 2 +- .../simulation_modules/volume_creation_module/__init__.py | 2 +- simpa/utils/deformation_manager.py | 6 ++++-- .../automatic_tests/device_tests/test_field_of_view.py | 1 - simpa_tests/automatic_tests/test_bandpass_filter.py | 1 - simpa_tests/do_coverage.py | 2 +- .../KWaveAcousticForwardConvenienceFunction.py | 2 +- 14 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/workflows/automatic_testing.yml b/.github/workflows/automatic_testing.yml index 6e6add93..63cf84e8 100644 --- a/.github/workflows/automatic_testing.yml +++ b/.github/workflows/automatic_testing.yml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] os: [ubuntu-latest, macos-latest, windows-latest] steps: diff --git a/README.md b/README.md index 9945dfb7..5960a5c3 100755 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The recommended way to install SIMPA is a manual installation from the GitHub re 4. `git pull` Now open a python instance in the 'simpa' folder that you have just downloaded. Make sure that you have your preferred -virtual environment activated (we also recommend python 3.8) +virtual environment activated (we also recommend python 3.10) 1. `pip install .` 2. Test if the installation worked by using `python` followed by `import simpa` then `exit()` diff --git a/docs/source/introduction.md b/docs/source/introduction.md index 9ce346d4..984bb3e0 100644 --- a/docs/source/introduction.md +++ b/docs/source/introduction.md @@ -18,7 +18,7 @@ The recommended way to install SIMPA is a manual installation from the GitHub re 4. `git pull` Now open a python instance in the 'simpa' folder that you have just downloaded. Make sure that you have your preferred -virtual environment activated (we also recommend python 3.8) +virtual environment activated (we also recommend python 3.10) 1. `pip install .` 2. Test if the installation worked by using `python` followed by `import simpa` then `exit()` diff --git a/simpa/core/__init__.py b/simpa/core/__init__.py index 8653966c..9a49b941 100644 --- a/simpa/core/__init__.py +++ b/simpa/core/__init__.py @@ -8,10 +8,12 @@ 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 @@ -29,4 +31,3 @@ def run(self, digital_device_twin: DigitalDeviceTwinBase): :param digital_device_twin: The digital twin that can be used by the digital device_twin. """ pass - \ No newline at end of file diff --git a/simpa/core/processing_components/__init__.py b/simpa/core/processing_components/__init__.py index c8306cbf..881a8f2d 100644 --- a/simpa/core/processing_components/__init__.py +++ b/simpa/core/processing_components/__init__.py @@ -19,4 +19,3 @@ def __init__(self, global_settings, component_settings_key: str): """ 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..c87e1e26 100644 --- a/simpa/core/simulation_modules/__init__.py +++ b/simpa/core/simulation_modules/__init__.py @@ -7,6 +7,7 @@ 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. diff --git a/simpa/core/simulation_modules/acoustic_forward_module/__init__.py b/simpa/core/simulation_modules/acoustic_forward_module/__init__.py index 2beb0eb6..e9f33d10 100644 --- a/simpa/core/simulation_modules/acoustic_forward_module/__init__.py +++ b/simpa/core/simulation_modules/acoustic_forward_module/__init__.py @@ -33,7 +33,7 @@ class AcousticForwardModelBaseAdapter(SimulationModule): 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 diff --git a/simpa/core/simulation_modules/reconstruction_module/__init__.py b/simpa/core/simulation_modules/reconstruction_module/__init__.py index 118874dc..05f8b3ef 100644 --- a/simpa/core/simulation_modules/reconstruction_module/__init__.py +++ b/simpa/core/simulation_modules/reconstruction_module/__init__.py @@ -25,7 +25,7 @@ class ReconstructionAdapterBase(SimulationModule): 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 diff --git a/simpa/core/simulation_modules/volume_creation_module/__init__.py b/simpa/core/simulation_modules/volume_creation_module/__init__.py index 7f2b3ffa..6b9f0753 100644 --- a/simpa/core/simulation_modules/volume_creation_module/__init__.py +++ b/simpa/core/simulation_modules/volume_creation_module/__init__.py @@ -20,7 +20,7 @@ class VolumeCreatorModuleBase(SimulationModule): 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 diff --git a/simpa/utils/deformation_manager.py b/simpa/utils/deformation_manager.py index 78d0a1ed..0c786a0d 100644 --- a/simpa/utils/deformation_manager.py +++ b/simpa/utils/deformation_manager.py @@ -26,7 +26,8 @@ def create_deformation_settings(bounds_mm, maximum_z_elevation_mm=1, filter_sigm # Add random permutations to the y-axis of the division knots all_scaling_value = np.multiply.outer( - np.cos(x_positions_vector / (bounds_mm[0][1] * (cosine_scaling_factor / np.pi)) - np.pi / (cosine_scaling_factor * 2)) ** 2, + np.cos(x_positions_vector / (bounds_mm[0][1] * (cosine_scaling_factor / + np.pi)) - np.pi / (cosine_scaling_factor * 2)) ** 2, np.cos(y_positions_vector / (bounds_mm[1][1] * (cosine_scaling_factor / np.pi)) - np.pi / (cosine_scaling_factor * 2)) ** 2) surface_elevations *= all_scaling_value @@ -60,7 +61,8 @@ def get_functional_from_deformation_settings(deformation_settings: dict): z_elevations_mm = deformation_settings[Tags.DEFORMATION_Z_ELEVATIONS_MM] order = "cubic" - functional_mm = RegularGridInterpolator(points=[x_coordinates_mm, y_coordinates_mm], values=z_elevations_mm, method=order) + functional_mm = RegularGridInterpolator( + points=[x_coordinates_mm, y_coordinates_mm], values=z_elevations_mm, method=order) return functional_mm diff --git a/simpa_tests/automatic_tests/device_tests/test_field_of_view.py b/simpa_tests/automatic_tests/device_tests/test_field_of_view.py index c1c79d87..cc63680c 100644 --- a/simpa_tests/automatic_tests/device_tests/test_field_of_view.py +++ b/simpa_tests/automatic_tests/device_tests/test_field_of_view.py @@ -101,4 +101,3 @@ def test_symmetric_with_odd_number_of_elements(self): self.assertAlmostEqual(ydim_end, 40) self.assertAlmostEqual(zdim_start, 0) self.assertAlmostEqual(zdim_end, 0) - diff --git a/simpa_tests/automatic_tests/test_bandpass_filter.py b/simpa_tests/automatic_tests/test_bandpass_filter.py index c499e2fd..404527d1 100644 --- a/simpa_tests/automatic_tests/test_bandpass_filter.py +++ b/simpa_tests/automatic_tests/test_bandpass_filter.py @@ -260,4 +260,3 @@ def test_butter_filter_with_random_signal(self, show_figure_on_screen=False): if show_figure_on_screen: self.visualize_filtered_spectrum(FILTERED_SIGNAL) - diff --git a/simpa_tests/do_coverage.py b/simpa_tests/do_coverage.py index cf9c4f69..88490637 100644 --- a/simpa_tests/do_coverage.py +++ b/simpa_tests/do_coverage.py @@ -25,4 +25,4 @@ cov.html_report(directory="../docs/test_coverage") # Exit with an appropriate code based on the test results -sys.exit(not result.wasSuccessful()) \ No newline at end of file +sys.exit(not result.wasSuccessful()) diff --git a/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py b/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py index 93b0a5e6..a9157c33 100644 --- a/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py +++ b/simpa_tests/manual_tests/acoustic_forward_models/KWaveAcousticForwardConvenienceFunction.py @@ -114,7 +114,7 @@ def test_convenience_function(self): get_detection_geometry(), speed_of_sound=1540, density=1000, alpha_coeff=0.0, spacing_mm=0.25) - + # reconstruct the time series data to compare it with initial pressure self.settings.set_reconstruction_settings({ Tags.RECONSTRUCTION_MODE: Tags.RECONSTRUCTION_MODE_PRESSURE, From faef9615dbb1bf135c977c98e82a8e405bb8e578 Mon Sep 17 00:00:00 2001 From: Kris Dreher Date: Mon, 29 Jul 2024 16:02:41 +0200 Subject: [PATCH 34/34] Updated pyproject.toml --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5b847014..be5efb74 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,11 +13,11 @@ description = "Simulation and Image Processing for Photonics and Acoustics" license = {text = "MIT"} readme = "README.md" keywords = ["simulation", "photonics", "acoustics"] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ "matplotlib>=3.5.0", # Uses PSF-License (MIT compatible) "numpy>=1.21.4", # Uses BSD-License (MIT compatible) - "scipy>=1.7.2,<1.14.0", # Uses BSD-like-License (MIT compatible) + "scipy>=1.13.0", # Uses BSD-like-License (MIT compatible) "pynrrd>=0.4.2", # Uses MIT-License (MIT compatible) "scikit-image>=0.18.3", # Uses BSD-License (MIT compatible) "xmltodict>=0.12.0", # Uses MIT-License (MIT compatible)