k-wave-python icon indicating copy to clipboard operation
k-wave-python copied to clipboard

Running kspaceFirstOrder3D alters it's input source and sensor parameters

Open precicely opened this issue 8 months ago • 0 comments

Describe the bug kspaceFirstOrder3D changes the contents and the formats of 2 of it's input parameters, namely source and sensor. As a consequence, one cannot run kspaceFirstOrder3D twice with the same input as it bugs out. This is apparent when trying to run kspaceFirstOrder3D with checkpointing enabled.

To Reproduce

from copy import copy
from pathlib import Path
from tempfile import TemporaryDirectory

import numpy as np

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):
    """
    See the 3D FFT Reconstruction For A Planar Sensor example for context.
    """
    scale = 2

    # create the computational grid
    PML_size = 10  # size of the PML in grid points
    N = Vector([32, 64, 64]) * scale - 2 * PML_size  # number of grid points
    d = Vector([0.2e-3, 0.2e-3, 0.2e-3]) / scale  # 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 * scale  # [grid points]
    p0 = ball_magnitude * make_ball(N, N / 2, ball_radius)
    p0 = smooth(p0, restore_max=True)

    source = kSource()
    source.p0 = p0

    # define a binary planar sensor
    sensor = kSensor()
    sensor_mask = np.zeros(N)
    sensor_mask[0] = 1
    sensor.mask = sensor_mask

    input_filename = directory / "kwave_input.h5"
    output_filename = directory / "kwave_output.h5"
    checkpoint_filename = directory / "kwave_checkpoint.h5"

    # set the input arguments
    simulation_options = SimulationOptions(
        save_to_disk=True,
        pml_size=PML_size,
        pml_inside=False,
        smooth_p0=False,
        data_cast="single",
        input_filename=input_filename,
        output_filename=output_filename,
    )

    checkpoint_timesteps = 300  # approximately half way through the simulation

    execution_options = SimulationExecutionOptions(
        is_gpu_simulation=False,
        checkpoint_file=checkpoint_filename,
        checkpoint_timesteps=checkpoint_timesteps,
        verbose_level=2
    )
    return kgrid, medium, source, sensor, simulation_options, execution_options



def main():

    with TemporaryDirectory() as tmpdir:
        tmpdir = Path(tmpdir)
        # create the simulation parameters
        kgrid, medium, source, sensor, simulation_options, execution_options = make_simulation_parameters(tmpdir)

        # copy sourrce and sensor
        source2 = copy(source)
        sensor2 = copy(sensor)

        sensor_data = kspaceFirstOrder3D(kgrid, source, sensor, medium, simulation_options, execution_options)
        
        # the line below will fail (commented out for now)
        # sensor_data = kspaceFirstOrder3D(kgrid, source, sensor, medium, simulation_options, execution_options)
       
        # This works but needs to use copies of source and sensor
        sensor_data = kspaceFirstOrder3D(kgrid, source2, sensor2, medium, simulation_options, execution_options)


if __name__ == "__main__":
    main()

Expected behavior kspaceFirstOrder3D should not (silently) alter it's inputs.

Context Came across this issue as I was developing the checkpointing code.

precicely avatar May 02 '25 06:05 precicely