Examples

Grating Couplers

Uniform, apodized and focusing grating couplers between a single-mode fiber and silicon waveguides.

Compiling…
GratingCouplers from laylight/grating_couplers.lyl, compiled in your browserOpen in playground

A grating coupler diffracts light out of the chip surface into an optical fiber held a few degrees off vertical, and back. It is etched into a wide silicon waveguide, about as wide as the fiber's mode, which a taper connects to the single-mode waveguide. The example draws three generations of the design.

The uniform grating of Taillaert et al. is the classic one: 20 trenches, 70 nm deep, with a 630 nm period and a fill factor of 0.5, across a 12 µm wide waveguide, which a 400 µm linear taper narrows to 450 nm. The apodized grating of Bozzola et al. varies the trenches along the grating, so that the light leaves it with a profile closer to the fiber's Gaussian mode: 24 fully etched trenches, whose positions and widths are taken from the paper's table. It is a simulation paper; the grating width and taper are assumed. The focusing grating of Van Laere et al. replaces the long taper by curved trenches that focus the light onto the end of the waveguide: its lines are confocal ellipses with a common focus at the waveguide's end, q λ = n_eff r − x cos θ_c for integers q, in a 37° sector 28 µm long.

References

  • D. Taillaert et al., "Grating couplers for coupling between optical fibers and nanophotonic waveguides," Japanese Journal of Applied Physics 45(8A), 6071–6077 (2006). doi:10.1143/JJAP.45.6071
  • A. Bozzola, L. Carroll, D. Gerace, I. Cristiani and L. C. Andreani, "Optimising apodized grating couplers in a pure SOI platform to −0.5 dB coupling efficiency," Optics Express 23(12), 16289–16304 (2015). doi:10.1364/OE.23.016289
  • F. Van Laere et al., "Compact focusing grating couplers for silicon-on-insulator integrated circuits," IEEE Photonics Technology Letters 19(23), 1919–1921 (2007). doi:10.1109/LPT.2007.908762

Parameters

ParameterValueSource
Uniform: period, fill factor, etch630 nm, 0.5, 70 nmTaillaert 2006, Fig. 3, Sec. 3.1
Uniform: width, taper12 µm; 400 µm linear taper to 450 nmTaillaert 2006, Sec. 3.2
Uniform: periods20assumed
Apodized: trench positions and widths24 trenches, fully etchedBozzola 2015, Table 1 (220 nm "constrained" design)
Apodized: width, taper12 µm, the uniform grating's taperassumed
Focusing: period, duty cycle, etch630 nm, 50 %, 70 nmVan Laere 2007
Focusing: fiber angle80° to the chip surfaceVan Laere 2007
Focusing: n_effλ/Λ + cos θ_c = 2.634 at λ = 1550 nmderived
Focusing: lines, sectorq = 20 to 43; 37°, 28 µmassumed, to fit the paper's 18.5 µm × 28 µm footprint

How it is written

Each grating is a cell. The uniform grating places its trenches in a loop on the shallow-etch layer. The apodized grating collects its fully etched trenches in a list and subtracts them from the grating region in one statement, slab - trenches. The focusing grating writes the paper's equation almost literally: for each line q it draws the two confocal ellipses that bound a trench, places them as helper shapes with place* (so they are not drawn themselves), and keeps (outer - inner) & sector, the band between them inside the grating's sector. The Python versions compute the same trenches as polygons from the polar form of the equation, since a boolean per trench is more code there.

Implementations

ImplementationLines of codeTokensLayout
Laylight71604reference
gdsfactory78 (1.10×)783 (1.30×)same within 2 nm
gdstk71 (1.00×)745 (1.23×)same within 2 nm
KLayout76 (1.07×)815 (1.35×)same within 2 nm
laylight/grating_couplers.lyl
import "tech.lyl"

# Trench centers (from the first one) and widths of the apodized grating,
# Bozzola et al. 2015, Table 1.
define trench_positions = [
    0nm, 712.7nm, 1377nm, 2061.7nm, 2769.7nm, 3429.3nm, 4159nm, 4846.6nm, 5569.2nm,
    6250.5nm, 6982.2nm, 7655.1nm, 8351.4nm, 9057.9nm, 9734.4nm, 10452.8nm,
    11121.2nm, 11813.3nm, 12512.3nm, 13197.8nm, 13916.8nm, 14583.6nm, 15317nm,
    16017.2nm
]
define trench_widths = [
    100.3nm, 100.7nm, 130.1nm, 121.3nm, 127.8nm, 150.2nm, 154.8nm, 145.9nm, 164.8nm,
    147.8nm, 153.4nm, 150.3nm, 133.3nm, 156.8nm, 162.5nm, 141nm, 164.5nm, 140.9nm,
    146.3nm, 130.8nm, 142.2nm, 137.7nm, 114.3nm, 132.7nm
]

# A uniform grating of shallow-etched trenches at the end of a long linear
# taper, after Taillaert et al., Jpn. J. Appl. Phys. 2006.
cell UniformGrating(
    period = 630nm,
    fill_factor = 0.5,
    periods = 20,
    width = 12um,
    taper_length = 400um,
    wire_width = 450nm,
) {
    layer si {
        shape taper with taper_length, wire_width, width as flare
        place flare.end at self.origin
        shape rect with periods * period, width as grating.left_center at self.origin
    }

    layer shallow {
        for i in 0..periods {
            shape rect with (1 - fill_factor) * period, width as trench
            place trench at ((i + 0.5) * period, 0)
        }
    }
}

# An apodized grating of fully etched trenches, after Bozzola et al., Opt.
# Express 2015, with the taper of the uniform grating.
# `margin` lies before the first and after the last trench center.
cell ApodizedGrating(
    width = 12um, taper_length = 400um, wire_width = 450nm, margin = 1um
) {
    layer si {
        shape taper with taper_length, wire_width, width as flare
        define last = trench_positions[len(trench_positions) - 1]
        shape rect with last + 2 * margin, width as slab.left_center at self.origin
        place flare.end at self.origin
        define trenches = []
        for i in 0..len(trench_positions) {
            shape rect with trench_widths[i], width as trench
            place trench at (margin + trench_positions[i], 0)
            trenches.append(trench)
        }
        shape slab - trenches as grating
    }
}

# A focusing grating: shallow-etched trenches along confocal ellipses whose
# common focus is the end of the access wire, after Van Laere et al., IEEE
# Photon. Technol. Lett. 2007.
cell FocusingGrating(
    period = 630nm,
    fill_factor = 0.5,
    wavelength = 1550nm,
    fiber_angle = 80deg,  # between the fiber and the chip surface
    first_order = 20,  # q of the first line
    lines = 24,
    opening = 37deg,
    length = 28um,
    wire_width = 500nm,
    wire_length = 10um,
) {
    marker { output { at wire.end } }

    layer si {
        shape circle with length, angle = opening, start_angle = -opening / 2 as sector
        place sector at self.origin
        shape straight with wire_length, wire_width as wire
        place wire.start at self.origin rotate 180deg
    }

    # Line q is q * wavelength = n_eff * r - cos(fiber_angle) * x: an ellipse
    # with a focus at the origin, eccentricity e and semi-axes q * a, q * b.
    layer shallow {
        define n_eff = wavelength / period + cos(fiber_angle)
        define e = cos(fiber_angle) / n_eff
        define a = wavelength / n_eff / (1 - e * e)
        define b = a * sqrt(1 - e * e)
        for q in first_order..first_order + lines {
            define q2 = q + 1 - fill_factor
            shape ellipse with q * a, q * b as inner
            shape ellipse with q2 * a, q2 * b as outer
            place* inner at (q * a * e, 0)
            place* outer at (q2 * a * e, 0)
            shape (outer - inner) & sector as trench
        }
    }
}

# The three couplers at the pitch of a fiber array.
cell GratingCouplers(pitch = 127um) {
    place UniformGrating at self.origin
    place ApodizedGrating at (0, -pitch)
    place FocusingGrating at (0, -2 * pitch)
}
gdsfactory/grating_couplers.py
"""Uniform, apodized and focusing grating couplers, as
laylight/grating_couplers.lyl."""

import gdsfactory as gf
import numpy as np

from tech import SHALLOW, SI

# Trench centers (from the first one) and widths of the apodized grating in nm,
# Bozzola et al. 2015, Table 1.
# fmt: off
TRENCH_POSITIONS = np.array([
    0, 712.7, 1377, 2061.7, 2769.7, 3429.3, 4159, 4846.6, 5569.2, 6250.5, 6982.2,
    7655.1, 8351.4, 9057.9, 9734.4, 10452.8, 11121.2, 11813.3, 12512.3, 13197.8,
    13916.8, 14583.6, 15317, 16017.2,
]) / 1000
TRENCH_WIDTHS = np.array([
    100.3, 100.7, 130.1, 121.3, 127.8, 150.2, 154.8, 145.9, 164.8, 147.8, 153.4,
    150.3, 133.3, 156.8, 162.5, 141, 164.5, 140.9, 146.3, 130.8, 142.2, 137.7,
    114.3, 132.7,
]) / 1000
# fmt: on


@gf.cell
def uniform_grating(
    period=0.63,
    fill_factor=0.5,
    periods=20,
    width=12,
    taper_length=400,
    wire_width=0.45,
):
    """A uniform grating of shallow-etched trenches at the end of a long linear
    taper, after Taillaert et al., Jpn. J. Appl. Phys. 2006."""
    c = gf.Component()
    flare = c << gf.components.taper(taper_length, wire_width, width, layer=SI)
    flare.dmovex(-taper_length)
    grating = c << gf.components.rectangle((periods * period, width), layer=SI)
    grating.dmovey(-width / 2)
    trench = gf.components.rectangle(
        ((1 - fill_factor) * period, width), layer=SHALLOW, centered=True
    )
    for i in range(periods):
        (c << trench).dmovex((i + 0.5) * period)
    return c


@gf.cell
def apodized_grating(width=12, taper_length=400, wire_width=0.45, margin=1):
    """An apodized grating of fully etched trenches, after Bozzola et al., Opt.
    Express 2015, with the taper of the uniform grating. `margin`: silicon
    before the first and after the last trench center."""
    c = gf.Component()
    flare = c << gf.components.taper(taper_length, wire_width, width, layer=SI)
    flare.dmovex(-taper_length)
    slab = gf.components.rectangle((TRENCH_POSITIONS[-1] + 2 * margin, width), layer=SI)
    trenches = gf.Component()
    for x, w in zip(TRENCH_POSITIONS, TRENCH_WIDTHS):
        trench = trenches << gf.components.rectangle((w, width), layer=SI)
        trench.dmovex(margin + x - w / 2)
    grating = c << gf.boolean(slab, trenches, "-", layer=SI)
    grating.dmovey(-width / 2)
    return c


@gf.cell
def focusing_grating(
    period=0.63,
    fill_factor=0.5,
    wavelength=1.55,
    fiber_angle=80,  # between the fiber and the chip surface
    first_order=20,  # q of the first line
    lines=24,
    opening=37,
    length=28,
    wire_width=0.5,
    wire_length=10,
):
    """A focusing grating: shallow-etched trenches along confocal ellipses whose
    common focus is the end of the access wire, after Van Laere et al., IEEE
    Photon. Technol. Lett. 2007."""
    c = gf.Component()
    phi = np.radians(opening) * np.linspace(-0.5, 0.5, 101)
    arc = np.column_stack([np.cos(phi), np.sin(phi)])
    c.add_polygon(np.vstack([(0, 0), length * arc]), layer=SI)
    wire = c << gf.components.rectangle((wire_length, wire_width), layer=SI)
    wire.dmove((-wire_length, -wire_width / 2))
    c.add_port(
        "o1", center=(-wire_length, 0), width=wire_width, orientation=180, layer=SI
    )
    # Line q: q * wavelength = n_eff * r - cos(fiber_angle) * x.
    cos_angle = np.cos(np.radians(fiber_angle))
    n_eff = wavelength / period + cos_angle
    line = arc * (wavelength / (n_eff - cos_angle * np.cos(phi)))[:, None]
    for q in range(first_order, first_order + lines):
        trench = np.vstack([q * line, (q + 1 - fill_factor) * line[::-1]])
        c.add_polygon(trench, layer=SHALLOW)
    return c


@gf.cell
def grating_couplers(pitch=127):
    """The three couplers at the pitch of a fiber array."""
    c = gf.Component()
    c << uniform_grating()
    (c << apodized_grating()).dmovey(-pitch)
    (c << focusing_grating()).dmovey(-2 * pitch)
    return c


if __name__ == "__main__":
    import sys

    grating_couplers().write_gds(sys.argv[1])
gdstk/grating_couplers.py
"""Uniform, apodized and focusing grating couplers, as
laylight/grating_couplers.lyl."""

import gdstk
import numpy as np

from tech import SHALLOW, SI
from waveguides import taper

# Trench centers (from the first one) and widths of the apodized grating in nm,
# Bozzola et al. 2015, Table 1.
# fmt: off
TRENCH_POSITIONS = np.array([
    0, 712.7, 1377, 2061.7, 2769.7, 3429.3, 4159, 4846.6, 5569.2, 6250.5, 6982.2,
    7655.1, 8351.4, 9057.9, 9734.4, 10452.8, 11121.2, 11813.3, 12512.3, 13197.8,
    13916.8, 14583.6, 15317, 16017.2,
]) / 1000
TRENCH_WIDTHS = np.array([
    100.3, 100.7, 130.1, 121.3, 127.8, 150.2, 154.8, 145.9, 164.8, 147.8, 153.4,
    150.3, 133.3, 156.8, 162.5, 141, 164.5, 140.9, 146.3, 130.8, 142.2, 137.7,
    114.3, 132.7,
]) / 1000
# fmt: on


def uniform_grating(
    period=0.63,
    fill_factor=0.5,
    periods=20,
    width=12,
    taper_length=400,
    wire_width=0.45,
):
    """A uniform grating of shallow-etched trenches at the end of a long linear
    taper, after Taillaert et al., Jpn. J. Appl. Phys. 2006."""
    cell = gdstk.Cell("UniformGrating")
    y = width / 2
    cell.add(taper(taper_length, wire_width, width, SI).translate(-taper_length, 0))
    cell.add(gdstk.rectangle((0, -y), (periods * period, y), **SI))
    w = (1 - fill_factor) * period
    for i in range(periods):
        x = (i + 0.5) * period
        cell.add(gdstk.rectangle((x - w / 2, -y), (x + w / 2, y), **SHALLOW))
    return cell


def apodized_grating(width=12, taper_length=400, wire_width=0.45, margin=1):
    """An apodized grating of fully etched trenches, after Bozzola et al., Opt.
    Express 2015, with the taper of the uniform grating. `margin`: silicon
    before the first and after the last trench center."""
    cell = gdstk.Cell("ApodizedGrating")
    y = width / 2
    cell.add(taper(taper_length, wire_width, width, SI).translate(-taper_length, 0))
    slab = gdstk.rectangle((0, -y), (TRENCH_POSITIONS[-1] + 2 * margin, y))
    trenches = [
        gdstk.rectangle((margin + x - w / 2, -y), (margin + x + w / 2, y))
        for x, w in zip(TRENCH_POSITIONS, TRENCH_WIDTHS)
    ]
    cell.add(*gdstk.boolean(slab, trenches, "not", **SI))
    return cell


def focusing_grating(
    period=0.63,
    fill_factor=0.5,
    wavelength=1.55,
    fiber_angle=80,  # between the fiber and the chip surface
    first_order=20,  # q of the first line
    lines=24,
    opening=37,
    length=28,
    wire_width=0.5,
    wire_length=10,
):
    """A focusing grating: shallow-etched trenches along confocal ellipses whose
    common focus is the end of the access wire, after Van Laere et al., IEEE
    Photon. Technol. Lett. 2007."""
    cell = gdstk.Cell("FocusingGrating")
    a = np.radians(opening) / 2
    cell.add(
        gdstk.ellipse(
            (0, 0), length, initial_angle=-a, final_angle=a, tolerance=1e-3, **SI
        ),
        gdstk.rectangle((-wire_length, -wire_width / 2), (0, wire_width / 2), **SI),
    )
    # Line q: q * wavelength = n_eff * r - cos(fiber_angle) * x.
    cos_angle = np.cos(np.radians(fiber_angle))
    n_eff = wavelength / period + cos_angle
    phi = np.linspace(-a, a, 101)
    line = wavelength / (n_eff - cos_angle * np.cos(phi)) * np.exp(1j * phi)
    for q in range(first_order, first_order + lines):
        trench = np.concatenate([q * line, (q + 1 - fill_factor) * line[::-1]])
        cell.add(gdstk.Polygon(trench, **SHALLOW))
    return cell


def grating_couplers(pitch=127):
    """The three couplers at the pitch of a fiber array."""
    cell = gdstk.Cell("GratingCouplers")
    cell.add(gdstk.Reference(uniform_grating()))
    cell.add(gdstk.Reference(apodized_grating(), (0, -pitch)))
    cell.add(gdstk.Reference(focusing_grating(), (0, -2 * pitch)))
    return cell


if __name__ == "__main__":
    import sys

    top = grating_couplers()
    lib = gdstk.Library()
    lib.add(top, *top.dependencies(True))
    lib.write_gds(sys.argv[1])
klayout/grating_couplers.py
"""Uniform, apodized and focusing grating couplers, as
laylight/grating_couplers.lyl."""

import klayout.db as kdb
import numpy as np

from tech import SHALLOW, SI, layout
from waveguides import polygon, taper

# Trench centers (from the first one) and widths of the apodized grating in nm,
# Bozzola et al. 2015, Table 1.
# fmt: off
TRENCH_POSITIONS = np.array([
    0, 712.7, 1377, 2061.7, 2769.7, 3429.3, 4159, 4846.6, 5569.2, 6250.5, 6982.2,
    7655.1, 8351.4, 9057.9, 9734.4, 10452.8, 11121.2, 11813.3, 12512.3, 13197.8,
    13916.8, 14583.6, 15317, 16017.2,
]) / 1000
TRENCH_WIDTHS = np.array([
    100.3, 100.7, 130.1, 121.3, 127.8, 150.2, 154.8, 145.9, 164.8, 147.8, 153.4,
    150.3, 133.3, 156.8, 162.5, 141, 164.5, 140.9, 146.3, 130.8, 142.2, 137.7,
    114.3, 132.7,
]) / 1000
# fmt: on


def uniform_grating(
    period=0.63,
    fill_factor=0.5,
    periods=20,
    width=12,
    taper_length=400,
    wire_width=0.45,
):
    """A uniform grating of shallow-etched trenches at the end of a long linear
    taper, after Taillaert et al., Jpn. J. Appl. Phys. 2006."""
    cell = layout.create_cell("UniformGrating")
    si = cell.shapes(layout.layer(SI))
    shallow = cell.shapes(layout.layer(SHALLOW))
    y = width / 2
    si.insert(taper(taper_length, wire_width, width).moved(-taper_length, 0))
    si.insert(kdb.DBox(0, -y, periods * period, y))
    w = (1 - fill_factor) * period
    for i in range(periods):
        x = (i + 0.5) * period
        shallow.insert(kdb.DBox(x - w / 2, -y, x + w / 2, y))
    return cell


def apodized_grating(width=12, taper_length=400, wire_width=0.45, margin=1):
    """An apodized grating of fully etched trenches, after Bozzola et al., Opt.
    Express 2015, with the taper of the uniform grating. `margin`: silicon
    before the first and after the last trench center."""
    cell = layout.create_cell("ApodizedGrating")
    si = cell.shapes(layout.layer(SI))
    y = width / 2
    si.insert(taper(taper_length, wire_width, width).moved(-taper_length, 0))
    slab = kdb.DBox(0, -y, TRENCH_POSITIONS[-1] + 2 * margin, y)
    trenches = kdb.Region()
    for x, w in zip(TRENCH_POSITIONS, TRENCH_WIDTHS):
        trench = kdb.DBox(margin + x - w / 2, -y, margin + x + w / 2, y)
        trenches.insert(trench.to_itype(layout.dbu))
    si.insert(kdb.Region(slab.to_itype(layout.dbu)) - trenches)
    return cell


def focusing_grating(
    period=0.63,
    fill_factor=0.5,
    wavelength=1.55,
    fiber_angle=80,  # between the fiber and the chip surface
    first_order=20,  # q of the first line
    lines=24,
    opening=37,
    length=28,
    wire_width=0.5,
    wire_length=10,
):
    """A focusing grating: shallow-etched trenches along confocal ellipses whose
    common focus is the end of the access wire, after Van Laere et al., IEEE
    Photon. Technol. Lett. 2007."""
    cell = layout.create_cell("FocusingGrating")
    si = cell.shapes(layout.layer(SI))
    shallow = cell.shapes(layout.layer(SHALLOW))
    phi = np.radians(opening) * np.linspace(-0.5, 0.5, 101)
    arc = np.column_stack([np.cos(phi), np.sin(phi)])
    si.insert(polygon(np.vstack([(0, 0), length * arc])))
    si.insert(kdb.DBox(-wire_length, -wire_width / 2, 0, wire_width / 2))
    # Line q: q * wavelength = n_eff * r - cos(fiber_angle) * x.
    cos_angle = np.cos(np.radians(fiber_angle))
    n_eff = wavelength / period + cos_angle
    line = arc * (wavelength / (n_eff - cos_angle * np.cos(phi)))[:, None]
    for q in range(first_order, first_order + lines):
        shallow.insert(
            polygon(np.vstack([q * line, (q + 1 - fill_factor) * line[::-1]]))
        )
    return cell


def grating_couplers(pitch=127):
    """The three couplers at the pitch of a fiber array."""
    cell = layout.create_cell("GratingCouplers")
    couplers = [uniform_grating(), apodized_grating(), focusing_grating()]
    for i, coupler in enumerate(couplers):
        trans = kdb.DTrans(kdb.DVector(0, -i * pitch))
        cell.insert(kdb.DCellInstArray(coupler.cell_index(), trans))
    return cell


if __name__ == "__main__":
    import sys

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

On this page