Source code for gdsfactory.components.spiral_external_io

"""Bends with grating couplers inside the spiral.

maybe: need to add grating coupler loopback as well
"""

from __future__ import annotations

import numpy as np
from numpy import float64

import gdsfactory as gf
from gdsfactory.cell import cell
from gdsfactory.component import Component
from gdsfactory.components.bend_euler import bend_euler
from gdsfactory.routing.manhattan import round_corners
from gdsfactory.typings import ComponentSpec, CrossSectionSpec


def get_bend_port_distances(bend: Component) -> tuple[float64, float64]:
    """Returns distance between bend ports."""
    p0, p1 = bend.ports.values()
    return abs(p0.x - p1.x), abs(p0.y - p1.y)


[docs] @cell def spiral_external_io( N: int = 6, x_inner_length_cutback: float = 300.0, x_inner_offset: float = 0.0, y_straight_inner_top: float = 0.0, xspacing: float = 3.0, yspacing: float = 3.0, bend: ComponentSpec = bend_euler, length: float | None = None, cross_section: CrossSectionSpec = "xs_sc", with_inner_ports: bool = False, y_straight_outer_offset: float = 0.0, inner_loop_spacing_offset: float = 0.0, mirror_straight: bool = False, **kwargs, ) -> Component: """Returns spiral with input and output ports outside the spiral. Args: N: number of loops. x_inner_length_cutback: x inner length. x_inner_offset: x inner offset. y_straight_inner_top: y straight inner top. xspacing: center to center x-spacing. yspacing: center to center y-spacing. bend: function. length: length in um, it is the approximates total length. cross_section: spec. with_inner_ports: if True, removes the internal S-bend and exposes new ports y_straight_outer_offset: amount to add/remove to the last points at the outer output of the spiral inner_loop_spacing_offset: extra difference between the inner ports mirror_straight: if True, mirrors the straight cross section in round_corners (can help when xs is asymmetric) kwargs: cross_section settings. """ if length: x_inner_length_cutback = length / (4 * (N - 1)) y_straight_inner_top += 5 x_inner_length_cutback += x_inner_offset xs = gf.get_cross_section(cross_section, **kwargs) _bend180 = gf.get_component(bend, angle=180, cross_section=xs) _bend90 = gf.get_component(bend, angle=90, cross_section=xs) # If with_arc_floorplan is not True, the radius doesn't represent the actual size of the bend bend_radius = _bend90.xsize - xs.width / 2 rx, ry = get_bend_port_distances(_bend90) _, rx180 = get_bend_port_distances(_bend180) # rx180, second arg since we rotate component = Component() inner_loop_spacing = 2 * bend_radius + 5.0 + inner_loop_spacing_offset # Create manhattan path going from west grating to westmost port of bend 180 x_inner_length = x_inner_length_cutback + 5.0 + xspacing y_inner_bend = y_straight_inner_top - bend_radius - 5.0 x_inner_loop = x_inner_length - 5.0 p1 = (x_inner_loop, y_inner_bend) p2 = (x_inner_loop + inner_loop_spacing, y_inner_bend) _pt = np.array(p1) pts_w = [_pt] for i in range(N): y1 = y_straight_inner_top + ry + (2 * i + 1) * yspacing x2 = inner_loop_spacing + 2 * rx + x_inner_length + (2 * i + 1) * xspacing y3 = -ry - (2 * i + 2) * yspacing y3 += 0 if i < N - 1 else y_straight_outer_offset x4 = -(2 * i + 1) * xspacing if i == N - 1: x4 = x4 - rx180 + xspacing _pt1 = np.array([_pt[0], y1]) _pt2 = np.array([x2, _pt1[1]]) _pt3 = np.array([_pt2[0], y3]) _pt4 = np.array([x4, _pt3[1]]) _pt5 = np.array([_pt4[0], 0]) _pt = _pt5 pts_w += [_pt1, _pt2, _pt3, _pt4, _pt5] pts_w = pts_w[:-2] # Create manhattan path going from east grating to eastmost port of bend 180 _pt = np.array(p2) pts_e = [_pt] for i in range(N): y1 = y_straight_inner_top + ry + (2 * i) * yspacing x2 = inner_loop_spacing + 2 * rx + x_inner_length + 2 * i * xspacing y3 = -ry - (2 * i + 1) * yspacing y3 += 0 if i < N - 1 else y_straight_outer_offset x4 = -2 * i * xspacing _pt1 = np.array([_pt[0], y1]) _pt2 = np.array([x2, _pt1[1]]) _pt3 = np.array([_pt2[0], y3]) _pt4 = np.array([x4, _pt3[1]]) _pt5 = np.array([_pt4[0], 0]) _pt = _pt5 pts_e += [_pt1, _pt2, _pt3, _pt4, _pt5] pts_e = pts_e[:-2] # Join the two bits of paths and extrude the spiral geometry if not with_inner_ports: route = round_corners( pts_w[::-1] + pts_e, bend=bend, cross_section=cross_section, **kwargs, ) component.add(route.references) component.add_port("o2", port=route.ports[0]) component.add_port("o1", port=route.ports[1]) length = route.length # If inner ports, do not join and layout routes separately else: pts_w[1][0] += bend_radius pts_e[1][0] += bend_radius route_west = round_corners( pts_w[1:], bend=bend, cross_section=cross_section, mirror_straight=mirror_straight, **kwargs, ) route_east = round_corners( pts_e[1:], bend=bend, cross_section=cross_section, mirror_straight=mirror_straight, **kwargs, ) component.add(route_west.references) component.add(route_east.references) component.add_port("o2", port=route_west.ports[0]) component.add_port("o1", port=route_east.ports[1]) component.add_port("o4", port=route_west.ports[1]) component.add_port("o3", port=route_east.ports[0]) length = route_west.length + route_east.length component.info["length"] = length return component
if __name__ == "__main__": spacing = 3 c = spiral_external_io( # N=15, # xspacing=spacing, # yspacing=spacing, # with_inner_ports=True, # x_inner_length_cutback=0, # y_straight_inner_top=0, # x_inner_offset=0, ) c.show(show_ports=True)