Examples

Variable Optical Attenuator

A silicon p-i-n variable optical attenuator: a 0.5 mm rib waveguide between p++ and n++ strips.

Compiling…
VOA from laylight/voa.lyl, compiled in your browserOpen in playground

A variable optical attenuator (VOA) sets the power in a waveguide. In a silicon p-i-n VOA, the waveguide is the intrinsic region of a diode: forward current injects electrons and holes, whose free-carrier absorption attenuates the light. Park et al. studied how the carrier lifetime, which the device geometry and the isolation of the slab control, sets the attenuation efficiency and speed.

The example is their submicrometer rib VOA: a 600 nm rib waveguide, 0.5 mm long, in the middle of a 4 µm intrinsic region, with p++ and n++ strips on either side of it. The slab ends at the outer edges of the doped strips; beyond them the silicon is etched down to the oxide, which is the isolation groove of the paper. Contacts and metal lines run along the strips, and each line has a probe pad on its outer side.

References

  • S. Park et al., "Influence of carrier lifetime on performance of silicon p-i-n variable optical attenuators fabricated on submicrometer rib waveguides," Optics Express 18(11), 11282 (2010). doi:10.1364/OE.18.011282

Parameters

ParameterValueSource
Length500 µm (the paper also has 0.25 and 1 mm)Park 2010
Rib width600 nm (200 nm silicon, 100 nm slab)Park 2010
Intrinsic region4 µm, p++ and n++ 1.7 µm from the rib on either sidePark 2010 (symmetric placement assumed)
Doped strips5 µm wideassumed
Contacts, metalthe strips shrunk by 1.5 µm and 1 µmassumed
Pads80 µm, at mid-lengthassumed

How it is written

Each layer is a block, and most blocks hold one statement and take one line: the contacts are [p_strip, n_strip] - 1500nm, the doped strips shrunk by 1.5 µm, and the metal lines the same strips shrunk by 1 µm. The strips themselves are placed by their outer edges on the slab's edges.

This device is mostly rectangles on seven layers. The Python versions loop over the two sides and over the layers of each side, adding a rectangle per layer in one line, and take about as many lines as Laylight; in tokens Laylight is the shortest by a third.

Implementations

ImplementationLines of codeTokensLayout
Laylight15149reference
gdsfactory16 (1.07×)230 (1.54×)identical
gdstk15 (1.00×)252 (1.69×)identical
KLayout15 (1.00×)273 (1.83×)identical
laylight/voa.lyl
import "tech.lyl"

# A p-i-n variable optical attenuator after Park et al., Opt. Express 2010:
# p++ and n++ strips along a rib waveguide, `width` wide on a 100 nm slab, on
# either side of an `intrinsic` region. The slab ends at the doped strips;
# isolation grooves down to the BOX surround it. The width of the strips, the
# contacts, metal and pads are assumed.
cell VOA(length = 500um, width = 600nm, intrinsic = 4um, doped_width = 5um) {
    layer si { shape rect with length, width as rib at self.origin }

    layer slab {
        shape rect with length, intrinsic + 2 * doped_width as slab at self.origin
    }

    layer ppp {
        shape rect with length, doped_width as p_strip.top_center at slab.top_center
    }

    layer npp {
        shape rect with length, doped_width as n_strip
        place n_strip.bottom_center at slab.bottom_center
    }

    layer contact { shape [p_strip, n_strip] - 1500nm as contacts }

    # Metal lines over the contacts, with an 80 um pad on each side.
    layer metal {
        shape [p_strip, n_strip] - 1um as lines
        shape rect with 80um, 80um as p_pad.bottom_center at lines.top_center
        clone p_pad as n_pad.top_center at lines.bottom_center
    }
}
gdsfactory/voa.py
"""A p-i-n variable optical attenuator, as laylight/voa.lyl."""

import gdsfactory as gf

from tech import CONTACT, METAL, NPP, PPP, SI, SLAB


@gf.cell
def voa(length=500, width=0.6, intrinsic=4, doped_width=5):
    """A p-i-n variable optical attenuator: p++ and n++ strips along a 600 nm rib
    waveguide on either side of a 4 um intrinsic region, after Park et al., Opt.
    Express 2010. The slab ends at the doped strips; isolation grooves down to
    the BOX surround it. The width of the strips, contacts, metal and pads are
    assumed."""
    c = gf.Component()
    slab_width = intrinsic + 2 * doped_width
    c << gf.components.rectangle((length, width), SI, centered=True)
    c << gf.components.rectangle((length, slab_width), SLAB, centered=True)
    for side, doped in [(1, PPP), (-1, NPP)]:
        y = side * (intrinsic + doped_width) / 2  # the center of the strip
        # The doped strip, the contact 1.5 um inside it and the metal 1 um inside.
        for layer, inset in [(doped, 0), (CONTACT, 1.5), (METAL, 1)]:
            size = (length - 2 * inset, doped_width - 2 * inset)
            (c << gf.components.rectangle(size, layer, centered=True)).dmovey(y)
        # An 80 um pad on the outer edge of the metal, in the middle.
        pad = c << gf.components.pad((80, 80), layer=METAL)
        pad.dmovey(y + side * (doped_width / 2 - 1 + 40))
    return c


if __name__ == "__main__":
    import sys

    voa().write_gds(sys.argv[1])
gdstk/voa.py
"""A p-i-n variable optical attenuator, as laylight/voa.lyl."""

import gdstk

from tech import CONTACT, METAL, NPP, PPP, SI, SLAB


def voa(length=500, width=0.6, intrinsic=4, doped_width=5):
    """A p-i-n variable optical attenuator: p++ and n++ strips along a 600 nm rib
    waveguide on either side of a 4 um intrinsic region, after Park et al., Opt.
    Express 2010. The slab ends at the doped strips; isolation grooves down to
    the BOX surround it. The width of the strips, contacts, metal and pads are
    assumed."""
    cell = gdstk.Cell("VOA")
    x, s = length / 2, intrinsic / 2 + doped_width
    cell.add(gdstk.rectangle((-x, -width / 2), (x, width / 2), **SI))
    cell.add(gdstk.rectangle((-x, -s), (x, s), **SLAB))
    for side, doped in [(1, PPP), (-1, NPP)]:
        y = side * (intrinsic + doped_width) / 2  # the center of the strip
        # The doped strip, the contact 1.5 um inside it and the metal 1 um inside.
        for layer, inset in [(doped, 0), (CONTACT, 1.5), (METAL, 1)]:
            dy = doped_width / 2 - inset
            cell.add(gdstk.rectangle((inset - x, y - dy), (x - inset, y + dy), **layer))
        # An 80 um pad on the outer edge of the metal, in the middle.
        y += side * (doped_width / 2 - 1 + 40)
        cell.add(gdstk.rectangle((-40, y - 40), (40, y + 40), **METAL))
    return cell


if __name__ == "__main__":
    import sys

    top = voa()
    lib = gdstk.Library()
    lib.add(top, *top.dependencies(True))
    lib.write_gds(sys.argv[1])
klayout/voa.py
"""A p-i-n variable optical attenuator, as laylight/voa.lyl."""

import klayout.db as kdb

from tech import CONTACT, METAL, NPP, PPP, SI, SLAB, layout


def voa(length=500, width=0.6, intrinsic=4, doped_width=5):
    """A p-i-n variable optical attenuator: p++ and n++ strips along a 600 nm rib
    waveguide on either side of a 4 um intrinsic region, after Park et al., Opt.
    Express 2010. The slab ends at the doped strips; isolation grooves down to
    the BOX surround it. The width of the strips, contacts, metal and pads are
    assumed."""
    cell = layout.create_cell("VOA")
    x, s = length / 2, intrinsic / 2 + doped_width
    cell.shapes(layout.layer(SI)).insert(kdb.DBox(-x, -width / 2, x, width / 2))
    cell.shapes(layout.layer(SLAB)).insert(kdb.DBox(-x, -s, x, s))
    for side, doped in [(1, PPP), (-1, NPP)]:
        y = side * (intrinsic + doped_width) / 2  # the center of the strip
        strip = kdb.DBox(-x, y - doped_width / 2, x, y + doped_width / 2)
        # The doped strip, the contact 1.5 um inside it and the metal 1 um inside.
        for layer, inset in [(doped, 0), (CONTACT, 1.5), (METAL, 1)]:
            cell.shapes(layout.layer(layer)).insert(strip.enlarged(-inset))
        # An 80 um pad on the outer edge of the metal, in the middle.
        y += side * (doped_width / 2 - 1 + 40)
        cell.shapes(layout.layer(METAL)).insert(kdb.DBox(-40, y - 40, 40, y + 40))
    return cell


if __name__ == "__main__":
    import sys

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

On this page