# standard python imports
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
# tidy3D import
import tidy3d as td
from tidy3d import web
td.config.simulation.use_local_subpixel = TrueHere, we look at a simple demonstration of how to excite a specific waveguide mode, and how to decompose the fields recorded in a monitor on the basis of waveguide modes, i.e. how to compute the power carried in each mode.
We also provide a comprehensive list of other tutorials such as how to define boundary conditions, how to defining spatially-varying sources and structures, how to model dispersive materials, and how to visualize simulation setups and results.
If you are new to the finite-difference time-domain (FDTD) method, we highly recommend going through our FDTD101 tutorials.
# standard python imports
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
# tidy3D import
import tidy3d as td
from tidy3d import web
td.config.simulation.use_local_subpixel = TrueFirst, we will do a simulation of a straight waveguide, using a silicon ridge waveguide on a silicon oxide substrate. We begin by defining some general parameters.
# Unit length is micron.
wg_height = 0.22
wg_width = 0.45
# Permittivity of waveguide and substrate
si_eps = 3.48**2
sio_eps = 1.45**2
# Free-space wavelength (in um) and frequency (in Hz)
lambda0 = 1.55
freq0 = td.C_0 / lambda0
fwidth = freq0 / 10
# Simulation size inside the PML along propagation direction
sim_length = 5
# Simulation domain size and total run time
sim_size = [sim_length, 4, 2]
run_time = 20 / fwidth
# Grid specification
grid_spec = td.GridSpec.auto(min_steps_per_wvl=20, wavelength=lambda0)When initializing ModeSource and ModeMonitor objects, one of the three values of the size parameter must be zero. This implicitly defines the propagation direction for the mode decomposition. In this example, the waveguide is oriented along the x-axis, and the mode is injected along the positive-x direction (“forward”). Below, we add a mode monitor that will show us the waveguide transmission at a range of frequencies, as well as a simple frequency monitor to examine the fields in the xy-plane at the central frequency. Note that we use a rather wide frequency range in the flux and modal monitors. This will be used later to demonstrate the effect of mode mismatch away from the central frequency and to discuss ways to remedy that.
# Waveguide and substrate materials
mat_wg = td.Medium(permittivity=si_eps)
mat_sub = td.Medium(permittivity=sio_eps)
# Substrate
substrate = td.Structure(
geometry=td.Box(
center=[0, 0, -sim_size[2]],
size=[td.inf, td.inf, 2 * sim_size[2]],
),
medium=mat_sub,
)
# Waveguide
waveguide = td.Structure(
geometry=td.Box(
center=[0, 0, wg_height / 2],
size=[100, wg_width, wg_height],
),
medium=mat_wg,
)
# Modal source parameters
src_pos = -sim_size[0] / 2 + 0.5
src_plane = td.Box(center=[src_pos, 0, 0], size=[0, 3, 2])
# xy-plane frequency-domain field monitor at central frequency
freq_mnt = td.FieldMonitor(
center=[0, 0, wg_height / 2], size=[100, 100, 0], freqs=[freq0], name="field"
)
# frequencies
mon_plane = td.Box(center=[-src_pos, 0, 0], size=[0, 3, 2])
Nfreqs = 17
freqs = np.linspace(freq0 - 2 * fwidth, freq0 + 2 * fwidth, Nfreqs)
fcent_ind = Nfreqs // 2 # index of the central frequency
# flux monitor
flux_mnt = td.FluxMonitor(
center=mon_plane.center, size=mon_plane.size, freqs=list(freqs), name="flux"
)
# Modal monitor at a range of frequencies
mode_mnt = td.ModeMonitor(
center=mon_plane.center,
size=mon_plane.size,
freqs=list(freqs),
mode_spec=td.ModeSpec(num_modes=3),
name="mode",
)Initialize simulation and visualize two cross-sections to make sure we have set up the device correctly.
# Simulation
sim = td.Simulation(
size=sim_size,
grid_spec=grid_spec,
structures=[substrate, waveguide],
sources=[],
monitors=[freq_mnt, mode_mnt, flux_mnt],
run_time=run_time,
boundary_spec=td.BoundarySpec.all_sides(boundary=td.PML()),
)
fig, (ax1, ax2) = plt.subplots(1, 2, tight_layout=True, figsize=(11, 4))
sim.plot(z=0, ax=ax1)
sim.plot(y=0, ax=ax2)
plt.show()Note that Tidy3D is warning us that the simulation does not contain a source. However, since this simulation is used to construct the mode simulation and will not be run directly, we can ignore this warning.
Before we can run a simulation with a mode source, we have to select which of the eigenmodes we would like to inject. To do that, we can first visualize the modes using the in-built eigenmode solver and plotting functions. The modes are computed at the central frequency of the source, and in order of decreasing effective index n, such that the modes that are fully below light-line (if any) should appear first. The solver assumes periodic boundary conditions at the boundaries of the 2D plane. Thus, for accurate results, the plane should be large enough for the fields to decay at the edges. In cases when found solutions do not decay to zero at the plane boundaries a warning is shown to make the user aware.
# Visualize the modes
mode_spec = td.ModeSpec(num_modes=3)
ms = td.ModeSimulation.from_simulation(
simulation=sim, plane=src_plane, mode_spec=mode_spec, freqs=[freq0]
)
mode_data = ms.run_local()
fig, axs = mode_data.plot_field_components(
field_names=("Ey", "Ez"),
mode_indices=(0, 1, 2),
val="abs",
f=freq0,
figsize=(12, 12),
)
mode_data.modes.to_dataframe()22:02:25 UTC WARNING: Mode field at frequency index 0, mode index 2 does not
decay at the plane boundaries.