Wavelength dependent S-parameters for Euler waveguide bend in SOI
Setup¶
import os
import numpy as np
import matplotlib.pyplot as plt
import gdsfactory as gf
from gdsfactory.technology import LogicalLayer, LayerLevel, LayerStack
import luminescent as lumi
# simulation folder
path = os.path.join("runs", "bend")
# dims [um]
# length units are arbitrary so long as they're consistent
r=5
w_wg = 0.5
h_wg = 0.22
bend_type = "euler"
# wavelengths [um].
# spectrum computed from single FDTD run. more wavelengths slightly increases runtime
wavelength = 1.55
bandwidth = 0.1
nwls = 11
wavelengths = np.linspace(wavelength - bandwidth / 2, wavelength + bandwidth / 2, nwls)
# "cpu" or "gpu"
device = "gpu"
# "euler" or "circular"
def setup(path,wavelength,wavelengths, r,w_wg,h_wg,bend_type):
# simulation parameters
nres = 6
subpixel_smoothing = 1
stop_decay = 1e-3
tmax = 100
PML_decay = [0.001, 0.001, 0.1] # x y z
views = [lumi.view("Hz", y="half", z=0)]
shot_every = 10 # movie [periods]
# vertical midplane symmetry as PMC boundary on z-
boundaries = ["PML", "PML", ("PMC", "PML")]
# margins
margin = 0.8
source_offset = 1
# vertical dims
zmargin = 0.8
zmin = 0
zmax = h_wg / 2 + zmargin
# materials
core = "Si"
clad = "SiO2"
materials = {
"Si": lumi.material(3.5),
"SiO2": lumi.material(1.5),
}
background = clad
# layers
WG = 1, 0
CLAD = 2, 0
layer_stack = LayerStack(
layers={
"core": LayerLevel(
layer=LogicalLayer(layer=WG),
thickness=h_wg / 2,
zmin=0,
material=core,
mesh_order=1,
)
}
)
# make component in gdsfactory. alternatively can import .gds into gdsfactory
c = gf.Component()
# Euler or circular bend
if bend_type == "euler":
b = c << gf.components.bend_euler(r)
elif bend_type == "circular":
b = c << gf.components.bend_circular(r)
s = c << gf.components.straight(length=source_offset, width=w_wg)
s.connect("o2", b.ports["o1"])
c << gf.components.bbox(component=c, layer=CLAD, right=margin, bottom=margin)
c.add_port("o1", port=b.ports["o1"])
c.add_port("o2", port=b.ports["o2"])
c.plot()
bbox = c.bbox_np().tolist()
bbox[0].append(zmin)
bbox[1].append(zmax)
# sources, monitors
w_port = w_wg + 2 * margin
frames = [
lumi.plane_frame(
"wg_mode",
start=[-w_port / 2, zmin],
stop=[w_port / 2, zmax],
boundaries=["PML", ["PMC", "PML"]],
nmodes=1,
modes="auto",
wls_mode_solver=[wavelength],
),
lumi.points_frame("wg_signal", points=[[0, 0]], time_domain=True),
]
sources = [
lumi.gf_source(
"o1", c, layer_stack, source_offset,
frame="wg_mode",
wl=wavelength,
bandwidth=max(max(wavelengths) - min(wavelengths), wavelength / 4),
)
]
monitors = [
# modal monitors
lumi.gf_monitor(
"o1", c, layer_stack,
frame="wg_mode",
),
lumi.gf_monitor(
"o2", c, layer_stack,
frame="wg_mode",
mirrored=True,
),
# time traces (not necessary for S-parameters but for visualizing the signal)
# lumi.gf_monitor("o2", c, layer_stack, name="o2_signal", frame="wg_signal"),
]
# writes .obj mesh files from gdsfactory component
lumi.mesh(path, c, layer_stack, materials, zmin, zmax)
# writes setup file
lumi.setup(
path=path,
wl=wavelength,
wls=wavelengths,
materials=materials,
background=background,
boundaries=boundaries,
bbox=bbox,
#
frames=frames,
sources=sources,
monitors=monitors,
#
nres=nres,
subpixel_smoothing=subpixel_smoothing,
tmax=tmax,
PML_decay=PML_decay,
#
views=views,
shot_every=shot_every,
stop_decay=stop_decay,
device=device
)
setup(path, wavelength, wavelengths, r, w_wg, h_wg,bend_type)
lumi backend: None
showing modes for wavelength 1.55
mode Boundary conditions: 0A00


wg_mode/1.55 mode 0 (neff=2.443+0.000j)
saving mode to runs/bend/modes/wg_mode/1.55/0.npz
using simulation folder runs/bend

Solve¶
lumi.solve(path)[Errno 2] No such file or directory: 'lumi'
========================================
backend using simulation folder runs/bend
========================================
setting up simulation...
processing geometry bodies...
generating voxels...
voxelation: 2.362919 seconds (22.97 M allocations: 1.103 GiB, 4.34% gc time, 721.79% compilation time)
Yee mode λ=1.0: neff 2.3795 -> 2.5238, overlap 0.9984, residual 6.3e-9
making sources o1 ...
making monitors o1 o2 ...
========================================
run: runs/bend
processor: GPU
cpu threads: 48
nres: 6.0
courant: 0.9
subpixel_smoothing: 1
boundaries: [:PML :PML; :PML :PML; :PMC :PML]
sources
o1: -1, 0.0, 0
monitors
o1: 0, 0, 0
o2: 5, 5, 0
original size: (92, 78, 9)
padded size: (112, 98, 12)
cell count: 131,712
step size: 31.0 steps/period
source duration: 3.94 periods
max time: 100 periods
max steps: 3,100
max load: 408,307,200 cell-steps
energy decay threshold: 0.001
========================================
running simulation...
accumulating dft field...
period 10, decay: 0.98, 1.33 seconds/period
period 20, decay: 0.95, 0.004 seconds/period
period 30, decay: 0.071, 0.006 seconds/period
period 40, decay: 0.019, 0.003 seconds/period
period 50, decay: 0.017, 0.003 seconds/period
period 57.81 stop on energy decay
simulation completed.
Movie¶
lumi.movie(path)---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[1], line 1
----> 1 lumi.movie(path)
NameError: name 'lumi' is not definedResults¶
sol = lumi.load(path)
o1 = sol["monitors"]["o1"]
o2 = sol["monitors"]["o2"]
# + denotes forward propagating mode (wrt port monitor normal)
S21 = o2["modes"][0]["+"] / o1["modes"][0]["-"]
S11 = o1["modes"][0]["+"] / o1["modes"][0]["-"]
mid=nwls//2
S21_=np.abs(S21[mid])**2
S11_=np.abs(S11[mid])**2
print("S21 power 1.55um:", S21_)
print("S11 power 1.55um:", S11_)
assert S21_>.983
assert S11_<.0025
plt.figure()
plt.scatter(wavelengths, np.abs(S21)**2, label="S21 power")
plt.scatter(wavelengths, np.abs(S11)**2, label="S11 power")
plt.xlabel("wavelength [um]")
plt.ylabel("power")
plt.ylim(0, 1)
plt.legend()
plt.show()
plt.figure()
plt.scatter(wavelengths, np.angle(S21), label="S21 phase")
plt.xlabel("wavelength [um]")
plt.ylabel("phase [rad]")
plt.show()loading solution from runs/bend
may take a minute if simulation folder is remotely mounted
S21 power 1.55um: 0.9954009329096918
S11 power 1.55um: 0.0011599907468467668

