在ipkiss中,器件是通过参数单元pcell的方式来定义的,一个完整的器件模型包含:布局视图Layout、网表视图Netlist以及行为模型视图CircuitModel,以1x2mmi为例:
所有代码如下:
from si_fab import all as pdk
from ipkiss3 import all as i3
import numba as nb
import numpy as np
from matplotlib import pyplot as plt
# Building the MMI PCell with properties that describe its geometry
class MMI1x2(i3.PCell):
"""MMI with 1 input and 2 outputs."""
_name_prefix = "MMI1x2"
trace_template = i3.TraceTemplateProperty(doc="Trace template of the access waveguide")
width = i3.PositiveNumberProperty(default=4.0, doc="Width of the MMI section.")
length = i3.PositiveNumberProperty(default=20.0, doc="Length of the MMI secion.")
taper_width = i3.PositiveNumberProperty(default=1.0, doc="Width of the taper.")
taper_length = i3.PositiveNumberProperty(default=5.0, doc="Length of the taper")
waveguide_spacing = i3.PositiveNumberProperty(default=2.0, doc="Spacing between the waveguides.")
def _default_trace_template(self):
return pdk.SiWireWaveguideTemplate()
class Layout(i3.LayoutView):
def _generate_elements(self, elems):
length = self.length
width = self.width
taper_length = self.taper_length
taper_width = self.taper_width
half_waveguide_spacing = 0.5 * self.waveguide_spacing
core_layer = self.trace_template.core_layer
cladding_layer = self.trace_template.cladding_layer
core_width = self.trace_template.core_width
# Si core
elems += i3.Rectangle(
layer=core_layer,
center=(0.5 * length, 0.0),
box_size=(length, width),
)
elems += i3.Wedge(
layer=core_layer,
begin_coord=(-taper_length, 0.0),
end_coord=(0.0, 0.0),
begin_width=core_width,
end_width=taper_width,
)
elems += i3.Wedge(
layer=core_layer,
begin_coord=(length, half_waveguide_spacing),
end_coord=(length + taper_length, half_waveguide_spacing),
begin_width=taper_width,
end_width=core_width,
)
elems += i3.Wedge(
layer=core_layer,
begin_coord=(length, -half_waveguide_spacing),
end_coord=(length + taper_length, -half_waveguide_spacing),
begin_width=taper_width,
end_width=core_width,
)
# Cladding
elems += i3.Rectangle(
layer=cladding_layer,
center=(0.5 * length, 0.0),
box_size=(length + 2 * taper_length, width + 2.0),
)
return elems
def _generate_ports(self, ports):
length = self.length
taper_length = self.taper_length
trace_template = self.trace_template
half_waveguide_spacing = 0.5 * self.waveguide_spacing
ports += i3.OpticalPort(
name="in1",
position=(-taper_length, 0.0),
angle=180.0,
trace_template=trace_template,
)
ports += i3.OpticalPort(
name="out1",
position=(length + taper_length, -half_waveguide_spacing),
angle=0.0,
trace_template=trace_template,
)
ports += i3.OpticalPort(
name="out2",
position=(length + taper_length, half_waveguide_spacing),
angle=0.0,
trace_template=trace_template,
)
return ports
class Netlist(i3.NetlistFromLayout):
pass
class CircuitModel(i3.CircuitModelView):
center_wavelength = i3.PositiveNumberProperty(doc="Center wavelength")
transmission = i3.NumpyArrayProperty(doc="Polynomial coefficients, transmission as a function of wavelength")
reflection_in = i3.NumpyArrayProperty(
doc="Polynomial coefficients, reflection at input port as a function of wavelength"
)
reflection_out = i3.NumpyArrayProperty(
doc="Polynomial coefficients, reflection at output ports as a function of wavelength"
)
def _default_center_wavelength(self):
return 1.55
def _default_transmission(self):
return [0.707]
def _default_reflection_in(self):
return [0.0]
def _default_reflection_out(self):
return [0.0]
def _generate_model(self):
return MMI1x2Model(
center_wavelength=self.center_wavelength,
transmission=self.transmission,
reflection_in=self.reflection_in,
reflection_out=self.reflection_out,
)
@nb.njit()
def polyval(p, x):
"""Simple polynomial evaluation using Horner's scheme."""
result = p[0]
for c in p[1:]:
result = result * x + c
return result
class MMI1x2Model(i3.CompactModel):
"""Model for a 1x2 MMI.
* center_wavelength: the center wavelength at which the device operates
* reflection_in: polynomial coefficients relating reflection at the input port and wavelength
* reflection_out: polynomial coefficients relating reflection at the output ports and wavelength
* transmission: polynomial coefficients relating transmission and wavelength
"""
parameters = [
"center_wavelength",
"reflection_in",
"reflection_out",
"transmission",
]
terms = [
i3.OpticalTerm(name="in1"),
i3.OpticalTerm(name="out1"),
i3.OpticalTerm(name="out2"),
]
def calculate_smatrix(parameters, env, S):
reflection_in = polyval(parameters.reflection_in, env.wavelength - parameters.center_wavelength)
reflection_out = polyval(parameters.reflection_out, env.wavelength - parameters.center_wavelength)
transmission = polyval(parameters.transmission, env.wavelength - parameters.center_wavelength)
S["in1", "out1"] = S["out1", "in1"] = transmission
S["in1", "out2"] = S["out2", "in1"] = transmission
S["in1", "in1"] = reflection_in
S["out1", "out1"] = S["out2", "out2"] = reflection_out
if __name__ == '__main__':
MMI1x2().Layout().visualize(annotate=True,show=False)
mmi_cm = MMI1x2().CircuitModel()
wavelengths = np.linspace(1.5, 1.6, 501)
S_total = mmi_cm.get_smatrix(wavelengths=wavelengths)
# 2. We plot the transmission
plt.figure(2)
plt.plot(wavelengths, i3.signal_power_dB(S_total["out1", "in1"]), "-", linewidth=2.2, label="in1 -> out1")
plt.plot(wavelengths, i3.signal_power_dB(S_total["out2", "in1"]), "-", linewidth=2.2, label="in1 -> out2")
plt.ylim(-5, 0)
plt.xlabel("Wavelength [um]", fontsize=16)
plt.ylabel("Transmission [dB]", fontsize=16)
plt.legend(fontsize=14, loc=4)
plt.show()
代码中包含:
器件参数:
_name_prefix = "MMI1x2"
trace_template = i3.TraceTemplateProperty(doc="Trace template of the access waveguide")
width = i3.PositiveNumberProperty(default=4.0, doc="Width of the MMI section.")
length = i3.PositiveNumberProperty(default=20.0, doc="Length of the MMI secion.")
taper_width = i3.PositiveNumberProperty(default=1.0, doc="Width of the taper.")
taper_length = i3.PositiveNumberProperty(default=5.0, doc="Length of the taper")
waveguide_spacing = i3.PositiveNumberProperty(default=2.0, doc="Spacing between the waveguides.")
def _default_trace_template(self):
return pdk.SiWireWaveguideTemplate()
器件形状(Layout):
class Layout(i3.LayoutView):
def _generate_elements(self, elems):
length = self.length
width = self.width
taper_length = self.taper_length
taper_width = self.taper_width
half_waveguide_spacing = 0.5 * self.waveguide_spacing
core_layer = self.trace_template.core_layer
cladding_layer = self.trace_template.cladding_layer
core_width = self.trace_template.core_width
# Si core
elems += i3.Rectangle(
layer=core_layer,
center=(0.5 * length, 0.0),
box_size=(length, width),
)
elems += i3.Wedge(
layer=core_layer,
begin_coord=(-taper_length, 0.0),
end_coord=(0.0, 0.0),
begin_width=core_width,
end_width=taper_width,
)
elems += i3.Wedge(
layer=core_layer,
begin_coord=(length, half_waveguide_spacing),
end_coord=(length + taper_length, half_waveguide_spacing),
begin_width=taper_width,
end_width=core_width,
)
elems += i3.Wedge(
layer=core_layer,
begin_coord=(length, -half_waveguide_spacing),
end_coord=(length + taper_length, -half_waveguide_spacing),
begin_width=taper_width,
end_width=core_width,
)
# Cladding
elems += i3.Rectangle(
layer=cladding_layer,
center=(0.5 * length, 0.0),
box_size=(length + 2 * taper_length, width + 2.0),
)
return elems
def _generate_ports(self, ports):
length = self.length
taper_length = self.taper_length
trace_template = self.trace_template
half_waveguide_spacing = 0.5 * self.waveguide_spacing
ports += i3.OpticalPort(
name="in1",
position=(-taper_length, 0.0),
angle=180.0,
trace_template=trace_template,
)
ports += i3.OpticalPort(
name="out1",
position=(length + taper_length, -half_waveguide_spacing),
angle=0.0,
trace_template=trace_template,
)
ports += i3.OpticalPort(
name="out2",
position=(length + taper_length, half_waveguide_spacing),
angle=0.0,
trace_template=trace_template,
)
return ports
器件与外界交互的端口(netlist):
class Netlist(i3.NetlistFromLayout):
pass
器件的行为模型:
class CircuitModel(i3.CircuitModelView):
center_wavelength = i3.PositiveNumberProperty(doc="Center wavelength")
transmission = i3.NumpyArrayProperty(doc="Polynomial coefficients, transmission as a function of wavelength")
reflection_in = i3.NumpyArrayProperty(
doc="Polynomial coefficients, reflection at input port as a function of wavelength"
)
reflection_out = i3.NumpyArrayProperty(
doc="Polynomial coefficients, reflection at output ports as a function of wavelength"
)
def _default_center_wavelength(self):
return 1.55
def _default_transmission(self):
return [0.707]
def _default_reflection_in(self):
return [0.0]
def _default_reflection_out(self):
return [0.0]
def _generate_model(self):
return MMI1x2Model(
center_wavelength=self.center_wavelength,
transmission=self.transmission,
reflection_in=self.reflection_in,
reflection_out=self.reflection_out,
)
该行为模型需要输入的参数包括:中心波长、端口传输谱,以及端口的发射谱,有了这些参数,行为模型可以继承紧凑模型(compactmodel):
紧凑模型(compactmodel)的定义:
class MMI1x2Model(i3.CompactModel):
"""Model for a 1x2 MMI.
* center_wavelength: the center wavelength at which the device operates
* reflection_in: polynomial coefficients relating reflection at the input port and wavelength
* reflection_out: polynomial coefficients relating reflection at the output ports and wavelength
* transmission: polynomial coefficients relating transmission and wavelength
"""
parameters = [
"center_wavelength",
"reflection_in",
"reflection_out",
"transmission",
]
terms = [
i3.OpticalTerm(name="in1"),
i3.OpticalTerm(name="out1"),
i3.OpticalTerm(name="out2"),
]
def calculate_smatrix(parameters, env, S):
reflection_in = polyval(parameters.reflection_in, env.wavelength - parameters.center_wavelength)
reflection_out = polyval(parameters.reflection_out, env.wavelength - parameters.center_wavelength)
transmission = polyval(parameters.transmission, env.wavelength - parameters.center_wavelength)
S["in1", "out1"] = S["out1", "in1"] = transmission
S["in1", "out2"] = S["out2", "in1"] = transmission
S["in1", "in1"] = reflection_in
S["out1", "out1"] = S["out2", "out2"] = reflection_out
紧凑模型包括,1、从行为模型继承的参数:
parameters = [
"center_wavelength",
"reflection_in",
"reflection_out",
"transmission",
]
2、仿真的光学端口:
terms = [
i3.OpticalTerm(name="in1"),
i3.OpticalTerm(name="out1"),
i3.OpticalTerm(name="out2"),
]
3、散射矩阵:
def calculate_smatrix(parameters, env, S):
reflection_in = polyval(parameters.reflection_in, env.wavelength - parameters.center_wavelength)
reflection_out = polyval(parameters.reflection_out, env.wavelength - parameters.center_wavelength)
transmission = polyval(parameters.transmission, env.wavelength - parameters.center_wavelength)
S["in1", "out1"] = S["out1", "in1"] = transmission
S["in1", "out2"] = S["out2", "in1"] = transmission
S["in1", "in1"] = reflection_in
S["out1", "out1"] = S["out2", "out2"] = reflection_out
有了行为模型,就可以对器件进行仿真:
MMI1x2().Layout().visualize(annotate=True,show=False)
mmi_cm = MMI1x2().CircuitModel()
wavelengths = np.linspace(1.5, 1.6, 501)
S_total = mmi_cm.get_smatrix(wavelengths=wavelengths)
# 2. We plot the transmission
plt.figure(2)
plt.plot(wavelengths, i3.signal_power_dB(S_total["out1", "in1"]), "-", linewidth=2.2, label="in1 -> out1")
plt.plot(wavelengths, i3.signal_power_dB(S_total["out2", "in1"]), "-", linewidth=2.2, label="in1 -> out2")
plt.ylim(-5, 0)
plt.xlabel("Wavelength [um]", fontsize=16)
plt.ylabel("Transmission [dB]", fontsize=16)
plt.legend(fontsize=14, loc=4)
plt.show()
仿真结果: