fullwave25 1.2.2.dev0__tar.gz → 1.2.3__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.
Files changed (82) hide show
  1. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/.bumpversion.toml +1 -1
  2. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/PKG-INFO +1 -1
  3. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/__init__.py +1 -1
  4. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/grid.py +3 -3
  5. fullwave25-1.2.3/fullwave/solver/binary_manager.py +150 -0
  6. fullwave25-1.2.3/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda118 +0 -0
  7. fullwave25-1.2.3/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
  8. fullwave25-1.2.3/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda129 +0 -0
  9. fullwave25-1.2.3/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda130 +0 -0
  10. fullwave25-1.2.3/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda118 +0 -0
  11. fullwave25-1.2.3/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda124 +0 -0
  12. {fullwave25-1.2.2.dev0/fullwave/solver/bins/gpu → fullwave25-1.2.3/fullwave/solver/bins/_gpu}/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda129 +0 -0
  13. fullwave25-1.2.3/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda130 +0 -0
  14. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/solver/input_file_writer.py +12 -0
  15. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/solver/launcher.py +20 -1
  16. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/solver/pml_builder.py +14 -0
  17. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/solver/solver.py +17 -1
  18. fullwave25-1.2.3/fullwave/source.py +326 -0
  19. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/transducer.py +177 -173
  20. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/pyproject.toml +2 -2
  21. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/uv.lock +1 -1
  22. fullwave25-1.2.2.dev0/fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_multi_gpu_cuda118 +0 -0
  23. fullwave25-1.2.2.dev0/fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124 +0 -0
  24. fullwave25-1.2.2.dev0/fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_multi_gpu_cuda129 +0 -0
  25. fullwave25-1.2.2.dev0/fullwave/solver/bins/exponential_attenuation/gpu/2d/fullwave2_2d_exponential_attenuation_multi_gpu_cuda130 +0 -0
  26. fullwave25-1.2.2.dev0/fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_multi_gpu_cuda118 +0 -0
  27. fullwave25-1.2.2.dev0/fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124 +0 -0
  28. fullwave25-1.2.2.dev0/fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_multi_gpu_cuda129 +0 -0
  29. fullwave25-1.2.2.dev0/fullwave/solver/bins/exponential_attenuation/gpu/3d/fullwave2_3d_exponential_attenuation_multi_gpu_cuda130 +0 -0
  30. fullwave25-1.2.2.dev0/fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda118 +0 -0
  31. fullwave25-1.2.2.dev0/fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
  32. fullwave25-1.2.2.dev0/fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda129 +0 -0
  33. fullwave25-1.2.2.dev0/fullwave/solver/bins/gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda130 +0 -0
  34. fullwave25-1.2.2.dev0/fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda118 +0 -0
  35. fullwave25-1.2.2.dev0/fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda124 +0 -0
  36. fullwave25-1.2.2.dev0/fullwave/solver/bins/gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda130 +0 -0
  37. fullwave25-1.2.2.dev0/fullwave/source.py +0 -206
  38. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/.gitignore +0 -0
  39. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/.pre-commit-config.yaml +0 -0
  40. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/.python-version +0 -0
  41. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/20260120_0844.log +0 -0
  42. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/CITATION.cff +0 -0
  43. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/CLAUDE.md +0 -0
  44. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/CONTRIBUTING.md +0 -0
  45. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/LICENSE +0 -0
  46. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/Makefile +0 -0
  47. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/README.md +0 -0
  48. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/beamformer.m +0 -0
  49. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/experiments/__init__.py +0 -0
  50. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/experiments/exp_scatterer_diameter.py +0 -0
  51. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/beamformer/__init__.py +0 -0
  52. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/beamformer/beamformer.py +0 -0
  53. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/constants/__init__.py +0 -0
  54. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/constants/material_properties.py +0 -0
  55. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium.py +0 -0
  56. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/__init__.py +0 -0
  57. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/domain.py +0 -0
  58. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/medium_builder.py +0 -0
  59. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/presets/__init__.py +0 -0
  60. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/presets/data/.keep +0 -0
  61. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat +0 -0
  62. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/presets/domain_abdominal_wall.py +0 -0
  63. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/presets/domain_background.py +0 -0
  64. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/presets/domain_scatterer.py +0 -0
  65. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/presets/domain_simple.py +0 -0
  66. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/medium_builder/presets/domain_water_gel.py +0 -0
  67. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/sensor.py +0 -0
  68. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/solver/__init__.py +0 -0
  69. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/solver/bins/database/relaxation_params_database_num_relax=2_20260113_0957.mat +0 -0
  70. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/solver/cuda_utils.py +0 -0
  71. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/solver/utils.py +0 -0
  72. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/__init__.py +0 -0
  73. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/check_functions.py +0 -0
  74. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/coordinates.py +0 -0
  75. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/memory_tempfile.py +0 -0
  76. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/numerical.py +0 -0
  77. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/plot_utils.py +0 -0
  78. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/pulse.py +0 -0
  79. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/relaxation_parameters.py +0 -0
  80. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/scatterer.py +0 -0
  81. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/fullwave/utils/signal_process.py +0 -0
  82. {fullwave25-1.2.2.dev0 → fullwave25-1.2.3}/ruff.toml +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "1.2.2-dev0"
2
+ current_version = "1.2.3"
3
3
  parse = """(?x)
4
4
  (?P<major>0|[1-9]\\d*)\\.
5
5
  (?P<minor>0|[1-9]\\d*)\\.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fullwave25
3
- Version: 1.2.2.dev0
3
+ Version: 1.2.3
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
@@ -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.2-dev0"
63
+ __version__ = "1.2.3"
64
64
 
65
65
  VERSION = __version__ # for convenience
66
66
  logger.info("Fullwave version: %s", __version__)
@@ -183,9 +183,9 @@ class Grid:
183
183
  else " Grid spacing (grid.dx, grid.dy): "
184
184
  )
185
185
  grid_spacing_str = (
186
- f"({self.dx * 1e3:.2e} m, {self.dy * 1e3:.2e} m, {self.dz * 1e3:.2e} m)"
186
+ f"({self.dx * 1e3:.2e} mm, {self.dy * 1e3:.2e} mm, {self.dz * 1e3:.2e} mm)"
187
187
  if self.is_3d
188
- else f"({self.dx * 1e3:.2e} m, {self.dy * 1e3:.2e} m)"
188
+ else f"({self.dx * 1e3:.2e} mm, {self.dy * 1e3:.2e} mm)"
189
189
  )
190
190
 
191
191
  num_grid_points_str_desc = (
@@ -203,7 +203,7 @@ class Grid:
203
203
  f" Speed of sound (grid.c0): {self.c0} m/s\n"
204
204
  f" Points per wavelength (PPW) (grid.ppw): {self.ppw}\n"
205
205
  f" Courant-Friedrichs-Lewy (CFL) number (grid.cfl): {self.cfl}\n"
206
- f" Wavelength (grid.wavelength): {self.wavelength * 1e3:.2e} m\n"
206
+ f" Wavelength (grid.wavelength): {self.wavelength * 1e3:.2e} mm\n"
207
207
  + grid_spacing_str_desc
208
208
  + grid_spacing_str
209
209
  + "\n"
@@ -0,0 +1,150 @@
1
+ """Binary manager for fullwave solver executables.
2
+
3
+ Handles locating, caching, and downloading solver binaries from GitHub releases.
4
+
5
+ Priority order when resolving a binary:
6
+ 1. Bundled binary (shipped with the package or dev install).
7
+ 2. Local cache at ``~/.cache/fullwave25/bins/``.
8
+ 3. Download from the GitHub release pinned by ``BINARY_RELEASE_TAG``.
9
+
10
+ Binaries are versioned independently from the Python package — update
11
+ ``BINARY_RELEASE_TAG`` only when new binaries are published.
12
+
13
+ The release tag can be overridden at runtime with the environment variable
14
+ ``FULLWAVE25_BINARY_TAG`` (e.g. ``export FULLWAVE25_BINARY_TAG=v1.2.2``).
15
+ """
16
+
17
+ import logging
18
+ import os
19
+ import stat
20
+ import urllib.error
21
+ import urllib.request
22
+ from pathlib import Path
23
+
24
+ logger = logging.getLogger("__main__." + __name__)
25
+
26
+ GITHUB_REPO = "pinton-lab/fullwave25"
27
+ CACHE_DIR = Path.home() / ".cache" / "fullwave25" / "bins"
28
+
29
+ # Pinned release tag for the solver binaries.
30
+ # Update this only when new binaries are uploaded to a GitHub release.
31
+ BINARY_RELEASE_TAG = "fullwave_bin_v1.1"
32
+
33
+
34
+ def _download_url(filename: str, tag: str) -> str:
35
+ return f"https://github.com/{GITHUB_REPO}/releases/download/{tag}/{filename}"
36
+
37
+
38
+ def _download_binary(url: str, dest: Path) -> None:
39
+ """Download *url* to *dest*, showing a tqdm progress bar when available."""
40
+ dest.parent.mkdir(parents=True, exist_ok=True)
41
+ tmp = dest.with_suffix(".download_tmp")
42
+ try:
43
+ try:
44
+ from tqdm import tqdm
45
+
46
+ class _Hook:
47
+ def __init__(self) -> None:
48
+ self._bar: tqdm | None = None
49
+
50
+ def __call__(self, block: int, block_size: int, total: int) -> None: # noqa: ARG002
51
+ if self._bar is None:
52
+ self._bar = tqdm(
53
+ total=total if total > 0 else None,
54
+ unit="B",
55
+ unit_scale=True,
56
+ desc=dest.name,
57
+ )
58
+ self._bar.update(block_size)
59
+
60
+ def close(self) -> None:
61
+ if self._bar is not None:
62
+ self._bar.close()
63
+
64
+ hook = _Hook()
65
+ try:
66
+ urllib.request.urlretrieve(url, tmp, reporthook=hook) # noqa: S310
67
+ finally:
68
+ hook.close()
69
+
70
+ except ImportError:
71
+ urllib.request.urlretrieve(url, tmp) # noqa: S310
72
+
73
+ tmp.rename(dest)
74
+ dest.chmod(dest.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
75
+ logger.info("Binary saved to %s", dest)
76
+
77
+ except Exception:
78
+ tmp.unlink(missing_ok=True)
79
+ raise
80
+
81
+
82
+ def ensure_binary(local_path: Path) -> Path:
83
+ """Return an executable path for the solver binary.
84
+
85
+ Parameters
86
+ ----------
87
+ local_path:
88
+ The expected bundled path (as computed by ``_retrieve_fullwave_simulation_path``).
89
+
90
+ Returns
91
+ -------
92
+ Path
93
+ Resolved path to an executable binary.
94
+
95
+ Raises
96
+ ------
97
+ RuntimeError
98
+ If the binary cannot be found locally and the download fails.
99
+
100
+ """
101
+ # 1. Bundled binary present (dev install or package that still ships binaries).
102
+ if local_path.exists():
103
+ logger.debug("Using bundled binary: %s", local_path)
104
+ return local_path
105
+
106
+ filename = local_path.name
107
+
108
+ # Resolve the required tag first so cache lookup is always tag-specific.
109
+ # This ensures that when BINARY_RELEASE_TAG is bumped (e.g. after a package
110
+ # update) the old cached binary is not silently reused — each tag gets its
111
+ # own subdirectory under CACHE_DIR.
112
+ tag = os.environ.get("FULLWAVE25_BINARY_TAG", BINARY_RELEASE_TAG)
113
+ cached = CACHE_DIR / tag / filename
114
+
115
+ # 2. Previously downloaded and cached for this exact tag.
116
+ if cached.exists():
117
+ logger.debug("Using cached binary: %s", cached)
118
+ return cached
119
+
120
+ # 3. Download from GitHub releases.
121
+ url = _download_url(filename, tag)
122
+
123
+ logger.info(
124
+ "Binary '%s' not found locally. Downloading from GitHub release '%s' …",
125
+ filename,
126
+ tag,
127
+ )
128
+
129
+ try:
130
+ _download_binary(url, cached)
131
+ except urllib.error.HTTPError as e:
132
+ msg = (
133
+ f"Could not download binary '{filename}' from:\n {url}\n"
134
+ f"HTTP error: {e.code} {e.reason}\n\n"
135
+ "Possible fixes:\n"
136
+ f" • Check that release '{tag}' exists and contains '{filename}'.\n"
137
+ f" • Override the tag: export FULLWAVE25_BINARY_TAG=<tag>\n"
138
+ f" • Place the binary manually at: {cached}\n"
139
+ )
140
+ raise RuntimeError(msg) from e
141
+ except Exception as e:
142
+ msg = (
143
+ f"Failed to download binary '{filename}' from:\n {url}\n"
144
+ f"Error: {e}\n\n"
145
+ f" • Place the binary manually at: {cached}\n"
146
+ f" • Or override the tag: export FULLWAVE25_BINARY_TAG=<tag>\n"
147
+ )
148
+ raise RuntimeError(msg) from e
149
+
150
+ return cached
@@ -160,6 +160,15 @@ class InputFileWriter:
160
160
  simulation_dir / "icmat.dat",
161
161
  np.transpose(self.source.icmat),
162
162
  )
163
+ p0_additive = getattr(self.source, "p0_additive", None)
164
+ incoords_add = getattr(self.source, "incoords_add", None)
165
+ if p0_additive is not None:
166
+ self._queue_ic_write(
167
+ simulation_dir / "icmat_add.dat",
168
+ np.transpose(p0_additive),
169
+ )
170
+ if incoords_add is not None:
171
+ self._queue_coords_write(simulation_dir / "icc_add.dat", incoords_add)
163
172
  self._queue_coords_write(simulation_dir / "icc.dat", self.source.incoords)
164
173
  self._copy_simulation_bin_file(simulation_dir)
165
174
 
@@ -1035,6 +1044,9 @@ class InputFileWriter:
1035
1044
  ("nTic", nt_ic),
1036
1045
  ("modT", self.sensor.sampling_modulus_time),
1037
1046
  ]
1047
+ n_sources_add = getattr(self.source, "n_sources_add", 0)
1048
+ if n_sources_add > 0:
1049
+ var_list.append(("ncoords_add", n_sources_add))
1038
1050
  if self.is_3d:
1039
1051
  var_list.extend(
1040
1052
  [
@@ -30,6 +30,7 @@ class Launcher:
30
30
  is_3d: bool = False,
31
31
  use_gpu: bool = True,
32
32
  cuda_device_id: str | int | list | None = None,
33
+ save_gpu_memory: bool = False,
33
34
  ) -> None:
34
35
  """Initialize a FullwaveLauncher instance.
35
36
 
@@ -50,6 +51,17 @@ class Launcher:
50
51
  for multiple GPUs, provide a list of device IDs.
51
52
  example 1: [0, 1] for using GPU 0 and GPU 1. or "0,1" as a string.
52
53
  example 2: 2 for using GPU 2 or "2" as a string.
54
+ save_gpu_memory : bool, optional
55
+ Whether to save GPU memory by using ICMAT_MEMORY_SAVING flag in the simulation.
56
+ The simulation does not load initial conditions into GPU memory and
57
+ it loads the slice of the wavefield needed for the current time step
58
+ from CPU memory at each time step.
59
+ Defaults to False. If True, it may significantly reduce GPU memory usage,
60
+ but it may also increase the simulation time
61
+ due to the overhead of data transfer between CPU and GPU
62
+ depending on the hardware and the simulation settings.
63
+ useful in 3D simulations with large grid sizes
64
+ where GPU memory is a limiting factor.
53
65
 
54
66
  """
55
67
  self._path_fullwave_simulation_bin = path_fullwave_simulation_bin
@@ -58,6 +70,7 @@ class Launcher:
58
70
  self.is_3d = is_3d
59
71
  self.use_gpu = use_gpu
60
72
  self.cuda_device_id = self._configure_cuda_device_id(cuda_device_id)
73
+ self.save_gpu_memory = save_gpu_memory
61
74
  logger.debug("Launcher instance created.")
62
75
 
63
76
  @staticmethod
@@ -203,7 +216,12 @@ class Launcher:
203
216
  logger.info("Running simulation...")
204
217
  with (simulation_dir / "fw2_execution.log").open("w", encoding="utf-8") as file:
205
218
  time_start = time()
206
- os.environ["CUDA_VISIBLE_DEVICES"] = self.cuda_device_id
219
+ env = os.environ.copy()
220
+ env["CUDA_VISIBLE_DEVICES"] = self.cuda_device_id
221
+ if self.save_gpu_memory:
222
+ env["ICMAT_MEMORY_SAVING"] = "1"
223
+ else:
224
+ env["ICMAT_MEMORY_SAVING"] = "0"
207
225
  subprocess.run( # noqa: S603
208
226
  command,
209
227
  check=True,
@@ -211,6 +229,7 @@ class Launcher:
211
229
  stdout=file,
212
230
  stderr=file,
213
231
  text=True,
232
+ env=env,
214
233
  # check=False,
215
234
  )
216
235
  time_passed = time() - time_start
@@ -317,10 +317,17 @@ class PMLBuilder:
317
317
  extended_grid_shape = tuple(
318
318
  s + 2 * self.num_boundary_points for s in self.source_org.grid_shape
319
319
  )
320
+ incoords_add_ext = (
321
+ self.source_org.incoords_add + self.num_boundary_points
322
+ if getattr(self.source_org, "incoords_add", None) is not None
323
+ else None
324
+ )
320
325
  self.extended_source = fullwave.Source(
321
326
  p0=self.source_org.p0,
322
327
  coords=self.source_org.incoords + self.num_boundary_points,
323
328
  grid_shape=extended_grid_shape,
329
+ p0_additive=self.source_org.p0_additive,
330
+ coords_additive=incoords_add_ext,
324
331
  )
325
332
  logger.debug("building extended source for pml...done")
326
333
 
@@ -1582,10 +1589,17 @@ class PMLBuilderExponentialAttenuation(PMLBuilder):
1582
1589
  extended_grid_shape = tuple(
1583
1590
  s + 2 * self.num_boundary_points for s in self.source_org.grid_shape
1584
1591
  )
1592
+ incoords_add_ext = (
1593
+ self.source_org.incoords_add + self.num_boundary_points
1594
+ if getattr(self.source_org, "incoords_add", None) is not None
1595
+ else None
1596
+ )
1585
1597
  self.extended_source = fullwave.Source(
1586
1598
  p0=self.source_org.p0,
1587
1599
  coords=self.source_org.incoords + self.num_boundary_points,
1588
1600
  grid_shape=extended_grid_shape,
1601
+ p0_additive=self.source_org.p0_additive,
1602
+ coords_additive=incoords_add_ext,
1589
1603
  )
1590
1604
  extended_sensor_grid_shape = tuple(
1591
1605
  s + 2 * self.num_boundary_points for s in self.sensor_org.grid_shape
@@ -16,6 +16,7 @@ from fullwave.utils import (
16
16
  check_functions,
17
17
  )
18
18
 
19
+ from .binary_manager import ensure_binary
19
20
  from .cuda_utils import get_cuda_architecture, retrieve_cuda_version
20
21
 
21
22
  logger = logging.getLogger("__main__." + __name__)
@@ -326,6 +327,7 @@ class Solver:
326
327
  use_exponential_attenuation: bool = False,
327
328
  use_isotropic_relaxation: bool = True,
328
329
  cuda_device_id: str | int | list | None = None,
330
+ save_gpu_memory: bool = False,
329
331
  ) -> None:
330
332
  """Initialize a Solver instance for the fullwave simulation.
331
333
 
@@ -401,6 +403,17 @@ class Solver:
401
403
  for multiple GPUs, provide a list of device IDs.
402
404
  example 1: [0, 1] for using GPU 0 and GPU 1. or "0,1" as a string.
403
405
  example 2: 2 for using GPU 2 or "2" as a string.
406
+ save_gpu_memory : bool, optional
407
+ Whether to save GPU memory by using ICMAT_MEMORY_SAVING flag in the simulation.
408
+ The simulation does not load initial conditions into GPU memory and
409
+ it loads the slice of the wavefield needed for the current time step
410
+ from CPU memory at each time step.
411
+ Defaults to False. If True, it may significantly reduce GPU memory usage,
412
+ but it may also increase the simulation time
413
+ due to the overhead of data transfer between CPU and GPU
414
+ depending on the hardware and the simulation settings.
415
+ useful in 3D simulations with large grid sizes
416
+ where GPU memory is a limiting factor.
404
417
 
405
418
  Raises
406
419
  ------
@@ -417,6 +430,7 @@ class Solver:
417
430
  self.medium: fullwave.Medium
418
431
  self.grid: fullwave.Grid
419
432
  self.input_file_writer: InputFileWriter
433
+ self.save_gpu_memory = save_gpu_memory
420
434
 
421
435
  self.run_on_memory = run_on_memory
422
436
  if run_on_memory:
@@ -461,13 +475,14 @@ class Solver:
461
475
  self.n_relax_mechanisms = medium.n_relaxation_mechanisms
462
476
 
463
477
  if path_fullwave_simulation_bin is None:
464
- path_fullwave_simulation_bin = _retrieve_fullwave_simulation_path(
478
+ local_path = _retrieve_fullwave_simulation_path(
465
479
  use_gpu=use_gpu,
466
480
  is_3d=self.is_3d,
467
481
  use_exponential_attenuation=self.use_exponential_attenuation,
468
482
  use_isotropic_relaxation=use_isotropic_relaxation,
469
483
  n_relax_mechanisms=self.n_relax_mechanisms,
470
484
  )
485
+ path_fullwave_simulation_bin = ensure_binary(local_path)
471
486
  else:
472
487
  check_functions.check_path_exists(path_fullwave_simulation_bin)
473
488
 
@@ -538,6 +553,7 @@ class Solver:
538
553
  is_3d=self.is_3d,
539
554
  use_gpu=self.use_gpu,
540
555
  cuda_device_id=self.cuda_device_id,
556
+ save_gpu_memory=self.save_gpu_memory,
541
557
  )
542
558
 
543
559
  if use_exponential_attenuation: