Results
After solving a GOAD problem, access the scattering results through the results property of the MultiProblem object. For information about checking the validity of the results, see Checks.
Basic Usage
from goad import MultiProblem, Settings
# Solve the problem and access results
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
results = mp.results
GOAD organizes scattering results into zones. Properties like
results.mueller,results.bins, andresults.scat_crossare convenience accessors that return data from the full zone. For zone-specific access (e.g., backscatter parameters), see the Zones section.
Mueller Matrices
The Mueller matrix describes the transformation of the Stokes vector during scattering. GOAD provides Mueller matrices in different forms and for different scattering components.
2D Mueller Matrix
Full angular distribution over theta and phi:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
# Get the 2D Mueller matrix
mueller = mp.results.mueller
print(f"Number of bins: {len(mueller)}")
print(f"Mueller matrix elements per bin: {len(mueller[0])}") # 16 elements
The Mueller matrix is returned as a list of 16-element lists, where each element corresponds to [s11, s12, s13, s14, s21, s22, s23, s24, s31, s32, s33, s34, s41, s42, s43, s44] for each bin.
1D Mueller Matrix
Phi-integrated Mueller matrix (theta only):
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
# Get the 1D phi-integrated Mueller matrix
mueller_1d = mp.results.mueller_1d
print(f"Number of theta bins: {len(mueller_1d)}")
The 1D Mueller matrix integrates over all phi angles at each theta, providing an azimuthally-averaged scattering pattern.
Mueller Matrix Components
GOAD separates scattering into beam and external diffraction components:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
# Access different scattering components
mueller_total = mp.results.mueller # Total scattering
mueller_beam = mp.results.mueller_beam # Beam component
mueller_ext = mp.results.mueller_ext # External diffraction
# Same for 1D Mueller matrices
mueller_1d_total = mp.results.mueller_1d
mueller_1d_beam = mp.results.mueller_1d_beam
mueller_1d_ext = mp.results.mueller_1d_ext
- Beam component: Direct scattering from ray tracing
- External diffraction: Diffraction around the particle exterior
- Total: Sum of beam and external diffraction components. If coherence is disabled, the total is just the linear sum of beam and external diffraction components.
Angular Bins
Access the angular coordinates for each bin:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
# Get 2D bins (theta, phi pairs)
bins_2d = mp.results.bins
for theta, phi in bins_2d[:5]: # First 5 bins
print(f"Theta: {theta}°, Phi: {phi}°")
# Get 1D bins (theta only)
bins_1d = mp.results.bins_1d
if bins_1d:
print(f"Theta values: {bins_1d}")
The bins correspond to the center values of each angular bin in the scattering calculation. It is also possible to directly access the theta and phi values from the binning scheme, which is useful if you need to access the bins without running the simulation.
Integrated Parameters
GOAD computes several integrated optical parameters from the Mueller matrix. These parameters are available directly on the results object for convenience, and are also accessible through zone-specific params dictionaries (see Zones for more details).
Scattering Cross Section
The total scattering cross section:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
scat_cross = mp.results.scat_cross
print(f"Scattering cross section: {scat_cross}")
Extinction Cross Section
The total extinction cross section (scattering + absorption):
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
ext_cross = mp.results.ext_cross
print(f"Extinction cross section: {ext_cross}")
Asymmetry Parameter
The asymmetry parameter g (average cosine of scattering angle):
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
g = mp.results.asymmetry
print(f"Asymmetry parameter: {g}")
Values range from -1 (complete backscattering) to +1 (complete forward scattering).
Single Scattering Albedo
The ratio of scattering to extinction:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
albedo = mp.results.albedo
print(f"Single scattering albedo: {albedo}")
Values range from 0 (pure absorption) to 1 (pure scattering).
Zones
GOAD organizes scattering results into zones. For configuration details, see the Zones section in Settings.
Accessing Zones
Access individual zones from the results:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
# Access all zones
zones = mp.results.zones
print(f"Available zones: {zones}")
# Access specific zones by type
full_zone = mp.results.full_zone
forward_zone = mp.results.forward_zone
backward_zone = mp.results.backward_zone
print(f"Full zone: {full_zone.name}, bins: {full_zone.num_bins}")
print(f"Forward zone: {forward_zone.name}, bins: {forward_zone.num_bins}")
print(f"Backward zone: {backward_zone.name}, bins: {backward_zone.num_bins}")
Zone-Specific Parameters
Each zone type provides different parameters:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
# Full zone parameters (requires full theta coverage)
full_zone = mp.results.full_zone
full_params = full_zone.params
print(f"Scattering cross section: {full_params['scatt_cross']}")
print(f"Extinction cross section: {full_params['ext_cross']}")
print(f"Asymmetry parameter: {full_params['asymmetry']}")
print(f"Single scattering albedo: {full_params['albedo']}")
# Backward zone parameters (lidar-relevant)
backward_zone = mp.results.backward_zone
back_params = backward_zone.params
print(f"Backscatter cross section: {back_params['backscatter_cross']}")
print(f"Lidar ratio: {back_params['lidar_ratio']}")
print(f"Depolarization ratio: {back_params['depolarization_ratio']}")
# Forward zone parameters (optical theorem)
forward_zone = mp.results.forward_zone
fwd_params = forward_zone.params
print(f"Extinction (optical theorem): {fwd_params['ext_cross_optical_theorem']}")
Full zone (requires full theta range 0-180°):
scatt_cross: Scattering cross sectionext_cross: Extinction cross sectionasymmetry: Asymmetry parameter galbedo: Single scattering albedo
Backward zone (θ = 180°):
backscatter_cross: Backscattering cross sectionlidar_ratio: Extinction-to-backscatter ratiodepolarization_ratio: Linear depolarization ratio
Forward zone (θ ≈ 0°):
ext_cross_optical_theorem: Extinction cross section via optical theorem
Zone Mueller Matrices
Each zone contains its own Mueller matrix data:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
# Access Mueller matrix for a specific zone
zone = mp.results.full_zone
mueller = zone.mueller # 2D Mueller matrix for this zone
mueller_1d = zone.mueller_1d # 1D phi-integrated Mueller matrix
bins = zone.bins # Angular bins for this zone
bins_1d = zone.bins_1d # 1D theta bins
print(f"Zone '{zone.name}' has {zone.num_bins} bins")
print(f"Mueller matrix shape: {mueller.shape}")
Power Budget
Track energy conservation throughout the simulation:
from goad import MultiProblem, Settings
mp = MultiProblem(Settings(geom_path="path/to/geometry.obj"))
mp.solve()
powers = mp.results.powers
print(f"Input power: {powers['input']}")
print(f"Output power: {powers['output']}")
print(f"Absorbed power: {powers['absorbed']}")
print(f"Missing power: {powers['missing']}")
# Check energy conservation
total_accounted = (
powers["output"]
+ powers["absorbed"]
+ powers["trnc_ref"]
+ powers["trnc_rec"]
+ powers["trnc_clip"]
+ powers["trnc_energy"]
+ powers["trnc_area"]
)
print(f"Energy conservation error: {powers['input'] - total_accounted}")
The power dictionary contains:
input: Incident beam power ie. the mean geometrical cross sectionoutput: Total near-field output power, the sum of scattering and absorptionabsorbed: Power absorbed by the particletrnc_ref: Power lost due to max total internal reflections reachedtrnc_rec: Power lost due to max recursions reachedtrnc_clip: Power lost during beam clippingtrnc_energy: Power lost due to minimum energy cutofftrnc_area: Power lost due to minimum area cutofftrnc_cop: Power lost due to total cutoff power thresholdclip_err: Error from clipping algorithmext_diff: External diffraction powermissing: Total unaccounted power
Complete Example
import numpy as np
from goad import BinningScheme, MultiProblem, Orientation, Settings, ZoneConfig
# Configure and solve
settings = Settings(
geom_path="path/to/geometry.obj",
wavelength=0.532,
orientation=Orientation.uniform(num_orients=100),
zones=[ZoneConfig(BinningScheme.simple(num_theta=180, num_phi=48))],
)
mp = MultiProblem(settings)
mp.solve()
# Access all results
results = mp.results
# Extract scattering phase function (S11 element)
mueller_1d = np.array(results.mueller_1d)
s11 = mueller_1d[:, 0] # First column is S11
theta = np.array(results.bins_1d)
# Print integrated parameters
print(f"Scattering cross section: {results.scat_cross:.6f}")
print(f"Extinction cross section: {results.ext_cross:.6f}")
print(f"Asymmetry parameter: {results.asymmetry:.4f}")
print(f"Single scattering albedo: {results.albedo:.4f}")
# Access zone-specific parameters
backward = results.backward_zone
print(f"Lidar ratio: {backward.params['lidar_ratio']:.4f}")
print(f"Backscatter cross section: {backward.params['backscatter_cross']:.6f}")
# Check power budget
powers = results.powers
efficiency = powers["output"] / powers["input"]
print(f"Scattering efficiency: {efficiency:.4f}")
print(f"Missing power fraction: {powers['missing'] / powers['input']:.2e}")
Result Properties Reference
| Property | Type | Description |
|---|---|---|
bins |
ndarray |
2D angular bins [[theta, phi], ...] |
bins_1d |
ndarray |
1D theta bins |
mueller |
ndarray |
2D Mueller matrix (total) |
mueller_beam |
ndarray |
2D Mueller matrix (beam component) |
mueller_ext |
ndarray |
2D Mueller matrix (external diffraction) |
mueller_1d |
ndarray |
1D Mueller matrix (total) |
mueller_1d_beam |
ndarray |
1D Mueller matrix (beam component) |
mueller_1d_ext |
ndarray |
1D Mueller matrix (external diffraction) |
scat_cross |
float |
Scattering cross section |
ext_cross |
float |
Extinction cross section |
asymmetry |
float |
Asymmetry parameter g |
albedo |
float |
Single scattering albedo |
powers |
dict[str, float] |
Power budget dictionary |
zones |
Zones |
Collection of all zones |
full_zone |
Zone |
Full scattering zone (0-180°) |
forward_zone |
Zone |
Forward scattering zone (θ ≈ 0°) |
backward_zone |
Zone |
Backward scattering zone (θ = 180°) |
Zone Properties Reference
| Property | Type | Description |
|---|---|---|
name |
str |
Zone name (e.g., "full", "forward", "backward") |
zone_type |
ZoneType |
Zone type enum (Full, Forward, Backward, Custom) |
num_bins |
int |
Number of angular bins in this zone |
bins |
ndarray |
2D angular bins for this zone |
bins_1d |
ndarray |
1D theta bins for this zone |
mueller |
ndarray |
2D Mueller matrix for this zone |
mueller_1d |
ndarray |
1D Mueller matrix for this zone |
params |
dict[str, float] |
Zone-specific integrated parameters |
label |
str \| None |
Optional user-defined label |