Source code for gdsfactory.routing.get_route_from_steps

from __future__ import annotations

from collections.abc import Iterable
from functools import partial

import numpy as np

import gdsfactory as gf
from gdsfactory.components.bend_euler import bend_euler
from gdsfactory.components.straight import straight
from gdsfactory.components.taper import taper
from gdsfactory.components.via_corner import via_corner
from gdsfactory.port import Port
from gdsfactory.routing.get_route_sbend import get_route_sbend
from gdsfactory.routing.manhattan import RouteError, get_route_error, round_corners
from gdsfactory.typings import (
    STEP_DIRECTIVES,
    ComponentSpec,
    CrossSectionSpec,
    MultiCrossSectionAngleSpec,
    Route,
)


[docs] def get_route_from_steps( port1: Port, port2: Port, steps: Iterable[dict[str, float]] | None = None, bend: ComponentSpec = bend_euler, straight: ComponentSpec = straight, taper: ComponentSpec | None = taper, cross_section: CrossSectionSpec | MultiCrossSectionAngleSpec = "xs_sc", with_sbend: bool = True, auto_widen: bool = False, auto_widen_minimum_length: float = 100, taper_length: float = 10, width_wide: float = 2, **kwargs, ) -> Route: """Returns a route formed by the given waypoints steps. Uses smooth euler bends instead of corners and tapers in straight sections. Tapering to wider straights reduces the optical loss when auto_widen=True. `get_route_from_steps` is a manual version of `get_route` and a more concise and convenient version of `get_route_from_waypoints` Args: port1: start port. port2: end port. steps: that define the route (x, y, dx, dy) [{'dx': 5}, {'dy': 10}]. bend: function that returns bends. straight: straight spec. taper: taper spec. cross_section: cross_section spec. with_sbend: whether to add sbend for impossible manhattan routes. auto_widen: whether to add tapers to wider straights. auto_widen_minimum_length: minimum length to auto widen. taper_length: length of taper. width_wide: width of the wider straight. kwargs: cross_section settings. .. plot:: :include-source: import gdsfactory as gf c = gf.Component("get_route_from_steps_sample") w = gf.components.straight() left = c << w right = c << w right.move((100, 80)) obstacle = gf.components.rectangle(size=(100, 10), port_type=None) obstacle1 = c << obstacle obstacle2 = c << obstacle obstacle1.ymin = 40 obstacle2.xmin = 25 p1 = left.ports['o2'] p2 = right.ports['o2'] route = gf.routing.get_route_from_steps( port1=p1, port2=p2, steps=[ {"x": 20}, {"y": 20}, {"x": 120}, {"y": 80}, ], ) c.add(route.references) c.plot() """ x, y = port1.center x2, y2 = port2.center waypoints = [(x, y)] steps = steps or [] for d in steps: if not STEP_DIRECTIVES.issuperset(d): invalid_step_directives = list(set(d.keys()) - STEP_DIRECTIVES) raise ValueError( f"Invalid step directives: {invalid_step_directives}." f"Valid directives are {list(STEP_DIRECTIVES)}" ) x = d["x"] if "x" in d else x x += d.get("dx", 0) y = d["y"] if "y" in d else y y += d.get("dy", 0) waypoints += [(x, y)] waypoints += [(x2, y2)] waypoints = np.array(waypoints) if isinstance(cross_section, list | tuple): xs_list = [] for element in cross_section: xs, angles = element xs = gf.get_cross_section(xs) xs = gf.get_cross_section(xs, **kwargs) xs_list.append((xs, angles)) cross_section = xs_list else: xs = cross_section = gf.get_cross_section(cross_section, **kwargs) if auto_widen: taper = gf.get_component( taper, length=taper_length, width1=xs.width, width2=width_wide, cross_section=cross_section, **kwargs, ) else: taper = None try: return round_corners( points=waypoints, bend=bend, straight=straight, taper=taper, cross_section=cross_section, with_sbend=with_sbend, auto_widen=auto_widen, width_wide=width_wide, taper_length=taper_length, auto_widen_minimum_length=auto_widen_minimum_length, **kwargs, ) except RouteError: if with_sbend: return get_route_sbend(port1, port2, cross_section=cross_section, **kwargs) return get_route_error(points=waypoints, with_sbend=False)
get_route_from_steps_electrical = partial( get_route_from_steps, bend="wire_corner", taper=None, cross_section="xs_m3" ) get_route_from_steps_electrical_multilayer = partial( get_route_from_steps, bend=via_corner, taper=None, cross_section=[ (gf.cross_section.metal2, (90, 270)), ("xs_metal_routing", (0, 180)), ], ) def test_route_from_steps(): c = gf.Component() layer = (2, 0) w = gf.components.straight(layer=layer) left = c << w right = c << w right.move((100, 80)) obstacle = gf.components.rectangle(size=(100, 10)) obstacle1 = c << obstacle obstacle2 = c << obstacle obstacle1.ymin = 40 obstacle2.xmin = 25 p1 = left.ports["o2"] p2 = right.ports["o2"] route = get_route_from_steps( port1=p1, port2=p2, steps=[ {"x": 20, "y": 0}, {"x": 20, "y": 20}, {"x": 120, "y": 20}, {"x": 120, "y": 80}, ], layer=layer, ) length = 186.548 assert route.length == length, route.length route = gf.routing.get_route_from_steps( port1=p1, port2=p2, steps=[ {"x": 20}, {"y": 20}, {"x": 120}, {"y": 80}, ], layer=layer, ) c.add(route.references) assert route.length == length, route.length return c if __name__ == "__main__": test_route_from_steps() # c = gf.Component("get_route_from_steps_sample") # w = gf.components.straight() # left = c << w # right = c << w # right.move((100, 80)) # p1 = left.ports["o2"] # p2 = right.ports["o2"] # route = get_route_from_steps( # port1=p2, # port2=p1, # steps=[ # {"x": 20, "y": 0}, # {"x": 20, "y": 20}, # {"x": 120, "y": 20}, # {"x": 120, "y": 80}, # ], # ) # c.add(route.references) # c.add(route.labels) # c.show(show_ports=True) c = gf.Component("pads_route_from_steps") pt = c << gf.components.pad_array(orientation=270, columns=3) pb = c << gf.components.pad_array(orientation=90, columns=3) pt.move((100, 200)) route = gf.routing.get_route_from_steps_electrical( pb.ports["e11"], pt.ports["e11"], steps=[ {"y": 200}, # {"z": 200}, ], # cross_section='metal_routing', # bend=gf.components.wire_corner, ) c.add(route.references) c.show(show_ports=True)