Skip to content

Settings

The Settings object configures a GOAD simulation. It controls physical parameters, numerical methods, and output options.

Basic Usage

At a minimum, you must specify the path to a geometry file or directory containing geometry files and give a refractive index:

from goad import Geom, MultiProblem, Settings

# Basic settings with minimal configuration
REFR_INDEX = 1.31 + 0j
geoms = Geom.from_file("path/to/geometry.obj", [REFR_INDEX])
settings = Settings()
mp = MultiProblem(settings, geoms)
mp.solve()

By default, the refractive index is assigned to all geometries and their constituent shapes. For advanced use, see Containment Tree.

Geometry

The geometry defines the units of the problem. If your geometry file is in microns, then you should also specify the wavelength in microns. All faces in the geometry must be planar and have some non-zero area. GOAD will return with an error if there are faces with zero area (ie. extremely thin triangles), since it needs to compute normals of each face by a cross product of 2 non-colinear edge vectors. You can make geometries in the open-source Blender software, or use some example geometries straight from Python here.

If you specify a directory, GOAD will attempt to load all files with the .obj extension in the directory. It will then choose geometries at random for each orientation in the simulation. See Orientation Distribution for more details.

Internally, the geometry (or list of geometries, if a directory was specified) holds a list of Shape objects. For simple particles, like a cube, there is just a single Shape object. If the scattering geometry is made up of multiple surfaces, then there will be one Shape for each surface. For example, a cube within a cube yields a single geometry with 2 constituent cube shapes.

Containment Tree

It is possible to show the hierarchy of shapes in a geometry by printing its containment graph:

from goad import Geom  # noqa: E402

geom = Geom.from_file("path/to/multi_shape.obj", [1.31 + 0j])[0]
print(geom.containment_tree())

Output:

medium : 1.0000 + 0.0000i
├── shape 0 : 1.3100 + 0.0000i
│   ├── shape 1 : 1.3100 + 0.0000i
│   │   └── shape 3 : 1.3100 + 0.0000i
│   │       └── shape 5 : 1.3100 + 0.0000i
│   └── shape 2 : 1.3100 + 0.0000i
└── shape 4 : 1.3100 + 0.0000i

The top line shows the surrounding medium and each subsequent line shows a shape, indented by its containment depth. Sibling shapes share a parent.

When running computations on particles with multiple layers, particles with embeddings, or other so-called poly-particle cases, you will probably need to set the refractive index of each shape directly (otherwise all constituents will have the same refractive index). Use geom.refr_index(idx) to read and geom.set_refr_index(idx, n) to write, where n is a Python complex:

from goad import Geom  # noqa: E402

geom = Geom.from_file("path/to/multi_shape.obj", [1.31 + 0j])[0]

# Override the refractive index of individual shapes
geom.set_refr_index(0, 1.5 + 0.0j)  # Set shape 0 to 1.5
geom.set_refr_index(1, 1.33 + 0.01j)  # Set shape 1 to 1.33 + 0.01j

print(geom.containment_tree())

Output:

medium : 1.0000 + 0.0000i
├── shape 0 : 1.5000 + 0.0000i
│   ├── shape 1 : 1.3300 + 0.0100i
│   │   └── shape 3 : 1.3100 + 0.0000i
│   │       └── shape 5 : 1.3100 + 0.0000i
│   └── shape 2 : 1.3100 + 0.0000i
└── shape 4 : 1.3100 + 0.0000i

Physical Parameters

Wavelength

The wavelength of incident light in micrometers:

from goad import Geom, MultiProblem, Settings  # noqa: E402

# Configure wavelength (in micrometers)
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(
    wavelength=0.532,  # 532 nm
)
mp = MultiProblem(settings, geoms)
mp.solve()

Default: 0.532 (532 nm, green laser)

Refractive Indices

The particle refractive index is supplied per-shape when the geometry is loaded (Geom.from_file(path, [n1, n2, ...])). A single complex value is broadcast to every shape; otherwise pass one entry per shape. The medium refractive index is set on Settings:

from goad import Geom, MultiProblem, Settings  # noqa: E402

# Configure refractive indices for particle and medium
geoms = Geom.from_file("path/to/geometry.obj", [1.5 + 0.01j])
settings = Settings(
    medium_refr_index=1.33 + 0.0j,
)
mp = MultiProblem(settings, geoms)
mp.solve()

Defaults:

  • Particle refractive index (broadcast): 1.31 + 0j (typical glass)
  • medium_refr_index: 1.0 + 0j (vacuum/air)

See Containment Tree for inspecting and overriding per-shape refractive indices on a loaded Geom.

Particle Scaling

Scales the entire problem, including geometry and wavelength. Does not change the physics, only used for improving clipping algorithm accuracy. The default value is usually sufficient.

Parameter: scale
Default: 1.0

Orientation Distribution

Define how particle orientations are sampled:

from goad import (  # noqa: E402
    EulerConvention,
    Geom,
    MultiProblem,
    Orientation,
    Settings,
)

# Configure particle orientation distribution
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(
    orientation=Orientation.uniform(
        num_orients=100, euler_convention=EulerConvention("ZYZ")
    ),
)
mp = MultiProblem(settings, geoms)
mp.solve()

Default: Orientation.uniform(num_orients=1) with EulerConvention('ZYZ')

For discrete orientations:

from goad import (  # noqa: E402
    Euler,
    EulerConvention,
    Geom,
    MultiProblem,
    Orientation,
    Settings,
)

# Configure discrete orientations
orients = Orientation.discrete(
    eulers=[Euler(0, 0, 0), Euler(45, 90, 0)], euler_convention=EulerConvention("ZYZ")
)
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(orientation=orients)
mp = MultiProblem(settings, geoms)
mp.solve()

The output will be an average over the orientations. Results from individual orientations are not stored. See Results for more details.

Seed

Seeds the random number generator for reproducibility.

Parameter: seed
Default: None

Zones

Zones define a set of query points to evaluate the far-field scattering at. By default, GOAD creates three zones:

  1. Full zone - The full scattering sphere from θ=0° to θ=180°. Used for computing integrated parameters like asymmetry, scattering cross-section, and albedo.
  2. Forward zone - A single point at θ=0.01° (slightly off-axis for numerical stability). Used for computing extinction cross-section via the optical theorem.
  3. Backward zone - A single point at θ=180°. Used for computing backscatter cross-section, lidar ratio, and depolarization ratio.

You can add additional zones as needed for your application. For backscattering-only applications, you can exclude the full zone and compute only at forward and backward scattering for faster computations.

Note: GOAD automatically determines the zone type based on the theta range of your binning scheme. If the theta range covers 0° to 180°, it is classified as a Full zone and integrated parameters (asymmetry, scattering cross-section, etc.) will be computed. If the range is partial, it is classified as a Custom zone and these parameters will not be computed.

Different parameters are computed depending on the zone type - see Integrated Parameters for more details.

Each zone is specified by a binning scheme and an optional label. See Binning Schemes for more info about binning schemes.

from goad import BinningScheme, Geom, MultiProblem, Settings, ZoneConfig  # noqa: E402

geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])

# Default: single full zone with interval binning (high-res forward/back)
settings = Settings()

# Custom full zone with simple binning
settings = Settings(
    zones=[ZoneConfig(BinningScheme.simple(180, 48))],
)

# Labeled zone
settings = Settings(
    zones=[ZoneConfig(BinningScheme.simple(90, 24), label="coarse")],
)

# Backscatter-only (no full zone, just forward + backward)
settings = Settings(zones=[])

mp = MultiProblem(settings, geoms)
mp.solve()

Default: A single full zone with interval binning (high resolution at forward and backward angles).

Binning Schemes

Control the angular resolution of scattering calculation in the far-field. As particle size increases, the width of peaks in the scattering decreases. GOAD currently requires the user to choose a sufficiently fine binning scheme to resolve the peaks. For phi angles, a relatively course binning scheme can be used, but for theta angles, care should be taken to ensure sufficient resolution, otherwise the integrated parameters lose accuracy. The compute time approximately scales with the number of bins, which for simple and interval binning schemes is just the product of the number of bins in each dimension.

Simple Binning

Uniform spacing in theta and phi:

from goad import BinningScheme, Geom, MultiProblem, Settings, ZoneConfig  # noqa: E402

# Configure angular binning for scattering output
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(
    zones=[ZoneConfig(BinningScheme.simple(num_theta=180, num_phi=48))],
)
mp = MultiProblem(settings, geoms)
mp.solve()

Default: BinningScheme.interval(thetas=[0, 5, 175, 179, 180], theta_spacings=[0.1, 2.0, 0.5, 0.1], phis=[0, 360], phi_spacings=[7.5])

Interval Binning

Variable resolution for different angular regions:

from goad import BinningScheme, Geom, MultiProblem, Settings, ZoneConfig  # noqa: E402

# Use variable angular resolution
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(
    zones=[
        ZoneConfig(
            BinningScheme.interval(
                thetas=[0, 90, 180],
                theta_spacings=[1, 2],  # 1° steps up to 90°, then 2° steps
                phis=[0, 360],
                phi_spacings=[2],
            )
        )
    ],
)
mp = MultiProblem(settings, geoms)
mp.solve()

This example uses 1° resolution for forward scattering (0-90°) and 2° for backward scattering (90-180°). interval binning schemes are useful if the user is only interested in a specific angular scattering range, eg 6°-25°.

Custom Binning

Specify arbitrary bin edges:

from goad import BinningScheme, Geom, MultiProblem, Settings, ZoneConfig  # noqa: E402

# Specify arbitrary bin edges
binning = BinningScheme.custom(
    bins=[
        [[0, 10], [0, 360]],  # Forward scattering cone
        [[10, 170], [0, 360]],  # Side scattering
        [[170, 180], [0, 360]],  # Backscattering cone
    ]
)
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(zones=[ZoneConfig(binning)])
mp = MultiProblem(settings, geoms)
mp.solve()

GOAD will compute the bin centres automatically. GOAD will not compute the 1D mueller matrix or integrated parameters if using a custom binning scheme.

Mapping Method

Choose how near-field results map to the far-field:

from goad import Geom, Mapping, MultiProblem, Settings  # noqa: E402

# Configure near-to-far field mapping method
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(
    mapping=Mapping("ad"),  # 'ad' for Aperture Diffraction, 'go' for Geometric Optics
)
mp = MultiProblem(settings, geoms)
mp.solve()

Options:

  • 'ad': Aperture Diffraction (default, more accurate). Suitable for fixed-orientation computations.
  • 'go': Geometric Optics (faster, suitable for very large particles). Generally not suitable for fixed-orientation computations.

Default: Mapping('ad')

Beam Tracing Parameters

Beams traced in the near-field if they pass the following checks, in order:

  • Beam power is below the threshold
  • Beam area is below the threshold
  • Beam number of recursion is above the threshold

If the beam is from a total internal reflection event, the beam is traced even if the recursion is above the threshold, as long as the total internal reflection count is below the threshold.

Beam Thresholds

Control when beams are truncated during ray tracing:

from goad import Geom, MultiProblem, Settings  # noqa: E402

# Configure beam tracing thresholds
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(
    beam_power_threshold=1e-6,  # Stop tracking beams below this power
    beam_area_threshold_fac=1e-3,  # Stop tracking beams smaller than this fraction
    cutoff=1e-10,  # Global energy cutoff
)
mp = MultiProblem(settings, geoms)
mp.solve()

Defaults:

  • beam_power_threshold: 0.005 (discard beams below 0.5% of incident power).
  • beam_area_threshold_fac: 0.1 (factor × λ² determines the physical area threshold, below which beams are discarded. It scales with λ² following the applicability of geometric optics).
  • cutoff: 0.99 (trace 99% of energy in the near field, then map. You generally want to use a value of at least 0.95 here, unless you have a good reason to do otherwise).

Lower thresholds and higher cutoff increases accuracy but slows computation.

Recursion Limits

Limit internal beams bounces:

from goad import Geom, MultiProblem, Settings  # noqa: E402

# Configure ray tracing limits
geoms = Geom.from_file("path/to/geometry.obj", [1.31 + 0j])
settings = Settings(
    max_rec=10,  # Maximum internal reflections
    max_tir=5,  # Maximum total internal reflections
)
mp = MultiProblem(settings, geoms)
mp.solve()

Defaults:

  • max_rec: 10 (maximum internal reflections)
  • max_tir: 10 (maximum total internal reflections)

Increase these for complex internal ray paths, but expect slower performance. High numbers of total internal reflections recommended for backscattering computations, ie. 20.

Output Options

Directory

Specify output directory for simulation data:

Parameter: directory
Default: "goad-run"

Coherence

Enable coherent beam addition (phase tracking):

Parameter: coherence
Default: True

Enables coherent beam addition with phase tracking for interference effects. If coherence is enabled, GOAD traces the phase of each beam, and combines amplitude matrices in the far-field with interference. If coherence is disabled, GOAD traces the phase of each beam, but combines the far-field contributions of beams in the far-field only by a linear summation of Mueller matrices. Coherence should be enabled for backscattering computations.

Verbosity

Suppress console output:

Parameter: quiet
Default: False

Set to True to silence progress messages.

Complete Example

from goad import (  # noqa: E402
    BinningScheme,
    Geom,
    Mapping,
    MultiProblem,
    Orientation,
    Settings,
    ZoneConfig,
)

# Complete configuration example
geoms = Geom.from_file("path/to/geometry.obj", [1.5 + 0.01j])
settings = Settings(
    medium_refr_index=1.0 + 0.0j,
    wavelength=0.532,
    orientation=Orientation.uniform(num_orients=100),
    zones=[ZoneConfig(BinningScheme.simple(num_theta=180, num_phi=48))],
    mapping=Mapping("ad"),
    beam_power_threshold=1e-6,
    beam_area_threshold_fac=1e-3,
    cutoff=0.999,
    max_rec=10,
    max_tir=5,
    coherence=False,
    quiet=False,
    directory="output/",
)
mp = MultiProblem(settings, geoms)
mp.solve()

Parameter Reference

Note: geometry loading is no longer part of Settings. Load geometry with Geom.from_file(path, refr_indices) and pass the result as the second argument to MultiProblem / Convergence.

Parameter Type Default Description
wavelength float 0.532 Wavelength in geometry units
medium_refr_index complex 1.0 + 0j Refractive index of the surrounding medium
orientation Orientation Orientation.uniform(1) Orientation distribution
zones list[ZoneConfig] Single full zone with interval binning Zone configurations for far-field evaluation
mapping Mapping Mapping('ad') Near-to-far field mapping method
beam_power_threshold float 0.005 Beam power truncation threshold
beam_area_threshold_fac float 0.1 Beam area truncation factor
cutoff float 0.99 Energy tracking cutoff
max_rec int 10 Maximum internal reflections
max_tir int 10 Maximum total internal reflections
scale float 1.0 Geometry scaling factor
seed int None Seed for random number generator
directory str "goad_run" Output directory path
coherence bool True Enable coherent beam addition
quiet bool False Suppress console output