Imaging with a vector-Apodizing Phase Plate coronagraph
=======================================================
We will introduce the propagation of polarized light through a vector
apodizing phase plate coronagraph (vAPP).
We’ll start by importing all relevant libraries and setting up our pupil
and focal grids. We also import precomputed phase file for the pupil and
the vAPP and the pupil the vAPP pattern was calculated for.
.. code:: ipython3
from hcipy import *
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import os
# For notebook animations
from matplotlib import animation
from IPython.display import HTML
.. code:: ipython3
pupil_grid = make_pupil_grid(512)
focal_grid = make_focal_grid(4, 40)
prop = FraunhoferPropagator(pupil_grid, focal_grid)
vAPP_phase = Field(read_fits('vAPP_phase.fits.gz').ravel(), pupil_grid)
telescope_pupil = Field(read_fits('vAPP_amplitude.fits.gz').ravel(), pupil_grid)
A vector apodizing phase plate can be modelled as a half-wave retarder
with a varying fast-axis orientation [1] [2]. A vAPP applies geometric
phase to the incoming wavefront. The applied geometric phase,
:math:`\delta (x, y)`, only depends on the fast-axis orientation,
:math:`\Phi (x, y)`, through:
.. math:: \delta (x, y)= \pm 2 \Phi (x, y).
The sign of the acquired phase depends on the input circular
polarization state. To simulate a vAPP, we define it as a phase retarder
with a fast-axis orientation that is the phase / 2 and a retardation of
:math:`\pi`.
We plot the phase pattern and the fast-axis orientation pattern.
[1] Snik, Frans, et al. “The vector-APP: a broadband apodizing phase
plate that yields complementary PSFs.” Proc. SPIE, Vol. 8450 (2012)
[2] Otten, Gilles PPL, et al. “Performance characterization of a
broadband vector Apodizing Phase Plate coronagraph.” Optics Express
22.24 (2014)
.. code:: ipython3
def quiver_plot_retarder(fast_axis_orientation, N):
'''Plot the fast-axis orientation of a retarder
Parameters
---------
fast_axis_orientation : Field
The input fast_axis_orientation that is plotted
N : Scalar
A scalar that determines the sampling of fast_axis_orientation that is plotted
'''
pupil_grid = fast_axis_orientation.grid
X = pupil_grid.x.reshape(pupil_grid.shape)[::N,::N]
Y = pupil_grid.y.reshape(pupil_grid.shape)[::N,::N]
U = np.cos(fast_axis_orientation.shaped[::N,::N])
V = np.sin(fast_axis_orientation.shaped[::N,::N])
quiveropts = dict(color='black', headlength=0, pivot='middle', scale=30, units='xy', width=.003, headwidth=0)
plt.quiver(X,Y,U,V,**quiveropts )
.. code:: ipython3
# Defining the properties of the vAPP
fast_axis_orientation = vAPP_phase / 2
phase_retardation = np.pi
circularity = 0
# Making the vAPP
vAPP_pol = PhaseRetarder(phase_retardation, fast_axis_orientation, circularity)
# Setting up the sampling of the quiver plot
N = 16
# Plotting the phase pattern and fast-axis orientation pattern
plt.figure()
plt.subplot(1,2,1)
imshow_field(fast_axis_orientation, mask=telescope_pupil, cmap='RdBu')
plt.subplot(1,2,2)
imshow_field(telescope_pupil, cmap='gray')
quiver_plot_retarder(fast_axis_orientation, N)
plt.gca().set_aspect('equal')
plt.show()
.. image:: output_5_0.png
The vAPP phase pattern has a phase ramp ( = polarization grating) to
separate the two circular polarization states when imaged on a detector.
The sampling of the fast-axis orientation is not good enough to fully
capture the phase pattern. Therefore, we subtract the grating pattern
from the phase pattern.
.. code:: ipython3
# Removing the grating pattern
fast_axis_orientation_new = (vAPP_phase - pupil_grid.y * 20 * 2 * np.pi) / 2
# Changing the values to make the pattern aesthetically pleasing
fast_axis_orientation_new = (fast_axis_orientation_new - np.pi / 2) % np.pi
# Setting up the sampling of the quiver plot
N = 16
# Plotting the phase pattern and fast-axis orientation pattern
plt.figure()
plt.subplot(1,2,1)
imshow_field(fast_axis_orientation_new, mask=telescope_pupil, cmap='RdBu')
plt.subplot(1,2,2)
imshow_field(telescope_pupil, cmap='gray')
quiver_plot_retarder(fast_axis_orientation_new, N)
plt.gca().set_aspect('equal')
plt.show()
.. image:: output_7_0.png
We simulate the vAPP by propagating a left-circularly polarized
wavefront through the vAPP. The intensity image is simulated by
propagating to the focal plane.
.. code:: ipython3
wavefront = Wavefront(telescope_pupil, 1, input_stokes_vector=(1, 0, 0, -1))
wavefront_out = vAPP_pol.forward(wavefront)
vAPP_PSF = prop(wavefront_out).intensity
imshow_field(np.log10(vAPP_PSF / vAPP_PSF.max()), vmin=-5)
plt.show()
.. image:: output_9_0.png
The vAPP PSF has a dark zone from 2-15 :math:`\lambda/D` and the
coronagraphic PSF is offset from the center because of the grating that
is added to the phase. For the right-circular polarization state, the
applied phase is opposite, and the PSF is different.
.. code:: ipython3
wavefront = Wavefront(telescope_pupil, 1, input_stokes_vector=(1, 0, 0, 1))
wavefront_out = vAPP_pol.forward(wavefront)
vAPP_PSF = prop(wavefront_out).I
imshow_field(np.log10(vAPP_PSF / vAPP_PSF.max()), vmin = -5)
plt.show()
.. image:: output_11_0.png
To simulate the observation of a star, we use unpolarized light.
Unpolarized light contains equal amount of left-circular polarization
and right-circular polarization. Therefore, the stellar PSF is a
combination of the two previously simulated PSFs.
.. code:: ipython3
wavefront = Wavefront(telescope_pupil, 1, input_stokes_vector=(1, 0, 0, 0))
wavefront_out = vAPP_pol.forward(wavefront)
vAPP_PSF = (prop(wavefront_out).I)
imshow_field(np.log10(vAPP_PSF / vAPP_PSF.max()), vmin = -5)
plt.show()
.. image:: output_13_0.png
In an ideal case is the vAPP described by a patterned half-wave
retarder. However, the retardation is not always half wave. This does
not change the coronagraphic PSF, but rather changes the amount of light
that is diffracted as the coronagraphic PSFs. The fraction that is not
diffracted is called polarization leakage and is imaged in the center of
the detector. Here we simulate the image plane of a vAPP for different
retardations.
.. code:: ipython3
retardation = np.linspace(0, 2 * np.pi, 37, endpoint=True)
wavefront = Wavefront(telescope_pupil, 1, input_stokes_vector=(1, 0, 0, 0))
vAPP_pol = PhaseRetarder(retardation[0], fast_axis_orientation, circularity)
fig = plt.figure()
im = imshow_field(focal_grid.x, focal_grid, vmin=-5, vmax=0)
title = plt.title('', fontsize='xx-large')
plt.close()
def animate(i):
vAPP_pol.phase_retardation = retardation[i]
wavefront_out = vAPP_pol.forward(wavefront)
vAPP_PSF = Field((prop(wavefront_out).I),focal_grid)
vAPP_PSF /= vAPP_PSF.max()
x, y = focal_grid.coords.separated_coords
im.set_data(x, y, np.log10(vAPP_PSF.shaped))
title.set_text('Retardance = {0} degrees'.format(round(np.degrees(retardation[i]),0)))
return im, title
ani = animation.FuncAnimation(fig, animate, range(len(retardation)), blit=True)
HTML(ani.to_html5_video())
.. raw:: html
The vAPP applies gemeometric phase that is inherently independent of
wavelength. The shape of the coronagraphic PSFs does not change as
function of wavelength, except for wavelength scaling. In addition, the
retardance changes as wavelength, causing the intensity of the
polarization leakage to change accordingly.
.. code:: ipython3
wavelengths = np.linspace(0.9, 1.1, 21)
retardation = lambda wavelength: np.pi / wavelength
fig = plt.figure()
im = imshow_field(focal_grid.x, focal_grid, vmin=-5, vmax=0)
title = plt.title('',fontsize = 'xx-large')
plt.close()
vAPP_pol = PhaseRetarder(retardation, fast_axis_orientation, circularity)
def animate(i):
wavefront = Wavefront(telescope_pupil, wavelength=wavelengths[i], input_stokes_vector=(1, 0, 0, 0))
wavefront_out = vAPP_pol.forward(wavefront)
vAPP_PSF = Field((prop(wavefront_out).I),focal_grid)
vAPP_PSF /= vAPP_PSF.max()
x, y = focal_grid.coords.separated_coords
im.set_data(x, y, np.log10(vAPP_PSF.shaped))
title.set_text('Normalized wavelength = %.2f' % wavelengths[i])
return im, title
ani = animation.FuncAnimation(fig, animate, range(len(wavelengths)), blit=True)
HTML(ani.to_html5_video())
.. raw:: html