diff --git a/simpa/__init__.py b/simpa/__init__.py index b336a76f..c25f2ebb 100644 --- a/simpa/__init__.py +++ b/simpa/__init__.py @@ -10,7 +10,7 @@ __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 52c90245..157ff678 100644 --- a/simpa/core/simulation.py +++ b/simpa/core/simulation.py @@ -56,7 +56,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 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 a67116e6..aad5adf1 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 @@ -170,7 +170,7 @@ def reconstruction_algorithm(self, time_series_sensor_data, detection_geometry): matlab_binary_path = self.component_settings[Tags.ACOUSTIC_MODEL_BINARY_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 62dc1f22..1459ba0b 100644 --- a/simpa/utils/matlab.py +++ b/simpa/utils/matlab.py @@ -21,7 +21,7 @@ def generate_matlab_cmd(matlab_binary_path: str, simulation_script_path: str, da :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 diff --git a/simpa_examples/minimal_optical_simulation.py b/simpa_examples/minimal_optical_simulation.py index c3646f22..f1cf3a71 100644 --- a/simpa_examples/minimal_optical_simulation.py +++ b/simpa_examples/minimal_optical_simulation.py @@ -152,7 +152,7 @@ def __init__(self): device = ExampleDeviceSlitIlluminationLinearDetector() - sp.simulate(pipeline, settings, device) + sp.simulate(pipeline, settings, device, logging_level=Tags.LOGGER_ERROR) if Tags.WAVELENGTH in settings: WAVELENGTH = settings[Tags.WAVELENGTH] diff --git a/simpa_examples/optical_and_acoustic_simulation.py b/simpa_examples/optical_and_acoustic_simulation.py index 2346d921..e853805a 100644 --- a/simpa_examples/optical_and_acoustic_simulation.py +++ b/simpa_examples/optical_and_acoustic_simulation.py @@ -93,7 +93,7 @@ def create_example_tissue(): 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.WAVELENGTHS: [700], Tags.DO_FILE_COMPRESSION: True, Tags.DO_IPASC_EXPORT: True } @@ -115,7 +115,7 @@ def create_example_tissue(): }) settings.set_acoustic_settings({ - Tags.ACOUSTIC_SIMULATION_3D: False, + # 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", diff --git a/simpa_tests/automatic_tests/test_additional_flags.py b/simpa_tests/automatic_tests/test_additional_flags.py index ca1c4731..ce0e9b33 100644 --- a/simpa_tests/automatic_tests/test_additional_flags.py +++ b/simpa_tests/automatic_tests/test_additional_flags.py @@ -1,24 +1,30 @@ +# SPDX-FileCopyrightText: 2021 Division of Intelligent Medical Systems, DKFZ +# SPDX-FileCopyrightText: 2021 Janek Groehl +# SPDX-License-Identifier: MIT + 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) + 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") - + 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: '.', @@ -27,8 +33,9 @@ def test_get_cmd_mcx_adapter(self): 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") - + 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 @@ -36,18 +43,20 @@ def test_get_cmd_kwave_adapter(self): 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") + 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()) + 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") - - + 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 + unittest.main() diff --git a/simpa_tests/manual_tests/digital_device_twins/VisualiseDevices.py b/simpa_tests/manual_tests/digital_device_twins/VisualiseDevices.py index a88dcebb..02b4ebe9 100644 --- a/simpa_tests/manual_tests/digital_device_twins/VisualiseDevices.py +++ b/simpa_tests/manual_tests/digital_device_twins/VisualiseDevices.py @@ -35,6 +35,7 @@ def visualise_result(self, show_figure_on_screen=True, save_path=None): sp.visualise_device(sp.RSOMExplorerP50(device_position_mm=np.asarray([50, 10, 0])), figure_save_path[2]) + if __name__ == "__main__": test = DeviceVisualisationTest() test.run_test(show_figure_on_screen=False) diff --git a/simpa_tests/manual_tests/executables/MATLABAdditionalFlags.py b/simpa_tests/manual_tests/executables/MATLABAdditionalFlags.py index 4e63a889..1f114949 100644 --- a/simpa_tests/manual_tests/executables/MATLABAdditionalFlags.py +++ b/simpa_tests/manual_tests/executables/MATLABAdditionalFlags.py @@ -34,7 +34,7 @@ def setup(self): """ path_manager = PathManager() - + self.settings = Settings({ Tags.WAVELENGTHS: [800], Tags.WAVELENGTH: 800, @@ -79,12 +79,12 @@ def setup(self): 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]))) + 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' + 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 @@ -100,11 +100,11 @@ def test_execution_of_additional_flag(self): :raises FileNotFoundError: if log file does not exist at expected location """ - - # perform cleaning before test + + # 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() @@ -112,24 +112,26 @@ def test_execution_of_additional_flag(self): # 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.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}") + raise FileNotFoundError( + f"Log file wasn't created with correct last given name at expected path {self.output_file_name}") def perform_test(self): """ @@ -139,8 +141,9 @@ def perform_test(self): 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 - + 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 4bf7da1d..b381045e 100644 --- a/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py +++ b/simpa_tests/manual_tests/executables/MCXAdditionalFlags.py @@ -31,7 +31,7 @@ def setup(self): """ path_manager = PathManager() - + self.settings = Settings({ Tags.WAVELENGTHS: [800], Tags.WAVELENGTH: 800, @@ -63,7 +63,7 @@ def setup(self): 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' + self.output_file_name = f'{self.output_name}.log' def run_simulation(self): # run pipeline including volume creation and optical mcx simulation @@ -78,11 +78,11 @@ def test_execution_of_additional_flag(self): :raises FileNotFoundError: if log file does not exist at expected location """ - - # perform cleaning before test + + # 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() @@ -90,26 +90,27 @@ def test_execution_of_additional_flag(self): # 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 """ output_name = f'{os.path.join(self.settings[Tags.SIMULATION_PATH], self.settings[Tags.VOLUME_NAME])}_output' - output_file_name = f'{output_name}.log' + output_file_name = f'{output_name}.log' - # perform cleaning before test + # 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}") + raise FileNotFoundError( + f"Log file wasn't created with correct last given name at expected path {output_file_name}") def perform_test(self): """ @@ -119,8 +120,9 @@ def perform_test(self): 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 - + pass # no figures are created that could be visualized + + if __name__ == '__main__': test = MCXAdditionalFlags() test.run_test(show_figure_on_screen=False) diff --git a/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithinHomogenousMedium.py b/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithinHomogenousMedium.py index 806863bd..b34937bb 100644 --- a/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithinHomogenousMedium.py +++ b/simpa_tests/manual_tests/optical_forward_models/AbsorptionAndScatteringWithinHomogenousMedium.py @@ -38,7 +38,7 @@ os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" -class TestAbsorptionAndScatteringWithInifinitesimalSlabExperiment(ManualIntegrationTestClass): +class TestAbsorptionAndScatteringWithinHomogeneousMedium(ManualIntegrationTestClass): def create_example_tissue(self, scattering_value=1e-30, absorption_value=1e-30, anisotropy_value=0.0): """ @@ -101,115 +101,115 @@ def test_low_scattering(self): Here, the slab is 10 mm long, mua and mus are both used with values of 0.05 mm^-1, so that mua+mus=0.1 mm^-1. We expect a decay ratio of e^1. """ - return self.test_simultion(absorption_value_1=0.01, - absorption_value_2=0.01, - scattering_value_1=1.0, - scattering_value_2=10.0, - anisotropy_value_1=0.0, - anisotropy_value_2=0.9, - title="Low Abs. Low Scat.") + return self.test_simulation(absorption_value_1=0.01, + absorption_value_2=0.01, + scattering_value_1=1.0, + scattering_value_2=10.0, + anisotropy_value_1=0.0, + anisotropy_value_2=0.9, + title="Low Abs. Low Scat.") def test_medium_scattering(self): """ Here, the slab is 10 mm long, mua and mus are both used with values of 0.05 mm^-1, so that mua+mus=0.1 mm^-1. We expect a decay ratio of e^1. """ - return self.test_simultion(absorption_value_1=0.01, - absorption_value_2=0.01, - scattering_value_1=10.0, - scattering_value_2=100.0, - anisotropy_value_1=0.0, - anisotropy_value_2=0.9, - title="Low Abs. Medium Scat.") + return self.test_simulation(absorption_value_1=0.01, + absorption_value_2=0.01, + scattering_value_1=10.0, + scattering_value_2=100.0, + anisotropy_value_1=0.0, + anisotropy_value_2=0.9, + title="Low Abs. Medium Scat.") def test_high_scattering_090(self): """ Here, the slab is 10 mm long, mua and mus are both used with values of 0.05 mm^-1, so that mua+mus=0.1 mm^-1. We expect a decay ratio of e^1. """ - return self.test_simultion(absorption_value_1=0.01, - absorption_value_2=0.01, - scattering_value_1=50.0, - scattering_value_2=500.0, - anisotropy_value_1=0.0, - anisotropy_value_2=0.9, - title="Anisotropy 0.9") + return self.test_simulation(absorption_value_1=0.01, + absorption_value_2=0.01, + scattering_value_1=50.0, + scattering_value_2=500.0, + anisotropy_value_1=0.0, + anisotropy_value_2=0.9, + title="Anisotropy 0.9") def simulate_perfect_result(self): """ Here, the slab is 10 mm long, mua and mus are both used with values of 0.05 mm^-1, so that mua+mus=0.1 mm^-1. We expect a decay ratio of e^1. """ - return self.test_simultion(absorption_value_1=0.01, - absorption_value_2=0.01, - scattering_value_1=50.0, - scattering_value_2=50.0, - anisotropy_value_1=0.0, - anisotropy_value_2=0.0, - title="Ideal Result") + return self.test_simulation(absorption_value_1=0.01, + absorption_value_2=0.01, + scattering_value_1=50.0, + scattering_value_2=50.0, + anisotropy_value_1=0.0, + anisotropy_value_2=0.0, + title="Ideal Result") def test_high_scattering_075(self): """ Here, the slab is 10 mm long, mua and mus are both used with values of 0.05 mm^-1, so that mua+mus=0.1 mm^-1. We expect a decay ratio of e^1. """ - return self.test_simultion(absorption_value_1=0.01, - absorption_value_2=0.01, - scattering_value_1=50.0, - scattering_value_2=200.0, - anisotropy_value_1=0.0, - anisotropy_value_2=0.75, - title="Anisotropy 0.75") + return self.test_simulation(absorption_value_1=0.01, + absorption_value_2=0.01, + scattering_value_1=50.0, + scattering_value_2=200.0, + anisotropy_value_1=0.0, + anisotropy_value_2=0.75, + title="Anisotropy 0.75") def test_high_scattering_025(self): """ Here, the slab is 10 mm long, mua and mus are both used with values of 0.05 mm^-1, so that mua+mus=0.1 mm^-1. We expect a decay ratio of e^1. """ - return self.test_simultion(absorption_value_1=0.01, - absorption_value_2=0.01, - scattering_value_1=50.0, - scattering_value_2=66.666666666666667, - anisotropy_value_1=0.0, - anisotropy_value_2=0.25, - title="Anisotropy 0.25") + return self.test_simulation(absorption_value_1=0.01, + absorption_value_2=0.01, + scattering_value_1=50.0, + scattering_value_2=66.666666666666667, + anisotropy_value_1=0.0, + anisotropy_value_2=0.25, + title="Anisotropy 0.25") def test_ignore_mcx_anisotropy_025(self): """ Here, the slab is 10 mm long, mua and mus are both used with values of 0.05 mm^-1, so that mua+mus=0.1 mm^-1. We expect a decay ratio of e^1. """ - return self.test_simultion(absorption_value_1=0.01, - absorption_value_2=0.01, - scattering_value_1=50.0, - scattering_value_2=66.666666666666667, - anisotropy_value_1=0.0, - anisotropy_value_2=0.25, - title="Ignore MCX Anisotropy 0.25", - use_mcx_anisotropy=False) + return self.test_simulation(absorption_value_1=0.01, + absorption_value_2=0.01, + scattering_value_1=50.0, + scattering_value_2=66.666666666666667, + anisotropy_value_1=0.0, + anisotropy_value_2=0.25, + title="Ignore MCX Anisotropy 0.25", + use_mcx_anisotropy=False) def test_ignore_mcx_anisotropy_075(self): """ Here, the slab is 10 mm long, mua and mus are both used with values of 0.05 mm^-1, so that mua+mus=0.1 mm^-1. We expect a decay ratio of e^1. """ - return self.test_simultion(absorption_value_1=0.01, - absorption_value_2=0.01, - scattering_value_1=50.0, - scattering_value_2=200.0, - anisotropy_value_1=0.0, - anisotropy_value_2=0.75, - title="Ignore MCX Anisotropy 0.75", - use_mcx_anisotropy=False) - - def test_simultion(self, scattering_value_1=1e-30, - absorption_value_1=1e-30, - anisotropy_value_1=1.0, - scattering_value_2=1e-30, - absorption_value_2=1e-30, - anisotropy_value_2=1.0, - title="Medium Abs. High Scat.", - use_mcx_anisotropy=True): + return self.test_simulation(absorption_value_1=0.01, + absorption_value_2=0.01, + scattering_value_1=50.0, + scattering_value_2=200.0, + anisotropy_value_1=0.0, + anisotropy_value_2=0.75, + title="Ignore MCX Anisotropy 0.75", + use_mcx_anisotropy=False) + + def test_simulation(self, scattering_value_1=1e-30, + absorption_value_1=1e-30, + anisotropy_value_1=1.0, + scattering_value_2=1e-30, + absorption_value_2=1e-30, + anisotropy_value_2=1.0, + title="Medium Abs. High Scat.", + use_mcx_anisotropy=True): # RUN SIMULATION 1 @@ -220,7 +220,10 @@ def test_simultion(self, scattering_value_1=1e-30, anisotropy_value=anisotropy_value_1) }) - self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = anisotropy_value_1 + if use_mcx_anisotropy: + self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = anisotropy_value_1 + else: + self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = anisotropy_value_2 pipeline = [ ModelBasedVolumeCreationAdapter(self.settings), @@ -241,10 +244,7 @@ def test_simultion(self, scattering_value_1=1e-30, anisotropy_value=anisotropy_value_2) }) - if use_mcx_anisotropy: - self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = anisotropy_value_2 - else: - self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = 0.9 + self.settings.get_optical_settings()[Tags.MCX_ASSUMED_ANISOTROPY] = anisotropy_value_2 pipeline = [ ModelBasedVolumeCreationAdapter(self.settings), @@ -309,5 +309,5 @@ def perform_test(self): if __name__ == '__main__': - test = TestAbsorptionAndScatteringWithInifinitesimalSlabExperiment() + test = TestAbsorptionAndScatteringWithinHomogeneousMedium() test.run_test(show_figure_on_screen=False)