fullwave25 1.2.6.dev3__tar.gz → 1.2.6.dev5__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.dev3 → fullwave25-1.2.6.dev5}/.bumpversion.toml +1 -1
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/PKG-INFO +1 -2
- fullwave25-1.2.6.dev5/debug_solver_bin/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
- fullwave25-1.2.6.dev5/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu +0 -0
- fullwave25-1.2.6.dev5/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124_ +0 -0
- fullwave25-1.2.6.dev5/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124_ +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/__init__.py +1 -1
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/launcher.py +20 -3
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/pml_builder.py +42 -46
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/solver.py +258 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/pyproject.toml +11 -3
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/ruff.toml +1 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/uv.lock +1 -12
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/.gitignore +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/.pre-commit-config.yaml +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/.python-version +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/20260120_0844.log +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/CITATION.cff +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/CLAUDE.md +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/CONTRIBUTING.md +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/LICENSE +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/Makefile +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/README.md +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/beamformer.m +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/experiments/__init__.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/experiments/exp_scatterer_diameter.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/beamformer/__init__.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/beamformer/beamformer.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/constants/__init__.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/constants/material_properties.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/grid.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/__init__.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/domain.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/medium_builder.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/__init__.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/data/.keep +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/domain_abdominal_wall.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/domain_background.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/domain_scatterer.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/domain_simple.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/domain_water_gel.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/sensor.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/__init__.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/binary_manager.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda118 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda129 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda130 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda118 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda124 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda129 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda130 +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/bins/database/relaxation_params_database_num_relax=2_20260113_0957.mat +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/cuda_utils.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/input_file_writer.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/solver/utils.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/source.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/transducer.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/__init__.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/check_functions.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/coordinates.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/memory_tempfile.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/numerical.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/plot_utils.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/pulse.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/relaxation_parameters.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/scatterer.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/signal_filter.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/utils/signal_process.py +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/ruff.log +0 -0
- {fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/signal_filter_example.png +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.dev5
|
|
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
|
|
@@ -13,7 +13,6 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
|
-
Requires-Dist: joblib>=1.5.3
|
|
17
16
|
Requires-Dist: matplotlib>=3.10.7
|
|
18
17
|
Requires-Dist: numba>=0.63.1
|
|
19
18
|
Requires-Dist: numexpr>=2.14.1
|
|
Binary file
|
fullwave25-1.2.6.dev5/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124_
ADDED
|
Binary file
|
fullwave25-1.2.6.dev5/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124_
ADDED
|
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-dev5"
|
|
64
64
|
|
|
65
65
|
VERSION = __version__ # for convenience
|
|
66
66
|
logger.info("Fullwave version: %s", __version__)
|
|
@@ -31,6 +31,7 @@ class Launcher:
|
|
|
31
31
|
use_gpu: bool = True,
|
|
32
32
|
cuda_device_id: str | int | list | None = None,
|
|
33
33
|
save_gpu_memory: bool = False,
|
|
34
|
+
verify_gpu: bool = True,
|
|
34
35
|
) -> None:
|
|
35
36
|
"""Initialize a FullwaveLauncher instance.
|
|
36
37
|
|
|
@@ -62,6 +63,11 @@ class Launcher:
|
|
|
62
63
|
depending on the hardware and the simulation settings.
|
|
63
64
|
useful in 3D simulations with large grid sizes
|
|
64
65
|
where GPU memory is a limiting factor.
|
|
66
|
+
verify_gpu : bool, optional
|
|
67
|
+
Whether to verify that the specified CUDA devices exist on the system.
|
|
68
|
+
Defaults to True. Set to False when generating input files only
|
|
69
|
+
(``generate_input_only=True``) on a machine that may not have
|
|
70
|
+
the target GPUs available.
|
|
65
71
|
|
|
66
72
|
"""
|
|
67
73
|
self._path_fullwave_simulation_bin = path_fullwave_simulation_bin
|
|
@@ -69,7 +75,10 @@ class Launcher:
|
|
|
69
75
|
assert self._path_fullwave_simulation_bin.exists(), error_msg
|
|
70
76
|
self.is_3d = is_3d
|
|
71
77
|
self.use_gpu = use_gpu
|
|
72
|
-
self.cuda_device_id = self._configure_cuda_device_id(
|
|
78
|
+
self.cuda_device_id = self._configure_cuda_device_id(
|
|
79
|
+
cuda_device_id,
|
|
80
|
+
verify_gpu=verify_gpu,
|
|
81
|
+
)
|
|
73
82
|
self.save_gpu_memory = save_gpu_memory
|
|
74
83
|
logger.debug("Launcher instance created.")
|
|
75
84
|
|
|
@@ -152,13 +161,20 @@ class Launcher:
|
|
|
152
161
|
raise ValueError(message)
|
|
153
162
|
|
|
154
163
|
@staticmethod
|
|
155
|
-
def _configure_cuda_device_id(
|
|
164
|
+
def _configure_cuda_device_id(
|
|
165
|
+
cuda_device_id: str | int | list | None,
|
|
166
|
+
*,
|
|
167
|
+
verify_gpu: bool = True,
|
|
168
|
+
) -> str:
|
|
156
169
|
"""Verify and assign the CUDA device ID.
|
|
157
170
|
|
|
158
171
|
Parameters
|
|
159
172
|
----------
|
|
160
173
|
cuda_device_id : str | int | None
|
|
161
174
|
The CUDA device ID to verify and assign.
|
|
175
|
+
verify_gpu : bool, optional
|
|
176
|
+
Whether to verify that the specified CUDA devices exist.
|
|
177
|
+
Defaults to True.
|
|
162
178
|
|
|
163
179
|
Returns
|
|
164
180
|
-------
|
|
@@ -167,7 +183,8 @@ class Launcher:
|
|
|
167
183
|
|
|
168
184
|
"""
|
|
169
185
|
output = Launcher._parse_cuda_device_id(cuda_device_id)
|
|
170
|
-
|
|
186
|
+
if verify_gpu:
|
|
187
|
+
Launcher._verify_cuda_devices_exist(output)
|
|
171
188
|
return output
|
|
172
189
|
|
|
173
190
|
def run(
|
|
@@ -5,13 +5,11 @@ import logging
|
|
|
5
5
|
from collections import OrderedDict
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
7
|
from functools import cached_property
|
|
8
|
-
from itertools import starmap
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
|
|
11
10
|
import matplotlib.pyplot as plt
|
|
12
11
|
import numexpr as ne
|
|
13
12
|
import numpy as np
|
|
14
|
-
from joblib import Parallel, delayed
|
|
15
13
|
from numpy.typing import NDArray
|
|
16
14
|
|
|
17
15
|
import fullwave
|
|
@@ -834,21 +832,24 @@ class PMLBuilder:
|
|
|
834
832
|
|
|
835
833
|
items = list(rename_dict.items())
|
|
836
834
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
835
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
836
|
+
futures = [
|
|
837
|
+
executor.submit(
|
|
838
|
+
_compute_one,
|
|
839
|
+
key_fw2,
|
|
840
|
+
key_py,
|
|
841
|
+
relaxation_param_dict,
|
|
842
|
+
alpha_target_higher_nu,
|
|
843
|
+
d_target_higher_nu,
|
|
844
|
+
alpha_target_pml,
|
|
845
|
+
d_target_pml,
|
|
846
|
+
n_polynomial,
|
|
847
|
+
self.is_3d,
|
|
848
|
+
self._apply_transition_and_pml,
|
|
849
|
+
)
|
|
850
|
+
for key_fw2, key_py in items
|
|
851
|
+
]
|
|
852
|
+
results = [f.result() for f in futures]
|
|
852
853
|
out_dict = dict(results)
|
|
853
854
|
|
|
854
855
|
logger.debug("Calculating PML a and b coefficients...")
|
|
@@ -876,13 +877,9 @@ class PMLBuilder:
|
|
|
876
877
|
# Return keys + values so parent can update dict safely
|
|
877
878
|
return (f"a_pml_{axis}{nu}", a, f"b_pml_{axis}{nu}", b)
|
|
878
879
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
prefer="processes",
|
|
883
|
-
)(
|
|
884
|
-
starmap(delayed(_worker), tasks),
|
|
885
|
-
)
|
|
880
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
881
|
+
futures = [executor.submit(_worker, nu, axis) for nu, axis in tasks]
|
|
882
|
+
results = [f.result() for f in futures]
|
|
886
883
|
|
|
887
884
|
for a_key, a_val, b_key, b_val in results:
|
|
888
885
|
out_dict[a_key] = a_val
|
|
@@ -1119,21 +1116,24 @@ class PMLBuilder:
|
|
|
1119
1116
|
raise ValueError(error_msg)
|
|
1120
1117
|
|
|
1121
1118
|
items = list(rename_dict.items())
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1119
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
1120
|
+
futures = [
|
|
1121
|
+
executor.submit(
|
|
1122
|
+
_compute_one,
|
|
1123
|
+
key_fw2,
|
|
1124
|
+
key_py,
|
|
1125
|
+
relaxation_param_dict,
|
|
1126
|
+
alpha_target_higher_nu,
|
|
1127
|
+
d_target_higher_nu,
|
|
1128
|
+
alpha_target_pml,
|
|
1129
|
+
d_target_pml,
|
|
1130
|
+
n_polynomial,
|
|
1131
|
+
self.is_3d,
|
|
1132
|
+
self._apply_transition_and_pml,
|
|
1133
|
+
)
|
|
1134
|
+
for key_fw2, key_py in items
|
|
1135
|
+
]
|
|
1136
|
+
results = [f.result() for f in futures]
|
|
1137
1137
|
out_dict = dict(results)
|
|
1138
1138
|
|
|
1139
1139
|
logger.debug("Calculating PML a and b coefficients...")
|
|
@@ -1162,13 +1162,9 @@ class PMLBuilder:
|
|
|
1162
1162
|
# Return keys + values so parent can update dict safely
|
|
1163
1163
|
return (f"a_pml_{axis}{nu}", a, f"b_pml_{axis}{nu}", b)
|
|
1164
1164
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
prefer="processes",
|
|
1169
|
-
)(
|
|
1170
|
-
starmap(delayed(_worker), tasks),
|
|
1171
|
-
)
|
|
1165
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
1166
|
+
futures = [executor.submit(_worker, nu, axis) for nu, axis in tasks]
|
|
1167
|
+
results = [f.result() for f in futures]
|
|
1172
1168
|
|
|
1173
1169
|
for a_key, a_val, b_key, b_val in results:
|
|
1174
1170
|
out_dict[a_key] = a_val
|
|
@@ -329,6 +329,7 @@ class Solver:
|
|
|
329
329
|
use_isotropic_relaxation: bool = True,
|
|
330
330
|
cuda_device_id: str | int | list | None = None,
|
|
331
331
|
save_gpu_memory: bool = False,
|
|
332
|
+
verify_gpu: bool = True,
|
|
332
333
|
) -> None:
|
|
333
334
|
"""Initialize a Solver instance for the fullwave simulation.
|
|
334
335
|
|
|
@@ -415,6 +416,11 @@ class Solver:
|
|
|
415
416
|
depending on the hardware and the simulation settings.
|
|
416
417
|
useful in 3D simulations with large grid sizes
|
|
417
418
|
where GPU memory is a limiting factor.
|
|
419
|
+
verify_gpu : bool, optional
|
|
420
|
+
Whether to verify that the specified CUDA devices exist on the system.
|
|
421
|
+
Defaults to True. Set to False when generating input files only
|
|
422
|
+
(``generate_input_only=True``) on a machine that may not have
|
|
423
|
+
the target GPUs available.
|
|
418
424
|
|
|
419
425
|
Raises
|
|
420
426
|
------
|
|
@@ -555,6 +561,7 @@ class Solver:
|
|
|
555
561
|
use_gpu=self.use_gpu,
|
|
556
562
|
cuda_device_id=self.cuda_device_id,
|
|
557
563
|
save_gpu_memory=self.save_gpu_memory,
|
|
564
|
+
verify_gpu=verify_gpu,
|
|
558
565
|
)
|
|
559
566
|
|
|
560
567
|
if use_exponential_attenuation:
|
|
@@ -758,6 +765,7 @@ class Solver:
|
|
|
758
765
|
release_after_write: bool = False,
|
|
759
766
|
highpass_cutoff_mhz: float | None = None,
|
|
760
767
|
bandpass_cutoff_mhz: tuple[float, float] | None = None,
|
|
768
|
+
gpu_memory_estimate: bool = True,
|
|
761
769
|
) -> NDArray[np.float64] | Path:
|
|
762
770
|
r"""Run the fullwave simulation and return the result as a NumPy array.
|
|
763
771
|
|
|
@@ -829,6 +837,9 @@ class Solver:
|
|
|
829
837
|
Uses cosine (Hann) tapers on both edges.
|
|
830
838
|
Cannot be combined with ``highpass_cutoff_mhz``.
|
|
831
839
|
Requires ``load_results=True``. Default is ``None`` (no filtering).
|
|
840
|
+
gpu_memory_estimate : bool
|
|
841
|
+
Whether to estimate GPU memory usage before running the simulation.
|
|
842
|
+
Default is True. If True, it estimates the GPU memory usage.
|
|
832
843
|
|
|
833
844
|
Returns
|
|
834
845
|
-------
|
|
@@ -937,6 +948,9 @@ class Solver:
|
|
|
937
948
|
)
|
|
938
949
|
logger.debug(message)
|
|
939
950
|
|
|
951
|
+
if gpu_memory_estimate:
|
|
952
|
+
self._estimate_gpu_memory(sensor)
|
|
953
|
+
|
|
940
954
|
if generate_input_only:
|
|
941
955
|
logger.info(
|
|
942
956
|
"Input data generation completed in %s. Skipping simulation execution.",
|
|
@@ -975,6 +989,250 @@ class Solver:
|
|
|
975
989
|
# which is a list of file names
|
|
976
990
|
return sim_result
|
|
977
991
|
|
|
992
|
+
def _estimate_gpu_memory(
|
|
993
|
+
self,
|
|
994
|
+
sensor: fullwave.Sensor,
|
|
995
|
+
) -> None:
|
|
996
|
+
"""Estimate and log GPU memory usage per device.
|
|
997
|
+
|
|
998
|
+
Provides a pre-launch estimate so users can verify that the simulation
|
|
999
|
+
fits in GPU memory before the binary starts allocating.
|
|
1000
|
+
|
|
1001
|
+
Parameters
|
|
1002
|
+
----------
|
|
1003
|
+
sensor : fullwave.Sensor
|
|
1004
|
+
The sensor that will actually be written to the input files (may
|
|
1005
|
+
differ from ``self.sensor`` when ``record_whole_domain=True``).
|
|
1006
|
+
|
|
1007
|
+
"""
|
|
1008
|
+
# show that this is an experimental feature
|
|
1009
|
+
logger.info("Estimating GPU memory usage... (experimental feature, may be inaccurate)")
|
|
1010
|
+
device_ids = self.fullwave_launcher.cuda_device_id.split(",")
|
|
1011
|
+
n_gpus = len(device_ids)
|
|
1012
|
+
|
|
1013
|
+
grid = self.pml_builder.extended_grid
|
|
1014
|
+
source = self.pml_builder.extended_source
|
|
1015
|
+
medium = self.pml_builder.extended_medium
|
|
1016
|
+
|
|
1017
|
+
depth = grid.nx
|
|
1018
|
+
lateral = grid.ny * grid.nz if self.is_3d else grid.ny
|
|
1019
|
+
halo_depth = 8
|
|
1020
|
+
|
|
1021
|
+
float_bytes = 4
|
|
1022
|
+
int_bytes = 4
|
|
1023
|
+
|
|
1024
|
+
c_map = medium.sound_speed
|
|
1025
|
+
c_range = int(np.rint(c_map.max()) - np.rint(c_map.min()))
|
|
1026
|
+
n_deriv_levels = 1 if c_range == 0 else c_range + 1
|
|
1027
|
+
|
|
1028
|
+
n_source_timesteps = source.icmat.shape[1]
|
|
1029
|
+
|
|
1030
|
+
gb = 1024.0**3
|
|
1031
|
+
|
|
1032
|
+
base_depth = depth // n_gpus
|
|
1033
|
+
remainder = depth % n_gpus
|
|
1034
|
+
|
|
1035
|
+
for rank, dev_id in enumerate(device_ids):
|
|
1036
|
+
depth_this = base_depth + (1 if rank < remainder else 0)
|
|
1037
|
+
|
|
1038
|
+
if n_gpus == 1:
|
|
1039
|
+
n_halo_sides = 0
|
|
1040
|
+
elif rank == 0 or rank == n_gpus - 1:
|
|
1041
|
+
n_halo_sides = 1
|
|
1042
|
+
else:
|
|
1043
|
+
n_halo_sides = 2
|
|
1044
|
+
local_depth = depth_this + n_halo_sides * halo_depth
|
|
1045
|
+
slab = local_depth * lateral
|
|
1046
|
+
|
|
1047
|
+
n_sources = max(source.n_sources // n_gpus, 0)
|
|
1048
|
+
n_sensors = max(sensor.n_sensors // n_gpus, 0)
|
|
1049
|
+
n_air_local = max(medium.n_air // n_gpus, 0)
|
|
1050
|
+
|
|
1051
|
+
if self.use_exponential_attenuation:
|
|
1052
|
+
total = self._mem_exponential(
|
|
1053
|
+
slab,
|
|
1054
|
+
n_deriv_levels,
|
|
1055
|
+
n_sources,
|
|
1056
|
+
n_source_timesteps,
|
|
1057
|
+
save_gpu_memory=self.save_gpu_memory,
|
|
1058
|
+
n_sensors=n_sensors,
|
|
1059
|
+
float_bytes=float_bytes,
|
|
1060
|
+
int_bytes=int_bytes,
|
|
1061
|
+
is_3d=self.is_3d,
|
|
1062
|
+
)
|
|
1063
|
+
else:
|
|
1064
|
+
total = self._mem_relaxation(
|
|
1065
|
+
slab,
|
|
1066
|
+
n_deriv_levels,
|
|
1067
|
+
n_sources,
|
|
1068
|
+
n_source_timesteps,
|
|
1069
|
+
save_gpu_memory=self.save_gpu_memory,
|
|
1070
|
+
n_air=n_air_local,
|
|
1071
|
+
n_sensors=n_sensors,
|
|
1072
|
+
n_relax=self.n_relax_mechanisms,
|
|
1073
|
+
float_bytes=float_bytes,
|
|
1074
|
+
int_bytes=int_bytes,
|
|
1075
|
+
is_3d=self.is_3d,
|
|
1076
|
+
)
|
|
1077
|
+
|
|
1078
|
+
mode = "exponential" if self.use_exponential_attenuation else "relaxation"
|
|
1079
|
+
saving = ", save_gpu_memory=True" if self.save_gpu_memory else ""
|
|
1080
|
+
logger.info(
|
|
1081
|
+
"GPU memory estimate [GPU %s] (%s mode%s): "
|
|
1082
|
+
"%.2f GB (depth=%d +%d halo, lateral=%d)",
|
|
1083
|
+
dev_id.strip(),
|
|
1084
|
+
mode,
|
|
1085
|
+
saving,
|
|
1086
|
+
total / gb,
|
|
1087
|
+
depth_this,
|
|
1088
|
+
n_halo_sides * halo_depth,
|
|
1089
|
+
lateral,
|
|
1090
|
+
)
|
|
1091
|
+
|
|
1092
|
+
@staticmethod
|
|
1093
|
+
def _mem_exponential(
|
|
1094
|
+
slab: int,
|
|
1095
|
+
n_deriv_levels: int,
|
|
1096
|
+
n_sources: int,
|
|
1097
|
+
n_source_timesteps: int,
|
|
1098
|
+
*,
|
|
1099
|
+
save_gpu_memory: bool,
|
|
1100
|
+
n_sensors: int,
|
|
1101
|
+
float_bytes: int,
|
|
1102
|
+
int_bytes: int,
|
|
1103
|
+
is_3d: bool,
|
|
1104
|
+
) -> int:
|
|
1105
|
+
"""Return estimated GPU bytes for exponential-attenuation solver.
|
|
1106
|
+
|
|
1107
|
+
Parameters
|
|
1108
|
+
----------
|
|
1109
|
+
slab : int
|
|
1110
|
+
Grid points per GPU slab (local_depth * lateral).
|
|
1111
|
+
n_deriv_levels : int
|
|
1112
|
+
Number of derivative-map levels.
|
|
1113
|
+
n_sources : int
|
|
1114
|
+
Approximate source count on this GPU.
|
|
1115
|
+
n_source_timesteps : int
|
|
1116
|
+
Number of source time steps.
|
|
1117
|
+
save_gpu_memory : bool
|
|
1118
|
+
Whether memory-saving mode is active.
|
|
1119
|
+
n_sensors : int
|
|
1120
|
+
Approximate sensor count on this GPU.
|
|
1121
|
+
float_bytes : int
|
|
1122
|
+
Bytes per float (4).
|
|
1123
|
+
int_bytes : int
|
|
1124
|
+
Bytes per int (4).
|
|
1125
|
+
is_3d: bool,
|
|
1126
|
+
Whether the simulation is 3D (affects sensor memory).
|
|
1127
|
+
|
|
1128
|
+
Returns
|
|
1129
|
+
-------
|
|
1130
|
+
int
|
|
1131
|
+
Total estimated bytes.
|
|
1132
|
+
|
|
1133
|
+
"""
|
|
1134
|
+
fb = float_bytes
|
|
1135
|
+
ib = int_bytes
|
|
1136
|
+
ndim = 3 if is_3d else 2
|
|
1137
|
+
n_fields = 4 if is_3d else 3 # p, u, [v], w
|
|
1138
|
+
|
|
1139
|
+
# wave fields: n_fields pairs x 2 time levels
|
|
1140
|
+
mem = n_fields * 2 * slab * fb
|
|
1141
|
+
# material: rho + K + beta + a_exp
|
|
1142
|
+
mem += 4 * slab * fb
|
|
1143
|
+
# derivative maps (dmap + dcmap)
|
|
1144
|
+
mem += 9 * 2 * n_deriv_levels * fb + slab * ib
|
|
1145
|
+
# source (icmat + coords)
|
|
1146
|
+
if n_sources > 0:
|
|
1147
|
+
mem += n_sources * fb if save_gpu_memory else n_sources * n_source_timesteps * fb
|
|
1148
|
+
mem += ndim * n_sources * ib
|
|
1149
|
+
# sensor (genoutframe + coordsout_local + p_idx_array)
|
|
1150
|
+
if n_sensors > 0:
|
|
1151
|
+
mem += n_sensors * fb
|
|
1152
|
+
mem += (ndim + 1) * n_sensors * ib
|
|
1153
|
+
mem += n_sensors * ib
|
|
1154
|
+
return mem
|
|
1155
|
+
|
|
1156
|
+
@staticmethod
|
|
1157
|
+
def _mem_relaxation(
|
|
1158
|
+
slab: int,
|
|
1159
|
+
n_deriv_levels: int,
|
|
1160
|
+
n_sources: int,
|
|
1161
|
+
n_source_timesteps: int,
|
|
1162
|
+
*,
|
|
1163
|
+
save_gpu_memory: bool,
|
|
1164
|
+
n_air: int,
|
|
1165
|
+
n_sensors: int,
|
|
1166
|
+
n_relax: int,
|
|
1167
|
+
float_bytes: int,
|
|
1168
|
+
int_bytes: int,
|
|
1169
|
+
is_3d: bool,
|
|
1170
|
+
) -> int:
|
|
1171
|
+
"""Return estimated GPU bytes for relaxation (power-law) solver.
|
|
1172
|
+
|
|
1173
|
+
Parameters
|
|
1174
|
+
----------
|
|
1175
|
+
slab : int
|
|
1176
|
+
Grid points per GPU slab (local_depth * lateral).
|
|
1177
|
+
n_deriv_levels : int
|
|
1178
|
+
Number of derivative-map levels.
|
|
1179
|
+
n_sources : int
|
|
1180
|
+
Approximate source count on this GPU.
|
|
1181
|
+
n_source_timesteps : int
|
|
1182
|
+
Number of source time steps.
|
|
1183
|
+
save_gpu_memory : bool
|
|
1184
|
+
Whether memory-saving mode is active.
|
|
1185
|
+
n_air : int
|
|
1186
|
+
Number of zero-pressure (air) coordinates on this GPU.
|
|
1187
|
+
n_sensors : int
|
|
1188
|
+
Approximate sensor count on this GPU.
|
|
1189
|
+
n_relax : int
|
|
1190
|
+
Number of relaxation mechanisms.
|
|
1191
|
+
float_bytes : int
|
|
1192
|
+
Bytes per float (4).
|
|
1193
|
+
int_bytes : int
|
|
1194
|
+
Bytes per int (4).
|
|
1195
|
+
is_3d: bool,
|
|
1196
|
+
Whether the simulation is 3D (affects sensor memory).
|
|
1197
|
+
|
|
1198
|
+
Returns
|
|
1199
|
+
-------
|
|
1200
|
+
int
|
|
1201
|
+
Total estimated bytes.
|
|
1202
|
+
|
|
1203
|
+
"""
|
|
1204
|
+
fb = float_bytes
|
|
1205
|
+
ib = int_bytes
|
|
1206
|
+
ndim = 3 if is_3d else 2
|
|
1207
|
+
n_fields = 4 if is_3d else 3 # p, u, [v], w
|
|
1208
|
+
|
|
1209
|
+
# wave fields: n_fields pairs x 2 time levels
|
|
1210
|
+
mem = n_fields * 2 * slab * fb
|
|
1211
|
+
# relaxation psi:
|
|
1212
|
+
mem += 2 * (ndim * n_relax * 2 * slab * fb)
|
|
1213
|
+
# material: rho + K + beta
|
|
1214
|
+
mem += 3 * slab * fb
|
|
1215
|
+
# kappa: 2 arrays (kappa_x1, kappa_x2)
|
|
1216
|
+
mem += 2 * slab * fb
|
|
1217
|
+
# PML: pml_x1 + pml_x2, each has 2 * n_relax arrays
|
|
1218
|
+
mem += 2 * (2 * n_relax) * slab * fb
|
|
1219
|
+
# (dmap + dcmap)
|
|
1220
|
+
mem += 9 * 2 * n_deriv_levels * fb + slab * ib
|
|
1221
|
+
# source (icmat + coords)
|
|
1222
|
+
if n_sources > 0:
|
|
1223
|
+
mem += n_sources * fb if save_gpu_memory else n_sources * n_source_timesteps * fb
|
|
1224
|
+
mem += ndim * n_sources * ib
|
|
1225
|
+
|
|
1226
|
+
# air
|
|
1227
|
+
if n_air > 0:
|
|
1228
|
+
mem += ndim * n_air * ib
|
|
1229
|
+
# sensor
|
|
1230
|
+
if n_sensors > 0:
|
|
1231
|
+
mem += n_sensors * fb
|
|
1232
|
+
mem += (ndim + 1) * n_sensors * ib
|
|
1233
|
+
mem += n_sensors * ib
|
|
1234
|
+
return mem
|
|
1235
|
+
|
|
978
1236
|
def print_info(self) -> None:
|
|
979
1237
|
"""Print the Solver instance information."""
|
|
980
1238
|
print(str(self))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fullwave25"
|
|
3
|
-
version = "1.2.6-
|
|
3
|
+
version = "1.2.6-dev5" # 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"
|
|
@@ -12,7 +12,6 @@ dependencies = [
|
|
|
12
12
|
"opencv-python-headless>=4.12.0.88",
|
|
13
13
|
"tomli>=2.3.0",
|
|
14
14
|
"numba>=0.63.1",
|
|
15
|
-
"joblib>=1.5.3",
|
|
16
15
|
"numexpr>=2.14.1",
|
|
17
16
|
]
|
|
18
17
|
authors = [{ name = "Masashi Sode" }, { name = "Gianmarco Pinton" }]
|
|
@@ -57,4 +56,13 @@ build-backend = "hatchling.build"
|
|
|
57
56
|
packages = ["fullwave", "fullwave.utils"]
|
|
58
57
|
|
|
59
58
|
[tool.hatch.build]
|
|
60
|
-
exclude = [
|
|
59
|
+
exclude = [
|
|
60
|
+
"tests",
|
|
61
|
+
"figs",
|
|
62
|
+
"examples",
|
|
63
|
+
".vscode",
|
|
64
|
+
".github",
|
|
65
|
+
"fullwave/solver/bins/gpu",
|
|
66
|
+
"fullwave/solver/bins/exponential_attenuation",
|
|
67
|
+
"fullwave/solver/bins/_exponential_attenuation",
|
|
68
|
+
]
|
|
@@ -735,10 +735,9 @@ wheels = [
|
|
|
735
735
|
|
|
736
736
|
[[package]]
|
|
737
737
|
name = "fullwave25"
|
|
738
|
-
version = "1.2.6.
|
|
738
|
+
version = "1.2.6.dev5"
|
|
739
739
|
source = { editable = "." }
|
|
740
740
|
dependencies = [
|
|
741
|
-
{ name = "joblib" },
|
|
742
741
|
{ name = "matplotlib" },
|
|
743
742
|
{ name = "numba" },
|
|
744
743
|
{ name = "numexpr" },
|
|
@@ -780,7 +779,6 @@ dev = [
|
|
|
780
779
|
requires-dist = [
|
|
781
780
|
{ name = "cupy-cuda12x", marker = "extra == 'examples'", specifier = ">=13.6.0" },
|
|
782
781
|
{ name = "ipykernel", marker = "extra == 'examples'", specifier = ">=6.28.0" },
|
|
783
|
-
{ name = "joblib", specifier = ">=1.5.3" },
|
|
784
782
|
{ name = "jupyter", marker = "extra == 'examples'", specifier = ">=1.0.0" },
|
|
785
783
|
{ name = "line-profiler", marker = "extra == 'dev'", specifier = ">=5.0.1" },
|
|
786
784
|
{ name = "mach-beamform", marker = "extra == 'examples'", specifier = ">=0.0.4" },
|
|
@@ -1034,15 +1032,6 @@ wheels = [
|
|
|
1034
1032
|
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
|
1035
1033
|
]
|
|
1036
1034
|
|
|
1037
|
-
[[package]]
|
|
1038
|
-
name = "joblib"
|
|
1039
|
-
version = "1.5.3"
|
|
1040
|
-
source = { registry = "https://pypi.org/simple" }
|
|
1041
|
-
sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" }
|
|
1042
|
-
wheels = [
|
|
1043
|
-
{ url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" },
|
|
1044
|
-
]
|
|
1045
|
-
|
|
1046
1035
|
[[package]]
|
|
1047
1036
|
name = "json5"
|
|
1048
1037
|
version = "0.12.1"
|
|
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
|
{fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/domain_background.py
RENAMED
|
File without changes
|
{fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/domain_scatterer.py
RENAMED
|
File without changes
|
{fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/fullwave/medium_builder/presets/domain_simple.py
RENAMED
|
File without changes
|
{fullwave25-1.2.6.dev3 → fullwave25-1.2.6.dev5}/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
|