goad-py 0.5.6__tar.gz → 0.6.1__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.
Potentially problematic release.
This version of goad-py might be problematic. Click here for more details.
- {goad_py-0.5.6 → goad_py-0.6.1}/PKG-INFO +1 -1
- goad_py-0.6.1/goad-py/examples/unified/10_advanced_parameters.py +216 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/python/goad_py/unified_convergence.py +245 -3
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/src/lib.rs +4 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/pyproject.toml +1 -1
- {goad_py-0.5.6 → goad_py-0.6.1}/python/goad_py/unified_convergence.py +245 -3
- {goad_py-0.5.6 → goad_py-0.6.1}/src/settings.rs +50 -0
- goad_py-0.5.6/goad-py/examples/unified/09_phips_convergence_results.json +0 -220
- {goad_py-0.5.6 → goad_py-0.6.1}/.github/workflows/python.yml +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/.github/workflows/rust.yml +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/.gitignore +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/Cargo.lock +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/Cargo.toml +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/LICENSE +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/README-python.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/README.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/blender/addon/__init__.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/blender/addon/goad_py/__init__.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/blender/addon.zip +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/blender/build.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/blender/dev.blend +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/config/default.toml +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/config_editor.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/8col.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/clip_test.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/concave1.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/concave2.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/cone.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/cube.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/cube2.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/cube_inside_cube.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/cube_inside_hex.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/cube_inside_ico.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/cubes.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex2.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex3.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex4.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex5.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex6.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex7.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex_20_30_30.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex_distort.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex_hollow.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/hex_indented.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/icosphere1.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/multiple.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/multiple2.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/multiple3.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/octo.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/octo2.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/para.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/para_rough1.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/plane_xy.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/plane_yz.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/plate.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/data/plate_distort.obj +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/multi-problem.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/problem-diff.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/problem1.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/examples/simplify.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/.gitignore +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/CLAUDE.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/Cargo.toml +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/DISTRIBUTION.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/README-python.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/UNIFIED_API.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/UNIFIED_API_SUMMARY.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/build_and_test.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/build_wheels_local.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/convergence.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/README.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/README.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/backscatter_convergence_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/convergence_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/ensemble_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/multiproblem_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/multiproblem_phips_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/phips_convergence_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/phips_ensemble_convergence_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/s11_convergence_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/direct/simple_example.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/01_simple_asymmetry.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/02_interval_binning.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/03_multiple_targets.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/04_mueller_element.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/05_backscatter_specific_bin.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/06_ensemble_convergence.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/07_advanced_config.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/08_parameter_sweep.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/09_phips_convergence.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/examples/unified/README.md +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/goad_py.pyi +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/plot.ipynb +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/publish_test.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/python/goad_py/__init__.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/python/goad_py/convergence.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/python/goad_py/goad_py.pyi +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/python/goad_py/phips_convergence.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/release.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/goad-py/test_wheels.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/python/goad_py/__init__.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/python/goad_py/convergence.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/python/goad_py/goad_py.pyi +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/python/goad_py/phips_convergence.py +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/setup.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/_quickstart.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/beam.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/bins.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/clip.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/containment.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/diff.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/distortion.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/field.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/fresnel.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/geom.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/lib.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/main.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/multiproblem.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/orientation.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/output.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/params.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/powers.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/problem.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/result.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/settings/cli.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/settings/constants.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/settings/loading.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/settings/validation.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/src/snell.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/template/custom_bins.toml +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/template/goad_pbs.sh +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/tests/fixed_orientation_tests.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/tests/helpers.rs +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/tests/test_data/fixed_hex_30_20_20_mueller_scatgrid +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/tests/test_data/fixed_hex_30_30_30_mueller_scatgrid +0 -0
- {goad_py-0.5.6 → goad_py-0.6.1}/tests/test_data/hex.obj +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example 10: Advanced Parameters - Comprehensive Demonstration
|
|
4
|
+
|
|
5
|
+
Demonstrates ALL modifiable parameters in run_convergence():
|
|
6
|
+
- Optical parameters (wavelength, refractive indices)
|
|
7
|
+
- Convergence parameters (batch size, max orientations, min batches)
|
|
8
|
+
- Beam tracing parameters (thresholds, recursion limits, cutoff)
|
|
9
|
+
- Geometry transformations (problem scale, per-axis scaling, distortion)
|
|
10
|
+
- Advanced configuration (mapping, coherence, field of view)
|
|
11
|
+
- Reproducibility (random seed)
|
|
12
|
+
|
|
13
|
+
This example showcases the full flexibility of the unified convergence API.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
import goad_py as goad
|
|
18
|
+
|
|
19
|
+
print("=" * 80)
|
|
20
|
+
print("Example 10: Advanced Parameters - ALL Modifiable Parameters")
|
|
21
|
+
print("=" * 80)
|
|
22
|
+
|
|
23
|
+
# Get paths relative to this script's location
|
|
24
|
+
script_dir = Path(__file__).parent
|
|
25
|
+
geom_file = script_dir / "../../../examples/data/hex.obj"
|
|
26
|
+
phips_bins_file = script_dir / "../../../phips_bins_edges.toml"
|
|
27
|
+
|
|
28
|
+
# Resolve to absolute paths
|
|
29
|
+
geom_file = str(geom_file.resolve())
|
|
30
|
+
phips_bins_file = str(phips_bins_file.resolve())
|
|
31
|
+
|
|
32
|
+
# Configuration summary
|
|
33
|
+
print("\n" + "=" * 80)
|
|
34
|
+
print("Configuration Summary:")
|
|
35
|
+
print("=" * 80)
|
|
36
|
+
print("OPTICAL PARAMETERS:")
|
|
37
|
+
print(" Wavelength: 0.633 μm (HeNe laser)")
|
|
38
|
+
print(" Particle refractive index: 1.5 + 0.01i (absorbing particle)")
|
|
39
|
+
print(" Medium refractive index: 1.33 + 0.0i (water)")
|
|
40
|
+
print()
|
|
41
|
+
print("CONVERGENCE PARAMETERS:")
|
|
42
|
+
print(" Batch size: 12 orientations per batch")
|
|
43
|
+
print(" Max orientations: 500")
|
|
44
|
+
print(" Min batches: 3")
|
|
45
|
+
print()
|
|
46
|
+
print("BEAM TRACING PARAMETERS:")
|
|
47
|
+
print(" Beam power threshold: 0.01 (stricter than default 0.05)")
|
|
48
|
+
print(" Beam area threshold factor: 2.0 (half of default 4.0)")
|
|
49
|
+
print(" Cutoff: 0.0005 (stricter than default 0.001)")
|
|
50
|
+
print(" Max recursion depth: 6 (limited for demonstration)")
|
|
51
|
+
print(" Max TIR bounces: 50 (half of default 100)")
|
|
52
|
+
print()
|
|
53
|
+
print("GEOMETRY TRANSFORMATIONS:")
|
|
54
|
+
print(" Problem scale: 2.0 (scales entire problem by 2x)")
|
|
55
|
+
print(" Per-axis geometry scale: [1.0, 1.0, 1.5] (stretch z-axis by 1.5x)")
|
|
56
|
+
print(" Distortion: 0.1 (adds 10% geometric distortion)")
|
|
57
|
+
print()
|
|
58
|
+
print("ADVANCED CONFIGURATION:")
|
|
59
|
+
print(" Mapping: GeometricOptics (instead of default ApertureDiffraction)")
|
|
60
|
+
print(" Coherence: True (enable coherent scattering calculations)")
|
|
61
|
+
print(" Field of view factor: 2.0 (doubled FOV)")
|
|
62
|
+
print()
|
|
63
|
+
print("REPRODUCIBILITY:")
|
|
64
|
+
print(" Random seed: 42 (for reproducible results)")
|
|
65
|
+
print()
|
|
66
|
+
print("CONVERGENCE TARGET:")
|
|
67
|
+
print(" Mode: PHIPS detector convergence")
|
|
68
|
+
print(" Detectors: Indices 1-19 (26° to 170°, skipping 18° forward scatter)")
|
|
69
|
+
print(" Tolerance: 50% relative SEM (relaxed for demo)")
|
|
70
|
+
print("=" * 80 + "\n")
|
|
71
|
+
|
|
72
|
+
# Converge on all PHIPS detectors except the first one (indices 1-19)
|
|
73
|
+
detector_indices = list(range(1, 20)) # [1, 2, 3, ..., 19]
|
|
74
|
+
|
|
75
|
+
print(f"Starting convergence on {len(detector_indices)} PHIPS detectors...\n")
|
|
76
|
+
|
|
77
|
+
# Run PHIPS convergence with ALL MODIFIABLE PARAMETERS
|
|
78
|
+
results = goad.run_convergence(
|
|
79
|
+
# Required parameters
|
|
80
|
+
geometry=geom_file,
|
|
81
|
+
targets=[
|
|
82
|
+
{
|
|
83
|
+
"tolerance": 0.25, # 50% relative SEM (relaxed for demo)
|
|
84
|
+
"tolerance_type": "relative",
|
|
85
|
+
"detector_indices": detector_indices,
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
mode=goad.PHIPSMode(bins_file=phips_bins_file),
|
|
89
|
+
# ========================================================================
|
|
90
|
+
# OPTICAL PARAMETERS
|
|
91
|
+
# ========================================================================
|
|
92
|
+
wavelength=0.633, # HeNe laser wavelength in microns
|
|
93
|
+
particle_refr_index_re=1.5, # Real part of particle refractive index
|
|
94
|
+
particle_refr_index_im=0.01, # Imaginary part (absorption)
|
|
95
|
+
medium_refr_index_re=1.33, # Real part of medium refractive index (water)
|
|
96
|
+
medium_refr_index_im=0.0, # Imaginary part (no absorption in medium)
|
|
97
|
+
# ========================================================================
|
|
98
|
+
# CONVERGENCE PARAMETERS
|
|
99
|
+
# ========================================================================
|
|
100
|
+
batch_size=12, # Orientations per batch (smaller batches for finer control)
|
|
101
|
+
max_orientations=500, # Maximum total orientations
|
|
102
|
+
min_batches=10, # Minimum batches before checking convergence
|
|
103
|
+
mueller_1d=False, # Mueller matrix output (N/A for PHIPS mode)
|
|
104
|
+
# ========================================================================
|
|
105
|
+
# BEAM TRACING PARAMETERS
|
|
106
|
+
# ========================================================================
|
|
107
|
+
beam_power_threshold=0.01, # Beam power threshold (stricter than default)
|
|
108
|
+
beam_area_threshold_fac=0.001, # Beam area threshold factor (tighter threshold)
|
|
109
|
+
cutoff=0.0005, # Ray power cutoff (more accurate but slower)
|
|
110
|
+
max_rec=6, # Maximum recursion depth (limited for demo)
|
|
111
|
+
max_tir=20, # Maximum total internal reflection bounces
|
|
112
|
+
# ========================================================================
|
|
113
|
+
# GEOMETRY TRANSFORMATIONS
|
|
114
|
+
# ========================================================================
|
|
115
|
+
scale=1.0, # Problem scaling factor (numerical changes only)
|
|
116
|
+
geom_scale=[3.0, 3.0, 6.0], # Per-axis geometry scaling
|
|
117
|
+
distortion=0.0, # Geometry distortion factor
|
|
118
|
+
# ========================================================================
|
|
119
|
+
# ADVANCED CONFIGURATION
|
|
120
|
+
# ========================================================================
|
|
121
|
+
mapping=goad.Mapping.ApertureDiffraction, # or GeometricOptics
|
|
122
|
+
coherence=True, # Enable coherent scattering calculations
|
|
123
|
+
fov_factor=2.0, # Field of view factor (doubled)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Print summary
|
|
127
|
+
print(results.summary())
|
|
128
|
+
|
|
129
|
+
# Save results
|
|
130
|
+
results.save("10_advanced_parameters_results.json")
|
|
131
|
+
|
|
132
|
+
# Show detailed PHIPS output
|
|
133
|
+
print("\n" + "=" * 80)
|
|
134
|
+
print("PHIPS Detector DSCS Values (all 20 detectors)")
|
|
135
|
+
print("=" * 80)
|
|
136
|
+
|
|
137
|
+
import json
|
|
138
|
+
|
|
139
|
+
with open("10_advanced_parameters_results.json", "r") as f:
|
|
140
|
+
data = json.load(f)
|
|
141
|
+
|
|
142
|
+
phips_dscs = data.get("phips_dscs")
|
|
143
|
+
detector_angles = data.get("detector_angles")
|
|
144
|
+
|
|
145
|
+
if phips_dscs and detector_angles:
|
|
146
|
+
print(
|
|
147
|
+
f"\n{'Detector':<10} {'Angle':<10} {'DSCS Mean':<15} {'DSCS SEM':<15} {'Rel. SEM':<10}"
|
|
148
|
+
)
|
|
149
|
+
print("-" * 70)
|
|
150
|
+
|
|
151
|
+
for i, (dscs_data, angle) in enumerate(zip(phips_dscs, detector_angles)):
|
|
152
|
+
mean, sem = dscs_data[0], dscs_data[1]
|
|
153
|
+
rel_sem = (sem / abs(mean) * 100) if mean != 0 else float("inf")
|
|
154
|
+
|
|
155
|
+
# Mark which detectors were converged on
|
|
156
|
+
marker = "✓" if i in detector_indices else "○"
|
|
157
|
+
|
|
158
|
+
print(
|
|
159
|
+
f"{marker} {i:<8} {angle:<10.1f} {mean:<15.4e} {sem:<15.4e} {rel_sem:<10.2f}%"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
print("\n" + "=" * 80)
|
|
163
|
+
print("Legend:")
|
|
164
|
+
print(" ✓ = Converged on this detector")
|
|
165
|
+
print(" ○ = Not a convergence target (but still computed)")
|
|
166
|
+
print("=" * 80)
|
|
167
|
+
|
|
168
|
+
# Show configuration that was used
|
|
169
|
+
print("\n" + "=" * 80)
|
|
170
|
+
print("Configuration Used (from saved results):")
|
|
171
|
+
print("=" * 80)
|
|
172
|
+
|
|
173
|
+
config = data.get("config", {})
|
|
174
|
+
if config:
|
|
175
|
+
print(f"\nOptical parameters:")
|
|
176
|
+
print(f" Wavelength: {config.get('wavelength')} μm")
|
|
177
|
+
print(
|
|
178
|
+
f" Particle refractive index: {config.get('particle_refr_index_re')} + {config.get('particle_refr_index_im')}i"
|
|
179
|
+
)
|
|
180
|
+
print(
|
|
181
|
+
f" Medium refractive index: {config.get('medium_refr_index_re')} + {config.get('medium_refr_index_im')}i"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
beam_tracing = config.get("beam_tracing", {})
|
|
185
|
+
if beam_tracing:
|
|
186
|
+
print(f"\nBeam tracing parameters:")
|
|
187
|
+
print(f" Max recursion depth: {beam_tracing.get('max_rec')}")
|
|
188
|
+
print(f" Max TIR bounces: {beam_tracing.get('max_tir')}")
|
|
189
|
+
print(f" Cutoff: {beam_tracing.get('cutoff')}")
|
|
190
|
+
print(f" Beam power threshold: {beam_tracing.get('beam_power_threshold')}")
|
|
191
|
+
print(
|
|
192
|
+
f" Beam area threshold factor: {beam_tracing.get('beam_area_threshold_fac')}"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
geom_transform = config.get("geometry_transform", {})
|
|
196
|
+
if geom_transform:
|
|
197
|
+
print(f"\nGeometry transformations:")
|
|
198
|
+
print(f" Problem scale: {geom_transform.get('scale')}")
|
|
199
|
+
if geom_transform.get("distortion"):
|
|
200
|
+
print(f" Distortion: {geom_transform.get('distortion')}")
|
|
201
|
+
if geom_transform.get("geom_scale"):
|
|
202
|
+
print(f" Per-axis geometry scale: {geom_transform.get('geom_scale')}")
|
|
203
|
+
|
|
204
|
+
advanced = config.get("advanced_config", {})
|
|
205
|
+
if advanced:
|
|
206
|
+
print(f"\nAdvanced configuration:")
|
|
207
|
+
print(f" Mapping: {advanced.get('mapping')}")
|
|
208
|
+
print(f" Coherence: {advanced.get('coherence')}")
|
|
209
|
+
if advanced.get("fov_factor"):
|
|
210
|
+
print(f" Field of view factor: {advanced.get('fov_factor')}")
|
|
211
|
+
|
|
212
|
+
if config.get("seed"):
|
|
213
|
+
print(f"\nReproducibility:")
|
|
214
|
+
print(f" Random seed: {config.get('seed')}")
|
|
215
|
+
|
|
216
|
+
print("\n✓ Example completed successfully!")
|
|
@@ -11,6 +11,8 @@ Features:
|
|
|
11
11
|
- Strict input validation
|
|
12
12
|
- Uniform output format (UnifiedResults)
|
|
13
13
|
- Support for parameter sweeps
|
|
14
|
+
- Full control over beam tracing, geometry transformations, and advanced optics
|
|
15
|
+
- Reproducible results via random seed control
|
|
14
16
|
"""
|
|
15
17
|
|
|
16
18
|
from dataclasses import dataclass, field, asdict
|
|
@@ -211,6 +213,96 @@ class PHIPSMode(ConvergenceMode):
|
|
|
211
213
|
# ============================================================================
|
|
212
214
|
|
|
213
215
|
|
|
216
|
+
@dataclass
|
|
217
|
+
class BeamTracingConfig:
|
|
218
|
+
"""Beam tracing performance and accuracy parameters."""
|
|
219
|
+
|
|
220
|
+
beam_power_threshold: float = 0.01
|
|
221
|
+
beam_area_threshold_fac: float = 0.01
|
|
222
|
+
cutoff: float = 0.999
|
|
223
|
+
max_rec: int = 10
|
|
224
|
+
max_tir: int = 10
|
|
225
|
+
|
|
226
|
+
def __post_init__(self):
|
|
227
|
+
"""Validate beam tracing parameters."""
|
|
228
|
+
if self.beam_power_threshold <= 0 or self.beam_power_threshold > 1:
|
|
229
|
+
raise ValueError(
|
|
230
|
+
f"beam_power_threshold must be in range (0, 1], got {self.beam_power_threshold}"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
if self.beam_area_threshold_fac <= 0:
|
|
234
|
+
raise ValueError(
|
|
235
|
+
f"beam_area_threshold_fac must be positive, got {self.beam_area_threshold_fac}"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
if self.cutoff < 0 or self.cutoff > 1:
|
|
239
|
+
raise ValueError(f"cutoff must be between 0 and 1, got {self.cutoff}")
|
|
240
|
+
|
|
241
|
+
if self.max_rec < 0:
|
|
242
|
+
raise ValueError(f"max_rec must be non-negative, got {self.max_rec}")
|
|
243
|
+
|
|
244
|
+
if self.max_tir < 0:
|
|
245
|
+
raise ValueError(f"max_tir must be non-negative, got {self.max_tir}")
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@dataclass
|
|
249
|
+
class GeometryTransformConfig:
|
|
250
|
+
"""Geometry transformation parameters.
|
|
251
|
+
|
|
252
|
+
Attributes:
|
|
253
|
+
scale: Problem scaling factor - scales the entire problem including geometry,
|
|
254
|
+
wavelength, and beam area thresholds (default: 1.0)
|
|
255
|
+
distortion: Geometry distortion factor (optional)
|
|
256
|
+
geom_scale: Per-axis geometry scaling [x, y, z] - scales only the geometry
|
|
257
|
+
in each dimension independently (optional)
|
|
258
|
+
"""
|
|
259
|
+
|
|
260
|
+
scale: float = 1.0
|
|
261
|
+
distortion: Optional[float] = None
|
|
262
|
+
geom_scale: Optional[List[float]] = None
|
|
263
|
+
|
|
264
|
+
def __post_init__(self):
|
|
265
|
+
"""Validate geometry transformations."""
|
|
266
|
+
if self.scale <= 0:
|
|
267
|
+
raise ValueError(f"scale must be positive, got {self.scale}")
|
|
268
|
+
|
|
269
|
+
if self.geom_scale is not None:
|
|
270
|
+
if len(self.geom_scale) != 3:
|
|
271
|
+
raise ValueError(
|
|
272
|
+
f"geom_scale must have exactly 3 values [x, y, z], "
|
|
273
|
+
f"got {len(self.geom_scale)}"
|
|
274
|
+
)
|
|
275
|
+
if any(s <= 0 for s in self.geom_scale):
|
|
276
|
+
raise ValueError("All geom_scale values must be positive")
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@dataclass
|
|
280
|
+
class AdvancedConfig:
|
|
281
|
+
"""Advanced optical calculation parameters."""
|
|
282
|
+
|
|
283
|
+
mapping: Optional[Any] = (
|
|
284
|
+
"ApertureDiffraction" # String that will be converted to enum in __post_init__
|
|
285
|
+
)
|
|
286
|
+
coherence: bool = True
|
|
287
|
+
fov_factor: Optional[float] = None
|
|
288
|
+
|
|
289
|
+
def __post_init__(self):
|
|
290
|
+
"""Validate advanced optics settings and convert mapping string to enum."""
|
|
291
|
+
# Convert string mapping to enum if needed
|
|
292
|
+
if isinstance(self.mapping, str):
|
|
293
|
+
if self.mapping == "ApertureDiffraction":
|
|
294
|
+
self.mapping = goad.Mapping.ApertureDiffraction
|
|
295
|
+
elif self.mapping == "GeometricOptics":
|
|
296
|
+
self.mapping = goad.Mapping.GeometricOptics
|
|
297
|
+
else:
|
|
298
|
+
raise ValueError(
|
|
299
|
+
f"Invalid mapping '{self.mapping}'. Must be 'ApertureDiffraction' or 'GeometricOptics'"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if self.fov_factor is not None and self.fov_factor <= 0:
|
|
303
|
+
raise ValueError(f"fov_factor must be positive, got {self.fov_factor}")
|
|
304
|
+
|
|
305
|
+
|
|
214
306
|
@dataclass
|
|
215
307
|
class ConvergenceConfig:
|
|
216
308
|
"""
|
|
@@ -220,6 +312,29 @@ class ConvergenceConfig:
|
|
|
220
312
|
- Standard convergence (integrated parameters, Mueller elements)
|
|
221
313
|
- PHIPS detector convergence
|
|
222
314
|
- Single geometry or ensemble averaging
|
|
315
|
+
|
|
316
|
+
Attributes:
|
|
317
|
+
geometry: Path to .obj file or directory of .obj files (ensemble)
|
|
318
|
+
mode: ConvergenceMode instance (StandardMode or PHIPSMode)
|
|
319
|
+
convergence_targets: List of convergence target dicts
|
|
320
|
+
|
|
321
|
+
wavelength: Wavelength in microns (default: 0.532)
|
|
322
|
+
particle_refr_index_re: Real part of particle refractive index (default: 1.31)
|
|
323
|
+
particle_refr_index_im: Imaginary part of particle refractive index (default: 0.0)
|
|
324
|
+
medium_refr_index_re: Real part of medium refractive index (default: 1.0)
|
|
325
|
+
medium_refr_index_im: Imaginary part of medium refractive index (default: 0.0)
|
|
326
|
+
|
|
327
|
+
batch_size: Orientations per batch (default: 24)
|
|
328
|
+
max_orientations: Maximum orientations (default: 100,000)
|
|
329
|
+
min_batches: Minimum batches before convergence check (default: 10)
|
|
330
|
+
|
|
331
|
+
beam_tracing: BeamTracingConfig instance for beam tracing parameters
|
|
332
|
+
geometry_transform: GeometryTransformConfig instance for geometry transformations
|
|
333
|
+
advanced_config: AdvancedConfig instance for advanced optical parameters
|
|
334
|
+
seed: Random seed for reproducibility (optional)
|
|
335
|
+
|
|
336
|
+
mueller_1d: Compute 1D Mueller matrix (default: True, standard mode only)
|
|
337
|
+
output_dir: Output directory path (optional)
|
|
223
338
|
"""
|
|
224
339
|
|
|
225
340
|
# Required fields
|
|
@@ -239,6 +354,20 @@ class ConvergenceConfig:
|
|
|
239
354
|
max_orientations: int = 100_000
|
|
240
355
|
min_batches: int = 10
|
|
241
356
|
|
|
357
|
+
# Beam tracing configuration
|
|
358
|
+
beam_tracing: BeamTracingConfig = field(default_factory=BeamTracingConfig)
|
|
359
|
+
|
|
360
|
+
# Geometry transformations
|
|
361
|
+
geometry_transform: GeometryTransformConfig = field(
|
|
362
|
+
default_factory=GeometryTransformConfig
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Advanced optics
|
|
366
|
+
advanced_config: AdvancedConfig = field(default_factory=AdvancedConfig)
|
|
367
|
+
|
|
368
|
+
# Random seed for reproducibility
|
|
369
|
+
seed: Optional[int] = None
|
|
370
|
+
|
|
242
371
|
# Mueller matrix output (only for StandardMode)
|
|
243
372
|
mueller_1d: bool = True
|
|
244
373
|
|
|
@@ -312,6 +441,24 @@ class ConvergenceConfig:
|
|
|
312
441
|
"batch_size": self.batch_size,
|
|
313
442
|
"max_orientations": self.max_orientations,
|
|
314
443
|
"min_batches": self.min_batches,
|
|
444
|
+
"beam_tracing": {
|
|
445
|
+
"beam_power_threshold": self.beam_tracing.beam_power_threshold,
|
|
446
|
+
"beam_area_threshold_fac": self.beam_tracing.beam_area_threshold_fac,
|
|
447
|
+
"cutoff": self.beam_tracing.cutoff,
|
|
448
|
+
"max_rec": self.beam_tracing.max_rec,
|
|
449
|
+
"max_tir": self.beam_tracing.max_tir,
|
|
450
|
+
},
|
|
451
|
+
"geometry_transform": {
|
|
452
|
+
"scale": self.geometry_transform.scale,
|
|
453
|
+
"distortion": self.geometry_transform.distortion,
|
|
454
|
+
"geom_scale": self.geometry_transform.geom_scale,
|
|
455
|
+
},
|
|
456
|
+
"advanced_config": {
|
|
457
|
+
"mapping": str(self.advanced_config.mapping),
|
|
458
|
+
"coherence": self.advanced_config.coherence,
|
|
459
|
+
"fov_factor": self.advanced_config.fov_factor,
|
|
460
|
+
},
|
|
461
|
+
"seed": self.seed,
|
|
315
462
|
"mueller_1d": self.mueller_1d,
|
|
316
463
|
"is_ensemble": self.is_ensemble(),
|
|
317
464
|
}
|
|
@@ -664,7 +811,7 @@ class UnifiedConvergence:
|
|
|
664
811
|
else:
|
|
665
812
|
geom_path_str = str(self.config.geometry)
|
|
666
813
|
|
|
667
|
-
# Create GOAD settings
|
|
814
|
+
# Create GOAD settings with all parameters
|
|
668
815
|
settings = goad.Settings(
|
|
669
816
|
geom_path=geom_path_str,
|
|
670
817
|
wavelength=self.config.wavelength,
|
|
@@ -673,8 +820,32 @@ class UnifiedConvergence:
|
|
|
673
820
|
medium_refr_index_re=self.config.medium_refr_index_re,
|
|
674
821
|
medium_refr_index_im=self.config.medium_refr_index_im,
|
|
675
822
|
binning=self.config.mode.get_binning(),
|
|
823
|
+
# Beam tracing parameters
|
|
824
|
+
beam_power_threshold=self.config.beam_tracing.beam_power_threshold,
|
|
825
|
+
beam_area_threshold_fac=self.config.beam_tracing.beam_area_threshold_fac,
|
|
826
|
+
cutoff=self.config.beam_tracing.cutoff,
|
|
827
|
+
max_rec=self.config.beam_tracing.max_rec,
|
|
828
|
+
max_tir=self.config.beam_tracing.max_tir,
|
|
829
|
+
# Geometry transformations
|
|
830
|
+
scale=self.config.geometry_transform.scale,
|
|
831
|
+
# Advanced configuration
|
|
832
|
+
mapping=self.config.advanced_config.mapping,
|
|
833
|
+
coherence=self.config.advanced_config.coherence,
|
|
676
834
|
)
|
|
677
835
|
|
|
836
|
+
# Set optional parameters if provided
|
|
837
|
+
if self.config.seed is not None:
|
|
838
|
+
settings.seed = self.config.seed
|
|
839
|
+
|
|
840
|
+
if self.config.geometry_transform.distortion is not None:
|
|
841
|
+
settings.distortion = self.config.geometry_transform.distortion
|
|
842
|
+
|
|
843
|
+
if self.config.geometry_transform.geom_scale is not None:
|
|
844
|
+
settings.geom_scale = self.config.geometry_transform.geom_scale
|
|
845
|
+
|
|
846
|
+
if self.config.advanced_config.fov_factor is not None:
|
|
847
|
+
settings.fov_factor = self.config.advanced_config.fov_factor
|
|
848
|
+
|
|
678
849
|
# Create convergence instance based on mode
|
|
679
850
|
if isinstance(self.config.mode, StandardMode):
|
|
680
851
|
self._setup_standard(settings)
|
|
@@ -881,19 +1052,46 @@ def run_convergence(
|
|
|
881
1052
|
tolerance_type: "relative" or "absolute"
|
|
882
1053
|
mode: ConvergenceMode instance, or string "auto"/"standard"/"phips"
|
|
883
1054
|
**kwargs: Additional settings:
|
|
1055
|
+
# Optical settings
|
|
884
1056
|
- wavelength: Wavelength in microns (default: 0.532)
|
|
885
1057
|
- particle_refr_index_re: Real part of particle refractive index
|
|
886
1058
|
- particle_refr_index_im: Imaginary part of particle refractive index
|
|
887
1059
|
- medium_refr_index_re: Real part of medium refractive index
|
|
888
1060
|
- medium_refr_index_im: Imaginary part of medium refractive index
|
|
1061
|
+
|
|
1062
|
+
# Convergence parameters
|
|
889
1063
|
- batch_size: Orientations per batch (default: 24)
|
|
890
1064
|
- max_orientations: Maximum orientations (default: 100,000)
|
|
891
1065
|
- min_batches: Minimum batches before convergence (default: 10)
|
|
892
1066
|
- mueller_1d: Compute 1D Mueller matrix (default: True, standard mode only)
|
|
1067
|
+
|
|
1068
|
+
# Mode settings
|
|
893
1069
|
- phips_bins_file: Path to PHIPS bins TOML (required if mode="phips")
|
|
894
1070
|
- n_theta: Number of theta bins for standard mode (default: 181)
|
|
895
1071
|
- n_phi: Number of phi bins for standard mode (default: 181)
|
|
896
1072
|
|
|
1073
|
+
# Beam tracing parameters
|
|
1074
|
+
- beam_power_threshold: Beam power threshold (default: 0.05)
|
|
1075
|
+
- beam_area_threshold_fac: Beam area threshold factor (default: 4.0)
|
|
1076
|
+
- cutoff: Ray power cutoff (default: 0.001)
|
|
1077
|
+
- max_rec: Max recursion depth (default: 100)
|
|
1078
|
+
- max_tir: Max TIR bounces (default: 100)
|
|
1079
|
+
|
|
1080
|
+
# Geometry transformations
|
|
1081
|
+
- scale: Problem scaling factor - scales entire problem including geometry,
|
|
1082
|
+
wavelength, and beam area thresholds (default: 1.0)
|
|
1083
|
+
- distortion: Geometry distortion factor (optional)
|
|
1084
|
+
- geom_scale: Per-axis geometry scaling [x, y, z] - scales only geometry
|
|
1085
|
+
in each dimension independently (optional)
|
|
1086
|
+
|
|
1087
|
+
# Advanced configuration
|
|
1088
|
+
- mapping: DSCS mapping scheme (default: goad.Mapping.ApertureDiffraction)
|
|
1089
|
+
- coherence: Enable coherent scattering (default: False)
|
|
1090
|
+
- fov_factor: Field of view factor (optional)
|
|
1091
|
+
|
|
1092
|
+
# Reproducibility
|
|
1093
|
+
- seed: Random seed for orientations (optional)
|
|
1094
|
+
|
|
897
1095
|
Returns:
|
|
898
1096
|
UnifiedResults object
|
|
899
1097
|
|
|
@@ -924,7 +1122,44 @@ def run_convergence(
|
|
|
924
1122
|
[{"variable": "S11", "tolerance": 0.1, "theta_indices": [180]}],
|
|
925
1123
|
batch_size=12
|
|
926
1124
|
)
|
|
1125
|
+
|
|
1126
|
+
# Advanced: custom beam tracing and geometry scaling
|
|
1127
|
+
results = run_convergence(
|
|
1128
|
+
"complex_particle.obj",
|
|
1129
|
+
"asymmetry",
|
|
1130
|
+
max_rec=200,
|
|
1131
|
+
max_tir=150,
|
|
1132
|
+
cutoff=0.0001,
|
|
1133
|
+
scale=2.0,
|
|
1134
|
+
seed=42
|
|
1135
|
+
)
|
|
927
1136
|
"""
|
|
1137
|
+
# Extract beam tracing parameters from kwargs
|
|
1138
|
+
beam_tracing = BeamTracingConfig(
|
|
1139
|
+
beam_power_threshold=kwargs.pop("beam_power_threshold", 0.05),
|
|
1140
|
+
beam_area_threshold_fac=kwargs.pop("beam_area_threshold_fac", 4.0),
|
|
1141
|
+
cutoff=kwargs.pop("cutoff", 0.001),
|
|
1142
|
+
max_rec=kwargs.pop("max_rec", 100),
|
|
1143
|
+
max_tir=kwargs.pop("max_tir", 100),
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
# Extract geometry transform parameters
|
|
1147
|
+
geometry_transform = GeometryTransformConfig(
|
|
1148
|
+
scale=kwargs.pop("scale", 1.0),
|
|
1149
|
+
distortion=kwargs.pop("distortion", None),
|
|
1150
|
+
geom_scale=kwargs.pop("geom_scale", None),
|
|
1151
|
+
)
|
|
1152
|
+
|
|
1153
|
+
# Extract advanced configuration parameters
|
|
1154
|
+
advanced_config = AdvancedConfig(
|
|
1155
|
+
mapping=kwargs.pop("mapping", "ApertureDiffraction"),
|
|
1156
|
+
coherence=kwargs.pop("coherence", False),
|
|
1157
|
+
fov_factor=kwargs.pop("fov_factor", None),
|
|
1158
|
+
)
|
|
1159
|
+
|
|
1160
|
+
# Extract seed
|
|
1161
|
+
seed = kwargs.pop("seed", None)
|
|
1162
|
+
|
|
928
1163
|
# Normalize targets to list of dicts
|
|
929
1164
|
target_dicts = _normalize_targets(targets, tolerance, tolerance_type)
|
|
930
1165
|
|
|
@@ -946,9 +1181,16 @@ def run_convergence(
|
|
|
946
1181
|
f"Invalid mode string '{mode}'. Must be 'auto', 'standard', or 'phips'"
|
|
947
1182
|
)
|
|
948
1183
|
|
|
949
|
-
# Build config
|
|
1184
|
+
# Build config with new parameters
|
|
950
1185
|
config = ConvergenceConfig(
|
|
951
|
-
geometry=geometry,
|
|
1186
|
+
geometry=geometry,
|
|
1187
|
+
mode=mode,
|
|
1188
|
+
convergence_targets=target_dicts,
|
|
1189
|
+
beam_tracing=beam_tracing,
|
|
1190
|
+
geometry_transform=geometry_transform,
|
|
1191
|
+
advanced_config=advanced_config,
|
|
1192
|
+
seed=seed,
|
|
1193
|
+
**kwargs, # Remaining kwargs (wavelength, particle_refr_index_re, etc.)
|
|
952
1194
|
)
|
|
953
1195
|
|
|
954
1196
|
# Run
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
use goad::{
|
|
2
2
|
self,
|
|
3
3
|
bins::BinningScheme,
|
|
4
|
+
diff::Mapping,
|
|
4
5
|
geom::Geom,
|
|
5
6
|
geom::Shape,
|
|
6
7
|
multiproblem::MultiProblem,
|
|
@@ -75,6 +76,9 @@ fn _goad_py(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
|
75
76
|
m.add_class::<Orientation>()?;
|
|
76
77
|
m.add_class::<Scheme>()?;
|
|
77
78
|
|
|
79
|
+
// Mapping enum
|
|
80
|
+
m.add_class::<Mapping>()?;
|
|
81
|
+
|
|
78
82
|
// Helper functions for orientations
|
|
79
83
|
m.add_function(wrap_pyfunction!(uniform_orientation, m)?)?;
|
|
80
84
|
m.add_function(wrap_pyfunction!(discrete_orientation, m)?)?;
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "goad-py"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.1"
|
|
8
8
|
description = "Physical optics light scattering computation"
|
|
9
9
|
authors = [{name = "Harry Ballington", email = "ballington@uni-wuppertal.de"}]
|
|
10
10
|
license = {text = "GPL-3.0"}
|