Source code for gdsfactory.components.tapers.taper

from __future__ import annotations

from functools import partial

import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.port import Port
from gdsfactory.typings import CrossSectionSpec, LayerSpec


[docs] @gf.cell_with_module_name def taper( length: float = 10.0, width1: float = 0.5, width2: float | None = None, layer: LayerSpec | None = None, port: Port | None = None, with_two_ports: bool = True, cross_section: CrossSectionSpec = "strip", port_names: tuple[str, str] = ("o1", "o2"), port_types: tuple[str, str] = ("optical", "optical"), with_bbox: bool = True, ) -> Component: """Linear taper, which tapers only the main cross section section. Args: length: taper length. width1: width of the west/left port. width2: width of the east/right port. Defaults to width1. layer: layer for the taper. port: can taper from a port instead of defining width1. with_two_ports: includes a second port. False for terminator and edge coupler fiber interface. cross_section: specification (CrossSection, string, CrossSectionFactory dict). port_names: input and output port names. Second name only used if with_two_ports. port_types: input and output port types. Second type only used if with_two_ports. with_bbox: box in bbox_layers and bbox_offsets to avoid DRC sharp edges. """ if len(port_types) != 2: raise ValueError("port_types should have two elements") x1 = gf.get_cross_section(cross_section, width=width1) if width2: width2 = gf.snap.snap_to_grid2x(width2) x2 = gf.get_cross_section(cross_section, width=width2) else: x2 = x1 width1 = x1.width width2 = x2.width width_max = max([width1, width2]) if layer: x = gf.get_cross_section(cross_section, width=width_max, layer=layer) else: x = gf.get_cross_section(cross_section, width=width_max) layer = layer or x.layer assert layer is not None if isinstance(port, gf.Port): width1 = port.width width2 = width2 or width1 c = gf.Component() y1 = width1 / 2 y2 = width2 / 2 if length: p1 = gf.kdb.DPolygon( [ gf.kdb.DPoint(0, y1), gf.kdb.DPoint(length, y2), gf.kdb.DPoint(length, -y2), gf.kdb.DPoint(0, -y1), ] ) c.add_polygon(p1, layer=layer) s0_width = x.sections[0].width for section in x.sections[1:]: delta_width = abs(section.width - s0_width) y1 = (width1 + delta_width) / 2 y2 = (width2 + delta_width) / 2 p1 = gf.kdb.DPolygon( [ gf.kdb.DPoint(0, y1), gf.kdb.DPoint(length, y2), gf.kdb.DPoint(length, -y2), gf.kdb.DPoint(0, -y1), ] ) c.add_polygon(p1, layer=section.layer) if with_bbox: x.add_bbox(c) c.add_port( name=port_names[0], center=(0, 0), width=width1, orientation=180, layer=layer, cross_section=x1, port_type=port_types[0], ) if with_two_ports: c.add_port( name=port_names[1], center=(length, 0), width=width2, orientation=0, layer=layer, cross_section=x2, port_type=port_types[1], ) c.info["length"] = length c.info["width1"] = float(width1) c.info["width2"] = float(width2) return c
[docs] @gf.cell_with_module_name def taper_strip_to_ridge( length: float = 10.0, width1: float = 0.5, width2: float = 0.5, w_slab1: float = 0.15, w_slab2: float = 6.0, layer_wg: LayerSpec = "WG", layer_slab: LayerSpec = "SLAB90", cross_section: CrossSectionSpec = "strip", use_slab_port: bool = False, ) -> Component: r"""Linear taper from strip to rib. Args: length: taper length (um). width1: in um. width2: in um. w_slab1: slab width in um. w_slab2: slab width in um. layer_wg: for input waveguide. layer_slab: for output waveguide with slab. cross_section: for input waveguide. use_slab_port: if True adds a second port for the slab. .. code:: __________________________ / | _______/____________|______________ / | width1 |w_slab1 | w_slab2 width2 ______\_____________|______________ \ | \__________________________ """ xs = gf.get_cross_section(cross_section) taper_wg = taper( length=length, width1=width1, width2=width2, cross_section=cross_section, layer=layer_wg, ) taper_slab = taper( length=length, width1=w_slab1, width2=w_slab2, cross_section=cross_section, with_bbox=False, layer=layer_slab, ) c = gf.Component() taper_ref_wg = c << taper_wg taper_ref_slab = c << taper_slab c.info["length"] = length c.add_port(name="o1", port=taper_ref_wg.ports["o1"]) if use_slab_port: c.add_port(name="o2", port=taper_ref_slab.ports["o2"]) else: c.add_port(name="o2", port=taper_ref_wg.ports["o2"]) if length: xs.add_bbox(c) c.flatten() return c
[docs] @gf.cell_with_module_name def taper_strip_to_ridge_trenches( length: float = 10.0, width: float = 0.5, slab_offset: float = 3.0, trench_width: float = 2.0, trench_layer: LayerSpec = "DEEP_ETCH", layer_wg: LayerSpec = "WG", trench_offset: float = 0.1, ) -> gf.Component: """Defines taper using trenches to define the etch. Args: length: in um. width: in um. slab_offset: in um. trench_width: in um. trench_layer: trench layer. layer_wg: waveguide layer. trench_offset: after waveguide in um. """ c = gf.Component() y0 = width / 2 + trench_width - trench_offset yL = width / 2 + trench_width - trench_offset + slab_offset # straight x = [0, length, length, 0] yw = [y0, yL, -yL, -y0] c.add_polygon(list(zip(x, yw, strict=False)), layer=layer_wg) # top trench ymin0 = width / 2 yminL = width / 2 ymax0 = width / 2 + trench_width ymaxL = width / 2 + trench_width + slab_offset x = [0, length, length, 0] ytt = [ymin0, yminL, ymaxL, ymax0] ytb = [-ymin0, -yminL, -ymaxL, -ymax0] c.add_polygon(list(zip(x, ytt, strict=False)), layer=trench_layer) c.add_polygon(list(zip(x, ytb, strict=False)), layer=trench_layer) c.add_port(name="o1", center=(0, 0), width=width, orientation=180, layer=layer_wg) c.add_port( name="o2", center=(length, 0), width=width, orientation=0, layer=layer_wg ) return c
taper_strip_to_slab150 = partial(taper_strip_to_ridge, layer_slab="SLAB150")
[docs] @gf.cell_with_module_name def taper_sc_nc( width1: float = 0.5, width2: float = 1, length: float = 20, layer_wg: LayerSpec = "WG", layer_nitride: LayerSpec = "WGN", width_tip_nitride: float = 0.15, width_tip_silicon: float = 0.15, cross_section: CrossSectionSpec = "strip", ) -> Component: """Taper from strip to nitride. Args: width1: strip width. width2: nitride width. length: taper length. layer_wg: strip layer. layer_nitride: nitride layer. width_tip_nitride: tip width for nitride. width_tip_silicon: tip width for strip. cross_section: cross_section specification. """ return taper_strip_to_ridge( layer_wg=layer_wg, layer_slab=layer_nitride, length=length, width1=width1, width2=width_tip_silicon, w_slab1=width_tip_nitride, w_slab2=width2, use_slab_port=True, cross_section=cross_section, )
[docs] @gf.cell_with_module_name def taper_nc_sc( width1: float = 1, width2: float = 0.5, length: float = 20, layer_wg: LayerSpec = "WG", layer_nitride: LayerSpec = "WGN", width_tip_nitride: float = 0.15, width_tip_silicon: float = 0.15, cross_section: CrossSectionSpec = "strip", ) -> Component: """Taper from nitride to strip. Args: width1: nitride width. width2: silicon width. length: taper length. layer_wg: nitride layer. layer_nitride: strip layer. width_tip_nitride: tip width for nitride. width_tip_silicon: tip width for strip. cross_section: cross_section specification. """ c = gf.Component() taper = taper_sc_nc( width1=width2, width2=width1, length=length, layer_wg=layer_wg, layer_nitride=layer_nitride, width_tip_nitride=width_tip_nitride, width_tip_silicon=width_tip_silicon, cross_section=cross_section, ) c.copy_child_info(taper) ref = c << taper ref.mirror_x() c.add_ports(ref.ports) c.auto_rename_ports() c.flatten() return c
taper_electrical = partial( taper, port_types=("electrical", "electrical"), port_names=("e1", "e2"), cross_section="metal_routing", )