fullwave25 1.2.6.dev0__tar.gz → 1.2.6.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/.bumpversion.toml +1 -1
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/PKG-INFO +1 -1
- fullwave25-1.2.6.dev1/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124 +0 -0
- fullwave25-1.2.6.dev1/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/__init__.py +1 -1
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/sensor.py +70 -6
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/binary_manager.py +1 -1
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/input_file_writer.py +29 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/pml_builder.py +34 -16
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/solver.py +27 -17
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/pyproject.toml +1 -1
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/uv.lock +1 -1
- fullwave25-1.2.6.dev0/debug_solver_bin/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
- fullwave25-1.2.6.dev0/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/.gitignore +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/.pre-commit-config.yaml +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/.python-version +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/20260120_0844.log +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/CITATION.cff +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/CLAUDE.md +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/CONTRIBUTING.md +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/LICENSE +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/Makefile +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/README.md +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/beamformer.m +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/experiments/__init__.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/experiments/exp_scatterer_diameter.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/beamformer/__init__.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/beamformer/beamformer.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/constants/__init__.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/constants/material_properties.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/grid.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/__init__.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/domain.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/medium_builder.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/__init__.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/data/.keep +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_abdominal_wall.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_background.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_scatterer.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_simple.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_water_gel.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/__init__.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda118 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda129 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda130 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda118 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda124 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda129 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda130 +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/bins/database/relaxation_params_database_num_relax=2_20260113_0957.mat +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/cuda_utils.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/launcher.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/solver/utils.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/source.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/transducer.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/__init__.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/check_functions.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/coordinates.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/memory_tempfile.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/numerical.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/plot_utils.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/pulse.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/relaxation_parameters.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/scatterer.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/utils/signal_process.py +0 -0
- {fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/ruff.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fullwave25
|
|
3
|
-
Version: 1.2.6.
|
|
3
|
+
Version: 1.2.6.dev1
|
|
4
4
|
Summary: Fullwave 2.5: Ultrasound wave propagation simulation with heterogeneous power law attenuation modelling capabilities
|
|
5
5
|
Author: Masashi Sode, Gianmarco Pinton
|
|
6
6
|
Maintainer: Masashi Sode
|
|
Binary file
|
|
Binary file
|
|
@@ -60,7 +60,7 @@ try:
|
|
|
60
60
|
__version__ = version("fullwave")
|
|
61
61
|
except PackageNotFoundError:
|
|
62
62
|
# Update via bump-my-version, not manually
|
|
63
|
-
__version__ = "1.2.6-
|
|
63
|
+
__version__ = "1.2.6-dev1"
|
|
64
64
|
|
|
65
65
|
VERSION = __version__ # for convenience
|
|
66
66
|
logger.info("Fullwave version: %s", __version__)
|
|
@@ -21,13 +21,16 @@ class Sensor:
|
|
|
21
21
|
outcoords: NDArray[np.int64]
|
|
22
22
|
sampling_modulus_time: int = 1
|
|
23
23
|
|
|
24
|
-
def __init__(
|
|
24
|
+
def __init__( # noqa: PLR0915
|
|
25
25
|
self,
|
|
26
26
|
mask: NDArray[np.bool] | None = None,
|
|
27
27
|
sampling_modulus_time: int = 1,
|
|
28
28
|
*,
|
|
29
29
|
coords: NDArray[np.int64] | None = None,
|
|
30
30
|
grid_shape: tuple[int, ...] | None = None,
|
|
31
|
+
mod_x: int | None = None,
|
|
32
|
+
mod_y: int | None = None,
|
|
33
|
+
mod_z: int = 0,
|
|
31
34
|
) -> None:
|
|
32
35
|
"""Sensor class for Fullwave.
|
|
33
36
|
|
|
@@ -36,7 +39,7 @@ class Sensor:
|
|
|
36
39
|
mask : NDArray[np.bool] | None
|
|
37
40
|
Binary matrix where the pressure is recorded at each time-step
|
|
38
41
|
shape: [nx, ny] for 2D, [nx, ny, nz] for 3D.
|
|
39
|
-
Mutually exclusive with coords/grid_shape.
|
|
42
|
+
Mutually exclusive with coords/grid_shape and mod_x/mod_y.
|
|
40
43
|
sampling_modulus_time: int
|
|
41
44
|
Sampling modulus in time. Default is 1 (record at every time step).
|
|
42
45
|
Changing this value to n will record the pressure every n time steps.
|
|
@@ -46,39 +49,95 @@ class Sensor:
|
|
|
46
49
|
Must be provided together with grid_shape.
|
|
47
50
|
grid_shape : tuple[int, ...] | None
|
|
48
51
|
Shape of the computational grid. Required when using coords input.
|
|
52
|
+
mod_x : int | None
|
|
53
|
+
Spatial decimation stride in x (depth) for sparse-grid sensor output.
|
|
54
|
+
When provided together with mod_y, activates sparse-grid mode.
|
|
55
|
+
In this mode the solver binary generates sensor positions automatically
|
|
56
|
+
as [::mod_x, ::mod_y] (and [::mod_z] for 3D) across the domain,
|
|
57
|
+
ignoring any explicit coords/mask. Mutually exclusive with mask/coords.
|
|
58
|
+
mod_y : int | None
|
|
59
|
+
Spatial decimation stride in y (lateral). Must be provided with mod_x.
|
|
60
|
+
mod_z : int
|
|
61
|
+
Spatial decimation stride in z (elevational). Only relevant in 3D simulations.
|
|
62
|
+
Providing mod_z > 0 together with mod_x and mod_y creates a 3D sparse sensor.
|
|
63
|
+
Default 0 (2D sensor, or no z-subsampling when used in a 3D run).
|
|
49
64
|
|
|
50
65
|
Raises
|
|
51
66
|
------
|
|
52
67
|
ValueError
|
|
53
68
|
If grid_shape is not provided when using coords input.
|
|
54
69
|
If both mask and coords are provided (mutually exclusive).
|
|
55
|
-
If
|
|
70
|
+
If only one of mod_x / mod_y is provided.
|
|
71
|
+
If mod_x/mod_y are mixed with mask or coords.
|
|
72
|
+
If none of the three input modes is supplied.
|
|
56
73
|
|
|
57
74
|
"""
|
|
58
|
-
if
|
|
75
|
+
if mod_x is not None or mod_y is not None:
|
|
76
|
+
# --- sparse-grid mode ---
|
|
77
|
+
if mod_x is None or mod_y is None:
|
|
78
|
+
msg = "Both mod_x and mod_y must be provided for sparse-grid sensor mode"
|
|
79
|
+
raise ValueError(msg)
|
|
80
|
+
if mask is not None or coords is not None:
|
|
81
|
+
msg = "mod_x/mod_y are mutually exclusive with mask and coords"
|
|
82
|
+
raise ValueError(msg)
|
|
83
|
+
if mod_x <= 0 or mod_y <= 0:
|
|
84
|
+
msg = "mod_x and mod_y must be positive integers"
|
|
85
|
+
raise ValueError(msg)
|
|
86
|
+
if mod_z < 0:
|
|
87
|
+
msg = "mod_z must be a non-negative integer"
|
|
88
|
+
raise ValueError(msg)
|
|
89
|
+
self.is_sparse_grid = True
|
|
90
|
+
self.mod_x = mod_x
|
|
91
|
+
self.mod_y = mod_y
|
|
92
|
+
self.mod_z = mod_z
|
|
93
|
+
self.is_3d = mod_z > 0
|
|
94
|
+
ndim = 3 if self.is_3d else 2
|
|
95
|
+
# Empty placeholder — the binary computes positions from mod values.
|
|
96
|
+
self.outcoords = np.empty((0, ndim), dtype=np.int64)
|
|
97
|
+
self.grid_shape = None
|
|
98
|
+
elif coords is not None:
|
|
59
99
|
if grid_shape is None:
|
|
60
100
|
msg = "grid_shape is required when using coords input"
|
|
61
101
|
raise ValueError(msg)
|
|
62
102
|
if mask is not None:
|
|
63
103
|
msg = "mask and coords are mutually exclusive"
|
|
64
104
|
raise ValueError(msg)
|
|
105
|
+
self.is_sparse_grid = False
|
|
106
|
+
self.mod_x = 0
|
|
107
|
+
self.mod_y = 0
|
|
108
|
+
self.mod_z = 0
|
|
65
109
|
self.outcoords = np.atleast_2d(coords).astype(np.int64, copy=False)
|
|
66
110
|
self.grid_shape = tuple(grid_shape)
|
|
111
|
+
self.is_3d = len(self.grid_shape) == 3
|
|
67
112
|
elif mask is not None:
|
|
113
|
+
self.is_sparse_grid = False
|
|
114
|
+
self.mod_x = 0
|
|
115
|
+
self.mod_y = 0
|
|
116
|
+
self.mod_z = 0
|
|
68
117
|
mask = np.atleast_2d(mask)
|
|
69
118
|
self.grid_shape = mask.shape
|
|
70
119
|
self.outcoords = map_to_coords(mask)
|
|
120
|
+
self.is_3d = len(self.grid_shape) == 3
|
|
71
121
|
else:
|
|
72
|
-
msg = "Either mask
|
|
122
|
+
msg = "Either mask, coords (with grid_shape), or mod_x with mod_y must be provided"
|
|
73
123
|
raise ValueError(msg)
|
|
74
124
|
|
|
75
125
|
self.sampling_modulus_time = sampling_modulus_time
|
|
76
|
-
self.is_3d = len(self.grid_shape) == 3
|
|
77
126
|
super().__init__()
|
|
78
127
|
logger.debug("Sensor instance created.")
|
|
79
128
|
|
|
80
129
|
def validate(self, grid_shape: NDArray[np.int64] | tuple) -> None:
|
|
81
130
|
"""Check if the sensor coordinates are consistent with the grid shape."""
|
|
131
|
+
if self.is_sparse_grid:
|
|
132
|
+
grid_shape = tuple(grid_shape) if isinstance(grid_shape, np.ndarray) else grid_shape
|
|
133
|
+
if len(grid_shape) == 3 and self.mod_z == 0:
|
|
134
|
+
msg = (
|
|
135
|
+
"Sparse-grid sensor used in a 3D simulation but mod_z was not provided. "
|
|
136
|
+
"Pass mod_z > 0 to Sensor (e.g. Sensor(mod_x=4, mod_y=4, mod_z=4))."
|
|
137
|
+
)
|
|
138
|
+
raise ValueError(msg)
|
|
139
|
+
logger.debug("Sparse-grid sensor validated.")
|
|
140
|
+
return
|
|
82
141
|
grid_shape = tuple(grid_shape) if isinstance(grid_shape, np.ndarray) else grid_shape
|
|
83
142
|
assert self.grid_shape == grid_shape, f"{self.grid_shape} != {grid_shape}"
|
|
84
143
|
assert self.n_sensors > 0, "No active sensor found."
|
|
@@ -173,6 +232,11 @@ class Sensor:
|
|
|
173
232
|
Formatted string containing source information.
|
|
174
233
|
|
|
175
234
|
"""
|
|
235
|
+
if self.is_sparse_grid:
|
|
236
|
+
mod_str = f"mod_x={self.mod_x}, mod_y={self.mod_y}"
|
|
237
|
+
if self.is_3d:
|
|
238
|
+
mod_str += f", mod_z={self.mod_z}"
|
|
239
|
+
return f"Sensor (sparse-grid): \n Strides: {mod_str}\n Is 3D: {self.is_3d}\n"
|
|
176
240
|
return (
|
|
177
241
|
f"Sensor: \n"
|
|
178
242
|
f" Number of sensors: {self.n_sensors}\n"
|
|
@@ -28,7 +28,7 @@ CACHE_DIR = Path.home() / ".cache" / "fullwave25" / "bins"
|
|
|
28
28
|
|
|
29
29
|
# Pinned release tag for the solver binaries.
|
|
30
30
|
# Update this only when new binaries are uploaded to a GitHub release.
|
|
31
|
-
BINARY_RELEASE_TAG = "fullwave_bin_v1.
|
|
31
|
+
BINARY_RELEASE_TAG = "fullwave_bin_v1.3"
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def _download_url(filename: str, tag: str) -> str:
|
|
@@ -36,6 +36,7 @@ class InputFileWriter:
|
|
|
36
36
|
use_exponential_attenuation: bool = False,
|
|
37
37
|
use_isotropic_relaxation: bool = False,
|
|
38
38
|
release_after_write: bool = False,
|
|
39
|
+
pml_thickness: int = 0,
|
|
39
40
|
) -> None:
|
|
40
41
|
"""Initialize the InputGeneratorBase instance.
|
|
41
42
|
|
|
@@ -74,6 +75,10 @@ class InputFileWriter:
|
|
|
74
75
|
Whether to release the variable from memory after writing to file.
|
|
75
76
|
This can help reduce memory usage when generating input files for large simulations.
|
|
76
77
|
default is False.
|
|
78
|
+
pml_thickness : int, optional
|
|
79
|
+
PML boundary thickness in grid points (n_pml_layer + n_transition_layer).
|
|
80
|
+
Required by the solver binary when using sparse grid (mod_x/mod_y != 0) to determine
|
|
81
|
+
the interior domain boundaries. Also written when mod_x == 0 for future use.
|
|
77
82
|
|
|
78
83
|
"""
|
|
79
84
|
logger.debug("Initializing InputFileWriter instance.")
|
|
@@ -99,6 +104,7 @@ class InputFileWriter:
|
|
|
99
104
|
self.is_3d = self.grid.is_3d
|
|
100
105
|
self.use_exponential_attenuation = use_exponential_attenuation
|
|
101
106
|
self.release_after_write = release_after_write
|
|
107
|
+
self.pml_thickness = pml_thickness
|
|
102
108
|
|
|
103
109
|
self._dim = int(
|
|
104
110
|
np.rint(self.medium.sound_speed.max()) - np.rint(self.medium.sound_speed.min()),
|
|
@@ -828,6 +834,7 @@ class InputFileWriter:
|
|
|
828
834
|
self._save_step_params(simulation_dir)
|
|
829
835
|
self._save_coords_params(simulation_dir)
|
|
830
836
|
self._save_d_params(simulation_dir, dim)
|
|
837
|
+
self._save_sparse_grid_params(simulation_dir)
|
|
831
838
|
|
|
832
839
|
if self.use_isotropic_relaxation:
|
|
833
840
|
rename_dict = {
|
|
@@ -894,6 +901,7 @@ class InputFileWriter:
|
|
|
894
901
|
self._save_step_params(simulation_dir)
|
|
895
902
|
self._save_coords_params(simulation_dir)
|
|
896
903
|
self._save_d_params(simulation_dir, dim)
|
|
904
|
+
self._save_sparse_grid_params(simulation_dir)
|
|
897
905
|
|
|
898
906
|
def _build_symbolic_links_for_dat_files(self, src_dir: Path, dst_dir: Path) -> None:
|
|
899
907
|
var_name_list = [
|
|
@@ -916,6 +924,9 @@ class InputFileWriter:
|
|
|
916
924
|
"ncoordszero",
|
|
917
925
|
"nTic",
|
|
918
926
|
"modT",
|
|
927
|
+
"modX",
|
|
928
|
+
"modY",
|
|
929
|
+
"pml_thickness",
|
|
919
930
|
"d",
|
|
920
931
|
"dmap",
|
|
921
932
|
"ndmap",
|
|
@@ -948,6 +959,8 @@ class InputFileWriter:
|
|
|
948
959
|
"bpmly2",
|
|
949
960
|
],
|
|
950
961
|
)
|
|
962
|
+
if self.is_3d:
|
|
963
|
+
var_name_list.append("modZ")
|
|
951
964
|
if self.is_3d and not self.use_isotropic_relaxation:
|
|
952
965
|
var_name_list.extend(
|
|
953
966
|
[
|
|
@@ -1074,6 +1087,22 @@ class InputFileWriter:
|
|
|
1074
1087
|
save_path = simulation_dir / f"{var_name}.dat"
|
|
1075
1088
|
self._queue_v_abs_write(np.int32, save_path, var)
|
|
1076
1089
|
|
|
1090
|
+
def _save_sparse_grid_params(self, simulation_dir: Path) -> None:
|
|
1091
|
+
"""Write sparse-grid parameters when the sensor is in sparse-grid mode.
|
|
1092
|
+
|
|
1093
|
+
modX / modY / modZ and pml_thickness are only written when the sensor
|
|
1094
|
+
was constructed with mod_x/mod_y (i.e. sensor.is_sparse_grid is True).
|
|
1095
|
+
In standard coordinate/mask mode these files are not produced, preserving
|
|
1096
|
+
backward compatibility with older binaries that do not expect them.
|
|
1097
|
+
"""
|
|
1098
|
+
if not self.sensor.is_sparse_grid:
|
|
1099
|
+
return
|
|
1100
|
+
self._queue_v_abs_write(np.int32, simulation_dir / "modX.dat", self.sensor.mod_x)
|
|
1101
|
+
self._queue_v_abs_write(np.int32, simulation_dir / "modY.dat", self.sensor.mod_y)
|
|
1102
|
+
if self.is_3d:
|
|
1103
|
+
self._queue_v_abs_write(np.int32, simulation_dir / "modZ.dat", self.sensor.mod_z)
|
|
1104
|
+
self._queue_v_abs_write(np.int32, simulation_dir / "pml_thickness.dat", self.pml_thickness)
|
|
1105
|
+
|
|
1077
1106
|
def _save_d_params(
|
|
1078
1107
|
self,
|
|
1079
1108
|
simulation_dir: Path,
|
|
@@ -353,14 +353,24 @@ class PMLBuilder:
|
|
|
353
353
|
logger.debug("building extended source for pml...done")
|
|
354
354
|
|
|
355
355
|
logger.debug("building extended sensor for pml...")
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
356
|
+
if self.sensor_org.is_sparse_grid:
|
|
357
|
+
# Sparse-grid sensor: no explicit coordinates to shift.
|
|
358
|
+
# Pass mod values through; the binary generates positions at run time.
|
|
359
|
+
self.extended_sensor = fullwave.Sensor(
|
|
360
|
+
mod_x=self.sensor_org.mod_x,
|
|
361
|
+
mod_y=self.sensor_org.mod_y,
|
|
362
|
+
mod_z=self.sensor_org.mod_z,
|
|
363
|
+
sampling_modulus_time=self.sensor_org.sampling_modulus_time,
|
|
364
|
+
)
|
|
365
|
+
else:
|
|
366
|
+
extended_sensor_grid_shape = tuple(
|
|
367
|
+
s + 2 * self.num_boundary_points for s in self.sensor_org.grid_shape
|
|
368
|
+
)
|
|
369
|
+
self.extended_sensor = fullwave.Sensor(
|
|
370
|
+
coords=self.sensor_org.outcoords + self.num_boundary_points,
|
|
371
|
+
grid_shape=extended_sensor_grid_shape,
|
|
372
|
+
sampling_modulus_time=self.sensor_org.sampling_modulus_time,
|
|
373
|
+
)
|
|
364
374
|
logger.debug("building extended sensor for pml...done")
|
|
365
375
|
if self.is_3d:
|
|
366
376
|
self.pml_mask_x, self.pml_mask_y, self.pml_mask_z = self._localize_pml_region()
|
|
@@ -1643,14 +1653,22 @@ class PMLBuilderExponentialAttenuation(PMLBuilder):
|
|
|
1643
1653
|
w0=getattr(self.source_org, "w0", None),
|
|
1644
1654
|
coords_w=incoords_w_ext,
|
|
1645
1655
|
)
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1656
|
+
if self.sensor_org.is_sparse_grid:
|
|
1657
|
+
self.extended_sensor = fullwave.Sensor(
|
|
1658
|
+
mod_x=self.sensor_org.mod_x,
|
|
1659
|
+
mod_y=self.sensor_org.mod_y,
|
|
1660
|
+
mod_z=self.sensor_org.mod_z,
|
|
1661
|
+
sampling_modulus_time=self.sensor_org.sampling_modulus_time,
|
|
1662
|
+
)
|
|
1663
|
+
else:
|
|
1664
|
+
extended_sensor_grid_shape = tuple(
|
|
1665
|
+
s + 2 * self.num_boundary_points for s in self.sensor_org.grid_shape
|
|
1666
|
+
)
|
|
1667
|
+
self.extended_sensor = fullwave.Sensor(
|
|
1668
|
+
coords=self.sensor_org.outcoords + self.num_boundary_points,
|
|
1669
|
+
grid_shape=extended_sensor_grid_shape,
|
|
1670
|
+
sampling_modulus_time=self.sensor_org.sampling_modulus_time,
|
|
1671
|
+
)
|
|
1654
1672
|
logger.debug("Extended source and sensor for PML built successfully.")
|
|
1655
1673
|
|
|
1656
1674
|
logger.debug("Localizing PML region...")
|
|
@@ -642,6 +642,8 @@ class Solver:
|
|
|
642
642
|
def _reshape_sensor_data(
|
|
643
643
|
raw_sensor_output: NDArray[np.float64],
|
|
644
644
|
sensor: fullwave.Sensor,
|
|
645
|
+
*,
|
|
646
|
+
n_t: int | None = None,
|
|
645
647
|
) -> NDArray[np.float64]:
|
|
646
648
|
"""Reshape the raw sensor output data.
|
|
647
649
|
|
|
@@ -651,12 +653,21 @@ class Solver:
|
|
|
651
653
|
The raw sensor output data from the simulation. [nt*ncoordsout, 1]
|
|
652
654
|
sensor: fullwave.Sensor
|
|
653
655
|
The sensor object used in the simulation.
|
|
656
|
+
n_t: int | None
|
|
657
|
+
Number of time steps in the extended grid. Required for sparse-grid
|
|
658
|
+
sensors because n_sensors is not known at Python time.
|
|
654
659
|
|
|
655
660
|
Returns
|
|
656
661
|
-------
|
|
657
662
|
NDArray[np.float64]: The reshaped sensor output data. [ncoordsout, nt]
|
|
658
663
|
|
|
659
664
|
"""
|
|
665
|
+
if sensor.is_sparse_grid:
|
|
666
|
+
if n_t is None:
|
|
667
|
+
msg = "n_t is required to reshape sparse-grid sensor output"
|
|
668
|
+
raise ValueError(msg)
|
|
669
|
+
n_t_recorded = -(-n_t // sensor.sampling_modulus_time) # ceiling division
|
|
670
|
+
return raw_sensor_output.reshape(n_t_recorded, -1).T
|
|
660
671
|
return raw_sensor_output.reshape(-1, sensor.n_sensors).T
|
|
661
672
|
|
|
662
673
|
def run(
|
|
@@ -783,30 +794,27 @@ class Solver:
|
|
|
783
794
|
)
|
|
784
795
|
logger.warning(warning_msg)
|
|
785
796
|
|
|
786
|
-
sensor_mask: NDArray[np.bool_]
|
|
787
797
|
if record_whole_domain:
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
self.pml_builder.extended_grid.ny,
|
|
793
|
-
self.pml_builder.extended_grid.nz,
|
|
794
|
-
),
|
|
795
|
-
dtype=bool,
|
|
796
|
-
)
|
|
797
|
-
else:
|
|
798
|
-
sensor_mask = np.zeros(
|
|
799
|
-
(self.pml_builder.extended_grid.nx, self.pml_builder.extended_grid.ny),
|
|
800
|
-
dtype=bool,
|
|
801
|
-
)
|
|
802
|
-
sensor_mask[:, :] = True
|
|
798
|
+
mod_x = 1
|
|
799
|
+
mod_y = 1
|
|
800
|
+
mod_z = 1 if self.is_3d else None
|
|
801
|
+
|
|
803
802
|
sensor = fullwave.Sensor(
|
|
804
|
-
|
|
803
|
+
mod_x=mod_x,
|
|
804
|
+
mod_y=mod_y,
|
|
805
|
+
mod_z=mod_z,
|
|
805
806
|
sampling_modulus_time=sampling_modulus_time_whole_domain,
|
|
806
807
|
)
|
|
807
808
|
else:
|
|
808
809
|
sensor = self.pml_builder.extended_sensor
|
|
809
810
|
|
|
811
|
+
# pml_thickness = PML + transition layers on each side, excluding ghost cells.
|
|
812
|
+
# Used by the binary to locate the interior domain when building a sparse sensor grid.
|
|
813
|
+
if record_whole_domain:
|
|
814
|
+
pml_thickness = 0
|
|
815
|
+
else:
|
|
816
|
+
pml_thickness = self.pml_builder.num_boundary_points - self.pml_builder.m_spatial_order
|
|
817
|
+
|
|
810
818
|
start_input_file_writer_time = time.time()
|
|
811
819
|
input_file_writer = InputFileWriter(
|
|
812
820
|
work_dir=self.work_dir,
|
|
@@ -818,6 +826,7 @@ class Solver:
|
|
|
818
826
|
use_exponential_attenuation=self.use_exponential_attenuation,
|
|
819
827
|
use_isotropic_relaxation=self.use_isotropic_relaxation,
|
|
820
828
|
release_after_write=release_after_write,
|
|
829
|
+
pml_thickness=pml_thickness,
|
|
821
830
|
)
|
|
822
831
|
simulation_dir = input_file_writer.run(
|
|
823
832
|
simulation_dir_name,
|
|
@@ -850,6 +859,7 @@ class Solver:
|
|
|
850
859
|
result = self._reshape_sensor_data(
|
|
851
860
|
sim_result,
|
|
852
861
|
sensor=sensor,
|
|
862
|
+
n_t=self.pml_builder.extended_grid.nt,
|
|
853
863
|
)
|
|
854
864
|
end_loading_time = time.time()
|
|
855
865
|
message = (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fullwave25"
|
|
3
|
-
version = "1.2.6-
|
|
3
|
+
version = "1.2.6-dev1" # Update via bump-my-version, not manually
|
|
4
4
|
description = "Fullwave 2.5: Ultrasound wave propagation simulation with heterogeneous power law attenuation modelling capabilities"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_background.py
RENAMED
|
File without changes
|
{fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_scatterer.py
RENAMED
|
File without changes
|
{fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_simple.py
RENAMED
|
File without changes
|
{fullwave25-1.2.6.dev0 → fullwave25-1.2.6.dev1}/fullwave/medium_builder/presets/domain_water_gel.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|