Examples

Thin-Film Lithium Niobate Modulator

A lithium niobate Mach-Zehnder modulator with micro-structured travelling-wave electrodes of T-shaped segments.

Compiling…
TFLNModulator from laylight/tfln_modulator.lyl, compiled in your browserOpen in playground

Thin-film lithium niobate (TFLN) modulators use the strong electro-optic effect of lithium niobate in a waveguide only about a micrometer wide, so that the electrodes can sit a few micrometers from the light. A Mach-Zehnder interferometer has one arm in each gap of a ground-signal-ground travelling-wave electrode. Bringing the electrodes closer lowers the drive voltage but raises the microwave loss, which limits the bandwidth. Kharel et al. break this trade-off with micro-structured electrodes: rows of T-shaped segments along the rail edges carry the field to the waveguide across a 5 µm gap, while the rails themselves stay far apart and keep the microwave loss of a wide electrode.

The example draws the modulator of the paper's Fig. 1: a 100 µm signal rail between two ground rails, 10 mm long, and on each of the four edges that face a waveguide a row of T-shaped segments with a period of 50 µm (6 µm stems, 45 µm × 2 µm heads, 5 µm between heads), whose heads face each other across the 5 µm gap. The 1.2 µm lithium niobate ridges run through the middle of the two gaps; at both ends S-bends from a common point form Y-branches. Each rail tapers into a probe pad at both ends.

References

  • P. Kharel, C. Reimer, K. Luke, L. He and M. Zhang, "Breaking voltage–bandwidth limits in integrated lithium niobate modulators using micro-structured electrodes," Optica 8(3), 357 (2021). doi:10.1364/OPTICA.416155

Parameters

ParameterValueSource
Electrode length10 mmKharel 2021, Sec. III (10 and 20 mm devices)
Signal width, gap100 µm, 5 µm between T-headsKharel 2021, Table I
T-segmentsstem 6 × 6 µm, head 45 × 2 µm, 5 µm between heads (50 µm period)Kharel 2021, Fig. 1
SplittersY-branchesKharel 2021, Fig. 1(a)
Ground width150 µmassumed
Ridge width1.2 µmassumed
S-bends, pad tapers, pads500 µm, 100 µm, 80 µmassumed

How it is written

The T-segments are the heart of the device: one loop runs over the four rail edges, each given as a frame whose direction points from the rail into the gap, and an inner loop places a stem on the edge and a head on the stem's far side. Because the frame carries the direction, the same two lines draw segments that point up on one edge and down on the next. A second loop over the three rails tapers each one into a pad at both ends, reading the rail's height for the taper. Laylight's version is the shortest of the four, in lines and in tokens.

Implementations

ImplementationLines of codeTokensLayout
Laylight48401reference
gdsfactory61 (1.27×)582 (1.45×)same within 2 nm
gdstk60 (1.25×)566 (1.41×)same within 2 nm
KLayout61 (1.27×)666 (1.66×)same within 2 nm
laylight/tfln_modulator.lyl
import "tech.lyl"

# A thin-film lithium niobate Mach-Zehnder modulator with micro-structured
# (T-shaped) travelling-wave electrodes, after Kharel et al., Optica 2021.
cell TFLNModulator(
    length = 10mm,  # electrode length
    signal_width = 100um,
    ground_width = 150um,
    gap = 5um,  # g: between the T-heads on either side of an arm
    stem_length = 6um,  # h
    stem_width = 6um,  # t
    head_width = 2um,  # s
    head_length = 45um,  # r
    head_gap = 5um,  # c: between neighbouring T-heads
    width = 1.2um,  # LN ridge
    sbend_length = 500um,
    taper_length = 100um,  # from the electrodes to the probe pads
    pad_size = 80um,
) {
    attribute {
        channel gap + 2 * (stem_length + head_width)   # between the rails
    }

    # Ground-signal-ground rails with rows of T-shaped segments along the four
    # edges that face an arm, tapered to probe pads at both ends.
    layer metal {
        shape rect with length, signal_width as signal.left_center at self.origin
        shape rect with length, ground_width as upper
        place upper.bottom_left at signal.top_left offset (0, channel)
        clone upper as lower.top_left at signal.bottom_left offset (0, -channel)

        define y = signal_width / 2
        define pitch = head_length + head_gap
        for edge in [
            (0, y, 90deg),
            (0, -y, -90deg),
            (0, y + channel, -90deg),
            (0, -y - channel, 90deg)
        ] {
            for i in 0..round(length / pitch) {
                shape rect with stem_length, stem_width as stem
                place stem.left_center at edge + ((i + 0.5) * pitch, 0)
                shape rect with head_width, head_length as head
                place head.left_center at stem.right_center
            }
        }

        for line in [signal, upper, lower] {
            shape taper with taper_length, line.height, pad_size as in_taper
            place in_taper.start at line.left_center rotate 180deg
            clone in_taper as out_taper.start at line.right_center
            shape rect with pad_size, pad_size as in_pad.left_center at in_taper.end
            clone in_pad as out_pad.left_center at out_taper.end
        }
    }

    # The arms run in the middle of the gaps; S-bends from a common point
    # form the Y-branches that split and combine the light.
    layer ln {
        for side in [1, -1] {
            define y = side * (signal_width + channel) / 2
            shape rect with length + 2 * (taper_length + pad_size), width as arm
            place arm.center at (length / 2, y)
            shape sbend with sbend_length, y, width as split.end at arm.left_center
            shape sbend with sbend_length, -y, width as combine
            place combine.start at arm.right_center
        }
    }
}
gdsfactory/tfln_modulator.py
"""A thin-film lithium niobate Mach-Zehnder modulator with T-shaped travelling-wave
electrodes, as laylight/tfln_modulator.lyl."""

import gdsfactory as gf

from tech import LN, METAL


@gf.cell
def tee(stem_length=6, stem_width=6, head_width=2, head_length=45):
    """A T-shaped electrode segment pointing up from (0, 0)."""
    c = gf.Component()
    stem = c << gf.components.rectangle((stem_width, stem_length), layer=METAL)
    head = c << gf.components.rectangle((head_length, head_width), layer=METAL)
    stem.dmove((-stem_width / 2, 0))
    head.dmove((-head_length / 2, stem_length))
    return c


@gf.cell
def tfln_modulator(
    length=10000,
    signal_width=100,
    ground_width=150,
    gap=5,
    stem_length=6,
    stem_width=6,
    head_width=2,
    head_length=45,
    head_gap=5,
    width=1.2,
    sbend_length=500,
    taper_length=100,
    pad_size=80,
):
    """GSG electrodes with rows of T-shaped segments along the edges that face the
    arms, tapered to probe pads; the arms are split and combined by Y-branches."""
    c = gf.Component()
    channel = gap + 2 * (stem_length + head_width)  # between the rails
    pitch = head_length + head_gap
    segment = tee(stem_length, stem_width, head_width, head_length)
    y = signal_width / 2
    for edge, mirror in [
        (y, False),
        (-y, True),
        (y + channel, True),
        (-y - channel, False),
    ]:
        row = c.add_ref(segment, columns=round(length / pitch), column_pitch=pitch)
        if mirror:
            row.dmirror_y()
        row.dmove((pitch / 2, edge))
    # Each line with its tapers and pads as one outline.
    end = taper_length + pad_size
    x = [-end, -taper_length, 0, length, length + taper_length, length + end]
    y_ground = y + channel + ground_width / 2
    for yc, w in [
        (0, signal_width),
        (y_ground, ground_width),
        (-y_ground, ground_width),
    ]:
        half = [pad_size / 2, pad_size / 2, w / 2, w / 2, pad_size / 2, pad_size / 2]
        upper = [(xi, yc + h) for xi, h in zip(x, half)]
        lower = [(xi, yc - h) for xi, h in zip(x, half)]
        c.add_polygon(upper + lower[::-1], layer=METAL)
    xs = gf.cross_section.cross_section(width=width, layer=LN)
    for ya in [y + channel / 2, -y - channel / 2]:
        arm = c << gf.components.straight(length + 2 * end, cross_section=xs)
        arm.dmove((-end, ya))
        split = c << gf.components.bend_s(
            (sbend_length, ya), npoints=200, cross_section=xs
        )
        split.connect("o2", arm.ports["o1"])
        combine = c << gf.components.bend_s(
            (sbend_length, -ya), npoints=200, cross_section=xs
        )
        combine.connect("o1", arm.ports["o2"])
    return c


if __name__ == "__main__":
    import sys

    tfln_modulator().write_gds(sys.argv[1])
gdstk/tfln_modulator.py
"""A thin-film lithium niobate Mach-Zehnder modulator with T-shaped travelling-wave
electrodes, as laylight/tfln_modulator.lyl."""

import gdstk

from tech import LN, METAL


def tee(stem_length=6, stem_width=6, head_width=2, head_length=45):
    """A T-shaped electrode segment pointing up from (0, 0)."""
    cell = gdstk.Cell("Tee")
    y = stem_length + head_width
    stem = gdstk.rectangle((-stem_width / 2, 0), (stem_width / 2, stem_length), **METAL)
    head = gdstk.rectangle(
        (-head_length / 2, stem_length), (head_length / 2, y), **METAL
    )
    cell.add(stem, head)
    return cell


def tfln_modulator(
    length=10000,
    signal_width=100,
    ground_width=150,
    gap=5,
    stem_length=6,
    stem_width=6,
    head_width=2,
    head_length=45,
    head_gap=5,
    width=1.2,
    sbend_length=500,
    taper_length=100,
    pad_size=80,
):
    """GSG electrodes with rows of T-shaped segments along the edges that face the
    arms, tapered to probe pads; the arms are split and combined by Y-branches."""
    cell = gdstk.Cell("TFLNModulator")
    channel = gap + 2 * (stem_length + head_width)  # between the rails
    pitch = head_length + head_gap
    segment = tee(stem_length, stem_width, head_width, head_length)
    y = signal_width / 2
    for edge, mirror in [
        (y, False),
        (-y, True),
        (y + channel, True),
        (-y - channel, False),
    ]:
        cell.add(
            gdstk.Reference(
                segment,
                (pitch / 2, edge),
                x_reflection=mirror,
                columns=round(length / pitch),
                spacing=(pitch, 0),
            )
        )
    # Each line with its tapers and pads as one outline.
    end = taper_length + pad_size
    x = [-end, -taper_length, 0, length, length + taper_length, length + end]
    y_ground = y + channel + ground_width / 2
    for yc, w in [
        (0, signal_width),
        (y_ground, ground_width),
        (-y_ground, ground_width),
    ]:
        half = [pad_size / 2, pad_size / 2, w / 2, w / 2, pad_size / 2, pad_size / 2]
        upper = [(xi, yc + h) for xi, h in zip(x, half)]
        lower = [(xi, yc - h) for xi, h in zip(x, half)]
        cell.add(gdstk.Polygon(upper + lower[::-1], **METAL))
    mid = sbend_length / 2
    for ya in [y + channel / 2, -y - channel / 2]:
        arm = gdstk.FlexPath((-end - sbend_length, 0), width, tolerance=1e-3, **LN)
        arm.bezier([(mid, 0), (mid, ya), (sbend_length, ya)], relative=True)
        arm.segment((length + 2 * end, 0), relative=True)
        arm.bezier([(mid, 0), (mid, -ya), (sbend_length, -ya)], relative=True)
        cell.add(arm)
    return cell


if __name__ == "__main__":
    import sys

    top = tfln_modulator()
    lib = gdstk.Library()
    lib.add(top, *top.dependencies(True))
    lib.write_gds(sys.argv[1])
klayout/tfln_modulator.py
"""A thin-film lithium niobate Mach-Zehnder modulator with T-shaped travelling-wave
electrodes, as laylight/tfln_modulator.lyl."""

import klayout.db as kdb
import numpy as np

from tech import LN, METAL, layout
from waveguides import path, polygon


def tee(stem_length=6, stem_width=6, head_width=2, head_length=45):
    """A T-shaped electrode segment pointing up from (0, 0)."""
    cell = layout.create_cell("Tee")
    metal = cell.shapes(layout.layer(METAL))
    y = stem_length + head_width
    metal.insert(kdb.DBox(-stem_width / 2, 0, stem_width / 2, stem_length))
    metal.insert(kdb.DBox(-head_length / 2, stem_length, head_length / 2, y))
    return cell


def tfln_modulator(
    length=10000,
    signal_width=100,
    ground_width=150,
    gap=5,
    stem_length=6,
    stem_width=6,
    head_width=2,
    head_length=45,
    head_gap=5,
    width=1.2,
    sbend_length=500,
    taper_length=100,
    pad_size=80,
):
    """GSG electrodes with rows of T-shaped segments along the edges that face the
    arms, tapered to probe pads; the arms are split and combined by Y-branches."""
    cell = layout.create_cell("TFLNModulator")
    metal = cell.shapes(layout.layer(METAL))
    ln = cell.shapes(layout.layer(LN))
    channel = gap + 2 * (stem_length + head_width)  # between the rails
    pitch = head_length + head_gap
    segment = tee(stem_length, stem_width, head_width, head_length).cell_index()
    columns = round(length / pitch)
    y = signal_width / 2
    for edge, mirror in [
        (y, False),
        (-y, True),
        (y + channel, True),
        (-y - channel, False),
    ]:
        trans = kdb.DCplxTrans(1, 0, mirror, pitch / 2, edge)
        step = kdb.DVector(pitch, 0)
        cell.insert(kdb.DCellInstArray(segment, trans, step, kdb.DVector(), columns, 1))
    # Each line with its tapers and pads as one outline.
    end = taper_length + pad_size
    x = [-end, -taper_length, 0, length, length + taper_length, length + end]
    y_ground = y + channel + ground_width / 2
    for yc, w in [
        (0, signal_width),
        (y_ground, ground_width),
        (-y_ground, ground_width),
    ]:
        half = [pad_size / 2, pad_size / 2, w / 2, w / 2, pad_size / 2, pad_size / 2]
        upper = [(xi, yc + h) for xi, h in zip(x, half)]
        lower = [(xi, yc - h) for xi, h in zip(x, half)]
        metal.insert(polygon(upper + lower[::-1]))
    # S-bends: cubic Bezier curves, as gdsfactory's bend_s.
    u = np.linspace(0, 1, 500)[:, None]
    weights = np.hstack([(1 - u) ** 3, 3 * u * (1 - u) ** 2, 3 * u**2 * (1 - u), u**3])
    mid = sbend_length / 2
    for ya in [y + channel / 2, -y - channel / 2]:
        control = np.array([(0, 0), (mid, 0), (mid, ya), (sbend_length, ya)])
        split = weights @ control - (end + sbend_length, 0)
        combine = split[::-1] * (-1, 1) + (length, 0)  # mirrored about the middle
        ln.insert(path(np.vstack([split, combine]), width))
    return cell


if __name__ == "__main__":
    import sys

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

On this page