Optics backends#

Optical systems behind one interface.

The spatial PSF machinery (toycoronagraph.psf, toycoronagraph.spatial) only asks an optical system for the image of a point source: its throughput, its total light, and its intensity on blocks of the image pixel grid. So a different coronagraph, or a starshade, can be added as a new backend without touching that machinery:

@register_backend('my_system')
class MySystemBackend:
    rotationally_symmetric = False  # True allows the fast 'rotate' mode
    bandlimit = None                # cycles per lambda/D of the intensity vs. source
                                    # position, if known (also needed by 'rotate')
    def __init__(self, optics): ...
    def source(self, x, y): ...     # returns an object with .throughput, .total,
                                    # .block(center, shape) and .frame()
    def unocculted(self): ...       # optional: the star without the starlight-suppression
                                    # element (same kind of object; the contrast_peak unit)

and then Optics(‘my_system’, img_pixel, psf_range, ((‘name’, value), …), module=’my.package’). The module is imported when the kind is not registered yet (e.g. in parallel worker processes). Two systems come with the package: Optics.vortex (hcipy; VortexBackend below) and Optics.lazuli (the Lazuli space telescope computed with prysm; toycoronagraph.lazuli, imported only when used).

Conventions shared by every backend:

* positions are in lambda/D, with the star at the origin; image pixel (row, col) is at
  x = (col - c) * pixel_scale, y = (row - c) * pixel_scale with c = (img_pixel - 1) / 2;
* intensities are in the package's units (hcipy intensity / normal_factor, as in
  radial_psf_series): each pixel holds the fraction of the source's light entering the
  aperture, so kernels, frames and totals are directly comparable;
* throughput is the source's total light relative to the same source without the
  starlight-suppression element.
class toycoronagraph.optics.Optics(kind, img_pixel, psf_range, params=(), module='toycoronagraph.optics')[source]#

Bases: object

Description of an optical system and of the image sampling (hashable, picklable).

Variables:
  • kind (str) – Name of a registered backend, e.g. ‘vortex’.

  • img_pixel (int) – Number of pixels on one edge of the image.

  • psf_range (float) – Half-width of the image in lambda/D.

  • params (tuple) – The backend’s parameters as sorted (name, value) pairs.

  • module (str) – Module that registers the backend (imported if needed).

classmethod vortex(charge, img_pixel, psf_range, lyot_mask_size=0.95)[source]#

An ideal vortex coronagraph with a circular aperture and a circular Lyot stop.

Parameters:
  • charge (int) – Vortex charge (topological charge of the focal-plane mask).

  • img_pixel (int) – Number of pixels on one edge of the image.

  • psf_range (float) – Half-width of the image, in lambda/D.

  • lyot_mask_size (float) – Lyot stop diameter relative to the aperture diameter.

Returns:

kind ‘vortex’ with params ((‘charge’, int), (‘lyot_mask_size’, float)).

Return type:

Optics

classmethod lazuli(charge, img_pixel, psf_range, wavelengths, weights, scene_wavelength, scene_diameter, pupil_samples, lyot_mask_size=0.95, opd='')[source]#

The Lazuli space telescope, alone or with an ideal vortex coronagraph, computed with prysm (toycoronagraph.lazuli; observe.optics_for builds it from a [[coronagraph]] table).

Parameters:
  • charge (int) – 0 for the telescope alone; else the vortex charge (even).

  • img_pixel (int) – Number of pixels on one edge of the image.

  • psf_range (float) – Half-width of the image, in scene lambda/D (scene_wavelength / scene_diameter).

  • wavelengths (sequence of float) – Wavelengths of the PSF, in m.

  • weights (sequence of float) – Relative weight of each wavelength (normalised here to sum 1).

  • scene_wavelength (float) – The scene’s wavelength in m ([observation] wavelength); with scene_diameter it sets the lambda/D unit of source positions and of psf_range.

  • scene_diameter (float) – The scene’s telescope diameter in m ([observation] diameter).

  • pupil_samples (int) – Pupil samples across the aperture (the OPD map’s size when there is one).

  • lyot_mask_size (float) – Lyot stop diameter relative to the aperture (used with a charge).

  • opd (str) – OPD map file (see lazuli.read_opd); “” for none. Stored as an absolute path, with the SHA-1 of the file (opd_sha1), so cached PSFs follow the map’s content.

Returns:

kind ‘lazuli’ (module ‘toycoronagraph.lazuli’); the wavelengths and weights are stored as tuples (hashable).

Return type:

Optics

param(name)[source]#

The value of one backend parameter.

Parameters:

name (str) – Parameter name, e.g. ‘charge’.

Returns:

The value stored in params (KeyError if there is none).

property pixel_scale#

Image pixel size, in lambda/D per pixel (2*psf_range/img_pixel).

Type:

float

property centre#

Pixel coordinate of the star on both axes, (img_pixel-1)/2.

Type:

float

backend()[source]#

The backend object of this system (cached per process; see get_backend).

settings()[source]#

Flat dict of plain values (for .npz files and cache checks).

Returns:

optics_kind, optics_module, img_pixel, psf_range, and ‘optics_<name>’ for every backend parameter.

Return type:

dict

classmethod from_settings(settings)[source]#

Rebuild from settings() (or from a saved spatial PSF model).

Parameters:

settings (dict) – Holds the keys of settings(); other keys are ignored. Values may be numpy arrays (as loaded from .npz): 0-d ones become Python scalars (.item()), 1-D ones tuples (e.g. the wavelengths of Optics.lazuli). optics_module defaults to ‘toycoronagraph.optics’.

Returns:

The described system (params sorted by name).

Return type:

Optics

exception toycoronagraph.optics.SamplingWarning[source]#

Bases: UserWarning

The pupil is sampled too coarsely for the PSFs’ far wings near the image edge (see MIN_SAMPLING).

toycoronagraph.optics.register_backend(kind)[source]#

Class decorator registering a backend under the given kind.

Parameters:

kind (str) – Name used as Optics.kind.

Returns:

The decorator; it returns the class unchanged.

Return type:

callable

toycoronagraph.optics.get_backend(optics)[source]#

The backend of an Optics, built once per process (each parallel worker keeps its own).

Parameters:

optics (Optics) – The system; its module is imported if its kind is not registered yet.

Returns:

The backend instance (e.g. VortexBackend).

Raises:

ValueError – If the kind is still unknown after importing optics.module.

class toycoronagraph.optics.PupilPlaneSource(backend, exit_field, throughput)[source]#

Bases: object

A point source behind a pupil-plane system: holds the field leaving the last pupil (e.g. after the Lyot stop) and focuses it onto any block of the image pixel grid.

Variables:

throughput (float) – The source’s light leaving the system relative to the same source without the starlight-suppression element.

Parameters:
  • backend – The backend (needs optics, normal_factor, pupil_grid and prop).

  • exit_field (hcipy.Wavefront) – Field leaving the last pupil, on backend.pupil_grid.

  • throughput (float) – See the class attributes.

property total#

Total light over the whole (infinite) focal plane (Parseval), in the package’s pixel units.

Type:

float

block(center, shape)[source]#

Intensity on a block of the image pixel grid (pixel size optics.pixel_scale).

Parameters:
  • center (tuple) – (x, y) of the block’s centre, in lambda/D from the star.

  • shape (tuple) – (ny, nx) size of the block, in pixels.

Returns:

(ny, nx) float64 intensity (rows = y, columns = x), in the package’s units.

Return type:

np.ndarray

frame()[source]#

np.ndarray: (img_pixel, img_pixel) float64 intensity on the whole image (= |field()|^2).

field()[source]#

np.ndarray: (img_pixel, img_pixel) complex field on the whole image (rows = y, columns = x).

class toycoronagraph.optics.VortexBackend(optics)[source]#

Bases: object

Ideal vortex coronagraph: circular aperture, vortex focal-plane mask, circular Lyot stop.

Rotationally symmetric, and a source enters only through a tilt across the pupil, so the intensity is band-limited in the source position at D/lambda: ‘rotate’ mode is valid.

Variables:
  • optics (Optics) – The system (kind ‘vortex’, params charge and lyot_mask_size).

  • pupil_grid – hcipy pupil grid, 2*img_pixel samples across 1.5 aperture diameters.

  • aperture – Circular aperture of diameter 1 on pupil_grid (4x supersampled edges).

  • prop (FraunhoferPropagator) – Pupil to the (img_pixel, img_pixel) image grid in lambda/D.

  • lyot_stop (Apodizer) – Circular Lyot stop of diameter lyot_mask_size.

  • coro (VortexCoronagraph) – The vortex mask (with its own internal propagation).

  • normal_factor (float) – Divisor turning hcipy intensities into the package’s units.

Parameters:

optics (Optics) – An Optics of kind ‘vortex’ (e.g. Optics.vortex(…)).

wavefront(x, y)[source]#

A point source at (x, y) lambda/D, as a tilted wavefront on the aperture.

Parameters:
  • x (float) – Source position, in lambda/D from the star (x along columns, y along rows).

  • y (float) – Source position, in lambda/D from the star (x along columns, y along rows).

Returns:

The aperture field with the source’s tilt, on pupil_grid.

Return type:

hcipy.Wavefront

source(x, y)[source]#

A point source at (x, y) lambda/D, propagated through the vortex and the Lyot stop.

Parameters:
  • x (float) – Source position, in lambda/D from the star.

  • y (float) – Source position, in lambda/D from the star.

Returns:

Its throughput is the light after the Lyot stop with the vortex relative to the light after the Lyot stop without it.

Return type:

PupilPlaneSource

unocculted()[source]#

The star without the vortex (the Lyot stop kept): the reference of the contrast_peak unit.

Returns:

The star through the aperture and the Lyot stop (throughput 1).

Return type:

PupilPlaneSource