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.
Files changed (20) hide show
  1. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/PKG-INFO +1 -1
  2. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/pyproject.toml +1 -1
  3. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/__init__.py +1 -1
  4. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/execute.py +24 -5
  5. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/simulate.py +38 -0
  6. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/validate.py +34 -10
  7. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/PKG-INFO +1 -1
  8. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/setup.cfg +0 -0
  9. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/__main__.py +0 -0
  10. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/binary_utils.py +0 -0
  11. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/client.py +0 -0
  12. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/config.py +0 -0
  13. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/exceptions.py +0 -0
  14. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/json_helper.py +0 -0
  15. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/py.typed +0 -0
  16. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro/setup.py +0 -0
  17. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/SOURCES.txt +0 -0
  18. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/dependency_links.txt +0 -0
  19. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/requires.txt +0 -0
  20. {wavesimpro-0.9.2 → wavesimpro-0.11.0}/src/wavesimpro.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavesimpro
3
- Version: 0.9.2
3
+ Version: 0.11.0
4
4
  Summary: WavesimPro Python API — cloud execution client for the Rayfos simulation platform
5
5
  Author: Rayfos
6
6
  Project-URL: Repository, https://github.com/rayfos/wavesimpro
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "wavesimpro"
7
- version = "0.9.2"
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"
@@ -49,7 +49,7 @@ from .json_helper import (
49
49
  parse_progress_data,
50
50
  )
51
51
 
52
- __version__ = "0.9.2"
52
+ __version__ = "0.11.0"
53
53
  __all__ = [
54
54
  # Client
55
55
  "RayfosClient",
@@ -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=src["position"][0],
287
- position_y=src["position"][1],
288
- position_z=src["position"][2],
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="topleft",
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 parameter is invalid
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 > GPU_MEMORY_MAX_GB:
181
- errors.append(
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 maximum {GPU_MEMORY_MAX_GB:.0f} GB. Reduce domain size or pixel count."
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 > GPU_MEMORY_OK_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 {GPU_MEMORY_OK_GB:.0f} GB. "
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
- errors.append(
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}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wavesimpro
3
- Version: 0.9.2
3
+ Version: 0.11.0
4
4
  Summary: WavesimPro Python API — cloud execution client for the Rayfos simulation platform
5
5
  Author: Rayfos
6
6
  Project-URL: Repository, https://github.com/rayfos/wavesimpro
File without changes