PSFs

PSFs in prysm have a very simple constructor;

>>> import numpy as np
>>> from prysm import PSF
>>> x = y = np.linspace(-1,1,128)
>>> z = np.random.random((128,128))
>>> ps = PSF(data=z, unit_x=x, unit_y=y)

PSFs are usually created from a pupil instance in a model, but the constructor can be used with e.g. experimental data.

>>> ps = PSF.from_pupil(your_pupil)

The encircled energy can be computed, for either a single point or an iterable (tuple, list, numpy array, …) of points;

>>> print(ps.encircled_energy(0.1), ps.encircled_energy([0.1, 0.2, 0.3])
12.309576159990891, array([12.30957616, 24.61581586, 36.92244558])

encircled energy is computed via the method described in V Baliga, B D Cohn, “Simplified Method For Calculating Encircled Energy,” Proc. SPIE 0892, Simulation and Modeling of Optical Systems, (9 June 1988).

The inverse can also be computed using the L-BFGS-B nonlinear optimization routine, but the wavelength and F/# must be provided for the initial guess,

>>> ps.wavelength, ps.fno = 0.5, 2
>>> print(ps.ee_radius(1))
0.008104694339936169

Baliga’s method is relatively slow for large arrays, so a dictionary is kept of all computed encircled energies at ps._ee. The encircled energy can be plotted. An axis limit must be provided if no encircled energy values have been computed. If some have, by default prysm will plot the computed values if no axis limit is given

>>> ps.plot_encircled_energy()

or

>>> ps.plot_encircled_energy(axlim=1, npts=50)

The PSF can be plotted in 2D,

>>> # ~0.838, exact value of energy contained in first airy zero
>>> from prysm.psf import FIRST_AIRY_ENCIRCLED
>>> ps.plot2d(axlim=0.8, power=2, interp_method='sinc', pix_grid=0.2, show_axlabels=True, show_colorbar=True, circle_ee=FIRST_AIRY_ENCIRCLED)

Both plot_encircled_energy and plot2d take the usual fig and ax kwargs as well. For plot2d, the axlim arg sets the x and y axis limits to symmetrical values of axlim, i.e. the limits above will be [0.8, 0.8], [0.8, 0.8]. power controls the stretch of the color scale. The image will be stretched by the 1/power power, e.g. 2 plots psf^(1/2). interp_method is passed to matplotlib. pix_grid will use the minor axis ticks to draw a light grid over the PSF, intended to show the size of a PSF relative to the pixels of a detector. Units of microns. show_axlabels and show_colorbar both default to True, and control whether the axis labels are set and if the colorbar is drawn. circle_ee will draw a dashed circle at the radius containing the specified portion of the energy, and another at the diffraction limited radius for a circular aperture.

PSFs are a subclass of Convolvable and inherit all methods and attributes. The complete documentation is below.


class prysm.psf.PSF(data, unit_x, unit_y)[source]

Bases: prysm.convolution.Convolvable

A Point Spread Function.

center_x : int
center sample along x
center_y : int
center sample along y
data : numpy.ndarray
PSF normalized intensity data
sample_spacing : float
center to center spacing of samples
unit_x : numpy.ndarray
x Cartesian axis locations of samples, 1D ndarray
unit_y numpy.ndarray
y Cartesian axis locations of samples, 1D ndarray
ee_radius(energy=0.8377850436212378)[source]
ee_radius_diffraction(energy=0.8377850436212378)[source]
ee_radius_ratio_to_diffraction(energy=0.8377850436212378)[source]
encircled_energy(radius)[source]

Compute the encircled energy of the PSF.

radius : float or iterable
radius or radii to evaluate encircled energy at
encircled energy
if radius is a float, returns a float, else returns a list.

implementation of “Simplified Method for Calculating Encircled Energy,” Baliga, J. V. and Cohn, B. D., doi: 10.1117/12.944334

static from_pupil(pupil, efl, Q=2)[source]

Use scalar diffraction propogation to generate a PSF from a pupil.

pupil : Pupil
Pupil, with OPD data and wavefunction
efl : int or float
effective focal length of the optical system
Q : int or float
ratio of pupil sample count to PSF sample count; Q > 2 satisfies nyquist
PSF
A new PSF instance
plot2d(axlim=25, power=1, clim=(None, None), interp_method='lanczos', pix_grid=None, invert=False, fig=None, ax=None, show_axlabels=True, show_colorbar=True, circle_ee=None, circle_ee_lw=None)[source]

Create a 2D plot of the PSF.

axlim : float
limits of axis, symmetric. xlim=(-axlim,axlim), ylim=(-axlim, axlim)
power : float
power to stretch the data by for plotting
clim : iterable
limits to use for log color scaling. If power != 1 and clim != (None, None), clim (log axes) takes precedence
interp_method : string
method used to interpolate the image between samples of the PSF
pix_grid : float
if not None, overlays gridlines with spacing equal to pix_grid. Intended to show the collection into camera pixels while still in the oversampled domain
invert : bool, optional
whether to invert the color scale
fig : matplotlib.figure.Figure, optional:
Figure containing the plot
ax : matplotlib.axes.Axis, optional:
Axis containing the plot
show_axlabels : bool
whether or not to show the axis labels
show_colorbar : bool
whether or not to show the colorbar
circle_ee : float, optional
relative encircled energy to draw a circle at, in addition to diffraction limited airy radius (1.22*λ*F#). First airy zero occurs at circle_ee=0.8377850436212378
circle_ee_lw : float, optional
linewidth passed to matplotlib for the encircled energy circles
fig : matplotlib.figure.Figure, optional
Figure containing the plot
ax : matplotlib.axes.Axis, optional
Axis containing the plot
plot_encircled_energy(axlim=None, npts=50, lw=3, fig=None, ax=None)[source]

Make a 1D plot of the encircled energy at the given azimuth.

azimuth : float
azimuth to plot at, in degrees
axlim : float
limits of axis, will plot [0, axlim]
npts : int, optional
number of points to use from [0, axlim]
lw : float, optional
linewidth provided directly to matplotlib
fig : matplotlib.figure.Figure, optional
Figure containing the plot
ax : matplotlib.axes.Axis, optional:
Axis containing the plot
fig : matplotlib.figure.Figure, optional
Figure containing the plot
ax : matplotlib.axes.Axis, optional:
Axis containing the plot