fullwave25 1.3.1.dev0__tar.gz → 1.3.2__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.3.1.dev0 → fullwave25-1.3.2}/.bumpversion.toml +1 -1
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/PKG-INFO +1 -1
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/__init__.py +1 -1
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/binary_manager.py +1 -1
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/solver.py +259 -75
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/pyproject.toml +1 -1
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/uv.lock +1 -1
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/.gitignore +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/.pre-commit-config.yaml +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/.python-version +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/CITATION.cff +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/CONTRIBUTING.md +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/LICENSE +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/Makefile +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/README.md +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/attenuation.png +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/beamformer.m +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/debug_solver_bin/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124_ +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124_ +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/experiments/__init__.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/experiments/exp_scatterer_diameter.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/beamformer/__init__.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/beamformer/beamformer.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/constants/__init__.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/constants/material_properties.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/grid.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/__init__.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/domain.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/medium_builder.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/__init__.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/data/.keep +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/domain_abdominal_wall.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/domain_background.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/domain_scatterer.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/domain_simple.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/domain_water_gel.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/sensor.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/__init__.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda118 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda129 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda130 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda118 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda124 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda129 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda130 +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/database/relaxation_params_database_num_relax=3_20260828_1513.mat +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/database/relaxation_params_database_num_relax=3_20260828_1513_invalid_cells.json +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/database/relaxation_params_database_num_relax=4_20260828_1456.mat +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/bins/database/relaxation_params_database_num_relax=4_20260828_1456_invalid_cells.json +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/cuda_utils.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/input_file_writer.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/launcher.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/pml_builder.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/shipped_database.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/source_type.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/solver/utils.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/source.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/transducer.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/transmit.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/__init__.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/check_functions.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/coordinates.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/memory_tempfile.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/numerical.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/plot_utils.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/pulse.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/relaxation_parameters.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/scatterer.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/signal_filter.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/utils/signal_process.py +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/ruff.toml +0 -0
- {fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/signal_filter_example.png +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fullwave25
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.2
|
|
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
|
|
@@ -66,7 +66,7 @@ try:
|
|
|
66
66
|
__version__ = version("fullwave")
|
|
67
67
|
except PackageNotFoundError:
|
|
68
68
|
# Update via bump-my-version, not manually
|
|
69
|
-
__version__ = "1.3.
|
|
69
|
+
__version__ = "1.3.2"
|
|
70
70
|
|
|
71
71
|
VERSION = __version__ # for convenience
|
|
72
72
|
logger.info("Fullwave version: %s", __version__)
|
|
@@ -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.4.
|
|
31
|
+
BINARY_RELEASE_TAG = "fullwave_bin_v1.4.4"
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def _download_url(filename: str, tag: str) -> str:
|
|
@@ -42,6 +42,7 @@ COMPATIBLE_CUDA_ARCHITECTURES = [
|
|
|
42
42
|
"sm_90", # Hopper: H100, H200
|
|
43
43
|
"sm_100", # Blackwell: RTX 50 series
|
|
44
44
|
"sm_101", # Blackwell: RTX 50 series
|
|
45
|
+
"sm_103", # Blackwell
|
|
45
46
|
"sm_120", # Blackwell: RTX 50 series
|
|
46
47
|
"sm_121", # Blackwell: RTX 50 series
|
|
47
48
|
]
|
|
@@ -56,18 +57,6 @@ VERIFIED_CUDA_ARCHITECTURES = [
|
|
|
56
57
|
]
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
COMPATIBLE_CUDA_VERSIONS = [
|
|
60
|
-
11.8,
|
|
61
|
-
12.4,
|
|
62
|
-
12.9,
|
|
63
|
-
13.0,
|
|
64
|
-
13.1,
|
|
65
|
-
]
|
|
66
|
-
|
|
67
|
-
COMPATIBLE_CUDA_RANGES = [
|
|
68
|
-
(11.8, 13.1),
|
|
69
|
-
]
|
|
70
|
-
|
|
71
60
|
VERIFIED_CUDA_VERSIONS = [
|
|
72
61
|
12.4,
|
|
73
62
|
12.9,
|
|
@@ -108,6 +97,7 @@ COMPATIBLE_CUDA_VERSIONS_ARCHITECTURES_set = {
|
|
|
108
97
|
(12.9, "sm_90"),
|
|
109
98
|
(12.9, "sm_100"),
|
|
110
99
|
(12.9, "sm_101"),
|
|
100
|
+
(12.9, "sm_103"),
|
|
111
101
|
(12.9, "sm_120"),
|
|
112
102
|
(12.9, "sm_121"),
|
|
113
103
|
# ---
|
|
@@ -149,7 +139,36 @@ def _make_cuda_arch_option(*, use_gpu: bool = True) -> str:
|
|
|
149
139
|
return arch_option
|
|
150
140
|
|
|
151
141
|
|
|
152
|
-
def _make_cuda_version_option(*, use_gpu: bool = True) -> tuple[str, float]:
|
|
142
|
+
def _make_cuda_version_option(*, cuda_arch: str, use_gpu: bool = True) -> tuple[str, float]:
|
|
143
|
+
"""Return the CUDA tag of the binary to run on this driver and architecture.
|
|
144
|
+
|
|
145
|
+
The driver reports the newest CUDA version it supports, and it also runs a binary
|
|
146
|
+
built with an older CUDA version. The choice is therefore the newest CUDA version
|
|
147
|
+
with a build for the architecture that is not newer than the driver. Where no such
|
|
148
|
+
build exists, the choice is the oldest build of the driver's CUDA major version,
|
|
149
|
+
which runs through CUDA minor version compatibility. A Blackwell GPU on a CUDA 12.8
|
|
150
|
+
driver takes this path, because its first build is CUDA 12.9.
|
|
151
|
+
|
|
152
|
+
Parameters
|
|
153
|
+
----------
|
|
154
|
+
cuda_arch : str
|
|
155
|
+
The architecture option of the first GPU, such as "sm_90".
|
|
156
|
+
use_gpu : bool, optional
|
|
157
|
+
Whether the simulation runs on the GPU. Without it, the driver version is
|
|
158
|
+
returned unchecked.
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
tuple[str, float]
|
|
163
|
+
The tag the binary name ends with, such as "cuda129", and its CUDA version.
|
|
164
|
+
|
|
165
|
+
Raises
|
|
166
|
+
------
|
|
167
|
+
ValueError
|
|
168
|
+
If the driver version cannot be read, or the architecture has no build as
|
|
169
|
+
old as the driver and none of the driver's CUDA major version.
|
|
170
|
+
|
|
171
|
+
"""
|
|
153
172
|
cuda_version: float = retrieve_cuda_version()
|
|
154
173
|
if use_gpu and cuda_version == -1:
|
|
155
174
|
error_msg = (
|
|
@@ -159,34 +178,38 @@ def _make_cuda_version_option(*, use_gpu: bool = True) -> tuple[str, float]:
|
|
|
159
178
|
logger.error(error_msg)
|
|
160
179
|
raise ValueError(error_msg)
|
|
161
180
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
181
|
+
if use_gpu:
|
|
182
|
+
built_versions = sorted(
|
|
183
|
+
version
|
|
184
|
+
for version, arch in COMPATIBLE_CUDA_VERSIONS_ARCHITECTURES_set
|
|
185
|
+
if arch == cuda_arch
|
|
167
186
|
)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
closest_version =
|
|
176
|
-
message = (
|
|
177
|
-
f"Warning: CUDA version {cuda_version} is not in the compatible versions: "
|
|
178
|
-
f"{COMPATIBLE_CUDA_VERSIONS}. "
|
|
179
|
-
f"Using the closest compatible version {closest_version} instead."
|
|
180
|
-
)
|
|
181
|
-
logger.warning(message)
|
|
182
|
-
cuda_version = closest_version
|
|
187
|
+
older_versions = [version for version in built_versions if version <= cuda_version]
|
|
188
|
+
same_major_versions = [
|
|
189
|
+
version for version in built_versions if int(version) == int(cuda_version)
|
|
190
|
+
]
|
|
191
|
+
if older_versions:
|
|
192
|
+
closest_version = older_versions[-1]
|
|
193
|
+
elif same_major_versions:
|
|
194
|
+
closest_version = same_major_versions[0]
|
|
183
195
|
else:
|
|
184
196
|
error_msg = (
|
|
185
|
-
f"No
|
|
186
|
-
"
|
|
197
|
+
f"No binary for architecture {cuda_arch} runs on a driver that supports "
|
|
198
|
+
f"CUDA {cuda_version}. The binaries for this architecture are built with "
|
|
199
|
+
f"CUDA {built_versions}. Please update the NVIDIA driver."
|
|
187
200
|
)
|
|
188
201
|
logger.error(error_msg)
|
|
189
202
|
raise ValueError(error_msg)
|
|
203
|
+
if closest_version != cuda_version:
|
|
204
|
+
message = (
|
|
205
|
+
f"Warning: CUDA version {cuda_version} of the driver has no binary for "
|
|
206
|
+
f"architecture {cuda_arch}. "
|
|
207
|
+
f"Using the closest compatible version {closest_version} instead."
|
|
208
|
+
)
|
|
209
|
+
if closest_version > cuda_version:
|
|
210
|
+
message += " It runs through CUDA minor version compatibility."
|
|
211
|
+
logger.warning(message)
|
|
212
|
+
cuda_version = closest_version
|
|
190
213
|
|
|
191
214
|
if use_gpu and cuda_version not in VERIFIED_CUDA_VERSIONS:
|
|
192
215
|
warning_msg = (
|
|
@@ -240,7 +263,10 @@ def _retrieve_fullwave_simulation_path(
|
|
|
240
263
|
use_isotropic_relaxation: bool = True,
|
|
241
264
|
) -> Path:
|
|
242
265
|
arch_option = _make_cuda_arch_option(use_gpu=use_gpu)
|
|
243
|
-
cuda_version_option, cuda_version = _make_cuda_version_option(
|
|
266
|
+
cuda_version_option, cuda_version = _make_cuda_version_option(
|
|
267
|
+
use_gpu=use_gpu,
|
|
268
|
+
cuda_arch=arch_option,
|
|
269
|
+
)
|
|
244
270
|
if use_isotropic_relaxation is False:
|
|
245
271
|
error_msg = (
|
|
246
272
|
"Currently, only isotropic relaxation is supported. "
|
|
@@ -251,10 +277,13 @@ def _retrieve_fullwave_simulation_path(
|
|
|
251
277
|
# isotropic_str = "_isotropic" if use_isotropic_relaxation else ""
|
|
252
278
|
isotropic_str = ""
|
|
253
279
|
|
|
254
|
-
_check_compatible_set(
|
|
280
|
+
if use_gpu and not _check_compatible_set(
|
|
255
281
|
cuda_version=cuda_version,
|
|
256
282
|
cuda_arch=arch_option,
|
|
257
|
-
)
|
|
283
|
+
):
|
|
284
|
+
error_msg = f"No binary is built with CUDA {cuda_version} for architecture {arch_option}."
|
|
285
|
+
logger.error(error_msg)
|
|
286
|
+
raise ValueError(error_msg)
|
|
258
287
|
if use_exponential_attenuation:
|
|
259
288
|
if is_3d and use_gpu:
|
|
260
289
|
path_fullwave_simulation_bin = (
|
|
@@ -725,15 +754,22 @@ class Solver:
|
|
|
725
754
|
CuPy arrays, then drain both the device and pinned memory pools.
|
|
726
755
|
This prevents stale allocations from causing memory pressure when
|
|
727
756
|
subsequent operations allocate large GPU arrays.
|
|
757
|
+
|
|
758
|
+
The release is best effort. It does nothing when CuPy is absent, and
|
|
759
|
+
it does nothing when CuPy is present without a usable CUDA driver,
|
|
760
|
+
which is the case on a machine with no GPU.
|
|
728
761
|
"""
|
|
729
762
|
gc.collect()
|
|
730
763
|
try:
|
|
731
764
|
import cupy as cp # noqa: PLC0415
|
|
765
|
+
except ImportError:
|
|
766
|
+
return
|
|
732
767
|
|
|
768
|
+
try:
|
|
733
769
|
cp.get_default_memory_pool().free_all_blocks()
|
|
734
770
|
cp.get_default_pinned_memory_pool().free_all_blocks()
|
|
735
|
-
except
|
|
736
|
-
|
|
771
|
+
except cp.cuda.runtime.CUDARuntimeError as error:
|
|
772
|
+
logger.debug("GPU memory pools were not released: %s", error)
|
|
737
773
|
|
|
738
774
|
@staticmethod
|
|
739
775
|
def _check_input(
|
|
@@ -1197,11 +1233,21 @@ class Solver:
|
|
|
1197
1233
|
|
|
1198
1234
|
n_source_timesteps = source.icmat.shape[1]
|
|
1199
1235
|
|
|
1200
|
-
|
|
1236
|
+
gib = 1024.0**3
|
|
1201
1237
|
|
|
1202
1238
|
base_depth = depth // n_gpus
|
|
1203
1239
|
remainder = depth % n_gpus
|
|
1204
1240
|
|
|
1241
|
+
box, hole = (None, None)
|
|
1242
|
+
if not self.use_exponential_attenuation:
|
|
1243
|
+
box, hole = self._relaxation_regions(medium)
|
|
1244
|
+
# Every GPU holds every source matrix and every zero pressure coordinate.
|
|
1245
|
+
source_points = sum(
|
|
1246
|
+
getattr(source, name, 0) or 0
|
|
1247
|
+
for name in ("n_sources", "n_sources_add", "n_sources_u", "n_sources_v", "n_sources_w")
|
|
1248
|
+
)
|
|
1249
|
+
body_start = 0
|
|
1250
|
+
|
|
1205
1251
|
for rank, dev_id in enumerate(device_ids):
|
|
1206
1252
|
depth_this = base_depth + (1 if rank < remainder else 0)
|
|
1207
1253
|
|
|
@@ -1213,10 +1259,12 @@ class Solver:
|
|
|
1213
1259
|
n_halo_sides = 2
|
|
1214
1260
|
local_depth = depth_this + n_halo_sides * halo_depth
|
|
1215
1261
|
slab = local_depth * lateral
|
|
1262
|
+
low = max(body_start - (halo_depth if rank > 0 else 0), 0)
|
|
1263
|
+
high = min(body_start + depth_this + (halo_depth if rank < n_gpus - 1 else 0), depth)
|
|
1264
|
+
body_start += depth_this
|
|
1216
1265
|
|
|
1217
1266
|
n_sources = max(source.n_sources // n_gpus, 0)
|
|
1218
1267
|
n_sensors = max(sensor.n_sensors // n_gpus, 0)
|
|
1219
|
-
n_air_local = max(medium.n_air // n_gpus, 0)
|
|
1220
1268
|
|
|
1221
1269
|
if self.use_exponential_attenuation:
|
|
1222
1270
|
total = self._mem_exponential(
|
|
@@ -1231,29 +1279,32 @@ class Solver:
|
|
|
1231
1279
|
is_3d=self.is_3d,
|
|
1232
1280
|
)
|
|
1233
1281
|
else:
|
|
1282
|
+
band, interior = self._band_and_interior(low, high, grid.shape, box, hole)
|
|
1234
1283
|
total = self._mem_relaxation(
|
|
1235
1284
|
slab,
|
|
1236
1285
|
n_deriv_levels,
|
|
1237
|
-
|
|
1286
|
+
source_points,
|
|
1238
1287
|
n_source_timesteps,
|
|
1239
1288
|
save_gpu_memory=self.save_gpu_memory,
|
|
1240
|
-
n_air=
|
|
1289
|
+
n_air=medium.n_air,
|
|
1241
1290
|
n_sensors=n_sensors,
|
|
1242
1291
|
n_relax=self.n_relax_mechanisms,
|
|
1243
1292
|
float_bytes=float_bytes,
|
|
1244
1293
|
int_bytes=int_bytes,
|
|
1245
1294
|
is_3d=self.is_3d,
|
|
1295
|
+
band=band,
|
|
1296
|
+
interior=interior,
|
|
1246
1297
|
)
|
|
1247
1298
|
|
|
1248
1299
|
mode = "exponential" if self.use_exponential_attenuation else "relaxation"
|
|
1249
1300
|
saving = ", save_gpu_memory=True" if self.save_gpu_memory else ""
|
|
1250
1301
|
logger.info(
|
|
1251
1302
|
"GPU memory estimate [GPU %s] (%s mode%s): "
|
|
1252
|
-
"%.2f
|
|
1303
|
+
"%.2f GiB (depth=%d +%d halo, lateral=%d)",
|
|
1253
1304
|
dev_id.strip(),
|
|
1254
1305
|
mode,
|
|
1255
1306
|
saving,
|
|
1256
|
-
total /
|
|
1307
|
+
total / gib,
|
|
1257
1308
|
depth_this,
|
|
1258
1309
|
n_halo_sides * halo_depth,
|
|
1259
1310
|
lateral,
|
|
@@ -1337,8 +1388,20 @@ class Solver:
|
|
|
1337
1388
|
float_bytes: int,
|
|
1338
1389
|
int_bytes: int,
|
|
1339
1390
|
is_3d: bool,
|
|
1391
|
+
band: int | None = None,
|
|
1392
|
+
interior: int | None = None,
|
|
1340
1393
|
) -> int:
|
|
1341
|
-
"""Return estimated GPU bytes for relaxation (power-law) solver.
|
|
1394
|
+
"""Return estimated GPU bytes for the relaxation (power-law) solver.
|
|
1395
|
+
|
|
1396
|
+
The count follows the allocation of the region trimmed kernel, the same
|
|
1397
|
+
count as ``nrelax_device_bytes`` in the solver's ``gpu_memory_nrelax.cuh``.
|
|
1398
|
+
Over the whole slab the card holds the pressure and one velocity for each
|
|
1399
|
+
axis at two time levels, ``kappa_x2``, ``rho``, ``K``, the two finite
|
|
1400
|
+
amplitude maps and ``dcmap``, and mechanism 1 of the pressure memory, its
|
|
1401
|
+
``a``, ``b`` and one memory variable for each axis. The mechanisms above
|
|
1402
|
+
the first hold the same over the interior box alone, and the momentum
|
|
1403
|
+
absorbing layer holds its ``a``, ``b`` and one memory variable for each
|
|
1404
|
+
axis over the band around the hole alone.
|
|
1342
1405
|
|
|
1343
1406
|
Parameters
|
|
1344
1407
|
----------
|
|
@@ -1347,7 +1410,7 @@ class Solver:
|
|
|
1347
1410
|
n_deriv_levels : int
|
|
1348
1411
|
Number of derivative-map levels.
|
|
1349
1412
|
n_sources : int
|
|
1350
|
-
|
|
1413
|
+
Source points this GPU holds, summed over every source kind.
|
|
1351
1414
|
n_source_timesteps : int
|
|
1352
1415
|
Number of source time steps.
|
|
1353
1416
|
save_gpu_memory : bool
|
|
@@ -1362,8 +1425,13 @@ class Solver:
|
|
|
1362
1425
|
Bytes per float (4).
|
|
1363
1426
|
int_bytes : int
|
|
1364
1427
|
Bytes per int (4).
|
|
1365
|
-
is_3d: bool
|
|
1366
|
-
Whether the simulation is 3D
|
|
1428
|
+
is_3d : bool
|
|
1429
|
+
Whether the simulation is 3D.
|
|
1430
|
+
band : int | None
|
|
1431
|
+
Grid points of the momentum layer band on this GPU. None takes the slab.
|
|
1432
|
+
interior : int | None
|
|
1433
|
+
Grid points of this GPU's share of the interior box of the mechanisms
|
|
1434
|
+
above the first. None takes the slab.
|
|
1367
1435
|
|
|
1368
1436
|
Returns
|
|
1369
1437
|
-------
|
|
@@ -1373,36 +1441,152 @@ class Solver:
|
|
|
1373
1441
|
"""
|
|
1374
1442
|
fb = float_bytes
|
|
1375
1443
|
ib = int_bytes
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
mem +=
|
|
1383
|
-
|
|
1384
|
-
mem +=
|
|
1385
|
-
|
|
1386
|
-
mem +=
|
|
1387
|
-
|
|
1388
|
-
mem += 2 * (2 * n_relax) * slab * fb
|
|
1389
|
-
# (dmap + dcmap)
|
|
1390
|
-
mem += 9 * 2 * n_deriv_levels * fb + slab * ib
|
|
1391
|
-
# source (icmat + coords)
|
|
1444
|
+
axes = 3 if is_3d else 2
|
|
1445
|
+
band = slab if band is None else band
|
|
1446
|
+
interior = slab if interior is None else interior
|
|
1447
|
+
per_mechanism = 2 + axes
|
|
1448
|
+
|
|
1449
|
+
mem = 2 * (axes + 1) * slab * fb
|
|
1450
|
+
mem += 5 * slab * fb
|
|
1451
|
+
mem += slab * ib
|
|
1452
|
+
mem += per_mechanism * slab * fb
|
|
1453
|
+
mem += per_mechanism * max(n_relax - 1, 0) * interior * fb
|
|
1454
|
+
mem += per_mechanism * band * fb
|
|
1455
|
+
mem += 9 * 2 * n_deriv_levels * fb
|
|
1392
1456
|
if n_sources > 0:
|
|
1393
|
-
|
|
1394
|
-
mem +=
|
|
1395
|
-
|
|
1396
|
-
# air
|
|
1457
|
+
samples = n_sources if save_gpu_memory else n_sources * n_source_timesteps
|
|
1458
|
+
mem += samples * fb + axes * n_sources * ib
|
|
1397
1459
|
if n_air > 0:
|
|
1398
|
-
mem +=
|
|
1399
|
-
# sensor
|
|
1460
|
+
mem += axes * n_air * ib
|
|
1400
1461
|
if n_sensors > 0:
|
|
1401
|
-
mem += n_sensors * fb
|
|
1402
|
-
mem += (ndim + 1) * n_sensors * ib
|
|
1403
|
-
mem += n_sensors * ib
|
|
1462
|
+
mem += n_sensors * (fb + (axes + 2) * ib)
|
|
1404
1463
|
return mem
|
|
1405
1464
|
|
|
1465
|
+
@staticmethod
|
|
1466
|
+
def _center_line_span(values: NDArray, axis: int, *, zeros: bool) -> tuple[int, int]:
|
|
1467
|
+
"""Return the first and one past the last index of a map's center line along one axis.
|
|
1468
|
+
|
|
1469
|
+
Parameters
|
|
1470
|
+
----------
|
|
1471
|
+
values : NDArray
|
|
1472
|
+
The map, on the host or on the GPU.
|
|
1473
|
+
axis : int
|
|
1474
|
+
The axis the line runs along. The other axes sit at their centers.
|
|
1475
|
+
zeros : bool
|
|
1476
|
+
True spans the zero cells, False the nonzero cells.
|
|
1477
|
+
|
|
1478
|
+
Returns
|
|
1479
|
+
-------
|
|
1480
|
+
tuple[int, int]
|
|
1481
|
+
The span, empty as ``(0, 0)`` when no cell matches.
|
|
1482
|
+
|
|
1483
|
+
"""
|
|
1484
|
+
index = tuple(
|
|
1485
|
+
slice(None) if a == axis else size // 2 for a, size in enumerate(values.shape)
|
|
1486
|
+
)
|
|
1487
|
+
line = values[index]
|
|
1488
|
+
line = line.get() if hasattr(line, "get") else np.asarray(line)
|
|
1489
|
+
found = np.flatnonzero(line == 0 if zeros else line != 0)
|
|
1490
|
+
if found.size == 0:
|
|
1491
|
+
return (0, 0)
|
|
1492
|
+
return (int(found[0]), int(found[-1]) + 1)
|
|
1493
|
+
|
|
1494
|
+
def _relaxation_regions(
|
|
1495
|
+
self,
|
|
1496
|
+
medium: fullwave.MediumRelaxationMaps,
|
|
1497
|
+
) -> tuple[list[tuple[int, int]] | None, list[tuple[int, int]] | None]:
|
|
1498
|
+
"""Return the interior box and the momentum layer hole the kernel will measure.
|
|
1499
|
+
|
|
1500
|
+
The kernel stores every mechanism above the first over the box where its
|
|
1501
|
+
``a`` is nonzero, and the momentum absorbing layer over the band around the
|
|
1502
|
+
box where the first momentum ``a`` is zero. Both are read here along the
|
|
1503
|
+
center lines of each axis, which is how the kernel finds the hole, and a
|
|
1504
|
+
clean box shape is assumed for both.
|
|
1505
|
+
|
|
1506
|
+
Parameters
|
|
1507
|
+
----------
|
|
1508
|
+
medium : fullwave.MediumRelaxationMaps
|
|
1509
|
+
The extended medium after the absorbing layer is built.
|
|
1510
|
+
|
|
1511
|
+
Returns
|
|
1512
|
+
-------
|
|
1513
|
+
tuple
|
|
1514
|
+
The box and the hole as one ``(start, end)`` span for each axis. None
|
|
1515
|
+
stands for a map the medium does not carry, which counts as the whole
|
|
1516
|
+
slab.
|
|
1517
|
+
|
|
1518
|
+
"""
|
|
1519
|
+
maps = medium.relaxation_param_dict_for_fw2
|
|
1520
|
+
box_maps = [maps.get(f"a_pml_u{nu}") for nu in range(2, self.n_relax_mechanisms + 1)]
|
|
1521
|
+
box_maps = [values for values in box_maps if values is not None]
|
|
1522
|
+
box = None
|
|
1523
|
+
if self.n_relax_mechanisms > 1 and len(box_maps) == self.n_relax_mechanisms - 1:
|
|
1524
|
+
box = []
|
|
1525
|
+
for axis in range(box_maps[0].ndim):
|
|
1526
|
+
spans = [self._center_line_span(values, axis, zeros=False) for values in box_maps]
|
|
1527
|
+
spans = [span for span in spans if span[1] > span[0]]
|
|
1528
|
+
box.append(
|
|
1529
|
+
(min(s for s, _ in spans), max(e for _, e in spans)) if spans else (0, 0)
|
|
1530
|
+
)
|
|
1531
|
+
hole = None
|
|
1532
|
+
if maps.get("a_pml_x1") is not None:
|
|
1533
|
+
values = maps["a_pml_x1"]
|
|
1534
|
+
hole = [self._center_line_span(values, axis, zeros=True) for axis in range(values.ndim)]
|
|
1535
|
+
if any(end <= start for start, end in hole):
|
|
1536
|
+
hole = [(0, 0)] * values.ndim
|
|
1537
|
+
return box, hole
|
|
1538
|
+
|
|
1539
|
+
@staticmethod
|
|
1540
|
+
def _band_and_interior(
|
|
1541
|
+
low: int,
|
|
1542
|
+
high: int,
|
|
1543
|
+
shape: tuple[int, ...],
|
|
1544
|
+
box: list[tuple[int, int]] | None,
|
|
1545
|
+
hole: list[tuple[int, int]] | None,
|
|
1546
|
+
) -> tuple[int, int]:
|
|
1547
|
+
"""Return one GPU's band and interior box cells, as the kernel counts them.
|
|
1548
|
+
|
|
1549
|
+
Parameters
|
|
1550
|
+
----------
|
|
1551
|
+
low : int
|
|
1552
|
+
The first grid row the GPU holds, its halo included.
|
|
1553
|
+
high : int
|
|
1554
|
+
One past the last grid row the GPU holds, its halo included.
|
|
1555
|
+
shape : tuple[int, ...]
|
|
1556
|
+
The extended grid shape.
|
|
1557
|
+
box : list[tuple[int, int]] | None
|
|
1558
|
+
The interior box, one span for each axis, or None for the whole slab.
|
|
1559
|
+
hole : list[tuple[int, int]] | None
|
|
1560
|
+
The momentum layer hole, one span for each axis, or None for the whole slab.
|
|
1561
|
+
|
|
1562
|
+
Returns
|
|
1563
|
+
-------
|
|
1564
|
+
tuple[int, int]
|
|
1565
|
+
The band cells and the interior cells of this GPU.
|
|
1566
|
+
|
|
1567
|
+
"""
|
|
1568
|
+
rows = high - low
|
|
1569
|
+
lateral = int(np.prod(shape[1:]))
|
|
1570
|
+
if box is None:
|
|
1571
|
+
interior = rows * lateral
|
|
1572
|
+
else:
|
|
1573
|
+
box_rows = max(0, min(high, box[0][1]) - max(low, box[0][0]))
|
|
1574
|
+
interior = box_rows * int(np.prod([end - start for start, end in box[1:]]))
|
|
1575
|
+
if hole is None:
|
|
1576
|
+
return rows * lateral, interior
|
|
1577
|
+
hole_low = min(max(hole[0][0] - low, 0), rows)
|
|
1578
|
+
hole_high = max(min(max(hole[0][1] - low, 0), rows), hole_low)
|
|
1579
|
+
hole_rows = hole_high - hole_low
|
|
1580
|
+
band = (rows - hole_rows) * lateral
|
|
1581
|
+
hole_columns = hole[1][1] - hole[1][0]
|
|
1582
|
+
strip_columns = shape[1] - hole_columns
|
|
1583
|
+
if len(shape) == 3:
|
|
1584
|
+
strip_depths = shape[2] - (hole[2][1] - hole[2][0])
|
|
1585
|
+
band += hole_rows * (strip_columns * shape[2] + hole_columns * strip_depths)
|
|
1586
|
+
else:
|
|
1587
|
+
band += hole_rows * strip_columns
|
|
1588
|
+
return band, interior
|
|
1589
|
+
|
|
1406
1590
|
def print_info(self) -> None:
|
|
1407
1591
|
"""Print the Solver instance information."""
|
|
1408
1592
|
print(str(self))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fullwave25"
|
|
3
|
-
version = "1.3.
|
|
3
|
+
version = "1.3.2" # 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"
|
|
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.3.1.dev0 → fullwave25-1.3.2}/debug_solver_bin/fullwave2_2d_2_relax_multi_gpu_cuda124
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
|
{fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/domain_abdominal_wall.py
RENAMED
|
File without changes
|
{fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/domain_background.py
RENAMED
|
File without changes
|
{fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/fullwave/medium_builder/presets/domain_scatterer.py
RENAMED
|
File without changes
|
|
File without changes
|
{fullwave25-1.3.1.dev0 → fullwave25-1.3.2}/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
|
|
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
|