{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Zernikes\n", "\n", "Prysm supports several flavors of Zernike polynomial:\n", "- Fringe\n", "- Noll\n", "- ANSI \"j\"\n", "- ANSI \"n,m\"\n", "\n", "The single index notations have identical interfaces, while the (n,m) notation is slightly different.\n", "\n", "Zernike notations are a subclass of [Pupil](./Pupils.ipynb), so they support the same arguments to `__init__`," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "from prysm import FringeZernike, NollZernike, ANSI1TermZernike, ANSI2TermZernike\n", "p = FringeZernike(samples=123, dia=456.7, wavelength=1.0, z_unit='nm', phase_mask='dodecagon')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "p2 = FringeZernike(Z4=1, Z9=1, Z98=1, norm=True)\n", "print(p2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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.\n", "\n", "A Zernike pupil can also be initalized with an iterable (list, tuple...) of coefficients," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "terms = np.random.rand(49) # 49 zernike coefficients, e.g. from a wavefront sensor\n", "fz3 = FringeZernike(terms)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zernike instances have a `truncate` method which discards terms with indices higher than n. For example," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fz3.truncate(16)\n", "print(fz3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fz3.top_n(5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "or the terms listed by their pairwise magnitudes and clocking angles," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fz3.magnitudes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Magnitudes is a dict keyed by the name with values of `(mag, ang)`. The angle is always zero if the term is rotationally invariant." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These things may be (bar) plotted;" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fz3.barplot(orientation='h', buffer=1, zorder=3)\n", "fz3.barplot_magnitudes(orientation='h', buffer=1, zorder=3)\n", "fz3.barplot_topn(n=5, orientation='h', buffer=1, zorder=3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`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.\n", "\n", "If you would like direct access to the underlying functions, the first few polynomials are provided as explicit functions and can be imported:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from prysm.zernike import defocus" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zernike functions are cached by prysm's internal routines," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from prysm.zernike import zcachemn\n", "# 8 is the index into prysm.zernike.zernikes, which loosely follows the Fringe ordering\n", "zcachemn(7, 7, norm=True, samples=128)\n", "zcachemn.clear() # you should never have to do this unless you want to release memory" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from prysm.zernike import ZCacheMN\n", "my_zcache = ZCacheMN()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See [Pupils](./Pupils.ipynb) for information about general pupil functions." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" } }, "nbformat": 4, "nbformat_minor": 2 }