1.1. MZI lattice filter¶
In this section, we will explain how to design a lattice filter based on Mach-Zender interferometers (MZI).
1.1.1. Directional coupler¶
The basic building block of an MZI is the directional coupler.
In this case, we use DirectionalCouplerSPower
from SiFab, the demonstration pdk distributed with this training material.
This class is useful as it has a precomputed model, which is used to calculate the correct length of the directional
coupler for a given power coupling coefficient into the cross arm.
We can visualize this component, run a circuit simulation and plot the transmission.
For more info on directional couplers available in SiFab,
have a look at Directional coupler.
from si_fab import all as pdk
import numpy as np
import pylab as plt
dc = pdk.DirectionalCouplerSPower(power_fraction=0.5,
target_wavelength=1.55)
dc_lv = dc.Layout()
dc_lv.visualize(annotate=True)
dc_cm = dc.CircuitModel()
wavelengths = np.linspace(1.51, 1.6, 500)
S = dc_cm.get_smatrix(wavelengths=wavelengths)
plt.figure()
plt.plot(wavelengths, np.abs(S["in1", "out1"])**2, linewidth=2.2, label="in1-out1 (through)")
plt.plot(wavelengths, np.abs(S["in1", "out2"])**2, linewidth=2.2, label="in1-out2 (drop)")
plt.axvline(x=dc.target_wavelength)
plt.title("Power transmission", fontsize=16)
plt.xlabel("Wavelength", fontsize=16)
plt.ylabel("Power", fontsize=16)
plt.xlim(1.5, 1.6)
plt.legend(fontsize=14, loc=1)
plt.legend()
plt.show()


1.1.2. MZI lattice filter¶
Now we can use this directional coupler to build an MZI lattice filter.
To achieve this, we define a Python class that inherits from CircuitCell
and we call it MZILatticeFilter
.
This lattice filter is a parametric circuit with customizable number of cascaded MZIs.
The lengths of the directional couplers used for the MZIs are automatically calculated by providing a list of the power
coupling coefficients.
class MZILatticeFilter(CircuitCell):
""" Mach-Zender interferometer lattice filter based on directional couplers with different power
coupling.
The number of power couplings should be equal to the number of delay lengths plus 1.
"""
directional_couplers = i3.ChildCellListProperty(doc="list of directional couplers")
center_wavelength = i3.PositiveNumberProperty(default=1.55, doc="Center wavelength")
delay_lengths = i3.ListProperty(default=[100.], doc="List of delay lengths")
bend_radius = i3.PositiveNumberProperty(default=5.0, doc="Bend radius")
phase_error_width_deviation = i3.NonNegativeNumberProperty(default=0.0)
phase_error_correlation_length = i3.NonNegativeNumberProperty(default=0.0)
def _default_directional_couplers(self):
power_couplings = [0.5, 0.5]
dir_couplers = [pdk.DirectionalCouplerSPower(name=self.name + "dc_{}".format(cnt),
power_fraction=p,
target_wavelength=self.center_wavelength)
for cnt, p in enumerate(power_couplings)]
return dir_couplers
def get_connector(self, offset_high):
from circuit.connector_functions import get_template
def connector(start_port, end_port, name=None, shape=None, connector_kwargs={}):
min_straight = self.bend_radius
tt = get_template(start_port, end_port)
tt_new = tt.cell.modified_copy()
tt_new.CircuitModel(phase_error_width_deviation=self.phase_error_width_deviation,
phase_error_correlation_length=self.phase_error_correlation_length)
shape = [
start_port.position,
start_port.position + (min_straight, 0.0),
start_port.position + (min_straight, offset_high),
end_port.position + (-min_straight, offset_high),
end_port.position - (min_straight, 0.0),
end_port.position,
]
cell = i3.RoundedWaveguide(name=name, trace_template=tt_new)
cell.Layout(shape=shape, bend_radius=self.bend_radius, manhattan=True)
return cell
return connector
def _default_child_cells(self):
child_cells = dict()
for cnt, dc in enumerate(self.directional_couplers):
child_cells["dc_{}".format(cnt)] = dc
return child_cells
def _default_connectors(self):
conn = []
for cnt, delay_length in enumerate(self.delay_lengths):
if delay_length > 0:
l_top = delay_length / 2
l_bot = 0
else:
l_top = 0
l_bot = - delay_length / 2
p1 = "dc_{}:out1".format(cnt)
p2 = "dc_{}:in1".format(cnt + 1)
conn.append((p1, p2, self.get_connector(offset_high=- 2 * self.bend_radius - l_bot)))
p1 = "dc_{}:out2".format(cnt)
p2 = "dc_{}:in2".format(cnt + 1)
conn.append((p1, p2, self.get_connector(offset_high=2 * self.bend_radius + l_top)))
return conn
def _default_place_specs(self):
distance = 4 * self.bend_radius
place_specs = []
for cnt in range(1, len(self.directional_couplers)):
spec = i3.PlaceRelative("dc_{}:in1".format(cnt), "dc_{}:out1".format(cnt - 1), (distance, 0))
place_specs.append(spec)
return place_specs
def _default_external_port_names(self):
epn = {"dc_0:in1": "in1",
"dc_0:in2": "in2",
"dc_{}:out1".format(len(self.delay_lengths)): "out1",
"dc_{}:out2".format(len(self.delay_lengths)): "out2"}
return epn
Let’s analyze the code above:
- Properties. We defined a list of properties that are used to design the MZI lattice filters.
The length of the list provided in
power_couplings
determines how many directional couplers are used to design the lattice filter. If 5 values are provided for the power coupling coefficients, then 4 values should be provided for thedelay_lengths
to be used to connect the 5 directional couplers to each other. The phase error properties can be used to perform a variability analysis of the MZI lattice filter. - Default directional couplers. In
_default_directional_couplers
we instantiate as many directional couplers as providedpower_couplings
. As mentioned earlier, the default directional couplers used in this example isDirectionalCouplerSPower
from SiFab. get_connector
function. This function returns a connector which is used to draw the arms connecting the directional couplers to each other.- CircuitCell properties. Next, the properties needed to create a circuit with
CircuitCell
are specified:- The directional couplers are added to the dictionary of child cells in
_default_child_cells
. - The connectors between the directional couplers are specified in
_default_connectors
. The functionget_connector
is used inside this method to obtain arms with length given by the values given bydelay_lengths
. - The placement of the directional couplers is defined in
_default_place_specs
. - The external ports of the final MZI lattice filter are exposed in
_default_external_port_names
.
- The directional couplers are added to the dictionary of child cells in
The connectivity defined in CircuitCell is automatically used to extract the netlist of the circuit and to perform circuit simulations.