wavesimpro 0.9.2__tar.gz → 0.11.0__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.
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/PKG-INFO +1 -1
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/pyproject.toml +1 -1
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/__init__.py +1 -1
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/execute.py +24 -5
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/simulate.py +38 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/validate.py +34 -10
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/PKG-INFO +1 -1
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/setup.cfg +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/__main__.py +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/binary_utils.py +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/client.py +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/config.py +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/exceptions.py +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/json_helper.py +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/py.typed +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/setup.py +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/SOURCES.txt +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/dependency_links.txt +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/requires.txt +0 -0
- {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "wavesimpro"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.11.0"
|
|
8
8
|
description = "WavesimPro Python API — cloud execution client for the Rayfos simulation platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -66,6 +66,8 @@ def execute_via_api(
|
|
|
66
66
|
preferred_machine_id: Optional[str] = None,
|
|
67
67
|
alpha: float = 0.75,
|
|
68
68
|
keep_residuals_list: bool = False,
|
|
69
|
+
strict_validation: bool = False,
|
|
70
|
+
gpu_memory_max_gb: Optional[float] = None,
|
|
69
71
|
) -> Tuple[np.ndarray, bool]:
|
|
70
72
|
"""
|
|
71
73
|
Execute a Wavesim simulation via the Rayfos REST API.
|
|
@@ -222,7 +224,9 @@ def execute_via_api(
|
|
|
222
224
|
# 7. Validate parameters
|
|
223
225
|
validate_parameters(
|
|
224
226
|
em_data, sources_info, wavelength, pixel_size,
|
|
225
|
-
threshold, max_iterations, boundary_width, is_vectorial
|
|
227
|
+
threshold, max_iterations, boundary_width, is_vectorial,
|
|
228
|
+
strict_validation=strict_validation,
|
|
229
|
+
gpu_memory_max_gb=gpu_memory_max_gb,
|
|
226
230
|
)
|
|
227
231
|
|
|
228
232
|
# 8. Upload RI/permittivity
|
|
@@ -280,12 +284,27 @@ def execute_via_api(
|
|
|
280
284
|
pol_map = {0: "x", 1: "y", 2: "z"}
|
|
281
285
|
pol_str = pol_map.get(pol_num, "none")
|
|
282
286
|
|
|
287
|
+
# Convert the user's source position (top-left corner in absolute
|
|
288
|
+
# corner-based μm — the MATLAB/Python convention) into the canonical
|
|
289
|
+
# WavesimProperties convention used by Rayfos Studio and consumed by
|
|
290
|
+
# the C# CommonServices: Position = CENTER of the source, expressed
|
|
291
|
+
# relative to the coordinate-system centre (which sits at the domain
|
|
292
|
+
# centre, world 0,0,0). Keeping every client emitting the same shape
|
|
293
|
+
# of WavesimProperties means the server has one coordinate path, not
|
|
294
|
+
# one per client.
|
|
295
|
+
src_extent_x = float(src_nx) * float(pixel_size)
|
|
296
|
+
src_extent_y = float(src_ny) * float(pixel_size)
|
|
297
|
+
src_extent_z = float(src_nz) * float(pixel_size)
|
|
298
|
+
position_x_centered = float(src["position"][0]) + src_extent_x / 2.0 - domain_x_um / 2.0
|
|
299
|
+
position_y_centered = float(src["position"][1]) + src_extent_y / 2.0 - domain_y_um / 2.0
|
|
300
|
+
position_z_centered = float(src["position"][2]) + src_extent_z / 2.0 - domain_z_um / 2.0
|
|
301
|
+
|
|
283
302
|
custom_source = create_custom_field_source(
|
|
284
303
|
name=f"source_{i + 1}",
|
|
285
304
|
field_data_id=source_ids[i],
|
|
286
|
-
position_x=
|
|
287
|
-
position_y=
|
|
288
|
-
position_z=
|
|
305
|
+
position_x=position_x_centered,
|
|
306
|
+
position_y=position_y_centered,
|
|
307
|
+
position_z=position_z_centered,
|
|
289
308
|
grid_nx=src_nx,
|
|
290
309
|
grid_ny=src_ny,
|
|
291
310
|
grid_nz=src_nz,
|
|
@@ -293,7 +312,7 @@ def execute_via_api(
|
|
|
293
312
|
amplitude_real=1.0,
|
|
294
313
|
amplitude_imag=0.0,
|
|
295
314
|
data_format="Complex64",
|
|
296
|
-
origin="
|
|
315
|
+
origin="center",
|
|
297
316
|
)
|
|
298
317
|
input_sources.append(custom_source)
|
|
299
318
|
|
|
@@ -27,6 +27,11 @@ def _caller_script_name() -> str:
|
|
|
27
27
|
return Path(path).stem
|
|
28
28
|
return "wavesimpro"
|
|
29
29
|
|
|
30
|
+
# Sentinel for configure() — distinguishes "not passed" from explicit None,
|
|
31
|
+
# which is meaningful for use_gpu (None = auto), gpu_memory_max_gb (None = default cap)
|
|
32
|
+
# and strict_validation (False = default).
|
|
33
|
+
_UNSET = object()
|
|
34
|
+
|
|
30
35
|
# Module-level defaults — overridden by configure() or per-call kwargs
|
|
31
36
|
_defaults = {
|
|
32
37
|
"api_key": None,
|
|
@@ -35,6 +40,8 @@ _defaults = {
|
|
|
35
40
|
"poll_interval": 10.0,
|
|
36
41
|
"engine_preference": "Any",
|
|
37
42
|
"use_gpu": None,
|
|
43
|
+
"strict_validation": False,
|
|
44
|
+
"gpu_memory_max_gb": None,
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
|
|
@@ -46,6 +53,8 @@ def configure(
|
|
|
46
53
|
poll_interval: Optional[float] = None,
|
|
47
54
|
engine_preference: Optional[str] = None,
|
|
48
55
|
use_gpu: Optional[bool] = None,
|
|
56
|
+
strict_validation=_UNSET,
|
|
57
|
+
gpu_memory_max_gb=_UNSET,
|
|
49
58
|
):
|
|
50
59
|
"""Set module-level defaults for remote execution.
|
|
51
60
|
|
|
@@ -59,6 +68,11 @@ def configure(
|
|
|
59
68
|
poll_interval: Status polling interval in seconds.
|
|
60
69
|
engine_preference: 'Any' (default), 'Python', or 'Cpp'.
|
|
61
70
|
use_gpu: None (auto), True (require GPU), False (force CPU).
|
|
71
|
+
strict_validation: When True, soft validation issues (GPU memory cap
|
|
72
|
+
exceeded, source field larger than domain) raise instead of
|
|
73
|
+
warning. Default False — simulations are not stopped by these.
|
|
74
|
+
gpu_memory_max_gb: Override the soft GPU memory cap (default 65 GB).
|
|
75
|
+
Pass math.inf to disable entirely.
|
|
62
76
|
"""
|
|
63
77
|
if api_key is not None:
|
|
64
78
|
_defaults["api_key"] = api_key
|
|
@@ -72,6 +86,10 @@ def configure(
|
|
|
72
86
|
_defaults["engine_preference"] = engine_preference
|
|
73
87
|
if use_gpu is not None:
|
|
74
88
|
_defaults["use_gpu"] = use_gpu
|
|
89
|
+
if strict_validation is not _UNSET:
|
|
90
|
+
_defaults["strict_validation"] = bool(strict_validation)
|
|
91
|
+
if gpu_memory_max_gb is not _UNSET:
|
|
92
|
+
_defaults["gpu_memory_max_gb"] = gpu_memory_max_gb
|
|
75
93
|
|
|
76
94
|
|
|
77
95
|
def simulate(
|
|
@@ -89,6 +107,8 @@ def simulate(
|
|
|
89
107
|
full_residuals: bool = False,
|
|
90
108
|
crop_boundaries: bool = True, # local only — accepted and ignored
|
|
91
109
|
callback: Optional[Callable] = None,
|
|
110
|
+
strict_validation: Optional[bool] = None,
|
|
111
|
+
gpu_memory_max_gb=_UNSET,
|
|
92
112
|
**kwargs,
|
|
93
113
|
):
|
|
94
114
|
"""Execute a Wavesim simulation remotely via the Rayfos API.
|
|
@@ -122,6 +142,12 @@ def simulate(
|
|
|
122
142
|
poll_interval = _defaults["poll_interval"]
|
|
123
143
|
engine_preference = _defaults["engine_preference"]
|
|
124
144
|
effective_use_gpu = use_gpu if use_gpu is not None else _defaults["use_gpu"]
|
|
145
|
+
effective_strict = (
|
|
146
|
+
_defaults["strict_validation"] if strict_validation is None else bool(strict_validation)
|
|
147
|
+
)
|
|
148
|
+
effective_gpu_max = (
|
|
149
|
+
_defaults["gpu_memory_max_gb"] if gpu_memory_max_gb is _UNSET else gpu_memory_max_gb
|
|
150
|
+
)
|
|
125
151
|
|
|
126
152
|
is_vectorial = any(len(s[1]) == 4 for s in sources)
|
|
127
153
|
boundary_width_px = int(np.round(boundary_width / pixel_size))
|
|
@@ -169,6 +195,8 @@ def simulate(
|
|
|
169
195
|
alpha=alpha,
|
|
170
196
|
keep_residuals_list=full_residuals,
|
|
171
197
|
progress_callback=_progress_cb,
|
|
198
|
+
strict_validation=effective_strict,
|
|
199
|
+
gpu_memory_max_gb=effective_gpu_max,
|
|
172
200
|
)
|
|
173
201
|
|
|
174
202
|
iterations = _last["iteration"] or 1
|
|
@@ -201,6 +229,8 @@ def simulate_pro(
|
|
|
201
229
|
poll_interval: float = 10.0,
|
|
202
230
|
description: Optional[str] = None,
|
|
203
231
|
server: Optional[str] = None,
|
|
232
|
+
strict_validation: Optional[bool] = None,
|
|
233
|
+
gpu_memory_max_gb=_UNSET,
|
|
204
234
|
**kwargs,
|
|
205
235
|
):
|
|
206
236
|
"""Execute a Wavesim simulation remotely with full control over execution options.
|
|
@@ -227,6 +257,12 @@ def simulate_pro(
|
|
|
227
257
|
|
|
228
258
|
effective_description = description or _defaults["description"] or _caller_script_name()
|
|
229
259
|
effective_use_gpu = use_gpu if use_gpu is not None else _defaults["use_gpu"]
|
|
260
|
+
effective_strict = (
|
|
261
|
+
_defaults["strict_validation"] if strict_validation is None else bool(strict_validation)
|
|
262
|
+
)
|
|
263
|
+
effective_gpu_max = (
|
|
264
|
+
_defaults["gpu_memory_max_gb"] if gpu_memory_max_gb is _UNSET else gpu_memory_max_gb
|
|
265
|
+
)
|
|
230
266
|
|
|
231
267
|
is_vectorial = any(len(s[1]) == 4 for s in sources)
|
|
232
268
|
boundary_width_px = int(np.round(boundary_width / pixel_size))
|
|
@@ -275,6 +311,8 @@ def simulate_pro(
|
|
|
275
311
|
alpha=alpha,
|
|
276
312
|
keep_residuals_list=full_residuals,
|
|
277
313
|
progress_callback=_progress_cb,
|
|
314
|
+
strict_validation=effective_strict,
|
|
315
|
+
gpu_memory_max_gb=effective_gpu_max,
|
|
278
316
|
)
|
|
279
317
|
|
|
280
318
|
iterations = _last["iteration"] or 1
|
|
@@ -24,6 +24,10 @@ class ParameterValidationError(Exception):
|
|
|
24
24
|
super().__init__(msg)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
+
DEFAULT_GPU_MEMORY_OK_GB = 16
|
|
28
|
+
DEFAULT_GPU_MEMORY_MAX_GB = 65
|
|
29
|
+
|
|
30
|
+
|
|
27
31
|
def validate_parameters(
|
|
28
32
|
refractive_index: np.ndarray,
|
|
29
33
|
sources: List[Dict[str, Any]],
|
|
@@ -33,6 +37,8 @@ def validate_parameters(
|
|
|
33
37
|
max_iterations: int = 1000,
|
|
34
38
|
boundary_width: int = 0,
|
|
35
39
|
is_vectorial: bool = True,
|
|
40
|
+
strict_validation: bool = False,
|
|
41
|
+
gpu_memory_max_gb: Optional[float] = None,
|
|
36
42
|
) -> List[str]:
|
|
37
43
|
"""
|
|
38
44
|
Validate simulation parameters before upload.
|
|
@@ -46,16 +52,30 @@ def validate_parameters(
|
|
|
46
52
|
max_iterations: Maximum iterations
|
|
47
53
|
boundary_width: Boundary width in pixels
|
|
48
54
|
is_vectorial: True for Maxwell, False for Helmholtz
|
|
55
|
+
strict_validation: If True, "soft" issues (GPU memory cap exceeded,
|
|
56
|
+
source field larger than domain) raise ParameterValidationError.
|
|
57
|
+
If False (default), they are emitted as warnings and the
|
|
58
|
+
simulation proceeds. Hard issues (NaN/Inf, empty arrays, missing
|
|
59
|
+
wavelength/pixel_size) always raise regardless of this flag.
|
|
60
|
+
gpu_memory_max_gb: Hard cap (GB) above which estimated GPU memory
|
|
61
|
+
triggers a soft error. Default 65. Pass math.inf (or any large
|
|
62
|
+
value) to disable the cap entirely.
|
|
49
63
|
|
|
50
64
|
Returns:
|
|
51
65
|
List of warning strings (empty if no warnings)
|
|
52
66
|
|
|
53
67
|
Raises:
|
|
54
|
-
ParameterValidationError: If any
|
|
68
|
+
ParameterValidationError: If any hard error is found, or if any soft
|
|
69
|
+
error is found while strict_validation=True.
|
|
55
70
|
"""
|
|
56
71
|
errors: List[str] = []
|
|
72
|
+
soft_errors: List[str] = []
|
|
57
73
|
warnings: List[str] = []
|
|
58
74
|
|
|
75
|
+
gpu_memory_max_gb = (
|
|
76
|
+
DEFAULT_GPU_MEMORY_MAX_GB if gpu_memory_max_gb is None else float(gpu_memory_max_gb)
|
|
77
|
+
)
|
|
78
|
+
|
|
59
79
|
logger.info("Validating simulation parameters...")
|
|
60
80
|
|
|
61
81
|
# 1. Validate refractive index / permittivity array
|
|
@@ -172,19 +192,17 @@ def validate_parameters(
|
|
|
172
192
|
bytes_per_voxel = 120 if is_vectorial else 40
|
|
173
193
|
gpu_memory_gb = (total_voxels * bytes_per_voxel) / (1024 ** 3)
|
|
174
194
|
|
|
175
|
-
GPU_MEMORY_OK_GB = 16
|
|
176
|
-
GPU_MEMORY_MAX_GB = 65
|
|
177
|
-
|
|
178
195
|
logger.info(f" GPU memory estimate: {gpu_memory_gb:.2f} GB ({'Vectorial' if is_vectorial else 'Scalar'})")
|
|
179
196
|
|
|
180
|
-
if gpu_memory_gb >
|
|
181
|
-
|
|
197
|
+
if gpu_memory_gb > gpu_memory_max_gb:
|
|
198
|
+
soft_errors.append(
|
|
182
199
|
f"Domain size too large for GPU. Estimated memory: {gpu_memory_gb:.2f} GB "
|
|
183
|
-
f"exceeds
|
|
200
|
+
f"exceeds cap {gpu_memory_max_gb:.0f} GB. Reduce domain size or pixel count, "
|
|
201
|
+
f"raise gpu_memory_max_gb, or keep strict_validation=False to proceed anyway."
|
|
184
202
|
)
|
|
185
|
-
elif gpu_memory_gb >
|
|
203
|
+
elif gpu_memory_gb > DEFAULT_GPU_MEMORY_OK_GB:
|
|
186
204
|
warnings.append(
|
|
187
|
-
f"GPU memory {gpu_memory_gb:.2f} GB exceeds standard {
|
|
205
|
+
f"GPU memory {gpu_memory_gb:.2f} GB exceeds standard {DEFAULT_GPU_MEMORY_OK_GB:.0f} GB. "
|
|
188
206
|
f"Will use advanced GPU instance."
|
|
189
207
|
)
|
|
190
208
|
|
|
@@ -211,7 +229,7 @@ def validate_parameters(
|
|
|
211
229
|
while len(src_shape) < 3:
|
|
212
230
|
src_shape = src_shape + (1,)
|
|
213
231
|
if src_shape[0] > nx or src_shape[1] > ny or src_shape[2] > nz:
|
|
214
|
-
|
|
232
|
+
soft_errors.append(
|
|
215
233
|
f"Source {i + 1}: field size [{src_shape[0]},{src_shape[1]},{src_shape[2]}] "
|
|
216
234
|
f"exceeds domain [{nx},{ny},{nz}]"
|
|
217
235
|
)
|
|
@@ -239,6 +257,12 @@ def validate_parameters(
|
|
|
239
257
|
if pol is not None and is_vectorial and pol not in (0, 1, 2):
|
|
240
258
|
warnings.append(f"Source {i + 1}: invalid polarization {pol} for vectorial simulation (expected 0=x, 1=y, 2=z)")
|
|
241
259
|
|
|
260
|
+
# In non-strict mode, soft errors are downgraded to warnings.
|
|
261
|
+
if strict_validation:
|
|
262
|
+
errors = errors + soft_errors
|
|
263
|
+
else:
|
|
264
|
+
warnings = warnings + soft_errors
|
|
265
|
+
|
|
242
266
|
# Report warnings
|
|
243
267
|
for w in warnings:
|
|
244
268
|
logger.warning(f" [!] {w}")
|
|
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
|