Waveguide mode size converters¶
Note: the cost of running the entire notebook is larger than 1 FlexCredit.
It is common to have waveguide components of different widths and potentially thicknesses on a photonic integrated circuit (PIC). Therefore, having a low-loss waveguide size converter becomes a necessity. The most common and simple size converter is adiabatic waveguide tapers. However, to achieve low loss and meet the adiabatic condition, the taper inevitable needs to be very long, which is not ideal in many modern high-density PIC designs. To aleviate this shortcoming of the conventional adiabatic taper, novel designs of compact size converter have emerged.
In this notebook, we aim to simulate different types of size converters and compare their performance. We first simulate linear adiabatic tapers of different lengths. Subsequently, we will demonstrate two compact designs: one based on Luneburg lens and the other based on semi-lens emerged from segment optimization. These novel designs achieve ~-0.5 dB loss while being only about 6$\lambda_0$ in footprint. Linear adiabatic taper can only achieve similar performance while being 30$\lambda_0$ long.
import numpy as np
import matplotlib.pyplot as plt
import tidy3d as td
import tidy3d.web as web
from tidy3d.plugins.mode import ModeSolver
To suppress unnecessary warnings, we set the logging level to "ERROR"
.
td.config.logging_level = "ERROR"
Define the simulation wavelength (frequency) range.
lda0 = 1.55 # central wavelength
freq0 = td.C_0 / lda0 # central frequency
ldas = np.linspace(1.5, 1.6, 101) # wavelength range
freqs = td.C_0 / ldas # frequency range
fwidth = 0.5 * (np.max(freqs) - np.min(freqs)) # width of the frequency distribution
All devices simulated in this notebook are based on silicon waveguide on a thick oxide layer. We define both materials as nondispersive since the simulation wavelength range is relatively small.
n_si = 3.48 # silicon refractive index
si = td.Medium(permittivity=n_si**2)
n_sio2 = 1.44 # silicon oxide refractive index
sio2 = td.Medium(permittivity=n_sio2**2)
Linear Taper¶
The most straightforward way to connect two waveguides of different widths is via an adiabatic taper. The taper shape can be linear, hyperbolic, Gaussian, and so on. Here, we demonstrate a linear taper and investigate how the loss scales with taper length. To be specific, we model a taper connecting the input waveguide of 10 $\mu m$ wide to an output waveguide of 500 nm wide. Both waveguides have the same thickness of 110 nm. TE0 mode is launched at the input waveguide.
Since we would like to perform a parameter sweep over the taper length, we will define a function called linear_taper_sim
to construct the simulation. This function will be called repeatedly to make a simulation batch. The structure of the taper including the input and output waveguides can be conveniently made using as a PolySlab.
w_in = 10 # input waveguied width
w_out = 0.5 # output waveguide width
t_wg = 0.11 # waveguide thickness
inf_eff = 1e3 # effective infinity of the model
# define the substrate structure
sub = td.Structure(
geometry=td.Box.from_bounds(
rmin=(-inf_eff, -inf_eff, -inf_eff), rmax=(inf_eff, inf_eff, 0)
),
medium=sio2,
)
# define a function to construct the simulation given the taper length
def linear_taper_sim(L_t):
# vertices of the taper
vertices = [
[-inf_eff, w_in / 2],
[0, w_in / 2],
[L_t, w_out / 2],
[inf_eff, w_out / 2],
[inf_eff, -w_out / 2],
[L_t, -w_out / 2],
[0, -w_in / 2],
[-inf_eff, -w_in / 2],
]
# construct the taper structure using a PolySlab
linear_taper = td.Structure(
geometry=td.PolySlab(vertices=vertices, axis=2, slab_bounds=(0, t_wg)),
medium=si,
)
# add a mode source that launches the TE0 mode at the input waveguide
mode_source = td.ModeSource(
center=(-lda0 / 2, 0, t_wg / 2),
size=(0, 1.2 * w_in, 6 * t_wg),
source_time=td.GaussianPulse(freq0=freq0, fwidth=fwidth),
direction="+",
mode_spec=td.ModeSpec(num_modes=1, target_neff=n_si),
mode_index=0,
)
# add a field monitor to visualize the field distribution at z=t_wg/2
field_monitor = td.FieldMonitor(
center=(0, 0, t_wg / 2), size=(td.inf, td.inf, 0), freqs=[freq0], name="field"
)
# add a flux monitor to measure transmission to the output waveguide
flux_monitor = td.FluxMonitor(
center=(lda0 / 2 + L_t, 0, t_wg / 2),
size=(0, 2 * w_out, 6 * t_wg),
freqs=freqs,
name="flux",
)
# define simulation domain size
Lx = L_t + 2 * lda0
Ly = w_in + 2 * lda0
Lz = t_wg + 1.5 * lda0
sim_size = (Lx, Ly, Lz)
run_time = 3e-12 # run time of the simulation
# define simulation
sim = td.Simulation(
center=(L_t / 2, 0, t_wg),
size=sim_size,
grid_spec=td.GridSpec.auto(min_steps_per_wvl=20, wavelength=lda0),
structures=[linear_taper, sub],
sources=[mode_source],
monitors=[field_monitor, flux_monitor],
run_time=run_time,
boundary_spec=td.BoundarySpec.all_sides(
boundary=td.PML()
), # pml is used in all boundaries
symmetry=(0, -1, 0),
) # a pec symmetry plane at y=0 can be used to reduce the grid points of the simulation
return sim
With the linear_taper_sim
function defined, we can use it to construct a simulation batch. We aim to simulate taper lengths of 10 $\mu m$, 20 $\mu m$, 50 $\mu m$, and 100 $\mu m$.
To visually verify the simulation setup, we can take a simulation from the batch and use the plot
method.
L_ts = np.array([10, 20, 50, 100]) # taper lengths to be simulated
sims = {f"L_t={L_t}": linear_taper_sim(L_t) for L_t in L_ts} # make a simulation batch
sim = sims["L_t=20"] # take one simulation (L_t=20) from the batch
sim.plot(z=t_wg / 2) # visualize the simualtion setup
plt.show()
Submit the simulation batch to the server.
batch = web.Batch(simulations=sims, verbose=True)
batch_results = batch.run(path_dir="data")
Output()
Output()
Output()
Output()
[14:13:27] Started working on Batch. container.py:402
Output()
[14:19:14] Batch complete. container.py:436
After the batch simulation is complete, we can see how the loss scales with the taper length. From the results, we can see that a 10 $\mu m$ taper has an insertion loss of ~-6 dB and a 20 $\mu m$ taper has an insertion loss of ~-3 dB. To achieve low loss, the taper needs to be longer than 50 $\mu m$, which is not sufficiently compact for many applications.
# using a for loop to plot the insertion losses at different taper lengths
for i, L_t in enumerate(L_ts):
sim_data = batch_results[f"L_t={L_t}"]
T = sim_data["flux"].flux
plt.plot(ldas, 10 * np.log10(T), label=f"{L_t} $\mu m$ taper")
plt.title("Insertion loss for different linear taper lengths")
plt.ylim(-10, 0)
plt.xlabel("Wavelength ($\mu m$)")
plt.ylabel("Insertion loss (dB)")
plt.legend()
plt.show()
Output()
Output()
Output()
Output()
The field distributions can also be plotted. From the field distributions, we can see the leakage of energy at the abrupt changes where the taper meets the straight waveguide. This leakage is reduced for longer tapers and thus the insertion loss is smaller.
f, ax = plt.subplots(2, 2, tight_layout=True, figsize=(8, 8))
for i, L_t in enumerate(L_ts):
sim_data = batch_results[f"L_t={L_t}"]
sim_data.plot_field(
field_monitor_name="field",
field_name="Ey",
f=freq0,
ax=ax[i // 2, i % 2],
vmin=-50,
vmax=50,
)
ax[i // 2, i % 2].set_xlim(L_t - 5 * lda0, L_t + lda0)
ax[i // 2, i % 2].set_title(f"{L_t} $\mu m$ taper")
Luneburg Lens Size Converter¶
Besides adiabatic tapers, there are many novel size converter designs that can achieve both compactness and low loss. The first example is a taper design based on the principle of a Luneburg lens, which we will demonstrate here.
A Luneburg lens is a spherical graded index lens where its refractive index varies with positions. The most common Luneburg lens employees a refractive index profile of $n(r)=\sqrt{2-(\frac{r}{R})^2}$, where $r$ is the distance to the center of the lens and $R$ is the radius of the lens. The result of the graded index is that light propagating parellel to the lens will be focused on the edge of the lens.
On the other hand, waveguides with different thicknesses have different effective indices. By applying the same principle of a classical Luneburg lens, we can design a spherical waveguide region where the thickness of the waveguide varies such that the effective index follows that of a Luneburg lens. Consequently, the incidet light from the input waveguide can be focused into the output waveguide and we achieve a low loss while having a small device footprint. The device design is adapted from S Hadi Badri and M M Gilarlue 2019 J. Opt. 21 125802.
To use this design, we first need to obtain the relationship between the effective index and the waveguide thickness. Here, we ignore the effect of finite waveguide width and only consider the effective index of a slab waveguide. This will leave room for further optimization, which is another topic that we will not cover in this example notebook. We will first use the ModeSolver to solve for the effective index. Note that the effective index here is that of the waveguide, not to confuse with the effective index used in a 2.5D simulation. The simulation here will be performed in full 3D.