PRT Defining Surface Types

written by Jaren N. Ashcraft

Polarization Ray Tracing (PRT) requires very explicit definition of light-matter interactions. In this tutorial, we will cover how to specify the different types of surfaces in Poke for accurate PRT.

Reflective Systems

Poke was initially developed (in part) to be a tool for astronomical telescopes. Consequently, reflective systems were the first to be supported. Let us begin by setting up a Rayfront to model the Hubble Space Telescope. We begin as we do in the Intro to Poke: Polarization Ray Tracing demo.

[1]:
from poke.poke_core import Rayfront
from poke.poke_math import np

pth_to_lens = 'C:/Users/Work/Desktop/poke/test_files/Hubble_Test.zmx'

# rayfront parameters
number_of_rays = 20 # across the entrance pupil
wavelength = 0.6e-6
pupil_radius = 1.2 # semi-aperture of Hubble
max_field_of_view = 0.08 # degrees

rf = Rayfront(number_of_rays, wavelength, pupil_radius, max_field_of_view, circle=False)
norm fov =  [0. 0.]
base ray shape  (4, 400)

Single-Layer Coatings

The single-layer coatings are handled using Fresnel’s equations. To specify them in Poke, simply supply the coating key of the surface dictionary with a float n_film.

[2]:
n_film = 1.200 + 1j*7.260

m1 = {"surf": 3, "coating": n_film, "mode": "reflect"}
m2 = {"surf": 5, "coating": n_film, "mode": "reflect"}
m3 = {"surf": 9, "coating": n_film, "mode": "reflect"}

surflist = [m1, m2, m3]

We can then proceed with running the raytrace and computing the Jones pupil.

[3]:
import zosapi
zosapi.App()

rf.as_polarized(surflist)
rf.trace_rayset(pth_to_lens)
rf.compute_jones_pupil(aloc=np.array([0., -1., 0.]))
tracing with global coordinates
tracing with global coordinates
tracing with global coordinates
1 Raysets traced through 3 surfaces

Plotting the Jones pupil reveals that everything looks about right for a Cassegrain telescope with a fold mirror!

[4]:
import poke.plotting as plot
plot.jones_pupil(rf)
../_images/notebooks_coating_types_7_0.png

Multilayer coating

Frequently telescope mirrors will be coated with some dielectric material to protect or enhance the coating. Here we invoke the multilayer thin-film algorithm from Peatross and Ware Chapter 4.7 to compute the effective reflection coefficients for PRT. For this coating, we will also define a dielectric substrate to deposit the metal on, since telescope mirrors are frequently metal films deposited on lightweighted glass.

The multilayer coating specification must be a list for Poke to recognize it as a multilayer stack. Here we define the coating as a list of tuples containing index in the first position and thickness in the second. The order that the surface appears in the list is the order that light hits the film. The final element of the list is a substrate, which is assumed to be semi-infinite and does not require a thickness.

[5]:
n_substrate = 1.5

n_Al = 1.200 + 1j*7.260
d_Al = 1000e-9

n_SiO2 = 1.4580
d_SiO2 = 50e-9

coating = [
    (n_SiO2, d_SiO2),
    (n_Al, d_Al),
    (n_substrate)
]

Lets update our Rayfront with these data and compute the Jones pupil

[6]:
m1 = {"surf": 3, "coating": coating, "mode": "reflect"}
m2 = {"surf": 5, "coating": coating, "mode": "reflect"}
m3 = {"surf": 9, "coating": coating, "mode": "reflect"}

surflist = [m1, m2, m3]
rf.surfaces = surflist
rf.compute_jones_pupil(aloc=np.array([0., 1., 0.]))
(1.458, 5e-08)
((1.2+7.26j), 1e-06)
(1.458, 5e-08)
((1.2+7.26j), 1e-06)
(1.458, 5e-08)
((1.2+7.26j), 1e-06)
(1.458, 5e-08)
((1.2+7.26j), 1e-06)
(1.458, 5e-08)
((1.2+7.26j), 1e-06)
(1.458, 5e-08)
((1.2+7.26j), 1e-06)

We can see that the addition of a dielectric layer, even a thin one, changes the phase pretty dramatically!

[7]:
plot.jones_pupil(rf)
../_images/notebooks_coating_types_13_0.png

Poke also happens to support another thin film algorithm for comparison. Macleod employs a similar thin film algorithm where the refractive index sign convention is assumed to be negative, which we’ve included in Poke.

This is not Poke’s default, so if you wish to change it, you can do so by updating Poke’s configuration.

[8]:
from poke.conf import Config

Config.refractive_index_sign = "negative"

Now we update the sign of the imaginary component is flipped and re-compute the Jones pupil

[9]:
n_Al = 1.200 - 1j*7.260

coating = [
    (n_SiO2, d_SiO2),
    (n_Al, d_Al),
    (n_substrate)
]

m1 = {"surf": 3, "coating": coating, "mode": "reflect"}
m2 = {"surf": 5, "coating": coating, "mode": "reflect"}
m3 = {"surf": 9, "coating": coating, "mode": "reflect"}

surflist = [m1, m2, m3]
rf.surfaces = surflist
rf.compute_jones_pupil(aloc=np.array([0., 1., 0.]))

This happens to flip the sign of the Jones pupil’s phase, but otherwise the amplitudes and phases look identical.

[10]:
plot.jones_pupil(rf)
../_images/notebooks_coating_types_19_0.png

We will reset the refractive index sign convention for the remainder of the demo

[11]:
Config.refractive_index_sign = "positive"

Refractive Systems

Refractive surfaces use a very similar interface to the one above, but require that we keep track of the index in the incident and exiting medium. For this section we will use a fast (F/1) singlet composed of a high refractive index material n = 2.

Single-Layer Coatings

Single-layer coatings for refractive material follow pretty directly from the reflective example, we just need to be sure to supply another index. First, we set up a new Rayfront.

[12]:
pth_to_lens = 'C:/Users/Work/Desktop/poke/experiments/physics_validation/singlet.zmx'

# rayfront parameters
number_of_rays = 23 # across the entrance pupil
wavelength = 0.6e-6
pupil_radius = 50e-3 / 2 # semi-aperture of Hubble
max_field_of_view = 1 # degrees

rf = Rayfront(number_of_rays, wavelength, pupil_radius, max_field_of_view, circle=True)
norm fov =  [0. 0.]
base ray shape  (4, 373)

To specify a single-layer refractive interation, Poke uses tuples as shown below to describe entering or exiting a dielectric material. Here we specify the first surface of the lens as the front and second surface of the lens as the rear.

[13]:
n_air = 1.
n_glass = 1.5

n_front = (n_air, n_glass)
n_rear = (n_glass, n_air)


front = {"surf": 2, "coating": n_front, "mode": "transmit"}
rear = {"surf": 3, "coating": n_rear, "mode": "transmit"}
[14]:
rf.as_polarized([front, rear])
rf.trace_rayset(pth_to_lens)
rf.compute_jones_pupil()
tracing with global coordinates
tracing with global coordinates
1 Raysets traced through 2 surfaces
[15]:
plot.jones_pupil(rf, coordinates='polar')
../_images/notebooks_coating_types_27_0.png