Skip to content

Commit

Permalink
Merge branch 'opd'
Browse files Browse the repository at this point in the history
  • Loading branch information
andykee committed Dec 1, 2023
2 parents 3a1be4e + ecab185 commit a0696a3
Show file tree
Hide file tree
Showing 25 changed files with 153 additions and 153 deletions.
4 changes: 2 additions & 2 deletions docs/_img/python/jitter.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
mpl.rcParams['figure.figsize'] = (4.5, 4.5)

mask = lentil.circlemask((256, 256), 120)
phase = lentil.zernike_compose(mask, [0, 0, 0, -300e-9, 50e-9, -100e-9, 50e-9])
pupil = lentil.Pupil(amplitude=mask, phase=phase, focal_length=10, pixelscale=1 / 240)
opd = lentil.zernike_compose(mask, [0, 0, 0, -300e-9, 50e-9, -100e-9, 50e-9])
pupil = lentil.Pupil(amplitude=mask, opd=opd, focal_length=10, pixelscale=1 / 240)
w = lentil.Wavefront(650e-9)
w *= pupil
w = lentil.propagate_dft(w, pixelscale=5e-6, shape=64, oversample=5)
Expand Down
4 changes: 2 additions & 2 deletions docs/_img/python/pixelate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
mpl.rcParams['figure.figsize'] = (4.5, 4.5)

mask = lentil.circlemask((256, 256), 120)
phase = lentil.zernike_compose(mask, [0, 0, 0, -300e-9, 50e-9, -100e-9, 50e-9])
pupil = lentil.Pupil(amplitude=mask, phase=phase, focal_length=10, pixelscale=1 / 240)
opd = lentil.zernike_compose(mask, [0, 0, 0, -300e-9, 50e-9, -100e-9, 50e-9])
pupil = lentil.Pupil(amplitude=mask, opd=opd, focal_length=10, pixelscale=1 / 240)
w = lentil.Wavefront(650e-9)
w *= pupil
w = lentil.propagate_dft(w, pixelscale=5e-6, shape=64, oversample=5)
Expand Down
2 changes: 1 addition & 1 deletion docs/_img/python/plane_transformations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

mask = lentil.util.circle((256, 256), 128)
opd = lentil.zernike.zernike(mask, 8) * 500e-9 # 500 nm of coma
pupil = lentil.Pupil(amplitude=mask, phase=opd,
pupil = lentil.Pupil(amplitude=mask, opd=opd,
focal_length=10, pixelscale=1/256)
rotation = lentil.Rotate(angle=30, unit='degrees')
flip = lentil.Flip(1)
Expand Down
2 changes: 1 addition & 1 deletion docs/_img/python/simple_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
mask = lentil.util.circle((256, 256), 128) - lentil.util.circle((256, 256), 128/3)
opd = lentil.zernike.zernike_compose(mask, coeffs=[0, 0, 0, 300e-9, 50e-9, -100e-9, 50e-9])

pupil = lentil.Pupil(amplitude=mask, phase=opd, focal_length=10,
pupil = lentil.Pupil(amplitude=mask, opd=opd, focal_length=10,
pixelscale=1/256)
detector = lentil.Image(pixelscale=5e-6)

Expand Down
4 changes: 2 additions & 2 deletions docs/_img/python/smear.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
mpl.rcParams['figure.figsize'] = (4.5, 4.5)

mask = lentil.circlemask((256, 256), 120)
phase = lentil.zernike_compose(mask, [0, 0, 0, -300e-9, 50e-9, -100e-9, 50e-9])
pupil = lentil.Pupil(amplitude=mask, phase=phase, focal_length=10, pixelscale=1 / 240)
opd = lentil.zernike_compose(mask, [0, 0, 0, -300e-9, 50e-9, -100e-9, 50e-9])
pupil = lentil.Pupil(amplitude=mask, opd=opd, focal_length=10, pixelscale=1 / 240)
w = lentil.Wavefront(650e-9)
w *= pupil
w = lentil.propagate_dft(w, pixelscale=5e-6, shape=64, oversample=5)
Expand Down
4 changes: 2 additions & 2 deletions docs/_img/python/smear_directional.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
mpl.rcParams['figure.figsize'] = (4.5, 4.5)

mask = lentil.circlemask((256, 256), 120)
phase = lentil.zernike_compose(mask, [0, 0, 0, -300e-9, 50e-9, -100e-9, 50e-9])
pupil = lentil.Pupil(amplitude=mask, phase=phase, focal_length=10, pixelscale=1 / 240)
opd = lentil.zernike_compose(mask, [0, 0, 0, -300e-9, 50e-9, -100e-9, 50e-9])
pupil = lentil.Pupil(amplitude=mask, opd=opd, focal_length=10, pixelscale=1 / 240)
w = lentil.Wavefront(650e-9)
w *= pupil
w = lentil.propagate_dft(w, pixelscale=5e-6, shape=64, oversample=5)
Expand Down
4 changes: 2 additions & 2 deletions docs/dev/tech_notes/prop_algorithm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ The general propagation algorithm implemented by :class:`prop.Propagate` is as f
.. code::
for each plane in planes
cache plane amplitude, phase, ptt_vector
cache plane amplitude, opd, ptt_vector
for each plane in planes
clear plane amplitude, phase, ptt_vector cache
clear plane amplitude, opd, ptt_vector cache
4 changes: 2 additions & 2 deletions docs/examples/planes/rb_element.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The example below assumes a ``dwdx`` influence function matrix is available.
self._x = np.zeros(6)
@property
def phase(self):
def opd(self):
return np.einsum('ijk,i->jk',DWDX, self.x)
@property
Expand Down Expand Up @@ -62,7 +62,7 @@ influence function matrix.
self._uerror = 1e-6
@property
def phase(self):
def opd(self):
return np.einsum('ijk,i->jk',DWDX, self.x)
@property
Expand Down
8 changes: 4 additions & 4 deletions docs/user/basics.optical_systems.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ multiplied in two ways:
>>> w1 = plane * w0
The :func:`~Plane.multiply` method constructs a complex phasor from the plane's
:attr:`~lentil.Plane.amplitude` and :attr:`~lentil.Plane.phase` attributes and the
:attr:`~lentil.Plane.amplitude` and :attr:`~lentil.Plane.opd` attributes and the
|Wavefront| wavelength. The plane complex phasor is then multiplied element-wise with
the wavefront's complex data array:

Expand All @@ -77,15 +77,15 @@ represented by a single |Pupil| plane:
>>> amplitude = lentil.circle(shape=(256, 256), radius=120)
>>> opd = lentil.zernike_compose(mask=amplitude,
... coeffs=[0, 0, 0, 100e-9, 300e-9, 0, -100e-9])
>>> pupil = lentil.Pupil(amplitude=amplitude, phase=opd, focal_length=10,
>>> pupil = lentil.Pupil(amplitude=amplitude, opd=opd, focal_length=10,
... pixelscale=1/240)
>>> plt.imshow(pupil.phase, origin='lower')
>>> plt.imshow(pupil.opd, origin='lower')

Segmented optical systems
=========================
Creating a model of a segmented aperture optical system in Lentil doesn't require any
special treatment. The |Plane| and |Pupil| objects work the same with sparse or
segmented amplitude, phase, and mask attributes as with monolithic ones.
segmented amplitude, opd, and mask attributes as with monolithic ones.

That being said, it is advantageous from a performance point of view to supply a
3-dimensional `segment mask` when specifying a Plane's :attr:`~lentil.Plane.mask`
Expand Down
59 changes: 29 additions & 30 deletions docs/user/basics.planes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ plane. A plane is defined by the following parameters:

* :attr:`~lentil.Plane.amplitude` - Defines the relative electric field amplitude
transmission through the plane
* :attr:`~lentil.Plane.phase` - Defines the electric field phase shift that a wavefront
* :attr:`~lentil.Plane.opd` - Defines the optical path difference that a wavefront
experiences when propagating through the plane
* :attr:`~lentil.Plane.mask` - Defines the binary mask over which the plane data is
valid. If `mask` is 2-dimensional, the plane is assumed to be monolithic. If `mask`
Expand Down Expand Up @@ -93,15 +93,15 @@ Once a Plane is defined, its attributes can be modified at any time:
:scale: 50

>>> p = lentil.Plane(amplitude=lentil.util.circle((256,256), 120))
>>> p.phase = 2e-6 * lentil.zernike(p.mask, index=4)
>>> plt.imshow(p.phase, origin='lower')
>>> p.opd = 2e-6 * lentil.zernike(p.mask, index=4)
>>> plt.imshow(p.opd, origin='lower')


Resampling or rescaling a Plane
-------------------------------
It is possible to resample a plane using either the :func:`~lentil.Plane.resample`
or :func:`~lentil.Plane.rescale` methods. Both methods use intrepolation to
resample the amplitude, phase, and mask attributes and readjust the pixelscale
resample the amplitude, opd, and mask attributes and readjust the pixelscale
attribute as necessary. The default behavior is to perform this interpolation
on a copy of the plane, but it is possible to operate in-place by setting
``inplace=True``.
Expand All @@ -111,7 +111,7 @@ on a copy of the plane, but it is possible to operate in-place by setting
Fitting and removing Plane tilt
-------------------------------
The plane's :func:`~lentil.Plane.fit_tilt` method performs a least squares fit to
estimate and remove tilt from the phase attribute. The tilt removed from the phase
estimate and remove tilt from the opd attribute. The tilt removed from the opd
attribute is accounted for by appending an equivalent :class:`~lentil.Tilt` object
to the plane's :attr:`~lentil.Plane.tilt` attribute. The default behavior is to
perform this operation on a copy of the plane, but it is possible to operate
Expand Down Expand Up @@ -147,9 +147,8 @@ Discretely sampled pupil attributes can also be specified:

* :attr:`~lentil.Pupil.amplitude` - Defines the relative electric field amplitude
transmission through the pupil
* :attr:`~lentil.Pupil.phase` - Defines the electric field phase shift that a wavefront
experiences when propagating through the pupil. This term is commonly known as the
optical path difference (OPD).
* :attr:`~lentil.Pupil.opd` - Defines the optical path difference that a wavefront
experiences when propagating through the pupil.
* :attr:`~lentil.Pupil.mask` - Defines the binary mask over which the pupil data is
valid. If `mask` is 2-dimensional, the pupil is assumed to be monolithic. If `mask`
is 3-dimensional, the pupil is assumed to be segmented with the segment masks
Expand All @@ -165,7 +164,7 @@ Create a pupil with:

.. code-block:: pycon
>>> p = lentil.Pupil(focal_length=10, pixelscale=1/100, amplitude=1, phase=0)
>>> p = lentil.Pupil(focal_length=10, pixelscale=1/100, amplitude=1, opd=0)
Image
=====
Expand All @@ -180,7 +179,7 @@ of the following can be specified:
the image plane will grow as necessary to capture all data.
* :attr:`~lentil.Image.amplitude` - Definers the relative electric field amplitude
transmission through the image plane.
* :attr:`~lentil.Image.phase` - Defines the electric field phase shift that a wavefront
* :attr:`~lentil.Image.opd` - Defines the optical path difference that a wavefront
experiences when propagating through the image plane.

Detector
Expand Down Expand Up @@ -318,12 +317,12 @@ efficiently modeling a grism.

Active optics and deformable mirrors
====================================
Active optics and deformable mirrors are easily represented by defining a phase that
Active optics and deformable mirrors are easily represented by defining an OPD that
depends on some parameterized state. Because there is no standard architecture for these
types of optical elements, Lentil does not provide a concrete implementation. Instead,
a custom subclass of either |Plane| or |Pupil| should be defined. The exact
implementation details will vary by application, but a simple example of a tip-tilt
mirror where the plane's phase is computed dynamically based on the state `x` is
mirror where the plane's OPD is computed dynamically based on the state `x` is
provided below. Additional examples can be found in Model Patterns under
:ref:`patterns.planes`.

Expand All @@ -346,14 +345,14 @@ provided below. Additional examples can be found in Model Patterns under
normalize=False)
@property
def phase(self):
def opd(self):
return np.einsum('ijk,i->jk', self._infl_fn, self.x)
.. code-block:: pycon
>>> tt = TipTiltMirror()
>>> tt.x = [1e-6, 3e-6]
>>> plt.imshow(tt.phase)
>>> plt.imshow(tt.opd)
>>> plt.colorbar()
.. plot::
Expand All @@ -363,17 +362,17 @@ provided below. Additional examples can be found in Model Patterns under
import lentil

mask = lentil.circlemask((256,256), 120)
phase = lentil.zernike_compose(mask, [0, 1e-6, 3e-6], normalize=False)
opd = lentil.zernike_compose(mask, [0, 1e-6, 3e-6], normalize=False)

im = plt.imshow(phase, origin='lower')
im = plt.imshow(opd, origin='lower')
plt.colorbar(im, fraction=0.046, pad=0.04)

Customizing Plane
=================
The Plane class or any of the classes derived from Plane can be subclassed to modify
any of the default behavior. Reasons to do this may include but are not limited to:

* Dynamically computing the :attr:`~lentil.Plane.phase` attribute
* Dynamically computing the :attr:`~lentil.Plane.opd` attribute
* Changing the Plane-Wavefront interaction by redefining the `Plane.multiply()` method
* Modifying the way a Plane is resampled or rescaled

Expand All @@ -390,9 +389,9 @@ Some general guidance for how to safely subclass Plane is provided below.
passing these attributes along to the ``super().__init__()`` call to ensure they are
properly set.

Redefining the amplitude, phase, or mask attributes
Redefining the amplitude, OPD, or mask attributes
---------------------------------------------------
Plane :attr:`~lentil.Plane.amplitude`, :attr:`~lentil.Plane.phase`, and
Plane :attr:`~lentil.Plane.amplitude`, :attr:`~lentil.Plane.opd`, and
:attr:`~lentil.Plane.mask` are all defined as properties, but Python allows you to
redefine them as class attributes without issue:

Expand All @@ -403,10 +402,10 @@ redefine them as class attributes without issue:
class CustomPlane(le.Plane):
def __init__(self):
self.amplitude = lentil.circle((256,256), 128)
self.phase = lentil.zernike(lentil.circlemask((256,256),128), 4)
self.opd = lentil.zernike(lentil.circlemask((256,256),128), 4)
If more dynamic behavior is required, the property can be redefined. For example, to
return a new random phase each time the :attr:`~lentil.Plane.phase` attribute is
return a new random OPD each time the :attr:`~lentil.Plane.opd` attribute is
accessed:

.. code-block:: python3
Expand All @@ -420,11 +419,11 @@ accessed:
self.amplitude = lentil.circle((256,256), 128)
@property
def phase(self):
def phaopdse(self):
return lentil.zernike_compose(self.mask, np.random.random(10))
It is also straightforward to implement a custom :attr:`~lentil.Plane.phase` property to
provide a stateful phase attribute:
It is also straightforward to implement a custom :attr:`~lentil.Plane.opd` property to
provide a stateful OPD attribute:

.. code-block:: python3
Expand All @@ -438,12 +437,12 @@ provide a stateful phase attribute:
self.x = x
@property
def phase(self):
def opd(self):
return lentil.zernike_compose(self.mask, self.x)
.. note::

Polychromatic or broadband diffraction propagations access the phase, amplitude,
Polychromatic or broadband diffraction propagations access the OPD, amplitude,
and mask attributes for each propagatioon wavelength. Because these attributes
remain fixed during a propagation, it is inefficient to repeatedly recompute
them. To mitigate this, it can be very useful to provide a mechanism for freezing
Expand All @@ -462,14 +461,14 @@ provide a stateful phase attribute:
self.amplitude = lentil.circle((256,256), 128)
@property
def phase(self):
def opd(self):
return lentil.zernike_compose(self.mask, np.random.random(10))
def freeze(self):
# Return a copy of CustomPlane with the phase attribute redefined
# to be a static copy of the phase when freeze() is called
# Return a copy of CustomPlane with the OPD attribute redefined
# to be a static copy of the OPD when freeze() is called
out = copy.deepcopy(self)
out.phase = self.phase.copy()
out.opd = self.opd.copy()
return out
Expand Down
8 changes: 4 additions & 4 deletions docs/user/basics.wavefront_error.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Representing wavefront error
****************************

Wavefront error is represented in a :class:`~lentil.Plane` by specifying its
:attr:`~lentil.Plane.phase` attribute. For static errors, a simple wavefront error map is
:attr:`~lentil.Plane.opd` attribute. For static errors, a simple wavefront error map is
sufficient. For more complicated errors that are random or time-varying in nature, a
more dynamic and/or state-based approach is required.

Expand All @@ -14,7 +14,7 @@ Lentil to a Numpy array.

.. note::

For :class:`~lentil.Pupil` planes, the :attr:`~lentil.Pupil.phase` attribute represents the optical
For :class:`~lentil.Pupil` planes, the :attr:`~lentil.Pupil.opd` attribute represents the optical
path difference (OPD) relative to the pupil's reference sphere.

.. _user.wavefront_error.sign:
Expand Down Expand Up @@ -71,8 +71,8 @@ containing the JWST NITCam static wavefront error:
>>> import numpy as np
>>> import lentil
>>> phase = np.load('path/to/nircam_wfe.npy')
>>> pupil = lentil.Pupil(focal_length=119.77, pixelscale=6.6035/1024, phase=phase)
>>> opd = np.load('path/to/nircam_wfe.npy')
>>> pupil = lentil.Pupil(focal_length=119.77, pixelscale=6.6035/1024, opd=opd)
.. image:: /_static/img/nircam.png
:scale: 50
Expand Down
4 changes: 2 additions & 2 deletions docs/user/performance.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ to clear any cached values.

The cached attributes are defined in a list in each Plane's
:attr:`~lentil.Plane.cache_attrs` attribute. This list is user-settable but the only
valid values are `'amplitude'` and `'phase'`. The default behavior is to cache both
amplitude and phase attributes.
valid values are `'amplitude'` and `'opd'`. The default behavior is to cache both
amplitude and OPD attributes.

DFT matrices
------------
Expand Down
4 changes: 2 additions & 2 deletions docs/user/plots/focus_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
focus = lentil.zernike(mask=np.ones((256, 256)), index=4)

pupil_neg = lentil.Pupil(amplitude=amp, pixelscale=1/240, focal_length=10)
pupil_neg.phase = -6e-6 * focus
pupil_neg.opd = -6e-6 * focus
w_neg = lentil.Wavefront(650e-9)
w_neg *= pupil_neg
w_neg = lentil.propagate_dft(w_neg, pixelscale=5e-6, shape=200, oversample=2)

pupil_pos = lentil.Pupil(amplitude=amp, pixelscale=1/240, focal_length=10)
pupil_pos.phase = 6e-6 * focus
pupil_pos.opd = 6e-6 * focus
w_pos = lentil.Wavefront(650e-9)
w_pos *= pupil_pos
w_pos = lentil.propagate_dft(w_pos, pixelscale=5e-6, shape=200, oversample=2)
Expand Down
2 changes: 1 addition & 1 deletion docs/user/plots/npix_prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

amp = lentil.circle((256, 256), 120)
opd = lentil.zernike(amp, 4) * 1e-6
pupil = lentil.Pupil(amplitude=amp, phase=opd, pixelscale=1 / 240, focal_length=10)
pupil = lentil.Pupil(amplitude=amp, opd=opd, pixelscale=1 / 240, focal_length=10)

w1 = lentil.Wavefront(wavelength=500e-9)
w1 *= pupil
Expand Down
4 changes: 2 additions & 2 deletions docs/user/plots/tilt_images.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
x_tilt = 8e-6 * lentil.zernike(amp, 3) # +x tilt
y_tilt = 8e-6 * lentil.zernike(amp, 2) # +y tilt

py = lentil.Pupil(focal_length=10, pixelscale=1 / 240, amplitude=amp, phase=y_tilt)
py = lentil.Pupil(focal_length=10, pixelscale=1 / 240, amplitude=amp, opd=y_tilt)
wy = lentil.Wavefront(650e-9)
wy *= py
wy = lentil.propagate_dft(wy, pixelscale=5e-6, shape=200, oversample=5)

px = lentil.Pupil(focal_length=10, pixelscale=1 /240, amplitude=amp, phase=x_tilt)
px = lentil.Pupil(focal_length=10, pixelscale=1 /240, amplitude=amp, opd=x_tilt)
wx = lentil.Wavefront(650e-9)
wx *= px
wx = lentil.propagate_dft(wx, pixelscale=5e-6, shape=200, oversample=5)
Expand Down
Loading

0 comments on commit a0696a3

Please sign in to comment.