Rotating Analyzer Ellipsometry

Scott Prahl

May 2020

[1]:
import numpy as np
import matplotlib.pyplot as plt

import pypolar.fresnel as fresnel
import pypolar.ellipsometry as ell

Ellipsometer Layout

A basic ellipsometer configuration is shown below. Typically the incident light is linearly polarized but the reflected light is, in general, elliptically polarized.

b28117eebb5d4adc803eabd9065e41b9

\[E_{rs} = r_s e^{j\delta_s} E_{is}\]

and

\[E_{rp} = r_p e^{j\delta_p}E_{ip}\]

The parameter \(\Delta\) describes the change in phase from parallel polarization after reflection (because \(E_p\) and \(E_s\) are in phase before incidence). The amplitude ratio (parallel vs perpendicular reflected light) is represented by \(\tan\psi\).

Rotating Ellipsometer

An ellipsometer is usually used at a single angle of incidence with linear or circularly polarized light. The reflected light passes through a rotating analyzer before hitting the detector. The reflected light is monitored over 360° with each rotation. This produces a sinusoidal signal that looks like

\[I(\phi) = I_\mathrm{DC} + I_C \cos 2\phi + I_S \sin 2\phi\]

where \(\phi\) is the angle that the analyzer makes with the plane of incidence. To extract the index of refraction of a substrate three things must be done

  1. fit the ellipsometer signal to obtain \(I_\mathrm{DC}\), \(I_S\), and \(I_C\)

  2. calculate \(\alpha =I_C/I_\mathrm{DC}\) and \(\beta =I_S/I_\mathrm{DC}\)

  3. calculate \(\rho =\tan\psi\cdot\exp(j\Delta)\) from \(\alpha\) and \(\beta\)

  4. calculate the index of refraction using \(\rho\)

Fitting to a sinusoid

We want to fit the detected signal \(I(\phi)\) to find average value \(I_\mathrm{DC}\) as well as the two Fourier coefficients \(I_S\) and \(I_C\)

\[I(\phi) = I_\mathrm{DC} + I_C \cos 2\phi + I_S \sin 2\phi\]

Our ellipsometer digitizes the signal every 5 degrees to produce an array of 72 elements, \(I_i\). The first challenge is to determine the coefficients \(I_\mathrm{DC}\), \(I_S\), and \(I_C\) using these discrete data points

\[I_i = I_\mathrm{DC} + I_C \cos2\phi_i + I_S \sin2\phi_i\]

where, \(\phi_i=2\pi i/N\).

The DC offset \(I_\mathrm{DC}\) is found by averaging over one analyzer rotation (\(0\le\phi\le2\pi\))

\[I_\mathrm{DC} = {1\over N}\sum_{i=0}^{N-1} I_i\]

The Fourier coefficients are given by

\[ I_S = {1\over \pi} \int_0^{2\pi} I(\phi)\sin(2\phi) \,{\rm d} \phi \qquad\mbox{and}\qquad I_C = {1\over \pi} \int_0^{2\pi} I(\phi)\cos(2\phi) \,{\rm d} \phi\]

For the discrete case this becomes

\[ I_S = {1\over \pi} \sum_{i=0}^{N-1} I_i\sin2\phi_i \cdot \Delta\phi \qquad\mbox{and}\qquad I_C = {1\over \pi} \sum_{i=0}^{N-1} I_i\cos2\phi_i \cdot \Delta\phi\]

where \(\Delta\phi=2\pi/N\).

If we substitute for for \(\Delta\phi\), then we recognize that \(I_C\) and \(I_S\) are just the weighted averages of \(I_i\) over one drum rotation

\[ I_S = {1\over N} \sum_{i=0}^{N-1} I_i\cdot 2\sin 2\phi_i \qquad\mbox{and}\qquad I_C = {1\over N} \sum_{i=0}^{N-1} I_i\cdot 2\cos 2\phi_i\]

where the quantities in brackets need only be calculated once at the beginning of the analysis. Every rotation of the drum requires three averages to be calculated.

Below is a test with random noise added to a known sinsoidal signal.

[2]:
degrees = np.linspace(0,360,num=72,endpoint=False)
phi = degrees*np.pi/180

# this is the signal we will try to recover
a=4.1
b=0.8
c=0.5
error=0.2
signal=ell.rotating_analyzer_signal(phi,a,b,c,error)

# finding the offset and coefficients of the sin() and cos() terms
# np.average sums the array and divides by the number of elements N
I_DC = np.average(signal)
I_S = 2*np.average(signal*np.sin(2*phi))
I_C = 2*np.average(signal*np.cos(2*phi))

plt.scatter(degrees,signal,s=5,color='red')
plt.plot(degrees,I_DC + I_S*np.sin(2*phi) + I_C*np.cos(2*phi))
plt.xlabel("Analyzer Angle (degrees)")
plt.ylabel("Ellipsometer Intensity")
plt.xlim(-10,370)
plt.show()

print("I_DC expected=%.3f obtained=%.3f" % (a,I_DC))
print("I_S  expected=%.3f obtained=%.3f" % (b,I_S))
print("I_C  expected=%.3f obtained=%.3f" % (c,I_C))
_images/10c-Ellipsometry_5_0.png
I_DC expected=4.100 obtained=4.062
I_S  expected=0.800 obtained=0.811
I_C  expected=0.500 obtained=0.553

Isotropic, Homogeneous Materials

Converting \(\alpha\) and \(\beta\) to surface properties requires a physical model for the surface. The simplest model is that of an isotropic flat material with reflection determined by the Fresnel reflection properties.

Tompkins 2005, page 282, writes >The major problem with this approach is that it ignores the surface layer of the material. All materials have this surface overlayer, which may due to surface roughness, surface oxide, surface reconstruction, etc. Therefore, any realistic model of the sample is more complicated than a simple air/material, and [this analysis] is not valid for any model involving a surface overlayer. However, [it] is quite useful as a limiting case …

The expression for the electric field at the detector can be found using Jones matrices. The incident light passes through a linear polarizer at an angle \(\theta_p\) relative to the plane of incidence. The light is reflected off the surface and then passes through a linear analyzer at an angle \(\phi\). The electric field is

\[\begin{split}E_D= \left[\begin{array}{cc} \cos^2\phi & \sin \phi\cos\phi\\ \sin\phi\cos\phi & \sin^2\phi\\ \end{array}\right] \cdot \left[\begin{array}{cc} r_p(\theta_i) & 0\\ 0 & r_s(\theta_i)\\ \end{array}\right] \cdot \left[\begin{array}{cc} \cos^2\theta_p & \sin \theta_p\cos \theta_p\\ \sin \theta_p\cos \theta_p & \sin^2 \theta_p\\ \end{array}\right] \cdot \left[\begin{array}{c} 1\\ 0\\ \end{array}\right]\end{split}\]

Therefore

\[\begin{split}E_D=\left[\begin{array}{cc} r_p(\theta_i)\cos^2\theta_p\cos^2\phi+r_s(\theta_i)\cos \theta_p\cos\phi\sin \theta_p \sin\phi\\ r_p(\theta_i)\cos^2 \theta_p\cos\phi\sin\phi+r_s(\theta_i)\cos P\sin \theta_p \sin^2\phi\\ \end{array}\right]\end{split}\]

and since the intensity is product of \(E_D\) with its conjugate transpose \(I=E_D\cdot E_D^T\) with a bit of algebra we find that

\[I_\mathrm{measured} = \cos^2\theta_p \cdot\left|r_p(\theta_i)\cos\theta_p \cos\phi+r_s(\theta_i)\sin\theta_p \sin\phi\right|^2\]

and with even more algebra this can be related back to the sinusoidal function

\[I(\phi) = I_\mathrm{DC} + I_C \cos 2\phi + I_S \sin 2\phi\]

which leads to

\[\alpha = {I_C\over I_\mathrm{DC}} = {\tan^2\psi -\tan^2 \theta_p \over \tan^2\psi+\tan^2 \theta_p} \qquad \mbox{and} \qquad \beta = {I_S\over I_\mathrm{DC}} = {2\tan\psi \cos\Delta \tan \theta_p \over \tan^2\psi+\tan^2 \theta_p}\]
[3]:
degrees = np.linspace(0,360,num=72,endpoint=False)
phi = degrees*np.pi/180

# create signal
m=3-0.2j                # sample index of refraction
P=30                    # incident polarization azimuth (degrees)
theta_p = np.radians(P) # incident polarization azimuth (radians)
th=70                   # angle of incidence (degrees)
theta_i = np.radians(th)  # angle of incidence (radians)

# Generate 72 intensities based on experimental conditions
# On reflected intensity for each angle of the rotating analyzer
phi_deg = np.linspace(0,360,num=72,endpoint=False)
phi = np.radians(phi_deg)
signal = ell.rotating_analyzer_signal_from_m(phi, m, theta_i, theta_p, noise=0.003)

# analyze signal
rho, fit = ell.rho_from_rotating_analyzer_data(phi, signal, theta_p)
m2 = ell.m_from_rho(rho,theta_i)

# display data and fit
plt.plot(degrees,signal,'xr')
plt.plot(degrees,fit)

plt.xlabel("Analyzer Angle (degrees)")
plt.ylabel("Ellipsometer Measurement")
plt.title("%.1f° Linear Polarized Light, No QWP" % np.degrees(theta_p))
plt.xlim(-10,370)
plt.show()


print("m=%.3f%+.3fj (expected)"%(m.real,m.imag))
print("m=%.3f%+.3fj"%(m2.real,m2.imag))

#rho2 = ell.rho_from_m(m,theta_i)
#print("rho=%.3f%+.3fj (expected)"%(rho2.real,rho2.imag))
#print("rho=%.3f%+.3fj"%(rho.real,rho.imag))
_images/10c-Ellipsometry_7_0.png
m=3.000-0.200j (expected)
m=2.983-0.151j

Ellipsometry Parameters

If linearly polarized light is incident with an azimuthal angle \(\theta_p\) (where \(\theta_p=0^\circ\) is in the plane of incidence) then the normalized intensity is

\[{I(\phi)\over I_\mathrm{DC}} = 1 + \alpha \cos 2\phi + \beta\sin 2\phi\]

No quarter wave plate in the incident beam (\(0\le\theta_p\le90°\))

The parameters \(\psi\) and \(\Delta\) can now be calculated from \(\alpha\) and \(\beta\)

\[\tan\psi = \sqrt{1+\alpha\over 1-\alpha}\cdot |\tan \theta_p| \qquad \mbox{and} \qquad \cos\Delta = {\beta\over\sqrt{1-\alpha^2}}\cdot{\tan \theta_p\over |\tan \theta_p|}\]

Or in terms of \(I_\mathrm{DC}\), \(I_S\), and \(I_C\)

\[\tan\psi = \sqrt{I_\mathrm{DC}+I_C\over I_\mathrm{DC}-I_C}\cdot |\tan \theta_p| \qquad \mbox{and} \qquad \cos\Delta = {I_S\over\sqrt{I_\mathrm{DC}^2-I_C^2}}\cdot{\tan \theta_p\over |\tan \theta_p|}\]

Example

  • generate a theoretical ellipsometer signal

  • fit the signal to determine m

[4]:
# Experimental Conditions
m=1.5-0.0j              # sample index of refraction
P=45                    # incident polarization azimuth (degrees)
theta_p = np.radians(P) # incident polarization azimuth (radians)
th=70                   # angle of incidence (degrees)
theta_i = np.radians(th)  # angle of incidence (radians)

# Generate 72 intensities based on experimental conditions
# On reflected intensity for each angle of the rotating analyzer
phiD = np.linspace(0,360,num=72,endpoint=False)
phi = np.radians(phiD)
signal = ell.rotating_analyzer_signal_from_m(phi, m, theta_i, theta_p, noise=0.0005)

# Calculate Delta, tanpsi, and index from 72 intensities
rho, fit = ell.rho_from_rotating_analyzer_data(phi, signal, theta_p)
m2 = ell.m_from_rho(rho, theta_i)
tanpsi, Delta = ell.tanpsi_Delta_from_rho(rho)

# Show the results
plt.plot(phiD, signal, 'or')
plt.plot(phiD, fit, color="blue")
plt.title(r"P=%d°, $\theta_i=$%d, m=%.1f-%.1fj" % (P,th,m.real,-m.imag))
plt.xlabel("Analyzer Angle (degrees)")
plt.ylabel("Ellipsometer Intensity")
plt.xlim(-10,370)
plt.show()

print("Fitted Delta=%.1f°, tanpsi=%.3f" % (np.degrees(Delta),tanpsi))
print("Original  refractive index = %.3f%+.3fj" % (m.real,m.imag))
print("Recovered refractive index = %.3f%+.3fj" % (m2.real,m2.imag))
_images/10c-Ellipsometry_10_0.png
Fitted Delta=4.5°, tanpsi=0.376
Original  refractive index = 1.500+0.000j
Recovered refractive index = 1.501-0.063j

Sensitivity to random gaussian noise

[5]:
# Experimental Conditions
m=3.0-0.2j                # sample index of refraction
P=20                    # incident linear polarization angle (degrees)
theta_p = np.radians(P) # incident linear polarization angle (radians)
th=30                   # angle of incidence (degrees)
theta_i = np.radians(th)# angle of incidence (radians)

# Generate 72 intensities based on experimental conditions
phi = np.radians(np.linspace(0,360,num=72,endpoint=False))

# error free signal needed to scale the plots
signal = ell.rotating_analyzer_signal_from_m(phi, m, theta_i, theta_p)
scale = signal.mean()
ymax = signal.max()

N=16
dev = np.zeros(N)
err = np.zeros(N)
print("m=%.3f%+.3f  expected" % (m.real,m.imag))

# create fit plots for each error
plt.subplots(4,4,figsize=(10,10))
for i in range(N):
    error = scale*2**(-i-2)
    signal = ell.rotating_analyzer_signal_from_m(phi, m, theta_i, theta_p, noise=error)
    rho, fit = ell.rho_from_rotating_analyzer_data(phi, signal, theta_p)
    m2 = ell.m_from_rho(rho, theta_i)
    dev[i]=abs(m2-m)
    rel_error = 100*error/ymax
    err[i]=rel_error
    plt.subplot(4,4,i+1)
    plt.plot(np.degrees(phi), signal, 'ob')
    plt.plot(np.degrees(phi), fit, 'k')
    plt.ylim(-0.1*ymax,1.1*ymax)
    plt.text(0,0.99*ymax,'%.4f%%'%rel_error)
    plt.yticks([])
    plt.xticks([])

    print("m=%.3f%+.3f  error=%.3f%%" % (m2.real,m2.imag,rel_error))

plt.show()
plt.scatter(err,dev)
plt.xlim(1e-4,100)
plt.ylim(5e-4,20)
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Relative Noise Added to Signal (%)')
plt.ylabel('Absolute Error in Measured Index')
plt.title("Expected m=%.3f%+.3f" % (m.real,m.imag))
plt.show()
m=3.000-0.200  expected
m=4.174+0.000  error=12.505%
m=1.202-1.351  error=6.253%
m=7.045+0.000  error=3.126%
m=12.137+0.000  error=1.563%
m=3.273+0.000  error=0.782%
m=2.454-1.073  error=0.391%
m=2.776-0.749  error=0.195%
m=2.906-0.545  error=0.098%
m=2.939-0.447  error=0.049%
m=2.953-0.412  error=0.024%
m=2.997-0.215  error=0.012%
m=2.996-0.226  error=0.006%
m=3.003-0.179  error=0.003%
m=3.000-0.199  error=0.002%
m=3.001-0.192  error=0.001%
m=3.001-0.197  error=0.000%
_images/10c-Ellipsometry_12_1.png
_images/10c-Ellipsometry_12_2.png

Not all measurements give equally good results

[6]:
# Experimental Conditions
phi = np.radians(np.linspace(0,360,num=72,endpoint=False))
tanpsi = 0.5

print("   P     Delta  tanpsi     Delta  tanpsi  Delta tanpsi")
for P in [1, 45, 89, 91, 178, -10, -89]:
    for Del in [10, 67, 120, 178]:

        Delta   = np.radians(Del)
        theta_p = np.radians(P)
        rho0 = tanpsi*np.exp(1j*Delta)

        print("%6.1f° %6.1f° %6.3f:   " % (P,Del,tanpsi), end='')
#        print(" [%.2f° %.2f] " % (np.degrees(np.angle(rho0)),rho.imag),end='')

        signal = ell.rotating_analyzer_signal_from_rho(phi, rho0, theta_p, noise=0.0002)

        rho, fit = ell.rho_from_rotating_analyzer_data(phi, signal, theta_p)
        print("%8.3f° %6.3f" % (np.degrees(np.angle(rho)),np.abs(rho)))

   P     Delta  tanpsi     Delta  tanpsi  Delta tanpsi
   1.0°   10.0°  0.500:      3.254°  0.507
   1.0°   67.0°  0.500:     67.174°  0.496
   1.0°  120.0°  0.500:    120.182°  0.503
   1.0°  178.0°  0.500:   -180.000°  0.540
  45.0°   10.0°  0.500:     10.008°  0.500
  45.0°   67.0°  0.500:     66.995°  0.500
  45.0°  120.0°  0.500:    120.001°  0.500
  45.0°  178.0°  0.500:    178.019°  0.500
  89.0°   10.0°  0.500:     12.174°  0.504
  89.0°   67.0°  0.500:     63.769°  0.444
  89.0°  120.0°  0.500:    122.523°  0.465
  89.0°  178.0°  0.500:   -180.000°  0.739
  91.0°   10.0°  0.500:     -0.000°  0.742
  91.0°   67.0°  0.500:     67.018°  0.500
  91.0°  120.0°  0.500:    122.454°  0.466
  91.0°  178.0°  0.500:   -180.000°  0.263
 178.0°   10.0°  0.500:     10.337°  0.499
 178.0°   67.0°  0.500:     66.973°  0.501
 178.0°  120.0°  0.500:    119.868°  0.498
 178.0°  178.0°  0.500:    176.081°  0.499
 -10.0°   10.0°  0.500:     10.001°  0.500
 -10.0°   67.0°  0.500:     67.001°  0.500
 -10.0°  120.0°  0.500:    120.000°  0.500
 -10.0°  178.0°  0.500:    178.094°  0.500
 -89.0°   10.0°  0.500:     -0.000°  0.643
 -89.0°   67.0°  0.500:     62.991°  0.432
 -89.0°  120.0°  0.500:    127.277°  0.412
 -89.0°  178.0°  0.500:    148.138°  0.587
[ ]: