Using the RCW module

The RCW module of pySCATMECH implements the SCATMECH::RCW_Model class, which is a rigorous coupled wave (RCW) solver for one-dimensionally periodic gratings. This notebook will demonstrate a number of its features.

First, we import the module. Since we are also using numpy and matplotlib, we will import those, too.

In [1]:
import matplotlib.pyplot as plt
import numpy as np
from pySCATMECH.rcw import *

Single line grating

We will start our demonstration with a single line grating. We can create a RCW_Model with parameters:

In [2]:
gratingParameters = {
    None : 'Single_Line_Grating', # The Grating model can be assigned with the `None` key
    'period' : 0.2,               # The period
    'medium_i' : 1,               # The incident medium0
    'medium_t' : 'silicon',       # The transmitting (substrate) material
    'material' : 'silicon',       # The grating material
    'space' : 1,                  # The space material
    'height' : 0.2,               # The height
    'topwidth' : 0.1,             # Top width
    'bottomwidth' : 0.15,         # Bottom width
    'offset' : 0,                 # Offset of top, relative to bottom
    'nlevels' : 10}               # Number of layers in the staircase approximation

rcwParameters = {
    'order' : 25,                 # number of Floquet orders considered
    'type' : 0,                   # 0 indicates reflection
    'lambda' : 0.532,             # wavelength
    'thetai' : 70,                # incident angle in degrees
    'rotation' : 0,               # rotation angle in degrees
    'grating' : gratingParameters
    }

model = RCW_Model(rcwParameters)

print(model)
{None : 'RCW_Model',
'order' : '25',
'type' : '0',
'lambda' : '0.532',
'thetai' : '70',
'rotation' : '0',
'grating' : {None : 'Single_Line_Grating',
              'period' : '0.2',
              'medium_i' : '(1,0)',
              'medium_t' : 'silicon',
              'material' : 'silicon',
              'space' : '(1,0)',
              'height' : '0.2',
              'topwidth' : '0.1',
              'bottomwidth' : '0.15',
              'offset' : '0',
              'nlevels' : '10'}}

Let’s show the NCS parameters for the 0th order diffraction (specular) as a function of wavelength:

In [3]:
# 250 wavelengths from 0.2 to 1.5...
wavelengths = np.linspace(0.2, 1.5, 250)

def rcw(wavelength):
    rcwParameters['lambda'] = wavelength
    model.setParameters(rcwParameters)
    return model.DiffractionEfficiency(0)

muellerMatrices = [rcw(wavelength) for wavelength in wavelengths]
N = [m[0,1] for m in muellerMatrices]
C = [m[2,2] for m in muellerMatrices]
S = [m[2,3] for m in muellerMatrices]

plt.plot(wavelengths*1000, N, label="N")
plt.plot(wavelengths*1000, C, label="C")
plt.plot(wavelengths*1000, S, label="S")
plt.xlabel('$\lambda$ / nm')
plt.legend()
plt.show()
Graph showing results of preceding Python code: Three complicated curves labeled N, C, and S as a function of wavelength.

Let’s do the same thing, but rotate the sample 45 degrees. The Mueller matrices are no longer block diagonal.

In [4]:
wavelengths = np.linspace(0.2, 1.5, 250)

rcwParameters['rotation'] = 45
model.setParameters(rcwParameters)

def rcw(wavelength):
    rcwParameters['lambda'] = wavelength
    model.setParameters(rcwParameters)
    return model.DiffractionEfficiency(0)

muellerMatrices = [rcw(wavelength) for wavelength in wavelengths]

plt.figure(figsize = [13,13])
for element in range(16):
    plt.subplot(4,4,element+1)
    if element==0:
        plt.ylim(0,1)
    else:
        plt.ylim(-1,1)
    plt.plot(wavelengths,[m[element//4,element%4] for m in muellerMatrices],'b-')

plt.show()
Graph showing results of preceding Python code: Four by four array of panes, each with one curve.

What does this grating look like?

In [5]:
def showGrating(model,x,z):
    xx, zz = np.meshgrid(x, z, sparse=True)

    def eps(x,z):
        return model.getEpsilon(x,z).real

    epsilon = np.array([[eps(x0,z0) for x0 in x] for z0 in z])

    fig = plt.figure(figsize=(6,5))
    ax = fig.add_subplot(111)
    h = ax.contourf(x,z,epsilon,256,cmap="brg")
    plt.colorbar(h,ax=ax)

    plt.show()

showGrating(model,np.linspace(-0.2, 0.2, 200),np.linspace(-0.3,.1,200))
Graph showing results of preceding Python code: Cross section of a trench profile.

One can view the diffraction pattern:

In [6]:
gratingParameters = {
    None : 'Single_Line_Grating',
    'period' : 10,
    'medium_i' : 1,
    'medium_t' : 'silicon',
    'material' : 'silicon',
    'space' : 1,
    'height' : '0.5',
    'topwidth' : '0.4',
    'bottomwidth' : '0.15',
    'offset' : '0',
    'nlevels' : '10'}

rcwParameters = {
    None : 'RCW_Model',
    'order' : '25',
    'type' : '0',
    'lambda' : '0.532',
    'thetai' : 45,
    'rotation' : 45,
    'grating' : gratingParameters
    }

model.setParameters(rcwParameters)

def plotDiffractionPattern(model):
    order = int(model.getParameter("order"))
    x, y, I = [], [], []
    for i in range(-order,order):
        direction = model.Direction(i)
        if direction[2] != 0 :
            x.append(direction[0])
            y.append(direction[1])
            I.append(model.DiffractionEfficiency(i)[0,0])

    thetas = np.linspace(0, 2*pi, 100)
    circlex = np.cos(thetas)
    circley = np.sin(thetas)

    plt.figure(figsize = (10,10))
    ax = plt.subplot(111)
    ax.scatter(x, y, s=np.array(I)*100)
    ax.plot(circlex, circley, c = 'r')
    plt.show()

plotDiffractionPattern(model)
Graph showing results of preceding Python code: The unit circle with circles of different sizes lying along a straight line.

Blazed reflection grating

In [7]:
# Set the lines per millimeter:
lpm = 600
# Set the nominal wavelength in µm:
blazeLambda = 1.00

##########################
period = 1000/lpm
sinBlaze = blazeLambda/2/period
height = period*sinBlaze
##########################

# Description of the grating...
gratingParameters = {None: 'Triangular_Grating',
                     'period': 1000/lpm,
                     'medium_i': 1,
                     'medium_t': 'aluminum',
                     'material': 'aluminum',
                     'amplitude' : height,
                     'aspect' : 1,
                     'nlevels' : 20}

# The RCW parameters
rcwParameters = {
    None : 'RCW_Model',
    'order' : 25,
    'type' : 0,
    'lambda' : 0.500,
    'thetai' : 0,
    'rotation' : 0,
    'grating' : gratingParameters
    }
model = RCW_Model(rcwParameters)

def efficiency(wavelength):
    """Set the wavelength and calculate the efficiency"""
    thetai=math.asin(wavelength/2/period)
    model.setParameters(wavelength=wavelength,thetai=thetai)
    return model.DiffractionEfficiency(1)

wavelengths = np.arange(0.2, 1.5, 0.02)

eff = [efficiency(L) for L in wavelengths]
effs = [Sensitivity('u') @ e @ Polarization('s') for e in eff]
effp = [Sensitivity('u') @ e @ Polarization('p') for e in eff]

plt.figure()
plt.plot(wavelengths, effs, label = "s-pol")
plt.plot(wavelengths, effp, label = "p-pol")
plt.xlabel("$\lambda$/µm")
plt.ylabel("Diffraction efficiency")
plt.legend()
plt.show()

showGrating(model,np.linspace(-6, 6, 200),np.linspace(-6,6,200))
Graph showing results of preceding Python code: Two curves
Graph showing results of preceding Python code: Cross section of a blazed grating.

Sinusoidal transmission grating

In [8]:
# Set the lines per millimeter:
lpm = 1000
# Sinusoid amplitude:
amplitude = .5
# Incident angle:
thetai = 45*deg


period = 1000/lpm
from pySCATMECH.fresnel import OpticalFunction

FusedSilica = OpticalFunction(lambda L: 1.4580 + 0.00354/L**2, np.arange(0.2,1.5,0.05))

# Description of the grating...
gratingParameters = {None: 'Sinusoidal_Relief_Grating',
                     'period': period,
                     'medium_i': 1,
                     'medium_t': FusedSilica,
                     'material': FusedSilica,
                     'amplitude' : amplitude,
                     'base' : 0,
                     'option' : 0,
                     'nlevels' : 20}

# The RCW parameters
rcwParameters = {
    None : 'RCW_Model',
    'order' : 10,
    'type' : 1,
    'lambda' : 0.500,
    'thetai' : 0,
    'rotation' : 0,
    'grating' : gratingParameters
    }
model = RCW_Model(rcwParameters)

def efficiency(wavelength):
    """Set the wavelength and calculate the efficiency"""
    rcwParameters['lambda'] = wavelength
    model.setParameters(rcwParameters)
    return model.DiffractionEfficiency(1)

wavelengths = np.arange(0.2, 1.5, 0.02)

eff = [efficiency(L) for L in wavelengths]

effs = [Sensitivity('u') @ e @ Polarization('s') for e in eff]
effp = [Sensitivity('u') @ e @ Polarization('p') for e in eff]

plt.figure()
plt.plot(wavelengths, effs, label = "s-pol")
plt.plot(wavelengths, effp, label = "p-pol")
plt.legend()
plt.show()

showGrating(model,np.linspace(-6, 6, 200),np.linspace(-6,6,200))
Graph showing results of preceding Python code: Two curves
Graph showing results of preceding Python code: Cross section of a sinusoidal grating.