wavesimpro 0.9.0__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.
- wavesimpro/__init__.py +98 -0
- wavesimpro/__main__.py +29 -0
- wavesimpro/binary_utils.py +936 -0
- wavesimpro/client.py +1475 -0
- wavesimpro/config.py +123 -0
- wavesimpro/exceptions.py +40 -0
- wavesimpro/execute.py +461 -0
- wavesimpro/json_helper.py +755 -0
- wavesimpro/py.typed +4 -0
- wavesimpro/setup.py +162 -0
- wavesimpro/simulate.py +162 -0
- wavesimpro/validate.py +251 -0
- wavesimpro-0.9.0.dist-info/METADATA +21 -0
- wavesimpro-0.9.0.dist-info/RECORD +16 -0
- wavesimpro-0.9.0.dist-info/WHEEL +5 -0
- wavesimpro-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,755 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Helper functions to build WavesimProperties JSON structure
|
|
3
|
+
|
|
4
|
+
This module provides Python functions to build the correct JSON structure
|
|
5
|
+
that matches RayfosCommandModels.WavesimProperties C# class.
|
|
6
|
+
|
|
7
|
+
Ported from examples/wavesim_json_helper.py with fixes for MATLAB API parity.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import uuid
|
|
12
|
+
from typing import List, Dict, Any, Optional
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def create_simulation_domain(
|
|
16
|
+
size_x: float = 10.0,
|
|
17
|
+
size_y: float = 10.0,
|
|
18
|
+
size_z: float = 10.0,
|
|
19
|
+
center_x: float = 0.0,
|
|
20
|
+
center_y: float = 0.0,
|
|
21
|
+
center_z: float = 0.0,
|
|
22
|
+
unit: str = "Micrometers"
|
|
23
|
+
) -> Dict[str, Any]:
|
|
24
|
+
"""
|
|
25
|
+
Create SimulationDomain structure
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
size_x, size_y, size_z: Domain size (width, height, depth)
|
|
29
|
+
center_x, center_y, center_z: Domain center position
|
|
30
|
+
unit: Unit of measurement (Micrometers, Nanometers, etc.)
|
|
31
|
+
"""
|
|
32
|
+
return {
|
|
33
|
+
"PositioningMode": "CenterAndSize",
|
|
34
|
+
"Position": {
|
|
35
|
+
"X": {"Value": center_x, "Unit": unit},
|
|
36
|
+
"Y": {"Value": center_y, "Unit": unit},
|
|
37
|
+
"Z": {"Value": center_z, "Unit": unit}
|
|
38
|
+
},
|
|
39
|
+
"Size": {
|
|
40
|
+
"Width": {"Value": size_x, "Unit": unit},
|
|
41
|
+
"Height": {"Value": size_y, "Unit": unit},
|
|
42
|
+
"Depth": {"Value": size_z, "Unit": unit}
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def create_stl_primitive(
|
|
48
|
+
name: str,
|
|
49
|
+
file_id: str,
|
|
50
|
+
medium_id: str,
|
|
51
|
+
center_x: float = 0.0,
|
|
52
|
+
center_y: float = 0.0,
|
|
53
|
+
center_z: float = 0.0,
|
|
54
|
+
rotation_x: float = 0.0,
|
|
55
|
+
rotation_y: float = 0.0,
|
|
56
|
+
rotation_z: float = 0.0,
|
|
57
|
+
scale_x: float = 1.0,
|
|
58
|
+
scale_y: float = 1.0,
|
|
59
|
+
scale_z: float = 1.0,
|
|
60
|
+
unit: str = "Micrometers"
|
|
61
|
+
) -> Dict[str, Any]:
|
|
62
|
+
"""
|
|
63
|
+
Create STL primitive structure
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
name: Primitive name
|
|
67
|
+
file_id: ID or filename of uploaded STL file
|
|
68
|
+
medium_id: Medium/Material ID to use
|
|
69
|
+
center_x, center_y, center_z: Position
|
|
70
|
+
rotation_x, rotation_y, rotation_z: Rotation angles in degrees
|
|
71
|
+
scale_x, scale_y, scale_z: Scale factors
|
|
72
|
+
unit: Unit of measurement
|
|
73
|
+
"""
|
|
74
|
+
return {
|
|
75
|
+
"Id": str(uuid.uuid4()),
|
|
76
|
+
"Name": name,
|
|
77
|
+
"ShapeType": "StlModel",
|
|
78
|
+
"MediumId": medium_id,
|
|
79
|
+
"PositioningMode": "CenterAndSize",
|
|
80
|
+
"Position": {
|
|
81
|
+
"X": {"Value": center_x, "Unit": unit},
|
|
82
|
+
"Y": {"Value": center_y, "Unit": unit},
|
|
83
|
+
"Z": {"Value": center_z, "Unit": unit}
|
|
84
|
+
},
|
|
85
|
+
"PositionAnchor": "Center",
|
|
86
|
+
"Rotation": {
|
|
87
|
+
"X": rotation_x,
|
|
88
|
+
"Y": rotation_y,
|
|
89
|
+
"Z": rotation_z
|
|
90
|
+
},
|
|
91
|
+
"StlDataId": file_id,
|
|
92
|
+
"ScaleFactors": {
|
|
93
|
+
"X": scale_x,
|
|
94
|
+
"Y": scale_y,
|
|
95
|
+
"Z": scale_z
|
|
96
|
+
},
|
|
97
|
+
"_tempStlData": {}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def create_box_primitive(
|
|
102
|
+
name: str,
|
|
103
|
+
medium_id: str,
|
|
104
|
+
center_x: float,
|
|
105
|
+
center_y: float,
|
|
106
|
+
center_z: float,
|
|
107
|
+
size_x: float,
|
|
108
|
+
size_y: float,
|
|
109
|
+
size_z: float,
|
|
110
|
+
unit: str = "Micrometers"
|
|
111
|
+
) -> Dict[str, Any]:
|
|
112
|
+
"""Create a box primitive"""
|
|
113
|
+
return {
|
|
114
|
+
"Type": "Box",
|
|
115
|
+
"Name": name,
|
|
116
|
+
"MediumId": medium_id,
|
|
117
|
+
"PositioningMode": "CenterAndSize",
|
|
118
|
+
"Position": {
|
|
119
|
+
"X": {"Value": center_x, "Unit": unit},
|
|
120
|
+
"Y": {"Value": center_y, "Unit": unit},
|
|
121
|
+
"Z": {"Value": center_z, "Unit": unit}
|
|
122
|
+
},
|
|
123
|
+
"Size": {
|
|
124
|
+
"X": {"Value": size_x, "Unit": unit},
|
|
125
|
+
"Y": {"Value": size_y, "Unit": unit},
|
|
126
|
+
"Z": {"Value": size_z, "Unit": unit}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def create_medium(
|
|
132
|
+
medium_id: str,
|
|
133
|
+
name: str,
|
|
134
|
+
refractive_index_real: float,
|
|
135
|
+
refractive_index_imag: float = 0.0,
|
|
136
|
+
category: str = "dielectric",
|
|
137
|
+
description: Optional[str] = None,
|
|
138
|
+
color: Optional[str] = None
|
|
139
|
+
) -> Dict[str, Any]:
|
|
140
|
+
"""
|
|
141
|
+
Create medium/material structure
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
medium_id: Unique ID for this medium (lowercase with hyphens)
|
|
145
|
+
name: Display name
|
|
146
|
+
refractive_index_real: Real part of refractive index
|
|
147
|
+
refractive_index_imag: Imaginary part (for absorption)
|
|
148
|
+
category: Material category ("gas", "dielectric", "semiconductor", "metal")
|
|
149
|
+
description: Optional description
|
|
150
|
+
color: Hex color code (e.g., "#FF0000")
|
|
151
|
+
"""
|
|
152
|
+
medium = {
|
|
153
|
+
"id": medium_id,
|
|
154
|
+
"name": name,
|
|
155
|
+
"category": category,
|
|
156
|
+
"mediumType": "medium",
|
|
157
|
+
"inputMethod": "refractive_index",
|
|
158
|
+
"primaryReal": refractive_index_real,
|
|
159
|
+
"primaryImaginary": refractive_index_imag,
|
|
160
|
+
"permeabilityReal": 1,
|
|
161
|
+
"permeabilityImaginary": 0
|
|
162
|
+
}
|
|
163
|
+
if description:
|
|
164
|
+
medium["description"] = description
|
|
165
|
+
if color:
|
|
166
|
+
medium["Color"] = color
|
|
167
|
+
return medium
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def create_point_source(
|
|
171
|
+
name: str = "PointSource1",
|
|
172
|
+
position_x: float = 0.0,
|
|
173
|
+
position_y: float = 0.0,
|
|
174
|
+
position_z: float = 0.0,
|
|
175
|
+
amplitude_real: float = 1.0,
|
|
176
|
+
amplitude_imag: float = 0.0,
|
|
177
|
+
polarization: str = "None",
|
|
178
|
+
unit: str = "Micrometers"
|
|
179
|
+
) -> Dict[str, Any]:
|
|
180
|
+
"""
|
|
181
|
+
Create Point source
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
name: Source name
|
|
185
|
+
position_x, position_y, position_z: Source position
|
|
186
|
+
amplitude_real, amplitude_imag: Complex amplitude
|
|
187
|
+
polarization: "None", "X", "Y", "Z"
|
|
188
|
+
unit: Unit of measurement
|
|
189
|
+
"""
|
|
190
|
+
return {
|
|
191
|
+
"Id": str(uuid.uuid4()),
|
|
192
|
+
"Name": name,
|
|
193
|
+
"SourceType": "Point",
|
|
194
|
+
"isEnabled": True,
|
|
195
|
+
"UseInternalPythonSource": False,
|
|
196
|
+
"Position": {
|
|
197
|
+
"X": {"Value": position_x, "Unit": unit},
|
|
198
|
+
"Y": {"Value": position_y, "Unit": unit},
|
|
199
|
+
"Z": {"Value": position_z, "Unit": unit}
|
|
200
|
+
},
|
|
201
|
+
"Polarization": polarization,
|
|
202
|
+
"Amplitude": {
|
|
203
|
+
"Real": amplitude_real,
|
|
204
|
+
"Imaginary": amplitude_imag
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def create_plane_wave_source(
|
|
210
|
+
name: str = "PlaneWave1",
|
|
211
|
+
position_x: float = 0.0,
|
|
212
|
+
position_y: float = 0.0,
|
|
213
|
+
position_z: float = 0.0,
|
|
214
|
+
source_plane: str = "XZ",
|
|
215
|
+
amplitude_real: float = 1.0,
|
|
216
|
+
amplitude_imag: float = 0.0,
|
|
217
|
+
size_x: float = 10.0,
|
|
218
|
+
size_y: float = 10.0,
|
|
219
|
+
size_z: float = 10.0,
|
|
220
|
+
polarization: str = "None",
|
|
221
|
+
theta: Optional[float] = None,
|
|
222
|
+
phi: Optional[float] = None,
|
|
223
|
+
origin: str = "center",
|
|
224
|
+
unit: str = "Micrometers"
|
|
225
|
+
) -> Dict[str, Any]:
|
|
226
|
+
"""
|
|
227
|
+
Create PlaneWave source
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
name: Source name
|
|
231
|
+
position_x, position_y, position_z: Source position
|
|
232
|
+
source_plane: "X", "Y", "Z", "XY", "YZ", "XZ" (UPPERCASE)
|
|
233
|
+
amplitude_real, amplitude_imag: Complex amplitude
|
|
234
|
+
size_x, size_y, size_z: Source dimensions
|
|
235
|
+
polarization: "None", "X", "Y", "Z" (UPPERCASE)
|
|
236
|
+
theta: Incident angle in radians (optional)
|
|
237
|
+
phi: Azimuthal angle in radians (optional)
|
|
238
|
+
origin: "center" or "topleft"
|
|
239
|
+
unit: Unit of measurement
|
|
240
|
+
"""
|
|
241
|
+
source = {
|
|
242
|
+
"SourceType": "PlaneWave",
|
|
243
|
+
"Name": name,
|
|
244
|
+
"IsEnabled": True,
|
|
245
|
+
"Position": {
|
|
246
|
+
"X": {"Value": position_x, "Unit": unit},
|
|
247
|
+
"Y": {"Value": position_y, "Unit": unit},
|
|
248
|
+
"Z": {"Value": position_z, "Unit": unit}
|
|
249
|
+
},
|
|
250
|
+
"Polarization": polarization,
|
|
251
|
+
"Amplitude": {
|
|
252
|
+
"Real": amplitude_real,
|
|
253
|
+
"Imaginary": amplitude_imag
|
|
254
|
+
},
|
|
255
|
+
"SourcePlane": source_plane,
|
|
256
|
+
"Size": {
|
|
257
|
+
"Width": {"Value": size_x, "Unit": unit},
|
|
258
|
+
"Height": {"Value": size_y, "Unit": unit},
|
|
259
|
+
"Depth": {"Value": size_z, "Unit": unit}
|
|
260
|
+
},
|
|
261
|
+
"Origin": origin
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if theta is not None:
|
|
265
|
+
source["Theta"] = theta
|
|
266
|
+
if phi is not None:
|
|
267
|
+
source["Phi"] = phi
|
|
268
|
+
|
|
269
|
+
return source
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def create_gaussian_beam_source(
|
|
273
|
+
name: str = "GaussianBeam1",
|
|
274
|
+
position_x: float = 0.0,
|
|
275
|
+
position_y: float = 0.0,
|
|
276
|
+
position_z: float = 0.0,
|
|
277
|
+
source_plane: str = "XZ",
|
|
278
|
+
amplitude_real: float = 1.0,
|
|
279
|
+
amplitude_imag: float = 0.0,
|
|
280
|
+
size_x: float = 10.0,
|
|
281
|
+
size_y: float = 10.0,
|
|
282
|
+
size_z: float = 10.0,
|
|
283
|
+
polarization: str = "None",
|
|
284
|
+
alpha: float = 3.0,
|
|
285
|
+
sigma: Optional[float] = None,
|
|
286
|
+
theta: Optional[float] = None,
|
|
287
|
+
phi: Optional[float] = None,
|
|
288
|
+
origin: str = "center",
|
|
289
|
+
unit: str = "Micrometers"
|
|
290
|
+
) -> Dict[str, Any]:
|
|
291
|
+
"""
|
|
292
|
+
Create GaussianBeam source
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
name: Source name
|
|
296
|
+
position_x, position_y, position_z: Source position
|
|
297
|
+
source_plane: "X", "Y", "Z", "XY", "YZ", "XZ"
|
|
298
|
+
amplitude_real, amplitude_imag: Complex amplitude
|
|
299
|
+
size_x, size_y, size_z: Source dimensions
|
|
300
|
+
polarization: "None", "X", "Y", "Z"
|
|
301
|
+
alpha: Gaussian width factor (default: 3.0)
|
|
302
|
+
sigma: Standard deviation (optional, overrides alpha)
|
|
303
|
+
theta: Incident angle in radians (optional)
|
|
304
|
+
phi: Azimuthal angle in radians (optional)
|
|
305
|
+
origin: "center" or "topleft"
|
|
306
|
+
unit: Unit of measurement
|
|
307
|
+
"""
|
|
308
|
+
source = {
|
|
309
|
+
"SourceType": "GaussianBeam",
|
|
310
|
+
"Name": name,
|
|
311
|
+
"IsEnabled": True,
|
|
312
|
+
"Position": {
|
|
313
|
+
"X": {"Value": position_x, "Unit": unit},
|
|
314
|
+
"Y": {"Value": position_y, "Unit": unit},
|
|
315
|
+
"Z": {"Value": position_z, "Unit": unit}
|
|
316
|
+
},
|
|
317
|
+
"Polarization": polarization,
|
|
318
|
+
"Amplitude": {
|
|
319
|
+
"Real": amplitude_real,
|
|
320
|
+
"Imaginary": amplitude_imag
|
|
321
|
+
},
|
|
322
|
+
"SourcePlane": source_plane,
|
|
323
|
+
"Size": {
|
|
324
|
+
"X": {"Value": size_x, "Unit": unit},
|
|
325
|
+
"Y": {"Value": size_y, "Unit": unit},
|
|
326
|
+
"Z": {"Value": size_z, "Unit": unit}
|
|
327
|
+
},
|
|
328
|
+
"Alpha": alpha,
|
|
329
|
+
"Origin": origin
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if sigma is not None:
|
|
333
|
+
source["Sigma"] = sigma
|
|
334
|
+
if theta is not None:
|
|
335
|
+
source["Theta"] = theta
|
|
336
|
+
if phi is not None:
|
|
337
|
+
source["Phi"] = phi
|
|
338
|
+
|
|
339
|
+
return source
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def create_voxels_model_primitive(
|
|
343
|
+
name: str,
|
|
344
|
+
voxels_data_id: str,
|
|
345
|
+
medium_id: str,
|
|
346
|
+
center_x: float = 0.0,
|
|
347
|
+
center_y: float = 0.0,
|
|
348
|
+
center_z: float = 0.0,
|
|
349
|
+
voxel_size_x: float = 0.1,
|
|
350
|
+
voxel_size_y: float = 0.1,
|
|
351
|
+
voxel_size_z: float = 0.1,
|
|
352
|
+
grid_nx: int = 32,
|
|
353
|
+
grid_ny: int = 32,
|
|
354
|
+
grid_nz: int = 32,
|
|
355
|
+
data_format: str = "Float32",
|
|
356
|
+
data_type: str = "RefractiveIndex",
|
|
357
|
+
unit: str = "Micrometers"
|
|
358
|
+
) -> Dict[str, Any]:
|
|
359
|
+
"""
|
|
360
|
+
Create VoxelsModel primitive for arbitrary refractive index distributions
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
name: Primitive name
|
|
364
|
+
voxels_data_id: ID of uploaded binary voxel data file
|
|
365
|
+
medium_id: Medium/Material ID for the primitive
|
|
366
|
+
center_x, center_y, center_z: Center position
|
|
367
|
+
voxel_size_x, voxel_size_y, voxel_size_z: Size of each voxel
|
|
368
|
+
grid_nx, grid_ny, grid_nz: Number of voxels in each dimension
|
|
369
|
+
data_format: 'Float32', 'Float64', 'Complex64', 'Complex128'
|
|
370
|
+
data_type: 'RefractiveIndex', 'ComplexRefractiveIndex', 'Permittivity', 'ComplexPermittivity'
|
|
371
|
+
unit: Unit of measurement
|
|
372
|
+
"""
|
|
373
|
+
return {
|
|
374
|
+
"ShapeType": "VoxelsModel",
|
|
375
|
+
"Name": name,
|
|
376
|
+
"MediumId": medium_id,
|
|
377
|
+
"VoxelsDataId": voxels_data_id,
|
|
378
|
+
"Position": {
|
|
379
|
+
"X": {"Value": center_x, "Unit": unit},
|
|
380
|
+
"Y": {"Value": center_y, "Unit": unit},
|
|
381
|
+
"Z": {"Value": center_z, "Unit": unit}
|
|
382
|
+
},
|
|
383
|
+
"SourceVoxelSize": {
|
|
384
|
+
"X": voxel_size_x,
|
|
385
|
+
"Y": voxel_size_y,
|
|
386
|
+
"Z": voxel_size_z
|
|
387
|
+
},
|
|
388
|
+
"SourceGridDimensions": {
|
|
389
|
+
"X": grid_nx,
|
|
390
|
+
"Y": grid_ny,
|
|
391
|
+
"Z": grid_nz
|
|
392
|
+
},
|
|
393
|
+
"DataFormat": data_format,
|
|
394
|
+
"DataType": data_type,
|
|
395
|
+
"Rotation": {"X": 0, "Y": 0, "Z": 0}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def create_custom_field_source(
|
|
400
|
+
name: str,
|
|
401
|
+
field_data_id: str,
|
|
402
|
+
position_x: float = 0.0,
|
|
403
|
+
position_y: float = 0.0,
|
|
404
|
+
position_z: float = 0.0,
|
|
405
|
+
grid_nx: int = 1,
|
|
406
|
+
grid_ny: int = 1,
|
|
407
|
+
grid_nz: int = 1,
|
|
408
|
+
polarization: str = "X",
|
|
409
|
+
amplitude_real: float = 1.0,
|
|
410
|
+
amplitude_imag: float = 0.0,
|
|
411
|
+
data_format: str = "Complex64",
|
|
412
|
+
origin: str = "center",
|
|
413
|
+
unit: str = "Micrometers"
|
|
414
|
+
) -> Dict[str, Any]:
|
|
415
|
+
"""
|
|
416
|
+
Create CustomField source for pre-computed field distributions (e.g., eigenmodes)
|
|
417
|
+
|
|
418
|
+
Backend derives voxel size from the simulation domain settings.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
name: Source name
|
|
422
|
+
field_data_id: ID of uploaded complex field data file
|
|
423
|
+
position_x, position_y, position_z: Source position
|
|
424
|
+
grid_nx, grid_ny, grid_nz: Grid dimensions of field data
|
|
425
|
+
polarization: 'X', 'Y', 'Z', 'None'
|
|
426
|
+
amplitude_real, amplitude_imag: Amplitude scaling
|
|
427
|
+
data_format: 'Complex64' or 'Complex128'
|
|
428
|
+
origin: 'center' or 'topleft'
|
|
429
|
+
unit: Unit of measurement
|
|
430
|
+
"""
|
|
431
|
+
return {
|
|
432
|
+
"SourceType": "CustomField",
|
|
433
|
+
"Name": name,
|
|
434
|
+
"IsEnabled": True,
|
|
435
|
+
"FieldDataId": field_data_id,
|
|
436
|
+
"Position": {
|
|
437
|
+
"X": {"Value": position_x, "Unit": unit},
|
|
438
|
+
"Y": {"Value": position_y, "Unit": unit},
|
|
439
|
+
"Z": {"Value": position_z, "Unit": unit}
|
|
440
|
+
},
|
|
441
|
+
"Polarization": polarization,
|
|
442
|
+
"Origin": origin,
|
|
443
|
+
"Amplitude": {
|
|
444
|
+
"Real": amplitude_real,
|
|
445
|
+
"Imaginary": amplitude_imag
|
|
446
|
+
},
|
|
447
|
+
"GridDimensions": {
|
|
448
|
+
"X": grid_nx,
|
|
449
|
+
"Y": grid_ny,
|
|
450
|
+
"Z": grid_nz
|
|
451
|
+
},
|
|
452
|
+
"DataFormat": data_format,
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def create_custom_from_file_source(
|
|
457
|
+
name: str,
|
|
458
|
+
file_id: str,
|
|
459
|
+
position_x: float = 0.0,
|
|
460
|
+
position_y: float = 0.0,
|
|
461
|
+
position_z: float = 0.0,
|
|
462
|
+
file_format: str = "JsonV2",
|
|
463
|
+
unit: str = "Micrometers"
|
|
464
|
+
) -> Dict[str, Any]:
|
|
465
|
+
"""
|
|
466
|
+
Create CustomFromFile source (external file)
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
name: Source name
|
|
470
|
+
file_id: ID or filename of uploaded source file
|
|
471
|
+
position_x, position_y, position_z: Source position
|
|
472
|
+
file_format: "JsonV2", "NumpyNpy", "MatlabMat", "RawBinary"
|
|
473
|
+
unit: Unit of measurement
|
|
474
|
+
"""
|
|
475
|
+
return {
|
|
476
|
+
"SourceType": "CustomFromFile",
|
|
477
|
+
"Name": name,
|
|
478
|
+
"IsEnabled": True,
|
|
479
|
+
"Position": {
|
|
480
|
+
"X": {"Value": position_x, "Unit": unit},
|
|
481
|
+
"Y": {"Value": position_y, "Unit": unit},
|
|
482
|
+
"Z": {"Value": position_z, "Unit": unit}
|
|
483
|
+
},
|
|
484
|
+
"FileDataId": file_id,
|
|
485
|
+
"FileFormat": file_format
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def create_field_monitor(
|
|
490
|
+
name: str,
|
|
491
|
+
center_x: float,
|
|
492
|
+
center_y: float,
|
|
493
|
+
center_z: float,
|
|
494
|
+
size_x: float,
|
|
495
|
+
size_y: float,
|
|
496
|
+
size_z: float,
|
|
497
|
+
plane: str = "xy",
|
|
498
|
+
record_ex: bool = True,
|
|
499
|
+
record_ey: bool = True,
|
|
500
|
+
record_ez: bool = True,
|
|
501
|
+
record_hx: bool = False,
|
|
502
|
+
record_hy: bool = False,
|
|
503
|
+
record_hz: bool = False,
|
|
504
|
+
unit: str = "Micrometers"
|
|
505
|
+
) -> Dict[str, Any]:
|
|
506
|
+
"""
|
|
507
|
+
Create field monitor
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
name: Monitor name
|
|
511
|
+
center_x, center_y, center_z: Monitor center position
|
|
512
|
+
size_x, size_y, size_z: Monitor size (use 0 for 2D plane)
|
|
513
|
+
plane: Monitor plane "xy", "yz", "xz" (lowercase)
|
|
514
|
+
record_ex, record_ey, record_ez: Record E-field components
|
|
515
|
+
record_hx, record_hy, record_hz: Record H-field components
|
|
516
|
+
unit: Unit of measurement
|
|
517
|
+
"""
|
|
518
|
+
return {
|
|
519
|
+
"id": str(uuid.uuid4()),
|
|
520
|
+
"name": name,
|
|
521
|
+
"monitorType": "field",
|
|
522
|
+
"isEnabled": True,
|
|
523
|
+
"PositioningMode": "CenterAndSize",
|
|
524
|
+
"Position": {
|
|
525
|
+
"X": {"Value": center_x, "Unit": unit},
|
|
526
|
+
"Y": {"Value": center_y, "Unit": unit},
|
|
527
|
+
"Z": {"Value": center_z, "Unit": unit}
|
|
528
|
+
},
|
|
529
|
+
"Size": {
|
|
530
|
+
"Width": {"Value": size_x, "Unit": unit},
|
|
531
|
+
"Height": {"Value": size_y, "Unit": unit},
|
|
532
|
+
"Depth": {"Value": size_z, "Unit": unit}
|
|
533
|
+
},
|
|
534
|
+
"plane": plane,
|
|
535
|
+
"spatialSampling": {"x": 1, "y": 1, "z": 1},
|
|
536
|
+
"RecordEx": record_ex,
|
|
537
|
+
"RecordEy": record_ey,
|
|
538
|
+
"RecordEz": record_ez,
|
|
539
|
+
"RecordHx": record_hx,
|
|
540
|
+
"RecordHy": record_hy,
|
|
541
|
+
"RecordHz": record_hz
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def create_flux_monitor(
|
|
546
|
+
name: str,
|
|
547
|
+
center_x: float,
|
|
548
|
+
center_y: float,
|
|
549
|
+
center_z: float,
|
|
550
|
+
size_x: float,
|
|
551
|
+
size_y: float,
|
|
552
|
+
size_z: float,
|
|
553
|
+
plane: str = "xy",
|
|
554
|
+
unit: str = "Micrometers"
|
|
555
|
+
) -> Dict[str, Any]:
|
|
556
|
+
"""
|
|
557
|
+
Create flux monitor
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
name: Monitor name
|
|
561
|
+
center_x, center_y, center_z: Monitor center position
|
|
562
|
+
size_x, size_y, size_z: Monitor size (use 0 for 2D plane)
|
|
563
|
+
plane: Monitor plane "xy", "yz", "xz" (lowercase)
|
|
564
|
+
unit: Unit of measurement
|
|
565
|
+
"""
|
|
566
|
+
return {
|
|
567
|
+
"monitorType": "flux",
|
|
568
|
+
"Name": name,
|
|
569
|
+
"IsEnabled": True,
|
|
570
|
+
"PositioningMode": "CenterAndSize",
|
|
571
|
+
"Position": {
|
|
572
|
+
"X": {"Value": center_x, "Unit": unit},
|
|
573
|
+
"Y": {"Value": center_y, "Unit": unit},
|
|
574
|
+
"Z": {"Value": center_z, "Unit": unit}
|
|
575
|
+
},
|
|
576
|
+
"Size": {
|
|
577
|
+
"X": {"Value": size_x, "Unit": unit},
|
|
578
|
+
"Y": {"Value": size_y, "Unit": unit},
|
|
579
|
+
"Z": {"Value": size_z, "Unit": unit}
|
|
580
|
+
},
|
|
581
|
+
"Plane": plane
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def create_permittivity_monitor(
|
|
586
|
+
name: str,
|
|
587
|
+
center_x: float,
|
|
588
|
+
center_y: float,
|
|
589
|
+
center_z: float,
|
|
590
|
+
size_x: float,
|
|
591
|
+
size_y: float,
|
|
592
|
+
size_z: float,
|
|
593
|
+
plane: str = "xy",
|
|
594
|
+
unit: str = "Micrometers"
|
|
595
|
+
) -> Dict[str, Any]:
|
|
596
|
+
"""
|
|
597
|
+
Create permittivity monitor
|
|
598
|
+
|
|
599
|
+
Args:
|
|
600
|
+
name: Monitor name
|
|
601
|
+
center_x, center_y, center_z: Monitor center position
|
|
602
|
+
size_x, size_y, size_z: Monitor size (use 0 for 2D plane)
|
|
603
|
+
plane: Monitor plane "xy", "yz", "xz" (lowercase)
|
|
604
|
+
unit: Unit of measurement
|
|
605
|
+
"""
|
|
606
|
+
return {
|
|
607
|
+
"id": str(uuid.uuid4()),
|
|
608
|
+
"name": name,
|
|
609
|
+
"monitorType": "permittivity",
|
|
610
|
+
"isEnabled": True,
|
|
611
|
+
"PositioningMode": "CenterAndSize",
|
|
612
|
+
"Position": {
|
|
613
|
+
"X": {"Value": center_x, "Unit": unit},
|
|
614
|
+
"Y": {"Value": center_y, "Unit": unit},
|
|
615
|
+
"Z": {"Value": center_z, "Unit": unit}
|
|
616
|
+
},
|
|
617
|
+
"Size": {
|
|
618
|
+
"Width": {"Value": size_x, "Unit": unit},
|
|
619
|
+
"Height": {"Value": size_y, "Unit": unit},
|
|
620
|
+
"Depth": {"Value": size_z, "Unit": unit}
|
|
621
|
+
},
|
|
622
|
+
"plane": plane,
|
|
623
|
+
"spatialSampling": {"x": 1, "y": 1, "z": 1}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
def create_wavesim_properties(
|
|
628
|
+
simulation_domain: Dict[str, Any],
|
|
629
|
+
primitives: List[Dict[str, Any]],
|
|
630
|
+
input_sources: List[Dict[str, Any]],
|
|
631
|
+
mediums: List[Dict[str, Any]],
|
|
632
|
+
monitors: List[Dict[str, Any]],
|
|
633
|
+
background_medium_id: str = "",
|
|
634
|
+
wavelength: float = 1.55,
|
|
635
|
+
voxel_size: float = 0.05,
|
|
636
|
+
wavelength_unit: str = "Micrometers",
|
|
637
|
+
voxel_unit: str = "Micrometers",
|
|
638
|
+
boundary_size_um: float = 0.0,
|
|
639
|
+
residual_norm_threshold: float = 1.0e-6,
|
|
640
|
+
residual_norm_max_iterations: int = 10000,
|
|
641
|
+
periodic_x: bool = False,
|
|
642
|
+
periodic_y: bool = False,
|
|
643
|
+
periodic_z: bool = False
|
|
644
|
+
) -> Dict[str, Any]:
|
|
645
|
+
"""
|
|
646
|
+
Create complete WavesimProperties structure
|
|
647
|
+
|
|
648
|
+
This is the top-level structure that should be serialized to JSON
|
|
649
|
+
and passed as argumentsJson to create_command().
|
|
650
|
+
|
|
651
|
+
Args:
|
|
652
|
+
simulation_domain: Domain from create_simulation_domain()
|
|
653
|
+
primitives: List of primitive structures
|
|
654
|
+
input_sources: List of source structures
|
|
655
|
+
mediums: List of medium structures
|
|
656
|
+
monitors: List of monitor structures
|
|
657
|
+
background_medium_id: ID of background medium
|
|
658
|
+
wavelength: Wavelength value
|
|
659
|
+
voxel_size: Voxel size value
|
|
660
|
+
wavelength_unit: Wavelength unit (default: 'Micrometers')
|
|
661
|
+
voxel_unit: Voxel size unit (default: 'Micrometers')
|
|
662
|
+
boundary_size_um: Boundary size in micrometers
|
|
663
|
+
residual_norm_threshold: Convergence threshold
|
|
664
|
+
residual_norm_max_iterations: Max iterations
|
|
665
|
+
periodic_x, periodic_y, periodic_z: Periodic boundary conditions
|
|
666
|
+
"""
|
|
667
|
+
return {
|
|
668
|
+
"SimulationDomain": simulation_domain,
|
|
669
|
+
"BackgroundMediumId": background_medium_id,
|
|
670
|
+
"Primitives": primitives,
|
|
671
|
+
"InputSources": input_sources,
|
|
672
|
+
"Mediums": mediums,
|
|
673
|
+
"Monitors": monitors,
|
|
674
|
+
"Wavelength": {
|
|
675
|
+
"Value": wavelength,
|
|
676
|
+
"Unit": wavelength_unit
|
|
677
|
+
},
|
|
678
|
+
"VoxelSize": {
|
|
679
|
+
"Value": voxel_size,
|
|
680
|
+
"Unit": voxel_unit
|
|
681
|
+
},
|
|
682
|
+
"BoundarySize": {
|
|
683
|
+
"Value": boundary_size_um,
|
|
684
|
+
"Unit": "Micrometers"
|
|
685
|
+
},
|
|
686
|
+
"ResidualNormThreshold": residual_norm_threshold,
|
|
687
|
+
"ResidualNormMaxIterations": residual_norm_max_iterations,
|
|
688
|
+
"PeriodicX": periodic_x,
|
|
689
|
+
"PeriodicY": periodic_y,
|
|
690
|
+
"PeriodicZ": periodic_z,
|
|
691
|
+
"DefaultLengthUnit": "Micrometers",
|
|
692
|
+
"CoordinateSystemCenter": {
|
|
693
|
+
"X": {"Value": 0.0, "Unit": "Micrometers"},
|
|
694
|
+
"Y": {"Value": 0.0, "Unit": "Micrometers"},
|
|
695
|
+
"Z": {"Value": 0.0, "Unit": "Micrometers"}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def parse_progress_data(progress_obj: Optional[Dict[str, Any]]) -> Dict[str, Any]:
|
|
701
|
+
"""
|
|
702
|
+
Parse CommandProgressDto with nested JSON strings.
|
|
703
|
+
|
|
704
|
+
Args:
|
|
705
|
+
progress_obj: CommandProgressDto dict from get_command_status()
|
|
706
|
+
|
|
707
|
+
Returns:
|
|
708
|
+
Dict with parsed progress fields: status, iteration, percentage,
|
|
709
|
+
max_iterations, residual_norm, threshold
|
|
710
|
+
"""
|
|
711
|
+
info: Dict[str, Any] = {}
|
|
712
|
+
|
|
713
|
+
if not progress_obj:
|
|
714
|
+
return info
|
|
715
|
+
|
|
716
|
+
if "status" in progress_obj:
|
|
717
|
+
info["status"] = progress_obj["status"]
|
|
718
|
+
|
|
719
|
+
# Parse Progress JSON string
|
|
720
|
+
progress_str = progress_obj.get("progress")
|
|
721
|
+
if progress_str:
|
|
722
|
+
try:
|
|
723
|
+
if isinstance(progress_str, str):
|
|
724
|
+
import json as _json
|
|
725
|
+
progress_data = _json.loads(progress_str)
|
|
726
|
+
else:
|
|
727
|
+
progress_data = progress_str
|
|
728
|
+
|
|
729
|
+
if "iteration" in progress_data:
|
|
730
|
+
info["iteration"] = progress_data["iteration"]
|
|
731
|
+
if "percentage" in progress_data:
|
|
732
|
+
info["percentage"] = progress_data["percentage"]
|
|
733
|
+
if "max_iterations" in progress_data:
|
|
734
|
+
info["max_iterations"] = progress_data["max_iterations"]
|
|
735
|
+
except (json.JSONDecodeError, TypeError, ValueError):
|
|
736
|
+
pass
|
|
737
|
+
|
|
738
|
+
# Parse Data JSON string
|
|
739
|
+
data_str = progress_obj.get("data")
|
|
740
|
+
if data_str:
|
|
741
|
+
try:
|
|
742
|
+
if isinstance(data_str, str):
|
|
743
|
+
import json as _json
|
|
744
|
+
data_obj = _json.loads(data_str)
|
|
745
|
+
else:
|
|
746
|
+
data_obj = data_str
|
|
747
|
+
|
|
748
|
+
if "residual_norm" in data_obj:
|
|
749
|
+
info["residual_norm"] = data_obj["residual_norm"]
|
|
750
|
+
if "residual_norm_threshold" in data_obj:
|
|
751
|
+
info["threshold"] = data_obj["residual_norm_threshold"]
|
|
752
|
+
except (json.JSONDecodeError, TypeError, ValueError):
|
|
753
|
+
pass
|
|
754
|
+
|
|
755
|
+
return info
|