2.3.2. 2D Grating

This example illustrates the use of A-FMM to calculate reflection and transmission from a 2D grating.

The calculations shown here are based on the paper: Lee, Sun-Goo, et al. “Polarization-independent electromagnetically induced transparency-like transmission in coupled guided-mode resonance structures.” Applied Physics Letters 110.11 (2017): 111106. 10.1063/1.4978670

2.3.2.1. Importing modules

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

import A_FMM

# general parameter of calculation
N = 5  # truncation order for Fourier expansion

2.3.2.2. Definition of layers

The structure under investigation is a 2D grating of square dielectric (n=2) pillars on top o two slabs of the same dielectric, all embedded in ait (n=1). The grating is build on a square lattice with lattice constant a=1.

The following code defines the needed layers.

2.3.2.2.1. Patterned layer

This layer is built in the usual way, by defining a Creator object and call one of the pre-defined geometries.

[2]:
# definition of parameter of layer
w_pc = 0.7

# define the creator and layer
creator = A_FMM.Creator()
creator.rect(eps_core=4.0, eps_clad=1.0, w=w_pc, h=w_pc)
pattern = A_FMM.Layer(Nx=N, Ny=N, creator=creator)

# plotting dielectric constant profile of the layer
x = np.linspace(-2.5, 2.5, 501)
y = np.linspace(-2.5, 2.5, 501)

eps = pattern.calculate_epsilon(x=x, y=y)

fig, ax = plt.subplots(1,1, figsize = (10,5))
contour = ax.contourf(np.squeeze(eps['x']), np.squeeze(eps['y']), np.squeeze(eps['eps'].real))
fig.colorbar(contour, ax=ax, label='epsilon')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('xy cross section of the patterned layer')
[2]:
Text(0.5, 1.0, 'xy cross section of the patterned layer')
../_images/jupyter_2D_grating_4_1.png

2.3.2.2.2. Uniform layers

Since these layer have no patterning they are defined by calling Layer_uniform.

[3]:
# define uniform layers
background = A_FMM.Layer_uniform(Nx=N, Ny=N, eps=1.0)
dielectric = A_FMM.Layer_uniform(Nx=N, Ny=N, eps=4.0)

2.3.2.3. Stack definition

The following code defines the 3D stack.

[4]:
# definition of relevant parameters for the stack (check the paper for parameters' meaning)
t_pc = 0.2
t_1 = 0.25
t_2 = 0.361
h = 1.1

# definition of material and thicknesses lists
layers = [background, pattern, dielectric, background, dielectric, background]
thicknesses = [1.0, t_pc, t_1, h, t_2, 1.0]

# stack definition
stack = A_FMM.Stack(layers=layers, d=thicknesses)

# plotting dielectric constant profile of a cross section of the stack
z = np.linspace(-1.0, 4.0, 501)
eps = stack.calculate_epsilon(x=x, z=z)

fig, ax = plt.subplots(1,1, figsize = (10,5))
contour = ax.contourf(np.squeeze(eps['x']), np.squeeze(eps['z']), np.squeeze(eps['eps'].real))
ax.invert_yaxis()
fig.colorbar(contour, ax=ax, label='epsilon')
ax.set_xlabel('x')
ax.set_ylabel('z')
ax.set_title('xz cross section of the multilayer')
[4]:
Text(0.5, 1.0, 'xz cross section of the multilayer')
../_images/jupyter_2D_grating_8_1.png

2.3.2.4. Transmission calculation

The following code calculates the transmission.

[5]:
# definition of the array of frequency to be used for the calculation
k0_array = np.linspace(0.72, 0.74, 201)
transmission = []

# loop over the frequencies for calculating transmission
for k0 in k0_array:
    stack.solve(k0=k0)
    transmission.append(stack.get_T(stack.NPW//2, stack.NPW//2, ordered=False))

fig, ax = plt.subplots(1,1, figsize = (10,5))
ax.plot(k0_array, transmission)
ax.set_xlabel('k0 (c/a)')
ax.set_ylabel('Transmission')
ax.grid()
ax.set_title('TE transmission')

[5]:
Text(0.5, 1.0, 'TE transmission')
../_images/jupyter_2D_grating_10_1.png

Note: get_T mode ordering is designed to work for guided modes, so the modes are ordered by decreasing effective index. When the layer involved are uniform and the eigenmodes are plane waves, ordering the modes is not the best strategy, especially for not normal incidence. For uniform layers solved with a number N of fourier components, one obtains 2N plane waves. The solutions are divided into to groups: the first N have polarization along x, the other have polarization along y. The order 0 sits in the middle of each group. The easy way to obtain the correct mode is to use layer.NPW//2 if the polarization is along x and layer.NPW//2 + layer.NPW if the polarization is along y.

If other orders are needed, check layer.G. It is a dictionary linking the modes index with the tuple representing the x and y order.

[6]:
print(pattern.G)
print(pattern.NPW//2)
{0: (-5, -5), 1: (-5, -4), 2: (-5, -3), 3: (-5, -2), 4: (-5, -1), 5: (-5, 0), 6: (-5, 1), 7: (-5, 2), 8: (-5, 3), 9: (-5, 4), 10: (-5, 5), 11: (-4, -5), 12: (-4, -4), 13: (-4, -3), 14: (-4, -2), 15: (-4, -1), 16: (-4, 0), 17: (-4, 1), 18: (-4, 2), 19: (-4, 3), 20: (-4, 4), 21: (-4, 5), 22: (-3, -5), 23: (-3, -4), 24: (-3, -3), 25: (-3, -2), 26: (-3, -1), 27: (-3, 0), 28: (-3, 1), 29: (-3, 2), 30: (-3, 3), 31: (-3, 4), 32: (-3, 5), 33: (-2, -5), 34: (-2, -4), 35: (-2, -3), 36: (-2, -2), 37: (-2, -1), 38: (-2, 0), 39: (-2, 1), 40: (-2, 2), 41: (-2, 3), 42: (-2, 4), 43: (-2, 5), 44: (-1, -5), 45: (-1, -4), 46: (-1, -3), 47: (-1, -2), 48: (-1, -1), 49: (-1, 0), 50: (-1, 1), 51: (-1, 2), 52: (-1, 3), 53: (-1, 4), 54: (-1, 5), 55: (0, -5), 56: (0, -4), 57: (0, -3), 58: (0, -2), 59: (0, -1), 60: (0, 0), 61: (0, 1), 62: (0, 2), 63: (0, 3), 64: (0, 4), 65: (0, 5), 66: (1, -5), 67: (1, -4), 68: (1, -3), 69: (1, -2), 70: (1, -1), 71: (1, 0), 72: (1, 1), 73: (1, 2), 74: (1, 3), 75: (1, 4), 76: (1, 5), 77: (2, -5), 78: (2, -4), 79: (2, -3), 80: (2, -2), 81: (2, -1), 82: (2, 0), 83: (2, 1), 84: (2, 2), 85: (2, 3), 86: (2, 4), 87: (2, 5), 88: (3, -5), 89: (3, -4), 90: (3, -3), 91: (3, -2), 92: (3, -1), 93: (3, 0), 94: (3, 1), 95: (3, 2), 96: (3, 3), 97: (3, 4), 98: (3, 5), 99: (4, -5), 100: (4, -4), 101: (4, -3), 102: (4, -2), 103: (4, -1), 104: (4, 0), 105: (4, 1), 106: (4, 2), 107: (4, 3), 108: (4, 4), 109: (4, 5), 110: (5, -5), 111: (5, -4), 112: (5, -3), 113: (5, -2), 114: (5, -1), 115: (5, 0), 116: (5, 1), 117: (5, 2), 118: (5, 3), 119: (5, 4), 120: (5, 5)}
60