from __future__ import annotations
import numpy as np
import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.components.taper import taper as taper_function
from gdsfactory.snap import snap_to_grid
from gdsfactory.typings import (
ComponentSpec,
CrossSectionSpec,
Floats,
LayerSpec,
)
_gaps = (0.2,) * 10
_widths = (0.5,) * 10
[docs]
@gf.cell
def grating_coupler_rectangular_arbitrary(
gaps: Floats = _gaps,
widths: Floats = _widths,
width_grating: float = 11.0,
length_taper: float = 150.0,
polarization: str = "te",
wavelength: float = 1.55,
taper: ComponentSpec | None = taper_function,
layer_grating: LayerSpec | None = None,
layer_slab: LayerSpec | None = None,
slab_xmin: float = -1.0,
slab_offset: float = 1.0,
fiber_angle: float | None = None,
cross_section: CrossSectionSpec = "xs_sc",
**kwargs,
) -> Component:
r"""Grating coupler uniform with rectangular shape (not elliptical).
Therefore it needs a longer taper.
Grating teeth are straight instead of elliptical.
Args:
gaps: list of gaps between grating teeth.
widths: list of grating widths.
width_grating: grating teeth width.
length_taper: taper length (um).
polarization: 'te' or 'tm'.
wavelength: in um.
taper: function.
layer_grating: Optional layer for grating. \
by default None uses cross_section.layer. \
if different from cross_section.layer expands taper.
layer_slab: layer that protects the slab under the grating.
slab_xmin: where 0 is at the start of the taper.
slab_offset: from edge of grating to edge of the slab.
fiber_angle: in degrees.
cross_section: for input waveguide port.
kwargs: cross_section settings.
.. code::
fiber
/ / / /
/ / / /
_|-|_|-|_|-|___ layer
layer_slab |
o1 ______________|
top view _________
/| | | | |
/ | | | | |
/taper_angle
/_ _| | | | |
wg_width | | | | | |
\ | | | | |
\ | | | | |
\ | | | | |
\|_|_|_|_|
<-->
taper_length
"""
xs = gf.get_cross_section(cross_section, **kwargs)
wg_width = xs.width
layer_wg = gf.get_layer(xs.layer)
layer_grating = gf.get_layer(layer_grating) or layer_wg
c = Component()
if taper:
taper_ref = c << gf.get_component(
taper,
length=length_taper,
width2=width_grating,
width1=wg_width,
cross_section=xs,
)
c.add_port(port=taper_ref.ports["o1"], name="o1")
xi = taper_ref.xmax
else:
length_taper = 0
xi = 0
widths = gf.snap.snap_to_grid(widths)
gaps = gf.snap.snap_to_grid(gaps)
y0 = width_grating / 2
for width, gap in zip(widths, gaps):
xi += gap
points = snap_to_grid(
np.array(
[
[xi, -y0],
[xi, +y0],
[xi + width, +y0],
[xi + width, -y0],
]
)
)
c.add_polygon(
points,
layer_grating,
)
xi += width
slab_xmin = length_taper - slab_offset
for section in xs.sections[1:]:
slab_xsize = (xi - length_taper) + section.width / 2
slab_ysize = width_grating + section.width
yslab = slab_ysize / 2
c.add_polygon(
[
(slab_xmin, yslab),
(slab_xmin + slab_xsize, yslab),
(slab_xmin + slab_xsize, -yslab),
(slab_xmin, -yslab),
],
layer=section.layer,
)
if layer_slab:
slab_xmax = length_taper + np.sum(widths) + np.sum(gaps) + slab_offset
slab_ysize = c.ysize + 2 * slab_offset
yslab = slab_ysize / 2
c.add_polygon(
[
(slab_xmin, yslab),
(slab_xmax, yslab),
(slab_xmax, -yslab),
(slab_xmin, -yslab),
],
layer_slab,
)
xport = np.round((xi + length_taper) / 2, 3)
c.add_port(
name="o2",
port_type=f"vertical_{polarization}",
center=(xport, 0),
orientation=0,
width=width_grating,
layer=xs.layer,
)
c.info["polarization"] = polarization
c.info["wavelength"] = wavelength
if fiber_angle is not None:
c.info["fiber_angle"] = fiber_angle
return c
if __name__ == "__main__":
c = grating_coupler_rectangular_arbitrary(cross_section="xs_rc")
# c = grating_coupler_rectangular_arbitrary(
# layer_grating=(3, 0), layer_slab=(2, 0), slab_offset=1
# )
c.show(show_ports=True)