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. A guide for transitioning and a tour of the new features has been prepared: Upgrading and a Tour of v0.17.
New Features¶
Note that this list is in logical order of dependence, not in order of importance.
New
mathops
functions:gamma()
,kronecker()
, andsign()
.jacobi
submodule for recursive jacobi polynomial computation, key functions arejacobi()
andjacobi_sequence()
.Changes to
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
ZCacheMN
which will replaceZCache
in prysm v0.18, use ofZcache
is deprecated. At that time,ZCacheMN
will be renamed toZCache
.
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
norm
orname
attributes; use the functions enumerated below to acquire these values based on an index.
New functions:
zernike_norm()
to calculate the norm of a Zernike term given its (n, m) radial and azimuthal orders.
n_m_to_fringe()
to convert (n, m) radial and azimuthal orders to fringe indices.
n_m_to_ansi_j()
to convert (n, m) radial and azimuthal orders to ANSI single-term indices.
ansi_j_to_n_m()
to perform the reverse ofn_m_to_ansi_j
.
noll_to_n_m()
to perform Noll to (n, m) radial and azimuthal indices.
zero_separation()
to calculate the zero separation, in fractions of 1, for example1 / 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:
ANSI2TermZernike
for ANSI Zernikes with (n, m) indices. See The 2D-Q note below for how these coefficients are entered.
ANSI1TermZernike
for ANSI Zernikes with j (single-term) indices.
New submodule
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:
QCon:
~prysm.qpoly.QCONSag
~prysm.qpoly.QCONCache
1D polynomials (Qbfs and Qcon) take keyword arguments A0..An with no limit.
Check the
qpoly
docs for the “raw” functions.__str__
dunder method forInterferogram
objects.prysm.otf.OTF
andPTF
for Optical Transfer Function and Phase Transfer Function analysis.generate_spider()
to generate masks for n-vaned spiders.Slicing rewrite and refactor:
Custom slicing logic has been removed from all classes and is now implemented on the
RichData
class from which nearly every class inherits. This reduces the amount of prysm-specific vocabulary users must know and improving the cohesion of the class system.
Subclasses now inherit the following:
(obj).slices()
.x
.y
.azavg
.azmedian
.azmin
.azmax
.azvar
.azstd
.azpv
(obj).exact_x
and.exact_y
for 1D sampling along the Cartesian axes
(obj).exact_xy
for 2D sampling on (x, y)
(obj).exact_polar
for 2D sampling on (r, p)
Units rewrite:
prysm now utilizes / understands astropy.units for all calculations using the object-oriented API.
BasicData
has becomeRichData
with a newxy_unit
andz_unit
kwarg. If this isNone
, the instance will adoptconfig.<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, thespatial_unit
, andphase_unit
arguments are no more, and should be generated loosely as follows: For more information, see the units documentation.
Labels rewrite:
prysm now has a labels system that mimics the units system. The constructor works loosely as follows:
>>> from prysm import Labels, Pupil
>>> lab = Labels(xybase='Pupil', z='OPD', xy_additions=['X', 'Y'])
>>> pu = Pupil(labels=lab)
Note that the Pupil class is used only for example, and the labels kwarg is nearly universal. For more information, see the labels documentation.
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
plot2d
and(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
prysm.psf.fwhm()
,one_over_e()
,one_over_e2()
for calculating the FWHM, 1/e, and 1/e^2 radii of PSFs.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.
BasicData
, has becomeRichData
.
Universal plotting elimiates or changes the signature of many methods:
prysm.psf.PSF.plot2d()
- use the same method name, note that arguments are different. For thecircle_ee
functionality, useprysm.plotting.annotate_psf()
.
prysm.psf.PSF.plot_slice_xy()
,prysm.otf.MTF.plot_slice_xy()
,prysm.otf.MTF.plot_tan_sag()
,prysm.otf.MTF.plot_azimuthal_average()
- useprysm.Slices.plot()
accessed as<obj>.slices().plot()
.
prysm.interferogram.Interferogram.plot_psd_slices()
- useInterferogram.psd().slices().plot()
. To replicate the power law limits, useprysm.plotting.add_psd_model()
.
prysm.interferogram.Interferogram.plot_psd_2d()
- useInterferogram.psd().plot2d()
.
default axis limits for PSFs and MTFs are no longer 20 and 200, but are the entire support of the object.
.slice_x
and.slice_y
onOpticalPhase
,PSF
andMTF
- use<obj>.slices().x or <obj>.slices().y
tan
andsag
properties deprecated onMTF
instances as well asexact_tan()
andexact_sag()
. Please access viamtf.slices().x
andmtf.slices().y
andexact_x()
andexact_y()
. Likewise, formtf.azimuthal_average()
, usemtf.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.
prysm.interferogram.Interferogram.psd()
now returns aPSD
object, which is just a fancyRichData
instance like any other prysm class.
prysm.psf.PSF.from_pupil()
normalization withnorm=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.Pupil
and subclasses no longer take arguments ofmask
andmask_target
, instead takingphase_mask
andtransmission
. This should improve clarity. Arguments may take a few forms -<ndarray>
,<str>
, or[<str>, <float>]
. In the ndarray case, the argument is used directly. Strings are passed to the mask cache with implicitradius=1
, while in the last case the argument is a tuple or list of the mask shape and radius.interp_method
parameters on plotting functions have been renamed tointerpolation
. This mimics matplotlib exactly, as prysm is simply wrapping matplotlib for these methods.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¶
Automatic hanning window generation when calculating PSDs has been fixed, and no longer results in an error for nonsquare arrays.
An issue where Welch windows may be generated off-center has been fixed.
An error/bug when calling
crop()
requiring 0 pixels of removal on a side has been fixed.prysm.objects.pinhole.analytic_ft()
no longer includes an errant call to meshgrid that causes out of memory exceptions and incorrect results.
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.