iactrace.core¶
Low-level ray tracing components. Most users should use the
Telescope and Camera
classes instead of these functions directly.
Render Engine¶
- iactrace.core.render_optics(optical_groups, obstruction_groups, sources, values, source_type)[source]¶
Render sources through the optics; return one flat
RayBundle.Materialises the full
(n_elements * n_sources * n_samples,)ray buffer. Userender_optics_accumulate()when only a small aggregate (image, response matrix, …) is needed.
- iactrace.core.trace_optics(optical_groups, obstruction_groups, ray_origins, ray_directions, values, record_trajectory=False)[source]¶
Trace rays from arbitrary origins through full optical system.
- Args:
optical_groups: List of OpticalElementGroup (combined mirrors + lenses). obstruction_groups: List of ObstructionGroup. ray_origins: (n_rays, 3). ray_directions: (n_rays, 3), normalized. values: (n_rays,). record_trajectory: When True, also collect the per-stage hit points and
return them as a
Trajectoryalongside the RayBundle. Off by default; when off, no trajectory is built and nothing extra is computed (mirrors thetrace_chain()record_trajectoryoption).- Returns:
A
TraceResult. Itsraysare in 3D space after all optical stages; itstrajectoryisNoneunlessrecord_trajectorywas set, in which case theTrajectoryholds the source point followed by each stage’s landing point (world frame),(n_stages + 1, n_rays, 3). It ends on the last optic – this kernel knows no camera.
Ray Bundle¶
- class iactrace.core.RayBundle[source]¶
Bases:
ModuleBundle of rays through the optical system.
Carries ray positions, directions, weights, path lengths, and a per-ray liveness flag.
The frame of
origins/directionsis implicit and depends on where the bundle came from:Telescope.renderandTelescope.tracereturn rays in the camera-local frame so they can be fed straight intoCamera.collect/Camera.image.Liveness vs throughput. IACTrace keeps the two ways a ray can be “lost” on separate axes:
alive(bool) answers “is this a valid, still-propagating ray?”. It is flipped off only by geometry / occlusion loss: a ray that misses every element in a stage, lands outside an aperture, is blocked by an obstruction, or misses the sensor. OnceFalseit staysFalse(an absorbing state), and theorigins/directionsof a dead ray are meaningless — always mask geometry withalivebefore reading positions.values(float >= 0) is the radiometric throughput of a live ray. Every physical coefficient multiplies into it — primary sampling weight, reflectivity / transmittance, quantum efficiency, concentrator throughput. A live ray may legitimately reach0(a perfectly absorbing coating, total internal reflection); that is distinct from a dead ray and is not recorded on thealiveaxis.
As an invariant a dead ray always carries
values == 0, so the image / response-matrix sums (which addvalues) need no masking; thealiveflag exists so per-ray consumers can tell why a ray is dark. “Carries light” is simplyalive & (values > 0).By the time the bundle reaches
Camera.collectthe entries ofvaluesare photoelectrons, not raw photons.- Attributes:
- origins: Ray positions in 3D (n_rays, 3). Meaningful only where
aliveisTrue.- directions: Ray direction vectors (n_rays, 3). Meaningful only
where
aliveisTrue.
values: Throughput-weighted ray intensities (n_rays,). path_length: Accumulated optical path length per ray
(n_rays,), in metres.
- n: Per-ray refractive index of the medium each ray is
currently propagating in (n_rays,). Carried so downstream consumers (sensor intersection, focal-surface analysis) can weight the final geometric leg correctly.
- alive: Per-ray liveness flag (n_rays,), boolean.
Truefor a valid, still-propagating ray. Defaults to all-
Trueat construction, i.e. a freshly built bundle is fully alive.
- origins = <dataclasses._MISSING_TYPE object>¶
- directions = <dataclasses._MISSING_TYPE object>¶
- values = <dataclasses._MISSING_TYPE object>¶
- path_length = <dataclasses._MISSING_TYPE object>¶
- n = <dataclasses._MISSING_TYPE object>¶
- alive = <dataclasses._MISSING_TYPE object>¶
- replace(**changes)[source]¶
Copy with the given fields replaced (functional update).
rays.replace(values=v)reads better than re-listing all six fields; unknown field names raiseTypeError.
- to_frame(origin, rotation)[source]¶
Express these rays in the local frame given by
origin+ Eulerrotation.originis the new frame’s position in the current frame;rotationare XYZ Euler angles in degrees.This is a pure coordinate transform: it moves
originsanddirectionsand leavesvalues/path_length/n/aliveuntouched.
Optical Element Composition¶
- class iactrace.core.OpticalElementGroup[source]¶
Bases:
ModuleOptical element group composing surface + aperture + interaction.
All optical elements (mirrors, lenses, slabs) are instances of this class configured with appropriate modules.
- __init__(positions, rotations, surface, aperture, interaction_module, sample_key, optical_stage=0, n_samples=100, bsdf=None)[source]¶
- positions = <dataclasses._MISSING_TYPE object>¶
- rotations = <dataclasses._MISSING_TYPE object>¶
- surface = <dataclasses._MISSING_TYPE object>¶
- aperture = <dataclasses._MISSING_TYPE object>¶
- interaction_module = <dataclasses._MISSING_TYPE object>¶
- optical_stage = <dataclasses._MISSING_TYPE object>¶
- n_samples = <dataclasses._MISSING_TYPE object>¶
- bsdf = <dataclasses._MISSING_TYPE object>¶
- sample_key = <dataclasses._MISSING_TYPE object>¶
- property n_elements¶
- property interaction¶
- property kind¶
User-facing element kind, derived from the interaction module.
- transform_to_world()[source]¶
Compute geometry from current surface params and transform to world coordinates.
Samples are generated at call time using the stored n_samples and sample_key.
- Returns:
Tuple of (points_world, normals_world, weights) arrays.
- sample_primary_geometry(roughness_salt)[source]¶
Sample this group’s aperture, with this group’s surface roughness applied.
- Args:
- roughness_salt: Integer folded into this group’s
sample_key to draw the roughness perturbation, keeping it independent from the aperture-sampling draw and from other call sites sharing the same
sample_key.
- roughness_salt: Integer folded into this group’s
- Returns:
Tuple of (points_world, normals_world, weights) arrays, as
transform_to_world(), withnormals_worldperturbed.
- intersect(element_idx, origins, directions)[source]¶
Intersect world-frame rays with element
element_idx.- Args:
element_idx: Index of the element within this group. origins, directions: (n_rays, 3) rays in world coordinates.
- Returns:
Tuple of
(t, points_world, normals_world), each(n_rays, ...).tisinfwhere the surface hit falls outside the element’s aperture.
- intersect_t(element_idx, origins, directions)[source]¶
Hit distance only, for the nearest-hit search over a stage.
Same
tasintersect(), minus the surface point and normal.- Args:
element_idx: Index of the element within this group. origins, directions:
(n_rays, 3)rays in world coordinates.- Returns:
(n_rays,)hit distances,infwhere the ray misses the surface or lands outside the element’s aperture.
- hit_geometry(element_idx, origins, directions)[source]¶
World-frame hit point and normal, for a per-ray element index.
The counterpart to
intersect_t(): once the search knows which element each ray settled on, this evaluates the surface there, once.- Args:
element_idx:
(n_rays,)per-ray element index. origins, directions:(n_rays, 3)rays in world coordinates.- Returns:
Tuple of
(points_world, normals_world), each(n_rays, 3).
- perturb_normals(normals, roughness_salt, element_idx=None)[source]¶
Apply this group’s own BSDF surface-roughness perturbation.
roughness_saltis folded into this group’ssample_key, so independent call sites drawing separate perturbations for the same group should pass distinct salts.
- apply_interaction(directions, normals, points, element_idx, current_n)[source]¶
Apply this group’s physical interaction (reflect/refract/slab) at a hit.
See
Interaction.apply()for the return value.
- interact(directions, normals, points, element_idx, current_n, roughness_salt)[source]¶
Perturb normals for roughness, then apply the physical interaction.
See
Interaction.apply()for the return value.
Apertures¶
- class iactrace.core.DiskAperture[source]¶
Bases:
ApertureCircular or annular aperture defined by outer and inner radii.
Supports solid disks (inner_radii=0) and annular rings.
- Attributes:
radii: Outer radius per element (N,) inner_radii: Inner radius per element (N,), 0 for solid disk
- radii = <dataclasses._MISSING_TYPE object>¶
- inner_radii = <dataclasses._MISSING_TYPE object>¶
- check(x, y, element_idx)[source]¶
Check if point (x, y) is within the aperture of the given element.
- sample(key, n_samples)[source]¶
Sample uniform 2D points on each element’s annular aperture.
- Args:
key: JAX PRNG key n_samples: Number of samples per element
- Returns:
(N, n_samples, 2) array of 2D sample points
- get_area_data()[source]¶
Return per-element data for area computation, vmapped over elements.
- Returns:
Array of shape (N, 2) with [inner_radius, outer_radius] per element.
- area_fn(data)[source]¶
Compute aperture area from a single element’s area data.
- Args:
data: [inner_radius, outer_radius] (2,)
- Returns:
Annular area (scalar)
- __init__(radii, inner_radii)¶
- class iactrace.core.PolygonAperture[source]¶
Bases:
ApertureConvex polygon aperture defined by vertices.
All elements in a group must have the same number of vertices (required for JAX array batching).
- Attributes:
vertices: Polygon vertices per element (N, K, 2), CCW order n_vertices: Number of vertices per polygon (static, same for all)
- vertices = <dataclasses._MISSING_TYPE object>¶
- n_vertices = <dataclasses._MISSING_TYPE object>¶
- sample(key, n_samples)[source]¶
Sample uniform 2D points on each element’s polygon aperture.
- Args:
key: JAX PRNG key n_samples: Number of samples per element
- Returns:
(N, n_samples, 2) array of 2D sample points
- get_area_data()[source]¶
Return per-element data for area computation, vmapped over elements.
- Returns:
Vertices array (N, K, 2).
- area_fn(data)[source]¶
Compute polygon area from a single element’s vertices.
- Args:
data: Polygon vertices (K, 2)
- Returns:
Polygon area (scalar)
- __init__(vertices, n_vertices)¶
Interactions¶
- class iactrace.core.Interaction[source]¶
Bases:
ModuleAbstract base for optical interaction modules.
- abstract property interaction_type¶
- abstract property kind¶
User-facing element kind.
- focal_scale(n_outside=1.0)[source]¶
Curvature<->focal-length scale factor for this interaction.
curvature = 1 / (scale * focal_length):2for mirrors,n_inside - n_outsidefor a single refracting surface. ReturnsNonewhere a focal length is not a meaningful concept (slabs), letting the caller decide how to report that.
- abstractmethod apply(directions, normals, points, element_idx, current_n)[source]¶
Apply the interaction at hit points.
- Args:
- directions, normals, points, element_idx: per-ray geometry
at the surface hit.
- current_n: per-ray refractive index of the medium the ray
is currently propagating in. Used as the incident-side index for refraction physics, so OPL is exact even through stacked refractive surfaces.
Returns a 5-tuple
(new_directions, new_positions, coefficients, opl_internal, new_n).
- __init__()¶
- class iactrace.core.ReflectInteraction[source]¶
Bases:
InteractionReflection interaction for mirrors.
Per-ray coefficient:
reflectivity_scalar[idx] * reflectivity(cos_theta_i, idx)
When
reflectivity is None(the default) the angular factor is unity, i.e. an ideal angle-independent mirror with responsereflectivity_scalar. Provide aTabulatedCoating(or anyCoating) to model a measured R(theta) curve. Reflection does not change the medium:new_n == current_n.- Attributes:
- reflectivity: Angle-dependent coating, or
Nonefor a flat angular response.
- reflectivity_scalar: Per-element bulk multiplier in
[0, 1], shape
(N,). Operations such asset_reflectivity()write here, leaving the coating untouched.
- reflectivity: Angle-dependent coating, or
- reflectivity = <dataclasses._MISSING_TYPE object>¶
- reflectivity_scalar = <dataclasses._MISSING_TYPE object>¶
- property interaction_type¶
- property kind¶
User-facing element kind.
- focal_scale(n_outside=1.0)[source]¶
Curvature<->focal-length scale factor for this interaction.
curvature = 1 / (scale * focal_length):2for mirrors,n_inside - n_outsidefor a single refracting surface. ReturnsNonewhere a focal length is not a meaningful concept (slabs), letting the caller decide how to report that.
- with_reflectivity_scalar(reflectivity_scalar)[source]¶
Return a copy with the bulk reflectivity multiplier replaced.
- scaled_reflectivity(factor)[source]¶
Return a copy with the bulk reflectivity multiplier scaled by
factor.
- apply(directions, normals, points, element_idx, current_n)[source]¶
Apply the interaction at hit points.
- Args:
- directions, normals, points, element_idx: per-ray geometry
at the surface hit.
- current_n: per-ray refractive index of the medium the ray
is currently propagating in. Used as the incident-side index for refraction physics, so OPL is exact even through stacked refractive surfaces.
Returns a 5-tuple
(new_directions, new_positions, coefficients, opl_internal, new_n).
- __init__(reflectivity, reflectivity_scalar)¶
- class iactrace.core.RefractInteraction[source]¶
Bases:
InteractionSingle-surface refraction interaction for lenses.
Per-ray coefficient:
transmittance_scalar[idx] * angular_response(cos_theta_i)
When
transmittance is None(the default) the angular response isfresnel_unpolarized()evaluated fromcurrent_n(the medium the ray is currently in) andn_inside(the far side of this surface). Snell’s law is always applied to bend the ray.Semantically, this represents the ray crossing a single interface from one medium into another. A real glass body (e.g. a biconvex lens) is modelled as two consecutive
RefractInteractionstages, front then back surface, and the render loop’s per-ray medium tracker carries the correct index through the glass interior between them, so OPL is exact.- Attributes:
- n_inside: Refractive index on the far side of this surface,
per element (N,). “Far side” means the medium the ray transmits into: for a front surface this is the glass index, for a back surface it is the ambient index.
- transmittance: Angle-dependent coating, or
Nonefor bare-interface Fresnel transmittance.
- transmittance_scalar: Per-element bulk multiplier in
[0, 1], shape
(N,).
- n_inside = <dataclasses._MISSING_TYPE object>¶
- transmittance = <dataclasses._MISSING_TYPE object>¶
- transmittance_scalar = <dataclasses._MISSING_TYPE object>¶
- property interaction_type¶
- property kind¶
User-facing element kind.
- focal_scale(n_outside=1.0)[source]¶
Curvature<->focal-length scale factor for this interaction.
curvature = 1 / (scale * focal_length):2for mirrors,n_inside - n_outsidefor a single refracting surface. ReturnsNonewhere a focal length is not a meaningful concept (slabs), letting the caller decide how to report that.
- with_transmittance_scalar(transmittance_scalar)[source]¶
Return a copy with the bulk transmittance multiplier replaced (clipped to [0, 1]).
- scaled_transmittance(factor)[source]¶
Return a copy with the bulk transmittance multiplier scaled by
factor.
- apply(directions, normals, points, element_idx, current_n)[source]¶
Apply the interaction at hit points.
- Args:
- directions, normals, points, element_idx: per-ray geometry
at the surface hit.
- current_n: per-ray refractive index of the medium the ray
is currently propagating in. Used as the incident-side index for refraction physics, so OPL is exact even through stacked refractive surfaces.
Returns a 5-tuple
(new_directions, new_positions, coefficients, opl_internal, new_n).
- __init__(n_inside, transmittance, transmittance_scalar)¶
- class iactrace.core.SlabInteraction[source]¶
Bases:
InteractionParallel-sided slab (window) interaction.
Per-ray coefficient:
transmittance_scalar[idx] * angular_response
When
transmittance is None(the default) the angular response is the standard Fresnel product at the two faces: by parallel-slab symmetry and Stokes reciprocity, both faces share the same single-face Fresnel coefficient so the result simplifies toT_face^2. Provide aCoatingto override with a vendor- supplied T(theta) curve for the complete slab; the coating fully replaces the Fresnel product. The TIR mask from the underlying geometry gates out invalid rays either way.The ray enters from its current medium (
current_n), refracts into the slab material, traverses it, and refracts back out into the same medium; slabs assume the ambient is symmetric across them, which is the usual case for a window.opl_internalis the per-rayn_in * Linside the slab.- Attributes:
n_inside: Per-element slab refractive index, shape
(N,). thickness: Per-element slab thickness, shape(N,). transmittance: Angle-dependent coating, orNonefor thebare-window Fresnel product.
- transmittance_scalar: Per-element bulk multiplier in
[0, 1], shape
(N,).
- transmittance_scalar: Per-element bulk multiplier in
- n_inside = <dataclasses._MISSING_TYPE object>¶
- thickness = <dataclasses._MISSING_TYPE object>¶
- transmittance = <dataclasses._MISSING_TYPE object>¶
- transmittance_scalar = <dataclasses._MISSING_TYPE object>¶
- property interaction_type¶
- property kind¶
User-facing element kind.
- with_transmittance_scalar(transmittance_scalar)[source]¶
Return a copy with the bulk transmittance multiplier replaced (clipped to [0, 1]).
- scaled_transmittance(factor)[source]¶
Return a copy with the bulk transmittance multiplier scaled by
factor.
- apply(directions, normals, points, element_idx, current_n)[source]¶
Apply the interaction at hit points.
- Args:
- directions, normals, points, element_idx: per-ray geometry
at the surface hit.
- current_n: per-ray refractive index of the medium the ray
is currently propagating in. Used as the incident-side index for refraction physics, so OPL is exact even through stacked refractive surfaces.
Returns a 5-tuple
(new_directions, new_positions, coefficients, opl_internal, new_n).
- __init__(n_inside, thickness, transmittance, transmittance_scalar)¶
Coatings¶
Angle-dependent reflectivity / transmittance applied at an interaction.
- class iactrace.core.Coating[source]¶
Bases:
ModuleAbstract base for angle-dependent optical coatings.
A coating maps the incidence-angle cosine of each ray to a coefficient in
[0, 1](reflectance or transmittance, depending on the surface type). All subclasses must return an array broadcastable tocos_theta_i.shape.- __init__()¶
- class iactrace.core.ConstantCoating[source]¶
Bases:
CoatingAngle-independent per-element coating.
- Attributes:
values: Per-element coefficient in
[0, 1], shape(N,).
- values = <dataclasses._MISSING_TYPE object>¶
- __init__(values)¶
- class iactrace.core.TabulatedCoating[source]¶
Bases:
CoatingLinear interpolation over a shared angle grid.
- Attributes:
- cos_table:
cos(angle)lookup axis, sorted ascending, shape (K,).cos_theta_i = 1-> normal incidence,cos_theta_i = 0-> grazing.- values: Per-element coefficient values aligned with
cos_table, shape(N, K).
- cos_table:
- cos_table = <dataclasses._MISSING_TYPE object>¶
- values = <dataclasses._MISSING_TYPE object>¶
- classmethod from_degrees(angles_deg, values, n_elements)[source]¶
Build a
TabulatedCoatingfrom human-readable angles.- Args:
- angles_deg: Sample angles in degrees, shape
(K,). Don’t need to be sorted since they are reordered into cos-ascending form internally.
- values: Coefficient values.
(K,)is broadcast to all n_elementselements;(N, K)is used as-is and must matchn_elementsalong the first axis.
n_elements: Number of elements
Nin the enclosing group.- angles_deg: Sample angles in degrees, shape
- Returns:
A ready-to-use coating with the cos-ascending lookup table precomputed.
- __init__(cos_table, values)¶
BSDF (surface scattering)¶
- class iactrace.core.BSDF[source]¶
Bases:
ModuleAbstract base for surface scattering models.
Subclasses implement
_sample_perturbation()which returns(angles, scale)for a given shape and element-index resolver. The base class handles tangent-frame construction and applies the perturbation.- perturb_normals(normals, key, element_idx=None)[source]¶
Perturb surface normals.
Works for any leading shape:
(n_rays, 3)with per-ray element_idx, or(N, S, 3)with element_idx=None when the element dimension is already present.- Args:
normals: (…, 3). key: JAX PRNG key. element_idx: Per-ray element index, or None for batch mode.
- Returns:
Perturbed normals (…, 3).
- __init__()¶
- class iactrace.core.GaussianBSDF[source]¶
Bases:
BSDFSingle-Gaussian surface roughness model.
Perturbs surface normals by Gaussian-distributed random angles. This is the standard model for surface microroughness.
- Attributes:
- scale: Per-element roughness sigma in arcseconds (N,).
Zero means perfect specular (no perturbation).
- scale = <dataclasses._MISSING_TYPE object>¶
- __init__(scale)¶
- class iactrace.core.DoubleGaussianBSDF[source]¶
Bases:
BSDFMixture of two Gaussians for surfaces with multi-scale roughness.
Models surfaces that have both fine-scale microroughness (narrow component) and broader scattering from mid-spatial-frequency errors (wide component). Each ray’s perturbation is drawn from the narrow Gaussian with probability
(1 - mix_weight)or from the wide Gaussian with probabilitymix_weight.- Attributes:
scale_narrow: Per-element narrow-component sigma in arcseconds (N,). scale_wide: Per-element wide-component sigma in arcseconds (N,). mix_weight: Per-element probability of the wide component (N,),
values in [0, 1].
- scale_narrow = <dataclasses._MISSING_TYPE object>¶
- scale_wide = <dataclasses._MISSING_TYPE object>¶
- mix_weight = <dataclasses._MISSING_TYPE object>¶
- __init__(scale_narrow, scale_wide, mix_weight)¶
Optical Physics¶
Functions for ray-surface interactions:
- iactrace.core.interactions.reflect(direction, normal)[source]¶
Reflect a ray’s direction off a surface.
- Args:
direction: Ray direction (3,), pointing into the surface. normal: Surface normal (3,), pointing outward.
- Returns:
reflected: Reflected direction (3,). cos_i: Cosine of the incidence angle (non-negative).
- iactrace.core.interactions.refract(direction, normal, n1, n2)[source]¶
Refract a ray’s direction through an interface (Snell’s law).
Handles rays from either side of the surface by flipping the normal if needed. On total internal reflection, the reflected direction is returned in place of the refracted one and
tirisTrue.- Args:
direction: Ray direction (3,), normalized. normal: Surface normal (3,), normalized, pointing outward. n1: Refractive index of the incident medium. n2: Refractive index of the transmitted medium.
- Returns:
refracted: Refracted direction (3,), or reflected if TIR. cos_i: Cosine of the incidence angle (non-negative, with the
ambient-vs-internal side correctly resolved).
tir: True if total internal reflection occurred.
- iactrace.core.interactions.refract_slab(direction, normal, position, n_out, n_in, thickness)[source]¶
Refract a ray through a parallel-sided slab (window).
- Args:
direction: Ray direction (3,), normalized. normal: Front-surface normal (3,), pointing outward. position: Entry point in world coordinates (3,). n_out: Refractive index of the ambient medium. n_in: Refractive index of the slab material. thickness: Slab thickness in the same units as
position.- Returns:
exit_direction: Ray direction after leaving the slab. exit_position: World-space point where the ray exits. cos_i: Cosine of the incidence angle on the slab from outside. valid:
Trueiff no total internal reflection occurred ateither face.
- path_length: Geometric distance the ray travels inside the
slab, in the same units as
thickness. Multiplied byn_inby the caller to obtain the OPL contributionn_in * L.
- iactrace.core.coatings.fresnel_unpolarized(cos_theta_i, n1, n2)[source]¶
Unpolarized Fresnel reflection and transmission coefficients.
The standard formula for an ideal bare dielectric interface, used as the implicit default by
RefractInteractionandSlabInteractionwhen no explicitCoatingis supplied. Average of s- and p-polarized intensities.The transmitted angle is derived internally from Snell’s law, so only the incidence cosine and the two indices are needed. Total internal reflection (
sin^2(theta_t) > 1) collapsescos_theta_tto zero, which the formula correctly turns intoR = 1, T = 0.- Args:
cos_theta_i: Cosine of the incidence angle. n1: Refractive index of the incident medium. n2: Refractive index of the transmitted medium.
- Returns:
R: Reflectance in
[0, 1]. T: TransmittanceT = 1 - R.
Surfaces¶
Surface-figure models. SurfaceGroup is the base; the concrete groups
below can be combined with SumSurfaceGroup (e.g. an
aspheric base plus a per-facet Zernike figure error).
- class iactrace.core.SurfaceGroup[source]¶
Bases:
ModuleAbstract base for batched surface parameters of N optical elements.
A SurfaceGroup stores per-element surface geometry and provides: - Sag/normal computation for the transform pipeline (vmapped per element) - Per-element sag and ray intersection for rendering and visualization
Subclasses must store an
offsetsarray of shape (N, 2) and implement the abstract methods below. SeeAsphericSurfaceGroupfor a concrete example.- offsets = <dataclasses._MISSING_TYPE object>¶
- compute_sag_and_normal_at(x, y)[source]¶
Compute surface point and normal at (x, y) for a single element.
- Args:
x: x-coordinate (scalar). y: y-coordinate (scalar).
- Returns:
Tuple of (point, normal) where point is (3,) and normal is (3,), normalized.
- sag_at(element_idx, x, y)[source]¶
Compute surface sag z(x, y) for a single element.
Used by the visualization module for mesh generation.
- Args:
element_idx: Element index within the group. x: x-coordinate in local frame (scalar). y: y-coordinate in local frame (scalar).
- Returns:
z: Surface sag at (x, y) relative to the element’s decenter.
- intersect_t_at(element_idx, ray_origin, ray_direction, max_iter=10, tol=None)[source]¶
Ray parameter and local landing point, without evaluating the surface.
- Args:
element_idx: Element index within the group. ray_origin: Ray origin in local coordinates (3,). ray_direction: Ray direction (3,). max_iter: Maximum Newton-Raphson iterations. tol: Absolute residual tolerance, or
Noneto derive it per ray.- Returns:
Tuple of
(t, x, y): the intersection distance (infon a miss) and the local in-surface coordinates of the landing point. On a miss the coordinates are those of the ray’s closest approach, so they stay finite for downstream masking.
- intersect_at(element_idx, ray_origin, ray_direction, max_iter=10, tol=None)[source]¶
Intersect a ray with a single element’s surface.
Used by the render pipeline for per-ray intersection. Generic over the surface type:
_intersect_t()resolves the ray parameter (the closed-form root for pure conics, Newton-refined from_t_guess()otherwise);point/normalfollow from the sag at the hit. On a miss (t = inf) they are evaluated at the ray origin, so they stay finite for downstream masking.- Args:
element_idx: Element index within the group. ray_origin: Ray origin in local coordinates (3,). ray_direction: Ray direction (3,). max_iter: Maximum Newton-Raphson iterations. tol: Absolute residual tolerance, or
None(the default) to deriveit per ray from the coordinate magnitudes. See
newton_raphson_intersect().- Returns:
- Tuple of (t, point, normal):
t: Intersection distance (scalar), inf on a miss.
point: Intersection point (3,).
normal: Surface normal at intersection (3,).
- __init__(offsets)¶
- class iactrace.core.SumSurfaceGroup[source]¶
Bases:
SurfaceGroupComposite surface whose sag is the sum of its components’ sags.
The composite’s own
offsetsdecenter the whole patch; component offsets (usually zero) are applied first, inside each component’s_sag_local.- Attributes:
- components: Tuple of component
SurfaceGroupinstances, each sized to the same
N.- offsets: Per-element in-surface decenter for the composite (N, 2)
(inherited).
- components: Tuple of component
- components = <dataclasses._MISSING_TYPE object>¶
- class iactrace.core.AsphericSurfaceGroup[source]¶
Bases:
SurfaceGroupBatched aspheric surface parameters for N optical elements.
A conic + even-polynomial surface used by
OpticalElementGroup. When sliced/vmapped to a single element, each becomes a scalar-parameter surface; the genericSurfaceGroupmachinery then handles sag, normal, and intersection. The conic provides a closed-form intersection initial guess via_t_guess().- Attributes:
curvatures: Per-element curvatures (N,) conics: Per-element conic constants (N,) aspherics: Per-element even aspheric coefficients
[A4, A6, ...](N, K); column
imultipliesr^(2i + 4). Seesag_raw().offsets: Per-element in-surface decenter (N, 2) (inherited)
- curvatures = <dataclasses._MISSING_TYPE object>¶
- conics = <dataclasses._MISSING_TYPE object>¶
- aspherics = <dataclasses._MISSING_TYPE object>¶
- __init__(offsets, curvatures, conics, aspherics)¶
- class iactrace.core.ZernikeSurfaceGroup[source]¶
Bases:
SurfaceGroupStandalone Zernike figure surface for N optical elements.
Represents a surface whose height is a sum of RMS-normalized Noll Zernike polynomials, independent of any conic/aspheric base. Use it on its own to describe a pure figure-error surface, or as a term inside a
SumSurfaceGroupto add a measured / random figure error on top of another surface.The normal is obtained by autodiff of the sag (inherited from
SurfaceGroup), and the intersection uses the inherited tangent-plane initial guess, which is appropriate for the shallow surfaces figure errors produce.- Attributes:
- coeffs: Per-element Noll coefficients in metres, shape
(N, J)with J <= 11. Columnmis Noll indexm + 1(Z1 = piston).- r_norm: Per-element normalization radius in metres, shape
(N,). rho = 1at this radius.
offsets: Per-element in-surface decenter (N, 2) (inherited).
- coeffs: Per-element Noll coefficients in metres, shape
- coeffs = <dataclasses._MISSING_TYPE object>¶
- r_norm = <dataclasses._MISSING_TYPE object>¶
- class iactrace.core.FreeformSurfaceGroup[source]¶
Bases:
SurfaceGroupPer-element freeform surface defined by a bicubically interpolated grid.
Each element carries a regular
(H, W)height map sampled over a rectangular domain; the sag at arbitrary(x, y)is the Catmull-Rom bicubic interpolation of that map.For a strongly curved freeform, compose it on top of an
AsphericSurfaceGroupin aSumSurfaceGroup(base term first) so the conic supplies the intersection initial guess.- Attributes:
- grid_z: Per-element height samples
(N, H, W)in metres. ``grid_z[n, j, i]`` is the height of element
nat columni(x), rowj(y).- x0, y0: Per-element grid origin
(N,)— the coordinate of column / row 0.
dx, dy: Per-element grid spacing
(N,)along x / y. offsets: Per-element in-surface decenter (N, 2) (inherited).- grid_z: Per-element height samples
- grid_z = <dataclasses._MISSING_TYPE object>¶
- x0 = <dataclasses._MISSING_TYPE object>¶
- y0 = <dataclasses._MISSING_TYPE object>¶
- dx = <dataclasses._MISSING_TYPE object>¶
- dy = <dataclasses._MISSING_TYPE object>¶
- classmethod from_extent(grid_z, half_width, half_height, offsets=None)[source]¶
Build from a grid centred on the origin spanning a given extent.
The grid columns span
[-half_width, half_width]and rows span[-half_height, half_height].half_width/half_heightmay be scalar (shared) or per-element(N,).
- iactrace.core.surfaces.sag(x, y, offset, curvature, conic, aspheric)[source]¶
Compute surface sag z(x,y) in local mirror coordinates.
- Args:
x: x-coordinate in local mirror frame (scalar) y: y-coordinate in local mirror frame (scalar) offset: (x0, y0) offset on parent surface (2,) curvature: Surface curvature (1/radius) conic: Conic constant k aspheric: Even aspheric coefficients
[A4, A6, ...](K,); entryimultipliesr^(2i + 4). Seesag_raw().- Returns:
z: Surface sag at (x, y) relative to offset point
- iactrace.core.surfaces.compute_sag_and_normal(x, y, offset, curvature, conic, aspheric)[source]¶
Compute surface point and normal at (x, y) with given parameters.
- Args:
x: x-coordinate in local mirror frame (scalar) y: y-coordinate in local mirror frame (scalar) offset: (x0, y0) offset on parent surface (2,) curvature: Surface curvature (1/radius) conic: Conic constant k aspheric: Even aspheric coefficients
[A4, A6, ...](K,); entryimultipliesr^(2i + 4). Seesag_raw().- Returns:
point: 3D surface point (3,) normal: Surface normal (3,), normalized
- iactrace.core.zernike_terms(u, v)[source]¶
RMS-normalized Noll Zernike polynomials Z1..Z11.
Evaluated in normalized Cartesian coordinates
u = x / r_normandv = y / r_norm(so the unit disk isu^2 + v^2 <= 1). The terms are written as Cartesian polynomials rather than via(rho, phi)so the gradient is smooth everywhere, including the origin.With the Noll RMS normalization each term has unit RMS over the unit disk, so a coefficient in metres equals that aberration’s RMS surface contribution in metres. If your surface is not circular, you have to rescale.
- Args:
u: Normalized x-coordinate (scalar or array). v: Normalized y-coordinate (scalar or array).
- Returns:
Array with the 11 lowest Noll terms stacked on the last axis, in Noll order: piston, tilt x/y, defocus, astigmatism (oblique/vertical), coma (vertical/horizontal), trefoil (vertical/oblique), primary spherical.
- iactrace.core.bicubic_interp(grid, u, v)[source]¶
Bicubic (Catmull-Rom) interpolation of a height grid.
- Args:
- grid: Height samples
(H, W);grid[j, i]is the height at grid column
i(x) and rowj(y).
u: Fractional column coordinate (x in grid units), scalar. v: Fractional row coordinate (y in grid units), scalar.
- grid: Height samples
- Returns:
Interpolated height (scalar). Exact at grid nodes. Queries outside the grid are clamped to the edge (flat extrapolation), keeping the Newton intersection well-behaved when it strays off the patch.
Intersection Functions¶
Geometric ray-primitive intersection tests (in
iactrace.core.intersections):
- iactrace.core.intersections.intersect_plane(ray_origin, ray_direction, plane_center, plane_rotation)[source]¶
Intersect ray with a plane defined by center and rotation matrix.
- Args:
ray_origin: Ray origin (3,) ray_direction: Ray direction (3,), assumed normalized plane_center: Plane center (3,) plane_rotation: Rotation matrix (3, 3) - Z-axis is normal
- Returns:
Tuple of (2D coordinates on plane (2,), t parameter (scalar))
- iactrace.core.intersections.intersect_sphere(ray_origin, ray_direction, center, radius)[source]¶
Intersect ray with sphere.
- Args:
ray_origin: Ray origin (3,) ray_direction: Ray direction (3,), assumed normalized center: Sphere center (3,) radius: Sphere radius (scalar)
- Returns:
t parameter of nearest intersection, jnp.inf if no hit
- iactrace.core.intersections.intersect_cylinder(ray_origin, ray_direction, p1, p2, radius)[source]¶
Intersect ray with a finite cylinder, end caps included.
- Args:
ray_origin: Ray origin (3,) ray_direction: Ray direction (3,), assumed normalized p1: First endpoint of cylinder axis (3,) p2: Second endpoint of cylinder axis (3,) radius: Cylinder radius (scalar)
- Returns:
t parameter of nearest intersection, jnp.inf if no hit
- iactrace.core.intersections.intersect_open_cylinder(ray_origin, ray_direction, p1, p2, radius)[source]¶
Intersect ray with finite cylinder without end caps (curved surface only).
- Args:
ray_origin: Ray origin (3,) ray_direction: Ray direction (3,), assumed normalized p1: First endpoint of cylinder axis (3,) p2: Second endpoint of cylinder axis (3,) radius: Cylinder radius (scalar)
- Returns:
t parameter of nearest intersection, jnp.inf if no hit
- iactrace.core.intersections.intersect_box(ray_origin, ray_direction, p1, p2)[source]¶
Intersect ray with AABB box.
- Args:
ray_origin: Ray origin (3,) ray_direction: Ray direction (3,), assumed normalized p1: lower edge of the bounding box (3,) p2: upper diagonal edge of the bounding box (3,)
- Returns:
t parameter of nearest intersection, jnp.inf if no hit
- iactrace.core.intersections.intersect_oriented_box(ray_origin, ray_direction, center, half_extents, rotation)[source]¶
Intersect ray with oriented bounding box.
- Args:
ray_origin: Ray origin (3,) ray_direction: Ray direction (3,), assumed normalized center: Box center (3,) half_extents: Half-sizes along local axes (3,) rotation: Rotation matrix (3, 3) transforming local to world coords
- Returns:
t parameter of nearest intersection, jnp.inf if no hit
- iactrace.core.intersections.intersect_triangle(ray_origin, ray_direction, v0, v1, v2)[source]¶
Intersect ray with triangle using Moeller-Trumbore algorithm.
- Args:
ray_origin: Ray origin (3,) ray_direction: Ray direction (3,), assumed normalized v0, v1, v2: Triangle vertices (3,) each
- Returns:
t parameter of intersection, jnp.inf if no hit
- iactrace.core.intersections.intersect_conic(ray_origin, ray_direction, curvature, conic)[source]¶
Compute closed-form ray-conic intersection parameter.
- Args:
ray_origin: Ray origin (3,) ray_direction: Ray direction (3,), assumed normalized curvature: Surface curvature (1/radius) conic: Conic constant (0=sphere, -1=paraboloid, <-1=hyperboloid, >-1=ellipsoid)
- Returns:
t: Ray parameter at the nearest forward intersection on the sag branch, inf if there is none.
Obstruction Groups¶
Classes for modeling ray obstructions:
- class iactrace.core.ObstructionGroup[source]¶
Bases:
ModuleBase class for grouped obstructions.
Subclasses supply their intersection kernel and stacked parameters via
_primitive(); the two traversal strategies are shared from here.- intersect_batch(origins, directions)[source]¶
Nearest hit distance per ray, for
(n_rays, 3)rays.Same answer as
vmap(self.intersect), but chooses how to walk the primitives based on how many rays there are.
- __init__()¶
- class iactrace.core.CylinderGroup[source]¶
Bases:
ObstructionGroupGroup of cylinders for efficient batched intersection.
- p1 = <dataclasses._MISSING_TYPE object>¶
- p2 = <dataclasses._MISSING_TYPE object>¶
- r = <dataclasses._MISSING_TYPE object>¶
- class iactrace.core.OpenCylinderGroup[source]¶
Bases:
ObstructionGroupGroup of open cylinders (no end caps) for efficient batched intersection.
An open cylinder is a finite cylindrical surface without circular caps at the ends. Useful for modeling tubes, pipes, or hollow cylindrical structures where rays can pass through the ends.
- p1 = <dataclasses._MISSING_TYPE object>¶
- p2 = <dataclasses._MISSING_TYPE object>¶
- r = <dataclasses._MISSING_TYPE object>¶
- class iactrace.core.BoxGroup[source]¶
Bases:
ObstructionGroupGroup of axis-aligned boxes for efficient batched intersection.
- p1 = <dataclasses._MISSING_TYPE object>¶
- p2 = <dataclasses._MISSING_TYPE object>¶
- class iactrace.core.SphereGroup[source]¶
Bases:
ObstructionGroupGroup of spheres for efficient batched intersection.
- centers = <dataclasses._MISSING_TYPE object>¶
- radii = <dataclasses._MISSING_TYPE object>¶
Transforms¶
Coordinate transformation utilities: