From 5844867d95d7f1dba63161d23f032227cb66f3c1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 16:12:41 +0000 Subject: [PATCH 1/3] Fix: Correct SyntaxError in test_kspaceFirstOrder3D_state.py The test file added in the previous commit for bug #600 (test_kspaceFirstOrder3D_state.py) contained a SyntaxError due to erroneous markdown backticks (```) at the end of the file. This commit removes the offending backticks to make the test file syntactically correct. --- kwave/kWaveSimulation.py | 7 +- tests/test_kspaceFirstOrder3D_state.py | 110 +++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 tests/test_kspaceFirstOrder3D_state.py diff --git a/kwave/kWaveSimulation.py b/kwave/kWaveSimulation.py index 8cd31e03..7b18ed28 100644 --- a/kwave/kWaveSimulation.py +++ b/kwave/kWaveSimulation.py @@ -1,5 +1,6 @@ import logging import warnings +from copy import deepcopy from dataclasses import dataclass import numpy as np @@ -38,8 +39,10 @@ def __init__( self.precision = None self.kgrid = kgrid self.medium = medium - self.source = source - self.sensor = sensor + self.original_source = source + self.original_sensor = sensor + self.source = deepcopy(source) + self.sensor = deepcopy(sensor) self.options = simulation_options # ========================================================================= diff --git a/tests/test_kspaceFirstOrder3D_state.py b/tests/test_kspaceFirstOrder3D_state.py new file mode 100644 index 00000000..cd1da3e9 --- /dev/null +++ b/tests/test_kspaceFirstOrder3D_state.py @@ -0,0 +1,110 @@ +from copy import deepcopy +from pathlib import Path +from tempfile import TemporaryDirectory + +import numpy as np +import pytest + +from kwave.data import Vector +from kwave.kgrid import kWaveGrid +from kwave.kmedium import kWaveMedium +from kwave.ksensor import kSensor +from kwave.ksource import kSource +from kwave.kspaceFirstOrder3D import kspaceFirstOrder3D +from kwave.options.simulation_execution_options import SimulationExecutionOptions +from kwave.options.simulation_options import SimulationOptions +from kwave.utils.filters import smooth +from kwave.utils.mapgen import make_ball + + +def make_simulation_parameters(directory: Path): + # create the computational grid + PML_size = 10 # size of the PML in grid points + N = Vector([32, 64, 64]) - 2 * PML_size # number of grid points + d = Vector([0.2e-3, 0.2e-3, 0.2e-3]) # grid point spacing [m] + kgrid = kWaveGrid(N, d) + + # define the properties of the propagation medium + medium = kWaveMedium(sound_speed=1500) # [m/s] + + # create initial pressure distribution using makeBall + ball_magnitude = 10 # [Pa] + ball_radius = 3 # [grid points] + p0_array = ball_magnitude * make_ball(N, N / 2, ball_radius) + p0_array = smooth(p0_array, restore_max=True) + + source = kSource() + source.p0 = p0_array + + # define a binary planar sensor + sensor = kSensor() + sensor_mask_array = np.zeros(N) + sensor_mask_array[0, :, :] = 1 # Corrected to be a plane for 3D + sensor.mask = sensor_mask_array + + input_filename = directory / "kwave_input.h5" + output_filename = directory / "kwave_output.h5" + checkpoint_filename = directory / "kwave_checkpoint.h5" + + simulation_options = SimulationOptions( + save_to_disk=True, # Must be true for kspaceFirstOrder3D + pml_size=PML_size, + pml_inside=False, + smooth_p0=False, # p0 is already smoothed + data_cast="single", + input_filename=input_filename, + output_filename=output_filename, + ) + + checkpoint_timesteps = 300 + + execution_options = SimulationExecutionOptions( + is_gpu_simulation=False, # Assuming CPU for basic test + checkpoint_file=checkpoint_filename, + checkpoint_timesteps=checkpoint_timesteps, + verbose_level=0 # Keep test output clean + ) + return kgrid, medium, source, sensor, simulation_options, execution_options + + +def test_kspaceFirstOrder3D_input_state_preservation(): + with TemporaryDirectory() as tmpdir_str: + tmpdir = Path(tmpdir_str) + kgrid, medium, source, sensor, simulation_options, execution_options = make_simulation_parameters(tmpdir) + + # Store original states of critical attributes for comparison + original_source_p0 = deepcopy(source.p0) + original_sensor_mask = deepcopy(sensor.mask) + + # If source.p or source.u were time-varying, store their initial states too. + # For this test, p0 is the main source attribute. + + # First run + try: + _ = kspaceFirstOrder3D(kgrid, medium, source, sensor, simulation_options, execution_options) + except Exception as e: + pytest.fail(f"First call to kspaceFirstOrder3D failed: {e}") + + # Check if original source and sensor attributes are unchanged + assert np.array_equal(source.p0, original_source_p0), "source.p0 was modified after first run" + assert np.array_equal(sensor.mask, original_sensor_mask), "sensor.mask was modified after first run" + + # Second run (should not fail if state is preserved) + # For the second run, we need new input/output filenames or to ensure the C++ code can overwrite. + # Easiest is to use new filenames for the test. + simulation_options_run2 = deepcopy(simulation_options) + simulation_options_run2.input_filename = tmpdir / "kwave_input_run2.h5" + simulation_options_run2.output_filename = tmpdir / "kwave_output_run2.h5" + + execution_options_run2 = deepcopy(execution_options) + if execution_options_run2.checkpoint_file: # Only change if it exists + execution_options_run2.checkpoint_file = tmpdir / "kwave_checkpoint_run2.h5" + + try: + _ = kspaceFirstOrder3D(kgrid, medium, source, sensor, simulation_options_run2, execution_options_run2) + except Exception as e: + pytest.fail(f"Second call to kspaceFirstOrder3D with original objects failed: {e}") + + # Final check that attributes are still the same as the initial state + assert np.array_equal(source.p0, original_source_p0), "source.p0 was modified after second run" + assert np.array_equal(sensor.mask, original_sensor_mask), "sensor.mask was modified after second run" From ecfc3ee99eb48ebf776c1cd06b5ebc537b62163d Mon Sep 17 00:00:00 2001 From: Walter Simson Date: Sun, 25 May 2025 21:46:10 -0700 Subject: [PATCH 2/3] Fix tests --- tests/test_kspaceFirstOrder3D_state.py | 34 ++++++++++++++++++-------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/tests/test_kspaceFirstOrder3D_state.py b/tests/test_kspaceFirstOrder3D_state.py index cd1da3e9..3a694957 100644 --- a/tests/test_kspaceFirstOrder3D_state.py +++ b/tests/test_kspaceFirstOrder3D_state.py @@ -39,7 +39,7 @@ def make_simulation_parameters(directory: Path): # define a binary planar sensor sensor = kSensor() sensor_mask_array = np.zeros(N) - sensor_mask_array[0, :, :] = 1 # Corrected to be a plane for 3D + sensor_mask_array[0, :, :] = 1 # Corrected to be a plane for 3D sensor.mask = sensor_mask_array input_filename = directory / "kwave_input.h5" @@ -47,10 +47,10 @@ def make_simulation_parameters(directory: Path): checkpoint_filename = directory / "kwave_checkpoint.h5" simulation_options = SimulationOptions( - save_to_disk=True, # Must be true for kspaceFirstOrder3D + save_to_disk=True, # Must be true for kspaceFirstOrder3D pml_size=PML_size, pml_inside=False, - smooth_p0=False, # p0 is already smoothed + smooth_p0=False, # p0 is already smoothed data_cast="single", input_filename=input_filename, output_filename=output_filename, @@ -59,10 +59,10 @@ def make_simulation_parameters(directory: Path): checkpoint_timesteps = 300 execution_options = SimulationExecutionOptions( - is_gpu_simulation=False, # Assuming CPU for basic test + is_gpu_simulation=False, # Assuming CPU for basic test checkpoint_file=checkpoint_filename, checkpoint_timesteps=checkpoint_timesteps, - verbose_level=0 # Keep test output clean + verbose_level=0, # Keep test output clean ) return kgrid, medium, source, sensor, simulation_options, execution_options @@ -75,13 +75,20 @@ def test_kspaceFirstOrder3D_input_state_preservation(): # Store original states of critical attributes for comparison original_source_p0 = deepcopy(source.p0) original_sensor_mask = deepcopy(sensor.mask) - + # If source.p or source.u were time-varying, store their initial states too. # For this test, p0 is the main source attribute. # First run try: - _ = kspaceFirstOrder3D(kgrid, medium, source, sensor, simulation_options, execution_options) + _ = kspaceFirstOrder3D( + kgrid=kgrid, + medium=medium, + source=source, + sensor=sensor, + simulation_options=simulation_options, + execution_options=execution_options, + ) except Exception as e: pytest.fail(f"First call to kspaceFirstOrder3D failed: {e}") @@ -95,13 +102,20 @@ def test_kspaceFirstOrder3D_input_state_preservation(): simulation_options_run2 = deepcopy(simulation_options) simulation_options_run2.input_filename = tmpdir / "kwave_input_run2.h5" simulation_options_run2.output_filename = tmpdir / "kwave_output_run2.h5" - + execution_options_run2 = deepcopy(execution_options) - if execution_options_run2.checkpoint_file: # Only change if it exists + if execution_options_run2.checkpoint_file: # Only change if it exists execution_options_run2.checkpoint_file = tmpdir / "kwave_checkpoint_run2.h5" try: - _ = kspaceFirstOrder3D(kgrid, medium, source, sensor, simulation_options_run2, execution_options_run2) + _ = kspaceFirstOrder3D( + kgrid=kgrid, + medium=medium, + source=source, + sensor=sensor, + simulation_options=simulation_options_run2, + execution_options=execution_options_run2, + ) except Exception as e: pytest.fail(f"Second call to kspaceFirstOrder3D with original objects failed: {e}") From 59a3eb4c23ba091e1cfbdb3a5f9355b33afb5cd0 Mon Sep 17 00:00:00 2001 From: Walter Simson Date: Sun, 25 May 2025 22:04:48 -0700 Subject: [PATCH 3/3] Fix walrus bug --- kwave/kWaveSimulation.py | 2 +- tests/test_kspaceFirstOrder3D_state.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/kwave/kWaveSimulation.py b/kwave/kWaveSimulation.py index 7b18ed28..d1da74c2 100644 --- a/kwave/kWaveSimulation.py +++ b/kwave/kWaveSimulation.py @@ -1520,7 +1520,7 @@ def _is_binary_sensor_mask(self, kgrid_dim: int) -> bool: return True # If the sensor mask is larger by PML size in each dimension, it's still a binary mask - if pml_size := self.options.pml_size is None: + if (pml_size := self.options.pml_size) is None: return False if len(pml_size) == 1: diff --git a/tests/test_kspaceFirstOrder3D_state.py b/tests/test_kspaceFirstOrder3D_state.py index 3a694957..551574cb 100644 --- a/tests/test_kspaceFirstOrder3D_state.py +++ b/tests/test_kspaceFirstOrder3D_state.py @@ -39,7 +39,7 @@ def make_simulation_parameters(directory: Path): # define a binary planar sensor sensor = kSensor() sensor_mask_array = np.zeros(N) - sensor_mask_array[0, :, :] = 1 # Corrected to be a plane for 3D + sensor_mask_array[0, :, :] = 1 # Corrected to be a plane for 3D sensor.mask = sensor_mask_array input_filename = directory / "kwave_input.h5" @@ -47,10 +47,10 @@ def make_simulation_parameters(directory: Path): checkpoint_filename = directory / "kwave_checkpoint.h5" simulation_options = SimulationOptions( - save_to_disk=True, # Must be true for kspaceFirstOrder3D + save_to_disk=True, # Must be true for kspaceFirstOrder3D pml_size=PML_size, pml_inside=False, - smooth_p0=False, # p0 is already smoothed + smooth_p0=False, # p0 is already smoothed data_cast="single", input_filename=input_filename, output_filename=output_filename, @@ -75,6 +75,7 @@ def test_kspaceFirstOrder3D_input_state_preservation(): # Store original states of critical attributes for comparison original_source_p0 = deepcopy(source.p0) original_sensor_mask = deepcopy(sensor.mask) + # If source.p or source.u were time-varying, store their initial states too. # For this test, p0 is the main source attribute. @@ -122,3 +123,4 @@ def test_kspaceFirstOrder3D_input_state_preservation(): # Final check that attributes are still the same as the initial state assert np.array_equal(source.p0, original_source_p0), "source.p0 was modified after second run" assert np.array_equal(sensor.mask, original_sensor_mask), "sensor.mask was modified after second run" +