fullwave25 1.2.6.dev2__py3-none-any.whl → 1.2.6.dev4__py3-none-any.whl

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.
fullwave/__init__.py CHANGED
@@ -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-dev2"
63
+ __version__ = "1.2.6-dev4"
64
64
 
65
65
  VERSION = __version__ # for convenience
66
66
  logger.info("Fullwave version: %s", __version__)
@@ -14,7 +14,8 @@ class Beamformer:
14
14
  For faster implementations, consider using libraries such as
15
15
  mach beamformer: https://github.com/Forest-Neurotech/mach
16
16
 
17
- References:
17
+ References
18
+ ----------
18
19
  - Fullwave2 BMME890 implementation
19
20
  - https://github.com/gfpinton/fullwave_bmme890/blob/master/fullwave2_launcher_imaging_planewave.m
20
21
 
fullwave/medium.py CHANGED
@@ -231,7 +231,8 @@ class MediumRelaxationMaps:
231
231
  ) -> None:
232
232
  """Check if the relaxation parameter updates have valid keys and matching shapes.
233
233
 
234
- Raises:
234
+ Raises
235
+ ------
235
236
  ValueError: If the keys do not match the desired keys or
236
237
  if the shapes of the values do not match the domain shape.
237
238
 
@@ -1147,11 +1148,13 @@ class Medium:
1147
1148
  it uses the relaxation parameters look up table
1148
1149
  to generate the relaxation parameters.
1149
1150
 
1150
- Returns:
1151
+ Returns
1152
+ -------
1151
1153
  MediumRelaxationMaps: An instance of MediumRelaxationMaps
1152
1154
  built from the retrieved relaxation parameters.
1153
1155
 
1154
- Raises:
1156
+ Raises
1157
+ ------
1155
1158
  ValueError: If an unknown attenuation_builder is specified.
1156
1159
 
1157
1160
  """
@@ -1216,7 +1219,8 @@ class Medium:
1216
1219
  def build_exponential(self) -> MediumExponentialAttenuation:
1217
1220
  """Build MediumExponentialAttenuation from alpha and power maps.
1218
1221
 
1219
- Returns:
1222
+ Returns
1223
+ -------
1220
1224
  MediumExponentialAttenuation: An instance of MediumExponentialAttenuation
1221
1225
  built from the alpha and power maps.
1222
1226
 
fullwave/sensor.py CHANGED
@@ -21,7 +21,7 @@ class Sensor:
21
21
  outcoords: NDArray[np.int64]
22
22
  sampling_modulus_time: int = 1
23
23
 
24
- def __init__( # noqa: PLR0915
24
+ def __init__(
25
25
  self,
26
26
  mask: NDArray[np.bool] | None = None,
27
27
  sampling_modulus_time: int = 1,
@@ -189,7 +189,8 @@ class Sensor:
189
189
  ) -> None:
190
190
  """Plot the transducer mask, optionally exporting and displaying the figure.
191
191
 
192
- Raises:
192
+ Raises
193
+ ------
193
194
  ValueError: If the sensor is 3D because plotting is not supported.
194
195
 
195
196
  """
@@ -113,7 +113,8 @@ def cuda_api_call(func: Callable) -> Callable:
113
113
  Decorator for CUDA API calls
114
114
  Raises RuntimeError if the CUDA call does not return CUDA_SUCCESS.
115
115
 
116
- Returns:
116
+ Returns
117
+ -------
117
118
  Callable: The wrapped function that checks CUDA API call results.
118
119
 
119
120
  """
@@ -138,7 +139,8 @@ def cuda_api_call_warn(func: Callable) -> Callable:
138
139
 
139
140
  Prints a warning message if the CUDA call does not return CUDA_SUCCESS.
140
141
 
141
- Returns:
142
+ Returns
143
+ -------
142
144
  Callable: The wrapped function that checks CUDA API call results.
143
145
 
144
146
  """
@@ -230,7 +232,8 @@ def get_cuda_device_specs() -> list[dict[str, Any]]:
230
232
  'cuda_cores': int
231
233
  }
232
234
 
233
- Returns:
235
+ Returns
236
+ -------
234
237
  A list of dictionaries containing specifications for each CUDA device.
235
238
 
236
239
  """
@@ -325,7 +328,8 @@ def get_cuda_architecture() -> list[dict[str, Any]]:
325
328
  'architecture': str,
326
329
  }
327
330
 
328
- Returns:
331
+ Returns
332
+ -------
329
333
  A list of dictionaries containing architecture information for each CUDA device.
330
334
 
331
335
  """
@@ -363,7 +367,8 @@ def get_cuda_architecture() -> list[dict[str, Any]]:
363
367
  def retrieve_cuda_version() -> float:
364
368
  """Retrieve the CUDA driver version.
365
369
 
366
- Returns:
370
+ Returns
371
+ -------
367
372
  str: CUDA version in the format "major.minor" or "unknown" if retrieval fails.
368
373
 
369
374
  """
@@ -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(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(cuda_device_id: str | int | list | None) -> str:
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
- Launcher._verify_cuda_devices_exist(output)
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
@@ -115,7 +113,7 @@ class PMLBuilder:
115
113
  pml_mask_x: NDArray[np.float64] = field(init=False)
116
114
  pml_mask_y: NDArray[np.float64] = field(init=False)
117
115
 
118
- def __init__( # noqa: PLR0915
116
+ def __init__(
119
117
  self,
120
118
  grid: fullwave.Grid,
121
119
  medium: fullwave.Medium,
@@ -834,21 +832,24 @@ class PMLBuilder:
834
832
 
835
833
  items = list(rename_dict.items())
836
834
 
837
- results = Parallel(n_jobs=self.medium_org.n_jobs, backend="threading")(
838
- delayed(_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
- )
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
- results = Parallel(
880
- n_jobs=self.medium_org.n_jobs, # use all cores
881
- backend="loky", # process-based; safe default for Python code
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
@@ -898,7 +895,7 @@ class PMLBuilder:
898
895
 
899
896
  return extended_medium
900
897
 
901
- def _apply_pml_3d( # noqa: PLR0915
898
+ def _apply_pml_3d(
902
899
  self,
903
900
  extended_medium: fullwave.MediumRelaxationMaps,
904
901
  theoritical_reflection_coefficient: float,
@@ -1119,21 +1116,24 @@ class PMLBuilder:
1119
1116
  raise ValueError(error_msg)
1120
1117
 
1121
1118
  items = list(rename_dict.items())
1122
- results = Parallel(n_jobs=self.medium_org.n_jobs, backend="threading")(
1123
- delayed(_compute_one)(
1124
- key_fw2,
1125
- key_py,
1126
- relaxation_param_dict,
1127
- alpha_target_higher_nu,
1128
- d_target_higher_nu,
1129
- alpha_target_pml,
1130
- d_target_pml,
1131
- n_polynomial,
1132
- self.is_3d,
1133
- self._apply_transition_and_pml,
1134
- )
1135
- for key_fw2, key_py in items
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
- results = Parallel(
1166
- n_jobs=self.medium_org.n_jobs, # use all cores
1167
- backend="loky", # process-based; safe default for Python code
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
@@ -1184,7 +1180,7 @@ class PMLBuilder:
1184
1180
 
1185
1181
  return extended_medium
1186
1182
 
1187
- def _apply_transition_and_pml( # noqa: C901, PLR0912, PLR0915
1183
+ def _apply_transition_and_pml( # noqa: PLR0912
1188
1184
  self,
1189
1185
  input_array: NDArray[np.float64],
1190
1186
  value_target: float,
@@ -1465,7 +1461,7 @@ class PMLBuilder:
1465
1461
  class PMLBuilderExponentialAttenuation(PMLBuilder):
1466
1462
  """A class to set up PML for exponential attenuation media."""
1467
1463
 
1468
- def __init__( # noqa: PLR0915
1464
+ def __init__(
1469
1465
  self,
1470
1466
  grid: fullwave.Grid,
1471
1467
  medium: fullwave.Medium,
fullwave/solver/solver.py CHANGED
@@ -309,7 +309,7 @@ class Solver:
309
309
  generates the required input files, and runs the simulation executable.
310
310
  """
311
311
 
312
- def __init__( # noqa: PLR0912, PLR0915, C901
312
+ def __init__( # noqa: PLR0912
313
313
  self,
314
314
  work_dir: Path,
315
315
  grid: fullwave.Grid,
@@ -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 = False,
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))
fullwave/solver/utils.py CHANGED
@@ -17,10 +17,12 @@ def load_dat_data(dat_file_path: Path, dtype: DTypeLike = np.float32) -> NDArray
17
17
  dat_file_path (Path): Path to the .dat file.
18
18
  dtype: Data type to use when reading the file.
19
19
 
20
- Raises:
20
+ Raises
21
+ ------
21
22
  ValueError: if dat_file_path does not exist.
22
23
 
23
- Returns:
24
+ Returns
25
+ -------
24
26
  NDArray[np.float64]: Array of data read from the file.
25
27
 
26
28
  """
@@ -58,7 +60,8 @@ def load_dat_and_reshape(
58
60
  n_sensors: Number of sensors
59
61
  dtype: Data type to use when reading the file.
60
62
 
61
- Returns:
63
+ Returns
64
+ -------
62
65
  NDArray[np.float64]: Array of data read from the file.
63
66
 
64
67
  """
@@ -88,7 +91,8 @@ def initialize_relaxation_param_dict(
88
91
  ) -> dict[str, NDArray[np.float64]]:
89
92
  """Initialize a dictionary with relaxation parameters.
90
93
 
91
- Returns:
94
+ Returns
95
+ -------
92
96
  dict[str, NDArray[np.float64]]: Dictionary of relaxation parameters.
93
97
 
94
98
  """
@@ -125,10 +129,12 @@ def load_data_with_time_step(
125
129
  dtype: Data type to use when reading the file.
126
130
  time_step: Time step index to load.
127
131
 
128
- Returns:
132
+ Returns
133
+ -------
129
134
  NDArray[np.float64]: Array of data read from the file.
130
135
 
131
- Raises:
136
+ Raises
137
+ ------
132
138
  ValueError: if file_path does not exist.
133
139
 
134
140
  """
@@ -159,10 +165,12 @@ def load_data_with_sensor_index(
159
165
  sensor_index: Sensor index to load.
160
166
  dtype: Data type to use when reading the file.
161
167
 
162
- Returns:
168
+ Returns
169
+ -------
163
170
  NDArray[np.float64]: Array of data read from the file.
164
171
 
165
- Raises:
172
+ Raises
173
+ ------
166
174
  ValueError: if file_path does not exist.
167
175
 
168
176
  """
fullwave/source.py CHANGED
@@ -30,7 +30,7 @@ class Source:
30
30
  w0: NDArray[np.float64] | None = None
31
31
  incoords_w: NDArray[np.int64] | None = None
32
32
 
33
- def __init__( # noqa: C901 PLR0912 PLR0915
33
+ def __init__( # noqa: PLR0912
34
34
  self,
35
35
  p0: NDArray[np.float64] | None = None,
36
36
  mask: NDArray[np.bool] | None = None,
fullwave/transducer.py CHANGED
@@ -22,7 +22,8 @@ logger = logging.getLogger("__main__." + __name__)
22
22
  def _make_pos_int(val: float | tuple[float] | tuple[int]) -> NDArray[np.int64]:
23
23
  """Force value to be a positive integer.
24
24
 
25
- Returns:
25
+ Returns
26
+ -------
26
27
  NDArray[np.int64]: Array with positive integers.
27
28
 
28
29
  """
@@ -202,7 +203,7 @@ class TransducerGeometry:
202
203
  ) = self._create_element_coords()
203
204
  logger.debug("TransducerGeometry instance created.")
204
205
 
205
- def _init_dimensions( # noqa: C901, PLR0912
206
+ def _init_dimensions( # noqa: PLR0912
206
207
  self,
207
208
  grid: Grid,
208
209
  element_width_px: int | None,
@@ -313,7 +314,7 @@ class TransducerGeometry:
313
314
  assert len(position_m) == 3, "position_m must have 3 elements for 3D transducer"
314
315
  return position_px, position_m
315
316
 
316
- def _create_element_coords( # noqa: PLR0912, PLR0915
317
+ def _create_element_coords( # noqa: PLR0912
317
318
  self,
318
319
  ) -> tuple[NDArray[np.int64], NDArray[np.int64], NDArray[np.int64], NDArray[np.int64]]:
319
320
  """Build flat coordinate arrays for source and sensor pixels.
@@ -813,15 +814,15 @@ class Transducer:
813
814
  def source_coords(self) -> NDArray[np.int64]:
814
815
  """Coordinates of active source pixels; shape [N_src, ndim]."""
815
816
  active_ids = np.where(self.active_source_elements)[0] + 1
816
- mask = np.isin(self.transducer_geometry._source_ids, active_ids) # noqa: SLF001
817
- return self.transducer_geometry._source_coords[mask] # noqa: SLF001
817
+ mask = np.isin(self.transducer_geometry._source_ids, active_ids)
818
+ return self.transducer_geometry._source_coords[mask]
818
819
 
819
820
  @property
820
821
  def sensor_coords(self) -> NDArray[np.int64]:
821
822
  """Coordinates of active sensor pixels; shape [N_snsr, ndim]."""
822
823
  active_ids = np.where(self.active_sensor_elements)[0] + 1
823
- mask = np.isin(self.transducer_geometry._sensor_ids, active_ids) # noqa: SLF001
824
- return self.transducer_geometry._sensor_coords[mask] # noqa: SLF001
824
+ mask = np.isin(self.transducer_geometry._sensor_ids, active_ids)
825
+ return self.transducer_geometry._sensor_coords[mask]
825
826
 
826
827
  @property
827
828
  def element_id_to_element_surface(self) -> dict[int, NDArray[np.int64]]:
@@ -986,8 +987,8 @@ class Transducer:
986
987
  @property
987
988
  def element_id_to_element_center(self) -> dict[int, NDArray[np.int64]]:
988
989
  """Return the dictionary mapping element IDs to their center coordinates."""
989
- coords = self.transducer_geometry._source_coords # noqa: SLF001
990
- ids = self.transducer_geometry._source_ids # noqa: SLF001
990
+ coords = self.transducer_geometry._source_coords
991
+ ids = self.transducer_geometry._source_ids
991
992
  n = self.transducer_geometry.number_elements
992
993
  ndim = coords.shape[1] if len(coords) else (3 if self.is_3d else 2)
993
994
  out: dict[int, NDArray[np.int64]] = {}
@@ -1049,7 +1050,7 @@ class Transducer:
1049
1050
 
1050
1051
  it plots whole transducer geometry including the inactive/active source and sensor elements.
1051
1052
  """
1052
- import matplotlib.pyplot as plt
1053
+ import matplotlib.pyplot as plt # noqa: PLC0415
1053
1054
 
1054
1055
  fig, ax = plt.subplots(1, 1, figsize=(10, 10))
1055
1056
  plot_mask = np.zeros(self.transducer_geometry.stored_grid_size)
@@ -1089,7 +1090,7 @@ class Transducer:
1089
1090
 
1090
1091
  it plots whole transducer geometry including the inactive/active source and sensor elements.
1091
1092
  """
1092
- import matplotlib.pyplot as plt
1093
+ import matplotlib.pyplot as plt # noqa: PLC0415
1093
1094
 
1094
1095
  fig, ax = plt.subplots(1, 1, figsize=(10, 10))
1095
1096
  plot_mask = np.zeros(self.transducer_geometry.stored_grid_size)
@@ -7,7 +7,8 @@ from typing import Any
7
7
  def check_instance(target_var: Any, instances: list[Any] | Any) -> None: # noqa: ANN401
8
8
  """Check whether target_var is an instance of the specified type.
9
9
 
10
- Raises:
10
+ Raises
11
+ ------
11
12
  TypeError: If target_var is not an instance of instance.
12
13
 
13
14
  """
@@ -21,7 +22,8 @@ def check_instance(target_var: Any, instances: list[Any] | Any) -> None: # noqa
21
22
  def check_path_exists(target_path: Path) -> None:
22
23
  """Check whether the provided target_path exists.
23
24
 
24
- Raises:
25
+ Raises
26
+ ------
25
27
  ValueError: If target_path does not exist.
26
28
 
27
29
  """
@@ -37,7 +39,8 @@ def check_compatible_value(
37
39
  ) -> None:
38
40
  """Check whether the provided value is in the list of compatible values.
39
41
 
40
- Raises:
42
+ Raises
43
+ ------
41
44
  ValueError: If value is not in compatible_values.
42
45
 
43
46
  """
@@ -16,7 +16,8 @@ def make_circle_idx(
16
16
  cen: The center of the circle.
17
17
  rad: The radius of the circle.
18
18
 
19
- Returns:
19
+ Returns
20
+ -------
20
21
  mask: The mask of the circle.
21
22
 
22
23
  """
@@ -32,7 +33,8 @@ def map_to_coords(
32
33
  ) -> NDArray[np.int64]:
33
34
  """Map the mask map to coordinates.
34
35
 
35
- Returns:
36
+ Returns
37
+ -------
36
38
  NDArray[np.int64]: An array of coordinates corresponding to non-zero elements in the mask.
37
39
 
38
40
  """
@@ -103,7 +105,8 @@ def map_to_coords_with_sort(map_data: NDArray[np.int64]) -> NDArray[np.int64]:
103
105
  Args:
104
106
  map_data: The mask map.
105
107
 
106
- Returns:
108
+ Returns
109
+ -------
107
110
  NDArray[np.int64]: An array of coordinates corresponding to non-zero elements in the mask.
108
111
 
109
112
  """
@@ -124,7 +127,8 @@ def map_to_coordinates(
124
127
  is_3d: Whether the grid is 3D or not.
125
128
  sort: Whether to sort the coordinates by the first dimension.
126
129
 
127
- Returns:
130
+ Returns
131
+ -------
128
132
  NDArray[np.int64]: An array of coordinates corresponding to non-zero elements in the mask.
129
133
 
130
134
  """
@@ -31,7 +31,7 @@ class MemoryTempfile:
31
31
  and create temporary files and directories.
32
32
  """
33
33
 
34
- def __init__( # noqa: C901, PLR0912
34
+ def __init__( # noqa: PLR0912
35
35
  self,
36
36
  *,
37
37
  preferred_paths: list | None = None,
@@ -182,7 +182,7 @@ class MemoryTempfile:
182
182
  self,
183
183
  suffix: str | None = None,
184
184
  prefix: str | None = None,
185
- dir: str | None = None, # noqa: A002
185
+ dir: str | None = None,
186
186
  ) -> str:
187
187
  """Create a temporary directory.
188
188
 
@@ -207,7 +207,7 @@ class MemoryTempfile:
207
207
  self,
208
208
  suffix: str | None = None,
209
209
  prefix: str | None = None,
210
- dir: str | None = None, # noqa: A002
210
+ dir: str | None = None,
211
211
  *,
212
212
  text: bool = False,
213
213
  ) -> tuple[int, str]:
@@ -241,7 +241,7 @@ class MemoryTempfile:
241
241
  self,
242
242
  suffix: str | None = None,
243
243
  prefix: str | None = None,
244
- dir: str | None = None, # noqa: A002
244
+ dir: str | None = None,
245
245
  ) -> tempfile.TemporaryDirectory[str]:
246
246
  """Create a temporary directory and return a TemporaryDirectory object.
247
247
 
@@ -275,7 +275,7 @@ class MemoryTempfile:
275
275
  newline: str | None = None,
276
276
  suffix: str | None = None,
277
277
  prefix: str | None = None,
278
- dir: str | None = None, # noqa: A002
278
+ dir: str | None = None,
279
279
  ) -> tempfile.SpooledTemporaryFile:
280
280
  """Create a spooled temporary file.
281
281
 
@@ -329,7 +329,7 @@ class MemoryTempfile:
329
329
  newline: str | None = None,
330
330
  suffix: str | None = None,
331
331
  prefix: str | None = None,
332
- dir: str | None = None, # noqa: A002
332
+ dir: str | None = None,
333
333
  *,
334
334
  delete: bool = True,
335
335
  ) -> tempfile.NamedTemporaryFile:
@@ -379,7 +379,7 @@ class MemoryTempfile:
379
379
  newline: str | None = None,
380
380
  suffix: str | None = None,
381
381
  prefix: str | None = None,
382
- dir: str | None = None, # noqa: A002
382
+ dir: str | None = None,
383
383
  ) -> tempfile.TemporaryFile:
384
384
  """Create and return a temporary file.
385
385
 
@@ -13,7 +13,7 @@ from numpy.typing import NDArray
13
13
  from tqdm import tqdm
14
14
 
15
15
 
16
- def plot_array( # noqa: C901, D417, PLR0912
16
+ def plot_array( # noqa: PLR0912
17
17
  x: NDArray[np.float64 | np.int64 | np.bool],
18
18
  aspect: float | None = None,
19
19
  vmin: float | None = None,
@@ -553,7 +553,7 @@ def plot_wave_propagation_animation(
553
553
  animation_data.save(export_name, writer="ffmpeg", dpi=dpi)
554
554
 
555
555
 
556
- def plot_wave_propagation_with_map( # noqa: PLR0915, C901
556
+ def plot_wave_propagation_with_map(
557
557
  propagation_map: NDArray[np.float64],
558
558
  c_map: NDArray[np.float64],
559
559
  rho_map: NDArray[np.float64],
@@ -868,7 +868,7 @@ def plot_wave_propagation_with_map( # noqa: PLR0915, C901
868
868
  plt.close("all")
869
869
 
870
870
 
871
- def plot_wave_propagation_snapshot( # noqa: PLR0915
871
+ def plot_wave_propagation_snapshot(
872
872
  propagation_map: NDArray[np.float64],
873
873
  c_map: NDArray[np.float64],
874
874
  rho_map: NDArray[np.float64],
@@ -25,7 +25,7 @@ def _check_cupy() -> bool:
25
25
  global _CUPY_AVAILABLE # noqa: PLW0603
26
26
  if _CUPY_AVAILABLE is None:
27
27
  try:
28
- import cupy # noqa: F401
28
+ import cupy # noqa: F401, PLC0415
29
29
 
30
30
  _CUPY_AVAILABLE = True
31
31
  except ImportError:
@@ -145,7 +145,7 @@ def apply_filter(
145
145
  )
146
146
 
147
147
  if use_gpu and _check_cupy():
148
- import cupy as cp
148
+ import cupy as cp # noqa: PLC0415
149
149
 
150
150
  logger.debug("apply_filter: using CuPy GPU backend")
151
151
  data_gpu = cp.asarray(data, dtype=cp.float64)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fullwave25
3
- Version: 1.2.6.dev2
3
+ Version: 1.2.6.dev4
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
@@ -1,11 +1,11 @@
1
- fullwave/__init__.py,sha256=N4GYVSocXju_a_cRIHpvcD7NZLZncFlAEoBefisiHd8,1780
1
+ fullwave/__init__.py,sha256=Wb8_IQQeLRrptNyonXRvriurzqUtnqV92_E4kr3MU4c,1780
2
2
  fullwave/grid.py,sha256=YbxAAgdYKR8AXwobtTZzHm9C4KR4D5--51zgbs0aQGY,7435
3
- fullwave/medium.py,sha256=NPg3NxLhR6roeroHM32lv-7rk0CALIExHrhSXpHh9MI,48302
4
- fullwave/sensor.py,sha256=q9T3_u8YS6b3Awo-FVqt-QYF05GFBhxqPO2OaL7Mvho,9311
5
- fullwave/source.py,sha256=dPh03qSh_-ASpmlC0HDFJGaolact2IP_wniXUoG6rUI,18758
6
- fullwave/transducer.py,sha256=LFS4tulRSwzU2R8FAvgif7oDSzirRsiXVm4wvqKoW50,48592
3
+ fullwave/medium.py,sha256=A3EoRNs1CM9gB2MISWp5n0ikRDEk9uQqEO6YSdp12jQ,48360
4
+ fullwave/sensor.py,sha256=McHyaSKxM11_Kfb_2hfnOtV9Z1UMBCLkeovncLT8_bA,9308
5
+ fullwave/source.py,sha256=gFitSme70CHecVVik31_Fv7Md7PEZ_4zGsKYohDivz4,18745
6
+ fullwave/transducer.py,sha256=uC9EcKJVpP9gTqKKLrq30sKizT1fhaRVDQcAuhuS9RI,48526
7
7
  fullwave/beamformer/__init__.py,sha256=tNPq_5lfLXfAdueyfQtdTxLTVEV6nEDkeSGAeTf7Q5s,88
8
- fullwave/beamformer/beamformer.py,sha256=cZzZDy3dS9YVUKA-O8TZAOqmlaziIAvXJTNVBkqUJf4,5587
8
+ fullwave/beamformer/beamformer.py,sha256=0jE_B5UlxDDBsUcQSHa5d1q3uSTSCyqgNiUJrU10juI,5601
9
9
  fullwave/constants/__init__.py,sha256=WOmkhLsSe86D71gwKsa4Tz8QPeOdayLGgvBCh7UEOPk,104
10
10
  fullwave/constants/material_properties.py,sha256=SouMKD4ONzYl0VU8ZsC2I_85y-ZLj9scRNwAArCD7bg,2729
11
11
  fullwave/medium_builder/__init__.py,sha256=QVz0sA4niq51O94RnXUVlHutx7FYEIa1TRcR5fDc_Ps,201
@@ -21,12 +21,12 @@ fullwave/medium_builder/presets/data/.keep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
21
21
  fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat,sha256=N3JFFxc9HW1S-NaTPcPlFgXWGIzlrr9X40JbDapTo4g,67348
22
22
  fullwave/solver/__init__.py,sha256=E0Jg5akBpicqTjku1puw9rAIoKiw7TwtYNDPiv8G5IY,30
23
23
  fullwave/solver/binary_manager.py,sha256=oz3WLUX2m0HHXCVaI8tBokDyHEUZcERzdCJWErE6Ig8,5011
24
- fullwave/solver/cuda_utils.py,sha256=iWa_6bicQwSsKGyp47hfAlyGBRZAGfN-sg2frzrQaVU,11352
24
+ fullwave/solver/cuda_utils.py,sha256=n7p3TJYbLAXmIzSMS5Q_8m4OWg5U9VG5_3hAxa0uTYI,11407
25
25
  fullwave/solver/input_file_writer.py,sha256=7wE86-ZnxwLhRw2adXUC8yAB3p9iH92E0t0Ew8q_lDQ,44617
26
- fullwave/solver/launcher.py,sha256=CsXSzcb7qphUeBAN8hGoFKuAYQO94_h__aoE_pJFgSE,9911
27
- fullwave/solver/pml_builder.py,sha256=hsjY1_uHLmb4KTGWGLzw0YPXT8q9zkZ53HEDzZqZFJs,70673
28
- fullwave/solver/solver.py,sha256=44SrAdojKEXCv9F9Z-PfAmTArjOBXkxG4dmrWNqu3Ro,41220
29
- fullwave/solver/utils.py,sha256=3lota1TDzwAo5kxlrvy30CEnSEyVe4Iqxnh4bpCBHiw,5389
26
+ fullwave/solver/launcher.py,sha256=zh-3HY4iKGmjreALu9WczDpWX3d74nOcIns9xVvnUIg,10531
27
+ fullwave/solver/pml_builder.py,sha256=gtkZjXG8ujvC0HMFjJ9RLaURQqjZKelLsj6FNE35P5k,70706
28
+ fullwave/solver/solver.py,sha256=Ziz7fOF_tq7ygv5MRhc2zULexcFK3Ft-raZl6JvwEcA,49836
29
+ fullwave/solver/utils.py,sha256=vM8Nwe4JFscb1hpkGoQHydsxC5_Xt93XQMmEOk5hVFg,5474
30
30
  fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda118,sha256=bIzyHFzr_bsIBSlCcRA3M9LlTSRrBVn6ASJHyozvcwU,1245432
31
31
  fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda124,sha256=NiYCmOzZIcvAzR7-Mjs3PtUzZGd31jSVQB1IT9nQrLU,1416160
32
32
  fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda129,sha256=TkyC8OqvFzRoSa_0XRWPmnlDBjPf8tG5NJaGQVFy_PY,1779992
@@ -37,17 +37,17 @@ fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda129,
37
37
  fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda130,sha256=8hzQfyodLl070a7nM-QCmv7ygijLMgRNvZxNOisRYSA,1571848
38
38
  fullwave/solver/bins/database/relaxation_params_database_num_relax=2_20260113_0957.mat,sha256=Fei5ie9wgAAN43pIdSFf6xVaPPDgOaBBZodi3qK6WtQ,168056
39
39
  fullwave/utils/__init__.py,sha256=yz_pbPte2XrSofb16mdqLmUxr2rXUE7FIzOjCT9y1IE,461
40
- fullwave/utils/check_functions.py,sha256=ezpwLvTSpriIfguvoWvdiVKjZxMy1m9diP6XZWDIL5E,1484
41
- fullwave/utils/coordinates.py,sha256=iIfvfrTQ1DA_WG6o2DbCtrjOY3wk076pKOmv7KsLIBs,4358
42
- fullwave/utils/memory_tempfile.py,sha256=5mBbIYDQ4lCt08HnQHsGy2Df5VWJa_6fDMyxIhPfw7U,14440
40
+ fullwave/utils/check_functions.py,sha256=benZqFjOQF7aaQDTBGaOq-eplJ-dC8Dz4jcqPE-9HSk,1514
41
+ fullwave/utils/coordinates.py,sha256=TEVQE9JXUIVFoKEyg8ZZMeGh31elbpecHnDgbljHQ14,4402
42
+ fullwave/utils/memory_tempfile.py,sha256=BzMRNvINzLYdtV8NcSNjrTr2F9Qow8vYl1o7RUXFWH8,14350
43
43
  fullwave/utils/numerical.py,sha256=khdi2KE3y_Ky9cEnmGXvVHjeYuKfb-8gQTV1a8nx9uA,3260
44
- fullwave/utils/plot_utils.py,sha256=PuOTYljHh5DeM6r6Bg_kh1gofa8OVtctKCX2k48Q2ho,37257
44
+ fullwave/utils/plot_utils.py,sha256=wXR6YMnlO0tCUeKoAELKRyi5K_kepKIBqLjlFSRKito,37205
45
45
  fullwave/utils/pulse.py,sha256=aqnCdBxivACouIGlot1aE2k4V9nctUxNCEPzdBpsupg,2811
46
46
  fullwave/utils/relaxation_parameters.py,sha256=DEHjr67eHGR-DqEaNsQViokseIsDywtjBjB1DvF4-OM,14106
47
47
  fullwave/utils/scatterer.py,sha256=CLqD085f9pqX4cF1YiT2IkzT5P0o1xp45Do7lf5BLnM,24049
48
- fullwave/utils/signal_filter.py,sha256=ZxnOXup8iQ-xFzVduvDNiPsq_3BvX6fQrqrHn7FTTdU,5193
48
+ fullwave/utils/signal_filter.py,sha256=_Tf42aBygGtIUrcSPybYqvZoAW2o_4dd-hHTEvZ8--E,5219
49
49
  fullwave/utils/signal_process.py,sha256=MP38r37JSPHN4sT-lIReXS5BT2GT_tkRxD0Y1wQ-lNs,5153
50
- fullwave25-1.2.6.dev2.dist-info/METADATA,sha256=Q4yUUXQOVVVeqmz0ZOOLWuvEq_8WEcK584Y2ZMp4LUM,19027
51
- fullwave25-1.2.6.dev2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
52
- fullwave25-1.2.6.dev2.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
53
- fullwave25-1.2.6.dev2.dist-info/RECORD,,
50
+ fullwave25-1.2.6.dev4.dist-info/METADATA,sha256=b2tSzbymlgrVigxYrdeWk8pvQ31Tzv9ZIGTQlNWW4Ac,18998
51
+ fullwave25-1.2.6.dev4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
52
+ fullwave25-1.2.6.dev4.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
53
+ fullwave25-1.2.6.dev4.dist-info/RECORD,,