Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
brandondube committed Oct 7, 2019
2 parents 1a075cc + d8ce31f commit 82f8ff1
Show file tree
Hide file tree
Showing 15 changed files with 1,606 additions and 126 deletions.
13 changes: 1 addition & 12 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,7 @@ prysm is on pypi:

prysm requires only `numpy <http://www.numpy.org/>`_ and `scipy <https://www.scipy.org/>`_.

If your environment has `numba <http://numba.pydata.org/>`_ installed, it will automatically accelerate many of prysm's compuations. To use an nVidia GPU, you must have `cupy <https://cupy.chainer.org/>`_ installed. Plotting uses `matplotlib <https://matplotlib.org/>`_. Images are read and written with `imageio <https://imageio.github.io/>`_. Some MTF utilities utilize `pandas <https://pandas.pydata.org/>`_. Reading of Zygo datx files requires `h5py <https://www.h5py.org/>`_.

pip can be directed to install these,

>>> pip install prysm[cpu] # for numba
>>> pip install prysm[cuda] # for cupy
>>> pip install prysm[img] # for imageio
>>> pip install prysm[Mx] # for h5py
>>> pip install prysm[mtf] # for pandas
>>> pip install prysm[deluxe] # I want it all

or they may be installed at any time.
To use an nVidia GPU, you must have `cupy <https://cupy.chainer.org/>`_ installed. Plotting uses `matplotlib <https://matplotlib.org/>`_. Images are read and written with `imageio <https://imageio.github.io/>`_. Some MTF utilities utilize `pandas <https://pandas.pydata.org/>`_. Reading of Zygo datx files requires `h5py <https://www.h5py.org/>`_. Installation of these must be done offline.

Features
--------
Expand Down
835 changes: 835 additions & 0 deletions docs/source/releases/Upgrading and a Tour of v0.17.ipynb

Large diffs are not rendered by default.

68 changes: 54 additions & 14 deletions docs/source/releases/v0.17.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
prysm v0.17
***********

As is becoming tradition, this release is not backwards compatible and will break your code base if you do anything with non-default units or MTF data. The authors apologize for this, and note that we make these changes to improve the maintainability and cleanliness of the library's codebase as it expands. This release brings a large number of new features in addition to these breaking changes.
As is becoming tradition, this release is not backwards compatible and will break your code base if you do anything with non-default units or MTF data. The authors apologize for this, and note that we make these changes to improve the maintainability and cleanliness of the library's codebase as it expands. This release brings a large number of new features in addition to these breaking changes. A guide for transitioning and a tour of the new features has been prepared: :doc:`Upgrading and a Tour of v0.17`.

New Features
============
Expand All @@ -11,27 +11,40 @@ Note that this list is in logical order of dependence, not in order of importanc

* New :mod:`~prysm.mathops` functions: :func:`~prysm.mathops.gamma`, :func:`~prysm.mathops.kronecker`, and :func:`~prysm.mathops.sign`.
* :mod:`~prysm.jacobi` submodule for recursive jacobi polynomial computation, key functions are :func:`~prysm.jacobi.jacobi` and :func:`~prysm.jacobi.jacobi_sequence`.
* New :mod:`~prysm.zernike` functions:
* * :func:`~prysm.zernike.zernike_norm` to calculate the norm of a Zernike term given its (n, m) radial and azimuthal orders.
* * :func:`~prysm.zernike.n_m_to_fringe` to convert (n, m) radial and azimuthal orders to fringe indices.
* * :func:`~prysm.zernike.n_m_to_ansi_j` to convert (n, m) radial and azimuthal orders to ANSI single-term indices.
* * :func:`~prysm.zernike.ansi_j_to_n_m` to perform the reverse of :code:`n_m_to_ansi_j`.
* * :func:`~prysm.zernike.noll_to_n_m` to perform Noll to (n, m) radial and azimuthal indices.
* * :func:`~prysm.zernike.zero_separation` to calculate the zero separation, in fractions of 1, for example :code:`1 / zero_separation(4)` returns 16, indicating 16 samples per radius are needed to Nyquist sample the 4th radial order Zernike polynomial (Primary Spherical).
* Recursively generated Zernike polynomials:
* * :class:`ANSI2TermZernike` for ANSI Zernikes with (n, m) indices. See The 2D-Q note below for how these coefficients are entered.
* * :class:`ANSI1TermZernike` for ANSI Zernikes with j (single-term) indices.
* :mod:`~prysm.qpoly` submodule for work with Qbfs, Qcon, and 2D-Q polynomials. The raw functions allow caching to achieve O(N) performance instead of O(n^2). The cache instances behave like the Zernike cache and allow constant time performance after the initial polynomial generation and storage. Key user-facing classes:
* Changes to :mod:`~prysm.zernike`:
* * Zernike terms can now be generated using recursive Jacobi polynomials instead of explicit expressions:
* * * performance is on average ~ 2-3x higher than prysm v0.16 when numba is installed
* * * numba will no longer be used when the explicit functions are removed in v0.18
* * * there is a new cache :class:`~prysm.zernike.ZCacheMN` which will replace :class:`~prysm.zernike.ZCache` in prysm v0.18, use of :code:`Zcache` is deprecated. At that time, :code:`ZCacheMN` will be renamed to :code:`ZCache`.
* * * * Likewise, functions for higher order Zernike polynomials (>trefoil; greater than Fringe index 11) will be removed in v0.18; these are currently deprecated.
* * * * explicit Zernike functions will no longer bear :code:`norm` or :code:`name` attributes; use the functions enumerated below to acquire these values based on an index.
* * New functions:
* * * :func:`~prysm.zernike.zernike_norm` to calculate the norm of a Zernike term given its (n, m) radial and azimuthal orders.
* * * :func:`~prysm.zernike.n_m_to_fringe` to convert (n, m) radial and azimuthal orders to fringe indices.
* * * :func:`~prysm.zernike.n_m_to_ansi_j` to convert (n, m) radial and azimuthal orders to ANSI single-term indices.
* * * :func:`~prysm.zernike.ansi_j_to_n_m` to perform the reverse of :code:`n_m_to_ansi_j`.
* * * :func:`~prysm.zernike.noll_to_n_m` to perform Noll to (n, m) radial and azimuthal indices.
* * * :func:`~prysm.zernike.zero_separation` to calculate the zero separation, in fractions of 1, for example :code:`1 / zero_separation(4)` returns 16, indicating 16 samples per radius are needed to Nyquist sample the 4th radial order Zernike polynomial (Primary Spherical).
* * New classes:
* * * :class:`~prysm.zernike.ANSI2TermZernike` for ANSI Zernikes with (n, m) indices. See The 2D-Q note below for how these coefficients are entered.
* * * :class:`~prysm.zernike.ANSI1TermZernike` for ANSI Zernikes with j (single-term) indices.
* New submodule :mod:`~prysm.qpoly` for work with Qbfs, Qcon, and 2D-Q polynomials. The raw functions allow caching to achieve O(N) performance instead of O(n^2). The cache instances behave like the Zernike cache and allow constant time performance after the initial polynomial generation and storage. 2D-Q terms did not make it into this release, but code with some bugs in it for generating the terms can be found in the qpoly module. Please help get this code working if this is an area you have knowledge in. Key user-facing classes:
* * Qbfs:
* * * :class:`~prysm.qpoly.QBFSSag`
* * * :class:`~prysm.qpoly.QBFSCache`
* * QCon:
<<<<<<< HEAD
* * * :class:`~prysm.qpoly.QCONSag`
* * * :class:`~prysm.qpoly.QCONCache`
* * 2D-Q:
* * * :class:`~prysm.qpoly.Q2DSag`
* * * :class:`~prysm.qpoly.Q2DCache`
* 1D polynomials (Qbfs and Qcon) take keyword arguments A0..An with no limit. 2D polynomials (2D-Q) take arguments of Am_n
=======
* * * :code:`~prysm.qpoly.QCONSag`
* * * :code:`~prysm.qpoly.QCONCache`
* 1D polynomials (Qbfs and Qcon) take keyword arguments A0..An with no limit.
>>>>>>> dev
* Check the :mod:`~prysm.qpoly` docs for the "raw" functions.
* :code:`__str__` dunder method for :class:`~prysm.interferogram.Interferogram` objects.
* :class:`prysm.otf.OTF` and :class:`~prysm.otf.PTF` for Optical Transfer Function and Phase Transfer Function analysis.
Expand All @@ -53,7 +66,11 @@ Note that this list is in logical order of dependence, not in order of importanc
* * * :code:`(obj).exact_xy` for 2D sampling on (x, y)
* * * :code:`(obj).exact_polar` for 2D sampling on (r, p)
* Units rewrite:
<<<<<<< HEAD
* * prysm now utilizes / understands `astropy.units <https://docs.astropy.org/en/stable/units/>`_ for all calculations using the object-oriented API. :class:`BasicData` has become :class:`RichData` with new :code:`xy_unit` and :code:`z_unit` kwargs. If this is :code:`None`, the instance will adopt :code:`config.<class>.default_<xy or z>_units`. These default units mimic the behavior of prysm < 0.17, so users not adjusting units will feel no change. To use custom units, the :code:`spatial_unit`, and :code:`phase_unit` arguments are no more, and should be generated loosely as follows: For more information, see the `units documentation <../user_guide/units-and-labels.html>_.
=======
* * prysm now utilizes / understands `astropy.units <https://docs.astropy.org/en/stable/units/>`_ for all calculations using the object-oriented API. :class:`BasicData` has become :class:`RichData` with a new :code:`xy_unit` and :code:`z_unit` kwarg. If this is :code:`None`, the instance will adopt :code:`config.<class>.default_<xy or z>_units`. These default units mimic the behavior of prysm < 0.17, so users not adjusting units will feel no change. To use custom units, the :code:`spatial_unit`, and :code:`phase_unit` arguments are no more, and should be generated loosely as follows: For more information, see the `units documentation <../user_guide/units-and-labels.html>`_.
>>>>>>> dev
* Labels rewrite:
* * prysm now has a labels system that mimics the units system. The constructor works loosely as follows:

Expand All @@ -63,22 +80,43 @@ Note that this list is in logical order of dependence, not in order of importanc

* * Note that the Pupil class is used only for example, and the labels kwarg is nearly universal. For more information, see the `labels documentation <../user_guide/units-and-labels.html>`_.
* Plotting rewrite:
* * Over time, plotting in prysm has grown fragmented, with minor variations on the same theme throughout the classes. To reduce the cognitive overhead for users, plotting has been made universal with a single :code:`plot2d` and :code:`(obj).slices().plot` implementaiton. This means that nearly all prysm classes can be plotted with exactly the same grammar.
* * Over time, plotting in prysm has grown fragmented, with minor variations on the same theme throughout the classes. To reduce the cognitive overhead for users, plotting has been made universal with a single :code:`plot2d` and :code:`(obj).slices().plot` implementaiton. This means that nearly all prysm classes can be plotted with exactly the same grammar. This brings many breaking changes, listed in the section below.
* new functions :meth:`prysm.psf.fwhm`, :meth:`~prysm.psf.one_over_e`, :meth:`~prysm.psf.one_over_e2` for calculating the FWHM, 1/e, and 1/e^2 radii of PSFs. :meth:`~prysm.psf.estimate_size` for size estimation at an arbitrary irradiance value.


New Dependencies
================

Prysm now depends on two new libraries. The former is more or less part of the core scientific stack, and the latter is a small pure-python library with no dependencies. Astropy is used for units, retry is used to make cleaner cache code. Pip should install these for you if they are not already installed.

* astropy (install from conda or pypi)
* retry (install from pypi)

Breaking changes
================

* Slicing and plotting refactoring breaks compatibilty with the prysm <= v0.16 API.
<<<<<<< HEAD
* * :class:`BasicData` has become :class:`~prysm._richdata.RichData`.
* * Universal plotting breaks much code that any of :code:`plot2d`, :code:`plot_slice_xy`, :code:`plot_azimuthal_average`, :code:`plot_psd_slices`, :code:`plot_psd_2d` or other plotting functions, or accesses :code:`.slice_x` and :code:`.slice_y` attribute variables. For plots, see :code:`.plot2d()` and :code:`.slices().plot()`. For slices, see :code:`.slices.<x,y,z,... as enumerated above>`. To replicate the power law limits in plot_psd_slices, use :func:`prysm.plotting.add_psd_model`.
* * :attr:`tan` and :attr:`sag` properties removed from :class:`~prysm.otf.MTF` instances as well as :meth:`exact_tan` and :meth:`exact_sag`. These are now accessed via :code:`mtf.slices().x` and :code:`mtf.slices().y` and :meth:`~prysm.otf.MTF.exact_x` and :meth:`~prysm.otf.MTF.exact_y`. Likewise, for :meth:`mtf.azimuthal_average`, use :code:`mtf.slices().azavg`. The changes to tan and sag are made because it is not guaranteed that the x and y slices of the MTF correspond to tan and sag without more information given about field angles. This is not something prysm has any knowledge of at this time.
=======
* * :class:`BasicData`, has become :class:`~prysm._richdata.RichData`.
* * Universal plotting elimiates or changes the signature of many methods:
* * * :meth:`prysm.psf.PSF.plot2d` - use the same method name, note that arguments are different. For the :code:`circle_ee` functionality, use :func:`prysm.plotting.annotate_psf`.
* * * :meth:`prysm.psf.PSF.plot_slice_xy`, :meth:`prysm.otf.MTF.plot_slice_xy`, :meth:`prysm.otf.MTF.plot_tan_sag`, :meth:`prysm.otf.MTF.plot_azimuthal_average` - use :meth:`prysm.Slices.plot` accessed as :code:`<obj>.slices().plot()`.
* * * :meth:`prysm.interferogram.Interferogram.plot_psd_slices` - use :code:`Interferogram.psd().slices().plot()`. To replicate the power law limits, use :func:`prysm.plotting.add_psd_model`.
* * * :meth:`prysm.interferogram.Interferogram.plot_psd_2d` - use :code:`Interferogram.psd().plot2d()`.
* * * default axis limits for PSFs and MTFs are no longer 20 and 200, but are the entire support of the object.
* * :code:`.slice_x` and :code:`.slice_y` on :class:`~prysm._phase.OpticalPhase`, :class:`~prysm.psf.PSF` and :class:`~prysm.otf.MTF` - use :code:`<obj>.slices().x or <obj>.slices().y`
* * :attr:`tan` and :attr:`sag` properties deprecated on :class:`~prysm.otf.MTF` instances as well as :meth:`exact_tan` and :meth:`exact_sag`. Please access via :code:`mtf.slices().x` and :code:`mtf.slices().y` and :meth:`~prysm.otf.MTF.exact_x` and :meth:`~prysm.otf.MTF.exact_y`. Likewise, for :meth:`mtf.azimuthal_average`, use :code:`mtf.slices().azavg`. These properties and functions will be removed in prysm v0.18. The changes to tan and sag are made because it is not guaranteed that the x and y slices of the MTF correspond to tan and sag without more information given about field angles. This is not something prysm has any knowledge of at this time.
>>>>>>> dev
* * :meth:`prysm.interferogram.Interferogram.psd` now returns a :class:`~prysm.interferogram.PSD` object, which is just a fancy :class:`~prysm._richdata.RichData` instance like any other prysm class.
* :meth:`prysm.psf.PSF.from_pupil` normalization with :code:`norm=radiometric` has changed to match Born & Wolf. Results using this kwarg generated with prysm >= 0.17 will not match those for prysm < 0.17 in terms of scaling. The contents will be otherwise the same.
* :class:`~prysm.pupil.Pupil` and subclasses no longer take arguments of :code:`mask` and :code:`mask_target`, instead taking :code:`phase_mask` and :code:`transmission`. This should improve clarity. Arguments may take a few forms - :code:`<ndarray>`, :code:`<str>`, or :code:`[<str>, <float>]`. In the ndarray case, the argument is used directly. Strings are passed to the mask cache with implicit :code:`radius=1`, while in the last case the argument is a tuple or list of the mask shape and radius.
* The default OPD unit for pupils is now nm instead of waves.
* :code:`interp_method` parameters on plotting functions have been renamed to :code:`interpolation`. This mimics matplotlib exactly, as prysm is simply wrapping matplotlib for these methods.
* :func:`prysm.geometry.triangle` was removed as it throws a Qhull error and cannot be made to work with the underlying implementation of N sided polygons.
* The optional dependency directives have been installed; triggering pip installs of these dependencies has a deleterious effect on user's conda environments, and the cupy dependency was not always resolved properly (users need cupy-cuda91, for example).

Bugfixes
========
Expand All @@ -93,3 +131,5 @@ Under-the-hood Changes
======================

* The use of astropy.units has changed the display of PSD units. While before they would appear as, for example, nm^2 / (cy/mm)^2, they are now reduced by astropy to, for example, nm^2 mm^2. The two are equivalent and there is no change to the meaning of results.

* prysm no longer optionally depends on numba. The reimplementation of the Zernike code based on Jacobi polynomials has led to a faster implementation than the previous functions when JIT compiled.
7 changes: 6 additions & 1 deletion prysm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
Chirp,
)
from prysm.degredations import Smear, Jitter
from prysm.zernike import FringeZernike, NollZernike, zernikefit
from prysm.zernike import FringeZernike, NollZernike, zernikefit, ANSI1TermZernike, ANSI2TermZernike
from prysm.qpoly import QBFSSag, QCONSag
from prysm.sample_data import sample_files
from prysm.propagation import Wavefront

Expand Down Expand Up @@ -86,6 +87,10 @@
'sample_files',
'RichData',
'Labels',
'ANSI1TermZernike',
'ANSI2TermZernike',
'QBFSSag',
'QCONSag',
]

__version__ = get_distribution('prysm').version
6 changes: 5 additions & 1 deletion prysm/_phase.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class OpticalPhase(RichData):
_data_attr = 'phase'
_data_type = 'phase'

def __init__(self, x, y, phase, labels, xy_unit=None, z_unit=None, wavelength=None):
def __init__(self, x, y, phase, labels, xy_unit=None, z_unit=None, wavelength=None, opd_unit=None):
"""Create a new instance of an OpticalPhase.
Note that this class is not intended to be used directly, and is meant
Expand Down Expand Up @@ -42,6 +42,10 @@ def __init__(self, x, y, phase, labels, xy_unit=None, z_unit=None, wavelength=No
wavelength of light, in microns
"""
if opd_unit is not None:
warnings.warn('opd_unit is deprecated, please use z_unit')
z_unit = opd_unit

super().__init__(x=x, y=y, data=phase, labels=labels,
xy_unit=xy_unit or config.phase_xy_unit,
z_unit=z_unit or config.phase_z_unit,
Expand Down
13 changes: 12 additions & 1 deletion prysm/_richdata.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Basic class holding data, used to recycle code."""
import copy
from collections.abc import Iterable
import warnings
from numbers import Number
from collections.abc import Iterable

from scipy import interpolate

Expand Down Expand Up @@ -395,6 +396,16 @@ def plot2d(self, xlim=None, ylim=None, clim=None, cmap=None,

return fig, ax

@property
def slice_x(self):
warnings.warn('.slice_x is deprecated and will be removed in prysm v0.18, please use .slices().x')
return self.slices().x

@property
def slice_y(self):
warnings.warn('.slice_y is deprecated and will be removed in prysm v0.18, please use .slices().y')
return self.slices().y


class Slices:
"""Slices of data."""
Expand Down
21 changes: 19 additions & 2 deletions prysm/coordinates.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ def make_rho_phi_grid(samples_x, samples_y=None, aligned='x', radius=1):
return rho, phi


def v_to_2v_sqaured_minus_one(v):
def v_to_2v_minus_one(v):
return 2 * v - 1


def v_to_2v2_minus_one(v):
return 2 * v ** 2 - 1


Expand All @@ -195,6 +199,16 @@ def v_to_v_fouth(v):
return v ** 4


def v_to_v2_times_1_minus_v2(v):
v2 = v ** 2
return v2 * (1 - v2)


def v_to_4v2_minus_4v_plus1(v):
v4 = 4 * v
return v4 * v4 - v4 + 1


def convert_transformation_to_v(transformation):
s = transformation
for letter in ('x', 'y', 'r', 't'):
Expand All @@ -207,7 +221,10 @@ class GridCache:
def __init__(self):
self.grids = {}
self.transformation_functions = {
'v -> 2v^2 - 1': v_to_2v_sqaured_minus_one,
'v -> 4v^2 - 4v + 1': v_to_4v2_minus_4v_plus1,
'v -> v^2 (1-v^2)': v_to_v2_times_1_minus_v2,
'v -> 2v^2 - 1': v_to_2v2_minus_one,
'v -> 2v - 1': v_to_2v_minus_one,
'v -> v^2': v_to_v_squared,
'v -> v^4': v_to_v_fouth,
}
Expand Down
Loading

0 comments on commit 82f8ff1

Please sign in to comment.