Open
Description
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.