iactrace.core

Low-level ray tracing components. Most users should use the Telescope class instead of these functions directly.

Rendering Functions

High-level functions for rendering images through a telescope:

iactrace.core.render(tel, sources, values, source_type, sensor_idx=0)[source]

Render sources through telescope onto sensor group.

Supports mixed reflective/refractive optical systems. Stage 0 can be mirrors, lenses, or slabs. Stages 1+ can be any interaction type.

Args:

tel: Telescope object sources: Source positions (n_sources, 3) for ‘point’ or directions for ‘parallel’ values: Source intensities (n_sources,) source_type: ‘point’ or ‘parallel’ sensor_idx: Index of sensor group to use

Returns:

Accumulated image with shape (n_sensors, *per_sensor_shape). For square sensors: (n_sensors, height, width) For hexagonal sensors: (n_sensors, n_pixels)

iactrace.core.render_debug(tel, sources, values, source_type, sensor_idx=0)[source]

Render without accumulation - returns raw hits.

Supports mixed reflective/refractive optical systems.

Args:

tel: Telescope object sources: Source positions (n_sources, 3) for ‘point’ or directions for ‘parallel’ values: Source intensities (n_sources,) source_type: ‘point’ or ‘parallel’ sensor_idx: Index of sensor group to use

Returns:
Tuple of (points, sensor_idx, values) arrays with all ray intersections:
  • points: (n_rays, 2) sensor coordinates for each ray

  • sensor_idx: (n_rays,) index of sensor each ray hit

  • values: (n_rays,) final intensity values (0 if ray missed)

iactrace.core.render_response_matrix(tel, sources, values, source_type, sensor_idx=0)[source]

Render multiple sources and return the source-to-pixel response matrix.

This function traces N sources through the telescope and returns an N×M matrix where each row contains one source’s contribution to all M pixels across all sensors. Uses incremental accumulation for memory efficiency.

Supports mixed reflective/refractive optical systems.

Args:

tel: Telescope object sources: Source positions (n_sources, 3) for ‘point’ or directions for ‘parallel’ values: Source intensities (n_sources,) source_type: ‘point’ or ‘parallel’ sensor_idx: Index of sensor group to use

Returns:

Array of shape (n_sources, n_sensors * n_pixels) where n_pixels is the flattened per-sensor size.

iactrace.core.trace_rays(tel, ray_origins, ray_directions, values, sensor_idx=0)[source]

Render classical rays through telescope onto sensor group.

Unlike render() which samples rays from primary mirror surfaces, this function traces rays from arbitrary external origins through the full optical system, including intersection with primary mirrors (stage 0).

Supports mixed reflective/refractive systems - rays are automatically reflected or refracted based on each optical element’s interaction type.

Args:

tel: Telescope object ray_origins: Ray starting positions (n_rays, 3) ray_directions: Ray directions (n_rays, 3), should be normalized values: Ray intensities (n_rays,) sensor_idx: Index of sensor group to use

Returns:

Accumulated image with shape (n_sensors, *per_sensor_shape).

iactrace.core.trace_rays_debug(tel, ray_origins, ray_directions, values, sensor_idx=0)[source]

Render classical rays without accumulation - returns raw hits.

Debug version of trace_rays that returns individual ray hit positions and values instead of accumulated image.

Supports mixed reflective/refractive systems.

Args:

tel: Telescope object ray_origins: Ray starting positions (n_rays, 3) ray_directions: Ray directions (n_rays, 3), should be normalized values: Ray intensities (n_rays,) sensor_idx: Index of sensor group to use

Returns:
Tuple of (points, sensor_idx, values):
  • points: (n_rays, 2) sensor coordinates for each ray

  • sensor_idx: (n_rays,) index of sensor each ray hit

  • values: (n_rays,) final intensity values (0 if ray missed)

Integrators

Classes for sampling rays on optical surfaces:

class iactrace.core.Integrator[source]

Bases: ABC

Abstract base class for optical element sampling integrators.

abstractmethod sample_group(group, key)[source]

Sample a single optical group and return updated object.

Args:

group: OpticalGroupBase object (mirror or lens) key: JAX random key

Returns:

OpticalGroup with sampled aperture positions and perturbation angles

sample_optical_groups(optical_groups, key)[source]

Sample all optical groups (mirrors and lenses) and return updated list.

This is the unified entry point for sampling any optical element.

Args:

optical_groups: List of OpticalGroupBase objects key: JAX random key

Returns:

List of OpticalGroupBase objects with sampled aperture positions

class iactrace.core.MCIntegrator[source]

Bases: Integrator

Monte Carlo integrator for optical element groups (mirrors and lenses).

__init__(n_samples=128)[source]
n_samples
sample_group(group, key)[source]

Sample a single optical group using Monte Carlo sampling.

Works with both mirror groups (AsphericDiskMirrorGroup, AsphericPolygonMirrorGroup) and lens groups (AsphericDiskLensGroup, PlanoSlabGroup).

The sampling process: 1. Generate uniform random 2D points on each aperture 2. Generate random perturbation angles for surface roughness 3. Geometry (3D points, normals, weights) is computed at render time

Args:

group: OpticalGroupBase object (mirror or lens) key: JAX random key

Returns:

OpticalGroup with sampled aperture positions and perturbation angles

Optical Physics

Functions for ray-surface interactions:

iactrace.core.reflect(direction, normal)[source]

Reflect ray direction off surface with given normal.

Implements the law of reflection: the angle of incidence equals the angle of reflection, with the reflected ray in the plane of incidence.

Args:

direction: Ray direction (3,), pointing towards surface normal: Surface normal (3,), pointing outward from surface

Returns:

reflected: Reflected direction (3,) cos_angle: Cosine of incident angle (positive value)

iactrace.core.refract(direction, normal, n1, n2)[source]

Refract ray direction through interface using Snell’s law.

Computes the refracted ray direction when light passes from a medium with refractive index n1 into a medium with refractive index n2.

The function handles rays coming from either side of the surface by checking the sign of the dot product and flipping the normal if needed.

Args:

direction: Ray direction (3,), normalized, pointing towards surface normal: Surface normal (3,), normalized, pointing outward n1: Refractive index of incident medium (scalar) n2: Refractive index of transmitted medium (scalar)

Returns:

refracted: Refracted direction (3,), or original direction if TIR cos_theta_t: Cosine of refracted angle tir: Boolean, True if total internal reflection occurred

iactrace.core.refract_slab(direction, normal, position, n_out, n_in, thickness)[source]

Refract ray through a parallel-sided slab (window).

Handles both entry and exit refractions for a slab with parallel surfaces. The exit surface normal is assumed to be opposite to the entry normal. Applies Fresnel transmission coefficients at both surfaces.

For flat slabs, the exiting ray direction equals the entering direction (but with a lateral offset). For curved slabs with parallel surfaces, there may be slight direction change due to the curved geometry.

Args:

direction: Ray direction (3,), normalized normal: Front surface normal (3,), pointing outward (into incident medium) position: Entry point position (3,) n_out: Refractive index of ambient medium (scalar) n_in: Refractive index of slab material (scalar) thickness: Slab thickness (scalar)

Returns:

exit_direction: Direction after exiting slab (3,) exit_position: Position where ray exits slab (3,) transmittance: Combined Fresnel transmission coefficient for both surfaces valid: Boolean, True if ray successfully transmitted (no TIR)

iactrace.core.fresnel_unpolarized(cos_theta_i, cos_theta_t, n1, n2)[source]

Compute Fresnel reflection and transmission coefficients for unpolarized light.

Uses the Fresnel equations to compute the fraction of light reflected (R) and transmitted (T) at an interface, averaged over s and p polarizations.

Args:

cos_theta_i: Cosine of incident angle (scalar or array) cos_theta_t: Cosine of transmitted angle (scalar or array) n1: Refractive index of incident medium n2: Refractive index of transmitted medium

Returns:

R: Reflectance (fraction of light reflected), 0 <= R <= 1 T: Transmittance (fraction of light transmitted), T = 1 - R

Surfaces

Aspheric surface calculations:

class iactrace.core.AsphericSurface[source]

Bases: Module

Aspheric surface defined by curvature, conic constant, and polynomial terms.

This class stores surface parameters and provides intersection functionality. For computing sag/normals in differentiable pipelines, use the standalone functions which enable full gradient flow.

curvature = <dataclasses._MISSING_TYPE object>
conic = <dataclasses._MISSING_TYPE object>
aspheric = <dataclasses._MISSING_TYPE object>
sag(x, y, offset)[source]

Compute surface sag z(x,y) in local mirror coordinates.

Convenience method that calls the standalone sag function.

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,)

Returns:

z: Surface sag at (x, y) relative to offset point

intersect(ray_origin, ray_direction, offset, max_iter=10, tol=1e-08)[source]

Find ray-surface intersection using Newton-Raphson iteration.

Uses closed-form conic intersection as initial guess, then refines with Newton-Raphson to account for aspheric terms.

Args:

ray_origin: Ray origin in local coordinates (3,) ray_direction: Ray direction (3,) offset: Surface offset (x0, y0) (2,) max_iter: Maximum Newton-Raphson iterations tol: Convergence tolerance

Returns:

t: Intersection distance point: Intersection point (3,) normal: Surface normal at intersection (3,)

__init__(curvature, conic, aspheric)
iactrace.core.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: Array of aspheric coefficients (K,)

Returns:

z: Surface sag at (x, y) relative to offset point

iactrace.core.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: Array of aspheric coefficients (K,)

Returns:

point: 3D surface point (3,) normal: Surface normal (3,), normalized

Intersection Functions

Geometric ray-primitive intersection tests:

iactrace.core.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,) 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.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.intersect_cylinder(ray_origin, ray_direction, p1, p2, radius)[source]

Single cylinder intersection (for vmapping).

iactrace.core.intersect_box(ray_origin, ray_direction, p1, p2)[source]

Single box intersection (for vmapping).

iactrace.core.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,) 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.intersect_triangle(ray_origin, ray_direction, v0, v1, v2)[source]

Intersect ray with triangle using Möller-Trumbore algorithm.

Args:

ray_origin: Ray origin (3,) ray_direction: Ray direction (3,) v0, v1, v2: Triangle vertices (3,) each

Returns:

t parameter of intersection, jnp.inf if no hit

iactrace.core.intersect_conic(ray_origin, ray_direction, curvature, conic)[source]

Compute closed-form ray-conic intersection parameter.

The conic surface is defined by the implicit equation:

c*(x² + y²) + (1+k)*c*z² - 2*z = 0

where c is curvature and k is the conic constant.

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 intersection (smallest positive root), inf if no intersection

Obstruction Groups

Classes for modeling ray obstructions:

class iactrace.core.ObstructionGroup[source]

Bases: Module

Base class for grouped obstructions.

config_type = ''
abstractmethod intersect(ray_origin, ray_direction)[source]

Returns min t across all primitives in group.

abstractmethod to_config(index)[source]

Convert a single obstruction at index to a config dict.

abstractmethod classmethod from_config(configs)[source]

Create an ObstructionGroup from a list of config dicts.

__init__()
class iactrace.core.CylinderGroup[source]

Bases: ObstructionGroup

CylinderGroup(p1, p2, r)

config_type = 'cylinder'

Group of cylinders for efficient batched intersection.

__init__(p1, p2, r)[source]
p1 = <dataclasses._MISSING_TYPE object>
p2 = <dataclasses._MISSING_TYPE object>
r = <dataclasses._MISSING_TYPE object>
intersect(ray_origin, ray_direction)[source]

Returns min t across all cylinders.

to_config(index)[source]

Convert cylinder at index to config dict.

classmethod from_config(configs)[source]

Create CylinderGroup from config dicts.

class iactrace.core.BoxGroup[source]

Bases: ObstructionGroup

BoxGroup(p1, p2)

config_type = 'box'

Group of axis-aligned boxes for efficient batched intersection.

__init__(p1, p2)[source]
p1 = <dataclasses._MISSING_TYPE object>
p2 = <dataclasses._MISSING_TYPE object>
intersect(ray_origin, ray_direction)[source]

Returns min t across all boxes.

to_config(index)[source]

Convert box at index to config dict.

classmethod from_config(configs)[source]

Create BoxGroup from config dicts.

class iactrace.core.SphereGroup[source]

Bases: ObstructionGroup

SphereGroup(centers, radii)

config_type = 'sphere'

Group of spheres for efficient batched intersection.

__init__(centers, radii)[source]
centers = <dataclasses._MISSING_TYPE object>
radii = <dataclasses._MISSING_TYPE object>
intersect(ray_origin, ray_direction)[source]

Returns min t across all spheres.

to_config(index)[source]

Convert sphere at index to config dict.

classmethod from_config(configs)[source]

Create SphereGroup from config dicts.

class iactrace.core.OrientedBoxGroup[source]

Bases: ObstructionGroup

OrientedBoxGroup(centers, half_extents, rotations)

config_type = 'oriented_box'

Group of oriented boxes for efficient batched intersection.

__init__(centers, half_extents, rotations)[source]
centers = <dataclasses._MISSING_TYPE object>
half_extents = <dataclasses._MISSING_TYPE object>
rotations = <dataclasses._MISSING_TYPE object>
intersect(ray_origin, ray_direction)[source]

Returns min t across all oriented boxes.

to_config(index)[source]

Convert oriented box at index to config dict.

classmethod from_config(configs)[source]

Create OrientedBoxGroup from config dicts.

class iactrace.core.TriangleGroup[source]

Bases: ObstructionGroup

TriangleGroup(v0, v1, v2)

config_type = 'triangle'

Group of triangles for efficient batched intersection.

__init__(v0, v1, v2)[source]
v0 = <dataclasses._MISSING_TYPE object>
v1 = <dataclasses._MISSING_TYPE object>
v2 = <dataclasses._MISSING_TYPE object>
intersect(ray_origin, ray_direction)[source]

Returns min t across all triangles.

to_config(index)[source]

Convert triangle at index to config dict.

classmethod from_config(configs)[source]

Create TriangleGroup from config dicts.

Transforms

Coordinate transformation utilities:

iactrace.core.euler_to_matrix(tip_tilt_rotation)[source]

Convert Euler angles (degrees) to rotation matrix.

Args:

tip_tilt_rotation: List of all 3 transformations(3,)

Returns:

Rotation matrix (3, 3)

iactrace.core.look_at_rotation(mirror_pos, target_pos, up)[source]

Compute rotation matrix to look from mirror_pos towards target_pos.

Args:

mirror_pos: Position to look from (3,) target_pos: Position to look at (3,) up: Up vector (3,)

Returns:

Rotation matrix (3, 3)