Imaging with a vortex coronagraph
=================================
We will simulate on-axis and off-axis images of stars through a
(ring-apodized) vortex coronagraph.
We’ll start by importing all relevant libraries and setting up our pupil
and focal grids. We’ll slightly oversize our pupil grid to more clearly
see the effects of the vortex coronagraph in the Lyot plane.
.. code:: ipython3
from hcipy import *
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
# For notebook animations
from matplotlib import animation
from IPython.display import HTML
.. code:: ipython3
mpl.rcParams['figure.dpi'] = 100
pupil_grid = make_pupil_grid(256, 1.5)
focal_grid = make_focal_grid(8, 12)
prop = FraunhoferPropagator(pupil_grid, focal_grid)
We start of by creating a circular aperture. A vortex coronagraph works
perfectly for a circular aperture. We’ll use supersampling to evaluate
this aperture to partially suppress sampling artefacts. We’ll also use a
slightly-undersized circular Lyot stop.
.. code:: ipython3
aperture = evaluate_supersampled(circular_aperture(1), pupil_grid, 4)
lyot_mask = evaluate_supersampled(circular_aperture(0.95), pupil_grid, 4)
plt.subplot(1,2,1)
plt.title('Aperture')
imshow_field(aperture, cmap='gray')
plt.subplot(1,2,2)
plt.title('Lyot stop')
imshow_field(lyot_mask, cmap='gray')
plt.show()
.. image:: output_4_0.png
We can perform non-coronagraphic imaging by just using the Fraunhofer
propagation defined above, to propagate the light from the pupil to the
focal plane of our telescope.
.. code:: ipython3
wf = Wavefront(aperture)
img_ref = prop(wf).intensity
imshow_field(np.log10(img_ref / img_ref.max()), vmin=-5, cmap='inferno')
plt.show()
.. image:: output_6_0.png
This shows the usual Airy pattern. We’ll now generate the vortex
coronagraph itself. It requires a pupil grid and the charge of the
vortex. The vortex coronagraph object propagates light from the pupil
plane to the Lyot plane.
.. code:: ipython3
charge = 2
coro = VortexCoronagraph(pupil_grid, charge)
lyot_stop = Apodizer(lyot_mask)
We can now propagate light through the vortex coronagraph. Internally
the vortex coronagraph performs many propagations through the vortex
with successively higher resolutions. This is done to adequately sample
the vortex singularity.
.. code:: ipython3
wf = Wavefront(aperture)
lyot_plane = coro(wf)
imshow_field(lyot_plane.intensity, cmap='inferno')
plt.show()
.. image:: output_10_0.png
We can now block this light with a Lyot stop.
.. code:: ipython3
post_lyot_mask = lyot_stop(lyot_plane)
img = prop(post_lyot_mask).intensity
imshow_field(np.log10(img / img_ref.max()), vmin=-5, vmax=0, cmap='inferno')
plt.show()
.. image:: output_12_0.png
The star has completely been suppressed. We can see the star appear
again, when we look at an off-axis object:
.. code:: ipython3
wf = Wavefront(aperture * np.exp(2j * np.pi * pupil_grid.x * 1.5))
img = prop(lyot_stop(coro(wf))).intensity
imshow_field(np.log10(img / img_ref.max()), vmin=-5, vmax=0, cmap='inferno')
plt.show()
.. image:: output_14_0.png
And the Lyot plane image looks totally different for an off-axis star:
.. code:: ipython3
lyot = coro(wf)
imshow_field(lyot.intensity, vmax=2, cmap='inferno')
plt.show()
.. image:: output_16_0.png
Unintuitively, the light in the Lyot stop is offset in the vertical
direction, while the star is offset in the horizontal direction. We can
see this effect clearer in an animation.
.. code:: ipython3
def create_offaxis_animation(coro):
fig = plt.figure()
plt.subplot(1,2,1)
plt.title('Lyot plane')
im1 = imshow_field(lyot_plane.intensity, vmax=1, cmap='inferno')
plt.subplot(1,2,2)
plt.title('Science image plane')
im2 = imshow_field(np.log10(img / img_ref.max()), vmin=-5, vmax=0, cmap='inferno')
plt.close(fig)
def animate(angular_separation):
wf = Wavefront(aperture * np.exp(2j * np.pi * pupil_grid.x * angular_separation))
lyot = coro(wf)
img = prop(lyot_stop(lyot))
im1.set_data(*pupil_grid.separated_coords, lyot.intensity.shaped)
im2.set_data(*focal_grid.separated_coords, np.log10(img.intensity.shaped / img_ref.max()))
return [im1, im2]
angular_separations = np.linspace(-5, 5, 51)
anim = animation.FuncAnimation(fig, animate, angular_separations, interval=160, blit=True)
return HTML(anim.to_html5_video())
create_offaxis_animation(coro)
.. raw:: html
We can also simulate vortex coronagraphs with other charges:
.. code:: ipython3
vortex4 = VortexCoronagraph(pupil_grid, charge=4)
create_offaxis_animation(vortex4)
.. raw:: html