from __future__ import annotations
import gdsfactory as gf
from gdsfactory.cell import cell
from gdsfactory.component import Component
from gdsfactory.components.coupler import coupler as coupler_function
from gdsfactory.components.mmi2x2 import mmi2x2 as mmi_splitter_function
from gdsfactory.components.mzi import mzi2x2_2x2 as mmi_coupler_function
from gdsfactory.components.mzi import mzi_coupler
from gdsfactory.components.taper import taper as taper_function
from gdsfactory.typings import ComponentSpec
[docs]
@cell
def mzi_lattice(
coupler_lengths: tuple[float, ...] = (10.0, 20.0),
coupler_gaps: tuple[float, ...] = (0.2, 0.3),
delta_lengths: tuple[float, ...] = (10.0,),
mzi: ComponentSpec = mzi_coupler,
splitter: ComponentSpec = coupler_function,
**kwargs,
) -> Component:
r"""Mzi lattice filter.
Args:
coupler_lengths: list of length for each coupler.
coupler_gaps: list of coupler gaps.
delta_lengths: list of length differences.
mzi: function for the mzi.
splitter: splitter function.
keyword Args:
length_y: vertical length for both and top arms.
length_x: horizontal length.
bend: 90 degrees bend library.
straight: straight function.
straight_y: straight for length_y and delta_length.
straight_x_top: top straight for length_x.
straight_x_bot: bottom straight for length_x.
cross_section: for routing (sxtop/sxbot to combiner).
.. code::
______ ______
| | | |
| | | |
cp1==| |===cp2=====| |=== .... ===cp_last===
| | | |
| | | |
DL1 | DL2 |
| | | |
|______| | |
|______|
"""
if len(coupler_lengths) != len(coupler_gaps):
raise ValueError(
f"Got {len(coupler_lengths)} coupler_lengths and "
f"{len(coupler_gaps)} coupler_gaps"
)
if len(coupler_lengths) != len(delta_lengths) + 1:
raise ValueError(
f"Got {len(coupler_lengths)} coupler_lengths and "
f"{len(delta_lengths)} delta_lengths. "
"You need one more coupler_length than delta_lengths "
)
c = Component()
splitter_settings = dict(gap=coupler_gaps[0], length=coupler_lengths[0])
combiner_settings = dict(gap=coupler_gaps[1], length=coupler_lengths[1])
cp1 = splitter1 = gf.get_component(splitter, **splitter_settings)
combiner1 = gf.get_component(splitter, **combiner_settings)
sprevious = c << gf.get_component(
mzi,
splitter=splitter1,
combiner=combiner1,
with_splitter=True,
delta_length=delta_lengths[0],
**kwargs,
)
c.add_ports(sprevious.get_ports_list(port_type="electrical"))
stages = []
for length, gap, delta_length in zip(
coupler_lengths[2:], coupler_gaps[2:], delta_lengths[1:]
):
splitter_settings = dict(gap=coupler_gaps[1], length=coupler_lengths[1])
combiner_settings = dict(length=length, gap=gap)
splitter1 = gf.get_component(splitter, **splitter_settings)
combiner1 = gf.get_component(splitter, **combiner_settings)
stage = c << gf.get_component(
mzi,
splitter=splitter1,
combiner=combiner1,
with_splitter=False,
delta_length=delta_length,
**kwargs,
)
splitter_settings = combiner_settings
stages.append(stage)
c.add_ports(stage.get_ports_list(port_type="electrical"))
for stage in stages:
stage.connect("o1", sprevious.ports["o4"])
# stage.connect('o2', sprevious.ports['o1'])
sprevious = stage
for port in cp1.get_ports_list(orientation=180, port_type="optical"):
c.add_port(port.name, port=port)
for port in sprevious.get_ports_list(orientation=0, port_type="optical"):
c.add_port(f"o_{port.name}", port=port)
c.auto_rename_ports()
return c
[docs]
@cell
def mzi_lattice_mmi(
coupler_widths=(None, None),
coupler_widths_tapers: tuple[float, ...] = (
1.0,
1.0,
),
coupler_lengths_tapers: tuple[float, ...] = (
10.0,
10.0,
),
coupler_lengths_mmis: tuple[float, ...] = (
5.5,
5.5,
),
coupler_widths_mmis: tuple[float, ...] = (
2.5,
2.5,
),
coupler_gaps_mmis: tuple[float, ...] = (
0.25,
0.25,
),
taper_functions_mmis=(
taper_function,
taper_function,
),
cross_sections_mmis=("xs_sc", "xs_sc"),
delta_lengths: tuple[float, ...] = (10.0,),
mzi=mmi_coupler_function,
splitter=mmi_splitter_function,
**kwargs,
) -> Component:
r"""Mzi lattice filter, with MMI couplers.
Args:
coupler_widths: (for each MMI coupler, list of) input and output straight width.
coupler_widths_tapers: (for each MMI coupler, list of) interface between input straights and mmi region.
coupler_lengths_tapers: (for each MMI coupler, list of) into the mmi region.
coupler_lengths_mmis: (for each MMI coupler, list of) in x direction.
coupler_widths_mmis: (for each MMI coupler, list of) in y direction.
coupler_gaps_mmis: (for each MMI coupler, list of) (width_taper + gap between tapered wg)/2.
taper_functions_mmis: (for each MMI coupler, list of) taper function.
cross_sections_mmis: (for each MMI coupler, list of) spec.
delta_lengths: list of length differences.
mzi: function for the mzi.
splitter: splitter function.
keyword Args:
length_y: vertical length for both and top arms.
length_x: horizontal length.
bend: 90 degrees bend library.
straight: straight function.
straight_y: straight for length_y and delta_length.
straight_x_top: top straight for length_x.
straight_x_bot: bottom straight for length_x.
cross_section: for routing (sxtop/sxbot to combiner).
.. code::
______ ______
| | | |
| | | |
cp1==| |===cp2=====| |=== .... ===cp_last===
| | | |
| | | |
DL1 | DL2 |
| | | |
|______| | |
|______|
"""
length = len(coupler_widths)
if all(
len(lst) != length
for lst in [
coupler_widths_tapers,
coupler_lengths_tapers,
coupler_lengths_mmis,
coupler_widths_mmis,
coupler_gaps_mmis,
taper_functions_mmis,
cross_sections_mmis,
]
):
raise ValueError("All MMI-related argument lists must be the same length.")
if len(coupler_widths) != len(delta_lengths) + 1:
raise ValueError(
f"Got {len(coupler_widths)} coupler_widths and "
f"{len(delta_lengths)} delta_lengths. "
"You need one more coupler_width than delta_lengths "
)
c = Component()
splitter_settings = dict(
width=coupler_widths[0],
width_taper=coupler_widths_tapers[0],
length_taper=coupler_lengths_tapers[0],
length_mmi=coupler_lengths_mmis[0],
width_mmi=coupler_widths_mmis[0],
gap_mmi=coupler_gaps_mmis[0],
taper=taper_functions_mmis[0],
cross_section=cross_sections_mmis[0],
)
combiner_settings = dict(
width=coupler_widths[1],
width_taper=coupler_widths_tapers[1],
length_taper=coupler_lengths_tapers[1],
length_mmi=coupler_lengths_mmis[1],
width_mmi=coupler_widths_mmis[1],
gap_mmi=coupler_gaps_mmis[1],
taper=taper_functions_mmis[1],
cross_section=cross_sections_mmis[1],
)
cp1 = splitter1 = gf.get_component(splitter, **splitter_settings)
combiner1 = gf.get_component(splitter, **combiner_settings)
sprevious = c << gf.get_component(
mzi,
splitter=splitter1,
combiner=combiner1,
with_splitter=True,
delta_length=delta_lengths[0],
**kwargs,
)
c.add_ports(sprevious.get_ports_list(port_type="electrical"))
stages = []
for (
coupler_width,
coupler_width_taper,
coupler_length_taper,
coupler_length_mmi,
coupler_width_mmi,
coupler_gap_mmi,
taper,
cross_section,
delta_length,
) in zip(
coupler_widths[2:],
coupler_widths_tapers[2:],
coupler_lengths_tapers[2:],
coupler_lengths_mmis[2:],
coupler_widths_mmis[2:],
coupler_gaps_mmis[2:],
taper_functions_mmis[2:],
cross_sections_mmis[2:],
delta_lengths[1:],
):
splitter_settings = dict(
width=coupler_widths[1],
width_taper=coupler_widths_tapers[1],
length_taper=coupler_lengths_tapers[1],
length_mmi=coupler_lengths_mmis[1],
width_mmi=coupler_widths_mmis[1],
gap_mmi=coupler_gaps_mmis[1],
taper=taper_functions_mmis[1],
cross_section=cross_sections_mmis[1],
)
combiner_settings = dict(
width=coupler_width,
width_taper=coupler_width_taper,
length_taper=coupler_length_taper,
length_mmi=coupler_length_mmi,
width_mmi=coupler_width_mmi,
gap_mmi=coupler_gap_mmi,
taper=taper,
cross_section=cross_section,
)
splitter1 = gf.get_component(splitter, **splitter_settings)
combiner1 = gf.get_component(splitter, **combiner_settings)
stage = c << gf.get_component(
mzi,
splitter=splitter1,
combiner=combiner1,
with_splitter=False,
delta_length=delta_length,
**kwargs,
)
splitter_settings = combiner_settings
stages.append(stage)
c.add_ports(stage.get_ports_list(port_type="electrical"))
for stage in stages:
stage.connect("o1", sprevious.ports["o4"])
# stage.connect('o2', sprevious.ports['o1'])
sprevious = stage
for port in cp1.get_ports_list(orientation=180, port_type="optical"):
c.add_port(port.name, port=port)
for port in sprevious.get_ports_list(orientation=0, port_type="optical"):
c.add_port(f"o_{port.name}", port=port)
c.auto_rename_ports()
return c
if __name__ == "__main__":
cpl = [10, 20, 30]
cpg = [0.1, 0.2, 0.3]
dl0 = [100, 200]
# cpl = [10, 20, 30, 40]
# cpg = [0.2, 0.3, 0.5, 0.5]
# dl0 = [0, 50, 100]
# c = mzi_lattice(
# coupler_lengths=cpl, coupler_gaps=cpg, delta_lengths=dl0, length_x=1
# )
# c = mzi_lattice(delta_lengths=(20,))
# c.show(show_ports=True)
c = mzi_lattice_mmi(
coupler_widths=(None,) * 5,
coupler_widths_tapers=(1.0,) * 5,
coupler_lengths_tapers=(10.0,) * 5,
coupler_lengths_mmis=(5.5,) * 5,
coupler_widths_mmis=(2.5,) * 5,
coupler_gaps_mmis=(0.25,) * 5,
taper_functions_mmis=(taper_function,) * 5,
cross_sections_mmis=("xs_sc",) * 5,
delta_lengths=(10.0,) * 4,
)
c.show(show_ports=True)