iactrace.camera

The Camera class and detector-side components.

Camera Class

class iactrace.camera.Camera[source]

Bases: Module

Camera for photon collection and imaging.

The Camera works in its local coordinate system. Sensor positions and rotations are relative to the camera origin (typically [0, 0, 0] for a single-sensor camera). The Telescope transforms the RayBundle into the camera frame before passing it here.

Pipeline:

rb = telescope.render(…) # LazyRayBundle camera.image(rb) # pixel image (fused, per-element fold) camera.response_matrix(rb) # per-source pixel response (fused) camera.collect(rb) # (pe_vals, pe_times, pix_id, detected) camera.trace(rb) # TraceResult through the camera (diagnostics) rb.materialise() # flat camera-frame RayBundle

Variables:

sensor_groups (list[SensorGroup]) – List of SensorGroup objects. Each group owns its pixel layout and its detection chain (optional concentrator + gap + photodetector), so different groups can carry different cones or photodetectors. Configure a group’s chain when constructing it, or via set_concentrator / set_photodetector / set_gap.

__init__(sensor_groups)[source]
sensor_groups = <dataclasses._MISSING_TYPE object>
collect(ray_bundle, sensor_idx=0)[source]

Per-ray output (pe_vals, pe_times, pix_id, detected).

Materialises a LazyRayBundle: per-ray output cannot be produced incrementally.

For the path rays took through the camera, see trace.

trace(ray_bundle, sensor_idx=0)[source]

The path rays take through the camera, for diagnostics / visualization.

Returns a TraceResult.

image(ray_bundle, sensor_idx=0)[source]

Pixel image of shape (n_sensors, *pixel_shape).

Accepts either a flat RayBundle (e.g. from Telescope.trace) or the LazyRayBundle returned by Telescope.render.

For the path rays took through the camera, see trace.

response_matrix(lazy_bundle, sensor_idx=0)[source]

Per-source pixel response, shape (n_sources, n_sensors, *pixel_shape).

Folds per stage-0 element instead of materialising the full ray buffer; peak memory is bounded by the matrix itself.

Requires a LazyRayBundle so the per-source structure is known. Pass telescope.render(…) directly.

set_sensor_positions(sensor_idx, positions)[source]

Set positions for sensors in a group.

set_sensor_rotations(sensor_idx, rotations)[source]

Set rotations for sensors in a group.

set_concentrator(sensor_idx, concentrator)[source]

Set/replace the concentrator on sensor group sensor_idx’s chain.

set_photodetector(sensor_idx, photodetector)[source]

Set/replace the photodetector on sensor group sensor_idx’s chain.

set_gap(sensor_idx, gap)[source]

Set the gap (upstream exit -> detector spacing) on a group’s chain.

get_info()[source]

Summary of the camera configuration.

classmethod from_yaml(filename)[source]

Load a Camera from a standalone camera YAML file.

Sensor positions are interpreted as camera-local coordinates.

Parameters:

filename – Path to camera YAML file.

Return type:

Camera object.

to_yaml(filename, precision=6, overwrite=True)[source]

Save camera to a standalone YAML file.

Sensor positions are written in camera-local coordinates.

Parameters:
  • filename – Output file path.

  • precision – Number of decimal places for float values.

  • overwrite – If True, overwrite existing file.

Return type:

Path to the saved file.

to_dict()[source]

Convert camera to a standalone configuration dictionary.

Sensor positions are in camera-local coordinates.

Sensor Groups

Sensors live in the camera-local frame and accumulate rays into pixels.

class iactrace.camera.SensorGroup[source]

Bases: Module

Abstract base class for sensor groups.

A sensor group contains N sensors at different positions/orientations that share the same pixel geometry and the same detection chain. Each group owns its DetectionChain (optional concentrator + gap + photodetector), so distinct groups in one Camera can carry different cones or photodetectors.

Variables:
  • positions (ndarray (N, 3)) – Sensor positions in 3D space.

  • rotations (ndarray (N, 3)) – Sensor rotations as Euler angles in degrees.

  • chain (iactrace.camera.detection_chain.DetectionChain) – The per-pixel DetectionChain applied to every pixel of this group.

positions = <dataclasses._MISSING_TYPE object>
rotations = <dataclasses._MISSING_TYPE object>
chain = <dataclasses._MISSING_TYPE object>
property n_sensors

Return number of sensors in the group.

abstractmethod get_accumulator_shape()[source]

Return the shape of the accumulator array per sensor.

abstractmethod pixel_index_and_mask(sensor_idx, x, y)[source]

Localize (x, y) to a flat pixel index plus a validity mask.

property pixel_frame_rotation
scatter(pix_id, valid, values)[source]

Sum values into the pixel accumulator by precomputed assignment.

abstractmethod in_bounds(x, y)[source]

Predicate: True for (x, y) inside the sensor’s active footprint.

abstract property pixel_centers

Pixel centres (n_pixels, 2) in the sensor-local frame.

Ordered to match the flat pixel index pixel_index_and_mask returns, so pixel_centers[i] is where pixel i sits.

with_concentrator(concentrator)[source]

Return a copy of this group with its chain’s concentrator replaced.

with_photodetector(photodetector)[source]

Return a copy of this group with its chain’s photodetector replaced.

with_gap(gap)[source]

Return a copy of this group with its chain’s gap replaced.

abstractmethod to_pixel_frame(sensor_rays, pix_id)[source]

Re-express tile-local rays in their assigned pixel’s local frame.

abstractmethod from_pixel_frame(points, pix_id)[source]

Map pixel-local points back to the tile-local frame.

__init__(positions, rotations, chain)
class iactrace.camera.SquareSensorGroup[source]

Bases: SensorGroup

Square pixel sensor group.

__init__(positions, rotations, width, height, bounds, edge_width=0.0, concentrator=None, photodetector=None, gap=0.0)[source]

Square-pixel sensor group.

Parameters:
  • positions (ndarray (N, 3)) – Sensor positions. (or (3,) for one).

  • rotations – Euler angles in degrees, same shape as positions.

  • width – Pixel count along x (> 0).

  • height – Pixel count along y (> 0).

  • bounds (ndarray) – (x_min, x_max, y_min, y_max) in the sensor-local frame.

  • edge_width – Dead-zone width at pixel edges (>= 0).

  • concentrator – Optional per-pixel light concentrator (e.g. a WinstonCone).

  • photodetector – Per-pixel detector response. None defaults to a perfect flat ConstantQE.

  • gap – Spacing from the concentrator exit (or the entrance plane when there is no concentrator) to the detector (>= 0).

Raises:

ValueError – on malformed shapes, non-positive width/height, degenerate bounds, or negative edge_width.

positions = <dataclasses._MISSING_TYPE object>
rotations = <dataclasses._MISSING_TYPE object>
width = <dataclasses._MISSING_TYPE object>
height = <dataclasses._MISSING_TYPE object>
edge_width = <dataclasses._MISSING_TYPE object>
x0 = <dataclasses._MISSING_TYPE object>
y0 = <dataclasses._MISSING_TYPE object>
dx = <dataclasses._MISSING_TYPE object>
dy = <dataclasses._MISSING_TYPE object>
property bounds

Return (x_min, x_max, y_min, y_max) pixel bounds.

get_accumulator_shape()[source]

Return the shape of the accumulator array per sensor.

pixel_index_and_mask(sensor_idx, x, y)[source]

Localize (x, y) to a flat pixel index plus a validity mask.

in_bounds(x, y)[source]

Predicate: True for (x, y) inside the sensor’s active footprint.

property pixel_centers

Pixel centres (n_pixels, 2) in the sensor-local frame.

Ordered to match the flat pixel index pixel_index_and_mask returns, so pixel_centers[i] is where pixel i sits.

to_pixel_frame(sensor_rays, pix_id)[source]

Re-express tile-local rays in their assigned pixel’s local frame.

from_pixel_frame(points, pix_id)[source]

Map pixel-local points back to the tile-local frame.

class iactrace.camera.HexagonalSensorGroup[source]

Bases: SensorGroup

Hexagonal pixel sensor group.

__init__(positions, rotations, hex_centers, edge_width=0.0, concentrator=None, photodetector=None, gap=0.0)[source]

Hexagonal-pixel sensor group.

Parameters:
  • positions (ndarray (N, 3)) – Sensor positions. (or (3,) for one).

  • rotations – Euler angles in degrees, same shape as positions.

  • hex_centers (ndarray (M, 2)) – Pixel centres. The grid geometry (size, rotation, offset, lookup table) is auto-detected from these on construction.

  • edge_width – Dead-zone width at pixel edges (>= 0).

  • concentrator – Optional per-pixel light concentrator (e.g. a WinstonCone).

  • photodetector – Per-pixel detector response. None defaults to a perfect flat ConstantQE.

  • gap – Spacing from the concentrator exit (or the entrance plane when there is no concentrator) to the detector (>= 0).

Raises:

ValueError – on malformed shapes, empty hex_centers, or negative edge_width.

positions = <dataclasses._MISSING_TYPE object>
rotations = <dataclasses._MISSING_TYPE object>
hex_centers = <dataclasses._MISSING_TYPE object>
n_pixels = <dataclasses._MISSING_TYPE object>
edge_width = <dataclasses._MISSING_TYPE object>
hex_size = <dataclasses._MISSING_TYPE object>
hex_inradius = <dataclasses._MISSING_TYPE object>
grid_rotation = <dataclasses._MISSING_TYPE object>
grid_offset = <dataclasses._MISSING_TYPE object>
lookup_table = <dataclasses._MISSING_TYPE object>
q_min = <dataclasses._MISSING_TYPE object>
r_min = <dataclasses._MISSING_TYPE object>
pixel_centers_grid = <dataclasses._MISSING_TYPE object>
get_accumulator_shape()[source]

Return the shape of the accumulator array per sensor.

property pixel_frame_rotation

The detected grid rotation – see SensorGroup.pixel_frame_rotation.

pixel_index_and_mask(sensor_idx, x, y)[source]

Localize (x, y) to a flat pixel index plus a validity mask.

property pixel_centers

Pixel centres (n_pixels, 2) in the sensor-local frame.

Ordered to match the flat pixel index pixel_index_and_mask returns, so pixel_centers[i] is where pixel i sits.

in_bounds(x, y)[source]

Predicate: True for (x, y) inside the sensor’s active footprint.

to_pixel_frame(sensor_rays, pix_id)[source]

Re-express tile-local rays in their assigned pixel’s local frame.

from_pixel_frame(points, pix_id)[source]

Map pixel-local points back to the tile-local frame.

Detection chain

Each SensorGroup owns a DetectionChain: an optional concentrator, a gap, and a photodetector, applied to every pixel of that group. Distinct groups in one camera can therefore carry different cones or photodetectors. Configure a group’s chain at construction (concentrator / photodetector / gap arguments) or functionally via the sensor_idx-keyed Camera.set_concentrator(), Camera.set_photodetector(), and Camera.set_gap() (documented on Camera above).

A ray reaching a pixel is traced onto the photodetector’s DetectionSurface (its photocathode geometry) and then weighted by the photodetector’s response.

class iactrace.camera.DetectionChain[source]

Bases: Module

A pixel’s detection train: (optional concentrator) -> surface -> photodetector.

Every chain traces rays up to the photodetector’s own sensor surface (its photocathode geometry, surface) and hands the resulting bundle back to the photodetector, which applies its detection efficiencies (QE, window response, …). Geometry is owned by the photodetector; the chain only places it, at the detector plane detector_z set by the concentrator + gap. The chain is identical for every pixel in a SensorGroup, so it runs once over all rays at once.

Variables:
concentrator = <dataclasses._MISSING_TYPE object>
photodetector = <dataclasses._MISSING_TYPE object>
gap = 0.0
with_concentrator(concentrator)[source]

Return a copy of this chain with its concentrator replaced.

with_photodetector(photodetector)[source]

Return a copy of this chain with its photodetector replaced.

with_gap(gap)[source]

Return a copy of this chain with its gap replaced.

property detector_z

-(length + gap).

Type:

Detector-plane position in the pixel-local frame

property surface

The photodetector’s sensor surface, placed at detector_z.

propagate(local_rays, record_trajectory=False)[source]

Trace local_rays to the sensor surface, then hand off to the photodetector.

local_rays are in the pixel-local frame (entrance at z = 0). With a concentrator, they are delivered to surface by the concentrator’s own to_surface. With no concentrator the rays advance straight onto the surface. The handover to the photodetector is just the resulting bundle – rays at the surface, pixel-local frame – which it weights by its own detection efficiency (reading any geometry it needs from the surface it owns). Optical path length is accumulated up to the surface (concentrator fill index on its internal leg, ray medium n on the free legs).

Returns a TraceResult; take its rays for the detected bundle. Pass record_trajectory=True to also populate its trajectory with the path through the chain (pixel-local frame): the wall-by-wall bounce path where the concentrator can report one, otherwise the straight entrance-to-landing segment. Off by default, and trajectory is then None.

__init__(concentrator, photodetector, gap=0.0)
class iactrace.camera.detector.surface.DetectionSurface[source]

Bases: Module

The sensor surface a detection chain delivers rays onto.

Parameters:
  • shape (iactrace.core.surfaces.SurfaceGroup) – Optional single-element core surface group giving the sag z(x, y) in the vertex frame (element 0 is used). Mutually exclusive with curvature / conic.

  • vertex_z (float) – Axial position of the surface vertex, relative to the detector plane (0 = at the plane).

  • curvature – c = 1 / R. 0 -> flat. > 0 concave toward the incoming light (bowl); < 0 convex (a dome bulging toward +z).

  • conic – Conic constant k (0 -> sphere).

  • radius (float) – Aperture radius; rays landing beyond it are dropped. None -> unbounded.

Raises:

ValueError – on non-positive radius, or when both shape and curvature / conic are given.

__init__(shape=None, *, vertex_z=0.0, curvature=0.0, conic=0.0, radius=None)[source]
vertex_z = <dataclasses._MISSING_TYPE object>
radius = <dataclasses._MISSING_TYPE object>
is_flat = <dataclasses._MISSING_TYPE object>
shape = <dataclasses._MISSING_TYPE object>
shifted(dz)[source]

Copy with vertex_z shifted by dz (relative -> absolute placement).

sag_fn()[source]

Return this surface’s z(x, y) sag function.

normals_at(points)[source]

Outward unit surface normals at the transverse positions of points.

stop(rays)[source]

Advance rays onto the surface (no concentrator).

Concentrators

Optional light concentrators (e.g. Winston cones) sit between the incoming rays and the photodetector. Optical path length through the guide is weighted by the concentrator’s fill index (1.0 for an air-filled cone). All cones share the PolygonalCone wall-tracing base.

class iactrace.camera.Concentrator[source]

Bases: Module

Abstract base for per-pixel light concentrators.

length = <dataclasses._MISSING_TYPE object>
abstractmethod to_surface(rays, surface)[source]

Deliver rays from the entrance aperture onto surface.

Parameters:
  • rays – Rays at the entrance aperture, pixel-local frame.

  • surface – The stopping surface, placed in the pixel-local frame.

Return type:

Rays landed on surface, same frame.

trace_to_surface(rays, surface)[source]

Same as to_surface, additionally reporting the path rays took.

apply(rays)[source]

Transport rays to the exit aperture (a flat plane at z = -length).

cross_sections()[source]

Optional geometry for iactrace.viz.show_sensor_chain.

__init__(length)
class iactrace.camera.PolygonalCone[source]

Bases: Concentrator

A hollow reflective cone whose n_sides facets are lofted around a meridian.

  • Geometry. The facet plane normals (n_hats) and the drawable cross-sections depend only on the polygon (n_sides, orientation) and on the meridian samples the subclass provides via _meridian.

  • Wall tracing. to_surface runs the shared trace_chain bounce loop, which asks the cone for the raw nearest wall hit (_nearest_hit); the cavity clamp (nearest_hit), the mouth-aperture mask (in_mouth) and the off-wall reflection book-keeping (reflect_ray) are shared here. All tracer coordinates are the cone frame (exit z = 0, mouth z = length).

  • Wall response. Every bounce costs wall_reflectivity, evaluated at the incidence angle of that bounce and at the ray’s wavelength.

n_sides = <dataclasses._MISSING_TYPE object>
orientation = <dataclasses._MISSING_TYPE object>
entrance_apothem = <dataclasses._MISSING_TYPE object>
exit_apothem = <dataclasses._MISSING_TYPE object>
reflectivity = <dataclasses._MISSING_TYPE object>
reflectivity_curve = <dataclasses._MISSING_TYPE object>
max_bounces = <dataclasses._MISSING_TYPE object>
wall_reflectivity(cos_theta_i, wavelength)[source]

Per-ray wall reflectivity, shape (n_rays,).

The scalar reflectivity is the flat bulk value; an optional reflectivity_curve multiplies in the coating response, evaluated at each ray’s actual incidence angle on the wall and at its wavelength.

Parameters:
  • cos_theta_i (ndarray (n_rays,)) – Cosine of the angle between the ray and the wall normal at the hit point. 1 is normal incidence, 0 grazing.

  • wavelength (ndarray (n_rays,)) – Per-ray wavelength.

property n_hats

(M, 2) inward plane normals of the polygon facets.

nearest_hit(o, d)[source]

Nearest forward wall hit for one ray, clamped to z in [0, length].

Returns (t, normal) with t = inf when the nearest wall root falls outside the cavity – e.g. the ray has dropped below the exit, where the infinite wall surface would otherwise give a spurious root.

reflect_ray(o, d, t, normal)[source]

Reflect one ray off a wall hit at parameter t.

Returns (new_origin, new_direction, path_added, cos_theta_i). The new origin is nudged just off the wall along the reflected ray.

in_mouth(xy)[source]

True where the transverse position xy lies inside the mouth polygon.

to_surface(rays, surface)[source]

Trace rays through the reflecting walls onto surface.

trace_to_surface(rays, surface)[source]

to_surface, also returning the per-bounce wall path.

cross_sections()[source]

Optional geometry for iactrace.viz.show_sensor_chain.

__init__(length, n_sides, orientation, entrance_apothem, exit_apothem, reflectivity, reflectivity_curve, max_bounces)
class iactrace.camera.WinstonCone[source]

Bases: PolygonalCone

Polygonal CPC (Winston cone) light guide.

Defined entirely by its physical dimensions; exit apothem, entrance apothem and length.

Parameters:
  • n_sides (int) – Number of facets (6 = hexagonal, 4 = square, …).

  • entrance_apothem (float) – Entrance inradius a1; the apothem at the entrance plane z = length. For a truncated cone this is the actual (truncated) entry.

  • exit_apothem (float) – Exit aperture inradius a2.

  • length (float) – Physical depth. None builds the full (untruncated) CPC and derives the length from a1/a2; a value truncates the cone (then entrance_apothem is the entry at that depth).

  • reflectivity (float) – Per-bounce wall reflectivity (scalar bulk value).

  • reflectivity_curve (iactrace.core.responses.ResponseCurve | None) – Optional coating curve (ResponseCurve) multiplying the scalar, evaluated at each bounce’s actual incidence angle and at the ray’s wavelength; None (default) is a flat wall response.

  • max_bounces (int) – Maximum reflections traced before a ray is absorbed.

  • orientation_deg – Rotation of the polygon about the optical axis.

__init__(n_sides, entrance_apothem, exit_apothem, length=None, reflectivity=0.9, max_bounces=10, orientation_deg=0.0, reflectivity_curve=None)[source]
n_sides = <dataclasses._MISSING_TYPE object>
exit_apothem = <dataclasses._MISSING_TYPE object>
entrance_apothem = <dataclasses._MISSING_TYPE object>
reflectivity = <dataclasses._MISSING_TYPE object>
reflectivity_curve = <dataclasses._MISSING_TYPE object>
max_bounces = <dataclasses._MISSING_TYPE object>
orientation = <dataclasses._MISSING_TYPE object>
length = <dataclasses._MISSING_TYPE object>
s = <dataclasses._MISSING_TYPE object>
c = <dataclasses._MISSING_TYPE object>
property k

Meridian offset a2 * (2 + s) of the wall parabola.

class iactrace.camera.OkumuraCone[source]

Bases: PolygonalCone

Okumura light collector: a polygonal cone with Bezier-curve walls.

A hollow light guide whose n_sides walls follow a quadratic or cubic Bezier meridian (Okumura 2012, arXiv:1205.3968) rather than the Winston paraboloid. Construct it either from an explicit list of interior control points or via quadratic / cubic, using the relative coordinates tabulated in the paper. Like the Winston cone, it answers the per-facet Bezier-meridian hit (_nearest_hit); the bounce loop is owned by the shared trace_chain.

The control points are given in the paper’s normalized box: the exit rim is (0, 0) and the mouth is (1, 1), so a control point (r, z) has r interpolating the inradius from exit_apothem to entrance_apothem and z interpolating the axial position from the exit plane to the mouth.

Parameters:
  • n_sides (int) – Number of facets.

  • entrance_apothem (float) – Mouth inradius a1.

  • exit_apothem (float) – Exit aperture inradius a2.

  • control_points (tuple[tuple[float, float], ...]) – Interior Bezier control points in normalized coordinates – [(P1r, P1z)] for a quadratic curve, [(P1r, P1z), (P2r, P2z)] for a cubic one. The endpoints (0, 0) (exit) and (1, 1) (mouth) are implied.

  • length (float) – Physical depth. None defaults to the length of the equivalent full Winston cone, L = (a1 + a2) * cos/sin(theta_max) with sin(theta_max) = a2 / a1.

  • reflectivity (scalar) – Per-bounce wall reflectivity.

  • reflectivity_curve (iactrace.core.responses.ResponseCurve | None) – Optional coating curve.

  • max_bounces (int) – Maximum reflections traced before a ray is absorbed.

  • orientation_deg – Rotation of the polygon about the optical axis.

Raises:

ValueError – if 0 < exit_apothem < entrance_apothem is violated, if no interior control point is given, or if the control points give a non-monotonic axial profile Z(t) (an ill-defined depth).

__init__(n_sides, entrance_apothem, exit_apothem, control_points, length=None, reflectivity=0.9, max_bounces=10, orientation_deg=0.0, reflectivity_curve=None)[source]
n_sides = <dataclasses._MISSING_TYPE object>
exit_apothem = <dataclasses._MISSING_TYPE object>
entrance_apothem = <dataclasses._MISSING_TYPE object>
length = <dataclasses._MISSING_TYPE object>
control_points = <dataclasses._MISSING_TYPE object>
r_coeffs = <dataclasses._MISSING_TYPE object>
z_coeffs = <dataclasses._MISSING_TYPE object>
reflectivity = <dataclasses._MISSING_TYPE object>
reflectivity_curve = <dataclasses._MISSING_TYPE object>
max_bounces = <dataclasses._MISSING_TYPE object>
orientation = <dataclasses._MISSING_TYPE object>
classmethod quadratic(n_sides, entrance_apothem, exit_apothem, p1, **kwargs)[source]

Build a quadratic Okumura cone from its single Bezier control point P1.

classmethod cubic(n_sides, entrance_apothem, exit_apothem, p1, p2, **kwargs)[source]

Build a cubic Okumura cone from its Bezier control points P1 and P2.

property degree

Degree of the Bezier meridian (2 = quadratic, 3 = cubic).

Photodetectors

A photodetector is the terminal element of a detection chain: it owns its sensor surface and weights each landed ray by its detection efficiency.

class iactrace.camera.PhotoDetector[source]

Bases: Module

Abstract base for photodetector response models.

A photodetector is the terminal element of a detection chain and owns two things:

  • Its surface (surface): the DetectionSurface the chain traces rays onto – by definition every photodetector has one. The base class provides the default (an unbounded flat detector at the chain’s detector plane); photodetectors with a curved / apertured photocathode override the property.

  • Its response (detect): it receives the rays the chain has delivered onto that surface and weights values by its detection efficiency. The handover is just the RayBundle; a photodetector with an angle-dependent response reads the geometry it needs from its own surface (e.g. normals_at at the landing positions, turned into incidence cosines with incidence_cos).

property surface

The sensor surface, with vertex_z relative to the detector plane.

abstractmethod detect(local_rays)[source]

Weight local_rays by detection efficiency at the sensor surface.

Parameters:

local_rays – Rays landed on the surface, pixel-local frame (true directions preserved; dead / undetected rays carry 0).

Return type:

Rays with photoelectron-weighted values; geometry unchanged.

outline()[source]

Optional active-area polygon (M, 2) for the diagnostic viz.

Expressed in the pixel-local frame. The default returns None (“not drawable”), in which case iactrace.viz.show_sensor_chain falls back to the entrance-aperture footprint.

envelope()[source]

Optional 3D envelope (z, rings) for iactrace.viz.show_sensor_chain.

Mirrors cross_sections for the detector side: a surface of revolution / lofted wall drawn around the detector plane so a physical photodetector body (e.g. a PMT’s glass front + tube) becomes visible.

  • z; shape (K,) axial samples in the pixel-local frame, with z = 0 at the photocathode (detector) plane and +z toward the incoming light. The viz offsets these to detector_z.

  • rings; shape (K, M, 2) wall cross-section at each slice (large M ~ round).

The default returns None (“no envelope drawn”); photodetectors with a physical body override it.

__init__()
class iactrace.camera.ConstantQE[source]

Bases: PhotoDetector

Flat scalar quantum efficiency with no spatial, angular or spectral structure.

Parameters:

qe (float) – Quantum efficiency in [0, 1].

__init__(qe=1.0)[source]
qe = <dataclasses._MISSING_TYPE object>
detect(local_rays)[source]

Weight local_rays by detection efficiency at the sensor surface.

Parameters:

local_rays – Rays landed on the surface, pixel-local frame (true directions preserved; dead / undetected rays carry 0).

Return type:

Rays with photoelectron-weighted values; geometry unchanged.

class iactrace.camera.PMT[source]

Bases: PhotoDetector

A photomultiplier: a sensor surface + a cylindrical body.

A self-contained photodetector bundling everything a PMT contributes to detection, applied after the chain hands rays over to the sensor surface:

  • Geometry. The sensor surface is the sensor surface the chain traces rays onto (surface): bounded by face_radius and placed with its vertex at vertex_z (relative to the detector plane; 0 = flush with the mount, > 0 peeks toward the light, as for a domed window).

  • Efficiency. The usual qe / qe_curve pair, applied to every ray landing on the sensor surface. By default this is the single bulk scalar qe (real PMT efficiencies are measured with the entrance glass in place, so qe is the whole measured number). For a wavelength- or angle-dependent photocathode, pass qe_curve.

  • Optional entrance window. Set window_index to also weight each ray by the unpolarized Fresnel transmittance at the air/window interface for its incident angle – the angular response a single measured scalar cannot capture. When you do, qe should be the intrinsic photocathode QE (the glass loss is then modelled by the Fresnel term, not folded into qe). A number gives a non-dispersive window; a RefractiveIndex model gives a dispersive one, n(lambda).

Parameters:
  • qe (float) – Bulk detection efficiency in [0, 1] applied at the photocathode. Defaults to 1.0. With qe_curve it acts as a bulk multiplier.

  • qe_curve (ndarray) – Optional QE(theta, lambda) ResponseCurve multiplying qe per ray; None (default) is flat.

  • window_index (iactrace.core.refractive_index.RefractiveIndex | None) – Refractive index of the entrance window. None (default) applies qe alone. A number > 1 (e.g. 1.48 for borosilicate glass) additionally weights each ray by the incident-angle Fresnel transmittance through the window; a RefractiveIndex model does the same with a dispersive n(lambda).

  • face_radius (float) – Sensor surface aperture radius (and the body radius).

  • surface – The sensor surface figure, a single-element SurfaceGroup (typically an AsphericSurfaceGroup, optionally summed with a ZernikeSurfaceGroup via SumSurfaceGroup). None (default) is a flat window.

  • vertex_z (float) – Axial position of the surface’s vertex, relative to the detector plane.

  • length (float) – Axial length of the cylindrical body behind the sensor surface. None defaults to 2 * face_radius.

  • n_facets (int) – Facets of the revolved body (48 ~ round).

Raises:

ValueError – on qe outside [0, 1], a constant window_index <= 1, non-positive face_radius, negative length, or n_facets < 3.

__init__(qe=1.0, *, qe_curve=None, window_index=None, face_radius, surface=None, vertex_z=0.0, length=None, n_facets=48)[source]
qe = <dataclasses._MISSING_TYPE object>
qe_curve = <dataclasses._MISSING_TYPE object>
window_index = <dataclasses._MISSING_TYPE object>
face_radius = <dataclasses._MISSING_TYPE object>
shape = <dataclasses._MISSING_TYPE object>
vertex_z = <dataclasses._MISSING_TYPE object>
length = <dataclasses._MISSING_TYPE object>
n_facets = <dataclasses._MISSING_TYPE object>
detect(local_rays)[source]

Weight local_rays by detection efficiency at the sensor surface.

Parameters:

local_rays – Rays landed on the surface, pixel-local frame (true directions preserved; dead / undetected rays carry 0).

Return type:

Rays with photoelectron-weighted values; geometry unchanged.

property surface

The sensor surface.

shape supplies the figure (flat by default; curved / aspheric / Zernike otherwise), placed with its vertex at vertex_z and bounded by face_radius.

outline()[source]

Optional active-area polygon (M, 2) for the diagnostic viz.

Expressed in the pixel-local frame. The default returns None (“not drawable”), in which case iactrace.viz.show_sensor_chain falls back to the entrance-aperture footprint.

envelope()[source]

Body-only cylinder for the viz: vertex_z -> vertex_z - length.