Examples

Broadband Directional Coupler

A 50/50 directional coupler made broadband by an asymmetric phase-control section between two symmetric couplers.

Compiling…
DirectionalCoupler from laylight/directional_coupler.lyl, compiled in your browserOpen in playground

A directional coupler transfers light between two waveguides running close together; the fraction it transfers depends on the coupling length and, since the coupling strength changes with wavelength, the split ratio of a plain coupler drifts across the band. Lu et al. make it broadband by putting a phase-control section between two symmetric couplers: there the two waveguides have different widths, so the light in them accumulates a wavelength-dependent phase difference that compensates the drift of the coupling.

The example is their TE 50/50 design. Two 500 nm waveguides with a 200 nm gap run side by side for 12.4 µm, taper over 1 µm to a 600 nm and a 400 nm waveguide with a 300 nm gap for 4.6 µm, taper back and couple again for 12.4 µm: a coupling region 31.4 µm long. A 90° bend of 5 µm radius on each of the four ports turns the waveguides away from each other. The paper does not say which waveguide of the phase-control section is the wide one; the example widens the upper one and keeps the gap centered on the axis.

References

  • Z. Lu et al., "Broadband silicon photonic directional coupler using asymmetric-waveguide based phase control," Optics Express 23(3), 3795 (2015). doi:10.1364/OE.23.003795

Parameters

ParameterValueSource
Symmetric couplers2 × 500 nm, 200 nm gap, L1 = 12.4 µm eachLu 2015, Sec. 2, Table 1
Phase-control section600 nm and 400 nm, 300 nm gap, L2 = 4.6 µmLu 2015, Sec. 2, Table 1
Tapers between the sections1 µm, linearLu 2015, Sec. 2
Port bends90°, radius 5 µmLu 2015, Sec. 2
Wide waveguide on top, gap centeredassumed

How it is written

Each waveguide through the coupling region is one polygon of 12 points, written out in a loop over the two sides: the side's sign mirrors the inner and outer edges, and the width of the phase-control section is picked from a list. The port bends are arc primitives placed by their end and start on the ends of the polygon. The gdstk version is about as short, since a RobustPath can taper its width and offset along each section; gdsfactory and KLayout build the outline with numpy.

Implementations

ImplementationLines of codeTokensLayout
Laylight28258reference
gdsfactory28 (1.00×)294 (1.14×)same within 3 nm
gdstk31 (1.11×)259 (1.00×)same within 3 nm
KLayout29 (1.04×)340 (1.32×)same within 3 nm
laylight/directional_coupler.lyl
import "tech.lyl"

# A broadband 50/50 directional coupler after Lu et al., Opt. Express 2015: two
# symmetric couplers around a phase-control section of a wide (upper) and a
# narrow waveguide, joined by linear tapers, with a 90 deg bend on every port.
cell DirectionalCoupler(
    width = 500nm,  # symmetric couplers
    gap = 200nm,
    length = 12.4um,  # L1, each symmetric coupler
    wide_width = 600nm,  # phase-control section
    narrow_width = 400nm,
    phase_gap = 300nm,
    phase_length = 4.6um,  # L2
    taper_length = 1um,
    radius = 5um,
) {
    layer si {
        define x1 = phase_length / 2   # where the sections end, from the centre
        define x2 = x1 + taper_length
        define x3 = x2 + length
        define phase_widths = [wide_width, narrow_width]
        for i in 0..2 {
            define s = 1 - 2 * i   # the upper waveguide, then the lower one
            define g = s * gap / 2   # inner edges: the gap stays centred
            define p = s * phase_gap / 2
            define o = g + s * width   # outer edges
            define q = p + s * phase_widths[i]
            shape polygon with [
                (-x3, g), (-x2, g), (-x1, p), (x1, p), (x2, g), (x3, g),
                (x3, o), (x2, o), (x1, q), (-x1, q), (-x2, o), (-x3, o)
            ] as guide at self.origin
            shape arc with radius, width, s * 90deg as left.end at (-x3, (g + o) / 2)
            clone left as right.start at (x3, (g + o) / 2)
        }
    }
}
gdsfactory/directional_coupler.py
"""A broadband directional coupler, as laylight/directional_coupler.lyl."""

import gdsfactory as gf
import numpy as np

from tech import SI


@gf.cell
def directional_coupler(
    width=0.5,
    gap=0.2,
    length=12.4,
    wide_width=0.6,
    narrow_width=0.4,
    phase_gap=0.3,
    phase_length=4.6,
    taper_length=1,
    radius=5,
):
    """Two symmetric couplers around a phase-control section of a wide and a
    narrow waveguide, joined by linear tapers, with a 90 deg bend on every port.
    The gap stays centred on the axis; the upper waveguide is the wide one."""
    x = np.cumsum([0, length, taper_length, phase_length, taper_length, length])
    x -= x[-1] / 2
    inner = np.array([gap, gap, phase_gap, phase_gap, gap, gap]) / 2
    xs = gf.cross_section.cross_section(width=width, layer=SI, radius_min=radius)
    c = gf.Component()
    for s, phase_width in [(1, wide_width), (-1, narrow_width)]:
        outer = inner + [width, width, phase_width, phase_width, width, width]
        outline = np.column_stack([np.r_[x, x[::-1]], s * np.r_[inner, outer[::-1]]])
        c.add_polygon(outline, layer=SI)
        y = s * (gap + width) / 2
        bend = gf.components.bend_circular(radius, angle=s * 90, cross_section=xs)
        (c << bend).dmove((x[-1], y))
        (c << bend).dmirror_x().dmove((x[0], y))
    return c


if __name__ == "__main__":
    import sys

    directional_coupler().write_gds(sys.argv[1])
gdstk/directional_coupler.py
"""A broadband directional coupler, as laylight/directional_coupler.lyl."""

import gdstk
import numpy as np

from tech import SI


def directional_coupler(
    width=0.5,
    gap=0.2,
    length=12.4,
    wide_width=0.6,
    narrow_width=0.4,
    phase_gap=0.3,
    phase_length=4.6,
    taper_length=1,
    radius=5,
):
    """Two symmetric couplers around a phase-control section of a wide and a
    narrow waveguide, joined by linear tapers, with a 90 deg bend on every port.
    The gap stays centred on the axis; the upper waveguide is the wide one."""
    x1 = phase_length / 2  # where the sections end, from the centre
    x2 = x1 + taper_length
    x3 = x2 + length
    cell = gdstk.Cell("DirectionalCoupler")
    for s, phase_width in [(1, wide_width), (-1, narrow_width)]:
        y = s * (gap + width) / 2
        offset = s * (phase_gap + phase_width - gap - width) / 2  # of the centreline
        guide = gdstk.RobustPath(
            (-x3 - radius, y + s * radius), width, tolerance=1e-3, **SI
        )
        guide.arc(radius, np.pi, np.pi + s * np.pi / 2)
        guide.segment((-x2, y))
        guide.segment((-x1, y), phase_width, [offset])  # a list: absolute offset
        guide.segment((x1, y))
        guide.segment((x2, y), width, [0])
        guide.segment((x3, y))
        guide.arc(radius, -s * np.pi / 2, 0)
        cell.add(guide)
    return cell


if __name__ == "__main__":
    import sys

    top = directional_coupler()
    lib = gdstk.Library()
    lib.add(top, *top.dependencies(True))
    lib.write_gds(sys.argv[1])
klayout/directional_coupler.py
"""A broadband directional coupler, as laylight/directional_coupler.lyl."""

import klayout.db as kdb
import numpy as np

from tech import SI, layout
from waveguides import path, polygon


def directional_coupler(
    width=0.5,
    gap=0.2,
    length=12.4,
    wide_width=0.6,
    narrow_width=0.4,
    phase_gap=0.3,
    phase_length=4.6,
    taper_length=1,
    radius=5,
):
    """Two symmetric couplers around a phase-control section of a wide and a
    narrow waveguide, joined by linear tapers, with a 90 deg bend on every port.
    The gap stays centred on the axis; the upper waveguide is the wide one."""
    x = np.cumsum([0, length, taper_length, phase_length, taper_length, length])
    x -= x[-1] / 2
    inner = np.array([gap, gap, phase_gap, phase_gap, gap, gap]) / 2
    a = np.linspace(0, np.pi / 2, 200)
    bend = path(np.column_stack([np.sin(a), 1 - np.cos(a)]) * radius, width)
    cell = layout.create_cell("DirectionalCoupler")
    si = cell.shapes(layout.layer(SI))
    for s, phase_width in [(1, wide_width), (-1, narrow_width)]:
        outer = inner + [width, width, phase_width, phase_width, width, width]
        outline = np.column_stack([np.r_[x, x[::-1]], s * np.r_[inner, outer[::-1]]])
        si.insert(polygon(outline))
        y = s * (gap + width) / 2
        si.insert(bend.transformed(kdb.DCplxTrans(1, 0, s < 0, x[-1], y)))
        si.insert(bend.transformed(kdb.DCplxTrans(1, 180, s > 0, x[0], y)))
    return cell


if __name__ == "__main__":
    import sys

    directional_coupler()
    layout.write(sys.argv[1])

On this page