Source code for gdsfactory.components.extension

from __future__ import annotations

import warnings
from functools import partial

import numpy as np
from numpy import ndarray

import gdsfactory as gf
from gdsfactory.cell import cell
from gdsfactory.component import Component
from gdsfactory.components.mmi1x2 import mmi1x2
from gdsfactory.cross_section import cross_section as cross_section_function
from gdsfactory.port import Port
from gdsfactory.typings import ComponentSpec, Coordinate, CrossSectionSpec, Layer

DEG2RAD = np.pi / 180


def line(
    p_start: Port | Coordinate,
    p_end: Port | Coordinate,
    width: float | None = None,
) -> tuple[float, float, float, float]:
    if isinstance(p_start, gf.Port):
        width = p_start.width
        p_start = p_start.center

    if isinstance(p_end, gf.Port):
        p_end = p_end.center

    w = width
    angle = np.arctan2(p_end[1] - p_start[1], p_end[0] - p_start[0])
    a = np.pi / 2
    p0 = move_polar_rad_copy(p_start, angle + a, w / 2)
    p1 = move_polar_rad_copy(p_start, angle - a, w / 2)
    p2 = move_polar_rad_copy(p_end, angle - a, w / 2)
    p3 = move_polar_rad_copy(p_end, angle + a, w / 2)
    return p0, p1, p2, p3


def move_polar_rad_copy(pos: Coordinate, angle: float, length: float) -> ndarray:
    """Returns the points of a position (pos) with angle, shifted by length.

    Args:
        pos: position.
        angle: in radians.
        length: extension length in um.
    """
    c = np.cos(angle)
    s = np.sin(angle)
    return pos + length * np.array([c, s])


[docs] @cell def extend_port(port: Port, length: float, layer: Layer | None = None) -> Component: """Returns a straight extension component out of a port. Args: port: port to extend. length: extension length in um. layer: for the straight section. """ c = Component() layer = layer or port.layer # Generate a port extension p_start = port.center angle = port.orientation p_end = move_polar_rad_copy(p_start, angle * DEG2RAD, length) w = port.width _line = line(p_start, p_end, w) c.add_polygon(_line, layer=layer) c.add_port(name="original", port=port) port_settings = port.to_dict() port_settings.update(center=p_end) c.add_port(**port_settings) return c
[docs] @gf.cell_with_child def extend_ports( component: ComponentSpec = mmi1x2, port_names: tuple[str, ...] | None = None, length: float = 5.0, extension: ComponentSpec | None = None, port1: str | None = None, port2: str | None = None, port_type: str = "optical", centered: bool = False, cross_section: CrossSectionSpec | None = None, extension_port_names: list[str] | None = None, **kwargs, ) -> Component: """Returns a new component with some ports extended. You can define extension Spec defaults to port cross_section of each port to extend. Args: component: component to extend ports. port_names: list of ports names to extend, if None it extends all ports. length: extension length. extension: function to extend ports (defaults to a straight). port1: extension input port name. port2: extension output port name. port_type: type of the ports to extend. centered: if True centers rectangle at (0, 0). cross_section: extension cross_section, defaults to port cross_section if port has no cross_section it creates one using width and layer. extension_port_names: extension port names add to the new component. Keyword Args: layer: port GDS layer. prefix: port name prefix. orientation: in degrees. width: port width. layers_excluded: List of layers to exclude. port_type: optical, electrical, .... clockwise: if True, sort ports clockwise, False: counter-clockwise. """ c = gf.Component() component = gf.get_component(component) cref = c << component if centered: cref.x = 0 cref.y = 0 ports_all = cref.get_ports_list() port_names_all = [p.name for p in ports_all] ports_to_extend = cref.get_ports_list(port_type=port_type, **kwargs) ports_to_extend_names = [p.name for p in ports_to_extend] ports_to_extend_names = port_names or ports_to_extend_names for port_name in ports_to_extend_names: if port_name not in port_names_all: warnings.warn( f"Port Name {port_name!r} not in {port_names_all}", stacklevel=3 ) for port in ports_all: port_name = port.name port = cref.ports[port_name] if port_name in ports_to_extend_names: if extension: extension_component = gf.get_component(extension) else: cross_section_extension = ( cross_section or port.cross_section or cross_section_function(layer=port.layer, width=port.width) ) if cross_section_extension is None: raise ValueError("cross_section=None for extend_ports") extension_component = gf.components.straight( length=length, cross_section=cross_section_extension, ) port_labels = list(extension_component.ports.keys()) port1 = port1 or port_labels[0] port2 = port2 or port_labels[-1] extension_ref = c << extension_component extension_ref.connect(port1, port) c.add_port(port_name, port=extension_ref.ports[port2]) extension_port_names = extension_port_names or [] [ c.add_port(name, port=extension_ref.ports[name]) for name in extension_port_names ] else: c.add_port(port_name, port=component.ports[port_name]) c.copy_child_info(component) return c
def test_extend_ports() -> None: import gdsfactory.components as pc width = 0.5 xs_strip = partial( gf.cross_section.strip, width=width, cladding_layers=None, ) c = pc.straight(cross_section=xs_strip) c1 = extend_ports( component=c, cross_section=xs_strip, ) assert len(c.ports) == len(c1.ports) p = assert_polygons(c1, 3) c2 = extend_ports(component=c, cross_section=xs_strip, port_names=("o1",)) p = assert_polygons(c2, 2) c3 = extend_ports(component=c, cross_section=xs_strip) p = len(c3.polygons) assert p == 0, p c4 = extend_ports(component=c, port_type="electrical") p = assert_polygons(c4, 1) def assert_polygons(component, n_polygons): result = len(component.polygons) assert result == 0, result assert len(component.references) == n_polygons, len(component.references) return result __all__ = ["extend_ports", "extend_port"] if __name__ == "__main__": # test_extend_ports() c0 = gf.c.straight() # p0 = c0["o1"] # c = extend_port(p0, length=100) c = extend_ports(c0, length=100) print(c.name) c.show()