Broadband Directional Coupler
A 50/50 directional coupler made broadband by an asymmetric phase-control section between two symmetric couplers.
DirectionalCoupler from laylight/directional_coupler.lyl, compiled in your browserOpen in playgroundA 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
| Parameter | Value | Source |
|---|---|---|
| Symmetric couplers | 2 × 500 nm, 200 nm gap, L1 = 12.4 µm each | Lu 2015, Sec. 2, Table 1 |
| Phase-control section | 600 nm and 400 nm, 300 nm gap, L2 = 4.6 µm | Lu 2015, Sec. 2, Table 1 |
| Tapers between the sections | 1 µm, linear | Lu 2015, Sec. 2 |
| Port bends | 90°, radius 5 µm | Lu 2015, Sec. 2 |
| Wide waveguide on top, gap centered | assumed |
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
| Implementation | Lines of code | Tokens | Layout |
|---|---|---|---|
| Laylight | 28 | 258 | reference |
| gdsfactory | 28 (1.00×) | 294 (1.14×) | same within 3 nm |
| gdstk | 31 (1.11×) | 259 (1.00×) | same within 3 nm |
| KLayout | 29 (1.04×) | 340 (1.32×) | same within 3 nm |
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)
}
}
}
"""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])
"""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])
"""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])