Source code for telescope_sim.outputs.intensity
"""IntensityOutputTap — channels-last stack of per-focal-plane PSF intensities.
Mirrors the canonical-family behaviour where one OutputTap collects the
PSF intensities from one or more named focal planes and stacks them along
the last axis (shape ``(H, W, n_focal_planes)``).
"""
from __future__ import annotations
from typing import Any
import numpy as np
from numpy.typing import NDArray
from telescope_sim.abc import OutputTap
from telescope_sim.registry import register
[docs]
@register("output_tap", "intensity")
class IntensityOutputTap(OutputTap):
"""Stack per-focal-plane PSF intensities along the channel (last) axis.
Parameters
----------
focal_plane_names
One or more named focal planes whose summed intensities feed this tap.
name
Output name (used as the key in ``sample()``'s returned ``images`` dict).
"""
def __init__(
self,
focal_plane_names: list[str],
*,
name: str = "psf",
) -> None:
if not focal_plane_names:
raise ValueError("IntensityOutputTap needs at least one focal_plane name")
self.name = name
self.focal_plane_names = list(focal_plane_names)
# The ABC's `source` field is informational here; we expose the
# composite source string for diagnostics.
self.source = "focal:" + ",".join(self.focal_plane_names)
[docs]
def extract(
self, fp_results: Any, *, overrides: dict[str, Any] | None = None
) -> NDArray[np.floating]:
"""Extract from a dict mapping focal_plane name → FocalPlaneResult.
Uses the summed-intensity field from each focal plane and stacks
channels-last so the canonical ``(H, W, n_filters)`` layout falls out.
``overrides`` is accepted for ABC compatibility and ignored — this tap
has no per-sample state.
"""
del overrides # this tap has no per-sample state
if not isinstance(fp_results, dict):
raise TypeError(
"IntensityOutputTap.extract expects a dict of FocalPlaneResults; "
f"got {type(fp_results).__name__}"
)
missing = [n for n in self.focal_plane_names if n not in fp_results]
if missing:
raise KeyError(f"focal planes {missing} not in available outputs {list(fp_results)}")
psfs = [
np.asarray(fp_results[n].intensity, dtype=np.float64) for n in self.focal_plane_names
]
return np.stack(psfs, axis=-1)
__all__ = ["IntensityOutputTap"]