Skip to content

S-Bend MMI Route

This example connects the output of a 1×2 MMI splitter to the input of a second MMI using a route that contains both explicit corner bends and a spliced S-bend.

The two MMIs are placed so that their ports sit at different heights — a common layout constraint when building photonic circuits on a fixed grid. A regular rectilinear route would use three 90° bends to handle the offset; the S-bend replaces the middle jog with a single smooth element, reducing back-reflections and tightening the footprint.

Two variants are shown:

  1. Microns (µm)add_route_from_corners_with_sbends — the recommended user-facing API, consistent with add_route_manual.
  2. Database units (dbu)add_route_from_corners_with_sbends_dbu — the low-level variant for when you already work in dbu (1 dbu = 1 nm in the generic PDK, so 1 µm = 1 000 dbu).

Imports

from functools import partial

import gdsfactory as gf
from gdsfactory.gpdk import PDK

from doroutes.routing import (
    add_route_from_corners_with_sbends,
    add_route_from_corners_with_sbends_dbu,
)

PDK.activate()

Component factories

We use the generic PDK's strip cross-section throughout. bend_euler is the standard 90° corner; bend_s is the S-bend element that will be spliced in where marked.

_BEND = partial(gf.get_component, "bend_euler", radius=5, cross_section="strip")
_STRAIGHT = partial(gf.get_component, "straight", cross_section="strip")
_BEND_S = partial(gf.get_component, "bend_s", cross_section="strip")

Floorplan

mmi_in sits at the origin. mmi_out is placed 80 µm to the right and 50 µm above, giving a 50 µm vertical offset between the two input/output ports. We will route mmi_in.o2mmi_out.o1.

Port positions (in µm): - mmi_in.o2 : (15.5, 0.625), angle 0° (east) - mmi_out.o1: (70.0, 50.0), angle 180° (west)

c = gf.Component("sbend_mmi_route")
mmi_in = c << gf.get_component("mmi1x2")
mmi_out = c << gf.get_component("mmi1x2")
mmi_out.dmove((80, 50))
sbend_mmi_route: ports ['o1', 'o2', 'o3'], KCell(name=mmi1x2_gdsfactorypcomponentspmmispmmi1x2_WNone_WT1_LT10_1f097353, ports=['o1', 'o2', 'o3'], pins=[], instances=[], locked=True, kcl=DEFAULT)

Route with corners + S-bend (µm)

The route is defined in microns — the standard user-facing unit.

mmi_in.o2
   │ east
   ├──────────────────┐ c0 (30, 0.625) — corner: east → north
                      │ north
                      │ (30, 10) — s-bend starts here (all_points[2])
                      ╭─────╮  s-bend: propagates north 30 µm,
                      │     │          shifts east 10 µm
                      ╰─────╯  (40, 40) — s-bend ends (all_points[5])
                      │ north
                      │ (40, 50) — corner: north → east
                      └──────────────────── mmi_out.o1

Corner indices in sbend_jogs are all_points indices where all_points = [start, *corners, stop]. Index 2 refers to corners[1] — the point where the S-bend begins. The S-bend spans all_points[2..5], i.e. through the two topology corners at indices 3 and 4.

corners_um = [
    (30.0, 0.625),  # c0 — east → north corner           (all_points[1])
    (30.0, 10.0),  # c1 — north segment end / sbend in  (all_points[2])
    (30.0, 25.0),  # c2 — Z topology corner 1           (all_points[3])
    (40.0, 25.0),  # c3 — Z topology corner 2           (all_points[4])
    (40.0, 40.0),  # c4 — sbend out                     (all_points[5])
    (40.0, 50.0),  # c5 — north → east corner           (all_points[6])
]

add_route_from_corners_with_sbends(
    component=c,
    start=mmi_in.ports["o2"],
    stop=mmi_out.ports["o1"],
    corners=corners_um,
    straight=_STRAIGHT,
    bend=_BEND,
    sbend_jogs={2: {"factory": _BEND_S}},
)

Result (µm)

import doroutes as dr

dr.util.show_cell(c)

Route with corners + S-bend (dbu)

The same route expressed in database units. This is the low-level API — useful when coordinates come from a layout engine or other dbu-native code.

c_dbu = gf.Component("sbend_mmi_route_dbu")
mmi_in_dbu = c_dbu << gf.get_component("mmi1x2")
mmi_out_dbu = c_dbu << gf.get_component("mmi1x2")
mmi_out_dbu.dmove((80, 50))

corners_dbu = [
    (30_000, 625),  # c0 — east → north corner           (all_points[1])
    (30_000, 10_000),  # c1 — north segment end / sbend in  (all_points[2])
    (30_000, 25_000),  # c2 — Z topology corner 1           (all_points[3])
    (40_000, 25_000),  # c3 — Z topology corner 2           (all_points[4])
    (40_000, 40_000),  # c4 — sbend out                     (all_points[5])
    (40_000, 50_000),  # c5 — north → east corner           (all_points[6])
]

add_route_from_corners_with_sbends_dbu(
    component=c_dbu,
    start=mmi_in_dbu.ports["o2"],
    stop=mmi_out_dbu.ports["o1"],
    corners=corners_dbu,
    straight=_STRAIGHT,
    bend=_BEND,
    sbend_jogs={2: {"factory": _BEND_S}},
)

Result (dbu)

dr.util.show_cell(c_dbu)