API reference
Top-level
telescope-sim — composable, config-driven simulation of multi-aperture telescope PSFs.
Built on HCIPy. The package exposes a pluggable pipeline of optical stages (aperture, correctors, coronagraph, focal plane, output taps, post-processing) driven by YAML configuration. Currently in alpha; concrete implementations are under active development.
- class telescope_sim.TelescopeSim(components: _PipelineComponents)[source]
Bases:
objectComposable telescope-PSF simulator.
Construct via one of the classmethods (not via
__init__directly):from_preset()— load a packaged preset by namefrom_yaml()— load a user-supplied YAML configfrom_components()— instantiate from already-resolved components (primarily for the pipeline’s own internal use and for advanced users bypassing the YAML/pydantic layer)
- property aperture: ApertureResult
- property focal_planes: dict[str, FocalPlane]
- classmethod from_components(components: _PipelineComponents) TelescopeSim[source]
Instantiate from already-resolved pipeline components.
- classmethod from_preset(name: str) TelescopeSim[source]
Load a packaged preset by name.
- classmethod from_yaml(path: str | Path) TelescopeSim[source]
Load a configuration YAML and build the pipeline.
- sample(actuations: Mapping[str, ArrayLike] | None = None, *, atmos: Any = None, output_overrides: Mapping[str, Mapping[str, Any]] | None = None, meas_strehl: bool = False, meas_pupil_opd: bool = False) dict[str, Any][source]
Run the optical chain and return a dict of outputs.
- Parameters:
actuations – Per-corrector actuator state. Keys are corrector names (matching those declared in the config); each value is whatever shape that corrector’s
set_actuatorsaccepts.atmos –
Per-sample external atmosphere. Any callable taking a
hcipy.Wavefrontand returning a modifiedhcipy.Wavefront(typically ahcipy.InfiniteAtmosphericLayerorhcipy.MultiLayerAtmosphere, but any wf→wf callable works). Applied at the front of the chain, before any corrector. The caller owns time evolution — v2 holds no atmosphere state.If the object also exposes
.phase_for(lam)returning HCIPy-convention phase = 2π·OPD/lam, the atmosphere’s OPD is seeded into the per-corrector cumulative-OPD stream, so fit-role correctors withfit_source="cumulative_phase_pre_self"will naturally fit to (and cancel) the atmosphere. Without.phase_forthe wavefront is still modified, but fit-role correctors only see corrector-chain OPD.The reference PSF is never atmospheric — it’s cached once at sim-build time with
atmos=Noneimplicit.output_overrides – Per-sample tap-config overrides, keyed by output name. For example,
{"psf": {"int_phot_flux": 5.0e7}}to vary the photon flux on a noisy-intensity tap from sample to sample. Taps that have no per-sample state ignore the override.meas_strehl – If True, includes a
strehlsentry in the returned dict.meas_pupil_opd – If True, includes a
pupil_opdentry in the returned dict: the cumulative pupil-plane OPD seen at the back of the chain, as anhcipy.Fieldon the simulator’s pupil grid in meters. Sums atmosphere (whenatmos.phase_foris available) + every DM-backed corrector’s surface × 2. Companion tosim.aperture.fieldfor masked display viahcipy.imshow_field(out["pupil_opd"], mask=sim.aperture.field).
- Returns:
images— dict of output_name → numpy arrayactuations— dict of corrector_name → numpy array(only for correctors with
target=True)strehls— present iffmeas_strehlis Truepupil_opd— present iffmeas_pupil_opdis True;hcipy.Fieldof cumulative pupil-plane OPD in meters- Return type:
Pipeline
Pipeline orchestrator — holds the optical chain and runs sample().
This module exposes the top-level TelescopeSim class, the entry
point for constructing a simulation from a YAML config, a preset, or a
validated pydantic config object.
- class telescope_sim.pipeline.TelescopeSim(components: _PipelineComponents)[source]
Bases:
objectComposable telescope-PSF simulator.
Construct via one of the classmethods (not via
__init__directly):from_preset()— load a packaged preset by namefrom_yaml()— load a user-supplied YAML configfrom_components()— instantiate from already-resolved components (primarily for the pipeline’s own internal use and for advanced users bypassing the YAML/pydantic layer)
- property aperture: ApertureResult
- property focal_planes: dict[str, FocalPlane]
- classmethod from_components(components: _PipelineComponents) TelescopeSim[source]
Instantiate from already-resolved pipeline components.
- classmethod from_preset(name: str) TelescopeSim[source]
Load a packaged preset by name.
- classmethod from_yaml(path: str | Path) TelescopeSim[source]
Load a configuration YAML and build the pipeline.
- sample(actuations: Mapping[str, ArrayLike] | None = None, *, atmos: Any = None, output_overrides: Mapping[str, Mapping[str, Any]] | None = None, meas_strehl: bool = False, meas_pupil_opd: bool = False) dict[str, Any][source]
Run the optical chain and return a dict of outputs.
- Parameters:
actuations – Per-corrector actuator state. Keys are corrector names (matching those declared in the config); each value is whatever shape that corrector’s
set_actuatorsaccepts.atmos –
Per-sample external atmosphere. Any callable taking a
hcipy.Wavefrontand returning a modifiedhcipy.Wavefront(typically ahcipy.InfiniteAtmosphericLayerorhcipy.MultiLayerAtmosphere, but any wf→wf callable works). Applied at the front of the chain, before any corrector. The caller owns time evolution — v2 holds no atmosphere state.If the object also exposes
.phase_for(lam)returning HCIPy-convention phase = 2π·OPD/lam, the atmosphere’s OPD is seeded into the per-corrector cumulative-OPD stream, so fit-role correctors withfit_source="cumulative_phase_pre_self"will naturally fit to (and cancel) the atmosphere. Without.phase_forthe wavefront is still modified, but fit-role correctors only see corrector-chain OPD.The reference PSF is never atmospheric — it’s cached once at sim-build time with
atmos=Noneimplicit.output_overrides – Per-sample tap-config overrides, keyed by output name. For example,
{"psf": {"int_phot_flux": 5.0e7}}to vary the photon flux on a noisy-intensity tap from sample to sample. Taps that have no per-sample state ignore the override.meas_strehl – If True, includes a
strehlsentry in the returned dict.meas_pupil_opd – If True, includes a
pupil_opdentry in the returned dict: the cumulative pupil-plane OPD seen at the back of the chain, as anhcipy.Fieldon the simulator’s pupil grid in meters. Sums atmosphere (whenatmos.phase_foris available) + every DM-backed corrector’s surface × 2. Companion tosim.aperture.fieldfor masked display viahcipy.imshow_field(out["pupil_opd"], mask=sim.aperture.field).
- Returns:
images— dict of output_name → numpy arrayactuations— dict of corrector_name → numpy array(only for correctors with
target=True)strehls— present iffmeas_strehlis Truepupil_opd— present iffmeas_pupil_opdis True;hcipy.Fieldof cumulative pupil-plane OPD in meters- Return type:
Abstract base classes
Abstract base classes for pluggable pipeline stages.
Each ABC defines the minimal interface a concrete implementation must satisfy
to be registered and consumed by the pipeline. See individual module docstrings
for semantics and the role/relationship system documented on Corrector.
- class telescope_sim.abc.Aperture[source]
Bases:
ABCABC for pupil-plane aperture constructors.
Subclasses are typically registered via
telescope_sim.registry.register():@register("aperture", "segmented_circular") class SegmentedCircularAperture(Aperture): def build(self, pupil_grid): ...
- abstractmethod build(pupil_grid: Any) ApertureResult[source]
Construct the aperture on the given pupil grid.
- Parameters:
pupil_grid – HCIPy pupil grid (typically
make_pupil_grid(resolution, extent)).- Returns:
The transmission field, collecting area, and optional segmentation.
- Return type:
- class telescope_sim.abc.ApertureResult(field: Any, area: float, segments: Any | None = None, segment_coords: NDArray[floating] | None = None, metadata: dict[str, ~typing.Any]=<factory>)[source]
Bases:
objectThe outputs of an
Aperturebuild step.- field
HCIPy
Fieldrepresenting the pupil-plane transmission (real-valued).- Type:
Any
- area
Effective collecting area in pupil-grid units squared (used for photon flux scaling in noise calculations).
- Type:
- segments
Optional
SegmentedAperture(HCIPy) for segmented mirrors.Nonefor monolithic apertures.- Type:
Any | None
- segment_coords
Optional
(n_segments, 2)array of segment center coordinates. Used by per-segment PTT measurement (_measure_atmos_ptt-style fits).- Type:
numpy._typing._array_like.NDArray[numpy.floating] | None
- class telescope_sim.abc.Coronagraph[source]
Bases:
ABCABC for pupil-plane coronagraph elements.
- class telescope_sim.abc.Corrector[source]
Bases:
ABCABC for pluggable pupil-plane correctors (DMs, segmented mirrors, etc.).
Concrete implementations must provide
apply()andset_actuators(). Implementations that participate in fit-roles or residual-fit targets must additionally providefit_surface().- abstract property actuators: NDArray
- abstractmethod apply(wf: Any) Any[source]
Apply the corrector’s current state to a wavefront in-chain.
- fit_surface(phase: NDArray[floating]) NDArray[floating][source]
Fit the corrector’s actuator basis to a pupil-plane OPD array.
Required when participating in
role=fitortarget_strategy=actuators_plus_residual_fit/residual_fit_only. Default implementation raisesNotImplementedErrorto make misconfiguration visible.Convention
Input
phase: pupil-plane optical path difference (OPD) in meters — i.e. path-length,OPD = 2 * surfacefor a single DM reflection. The pipeline accumulates this as it walks the chain.Output: matching caller-facing actuator values — i.e. values that, when set via
set_actuators(), would reproduce the input OPD as this corrector’s surface contribution (modulo the round-trip factor; surface = OPD / 2).Sign: matching, not cancellation. To cancel the input OPD, the caller negates. The pipeline does this automatically at the apply site for
wavefront_role="fit".Y echoes for
target_strategy="actuators_plus_residual_fit"and"residual_fit_only"consumefit_surfaceunnegated: Y reports the wavefront state in this corrector’s basis (matches legacy v1out_actuate = caller + matching_fit(atmos)). An ML model trained to predict Y is then driven through-Yby the downstream controller to cancel.
- class telescope_sim.abc.FocalPlane[source]
Bases:
ABCABC for focal-plane setups (propagator + grid + optional detector).
- class telescope_sim.abc.LoaderBindable[source]
Bases:
objectMarker mixin for post-processors that need loader-injected dependencies.
The loader walks each output’s post-processing list and calls
_bind_loader_dependencies(aperture_result, focal_planes, focal_plane_names)on any processor exposing the hook. Used bynoisy_detector(needs the focal grid + aperture area) andconvolve_image(needs the reference PSF sum). Processors without runtime dependencies simply don’t expose the hook and the loader skips them.
- class telescope_sim.abc.OutputTap[source]
Bases:
ABCABC for output extractors on named intermediate wavefronts.
- abstractmethod extract(wf: Any, *, overrides: dict[str, Any] | None = None) NDArray[floating][source]
Return a numpy array representing this tap’s view of the wavefront.
- Parameters:
wf – The tap’s input — typically the
fp_resultsdict mapping focal- plane name toFocalPlaneResult.overrides – Per-sample tap-config overrides from
sim.sample(output_overrides=...). Most taps ignore this; taps that have per-sample state (e.g. photon flux for a noisy detector) read named fields here.
- class telescope_sim.abc.PipelineContext(output_name: str, focal_plane_name: str, reference_peak_intensity: float | None = None, reference_psf_sum: float | None = None, overrides: dict[str, ~typing.Any]=<factory>, extras: dict[str, ~typing.Any]=<factory>)[source]
Bases:
objectPer-sample state available to post-processors.
Populated by the pipeline before running the post-processing list for each output. Includes reference values (peak intensity, normalized reference PSF) used by normalization steps, plus any per-sample overrides the caller supplied via
sim.sample(output_overrides={...}).
- class telescope_sim.abc.PostProcessor[source]
Bases:
ABCABC for image-space transforms applied after wavefront extraction.
Aperture ABC — constructs the static pupil-plane transmission mask.
Implementations are registered with @register("aperture", "<name>") and
return an ApertureResult from Aperture.build(). The pipeline
calls build() once at construction time.
- class telescope_sim.abc.aperture.Aperture[source]
Bases:
ABCABC for pupil-plane aperture constructors.
Subclasses are typically registered via
telescope_sim.registry.register():@register("aperture", "segmented_circular") class SegmentedCircularAperture(Aperture): def build(self, pupil_grid): ...
- abstractmethod build(pupil_grid: Any) ApertureResult[source]
Construct the aperture on the given pupil grid.
- Parameters:
pupil_grid – HCIPy pupil grid (typically
make_pupil_grid(resolution, extent)).- Returns:
The transmission field, collecting area, and optional segmentation.
- Return type:
- class telescope_sim.abc.aperture.ApertureResult(field: Any, area: float, segments: Any | None = None, segment_coords: NDArray[floating] | None = None, metadata: dict[str, ~typing.Any]=<factory>)[source]
Bases:
objectThe outputs of an
Aperturebuild step.- field
HCIPy
Fieldrepresenting the pupil-plane transmission (real-valued).- Type:
Any
- area
Effective collecting area in pupil-grid units squared (used for photon flux scaling in noise calculations).
- Type:
- segments
Optional
SegmentedAperture(HCIPy) for segmented mirrors.Nonefor monolithic apertures.- Type:
Any | None
- segment_coords
Optional
(n_segments, 2)array of segment center coordinates. Used by per-segment PTT measurement (_measure_atmos_ptt-style fits).- Type:
numpy._typing._array_like.NDArray[numpy.floating] | None
Corrector ABC — pupil-plane element with actuator state and role-based behavior.
Two orthogonal axes control a corrector’s role in the pipeline:
wavefront_role: how actuator state gets set duringsample()"actuate"— set by the ML model (caller’sactuationsdict)"impose"— set by the caller via a separate channel(e.g. environment, atmosphere, fixed aberrations)
"fit"— lstsq-fit to another corrector’s surface or a namedwavefront. Requires
fit_source.
fit_source (used when wavefront_role == "fit") can be:
a corrector name (string) — fit to that corrector’s surface
a named wavefront — fit to that wavefront’s phase
the special value
"cumulative_phase_pre_self"— fit to cumulative phase just beforethis corrector in the chain
Common patterns this expresses:
Segment-impose / DM-fit:
segments(actuate, target=actuators) plus a downstreamxineticscorrector (fit,fit_source=segments, target=none) — the segmented mirror’s commanded surface is approximated by the DM in the beam, but Y reports the segment commands.Cumulative residual-fit DM: an
actuatecorrector withtarget_strategy=actuators_plus_residual_fitandfit_source="cumulative_phase_pre_self"— Y reportsmodel_actuators + fit(cumulative_phase_pre_self), i.e. the correction the model would have needed to fully cancel everything upstream. The upstream signal can be other imposed correctors and the externalatmos=...kwarg tosample(); atmosphere is not a corrector but it does seed the cumulative-OPD stream when it exposes.phase_for(lam).Stacked imposes + fitted DM: any number of
imposecorrectors followed by a singleactuatecorrector withtarget_strategy=actuators_plus_residual_fitfor ML training against cumulative residual phase.
- class telescope_sim.abc.corrector.Corrector[source]
Bases:
ABCABC for pluggable pupil-plane correctors (DMs, segmented mirrors, etc.).
Concrete implementations must provide
apply()andset_actuators(). Implementations that participate in fit-roles or residual-fit targets must additionally providefit_surface().- abstract property actuators: NDArray
- abstractmethod apply(wf: Any) Any[source]
Apply the corrector’s current state to a wavefront in-chain.
- fit_surface(phase: NDArray[floating]) NDArray[floating][source]
Fit the corrector’s actuator basis to a pupil-plane OPD array.
Required when participating in
role=fitortarget_strategy=actuators_plus_residual_fit/residual_fit_only. Default implementation raisesNotImplementedErrorto make misconfiguration visible.Convention
Input
phase: pupil-plane optical path difference (OPD) in meters — i.e. path-length,OPD = 2 * surfacefor a single DM reflection. The pipeline accumulates this as it walks the chain.Output: matching caller-facing actuator values — i.e. values that, when set via
set_actuators(), would reproduce the input OPD as this corrector’s surface contribution (modulo the round-trip factor; surface = OPD / 2).Sign: matching, not cancellation. To cancel the input OPD, the caller negates. The pipeline does this automatically at the apply site for
wavefront_role="fit".Y echoes for
target_strategy="actuators_plus_residual_fit"and"residual_fit_only"consumefit_surfaceunnegated: Y reports the wavefront state in this corrector’s basis (matches legacy v1out_actuate = caller + matching_fit(atmos)). An ML model trained to predict Y is then driven through-Yby the downstream controller to cancel.
Coronagraph ABC — optional pupil-plane stage between correctors and focal plane.
The pipeline always includes a coronagraph slot; the identity implementation
is a no-op that keeps the chain uniform when no coronagraph is configured. The
reference PSF (for Strehl normalization) is always generated with the identity
coronagraph regardless of what’s installed.
- class telescope_sim.abc.coronagraph.Coronagraph[source]
Bases:
ABCABC for pupil-plane coronagraph elements.
FocalPlane ABC — owns a propagator, focal grid, wavelength sampling, optional detector.
Multiple named focal planes can fan out from the same pupil_post_coro wavefront,
each with its own propagator and grid. This supports per-filter PSFs and
mixed-physics setups (e.g. an angular focal plane for the science detector
alongside a physical focal plane for fiber coupling).
- class telescope_sim.abc.focal_plane.FocalPlane[source]
Bases:
ABCABC for focal-plane setups (propagator + grid + optional detector).
OutputTap ABC — extracts a numpy array from a named intermediate wavefront.
Output taps reference a named wavefront in the pipeline (pupil_post_coro,
focal-plane names, pupil_pre_<corrector>, etc.) and produce an array that
becomes one of the entries in the returned images dict.
- class telescope_sim.abc.output_tap.OutputTap[source]
Bases:
ABCABC for output extractors on named intermediate wavefronts.
- abstractmethod extract(wf: Any, *, overrides: dict[str, Any] | None = None) NDArray[floating][source]
Return a numpy array representing this tap’s view of the wavefront.
- Parameters:
wf – The tap’s input — typically the
fp_resultsdict mapping focal- plane name toFocalPlaneResult.overrides – Per-sample tap-config overrides from
sim.sample(output_overrides=...). Most taps ignore this; taps that have per-sample state (e.g. photon flux for a noisy detector) read named fields here.
PostProcessor ABC — ordered image transforms applied per output.
Each output declares a list of post-processors that run in order. A processor
takes the current image plus a PipelineContext (which exposes
reference PSFs, peak intensities, and any per-sample state needed for
normalization) and returns the next image. Processors may change the array’s
shape (e.g. fft_channels appends channels; channels_first transposes).
- class telescope_sim.abc.post_processor.LoaderBindable[source]
Bases:
objectMarker mixin for post-processors that need loader-injected dependencies.
The loader walks each output’s post-processing list and calls
_bind_loader_dependencies(aperture_result, focal_planes, focal_plane_names)on any processor exposing the hook. Used bynoisy_detector(needs the focal grid + aperture area) andconvolve_image(needs the reference PSF sum). Processors without runtime dependencies simply don’t expose the hook and the loader skips them.
- class telescope_sim.abc.post_processor.PipelineContext(output_name: str, focal_plane_name: str, reference_peak_intensity: float | None = None, reference_psf_sum: float | None = None, overrides: dict[str, ~typing.Any]=<factory>, extras: dict[str, ~typing.Any]=<factory>)[source]
Bases:
objectPer-sample state available to post-processors.
Populated by the pipeline before running the post-processing list for each output. Includes reference values (peak intensity, normalized reference PSF) used by normalization steps, plus any per-sample overrides the caller supplied via
sim.sample(output_overrides={...}).
Configuration
Pydantic v2 schemas for the telescope-sim configuration.
This module exposes the top-level SimConfig and per-stage
sub-schemas. The schema is intentionally permissive on the per-stage
config payloads: those are passed straight through to the registered
implementation’s constructor, which is free to validate further.
- class telescope_sim.config.schema.CorrectorConfig(*, type: str, wavefront_role: Literal['actuate', 'impose', 'fit'] = 'actuate', target_strategy: Literal['none', 'actuators', 'actuators_plus_residual_fit', 'residual_fit_only'] = 'none', fit_source: str | None = None, target: bool = False, **extra_data: Any)[source]
Bases:
StageConfigA corrector + its role/target-strategy settings.
- model_config = {'extra': 'allow'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- target_strategy: Literal['none', 'actuators', 'actuators_plus_residual_fit', 'residual_fit_only']
- wavefront_role: Literal['actuate', 'impose', 'fit']
- class telescope_sim.config.schema.OutputConfig(*, tap: StageConfig, post_processing: list[StageConfig | str] = <factory>)[source]
Bases:
BaseModelOne named output: a tap plus its ordered post-processing list.
- model_config = {'extra': 'forbid'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- post_processing: list[StageConfig | str]
- tap: StageConfig
- class telescope_sim.config.schema.PostProcessorConfig(*, type: str, **extra_data: Any)[source]
Bases:
BaseModelEither
{type: <name>, ...payload}or just a bare string for no-arg processors.- model_config = {'extra': 'allow'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class telescope_sim.config.schema.PupilConfig(*, resolution: int, extent: float)[source]
Bases:
BaseModel- model_config = {'extra': 'forbid'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class telescope_sim.config.schema.SimConfig(*, pupil: ~telescope_sim.config.schema.PupilConfig, aperture: ~telescope_sim.config.schema.StageConfig, correctors: dict[str, ~telescope_sim.config.schema.CorrectorConfig] = <factory>, corrector_chain: list[str] = <factory>, coronagraph: ~telescope_sim.config.schema.StageConfig | None = None, focal_planes: dict[str, ~telescope_sim.config.schema.StageConfig], outputs: dict[str, ~telescope_sim.config.schema.OutputConfig], strehl_method: ~typing.Literal['peak', 'matched_filter'] | None = None, strehl_core_rad: float | None = None)[source]
Bases:
BaseModelTop-level simulation config.
- aperture: StageConfig
- coronagraph: StageConfig | None
- correctors: dict[str, CorrectorConfig]
- focal_planes: dict[str, StageConfig]
- model_config = {'arbitrary_types_allowed': True, 'extra': 'forbid'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- outputs: dict[str, OutputConfig]
- pupil: PupilConfig
- class telescope_sim.config.schema.StageConfig(*, type: str, **extra_data: Any)[source]
Bases:
BaseModelCommon shape:
{type: <name>, ...payload}.The
typefield selects which registered implementation to use; everything else is forwarded to its constructor.- model_config = {'extra': 'allow'}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
YAML loader — parses a config file, validates it, and builds the pipeline.
Resolves stage types via telescope_sim.registry, instantiates the
concrete classes, and assembles a telescope_sim.pipeline.TelescopeSim.
Triggering imports of the standard implementations as a side effect of importing this module ensures the registry is populated before any config is resolved.
- telescope_sim.config.loader.build(config: SimConfig) TelescopeSim[source]
Instantiate a TelescopeSim from a validated config.
- telescope_sim.config.loader.build_from_preset(name: str) TelescopeSim[source]
Resolve a preset name to a packaged YAML and build the pipeline.
- telescope_sim.config.loader.build_from_yaml(path: str | Path) TelescopeSim[source]
Convenience: load a YAML file and build the pipeline in one step.
Concrete implementations
Segmented-circular aperture: N circular sub-apertures arranged on a layout.
- Supported layouts:
“elf”: ring of sub-apertures at a given radius
“custom”: user-provided positions
This is the minimal mini-ELF / DASIE aperture builder the canonical-family fixtures use. Hexagonal / monolithic / external_pupil variants are separate implementations.
- class telescope_sim.apertures.segmented_circular.SegmentedCircularAperture(segment_diameter: float, layout: str = 'elf', n_segments: int | None = None, ring_radius: float | None = None, positions: NDArray[floating] | None = None, supersample: int = 16, spider: dict | None = None)[source]
Bases:
ApertureN circular sub-apertures on a configurable layout.
- Parameters:
segment_diameter (float) – Diameter of each sub-aperture in pupil-grid units.
layout (str) – “elf” or “custom”.
n_segments (int | None) – Number of segments (required for “elf”).
ring_radius (float | None) – Radius of the ELF ring (required for “elf”).
positions (numpy._typing._array_like.NDArray[numpy.floating] | None) – Explicit (N, 2) list of (x, y) coordinates (required for “custom”).
supersample (int) – Supersampling factor for aperture evaluation (default 16, matching the canonical 2024-09 implementation; older variants used 1).
spider (dict | None) – Optional dict
{width, angle}. If provided, two perpendicular spiders are multiplied into the aperture mask.angleis in degrees; the second spider is offset by 90°. Mirrors the canonical spider construction.
- build(pupil_grid) ApertureResult[source]
Construct the aperture on the given pupil grid.
- Parameters:
pupil_grid – HCIPy pupil grid (typically
make_pupil_grid(resolution, extent)).- Returns:
The transmission field, collecting area, and optional segmentation.
- Return type:
ExternalPupilAperture — wraps an arbitrary Python callable as an aperture.
Two calling conventions are supported via the mode field:
"field": the function returns an HCIPyFielddirectly when called with apupil_gridkeyword. Use for callables likemiles_pupil. generate_pupil(outer=..., pupil_grid=...)andmiles_synthpsf. generate_pupil(...)."callable": the function returns an HCIPy aperture callable when called with whatever kwargs are supplied; the pipeline then evaluates it on the pupil grid viaevaluate_supersampled. Use for callables likehcipy.make_keck_aperture()orhcipy.make_circular_aperture(D).
The module is imported via importlib; the path can be either a
standard dotted module name (my_package.my_module) or an absolute
filesystem path to a .py file (/path/to/file.py).
- class telescope_sim.apertures.external_pupil.ExternalPupilAperture(module: str = '', function: str = 'generate_pupil', mode: str = 'field', kwargs: dict[str, ~typing.Any]=<factory>, area: float = 0.0, supersample: int = 16)[source]
Bases:
ApertureAperture built by an external Python callable.
- Parameters:
module (str) – Dotted module name or filesystem path to a
.pyfile.function (str) – Name of the callable inside the module.
mode (str) –
"field"(default) or"callable"— see module docstring.kwargs (dict[str, Any]) – Extra kwargs forwarded to the callable.
area (float) – Effective collecting area (used for photon flux scaling). Optional; defaults to
0.0(only matters for noise computations).supersample (int) – Used only when
mode == "callable".
- build(pupil_grid: Any) ApertureResult[source]
Construct the aperture on the given pupil grid.
- Parameters:
pupil_grid – HCIPy pupil grid (typically
make_pupil_grid(resolution, extent)).- Returns:
The transmission field, collecting area, and optional segmentation.
- Return type:
Segmented piston/tip/tilt corrector — wraps HCIPy’s SegmentedDeformableMirror.
Actuator state has shape (n_segments, 3): piston (meters), tip-slope,
tilt-slope per segment. Caller-facing values are scaled by piston_scale
and tip_tilt_scale; internally the corrector multiplies through to get
the absolute surface deformation HCIPy expects.
This corrector is the primary “actuate” or “impose” element for the
canonical-family fixtures (mini-ELF and similar segmented designs). It
supports the role-based wavefront / target-strategy system documented on
telescope_sim.abc.Corrector.
- class telescope_sim.correctors.segmented_ptt.SegmentedPTTCorrector(segments: Any, segment_coords: NDArray[floating], *, name: str = 'segments', piston_scale: float = 1e-06, tip_tilt_scale: float = 1e-06, wavefront_role: Literal['actuate', 'impose', 'fit'] = 'actuate', target_strategy: Literal['none', 'actuators', 'actuators_plus_residual_fit', 'residual_fit_only'] = 'none', fit_source: str | None = None, target: bool = False)[source]
Bases:
CorrectorPer-segment piston/tip/tilt actuator on top of HCIPy’s SegmentedDeformableMirror.
Construct from an aperture build result that includes segments (
ApertureResult.segments) and segment coordinates.- property actuators: NDArray
Return the caller-facing (n_segments, 3) PTT values.
HCIPy stores actuators in block layout
[p_0..p_{n-1}, t_0..t_{n-1}, T_0..T_{n-1}](not row-major); de-interleave back to the per-segment(p, t, T)rows callers expect.
- fit_surface(phase: NDArray[floating]) NDArray[floating][source]
Per-segment least-squares fit of piston/tip/tilt to a pupil-plane OPD.
Mirrors the canonical v1
_measure_atmos_ptt(seeCorrector.fit_surfacefor the convention). Input is OPD in meters; for each segment, fitsOPD ≈ p + t_x*x + t_y*yover the segment’s pixels. Returns matching caller-facing PTT — an(n_segments, 3)array divided bypiston_scale/tip_tilt_scaleand by 2 (surface→OPD round-trip).The aperture-masked mean of the input is subtracted before the per-segment lstsq (idempotent with the post-fit per-segment mean-piston subtraction below, but keeps the convention uniform with
ZernikeCorrector.fit_surface).
Zernike-basis deformable mirror corrector.
Wraps HCIPy’s DeformableMirror over a Zernike mode basis with the
per-mode normalization the VAMPIRES-family variants use (each mode is
divided by its peak absolute value so caller-facing actuator amplitudes
are in units of max-mode-amplitude × actuate_scale).
Caller-facing actuator state has shape (n_modes,). The corrector
multiplies caller values by actuate_scale before handing them to the
underlying HCIPy DM.
- class telescope_sim.correctors.zernike.ZernikeCorrector(n_modes: int, zernike_diameter: float, *, starting_mode: int = 2, actuate_scale: float = 1.0, name: str = 'zernike_dm', wavefront_role: Literal['actuate', 'impose', 'fit'] = 'actuate', target_strategy: Literal['none', 'actuators', 'actuators_plus_residual_fit', 'residual_fit_only'] = 'none', fit_source: str | None = None, target: bool = False)[source]
Bases:
CorrectorZernike-mode deformable mirror.
- Parameters:
n_modes – Number of Zernike modes (Noll indexing; starts at
starting_mode).zernike_diameter – Diameter (pupil-plane units) over which the Zernike basis is defined.
starting_mode – First Noll index in the basis (default 2, skipping piston).
actuate_scale – Multiplicative factor applied to caller actuator values before they are written to the underlying HCIPy DM.
- property actuators: NDArray
- fit_surface(phase: NDArray[floating]) NDArray[floating][source]
Project a pupil-plane OPD onto the Zernike basis.
Input is OPD in meters (path-length); output is caller-facing actuator amplitudes that reproduce that OPD as this DM’s surface contribution (matching, not cancellation — see
Corrector.fit_surface()for the convention). Modes are peak-normalized; this is the standard diagonal projection, which is exact when the basis is orthogonal over the aperture footprint (e.g. a clean circular aperture).The
/ 2.0is the surface→OPD round-trip factor: setting actuator valuevproduces surfacev * actuate_scalebut contributes2 * v * actuate_scaleto the OPD.Defensive: the aperture-masked mean of the input is subtracted before the lstsq. Uniform OPD offsets are unobservable in the PSF, but a non-zero-mean input (atmosphere phase screen, fit-source corrector surface, etc.) would otherwise be absorbed into non-piston modes, distorting them and polluting any ML target derived from the fit.
Actuator-grid deformable mirror corrector.
Wraps HCIPy’s DeformableMirror over an N×N grid of influence
functions (make_gaussian_influence_functions or
make_xinetics_influence_functions), driven by raw per-actuator
commands. DM misalignment relative to the pupil — rotation and
mirrored actuator indexing — is baked in at construction so a
simulated bench can reproduce the command-to-surface mapping of a
physically mounted DM.
Caller-facing actuator state has shape (N²,) (a shaped (N, N)
command array is accepted equivalently). The corrector multiplies
caller values by actuate_scale before handing them to the
underlying HCIPy DM, so callers command in their own units (e.g. the
bench’s raw DM units) and actuate_scale carries the
units→meters-of-surface calibration.
Command-array and misalignment conventions
A shaped
(N, N)command indexes the actuator lattice ascmd[iy, ix]: axis 0 walks the y coordinate ascending, axis 1 walks x ascending. Flattening is row-major, matching HCIPy’s actuator-position ordering (x varies fastest). The lattice is centered on the optical axis with spacingactuator_pitch.flip_x/flip_ymirror the command indexing (fliplr/flipudof the shaped command) before it reaches the DM — the simulated analogue of a mirrored cable/mapping between the command array and the physical actuators. Theactuatorsreadback un-applies the flips, so callers always round-trip their own values.rotation_degrotates the influence-function geometry: positive values rotate the DM counterclockwise relative to the pupil (x right, y up) as seen in a plotted surface. Composition order is flip-then-rotate: the command array is mirrored first, then the rotated DM renders it.
- class telescope_sim.correctors.actuator_grid.ActuatorGridCorrector(num_actuators: int, actuator_pitch: float, *, influence: str = 'gaussian', crosstalk: float = 0.15, rotation_deg: float = 0.0, flip_x: bool = False, flip_y: bool = False, actuate_scale: float = 1.0, name: str = 'actuator_dm', wavefront_role: Literal['actuate', 'impose', 'fit'] = 'actuate', target_strategy: Literal['none', 'actuators', 'actuators_plus_residual_fit', 'residual_fit_only'] = 'none', fit_source: str | None = None, target: bool = False)[source]
Bases:
CorrectorN×N actuator-grid deformable mirror with influence functions.
- Parameters:
num_actuators – Actuators per side; the DM has
num_actuators²total.actuator_pitch – Actuator spacing in pupil-plane projected units (meters for a metric pupil grid).
influence – Influence-function model:
"gaussian"(Gaussian pokes with nearest-neighbourcrosstalk) or"xinetics"(HCIPy’s measured Xinetics actuator shape).crosstalk – Influence-function value at a nearest-neighbour actuator. Only meaningful for
influence="gaussian"; ignored for"xinetics"(whose shape is fixed by measurement).rotation_deg – DM orientation relative to the pupil. Positive values rotate the DM counterclockwise (x right, y up) as seen in a plotted surface.
flip_x – Mirror the command-array indexing along x (
fliplr) / y (flipud) before commands reach the DM. Applied before the rotation (flip-then-rotate).flip_y – Mirror the command-array indexing along x (
fliplr) / y (flipud) before commands reach the DM. Applied before the rotation (flip-then-rotate).actuate_scale – Multiplicative factor applied to caller actuator values before they are written to the underlying HCIPy DM (caller units → meters of surface).
- property actuators: NDArray
- fit_surface(phase: NDArray[floating]) NDArray[floating][source]
Least-squares-project a pupil-plane OPD onto the influence basis.
Input is OPD in meters (path-length); output is caller-facing actuator commands that reproduce that OPD as this DM’s surface contribution (matching, not cancellation — see
Corrector.fit_surface()for the convention). The fit is an aperture-masked regularized least squares onto the (rotated) influence functions; command flips are un-applied on the way out, soset_actuators(fit_surface(opd))reproduces the fit regardless of the configured misalignment.The
/ 2.0is the surface→OPD round-trip factor: setting actuator valuevproduces surfacev * actuate_scalebut contributes2 * v * actuate_scaleto the OPD.The aperture-masked mean of the input is subtracted before the fit. A uniform OPD offset is unobservable in the PSF, and unlike a global phase the DM’s approximation of one is not: the influence basis only holds a flat to within a print-through ripple, so fitting the mean would leak
mean x rippleinto the corrected wavefront and pollute residual-fit ML targets with an arbitrary common-mode term. Piston is therefore never commanded.
Standard coronagraph implementations: identity, vortex, vector_vortex.
A coronagraph wraps an HCIPy coronagraph element and applies it after the corrector chain but before propagation. The pipeline always generates the reference PSF (for Strehl normalization) with the coronagraph bypassed.
Each implementation takes an optional lyot Aperture config that
specifies the downstream Lyot stop’s transmission field.
- class telescope_sim.coronagraphs.standard.IdentityCoronagraph(**_: Any)[source]
Bases:
CoronagraphPassthrough — used as the “no coronagraph” placeholder.
- class telescope_sim.coronagraphs.standard.VectorVortexCoronagraphImpl(charge: int = 4, lyot: dict | None = None)[source]
Bases:
CoronagraphHCIPy VectorVortexCoronagraph at a configurable charge.
- class telescope_sim.coronagraphs.standard.VortexCoronagraphImpl(charge: int = 2, lyot: dict | None = None)[source]
Bases:
CoronagraphHCIPy VortexCoronagraph at a configurable charge.
The Lyot stop is built from an
Aperturesub-config; the coronagraph is constructed lazily once the pipeline binds a pupil grid via_bind_pupil_grid().
Angular focal plane: arcsec-extent focal grid with FraunhoferPropagator.
This is the canonical-family focal plane: extent in arcsec, broadband
sampling via N monochromatic wavefronts across a fractional bandwidth.
The reference PSF (at-rest, no actuators, no atmosphere) is built once at
construction time and exposed via reference_psf,
reference_peak_intensity, and reference_psf_sum.
Detector noise is currently deferred; we’ll add a NoisyDetector path when the canonical fixtures that exercise it land. The canonical-family fixtures #01/#02/#10/#11 don’t use detectors.
- class telescope_sim.focal_planes.angular.AngularFocalPlane(*, central_lam: float, focal_extent: float, focal_res: int, fractional_bandwidth: float = 0.0, num_samples: int = 1, name: str = 'filter')[source]
Bases:
FocalPlaneOne filter on an arcsec-extent focal grid.
- Parameters:
central_lam – Central wavelength in meters.
focal_extent – Full focal-plane extent in arcsec (square).
focal_res – Number of pixels per side (square).
fractional_bandwidth – Width of the broadband sampling range, fraction of central_lam.
num_samples – Number of monochromatic wavelength samples across the bandwidth.
name (str) – Filter name, used as the wavefront name in the chain (e.g.
"focal:H_band").
- build(pupil_grid: Any, aperture_field: Any) None[source]
Build the propagator, wavefronts, and reference PSF for this filter.
- compute_reference_psf(corrector_chain: list[Any]) None[source]
Walk the chain at-rest (no coronagraph) for normalization/strehl.
- property lam_setup: _LamSetup
Physical focal plane — focal-plane grid in physical units (meters).
Used when the focal-plane physics depends on absolute spot size rather
than angular extent (e.g. fiber coupling, where the MMF mode field is
defined in physical coordinates). Construction requires an explicit
focal_length so the FraunhoferPropagator can convert between pupil
and focal-plane geometries.
This focal plane retains the per-wavelength focal-plane wavefronts so
downstream taps (notably fiber_coupled) can use the full complex
field rather than just summed intensity.
- class telescope_sim.focal_planes.physical.FocalPlaneResult(intensity: NDArray[floating], wavefronts: list[Any])[source]
Bases:
objectPer-sample focal-plane output retained for downstream taps.
- class telescope_sim.focal_planes.physical.PhysicalFocalPlane(*, central_lam: float, focal_extent: float, focal_res: int, focal_length: float, fractional_bandwidth: float = 0.0, num_samples: int = 1, wavefront_total_power: float | None = None, name: str = 'filter')[source]
Bases:
FocalPlaneFocal-plane grid in physical (metres) units.
- Parameters:
central_lam – Central wavelength in meters.
focal_extent – Full focal-plane extent in meters (square).
focal_res – Number of pixels per side.
focal_length – Optical focal length (meters); passed to
FraunhoferPropagator.fractional_bandwidth – Broadband sampling — same semantics as
AngularFocalPlane.num_samples – Broadband sampling — same semantics as
AngularFocalPlane.
- property lam_setup: _PhysicalLamSetup
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)).
- class telescope_sim.outputs.intensity.IntensityOutputTap(focal_plane_names: list[str], *, name: str = 'psf')[source]
Bases:
OutputTapStack 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 (str) – Output name (used as the key in
sample()’s returnedimagesdict).
- extract(fp_results: Any, *, overrides: dict[str, Any] | None = None) NDArray[floating][source]
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.overridesis accepted for ABC compatibility and ignored — this tap has no per-sample state.
FiberDualOutputTap — dual-stack focal intensity + fiber-coupled intensity.
For dual-output fiber experiments (e.g. fixture #15): runs each focal-plane
wavefront through a multi-mode fiber (HCIPy StepIndexFiber) and produces
np.stack([focal_intensity, fiber_intensity]) summed over wavelengths.
Returns shape (2, H, W, 1) matching the legacy fiber variant — first
axis is [focal, mmf], last axis is the (single) filter channel.
- class telescope_sim.outputs.fiber_dual.FiberDualOutputTap(focal_plane_name: str, fiber: dict, *, name: str = 'fiber_dual')[source]
Bases:
OutputTapStack focal-plane intensity and fiber-coupled intensity along axis 0.
- Parameters:
focal_plane_name – Name of the (physical) focal plane to consume.
fiber – Sub-config for the fiber:
{type, ...kwargs}. Currently only"step_index"is supported.name (str) – Output name (key in
sample()['images']).
- extract(fp_results: Any, *, overrides: dict[str, Any] | None = None) NDArray[floating][source]
Return a numpy array representing this tap’s view of the wavefront.
- Parameters:
wf – The tap’s input — typically the
fp_resultsdict mapping focal- plane name toFocalPlaneResult.overrides – Per-sample tap-config overrides from
sim.sample(output_overrides=...). Most taps ignore this; taps that have per-sample state (e.g. photon flux for a noisy detector) read named fields here.
Normalization post-processors used by the canonical-family fixtures.
- class telescope_sim.post.normalization.ChannelsFirst[source]
Bases:
PostProcessorTranspose
(H, W, C)→(C, H, W)for PyTorch-style consumers.
- class telescope_sim.post.normalization.MaxImageNorm[source]
Bases:
PostProcessorDivide each channel by its own max value.
Mirrors the coronagraph-variant max_im_norm flag — useful when the per-image dynamic range varies a lot (e.g. coronagraph variants where the reference PSF peak isn’t a meaningful normalization target).
- class telescope_sim.post.normalization.MaxIntensityNorm[source]
Bases:
PostProcessorDivide each channel by the corresponding reference PSF peak intensity.
Mirrors the canonical max_inten_norm flag — the per-filter peak of the at-rest reference PSF is precomputed by the pipeline; this transform divides each channel by that scalar. Channels here = last axis of an
(H, W, C)image stack, where C corresponds to the focal planes listed in the parent IntensityOutputTap.
- class telescope_sim.post.normalization.PerSampleNorm[source]
Bases:
PostProcessorMin-max normalize each channel to [0, 1].
NoisyDetector post-processor.
Refactored in v2.0.0a9 from the v2.0.0a7 NoisyIntensityOutputTap. The
math is unchanged — single-call Detector.integrate(power_field, dt=1)
with the field built from intensity * grid.weights and optionally
rescaled to flux * area. The move to a post-processor lets noise
compose with the new convolve_image processor (convolve first, then
apply noise to the convolved image — physically correct ordering).
Per-sample override flow: the pipeline populates
PipelineContext.overrides with output_overrides[output_name]
before invoking each post-processor in the chain. This processor reads
int_phot_flux from the overrides dict, falling back to the YAML
default.
Loader dependencies (focal grid + aperture area) are injected via
_bind_loader_dependencies at sim-build time. Eagerly constructs the
underlying hcipy.NoisyDetector so its flat_field setter’s
np.random.normal(...) call burns RNG state BEFORE any user-level
np.random.seed() reset — matches legacy sampler-init timing.
Single-focal-plane only: the post-processor needs one focal grid, so multi-channel outputs that want noise must split into one output per filter.
- class telescope_sim.post.noisy_detector.NoisyDetectorPostProcessor(*, int_phot_flux: float | None = None, detector: dict[str, Any] | None = None, clamp_nonnegative: bool = True)[source]
Bases:
PostProcessor,LoaderBindableApply HCIPy
NoisyDetectornoise to an intensity image.- Parameters:
int_phot_flux – Photon flux in photons/m². When set, rescales the input image so total accumulated charge =
int_phot_flux * aperture_area(the legacy _addNoiseToObservation contract).Noneintegrates the input at its natural scale — useful when an upstreamconvolve_imagepost-processor has already imposed an absolute brightness.detector – Sub-config forwarded to
hcipy.NoisyDetector:read_noise,dark_current_rate,flat_field,include_photon_noise,subsampling.clamp_nonnegative – If
True, applynp.absto the read-out image. Mirrors the legacy_addNoiseToObservationworkaround for Gaussian read noise pulling pixels negative. Disable for science-faithful analyses.
Convolve-with-image post-processor.
Closes the convolve_im feature gap from the v2.0.0a6 audit.
Legacy behavior (multi_aperture_psf.py:489-491):
- if convolve_im is not None:
- out_samp = fftconvolve(convolve_im, psf / lam_setup[‘ref_psf_sum’],
mode=’same’)
- else:
out_samp = psf
The PSF is normalized by the at-rest reference’s sum (so the kernel
integrates to ~1, preserving the input image’s flux scale), then
scipy.signal.fftconvolve(image, kernel, mode='same') produces an
output with the same shape as the input image.
v2 ships this as a post-processor (rather than a tap option) so it
composes cleanly with the new noisy_detector post-processor:
intensity → convolve → noise is the physical chain. Per-sample image
override via the PipelineContext.overrides channel.
Single-focal-plane only — the kernel normalization needs a single
reference_psf_sum. Multi-channel outputs that want convolution must
split into one output per filter.
- class telescope_sim.post.convolve.ConvolveImagePostProcessor(*, image: Any = None)[source]
Bases:
PostProcessor,LoaderBindableConvolve a PSF image with a caller-supplied scene.
- Parameters:
image – Default scene to convolve with. 2D
ndarray.Nonemeans no default — the caller must supplyconvolve_imageviaoutput_overridesper sample.
Strehl ratio estimators — exactly mirroring the legacy _strehl.
Two methods, selected by SimConfig.strehl_method:
"peak": legacypsf.flat[ref_psf.argmax()] / ref_psf.max(). Reads the current PSF at the reference PSF’s argmax — so a tip-tilt that moves the peak off-center correctly drops Strehl below 1."matched_filter": legacysum(psf_core * ref_core) / sum(ref_core**2). A matched-filter projection over a circular core mask centered on the focal-grid origin. Pixels near the reference peak are upweighted by the reference intensity itself.
The legacy code precomputed all reference-PSF-derived quantities
(argmax, mask, weighted sums) once per filter at __init__. We do the
same with frozen dataclass estimators built in
build_strehl_estimator(). The per-sample compute() is then
O(1) for peak and O(core_pixels) for matched-filter — no recomputed
argmax or mask.
- class telescope_sim.strehl.StrehlEstimator(*args, **kwargs)[source]
Bases:
ProtocolAnything with a cached state and a .compute(psf) -> float method.
- telescope_sim.strehl.build_strehl_estimator(method: str, reference_psf: NDArray[floating], focal_grid: Any, core_radius_rad: float | None) StrehlEstimator[source]
Build the cached estimator for the configured method.
Called once per focal plane at pipeline construction, after the at-rest reference PSF has been captured.
- Parameters:
method –
"peak"or"matched_filter".reference_psf – The at-rest reference PSF (full 2D array).
focal_grid – The HCIPy focal grid (
.xand.yflat coordinate arrays). Required formatched_filter; ignored forpeak.core_radius_rad – Radius of the core mask in radians, measured from the focal-grid origin. Required for
matched_filter; ignored forpeak.
Helpers
Diagnostic plotting helpers for tutorials and interactive use.
These are convenience wrappers — nothing here is on the simulation hot path. Functions here adopt a single visual convention: every PSF panel is paired with the cumulative pupil-plane OPD that produced it, both with colorbars.
- telescope_sim.helpers.diagnostics.plot_opd_and_psfs(sim: Any, out: dict[str, Any], *, log_dynamic_range: float = 1e-08, suptitle: str | None = None, figsize: tuple[float, float] | None = None) Any[source]
Plot cumulative pupil OPD (left) and per-filter PSFs (right) side-by-side.
Adopts the recommended diagnostic convention: every PSF is shown next to the wavefront state that produced it. The OPD panel uses
hcipy.imshow_field()with the aperture as mask; PSFs usematplotlib.colors.LogNormso the colorbar labels carry the actual intensity values. Every panel gets a colorbar.- Parameters:
sim –
TelescopeSiminstance. Used forsim.aperture.field(mask) andsim.focal_planes(filter labels).out – Dict returned by
sim.sample(meas_pupil_opd=True). Must containimages["psf"]andpupil_opd. Ifstrehlsis also present (i.e.meas_strehl=True), Strehl values annotate the per-filter titles.log_dynamic_range – Ratio of
vmintovmaxfor the PSF colormap. Default1e-8(eight decades of dynamic range).suptitle – Optional figure suptitle.
figsize – Override the auto-computed figure size. Default is
(4 * (1 + n_filters), 4).
- Returns:
The constructed figure (not closed); caller can
plt.show()or further customize.- Return type:
matplotlib.figure.Figure
Registry
Tiny dict-of-dicts registry for pluggable pipeline stages.
Usage:
from telescope_sim.registry import register
from telescope_sim.abc import Aperture
@register("aperture", "segmented_circular")
class SegmentedCircularAperture(Aperture):
...
cls = registry["aperture"]["segmented_circular"]
Users may register their own implementations in their own packages without modifying telescope-sim.
- telescope_sim.registry.available(kind: str) list[str][source]
List registered implementation names for a given kind.