from __future__ import annotations
import gdsfactory as gf
from gdsfactory import Component
from gdsfactory.cross_section import strip
from gdsfactory.typings import CrossSectionSpec
def _generate_fins(c, x, fin_size, bend):
num_fins = int(x.width // (2 * fin_size[1]))
y0 = bend.ports["o1"].x, -num_fins * (2 * fin_size[1]) / 2.0 + fin_size[1]
for i in range(num_fins):
y = y0 + i * 2 * fin_size[1]
rectangle = c << gf.components.rectangle(
size=(fin_size[0], fin_size[1]),
layer=x.layer,
centered=True,
port_type=None,
port_orientations=None,
)
rectangle.movex(
destination=bend.ports["o1"].x
- (1 - bend.ports["o1"].orientation / 90.0) * fin_size[0] / 2.0
)
rectangle.movey(
origin=rectangle.y,
destination=bend.ports["o1"].y + y,
)
c.absorb(rectangle)
return c
def _generate_bends(c, x_top, x_bot, dx, dy, gap):
input_bend_top = (
c << gf.components.bend_s(size=(dx, dy), cross_section=x_top).mirror()
)
input_bend_bottom = c << gf.components.bend_s(
size=(dx, dy), cross_section=x_bot
).mirror().mirror(p1=(1, 0))
dy = input_bend_bottom.ports["o2"].y - input_bend_top.ports["o2"].y
input_bend_top.movey(destination=dy / 2.0 + gap / 2.0)
input_bend_bottom.movey(
destination=-dy / 2.0 - (x_bot.width / 2.0 + x_top.width / 2.0 + gap / 2.0)
)
return (c, input_bend_top, input_bend_bottom)
def _generate_straights(c, length, x_top, x_bot, input_bend_top, input_bend_bottom):
top_straight = c << gf.components.straight(length=length, cross_section=x_top)
bottom_straight = c << gf.components.straight(length=length, cross_section=x_bot)
top_straight.movey(destination=input_bend_top.ports["o2"].y)
bottom_straight.movey(destination=input_bend_bottom.ports["o2"].y)
return (c, top_straight, bottom_straight)
def _generate_gratings(c, length, period, dc, gap, x, bottom_straight, x_bot):
num = length // period
x_size = period * dc
start = length - (num - 1) * period
for i in range(int(num)):
x_start = start + i * period
rectangle = c << gf.components.rectangle(
size=(x_size, gap),
layer=x.layer,
centered=True,
port_type=None,
port_orientations=None,
)
rectangle.movex(destination=x_start)
rectangle.movey(
destination=bottom_straight.ports["o1"].y + x_bot.width / 2.0 + gap / 2.0
)
return c
def _absorb(c, *refs):
for ref in refs:
c.absorb(ref)
return c
[docs]
@gf.cell
def cdc(
length: float = 30.0,
gap: float = 0.5,
period: float = 0.220,
dc: float = 0.5,
dx: float = 10.0,
dy: float = 1.8,
width_top: float = 2.0,
width_bot: float = 0.75,
fins: bool = False,
fin_size: tuple[float, float] = (0.2, 0.05),
cross_section: CrossSectionSpec = strip,
**kwargs,
) -> Component:
"""Grating-Assisted Contra-Directional Coupler.
Args:
length : Length of the coupling region.
gap: Distance between the two straights.
period: Period of the grating.
dc: Duty cycle of the grating. Must be between 0 and 1.
width_top: Width of the top straight in the coupling region.
width_bot: Width of the bottom straight in the coupling region.
dx: size of bends in x-direction.
dy: size of bends in y-direction.
fins: If `True`, adds fins to the input/output straights.
In this case a different template for the component must be specified.
This feature is useful when performing electron-beam lithography
and using different beam currents
for fine features (helps to reduce stitching errors).
fin_size: Specifies the x- and y-size of the `fins`. Defaults to 200 nm x 50 nm.
cross_section: CrossSection spec.
kwargs:
cross_section kwargs.
"""
x = gf.get_cross_section(cross_section, **kwargs)
x_top = x.copy(width=width_top)
x_bot = x.copy(width=width_bot)
c = gf.Component()
c, input_bend_top, input_bend_bottom = _generate_bends(c, x_top, x_bot, dx, dy, gap)
c, top_straight, bottom_straight = _generate_straights(
c, length, x_top, x_bot, input_bend_top, input_bend_bottom
)
bend_output_top = c << input_bend_top.parent.mirror()
bend_output_bottom = c << input_bend_bottom.parent.mirror()
input_bend_top.connect("o2", top_straight.ports["o1"])
input_bend_bottom.connect("o2", bottom_straight.ports["o1"])
bend_output_top.connect("o2", top_straight.ports["o2"])
bend_output_bottom.connect("o2", bottom_straight.ports["o2"])
c = _generate_gratings(c, length, period, dc, gap, x, bottom_straight, x_bot)
if fins:
c = _generate_fins(c, x_top, fin_size, input_bend_top)
c = _generate_fins(c, x_bot, fin_size, input_bend_bottom)
c = _generate_fins(c, x_top, fin_size, bend_output_top)
c = _generate_fins(c, x_bot, fin_size, bend_output_bottom)
c = _absorb(
c,
input_bend_top,
input_bend_bottom,
top_straight,
bottom_straight,
bend_output_top,
bend_output_bottom,
)
x.add_bbox(c)
c.add_port("o1", port=input_bend_top.ports["o1"])
c.add_port("o2", port=input_bend_bottom.ports["o1"])
c.add_port("o3", port=bend_output_top.ports["o1"])
c.add_port("o4", port=bend_output_bottom.ports["o1"])
return c
if __name__ == "__main__":
c = cdc(fins=False)
c.show(show_ports=False)