fullwave25 1.2.6.dev2__tar.gz → 1.2.6.dev4__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 (76) hide show
  1. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/.bumpversion.toml +1 -1
  2. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/.pre-commit-config.yaml +1 -1
  3. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/PKG-INFO +1 -2
  4. fullwave25-1.2.6.dev4/debug_solver_bin/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
  5. fullwave25-1.2.6.dev4/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu +0 -0
  6. fullwave25-1.2.6.dev4/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124_ +0 -0
  7. fullwave25-1.2.6.dev4/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124_ +0 -0
  8. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/__init__.py +1 -1
  9. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/beamformer/beamformer.py +2 -1
  10. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium.py +8 -4
  11. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/sensor.py +3 -2
  12. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/cuda_utils.py +10 -5
  13. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/launcher.py +20 -3
  14. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/pml_builder.py +46 -50
  15. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/solver.py +259 -1
  16. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/utils.py +16 -8
  17. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/source.py +1 -1
  18. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/transducer.py +12 -11
  19. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/check_functions.py +6 -3
  20. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/coordinates.py +8 -4
  21. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/memory_tempfile.py +7 -7
  22. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/plot_utils.py +3 -3
  23. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/signal_filter.py +2 -2
  24. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/pyproject.toml +11 -3
  25. fullwave25-1.2.6.dev4/ruff.log +1177 -0
  26. fullwave25-1.2.6.dev4/ruff.toml +140 -0
  27. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/uv.lock +1 -12
  28. fullwave25-1.2.6.dev2/ruff.toml +0 -114
  29. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/.gitignore +0 -0
  30. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/.python-version +0 -0
  31. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/20260120_0844.log +0 -0
  32. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/CITATION.cff +0 -0
  33. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/CLAUDE.md +0 -0
  34. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/CONTRIBUTING.md +0 -0
  35. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/LICENSE +0 -0
  36. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/Makefile +0 -0
  37. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/README.md +0 -0
  38. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/beamformer.m +0 -0
  39. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/debug_solver_bin/fullwave2_2d_exponential_attenuation_multi_gpu_cuda124 +0 -0
  40. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/debug_solver_bin/fullwave2_3d_exponential_attenuation_multi_gpu_cuda124 +0 -0
  41. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/experiments/__init__.py +0 -0
  42. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/experiments/exp_scatterer_diameter.py +0 -0
  43. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/beamformer/__init__.py +0 -0
  44. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/constants/__init__.py +0 -0
  45. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/constants/material_properties.py +0 -0
  46. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/grid.py +0 -0
  47. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/__init__.py +0 -0
  48. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/domain.py +0 -0
  49. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/medium_builder.py +0 -0
  50. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/presets/__init__.py +0 -0
  51. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/presets/data/.keep +0 -0
  52. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/presets/data/abdominal_wall/i2365f_etfw1.mat +0 -0
  53. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/presets/domain_abdominal_wall.py +0 -0
  54. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/presets/domain_background.py +0 -0
  55. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/presets/domain_scatterer.py +0 -0
  56. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/presets/domain_simple.py +0 -0
  57. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/medium_builder/presets/domain_water_gel.py +0 -0
  58. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/__init__.py +0 -0
  59. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/binary_manager.py +0 -0
  60. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda118 +0 -0
  61. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda124 +0 -0
  62. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda129 +0 -0
  63. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/_gpu/2d/num_relax=2/fullwave2_2d_2_relax_multi_gpu_cuda130 +0 -0
  64. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda118 +0 -0
  65. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda124 +0 -0
  66. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda129 +0 -0
  67. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/_gpu/3d/num_relax=2/fullwave2_3d_2_relax_multi_gpu_cuda130 +0 -0
  68. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/bins/database/relaxation_params_database_num_relax=2_20260113_0957.mat +0 -0
  69. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/solver/input_file_writer.py +0 -0
  70. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/__init__.py +0 -0
  71. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/numerical.py +0 -0
  72. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/pulse.py +0 -0
  73. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/relaxation_parameters.py +0 -0
  74. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/scatterer.py +0 -0
  75. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/fullwave/utils/signal_process.py +0 -0
  76. {fullwave25-1.2.6.dev2 → fullwave25-1.2.6.dev4}/signal_filter_example.png +0 -0
@@ -1,5 +1,5 @@
1
1
  [tool.bumpversion]
2
- current_version = "1.2.6-dev2"
2
+ current_version = "1.2.6-dev4"
3
3
  parse = """(?x)
4
4
  (?P<major>0|[1-9]\\d*)\\.
5
5
  (?P<minor>0|[1-9]\\d*)\\.
@@ -11,7 +11,7 @@ repos:
11
11
  args: ["--maxkb=10000"]
12
12
  - repo: https://github.com/astral-sh/ruff-pre-commit
13
13
  # Ruff version.
14
- rev: v0.11.0
14
+ rev: v0.15.2
15
15
  hooks:
16
16
  # Run the linter.
17
17
  - id: ruff
@@ -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
@@ -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
 
@@ -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
 
@@ -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,