Polarization control is one of the central themes in integrated silicon photonics. Different polarization modes not only allow for more information-carrying channels but also enable a wide range of applications given their different characteristics. For example, waveguide TE modes usually have better confinement and thus they are less prone to sidewall roughness. TM modes, on the other hand, have a larger penetration depth into the top and bottom claddings, which makes them suitable for sensing applications. As a result, integrated silicon photonic filters that selectively transmit or block certain polarization are very useful.
This notebook demonstrates the modeling of a compact TM-pass polarization filter based on photonic crystal waveguide. The photonic crystal is an air-bridged silicon slab with periodic air holes arranged in a triangular lattice. It is possible to achieve a TM-pass but TE-block device within a frequency range by utilizing bandgap engineering and index guiding mechanism. The design parameters adopted from Chandra Prakash and Mrinal Sen , "Optimization of silicon-photonic crystal (PhC) waveguide for a compact and high extinction ratio TM-pass polarization filter", Journal of Applied Physics 127, 023101 (2020) are optimized for the telecom frequency to have a ~-0.5 dB TM transmission and ~-40 dB TE transmission.
import numpy as np import matplotlib.pyplot as plt import tidy3d as td import tidy3d.web as web from tidy3d.plugins.mode import ModeSolver
This device is designed to work in a wide frequency range from 1480 nm to 1620 nm.
lda0 = 1.55 # central wavelength freq0 = td.C_0 / lda0 # central frequency ldas = np.linspace(1.48, 1.62, 100) # wavelength range of interest freqs = td.C_0 / ldas # frequency range of interest
Since the photonic crystal slab is air-bridged, we only need to define two materials: silicon and air. The frequency dispersion of the silicon refractive index in the frequency range of interest is quite small. Therefore, in this notebook, we model it as non-dispersive and lossless.
n_si = 3.47 # silicon refractive index si = td.Medium(permittivity=n_si**2) n_air = 1 # air refractive index air = td.Medium(permittivity=n_air)
For the photonic crystal to work as a filter, the geometric parameters need to be carefully chosen such that bandgap lies within the frequency range of interest. The design process would require the calculation of band structures. In this notebook, we will skip the band structure calculation and only model the optimized device. For band diagram simulation, refer to the photonic crystal slab band structure calculation notebook.
Define the geometric parameters for the photonic crystal as well as the input and output straight waveguides.
a = 0.42 # lattice constant t = 0.75 * a # slab thickness r = 0.3 * a # radius of air holes w = 0.73 * a # width of the photonic crystal waveguide section N_holes = 11 # number of holes in each row N_rows = 7 # number of rows of holes on each side of the waveguide L = N_holes * a # length of the photonic crystal waveguide D = 0.4 # width of the input and output waveguides inf_eff = 1e3 # effective infinity of the model
To build the device, we define the silicon slab, input and output straight waveguides, and air holes. The air holes are systematically defined using a nested for loop. Due to the mirror symmetry of the device with respect to the $xz$ plane, We only define the air holes in $y>0$. Later, we will define the symmetry condition in the simulation.
# define the silicon slab si_slab = td.Structure( geometry=td.Box.from_bounds( rmin=(-L / 2, -N_rows * np.sqrt(3) * a / 2 - w / 2, 0), rmax=(L / 2, N_rows * np.sqrt(3) * a / 2 + w / 2, t), ), medium=si, ) # define the input and output straight waveguides si_wg = td.Structure( geometry=td.Box.from_bounds( rmin=(-inf_eff, -D / 2, 0), rmax=(inf_eff, D / 2, t), ), medium=si, ) # systematically define air holes holes =  for i in range(N_rows): if i % 2 == 0: shift = a / 2 N = N_holes else: shift = 0 N = N_holes + 1 for j in range(N): holes.append( td.Structure( geometry=td.Cylinder( center=( (j - (N_holes) / 2) * a + shift, (w / 2 + r) + (i) * np.sqrt(3) * a / 2, t / 2, ), radius=r, length=t, ), medium=air, ) )
A ModeSouce is defined at the input waveguide to launch either the fundamental TE or TM mode. A FluxMonitor is defined at the output waveguide to measure the transmission. In addition, we define a FieldMonitor to visualize the field propagation and scattering in the $xy$ plane.
# simulation domain size Lx = 1.5 * L Ly = 2 * N_rows * a + lda0 Lz = 7 * t sim_size = (Lx, Ly, Lz) # define a mode source at the input waveguide fwidth = 0.5 * (np.max(freqs) - np.min(freqs)) mode_spec = td.ModeSpec(num_modes=1, target_neff=n_si) mode_source = td.ModeSource( center=(-Lx / 2 + lda0 / 2, 0, t / 2), size=(0, 4 * D, 5 * t), source_time=td.GaussianPulse(freq0=freq0, fwidth=fwidth), direction="+", mode_spec=mode_spec, mode_index=0, ) # define a flux monitor at the output waveguide flux_monitor = td.FluxMonitor( center=(Lx / 2 - lda0 / 2, 0, t / 2), size=mode_source.size, freqs=freqs, name="flux", ) # define a field monitor in the xy plane field_monitor = td.FieldMonitor( center=(0, 0, t / 2), size=(td.inf, td.inf, 0), freqs=[freq0], name="field", )
For periodic structures, it is better the define a grid that is commensurate with the periodicity. Therefore, we use UniformGrid in the $x$ and $y$ directions. In the $z$ direction, a nonuniform grid can be used.
# define grids steps_per_unit_cell = 20 grid_spec = td.GridSpec( grid_x=td.UniformGrid(dl=a / steps_per_unit_cell), grid_y=td.UniformGrid(dl=a / steps_per_unit_cell * np.sqrt(3) / 2), grid_z=td.AutoGrid(min_steps_per_wvl=steps_per_unit_cell), )
Since the TE and TM modes share different symmetry with respect to the $xz$ plane, we can selectively launch them by setting the appropriate symmetry condition. The simulation for TE incidence is done by setting the symmetry condition to
(0,-1,0) while the TM incidence corresponds to
For this simulation, we set a relatively long run time of 20 ps to ensure the field decays sufficiently such that the simulation result is accurate.
# define the te incidence simulation run_time = 2e-11 # simulation run time sim_te = td.Simulation( center=(0, 0, 0), size=sim_size, grid_spec=grid_spec, structures=[si_slab, si_wg] + holes, sources=[mode_source], monitors=[flux_monitor, field_monitor], run_time=run_time, boundary_spec=td.BoundarySpec.all_sides(boundary=td.PML()), symmetry=(0, -1, 0), )
To quickly check if the structures, source, and monitors are correctly defined, use the
plot method to visualize the simulation.
sim_te.plot(z=t / 2) plt.show()