goad-py 0.7.0__cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.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.
Potentially problematic release.
This version of goad-py might be problematic. Click here for more details.
- goad_py/__init__.py +51 -0
- goad_py/_goad_py.abi3.so +0 -0
- goad_py/convergence.py +830 -0
- goad_py/convergence_display.py +499 -0
- goad_py/goad_py.pyi +453 -0
- goad_py/phips_convergence.py +608 -0
- goad_py/unified_convergence.py +1327 -0
- goad_py-0.7.0.dist-info/METADATA +99 -0
- goad_py-0.7.0.dist-info/RECORD +10 -0
- goad_py-0.7.0.dist-info/WHEEL +4 -0
goad_py/goad_py.pyi
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GOAD Python API Type Definitions
|
|
3
|
+
|
|
4
|
+
This file provides comprehensive type hints for the GOAD (Geometric Optics
|
|
5
|
+
Approximation for Diffraction) Python bindings. GOAD simulates light scattering
|
|
6
|
+
by arbitrary 3D geometries using geometric optics with diffraction corrections.
|
|
7
|
+
|
|
8
|
+
The main workflow is:
|
|
9
|
+
1. Create Settings with geometry path (all other parameters have sensible defaults)
|
|
10
|
+
2. Use Problem for single-orientation or MultiProblem for multi-orientation simulations
|
|
11
|
+
3. Call py_solve() to run the computation
|
|
12
|
+
4. Access results through the .results property
|
|
13
|
+
|
|
14
|
+
Default behavior (minimal setup):
|
|
15
|
+
- Single random orientation (perfect for initial exploration)
|
|
16
|
+
- Interval angular binning with ~1000 bins (good coverage, reasonable performance)
|
|
17
|
+
- Wavelength: 532nm (green laser)
|
|
18
|
+
- Refractive index: 1.31 + 0.0i (typical glass particle in air)
|
|
19
|
+
|
|
20
|
+
Example (minimal setup):
|
|
21
|
+
import goad_py as goad
|
|
22
|
+
|
|
23
|
+
settings = goad.Settings("particle.obj")
|
|
24
|
+
mp = goad.MultiProblem(settings)
|
|
25
|
+
mp.py_solve()
|
|
26
|
+
|
|
27
|
+
results = mp.results
|
|
28
|
+
print(f"Scattering cross-section: {results.scat_cross}")
|
|
29
|
+
print(f"Extinction cross-section: {results.ext_cross}")
|
|
30
|
+
print(f"Asymmetry parameter: {results.asymmetry}")
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from typing import Optional, List, Dict, Tuple
|
|
34
|
+
import numpy as np
|
|
35
|
+
|
|
36
|
+
class Euler:
|
|
37
|
+
"""Euler angles for rotations."""
|
|
38
|
+
alpha: float
|
|
39
|
+
beta: float
|
|
40
|
+
gamma: float
|
|
41
|
+
|
|
42
|
+
def __init__(self, alpha: float, beta: float, gamma: float) -> None: ...
|
|
43
|
+
def __repr__(self) -> str: ...
|
|
44
|
+
|
|
45
|
+
class EulerConvention:
|
|
46
|
+
"""Euler angle conventions."""
|
|
47
|
+
XZX: 'EulerConvention'
|
|
48
|
+
XYX: 'EulerConvention'
|
|
49
|
+
YXY: 'EulerConvention'
|
|
50
|
+
YZY: 'EulerConvention'
|
|
51
|
+
ZYZ: 'EulerConvention'
|
|
52
|
+
ZXZ: 'EulerConvention'
|
|
53
|
+
XZY: 'EulerConvention'
|
|
54
|
+
XYZ: 'EulerConvention'
|
|
55
|
+
YXZ: 'EulerConvention'
|
|
56
|
+
YZX: 'EulerConvention'
|
|
57
|
+
ZYX: 'EulerConvention'
|
|
58
|
+
ZXY: 'EulerConvention'
|
|
59
|
+
|
|
60
|
+
class Scheme:
|
|
61
|
+
"""Orientation scheme (uniform or discrete)."""
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
class Orientation:
|
|
65
|
+
"""Full orientation specification."""
|
|
66
|
+
scheme: Scheme
|
|
67
|
+
euler_convention: EulerConvention
|
|
68
|
+
|
|
69
|
+
def __init__(self, scheme: Scheme, euler_convention: Optional[EulerConvention] = None) -> None: ...
|
|
70
|
+
def __repr__(self) -> str: ...
|
|
71
|
+
|
|
72
|
+
class Geom:
|
|
73
|
+
"""Geometry representation."""
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
class Shape:
|
|
77
|
+
"""Shape within geometry."""
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
class Results:
|
|
81
|
+
"""Results from problem solving."""
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def bins(self) -> List[tuple[float, float]]: ...
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def bins_1d(self) -> Optional[List[float]]: ...
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def mueller(self) -> List[List[float]]: ...
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def mueller_beam(self) -> List[List[float]]: ...
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def mueller_ext(self) -> List[List[float]]: ...
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def mueller_1d(self) -> List[List[float]]: ...
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def mueller_1d_beam(self) -> List[List[float]]: ...
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def mueller_1d_ext(self) -> List[List[float]]: ...
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def asymmetry(self) -> Optional[float]: ...
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def scat_cross(self) -> Optional[float]: ...
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def ext_cross(self) -> Optional[float]: ...
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def albedo(self) -> Optional[float]: ...
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def params(self) -> Dict[str, Optional[float]]: ...
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def powers(self) -> Dict[str, float]: ...
|
|
124
|
+
|
|
125
|
+
class BinningScheme:
|
|
126
|
+
"""Angular binning scheme for scattering calculations.
|
|
127
|
+
|
|
128
|
+
Defines how to discretize the scattering sphere into angular bins
|
|
129
|
+
for Mueller matrix and amplitude computations. Supports simple
|
|
130
|
+
regular grids, custom intervals, and arbitrary bin arrangements.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def __init__(self, bins: List[tuple[float, float]]) -> None: ...
|
|
134
|
+
|
|
135
|
+
@staticmethod
|
|
136
|
+
def simple(num_theta: int, num_phi: int) -> 'BinningScheme':
|
|
137
|
+
"""Create a simple regular grid binning scheme.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
num_theta: Number of bins in theta direction (0 to 180 degrees)
|
|
141
|
+
num_phi: Number of bins in phi direction (0 to 360 degrees)
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
BinningScheme with regular angular grid
|
|
145
|
+
|
|
146
|
+
Raises:
|
|
147
|
+
ValueError: If num_theta or num_phi is zero or negative
|
|
148
|
+
"""
|
|
149
|
+
...
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def interval(
|
|
153
|
+
thetas: List[float],
|
|
154
|
+
theta_spacings: List[float],
|
|
155
|
+
phis: List[float],
|
|
156
|
+
phi_spacings: List[float]
|
|
157
|
+
) -> 'BinningScheme':
|
|
158
|
+
"""Create binning scheme with custom intervals.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
thetas: Key theta angles in degrees
|
|
162
|
+
theta_spacings: Spacing around each theta value
|
|
163
|
+
phis: Key phi angles in degrees
|
|
164
|
+
phi_spacings: Spacing around each phi value
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
BinningScheme with custom intervals
|
|
168
|
+
"""
|
|
169
|
+
...
|
|
170
|
+
|
|
171
|
+
@staticmethod
|
|
172
|
+
def custom(bins: List[tuple[float, float]]) -> 'BinningScheme':
|
|
173
|
+
"""Create binning scheme from explicit (theta, phi) pairs.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
bins: List of (theta, phi) angle pairs in degrees
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
BinningScheme with custom bin locations
|
|
180
|
+
"""
|
|
181
|
+
...
|
|
182
|
+
|
|
183
|
+
class Settings:
|
|
184
|
+
"""Simulation parameters and physical properties.
|
|
185
|
+
|
|
186
|
+
Contains all parameters needed for a GOAD simulation including
|
|
187
|
+
geometry path, optical properties, orientation scheme, and
|
|
188
|
+
numerical settings. Most parameters have sensible defaults.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
def __init__(
|
|
192
|
+
self,
|
|
193
|
+
geom_path: str,
|
|
194
|
+
wavelength: float = 0.532,
|
|
195
|
+
particle_refr_index_re: float = 1.31,
|
|
196
|
+
particle_refr_index_im: float = 0.0,
|
|
197
|
+
medium_refr_index_re: float = 1.0,
|
|
198
|
+
medium_refr_index_im: float = 0.0,
|
|
199
|
+
orientation: Optional[Orientation] = None,
|
|
200
|
+
binning: Optional[BinningScheme] = None,
|
|
201
|
+
beam_power_threshold: float = 0.005,
|
|
202
|
+
beam_area_threshold_fac: float = 0.1,
|
|
203
|
+
cutoff: float = 0.99,
|
|
204
|
+
max_rec: int = 10,
|
|
205
|
+
max_tir: int = 10,
|
|
206
|
+
scale: float = 1.0,
|
|
207
|
+
directory: str = "goad_run"
|
|
208
|
+
) -> None:
|
|
209
|
+
"""Initialize simulation settings.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
geom_path: Path to geometry file (.obj format)
|
|
213
|
+
wavelength: Incident wavelength in geometry units (default: 0.532)
|
|
214
|
+
particle_refr_index_re: Real part of particle refractive index (default: 1.31)
|
|
215
|
+
particle_refr_index_im: Imaginary part of particle refractive index (default: 0.0)
|
|
216
|
+
medium_refr_index_re: Real part of medium refractive index (default: 1.0)
|
|
217
|
+
medium_refr_index_im: Imaginary part of medium refractive index (default: 0.0)
|
|
218
|
+
orientation: Orientation scheme (default: None → 1 random uniform orientation)
|
|
219
|
+
binning: Angular binning scheme (default: None → interval binning: theta=[0°,30°,60°,120°,180°] with spacings [2°,5°,10°,5°], phi=[0°,90°,180°,270°,360°] with 15° spacings, creating ~1000 bins)
|
|
220
|
+
beam_power_threshold: Ray termination threshold (default: 0.005)
|
|
221
|
+
beam_area_threshold_fac: Area threshold factor (default: 0.1)
|
|
222
|
+
cutoff: Power cutoff fraction 0-1 (default: 0.99)
|
|
223
|
+
max_rec: Maximum recursion depth (default: 10)
|
|
224
|
+
max_tir: Maximum total internal reflections (default: 10)
|
|
225
|
+
scale: Geometry scaling factor (default: 1.0)
|
|
226
|
+
directory: Output directory for results (default: "goad_run")
|
|
227
|
+
|
|
228
|
+
Raises:
|
|
229
|
+
ValueError: If wavelength <= 0 or cutoff not in [0,1]
|
|
230
|
+
FileNotFoundError: If geometry file doesn't exist
|
|
231
|
+
"""
|
|
232
|
+
...
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def euler(self) -> List[float]: ...
|
|
236
|
+
|
|
237
|
+
@euler.setter
|
|
238
|
+
def euler(self, value: List[float]) -> None: ...
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def orientation(self) -> Orientation: ...
|
|
242
|
+
|
|
243
|
+
@orientation.setter
|
|
244
|
+
def orientation(self, value: Orientation) -> None: ...
|
|
245
|
+
|
|
246
|
+
class Problem:
|
|
247
|
+
"""Single orientation problem."""
|
|
248
|
+
|
|
249
|
+
def __init__(self, settings: Optional[Settings] = None, geom: Optional[Geom] = None) -> None: ...
|
|
250
|
+
|
|
251
|
+
def py_solve(self) -> None: ...
|
|
252
|
+
|
|
253
|
+
def py_print_stats(self) -> None: ...
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def results(self) -> Results: ...
|
|
257
|
+
|
|
258
|
+
class MultiProblem:
|
|
259
|
+
"""Multi-orientation light scattering simulation for a single geometry.
|
|
260
|
+
|
|
261
|
+
Computes orientation-averaged scattering properties by running multiple
|
|
262
|
+
single-orientation simulations and averaging the results. Supports both
|
|
263
|
+
random and systematic orientation sampling schemes. Results include
|
|
264
|
+
Mueller matrices, cross-sections, and derived optical parameters.
|
|
265
|
+
|
|
266
|
+
Example:
|
|
267
|
+
orientations = goad.create_uniform_orientation(100)
|
|
268
|
+
settings = goad.Settings("particle.obj", orientation=orientations)
|
|
269
|
+
mp = goad.MultiProblem(settings)
|
|
270
|
+
mp.py_solve()
|
|
271
|
+
print(f"Scattering cross-section: {mp.results.scat_cross}")
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
def __init__(self, settings: Settings, geom: Optional[Geom] = None) -> None:
|
|
275
|
+
"""Initialize multi-orientation problem.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
settings: Simulation parameters including orientation scheme
|
|
279
|
+
geom: Geometry object (loaded from settings.geom_path if None)
|
|
280
|
+
|
|
281
|
+
Raises:
|
|
282
|
+
FileNotFoundError: If geometry file cannot be loaded
|
|
283
|
+
"""
|
|
284
|
+
...
|
|
285
|
+
|
|
286
|
+
def py_solve(self) -> None:
|
|
287
|
+
"""Solve the multi-orientation scattering problem.
|
|
288
|
+
|
|
289
|
+
Computes scattering properties averaged over all orientations using
|
|
290
|
+
parallel processing. The Global Interpreter Lock (GIL) is released
|
|
291
|
+
during computation to allow concurrent Python operations.
|
|
292
|
+
|
|
293
|
+
Raises:
|
|
294
|
+
RuntimeError: If computation fails
|
|
295
|
+
"""
|
|
296
|
+
...
|
|
297
|
+
|
|
298
|
+
def py_writeup(self) -> None:
|
|
299
|
+
"""Write results to output files in the specified directory."""
|
|
300
|
+
...
|
|
301
|
+
|
|
302
|
+
def py_reset(self) -> None:
|
|
303
|
+
"""Reset problem to initial state and regenerate orientations."""
|
|
304
|
+
...
|
|
305
|
+
|
|
306
|
+
def py_regenerate_orientations(self) -> None:
|
|
307
|
+
"""Regenerate random orientations (useful for statistical sampling)."""
|
|
308
|
+
...
|
|
309
|
+
|
|
310
|
+
@property
|
|
311
|
+
def results(self) -> Results:
|
|
312
|
+
"""Access orientation-averaged simulation results.
|
|
313
|
+
|
|
314
|
+
Returns the complete Results object containing Mueller matrices,
|
|
315
|
+
amplitude matrices, power distributions, and derived parameters
|
|
316
|
+
averaged over all orientations.
|
|
317
|
+
"""
|
|
318
|
+
...
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def num_orientations(self) -> int:
|
|
322
|
+
"""Number of orientations in the current simulation."""
|
|
323
|
+
...
|
|
324
|
+
|
|
325
|
+
# Helper functions
|
|
326
|
+
def uniform_orientation(num_orients: int) -> Scheme: ...
|
|
327
|
+
|
|
328
|
+
def discrete_orientation(eulers: List[Euler]) -> Scheme: ...
|
|
329
|
+
|
|
330
|
+
def create_uniform_orientation(num_orients: int, euler_convention: Optional[EulerConvention] = None) -> Orientation: ...
|
|
331
|
+
|
|
332
|
+
def create_discrete_orientation(eulers: List[Euler], euler_convention: Optional[EulerConvention] = None) -> Orientation: ...
|
|
333
|
+
|
|
334
|
+
def sum_as_string(a: int, b: int) -> str: ...
|
|
335
|
+
|
|
336
|
+
def goad_py_add() -> None: ...
|
|
337
|
+
|
|
338
|
+
# Convergence Analysis Classes
|
|
339
|
+
|
|
340
|
+
class Convergable:
|
|
341
|
+
"""Represents a variable to monitor for convergence.
|
|
342
|
+
|
|
343
|
+
Defines convergence criteria for integrated scattering parameters
|
|
344
|
+
including asymmetry parameter, scattering cross-section, extinction
|
|
345
|
+
cross-section, and single-scattering albedo.
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
variable: str
|
|
349
|
+
tolerance_type: str
|
|
350
|
+
tolerance: float
|
|
351
|
+
|
|
352
|
+
def __init__(
|
|
353
|
+
self,
|
|
354
|
+
variable: str,
|
|
355
|
+
tolerance_type: str = 'relative',
|
|
356
|
+
tolerance: float = 0.01
|
|
357
|
+
) -> None:
|
|
358
|
+
"""Initialize convergence criterion.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
variable: Variable to monitor ('asymmetry', 'scatt', 'ext', 'albedo')
|
|
362
|
+
tolerance_type: 'relative' or 'absolute' tolerance
|
|
363
|
+
tolerance: Tolerance value (relative as fraction, absolute as value)
|
|
364
|
+
|
|
365
|
+
Raises:
|
|
366
|
+
ValueError: If variable name or tolerance_type is invalid
|
|
367
|
+
"""
|
|
368
|
+
...
|
|
369
|
+
|
|
370
|
+
class ConvergenceResults:
|
|
371
|
+
"""Results from a convergence study.
|
|
372
|
+
|
|
373
|
+
Contains final convergence status, parameter values with uncertainties,
|
|
374
|
+
and complete convergence history for analysis.
|
|
375
|
+
"""
|
|
376
|
+
|
|
377
|
+
converged: bool
|
|
378
|
+
n_orientations: int
|
|
379
|
+
values: Dict[str, float]
|
|
380
|
+
sem_values: Dict[str, float]
|
|
381
|
+
mueller_1d: Optional[np.ndarray]
|
|
382
|
+
mueller_2d: Optional[np.ndarray]
|
|
383
|
+
convergence_history: List[Tuple[int, str, float]]
|
|
384
|
+
warning: Optional[str]
|
|
385
|
+
|
|
386
|
+
def __init__(
|
|
387
|
+
self,
|
|
388
|
+
converged: bool,
|
|
389
|
+
n_orientations: int,
|
|
390
|
+
values: Dict[str, float],
|
|
391
|
+
sem_values: Dict[str, float],
|
|
392
|
+
mueller_1d: Optional[np.ndarray] = None,
|
|
393
|
+
mueller_2d: Optional[np.ndarray] = None,
|
|
394
|
+
convergence_history: List[Tuple[int, str, float]] = None,
|
|
395
|
+
warning: Optional[str] = None
|
|
396
|
+
) -> None: ...
|
|
397
|
+
|
|
398
|
+
class Convergence:
|
|
399
|
+
"""Runs multiple MultiProblems until convergence criteria are met.
|
|
400
|
+
|
|
401
|
+
Implements statistical convergence analysis for scattering parameters
|
|
402
|
+
using batch-based standard error estimation. Monitors multiple variables
|
|
403
|
+
simultaneously and stops when all meet their convergence criteria.
|
|
404
|
+
|
|
405
|
+
Example:
|
|
406
|
+
convergence = Convergence(
|
|
407
|
+
settings=goad.Settings("particle.obj"),
|
|
408
|
+
convergables=[
|
|
409
|
+
Convergable('asymmetry', 'absolute', 0.005),
|
|
410
|
+
Convergable('scatt', 'relative', 0.01),
|
|
411
|
+
],
|
|
412
|
+
batch_size=100
|
|
413
|
+
)
|
|
414
|
+
results = convergence.run()
|
|
415
|
+
"""
|
|
416
|
+
|
|
417
|
+
def __init__(
|
|
418
|
+
self,
|
|
419
|
+
settings: Settings,
|
|
420
|
+
convergables: List[Convergable],
|
|
421
|
+
batch_size: int = 24,
|
|
422
|
+
max_orientations: int = 100_000,
|
|
423
|
+
min_batches: int = 10,
|
|
424
|
+
mueller_1d: bool = True,
|
|
425
|
+
mueller_2d: bool = False
|
|
426
|
+
) -> None:
|
|
427
|
+
"""Initialize convergence study.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
settings: GOAD settings for the simulation
|
|
431
|
+
convergables: List of variables to monitor for convergence
|
|
432
|
+
batch_size: Number of orientations per iteration
|
|
433
|
+
max_orientations: Maximum total orientations before stopping
|
|
434
|
+
min_batches: Minimum number of batches before allowing convergence
|
|
435
|
+
mueller_1d: Whether to collect 1D Mueller matrices
|
|
436
|
+
mueller_2d: Whether to collect 2D Mueller matrices
|
|
437
|
+
|
|
438
|
+
Raises:
|
|
439
|
+
ValueError: If parameters are invalid or no convergables specified
|
|
440
|
+
"""
|
|
441
|
+
...
|
|
442
|
+
|
|
443
|
+
def run(self) -> ConvergenceResults:
|
|
444
|
+
"""Run the convergence study.
|
|
445
|
+
|
|
446
|
+
Executes batches of orientations until all convergence criteria
|
|
447
|
+
are met or maximum orientations reached. Provides progress updates
|
|
448
|
+
and rigorous statistical analysis.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
ConvergenceResults containing final values and convergence status
|
|
452
|
+
"""
|
|
453
|
+
...
|