Zernikes¶
Prysm supports two flavors of Zernike polynomials; the Fringe set up to the 49th term, and the Zemax Standard set up to the 48th term. They have identical interfaces, so only one will be shown here.
Zernike notations are a subclass of Pupil
, so they support the same arguments to __init__
;
>>> from prysm import FringeZernike, StandardZernike
>>> p = FringeZernike(samples=123, epd=456.7, wavelength=1.0, opd_unit='nm', mask='dodecagon')
There are additional keyword arguments for each term, and the base (0 or 1) can be supplied. With base 0, the terms start at Z0 and range to Z48. With base 1, they start at Z1 and range to Z49 (or Z48, for Standard Zernikes). The Fringe set can also be used with unit RMS amplitude via the rms_norm
keyword argument. Both notations also have nice print statements.
>>> p2 = FringeZernike(Z1=1, Z9=1, Z48=1, base=0, rms_norm=True)
>>> print(p2)
rms normalized Fringe Zernike description with:
+1.000 Z1 - Tip (Y)
+1.000 Z9 - Primary Trefoil Y
+1.000 Z48 - Quinternary Spherical
13.300 PV, 1.722 RMS
Notice that the RMS value is equal to sqrt(1^2 + 1^2 + 1^2) = sqrt(3) = 1.732 ~= 1.722. The difference of ~1% is due to the array sizes used by prysm by default, if increased, e.g. by adding samples=1204
to the above p2 = FringeZernike(...)
line, the value would converge to the analytical one.
A Zernike pupil can also be initalized with an iterable (list) of coefficients,
>>> import numpy as np
>>> terms = np.random.rand(49) # 49 zernike coefficients, e.g. from a wavefront sensor
>>> fz3 = FringeZernike(terms)
FringeZernike has many features StandardZernike does not. At the module level are two functions,
>>> from prysm.fringezernike import fzname, fzset_to_magnitude_angle
fzname
takes an index and optional base (default 0) kwarg and returns the name of that term. fzset_to_magnitude_angle
takes a non-sparse iterable of fringe zernike coefficients, starting with piston, and returns a dictionary with keywords of terms (e.g. “Primary Astigmatism”) and items that are length 2 tuples of (magnitude, angle) where magnitude is in the same units as the zernike coefficients and the angle is in degrees. fzset_to_magnitude_angle
’s output format can be seen below on the example of FringeZernike.magnitudes
.
FringeZernike
instances have a truncate
method which discards terms with indices higher than n. For example,
>>> fz3.truncate(16)
this is less efficient, however, than simply slicing the coefficient vector,
>>> fz4 = FringeZernike(terms[:16])
and this slicing alternative should be used when one is sensitive to performance.
The top few terms may be extracted,
>>> fz4.top_n(5)
[(0.9819642202790644, 5, 'Defocus'),
(0.9591004803909723, 7, 'Primary Astigmatism 45°'),
(0.9495975015239123, 28, 'Primary Pentafoil X'),
(0.889828680566406, 8, 'Primary Coma Y'),
(0.8831549729997366, 21, 'Secondary Trefoil X')]
or the terms listed by their pairwise magnitudes and clocking angles,
>>> fz4.magnitudes
{'Piston': (0.7546621028832683, 90.0),
'Tilt': (0.6603839752967117, 26.67150180654403),
'Defocus': (0.14327809298942284, 90.0),
'Primary Astigmatism': (0.7219964602989639, 85.19763587918983),
'Primary Coma': (1.1351347586960325, 48.762867211696374),
...
'Quinternary Spherical': (0.4974741523638292, 90.0)}
These things may be (bar) plotted;
>>> fz4.barplot(orientation='h', buffer=1, zorder=3)
>>> fz4.barplot_magnitudes(orientation='h', buffer=1, zorder=3)
>>> fz4.barplot_topn(n=5, orientation='h', buffer=1, zorder=3)
orientation
controls the axis on which the terms are enumerated. h
results in vertical bars, v
is also accepted, as are horizontal
and vertical
. buffer
is the number of terms’ worth of spaces left on each side. The default of 1 leaves a one bar margin. zorder
is passed to matplotlib – the default of 3 places the bars above any gridlines, which is an aesthetic choice. Matplotlib has a general default of 1.
If you would like direct access to the underlying functions, there are two paths. prysm._zernike
contains functions for the first 49 (Fringe ordered) zernike polynomials, for example
>>> from prysm._zernike import defocus
each of these takes arguments of (rho, phi). They have names which end with _x
, or _00
and _45
for the terms which have that naming convention.
Perhaps more convenient is a dictionary which numbers them,
>>> from prysm._zernike import _zernikes
>>> from prysm.fringezernike import zernmap
>>> zfunction = _zernikes[zernmap[8]]
>>> zfunction, zfunction.name, zfunction.norm
<function prysm._zernike.primary_spherical(rho, phi)>, 'Primary Spherical', 2.23606797749979
If these will always be evaluted over a square region enclosing the unit circle, a cache is available to speed computation,
>>> from prysm.fringezernike import fcache
>>> fcache(8, norm=True, samples=128) # base 0
>>> fcache.clear() # you should never have to do this unless you want to release memory
This cache instance is used internally by prysm, if you modify the returned arrays in-place, you probably won’t like the result. You can create your own independent instance,
>>> from prysm.fringezernike import FZCache
>>> my_fzcache = FZCache()
See pupils for information about general pupil functions. Below, the Fringe type of Zernike description has its full documentation printed.
-
class
prysm.fringezernike.
FringeZernike
(*args, **kwargs)[source]¶ Bases:
prysm.pupil.Pupil
-
barplot
(orientation='h', buffer=1, zorder=3, fig=None, ax=None)[source]¶ Creates a barplot of coefficients and their names.
- orientation : str, {‘h’, ‘v’, ‘horizontal’, ‘vertical’}
- orientation of the plot
- buffer : float, optional
- buffer to use around the left and right (or top and bottom) bars
- zorder : int, optional
- zorder of the bars. Use zorder > 3 to put bars in front of gridlines
- fig : matplotlib.figure.Figure
- Figure containing the plot
- ax : matplotlib.axes.Axis
- Axis containing the plot
- fig : matplotlib.figure.Figure
- Figure containing the plot
- ax : matplotlib.axes.Axis
- Axis containing the plot
-
barplot_magnitudes
(orientation='h', sort=False, buffer=1, zorder=3, fig=None, ax=None)[source]¶ Create a barplot of magnitudes of coefficient pairs and their names.
E.g., astigmatism will get one bar.
- orientation : str, {‘h’, ‘v’, ‘horizontal’, ‘vertical’}
- orientation of the plot
- sort : bool, optional
- whether to sort the zernikes in descending order
- buffer : float, optional
- buffer to use around the left and right (or top and bottom) bars
- zorder : int, optional
- zorder of the bars. Use zorder > 3 to put bars in front of gridlines
- fig : matplotlib.figure.Figure
- Figure containing the plot
- ax : matplotlib.axes.Axis
- Axis containing the plot
- fig : matplotlib.figure.Figure
- Figure containing the plot
- ax : matplotlib.axes.Axis
- Axis containing the plot
-
barplot_topn
(n=5, orientation='h', buffer=1, zorder=3, fig=None, ax=None)[source]¶ Plot the top n terms in the wavefront.
- n : int, optional
- plot the top n terms.
- orientation : str, {‘h’, ‘v’, ‘horizontal’, ‘vertical’}
- orientation of the plot
- buffer : float, optional
- buffer to use around the left and right (or top and bottom) bars
- zorder : int, optional
- zorder of the bars. Use zorder > 3 to put bars in front of gridlines
- fig : matplotlib.figure.Figure
- Figure containing the plot
- ax : matplotlib.axes.Axis
- Axis containing the plot
- fig : matplotlib.figure.Figure
- Figure containing the plot
- ax : matplotlib.axes.Axis
- Axis containing the plot
-
build
()[source]¶ - Uses the wavefront coefficients stored in this class instance to
- build a wavefront model.
- self.phase : numpy.ndarray
- arrays containing the phase associated with the pupil
- self.fcn : numpy.ndarray
- array containing the wavefunction of the pupil plane
-
magnitudes
¶ Returns the magnitude and angles of the zernike components in this wavefront.
-
top_n
(n=5)[source]¶ Identify the top n terms in the wavefront.
- n : int, optional
- identify the top n terms.
- list
- list of tuples (magnitude, index, term)
-