Skip to content

Commit ef735e0

Browse files
committed
Collect & Compare Example Inputs/Outputs
1 parent 6d1737b commit ef735e0

File tree

15 files changed

+612
-52
lines changed

15 files changed

+612
-52
lines changed

Makefile

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,29 @@ KWAVE_MATLAB_PATH = $(abspath ../k-wave) # Get absolute path of k-wave directory
1010

1111
# Define the artifact directory
1212
COLLECTED_VALUES_DIR = $(abspath tests/matlab_test_data_collectors/python_testers/collectedValues)
13+
EXAMPLE_OUTPUT_DIR = /tmp/example_runs
14+
COMPARE_OUTPUT_DIR_A ?= /tmp/example_runs
15+
COMPARE_OUTPUT_DIR_B ?= /tmp/example_runs_1
1316

1417
# Default target
1518
all: run-examples test
1619

1720
# Target to run all examples
1821
run-examples:
1922
@echo "Running all examples..."
20-
@MPLBACKEND=Agg $(PYTHON) run_examples.py
23+
@PYTHONPATH="$(CURDIR):$$PYTHONPATH" MPLBACKEND=Agg $(PYTHON) run_examples.py
24+
25+
# Target to run all examples and collect non-dated HDF5 inputs/outputs
26+
run-examples-no-dates:
27+
@echo "Running all examples with non-dated HDF5 filenames..."
28+
@rm -rf $(EXAMPLE_OUTPUT_DIR)
29+
@PYTHONPATH="$(CURDIR):$$PYTHONPATH" KWAVE_USE_DATED_FILENAMES=0 MPLBACKEND=Agg $(PYTHON) run_examples.py
30+
@echo "Collected example files in $(EXAMPLE_OUTPUT_DIR)"
31+
32+
# Target to compare two example output roots
33+
compare-example-outputs:
34+
@echo "Comparing HDF outputs: $(COMPARE_OUTPUT_DIR_A) vs $(COMPARE_OUTPUT_DIR_B)"
35+
@PYTHONPATH="$(CURDIR):$$PYTHONPATH" $(PYTHON) scripts/compare_example_outputs.py $(COMPARE_OUTPUT_DIR_A) $(COMPARE_OUTPUT_DIR_B)
2136

2237
# Target to run pytest, which depends on running the MATLAB script first
2338
test: $(COLLECTED_VALUES_DIR)
@@ -41,4 +56,4 @@ clean-collected_values:
4156
@echo "Cleaning collected values directory..."
4257
@rm -rf $(COLLECTED_VALUES_DIR)
4358

44-
.PHONY: all run-examples run-tests clean-python clean-collected_values clean
59+
.PHONY: all run-examples run-examples-no-dates compare-example-outputs run-tests clean-python clean-collected_values clean

examples/checkpointing/checkpoint.py

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
from copy import copy
21
from pathlib import Path
3-
from tempfile import TemporaryDirectory
2+
from shutil import copy2
43

54
import numpy as np
65

@@ -15,6 +14,8 @@
1514
from kwave.utils.filters import smooth
1615
from kwave.utils.mapgen import make_ball
1716

17+
EXAMPLE_OUTPUT_DIR = Path("/tmp/example_runs/checkpointing")
18+
1819
# This script demonstrates how to use the checkpointing feature of k-Wave.
1920
# It runs the same simulation twice, with the second run starting from a
2021
# checkpoint file created during the first run.
@@ -30,6 +31,17 @@
3031
# Note: checkpoint timesteps and checkpoint interval must be integers.
3132

3233

34+
def _next_available_path(path: Path) -> Path:
35+
if not path.exists():
36+
return path
37+
suffix = 1
38+
while True:
39+
candidate = path.with_name(f"{path.stem}_{suffix}{path.suffix}")
40+
if not candidate.exists():
41+
return candidate
42+
suffix += 1
43+
44+
3345
def make_simulation_parameters(directory: Path, checkpoint_timesteps: int):
3446
"""
3547
See the 3D FFT Reconstruction For A Planar Sensor example for context.
@@ -71,6 +83,7 @@ def make_simulation_parameters(directory: Path, checkpoint_timesteps: int):
7183
pml_inside=False,
7284
smooth_p0=False,
7385
data_cast="single",
86+
allow_file_overwrite=True,
7487
input_filename=input_filename,
7588
output_filename=output_filename,
7689
)
@@ -84,23 +97,31 @@ def main():
8497
# the halfway point.
8598
checkpoint_timesteps = 106
8699

87-
with TemporaryDirectory() as tmpdir:
88-
tmpdir = Path(tmpdir)
89-
# create the simulation parameters for the 1st run
90-
kgrid, medium, source, sensor, simulation_options, execution_options = make_simulation_parameters(
91-
directory=tmpdir, checkpoint_timesteps=checkpoint_timesteps
92-
)
93-
kspaceFirstOrder3D(kgrid, source, sensor, medium, simulation_options, execution_options)
94-
print("Temporary directory contains 3 files (including checkpoint):")
95-
print("\t-", "\n\t- ".join([f.name for f in tmpdir.glob("*.h5")]))
96-
97-
# create the simulation parameters for the 2nd run
98-
kgrid, medium, source, sensor, simulation_options, execution_options = make_simulation_parameters(
99-
directory=tmpdir, checkpoint_timesteps=checkpoint_timesteps
100-
)
101-
kspaceFirstOrder3D(kgrid, source, sensor, medium, simulation_options, execution_options)
102-
print("Temporary directory contains 2 files (checkpoint has been deleted):")
103-
print("\t-", "\n\t- ".join([f.name for f in tmpdir.glob("*.h5")]))
100+
EXAMPLE_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
101+
for filename in ("kwave_input.h5", "kwave_output.h5", "kwave_checkpoint.h5"):
102+
(EXAMPLE_OUTPUT_DIR / filename).unlink(missing_ok=True)
103+
104+
# create the simulation parameters for the 1st run
105+
kgrid, medium, source, sensor, simulation_options, execution_options = make_simulation_parameters(
106+
directory=EXAMPLE_OUTPUT_DIR,
107+
checkpoint_timesteps=checkpoint_timesteps,
108+
)
109+
kspaceFirstOrder3D(kgrid, source, sensor, medium, simulation_options, execution_options)
110+
copy2(simulation_options.input_filename, _next_available_path(EXAMPLE_OUTPUT_DIR / "run_1_kwave_input.h5"))
111+
copy2(simulation_options.output_filename, _next_available_path(EXAMPLE_OUTPUT_DIR / "run_1_kwave_output.h5"))
112+
print("Checkpoint output directory after run_1:")
113+
print("\t-", "\n\t- ".join([f.name for f in EXAMPLE_OUTPUT_DIR.glob("*.h5")]))
114+
115+
# create the simulation parameters for the 2nd run
116+
kgrid, medium, source, sensor, simulation_options, execution_options = make_simulation_parameters(
117+
directory=EXAMPLE_OUTPUT_DIR,
118+
checkpoint_timesteps=checkpoint_timesteps,
119+
)
120+
kspaceFirstOrder3D(kgrid, source, sensor, medium, simulation_options, execution_options)
121+
copy2(simulation_options.input_filename, _next_available_path(EXAMPLE_OUTPUT_DIR / "run_2_kwave_input.h5"))
122+
copy2(simulation_options.output_filename, _next_available_path(EXAMPLE_OUTPUT_DIR / "run_2_kwave_output.h5"))
123+
print("Checkpoint output directory after run_2:")
124+
print("\t-", "\n\t- ".join([f.name for f in EXAMPLE_OUTPUT_DIR.glob("*.h5")]))
104125

105126

106127
if __name__ == "__main__":

examples/sd_directivity_modelling_2D/sd_directivity_modelling_2D.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import os
21
from copy import deepcopy
3-
from tempfile import gettempdir
42

53
import matplotlib.pyplot as plt
64
import numpy as np
@@ -73,9 +71,8 @@
7371
# run the simulation
7472

7573
input_filename = f"example_input_{source_loop + 1}_input.h5"
76-
pathname = gettempdir()
77-
input_file_full_path = os.path.join(pathname, input_filename)
78-
simulation_options = SimulationOptions(save_to_disk=True, input_filename=input_filename, data_path=pathname)
74+
output_filename = f"example_output_{source_loop + 1}_output.h5"
75+
simulation_options = SimulationOptions(save_to_disk=True, input_filename=input_filename, output_filename=output_filename)
7976
# run the simulation
8077
sensor_data = kspaceFirstOrder2DC(
8178
medium=medium,

examples/sd_focussed_detector_2D/sd_focussed_detector_2D.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# # Focussed Detector In 2D Example
22
# This example shows how k-Wave-python can be used to model the output of a focused semicircular detector, where the directionality arises from spatially averaging across the detector surface. Unlike the original example in k-Wave, this example does not visualize the simulation, as this functionality is not intrinsically supported by the accelerated binaries.
33

4-
import os
54
from copy import deepcopy
6-
from tempfile import gettempdir
75

86
import matplotlib.pyplot as plt
97
import numpy as np
@@ -43,9 +41,7 @@
4341

4442
# ## Define simulation parameters
4543
input_filename = "example_sd_focused_2d_input.h5"
46-
pathname = gettempdir()
47-
input_file_full_path = os.path.join(pathname, input_filename)
48-
simulation_options = SimulationOptions(save_to_disk=True, input_filename=input_filename, data_path=pathname)
44+
simulation_options = SimulationOptions(save_to_disk=True, input_filename=input_filename)
4945

5046

5147
# ## Run simulation with first source

examples/sd_focussed_detector_3D/sd_focussed_detector_3D.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
# Focussed Detector In 3D Example
22
# This example shows how k-Wave can be used to model the output of a focussed bowl detector where the directionality arises from spatially averaging across the detector surface.
33

4-
import os
54
from copy import deepcopy
6-
from tempfile import gettempdir
75

86
import matplotlib.pyplot as plt
97
import numpy as np
@@ -33,10 +31,8 @@
3331
_ = kgrid.makeTime(medium.sound_speed)
3432

3533
input_filename = "example_sd_focused_3d_input.h5"
36-
pathname = gettempdir()
37-
input_file_full_path = os.path.join(pathname, input_filename)
3834
simulation_options = SimulationOptions(
39-
save_to_disk=True, input_filename=input_filename, data_path=pathname, pml_size=10, data_cast="single"
35+
save_to_disk=True, input_filename=input_filename, pml_size=10, data_cast="single"
4036
)
4137

4238
# create a concave sensor

examples/us_bmode_linear_transducer/us_bmode_linear_transducer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
# simulation settings
2727
DATA_CAST = "single"
28-
RUN_SIMULATION = False
28+
RUN_SIMULATION = True
2929

3030
pml_size_points = Vector([20, 10, 10]) # [grid points]
3131
grid_size_points = Vector([256, 128, 128]) - 2 * pml_size_points # [grid points]

examples/us_bmode_phased_array/us_bmode_phased_array.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# simulation settings
2323
DATA_CAST = "single"
2424
RUN_SIMULATION = True
25+
RNG_SEED = 123456
2526

2627

2728
pml_size_points = Vector([15, 10, 10]) # [grid points]
@@ -83,18 +84,19 @@
8384

8485
not_transducer = NotATransducer(transducer, kgrid, **not_transducer)
8586

87+
rng = np.random.default_rng(RNG_SEED)
8688

8789
# Define a random distribution of scatterers for the medium
8890
background_map_mean = 1
8991
background_map_std = 0.008
90-
background_map = background_map_mean + background_map_std * np.random.randn(kgrid.Nx, kgrid.Ny, kgrid.Nz)
92+
background_map = background_map_mean + background_map_std * rng.standard_normal((kgrid.Nx, kgrid.Ny, kgrid.Nz))
9193

9294
sound_speed_map = c0 * background_map
9395
density_map = rho0 * background_map
9496

9597

9698
# Define a random distribution of scatterers for the highly scattering region
97-
scattering_map = np.random.randn(kgrid.Nx, kgrid.Ny, kgrid.Nz)
99+
scattering_map = rng.standard_normal((kgrid.Nx, kgrid.Ny, kgrid.Nz))
98100
scattering_c0 = np.clip(c0 + 25 + 75 * scattering_map, 1400, 1600)
99101
scattering_rho0 = scattering_c0 / 1.5
100102

kwave/kWaveSimulation_helper/save_to_disk_func.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@
1515

1616

1717
def save_to_disk_func(
18-
kgrid: kWaveGrid, medium: kWaveMedium, source, opt: SimulationOptions, auto_chunk: bool, values: dotdict, flags: dotdict
18+
kgrid: kWaveGrid,
19+
medium: kWaveMedium,
20+
source,
21+
opt: SimulationOptions,
22+
auto_chunk: bool,
23+
values: dotdict,
24+
flags: dotdict,
25+
input_filename: str | None = None,
1926
):
2027
# update command line status
2128
logging.log(logging.INFO, f" precomputation completed in {scale_time(TicToc.toc())}")
@@ -63,7 +70,7 @@ def save_to_disk_func(
6370
# =========================================================================
6471

6572
remove_z_dimension(float_variables, kgrid.dim)
66-
save_file(opt.input_filename, integer_variables, float_variables, opt.hdf_compression_level, auto_chunk=auto_chunk)
73+
save_file(input_filename or opt.input_filename, integer_variables, float_variables, opt.hdf_compression_level, auto_chunk=auto_chunk)
6774

6875
# update command line status
6976
logging.log(logging.INFO, f" completed in {scale_time(TicToc.toc())}")

kwave/kspaceFirstOrder2D.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from kwave.kWaveSimulation import kWaveSimulation
1313
from kwave.kWaveSimulation_helper import retract_transducer_grid_size, save_to_disk_func
1414
from kwave.options.simulation_execution_options import SimulationExecutionOptions
15-
from kwave.options.simulation_options import SimulationOptions
15+
from kwave.options.simulation_options import SimulationOptions, resolve_filenames_for_run
1616
from kwave.utils.dotdictionary import dotdict
1717
from kwave.utils.interp import interpolate2d
1818
from kwave.utils.pml import get_pml
@@ -300,6 +300,7 @@ def kspaceFirstOrder2D(
300300
if options.save_to_disk:
301301
# store the pml size for resizing transducer object below
302302
retract_size = [[options.pml_x_size, options.pml_y_size, options.pml_z_size]]
303+
input_filename, output_filename = resolve_filenames_for_run(k_sim.options)
303304

304305
# run subscript to save files to disk
305306
save_to_disk_func(
@@ -348,6 +349,7 @@ def kspaceFirstOrder2D(
348349
"cuboid_corners": k_sim.cuboid_corners,
349350
}
350351
),
352+
input_filename=input_filename,
351353
)
352354

353355
# run subscript to resize the transducer object if the grid has been expanded
@@ -359,5 +361,5 @@ def kspaceFirstOrder2D(
359361

360362
executor = Executor(simulation_options=simulation_options, execution_options=execution_options)
361363
executor_options = execution_options.as_list(sensor=k_sim.sensor)
362-
sensor_data = executor.run_simulation(k_sim.options.input_filename, k_sim.options.output_filename, options=executor_options)
364+
sensor_data = executor.run_simulation(input_filename, output_filename, options=executor_options)
363365
return sensor_data

kwave/kspaceFirstOrder3D.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from kwave.kWaveSimulation import kWaveSimulation
1313
from kwave.kWaveSimulation_helper import retract_transducer_grid_size, save_to_disk_func
1414
from kwave.options.simulation_execution_options import SimulationExecutionOptions
15-
from kwave.options.simulation_options import SimulationOptions
15+
from kwave.options.simulation_options import SimulationOptions, resolve_filenames_for_run
1616
from kwave.utils.dotdictionary import dotdict
1717
from kwave.utils.interp import interpolate3d
1818
from kwave.utils.pml import get_pml
@@ -313,6 +313,7 @@ def kspaceFirstOrder3D(
313313
if options.save_to_disk:
314314
# store the pml size for resizing transducer object below
315315
retract_size = [[options.pml_x_size, options.pml_y_size, options.pml_z_size]]
316+
input_filename, output_filename = resolve_filenames_for_run(k_sim.options)
316317

317318
# run subscript to save files to disk
318319
save_to_disk_func(
@@ -361,6 +362,7 @@ def kspaceFirstOrder3D(
361362
"cuboid_corners": k_sim.cuboid_corners,
362363
}
363364
),
365+
input_filename=input_filename,
364366
)
365367

366368
# run subscript to resize the transducer object if the grid has been expanded
@@ -372,5 +374,5 @@ def kspaceFirstOrder3D(
372374

373375
executor = Executor(simulation_options=simulation_options, execution_options=execution_options)
374376
executor_options = execution_options.as_list(sensor=k_sim.sensor)
375-
sensor_data = executor.run_simulation(k_sim.options.input_filename, k_sim.options.output_filename, options=executor_options)
377+
sensor_data = executor.run_simulation(input_filename, output_filename, options=executor_options)
376378
return sensor_data

0 commit comments

Comments
 (0)