Microring Resonators
A 1.5 µm-radius all-pass microring, and an ultrahigh-Q racetrack with Euler bends and a pulley coupler.
Microrings from laylight/microring.lyl, compiled in your browserOpen in playgroundA microring resonator is a closed waveguide loop next to a bus waveguide: light at the wavelengths for which the loop is a whole number of wavelengths long couples into it and is lost, so the bus transmits a comb of narrow dips. The example holds two very different rings.
The all-pass microring of Xu, Fattal and Beausoleil is one of the smallest silicon rings: 1.5 µm in radius, with a 450 nm wide ring waveguide. Its bus is only 250 nm wide, narrower than the ring, so that the propagation constants of the bus and the bent ring waveguide match and light can couple across the 340 nm gap of such a short coupler.
The Euler racetrack of Zhang et al. reaches an ultrahigh Q with wide multimode waveguides: 1.6 µm everywhere, so little light reaches the rough sidewalls. The straight sections are 260 µm long. Each 180° bend is an Euler bend whose curvature grows from zero at the straight sections to 1/15 µm⁻¹ at the apex, so the fundamental mode passes from straight to bent waveguide without exciting higher-order modes. The access waveguide wraps around the apex of one bend, concentric with it at a 260 nm gap, and turns back to vertical on either side.
The paper's bends start from a small curvature of 1/600 µm⁻¹ rather than from zero; the example's Euler bends start from zero, the standard Euler bend of all four tools. Their effective radius comes out at 30.0 µm instead of the paper's 29 µm. The gap is 260 nm at the apex and closes to 255 nm at the ends of the 20° coupler, since the Euler bend straightens out away from its apex while the access arc stays circular.
References
- Q. Xu, D. Fattal and R. G. Beausoleil, "Silicon microring resonators with 1.5-µm radius," Optics Express 16(6), 4309–4315 (2008). doi:10.1364/OE.16.004309
- L. Zhang et al., "Ultrahigh-Q silicon racetrack resonators," Photonics Research 8(5), 684–689 (2020). doi:10.1364/PRJ.387816
Parameters
| Parameter | Value | Source |
|---|---|---|
| Ring radius (to the waveguide center) | 1.5 µm | Xu 2008 |
| Ring and bus width | 450 nm and 250 nm | Xu 2008 |
| Gap | 340 nm | Xu 2008 |
| Racetrack width | 1.6 µm | Zhang 2020 |
| Straight sections | 260 µm | Zhang 2020 |
| Bends | 180° Euler bends, minimum radius 15 µm at the apex | Zhang 2020 (curvature starting from zero instead of 1/600 µm⁻¹) |
| Coupler | 600 nm access waveguide, 260 nm gap at the apex, radius 16.36 µm | Zhang 2020 (width derived from the concentric radius) |
| Coupler angle, leads | 20°, 10 µm | assumed |
How it is written
The racetrack is four shapes chained by their anchors: a straight path, an euler bend placed on its end, and clones of both placed on the bend's end and on the second straight's end. The access waveguide starts at a point computed from the bend's right_center anchor (its apex); a loop over the two sides places two arcs and a lead, the first one turned by rotate side * 90deg. gdsfactory's version builds the same chain by adding gf.path segments and extruding them, which is about as short; gdstk and KLayout compute the racetrack's centerline with the Euler helper of the waveguides example.
Implementations
| Implementation | Lines of code | Tokens | Layout |
|---|---|---|---|
| Laylight | 34 | 316 | reference |
| gdsfactory | 36 (1.06×) | 344 (1.09×) | same within 3 nm |
| gdstk | 39 (1.15×) | 468 (1.48×) | same within 2 nm |
| KLayout | 48 (1.41×) | 596 (1.89×) | same within 3 nm |
import "tech.lyl"
# An all-pass microring of 1.5 um radius whose bus is narrower than the ring
# for phase matching, after Xu et al., Opt. Express 2008 (bus length assumed).
# The radius is that of the waveguide's center.
cell AllPassRing(
radius = 1.5um, width = 450nm, bus_width = 250nm, gap = 340nm, bus_length = 10um
) {
layer si {
shape rect with bus_length, bus_width as bus at self.origin
shape ring with radius, width as ring
place ring at bus.top_center offset (0, gap + width / 2 + radius)
}
}
# A racetrack of multimode waveguides whose 180 deg bends are Euler bends,
# their curvature growing from 0 at the straights to 1 / radius at the apex,
# with a pulley coupler concentric with one apex, after Zhang et al., Photonics
# Res. 2020. The coupler angle and the leads are assumed.
cell EulerRacetrack(
radius = 15um, # smallest bend radius, at the apex
width = 1.6um,
length = 260um, # straight sections
gap = 260nm, # at the apex
access_width = 600nm,
coupler_angle = 20deg,
lead_length = 10um,
) {
layer si {
shape path with [(0, 0), (length, 0)], width as bottom.start at self.origin
clone bottom as top.start at right.end
shape euler with {
min_radius = radius, width = width, angle = 180deg, p = 1
} as right.start at bottom.end
clone right as left.start at top.end
# The access waveguide is concentric with the apex of the right bend
# and turns back to vertical on either side of it.
define apex = right.right_center + (gap + access_width / 2, 0)
define r = radius + width / 2 + gap + access_width / 2
for side in [1, -1] {
shape arc with r, access_width, side * coupler_angle / 2 as half
place half.start at apex rotate side * 90deg
shape arc with r, access_width, -side * coupler_angle / 2 as bend
place bend.start at half.end
shape path with [(0, 0), (lead_length, 0)], access_width as lead
place lead.start at bend.end
}
}
}
# The two resonators side by side.
cell Microrings {
place AllPassRing at (-50um, 0)
place EulerRacetrack at self.origin
}
"""A circular microring and an Euler-bend racetrack, as laylight/microring.lyl."""
import gdsfactory as gf
from tech import SI
@gf.cell
def all_pass_ring(radius=1.5, width=0.45, bus_width=0.25, gap=0.34, bus_length=10):
"""An all-pass microring of 1.5 um radius whose bus is narrower than the
ring for phase matching, after Xu et al., Opt. Express 2008 (bus length
assumed)."""
c = gf.Component()
c << gf.components.rectangle((bus_length, bus_width), SI, centered=True)
ring = c << gf.components.ring(radius, width, layer=SI)
ring.dmove((0, bus_width / 2 + gap + width / 2 + radius))
return c
@gf.cell
def euler_racetrack(
radius=15,
width=1.6,
length=260,
gap=0.26,
access_width=0.6,
coupler_angle=20,
lead_length=10,
):
"""A racetrack of multimode waveguides whose 180 deg bends are Euler bends,
their curvature growing from 0 at the straights to 1 / radius at the apex,
with a pulley coupler concentric with one apex, after Zhang et al.,
Photonics Res. 2020. The coupler angle and the leads are assumed."""
c = gf.Component()
half = gf.path.straight(length) + gf.path.euler(radius, 180, p=1)
track = c << (half + half).extrude(width=width, layer=SI)
# The access waveguide is concentric with the apex of the right bend and
# turns back to vertical on either side of it.
apex = (track.dxmax + gap + access_width / 2, track.dy)
r = radius + width / 2 + gap + access_width / 2
for side in [1, -1]:
a = side * coupler_angle / 2
access = gf.path.arc(r, a) + gf.path.arc(r, -a) + gf.path.straight(lead_length)
ref = c << access.extrude(width=access_width, layer=SI)
ref.drotate(90 * side)
ref.dmove(apex)
return c
@gf.cell
def microrings():
"""The two resonators side by side."""
c = gf.Component()
(c << all_pass_ring()).dmove((-50, 0))
c << euler_racetrack()
return c
if __name__ == "__main__":
import sys
microrings().write_gds(sys.argv[1])
"""A circular microring and an Euler-bend racetrack, as laylight/microring.lyl."""
import gdstk
import numpy as np
from tech import SI
from waveguides import euler
def all_pass_ring(radius=1.5, width=0.45, bus_width=0.25, gap=0.34, bus_length=10):
"""An all-pass microring of 1.5 um radius whose bus is narrower than the
ring for phase matching, after Xu et al., Opt. Express 2008 (bus length
assumed)."""
cell = gdstk.Cell("AllPassRing")
x, y = bus_length / 2, bus_width / 2
cell.add(gdstk.rectangle((-x, -y), (x, y), **SI))
center = (0, y + gap + width / 2 + radius)
r1, r2 = radius - width / 2, radius + width / 2
cell.add(gdstk.ellipse(center, r2, inner_radius=r1, tolerance=1e-3, **SI))
return cell
def euler_racetrack(
radius=15,
width=1.6,
length=260,
gap=0.26,
access_width=0.6,
coupler_angle=20,
lead_length=10,
):
"""A racetrack of multimode waveguides whose 180 deg bends are Euler bends,
their curvature growing from 0 at the straights to 1 / radius at the apex,
with a pulley coupler concentric with one apex, after Zhang et al.,
Photonics Res. 2020. The coupler angle and the leads are assumed."""
cell = gdstk.Cell("EulerRacetrack")
bend = euler(radius, 180, p=1) + (length, 0)
height = bend[-1, 1]
# The closed loop: the right bend, the left bend and back to the start.
track = np.vstack([bend, (length, height) - bend, [(length, 0)]])
cell.add(gdstk.FlexPath(track, width, tolerance=1e-3, **SI))
# The access waveguide runs up past the right bend: a reverse bend, an arc
# concentric with the apex of the bend and another reverse bend.
r = radius + width / 2 + gap + access_width / 2
a = np.radians(coupler_angle / 2)
x = bend[:, 0].max() - radius + r * (2 * np.cos(a) - 1)
y = height / 2 - 2 * r * np.sin(a) - lead_length
access = gdstk.FlexPath((x, y), access_width, tolerance=1e-3, **SI)
access.segment((0, lead_length), relative=True).arc(r, np.pi, np.pi - a)
access.arc(r, -a, a).arc(r, np.pi + a, np.pi)
cell.add(access.segment((0, lead_length), relative=True))
return cell
def microrings():
"""The two resonators side by side."""
cell = gdstk.Cell("Microrings")
cell.add(gdstk.Reference(all_pass_ring(), (-50, 0)))
cell.add(gdstk.Reference(euler_racetrack()))
return cell
if __name__ == "__main__":
import sys
top = microrings()
lib = gdstk.Library()
lib.add(top, *top.dependencies(True))
lib.write_gds(sys.argv[1])
"""A circular microring and an Euler-bend racetrack, as laylight/microring.lyl."""
import klayout.db as kdb
import numpy as np
from tech import SI, layout
from waveguides import euler, path, polygon
def arc(x, y, radius, a1, a2, n=500):
"""Points of a circular arc around (x, y) from angle a1 to a2 (radians)."""
a = np.linspace(a1, a2, n)
return np.column_stack([x + radius * np.cos(a), y + radius * np.sin(a)])
def all_pass_ring(radius=1.5, width=0.45, bus_width=0.25, gap=0.34, bus_length=10):
"""An all-pass microring of 1.5 um radius whose bus is narrower than the
ring for phase matching, after Xu et al., Opt. Express 2008 (bus length
assumed)."""
cell = layout.create_cell("AllPassRing")
si = cell.shapes(layout.layer(SI))
x, y = bus_length / 2, bus_width / 2
si.insert(kdb.DBox(-x, -y, x, y))
cy = y + gap + width / 2 + radius
ring = polygon(arc(0, cy, radius + width / 2, 0, 2 * np.pi))
hole = arc(0, cy, radius - width / 2, 0, 2 * np.pi)
ring.insert_hole([kdb.DPoint(*p) for p in hole])
si.insert(ring)
return cell
def euler_racetrack(
radius=15,
width=1.6,
length=260,
gap=0.26,
access_width=0.6,
coupler_angle=20,
lead_length=10,
):
"""A racetrack of multimode waveguides whose 180 deg bends are Euler bends,
their curvature growing from 0 at the straights to 1 / radius at the apex,
with a pulley coupler concentric with one apex, after Zhang et al.,
Photonics Res. 2020. The coupler angle and the leads are assumed."""
cell = layout.create_cell("EulerRacetrack")
si = cell.shapes(layout.layer(SI))
bend = euler(radius, 180, p=1) + (length, 0)
height = bend[-1, 1]
# The closed loop: the right bend, the left bend and back to the start.
si.insert(path(np.vstack([bend, (length, height) - bend, [(length, 0)]]), width))
# The access waveguide runs up past the right bend: a reverse bend, an arc
# concentric with the apex of the bend (around its center of curvature
# there) and another reverse bend (around centers 2 r further out).
r = radius + width / 2 + gap + access_width / 2
a = np.radians(coupler_angle / 2)
x, y = bend[:, 0].max() - radius, height / 2
x2, y2 = x + 2 * r * np.cos(a), 2 * r * np.sin(a)
access = [
[(x2 - r, y - y2 - lead_length)],
arc(x2, y - y2, r, np.pi, np.pi - a),
arc(x, y, r, -a, a),
arc(x2, y + y2, r, np.pi + a, np.pi),
[(x2 - r, y + y2 + lead_length)],
]
si.insert(path(np.vstack(access), access_width))
return cell
def microrings():
"""The two resonators side by side."""
cell = layout.create_cell("Microrings")
for child, x in [(all_pass_ring(), -50), (euler_racetrack(), 0)]:
cell.insert(kdb.DCellInstArray(child.cell_index(), kdb.DCplxTrans(x, 0)))
return cell
if __name__ == "__main__":
import sys
microrings()
layout.write(sys.argv[1])