caustic_irradiance — OPTICS watervision op

• Data kinds: image2d → table

• Call: import fullseye as fs; fs.ledger.caustic_irradiance(height_field, extent_m, depth_m, light_dir, n_water=None, size=None, method='photon', ray_density=4, periodic=True, max_gain=20.0) -> 'dict' (to call the implementation directly, import watervision; watervision.caustic_irradiance(height_field, extent_m, depth_m, light_dir, n_water=None, size=None, method='photon', ray_density=4, periodic=True, max_gain=20.0) -> 'dict'; from the registry, opsoptics.get("caustic_irradiance"))

• GPU: none (CPU reference implementation only)

Usage

The irradiance a collimated light leaves on a flat floor below a wavy water surface -> dict.

height_field (ny, nx) is the surface height (m) of a periodic patch of extent_m metres (a length, or (Lx, Ly)); it is treated as band limited (the Nyquist row and column are dropped) and sampled finer by an integer ray_density (rays per cell and axis; the default 4 is for the photon method, the wavefront method triangulates its landing points and is usually run with 1 or 2) with a Fourier interpolation that also gives the exact slopes. depth_m is the depth of the floor below the mean level (it must lie below every wave crest). light_dir is the direction the light travels, a 3-vector with a downward z or (zenith_deg, azimuth_deg). n_water defaults to the material-table index at 0.55 um. size is the floor grid (ny, nx) (default: that of the height field).

method="photon": one ray per fine cell leaves the surface, is refracted by the vector form of Snell's law with the normal of the local slope, carries the power the beam puts on that cell, E0 (d.(-grad h, 1)) dx dy, times the unpolarised Fresnel transmittance, lands on the floor and is binned by bilinear splatting (the weights add up exactly, so the energy is conserved). method="wavefront": the map surface to floor, (x, y) -> (x, y) + s t_xy, has the Jacobian J (spectral derivatives of the periodic displacement) and the irradiance at a landing point is the carried power over |det J|; it is interpolated linearly onto the floor grid. Where det J falls below 1 / max_gain or turns negative the map folds (a caustic: the geometric gain is unbounded and several sheets overlap), the wavefront result is inf there and fold_mask flags the pixels (dilated by one pixel). The unit is the irradiance of the beam, measured on the horizontal floor, so a flat surface gives exactly cos(theta_i) T(theta_i) everywhere (the refracted beam is narrower by cos(theta_i) / cos(theta_t) and meets the floor at cos(theta_t)).

The table has irradiance (ny, nx), fold_mask, x_m / y_m (floor nodes), incident_flux (the power of the beam through the patch, summed over the rays), incident_flux_analytic (cos(theta_i) Lx Ly), transmitted_flux (after Fresnel), floor_flux (the integral of the irradiance, photon only), lost_flux_fraction (photons that left a non-periodic patch), mean_transmittance, flat_irradiance (cos(theta_i) T), theta_i_deg, theta_t_deg, n_water and n_rays. Non-finite heights, a floor above a crest, a light not pointing down and a patch too large for the ray cap are refused.

Family-wide input contract (fail-closed)

Every optics op validates its input before computing (nothing slips through silently):

• Units are baked into the argument name — _mm / _um / _deg / _mrad. Confusing mm with µm does not crash; it yields a plausible-looking wrong answer, so the name prevents it. Nothing here guesses the unit from the magnitude.

• Strings raise ValueError — float('50') succeeds, so an unparsed configuration value would slip through as a length (measured: thin_lens('50', '200') returned a plausible 66.667 mm). bool is refused too, as the implicit promotion True == 1.

• complex / masked arrays raise ValueError (real-valued slots only; silently dropping the imaginary part or peeling off the mask is refused). NaN/Inf raises ValueError on every input.

• Division by zero and its relatives are refused by name: focal length 0, radius of curvature 0, refractive index <= 0, a fully opaque aperture (all zeros, so the normalisation is 0/0), a PSF whose sum is <= 0, a Stokes vector with S0 = 0, and an object sitting at the front focal point (the image is at infinity).

• Only two ops return a non-finite value, and both state it as a contract: depth_of_field returns far_mm = inf beyond the hyperfocal distance (that is what the hyperfocal distance means), and gaussian_beam returns wavefront_radius_mm = inf at the waist (the radius of curvature of a plane wavefront). Both also return a finite companion (far_is_infinite / curvature_per_mm). Any other silent NaN/Inf is detected internally and raises ValueError — "float64 overflowed" and "the answer is infinite" are different claims, so the first is never returned wearing the face of the second.

• Size caps: generated grids are capped by optics.MAX_GRID (4096); supplied fields/PSFs/apertures by optics.MAX_FIELD_ELEMENTS (2^24); ABCD element chains by optics.MAX_SYSTEM_ELEMENTS (1024); Zernike by MAX_ZERNIKE_TERMS (512) / MAX_ZERNIKE_ORDER (40) / MAX_ZERNIKE_BASIS (2^25). This closes, fail-closed, the paths where a small argument triggers a huge internal allocation (measured: n_max=40 × 4096² needs 108 GB).

• Physically impossible states are refused too: a Stokes vector with degree of polarisation > 1, negative transmittance, negative intensity, and invalid Zernike indices such as n-|m| odd.

Detailed usage guide

• optics_imaging family guide

References (sample data, literature)

• Sample-data catalog (download URLs / licences) — 2-D uses skimage.data (BSD/public domain) plus synthetic images; 3-D lists download URLs for real data sources (Stanford, PDS, …).

• Operator provenance and references — the sources of the research/methods this op family came from.

• The canonical algorithm (author, year) and its uses are named in the family usage guide above.

Runnable examples (verified samples that actually call this op)

• poc_water_inspection_images — py -3.11 examples/poc_water_inspection_images.py

Ops the type connects to (they accept table as input)

abcd_matrix · wavefront_stats · sfr_from_edge · paraxial_trace · seidel_coefficients · spot_stats · tolerance_analysis · wavefront_from_opd

Same category (watervision)

bottle_image · fill_level_from_image · wet_surface_appearance · wet_dry_inspection_pair · underwater_inspection_image · caustic_floor_segmentation


*Provenance: watervision.py — OPTICS operator registry. This per-op note is generated by tools/opdocs.py md (do not hand-edit).*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.