Zernikes

Prysm supports several flavors of Zernike polynomial: - Fringe - Noll - ANSI “j” - ANSI “n,m”

The single index notations have identical interfaces, while the (n,m) notation is slightly different.

Zernike notations are a subclass of Pupil, so they support the same arguments to __init__,

[1]:
%matplotlib inline
from prysm import FringeZernike, NollZernike, ANSI1TermZernike, ANSI2TermZernike
p = FringeZernike(samples=123, dia=456.7, wavelength=1.0, z_unit='nm', phase_mask='dodecagon')

There are additional keyword arguments for each term. The polynomials are orthogonal, having unit amplitude (zero-to-peak). They can also be used with unit variance via the norm keyword argument, in which case they are orthonormal. The polynomial classes have friendly print statements:

[2]:
p2 = FringeZernike(Z4=1, Z9=1, Z98=1, norm=True)
print(p2)
rms normalized Fringe Zernike description with:
        +1.000 Z4 - Defocus
        +1.000 Z9 - Primary Spherical
        +1.000 Z98 - 8th Coma Y
        11.758 PV, 1.718 RMS [nm]

Notice that we include a 98th term which you likely will not find in most other programs, and that the RMS value is equal to sqrt(1^2 + 1^2 + 1^2) = sqrt(3) = 1.718 ~= 1.722. The difference is due to the array sizes used by prysm by default, if increased, e.g. by adding samples=1204 to the constructor, the value would converge to the analytical one.

A Zernike pupil can also be initalized with an iterable (list, tuple…) of coefficients,

[3]:
import numpy as np
terms = np.random.rand(49)  # 49 zernike coefficients, e.g. from a wavefront sensor
fz3 = FringeZernike(terms)

Zernike instances have a truncate method which discards terms with indices higher than n. For example,

[4]:
fz3.truncate(16)
print(fz3)
Fringe Zernike description with:
        +0.955 Z1 - Piston
        +0.879 Z2 - Tilt Y
        +0.776 Z3 - Tilt X
        +0.705 Z4 - Defocus
        +0.732 Z5 - Primary Astigmatism 00°
        +0.257 Z6 - Primary Astigmatism 45°
        +0.600 Z7 - Primary Coma Y
        +0.698 Z8 - Primary Coma X
        +0.266 Z9 - Primary Spherical
        +0.590 Z10 - Primary Trefoil Y
        +0.114 Z11 - Primary Trefoil X
        +0.019 Z12 - Secondary Astigmatism 00°
        +0.990 Z13 - Secondary Astigmatism 45°
        +0.390 Z14 - Secondary Coma Y
        +0.257 Z15 - Secondary Coma X
        +0.549 Z16 - Secondary Spherical
        +0.704 Z17 - Primary Quadrafoil 00°
        67.421 PV, 5.584 RMS [nm]
[5]:
fz3.top_n(5)
[5]:
[(0.9897696655966812, 13, 'Secondary Astigmatism 45°'),
 (0.954654808201971, 1, 'Piston'),
 (0.8788744913189528, 2, 'Tilt Y'),
 (0.7757020181441167, 3, 'Tilt X'),
 (0.7323050708346519, 5, 'Primary Astigmatism 00°')]

or the terms listed by their pairwise magnitudes and clocking angles,

[6]:
fz3.magnitudes
[6]:
{'Piston': (0.954654808201971, 0),
 'Tilt': (1.1722346149316711, 48.5681054763325),
 'Defocus': (0.7053046706124283, 0),
 'Primary Astigmatism': (0.7761654526285778, 70.64632985233843),
 'Primary Coma': (0.9199310338976827, 40.672119822341074),
 'Primary Spherical': (0.26631457878325804, 0),
 'Primary Trefoil': (0.6006862836883362, 79.04020898600335),
 'Secondary Astigmatism': (0.9899512332479693, 1.0973789004428935),
 'Secondary Coma': (0.4672182811703899, 56.59991456593365),
 'Secondary Spherical': (0.5485796066693523, 0),
 'Primary Quadrafoil': (0.7043664001503012, 0)}

Magnitudes is a dict keyed by the name with values of (mag, ang). The angle is always zero if the term is rotationally invariant.

These things may be (bar) plotted;

[7]:
fz3.barplot(orientation='h', buffer=1, zorder=3)
fz3.barplot_magnitudes(orientation='h', buffer=1, zorder=3)
fz3.barplot_topn(n=5, orientation='h', buffer=1, zorder=3)
[7]:
(<Figure size 432x288 with 1 Axes>,
 <AxesSubplot:ylabel='OPD [$\\mathrm{nm}$]'>)
../_images/user_guide_Zernikes_13_1.png
../_images/user_guide_Zernikes_13_2.png
../_images/user_guide_Zernikes_13_3.png

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, the first few polynomials are provided as explicit functions and can be imported:

[8]:
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.

Zernike functions are cached by prysm’s internal routines,

[9]:
from prysm.zernike import zcachemn
# 8 is the index into prysm.zernike.zernikes, which loosely follows the Fringe ordering
zcachemn(7, 7, norm=True, samples=128)
zcachemn.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,

[10]:
from prysm.zernike import ZCacheMN
my_zcache = ZCacheMN()

See Pupils for information about general pupil functions.