Routing

Routing allows you to route waveguides between component ports

[1]:
import gdsfactory as gf

gf.config.set_plot_options(show_subports=False)
2021-09-25 16:19:39.109 | INFO     | gdsfactory.config:<module>:51 - 3.2.9
[2]:
c = gf.Component()
mmi1 = c << gf.components.mmi1x2()
mmi2 = c << gf.components.mmi1x2()
mmi2.move((100, 50))
c
../_images/notebooks_04_routing_2_0.png
[2]:
Unnamed_b167dda7: uid 1, ports [], aliases [], 0 polygons, 2 references

get_route

get_route returns a Manhattan route between 2 ports

[3]:
gf.routing.get_route?
[4]:
c = gf.Component("sample_connect")
mmi1 = c << gf.components.mmi1x2()
mmi2 = c << gf.components.mmi1x2()
mmi2.move((100, 50))
route = gf.routing.get_route(mmi1.ports["o2"], mmi2.ports["o1"])
c.add(route.references)
c
../_images/notebooks_04_routing_5_0.png
[4]:
sample_connect: uid 5, ports [], aliases [], 0 polygons, 7 references
[5]:
route
[5]:
Route(references=[DeviceReference (parent Device "bend_euler_cross_sectio_a3a0edf9", ports ['o1', 'o2'], origin [67.24   0.625], rotation 0, x_reflection False), DeviceReference (parent Device "bend_euler_cross_sectio_a3a0edf9", ports ['o1', 'o2'], origin [77.24 40.  ], rotation 90.0, x_reflection True), DeviceReference (parent Device "straight_cross_sectionc_0fad2ff6", ports ['o1', 'o2'], origin [12.75   0.625], rotation 0, x_reflection False), DeviceReference (parent Device "straight_cross_sectionc_a6977867", ports ['o1', 'o2'], origin [77.24  10.625], rotation 90.0, x_reflection False), DeviceReference (parent Device "straight_cross_sectionc_aca44847", ports ['o1', 'o2'], origin [87.24 50.  ], rotation 0, x_reflection False)], ports=(Port (name o1, midpoint [12.75   0.625], width 0.5, orientation 180.0, layer (1, 0), port_type optical), Port (name o2, midpoint [87.25 50.  ], width 0.5, orientation 0.0, layer (1, 0), port_type optical)), length=117.473)

Connect strip: Problem

sometimes there are obstacles that connect strip does not see!

[6]:
c = gf.Component("sample_problem")
mmi1 = c << gf.components.mmi1x2()
mmi2 = c << gf.components.mmi1x2()
mmi2.move((110, 50))
x = c << gf.components.cross(length=20)
x.move((135, 20))
route = gf.routing.get_route(mmi1.ports["o2"], mmi2.ports["o2"])
c.add(route.references)
c
../_images/notebooks_04_routing_8_0.png
[6]:
sample_problem: uid 14, ports [], aliases [], 0 polygons, 8 references

Solution: Connect strip way points

You can also specify the points along the route

[7]:
gf.routing.get_route_waypoints?
Object `gf.routing.get_route_waypoints` not found.
[8]:
c = gf.Component("sample_avoid_obstacle")
mmi1 = c << gf.components.mmi1x2()
mmi2 = c << gf.components.mmi1x2()
mmi2.move((110, 50))
x = c << gf.components.cross(length=20)
x.move((135, 20))

x0 = mmi1.ports["o3"].x
y0 = mmi1.ports["o3"].y


x2 = mmi2.ports["o3"].x
y2 = mmi2.ports["o3"].y

route = gf.routing.get_route_from_waypoints(
    [(x0, y0), (x2 + 40, y0), (x2 + 40, y2), (x2, y2)]
)
c.add(route.references)
c
../_images/notebooks_04_routing_11_0.png
[8]:
sample_avoid_obstacle: uid 25, ports [], aliases [], 0 polygons, 8 references
[9]:
route.length
[9]:
233.598
[10]:
route.ports
[10]:
(Port (name o1, midpoint [12.75  -0.625], width 0.5, orientation 180.0, layer (1, 0), port_type optical),
 Port (name o2, midpoint [122.75   49.375], width 0.5, orientation 180.0, layer (1, 0), port_type optical))
[11]:
route.references
[11]:
[DeviceReference (parent Device "bend_euler_cross_sectio_a3a0edf9", ports ['o1', 'o2'], origin [152.75   -0.625], rotation 0, x_reflection False),
 DeviceReference (parent Device "bend_euler_cross_sectio_a3a0edf9", ports ['o1', 'o2'], origin [162.75   39.375], rotation 90, x_reflection False),
 DeviceReference (parent Device "straight_cross_sectionc_e7bc1881", ports ['o1', 'o2'], origin [12.75  -0.625], rotation 0, x_reflection False),
 DeviceReference (parent Device "straight_cross_sectionc_134885ba", ports ['o1', 'o2'], origin [162.75    9.375], rotation 90.0, x_reflection False),
 DeviceReference (parent Device "straight_cross_sectionc_134885ba", ports ['o1', 'o2'], origin [152.75   49.375], rotation 180.0, x_reflection False)]

Lets say that we want to extrude the waveguide using a different waveguide crosssection, for example using a different layer

[12]:
import gdsfactory as gf

c = gf.Component("sample_connect")
mmi1 = c << gf.components.mmi1x2()
mmi2 = c << gf.components.mmi1x2()
mmi2.move((100, 50))
route = gf.routing.get_route(
    mmi1.ports["o3"], mmi2.ports["o1"], cross_section=gf.cross_section.metal1
)
c.add(route.references)
c
../_images/notebooks_04_routing_16_0.png
[12]:
sample_connect: uid 35, ports [], aliases [], 0 polygons, 7 references

get_route_from_waypoints

Sometimes you need to set up a route with custom waypoints. get_route_from_waypoints is a manual version of get_route

[13]:
import gdsfactory as gf

c = gf.Component("waypoints_sample")

w = gf.components.straight()
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


p0x, p0y = left.ports["o2"].midpoint
p1x, p1y = right.ports["o2"].midpoint
o = 10  # vertical offset to overcome bottom obstacle
ytop = 20


routes = gf.routing.get_route_from_waypoints(
    [
        (p0x, p0y),
        (p0x + o, p0y),
        (p0x + o, ytop),
        (p1x + o, ytop),
        (p1x + o, p1y),
        (p1x, p1y),
    ],
)
c.add(routes.references)
c
../_images/notebooks_04_routing_18_0.png
[13]:
waypoints_sample: uid 44, ports [], aliases [], 0 polygons, 10 references

get_route_from_steps

As you can see waypoints can only change one point (x or y) at a time, making the waypoint definition a bit redundant.

You can also use a get_route_from_steps which is a more concise route definition, that supports defining only the new steps x or y together with increments dx or dy

get_route_from_steps is a manual version of get_route and a more concise and convenient version of get_route_from_waypoints

[14]:
import gdsfactory as gf

c = gf.Component("get_route_from_steps")
w = gf.components.straight()
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

port1 = left.ports["o2"]
port2 = right.ports["o2"]

routes = gf.routing.get_route_from_steps(
    port1=port1,
    port2=port2,
    steps=[
        {"x": 20, "y": 0},
        {"x": 20, "y": 20},
        {"x": 120, "y": 20},
        {"x": 120, "y": 80},
    ],
)
c.add(routes.references)
c
../_images/notebooks_04_routing_20_0.png
[14]:
get_route_from_steps: uid 52, ports [], aliases [], 0 polygons, 10 references
[15]:
import gdsfactory as gf

c = gf.Component("get_route_from_steps_shorter_syntax")
w = gf.components.straight()
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

port1 = left.ports["o2"]
port2 = right.ports["o2"]

routes = gf.routing.get_route_from_steps(
    port1=port1,
    port2=port2,
    steps=[
        {"x": 20},
        {"y": 20},
        {"x": 120},
        {"y": 80},
    ],
)
c.add(routes.references)
c
../_images/notebooks_04_routing_21_0.png
[15]:
get_route_from_steps_shorter_syntax: uid 60, ports [], aliases [], 0 polygons, 10 references

get_bundle

Problem

See the route collisions When connecting groups of ports using a regular manhattan single-route router such as get route

[16]:
import gdsfactory as gf

xs_top = [0, 10, 20, 40, 50, 80]
pitch = 127
N = len(xs_top)
xs_bottom = [(i - N / 2) * pitch for i in range(N)]

top_ports = [gf.Port(f"top_{i}", (xs_top[i], 0), 0.5, 270) for i in range(N)]

bottom_ports = [gf.Port(f"bottom_{i}", (xs_bottom[i], -100), 0.5, 90) for i in range(N)]

c = gf.Component(name="connect_bundle")

for p1, p2 in zip(top_ports, bottom_ports):
    route = gf.routing.get_route(p1, p2)
    c.add(route.references)

c
../_images/notebooks_04_routing_23_0.png
[16]:
connect_bundle: uid 68, ports [], aliases [], 0 polygons, 30 references

solution

get_bundle provides you with river routing capabilities, that you can use to route bundles of ports without collisions

[17]:
c = gf.Component(name="connect_bundle")
routes = gf.routing.get_bundle(top_ports, bottom_ports)
for route in routes:
    c.add(route.references)

c
../_images/notebooks_04_routing_25_0.png
[17]:
connect_bundle: uid 79, ports [], aliases [], 0 polygons, 30 references
[18]:
import gdsfactory as gf

ys_right = [0, 10, 20, 40, 50, 80]
pitch = 127.0
N = len(ys_right)
ys_left = [(i - N / 2) * pitch for i in range(N)]

right_ports = [gf.Port(f"R_{i}", (0, ys_right[i]), 0.5, 180) for i in range(N)]
left_ports = [gf.Port(f"L_{i}".format(i), (-400, ys_left[i]), 0.5, 0) for i in range(N)]

# you can also mess up the port order and it will sort them by default
left_ports.reverse()

c = gf.Component(name="connect_bundle2")
routes = gf.routing.get_bundle(right_ports, left_ports, sort_ports=True)
for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_26_0.png
[18]:
connect_bundle2: uid 95, ports [], aliases [], 0 polygons, 30 references
[19]:
xs_top = [0, 10, 20, 40, 50, 80]
pitch = 127.0
N = len(xs_top)
xs_bottom = [(i - N / 2) * pitch for i in range(N)]

top_ports = [gf.Port(f"top_{i}", (xs_top[i], 0), 0.5, 270) for i in range(N)]

bottom_ports = [gf.Port(f"bottom_{i}", (xs_bottom[i], -400), 0.5, 90) for i in range(N)]

c = gf.Component(name="connect_bundle")
routes = gf.routing.get_bundle(top_ports, bottom_ports, separation=5.)
for route in routes:
    c.add(route.references)

c
../_images/notebooks_04_routing_27_0.png
[19]:
connect_bundle: uid 111, ports [], aliases [], 0 polygons, 30 references

get_bundle can also route bundles through corners

[20]:
import gdsfactory as gf
from gdsfactory.cell import cell
from gdsfactory.component import Component
from gdsfactory.port import Port


@cell
def test_connect_corner(N=6, config="A"):

    d = 10.0
    sep = 5.0
    top_cell = gf.Component(name="connect_corner")

    if config in ["A", "B"]:
        a = 100.0
        ports_A_TR = [
            Port("A_TR_{}".format(i), (d, a / 2 + i * sep), 0.5, 0) for i in range(N)
        ]
        ports_A_TL = [
            Port("A_TL_{}".format(i), (-d, a / 2 + i * sep), 0.5, 180) for i in range(N)
        ]
        ports_A_BR = [
            Port("A_BR_{}".format(i), (d, -a / 2 - i * sep), 0.5, 0) for i in range(N)
        ]
        ports_A_BL = [
            Port("A_BL_{}".format(i), (-d, -a / 2 - i * sep), 0.5, 180)
            for i in range(N)
        ]

        ports_A = [ports_A_TR, ports_A_TL, ports_A_BR, ports_A_BL]

        ports_B_TR = [
            Port("B_TR_{}".format(i), (a / 2 + i * sep, d), 0.5, 90) for i in range(N)
        ]
        ports_B_TL = [
            Port("B_TL_{}".format(i), (-a / 2 - i * sep, d), 0.5, 90) for i in range(N)
        ]
        ports_B_BR = [
            Port("B_BR_{}".format(i), (a / 2 + i * sep, -d), 0.5, 270) for i in range(N)
        ]
        ports_B_BL = [
            Port("B_BL_{}".format(i), (-a / 2 - i * sep, -d), 0.5, 270)
            for i in range(N)
        ]

        ports_B = [ports_B_TR, ports_B_TL, ports_B_BR, ports_B_BL]

    elif config in ["C", "D"]:
        a = N * sep + 2 * d
        ports_A_TR = [
            Port("A_TR_{}".format(i), (a, d + i * sep), 0.5, 0) for i in range(N)
        ]
        ports_A_TL = [
            Port("A_TL_{}".format(i), (-a, d + i * sep), 0.5, 180) for i in range(N)
        ]
        ports_A_BR = [
            Port("A_BR_{}".format(i), (a, -d - i * sep), 0.5, 0) for i in range(N)
        ]
        ports_A_BL = [
            Port("A_BL_{}".format(i), (-a, -d - i * sep), 0.5, 180) for i in range(N)
        ]

        ports_A = [ports_A_TR, ports_A_TL, ports_A_BR, ports_A_BL]

        ports_B_TR = [
            Port("B_TR_{}".format(i), (d + i * sep, a), 0.5, 90) for i in range(N)
        ]
        ports_B_TL = [
            Port("B_TL_{}".format(i), (-d - i * sep, a), 0.5, 90) for i in range(N)
        ]
        ports_B_BR = [
            Port("B_BR_{}".format(i), (d + i * sep, -a), 0.5, 270) for i in range(N)
        ]
        ports_B_BL = [
            Port("B_BL_{}".format(i), (-d - i * sep, -a), 0.5, 270) for i in range(N)
        ]

        ports_B = [ports_B_TR, ports_B_TL, ports_B_BR, ports_B_BL]

    if config in ["A", "C"]:
        for ports1, ports2 in zip(ports_A, ports_B):
            routes = gf.routing.get_bundle(ports1, ports2, layer=(2, 0), radius=5)
            for route in routes:
                top_cell.add(route.references)

    elif config in ["B", "D"]:
        for ports1, ports2 in zip(ports_A, ports_B):
            routes = gf.routing.get_bundle(ports2, ports1, layer=(2, 0), radius=5)
            for route in routes:
                top_cell.add(route.references)

    return top_cell


c = test_connect_corner(config="A")
c
../_images/notebooks_04_routing_29_0.png
[20]:
test_connect_corner_configA: uid 127, ports [], aliases [], 0 polygons, 72 references
[21]:
c = test_connect_corner(config="C")
c
../_images/notebooks_04_routing_30_0.png
[21]:
test_connect_corner_configC: uid 137, ports [], aliases [], 0 polygons, 168 references
[22]:
@cell
def test_connect_bundle_udirect(dy=200, angle=270):

    xs1 = [-100, -90, -80, -55, -35, 24, 0] + [200, 210, 240]

    axis = "X" if angle in [0, 180] else "Y"

    pitch = 10.0
    N = len(xs1)
    xs2 = [70 + i * pitch for i in range(N)]

    if axis == "X":
        ports1 = [Port(f"top_{i}", (0, xs1[i]), 0.5, angle) for i in range(N)]

        ports2 = [Port(f"bottom_{i}", (dy, xs2[i]), 0.5, angle) for i in range(N)]

    else:
        ports1 = [Port(f"top_{i}", (xs1[i], 0), 0.5, angle) for i in range(N)]

        ports2 = [Port(f"bottom_{i}", (xs2[i], dy), 0.5, angle) for i in range(N)]

    top_cell = Component(name="connect_bundle_udirect")
    routes = gf.routing.get_bundle(ports1, ports2, radius=10.0)
    for route in routes:
        top_cell.add(route.references)

    return top_cell


c = test_connect_bundle_udirect()
c
../_images/notebooks_04_routing_31_0.png
[22]:
test_connect_bundle_udirect: uid 152, ports [], aliases [], 0 polygons, 50 references
[23]:
@cell
def test_connect_bundle_u_indirect(dy=-200, angle=180):
    xs1 = [-100, -90, -80, -55, -35] + [200, 210, 240]
    axis = "X" if angle in [0, 180] else "Y"
    pitch = 10.0
    N = len(xs1)
    xs2 = [50 + i * pitch for i in range(N)]

    a1 = angle
    a2 = a1 + 180

    if axis == "X":
        ports1 = [Port("top_{}".format(i), (0, xs1[i]), 0.5, a1) for i in range(N)]

        ports2 = [Port("bottom_{}".format(i), (dy, xs2[i]), 0.5, a2) for i in range(N)]

    else:
        ports1 = [Port("top_{}".format(i), (xs1[i], 0), 0.5, a1) for i in range(N)]

        ports2 = [Port("bottom_{}".format(i), (xs2[i], dy), 0.5, a2) for i in range(N)]

    top_cell = Component("connect_bundle_u_indirect")
    routes = gf.routing.get_bundle(
        ports1, ports2, bend_factory=gf.components.bend_euler, radius=10
    )
    for route in routes:
        top_cell.add(route.references)

    return top_cell


c = test_connect_bundle_u_indirect(angle=0)
c
['north', 'west']
../_images/notebooks_04_routing_32_1.png
[23]:
test_connect_bundle_u_i_d289aeae: uid 179, ports [], aliases [], 0 polygons, 72 references
[24]:
import gdsfactory as gf


@gf.cell
def test_north_to_south():
    dy = 200.0
    xs1 = [-500, -300, -100, -90, -80, -55, -35, 200, 210, 240, 500, 650]

    pitch = 10.0
    N = len(xs1)
    xs2 = [-20 + i * pitch for i in range(N // 2)]
    xs2 += [400 + i * pitch for i in range(N // 2)]

    a1 = 90
    a2 = a1 + 180

    ports1 = [gf.Port("top_{}".format(i), (xs1[i], 0), 0.5, a1) for i in range(N)]
    ports2 = [gf.Port("bottom_{}".format(i), (xs2[i], dy), 0.5, a2) for i in range(N)]

    c = gf.Component()
    routes = gf.routing.get_bundle(ports1, ports2, auto_widen=False)
    for route in routes:
        c.add(route.references)

    return c


c = test_north_to_south()
c
../_images/notebooks_04_routing_33_0.png
[24]:
test_north_to_south: uid 209, ports [], aliases [], 0 polygons, 60 references
[25]:
def demo_connect_bundle():
    """combines all the connect_bundle tests"""
    y = 400.0
    x = 500
    y0 = 900
    dy = 200.0
    c = Component("connect_bundle")
    for j, s in enumerate([-1, 1]):
        for i, angle in enumerate([0, 90, 180, 270]):
            _cmp = test_connect_bundle_u_indirect(dy=s * dy, angle=angle)
            _cmp_ref = _cmp.ref(position=(i * x, j * y))
            c.add(_cmp_ref)

            _cmp = test_connect_bundle_udirect(dy=s * dy, angle=angle)
            _cmp_ref = _cmp.ref(position=(i * x, j * y + y0))
            c.add(_cmp_ref)

    for i, config in enumerate(["A", "B", "C", "D"]):
        _cmp = test_connect_corner(config=config)
        _cmp_ref = _cmp.ref(position=(i * x, 1700))
        c.add(_cmp_ref)

    # _cmp = test_facing_ports()
    # _cmp_ref = _cmp.ref(position=(800, 1820))
    # c.add(_cmp_ref)

    return c


c = demo_connect_bundle()
c
['north', 'west']
['east', 'south']
['north', 'east']
['east', 'north']
../_images/notebooks_04_routing_34_1.png
[25]:
connect_bundle: uid 240, ports [], aliases [], 0 polygons, 20 references
[26]:
import gdsfactory as gf

c = gf.Component("route_bend_5um")
c1 = c << gf.components.mmi2x2()
c2 = c << gf.components.mmi2x2()

c2.move((100, 50))
routes = gf.routing.get_bundle(
    [c1.ports["o4"], c1.ports["o3"]], [c2.ports["o1"], c2.ports["o2"]], radius=5
)
for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_35_0.png
[26]:
route_bend_5um: uid 351, ports [], aliases [], 0 polygons, 12 references
[27]:
import gdsfactory as gf

c = gf.Component("electrical")
c1 = c << gf.components.pad()
c2 = c << gf.components.pad()
c2.move((200, 100))
routes = gf.routing.get_bundle(
    [c1.ports["e3"]], [c2.ports["e1"]], cross_section=gf.cross_section.metal1
)
for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_36_0.png
[27]:
electrical: uid 362, ports [], aliases [], 0 polygons, 7 references
[28]:
c = gf.Component("get_bundle_with_ubends_bend_from_top")
pad_array = gf.components.pad_array()

c1 = c << pad_array
c2 = c << pad_array
c2.rotate(90)
c2.movex(1000)
c2.ymax = -200

routes_bend180 = gf.routing.get_routes_bend180(
    ports=c2.get_ports_list(),
    radius=75 / 2,
    cross_section=gf.cross_section.metal1,
    bend_port1="e1",
    bend_port2="e2",
)
c.add(routes_bend180.references)

routes = gf.routing.get_bundle(
    c1.get_ports_list(), routes_bend180.ports, cross_section=gf.cross_section.metal1
)
for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_37_0.png
[28]:
get_bundle_with_ubends_bend_from_top: uid 371, ports [], aliases [], 0 polygons, 26 references
[29]:
c = gf.Component("get_bundle_with_ubends_bend_from_bottom")
pad_array = gf.components.pad_array()

c1 = c << pad_array
c2 = c << pad_array
c2.rotate(90)
c2.movex(1000)
c2.ymax = -200

routes_bend180 = gf.routing.get_routes_bend180(
    ports=c2.get_ports_list(),
    radius=75 / 2,
    cross_section=gf.cross_section.metal1,
    bend_port1="e2",
    bend_port2="e1",
)
c.add(routes_bend180.references)

routes = gf.routing.get_bundle(
    c1.get_ports_list(), routes_bend180.ports, cross_section=gf.cross_section.metal1
)
for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_38_0.png
[29]:
get_bundle_with_ubends_bend_from_bottom: uid 392, ports [], aliases [], 0 polygons, 26 references

Problem

Sometimes 90 degrees routes do not have enough space for a Manhattan route

[30]:
import gdsfactory as gf

c = gf.Component("route_fail_1")
c1 = c << gf.components.nxn(east=3, ysize=20)
c2 = c << gf.components.nxn(west=3)
c2.move((80, 0))
c
../_images/notebooks_04_routing_40_0.png
[30]:
route_fail_1: uid 413, ports [], aliases [], 0 polygons, 2 references
[31]:
import gdsfactory as gf

c = gf.Component("route_fail_1")
c1 = c << gf.components.nxn(east=3, ysize=20)
c2 = c << gf.components.nxn(west=3)
c2.move((80, 0))
routes = gf.routing.get_bundle(
    c1.get_ports_list(orientation=0),
    c2.get_ports_list(orientation=180),
    auto_widen=False,
)
for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_41_0.png
[31]:
route_fail_1: uid 418, ports [], aliases [], 0 polygons, 21 references
[32]:
c = gf.Component("route_fail_2")
pitch = 2.0
ys_left = [0, 10, 20]
N = len(ys_left)
ys_right = [(i - N / 2) * pitch for i in range(N)]

right_ports = [gf.Port(f"R_{i}", (0, ys_right[i]), 0.5, 180) for i in range(N)]
left_ports = [gf.Port(f"L_{i}", (-50, ys_left[i]), 0.5, 0) for i in range(N)]
left_ports.reverse()
routes = gf.routing.get_bundle(right_ports, left_ports, radius=5)

for i, route in enumerate(routes):
    c.add(route.references)
c
../_images/notebooks_04_routing_42_0.png
[32]:
route_fail_2: uid 433, ports [], aliases [], 0 polygons, 19 references

Solution

Add Sbend routes using get_bundle_sbend

[33]:
import gdsfactory as gf

c = gf.Component("route_solution_1_get_bundle_sbend")
c1 = c << gf.components.nxn(east=3, ysize=20)
c2 = c << gf.components.nxn(west=3)
c2.move((80, 0))
routes = gf.routing.get_bundle_sbend(
    c1.get_ports_list(orientation=0), c2.get_ports_list(orientation=180)
)
c.add(routes.references)
c
/home/docs/checkouts/readthedocs.org/user_builds/gdsfactory/checkouts/latest/gdsfactory/components/bezier.py:110: RuntimeWarning: divide by zero encountered in double_scalars
  min_bend_radius = gf.snap.snap_to_grid(1 / max(np.abs(curv)))
../_images/notebooks_04_routing_44_1.png
[33]:
route_solution_1_get_bundle_sbend: uid 444, ports [], aliases [], 0 polygons, 5 references
[34]:
routes
[34]:
Routes(references=[DeviceReference (parent Device "bend_s_size72_0", ports ['o1', 'o2'], origin [8.   1.25], rotation 0, x_reflection False), DeviceReference (parent Device "bend_s_size72_m6", ports ['o1', 'o2'], origin [ 8. 10.], rotation 0, x_reflection False), DeviceReference (parent Device "bend_s_size72_m12", ports ['o1', 'o2'], origin [ 8.   18.75], rotation 0, x_reflection False)], lengths=[72.0, 72.354, 73.398], ports=None, bend_radius=[inf, 159.432, 83.82])
[35]:
c = gf.Component("route_solution_2_get_bundle_sbend")
route = gf.routing.get_bundle_sbend(right_ports, left_ports)
c.add(route.references)
../_images/notebooks_04_routing_46_0.png
[35]:
route_solution_2_get_bundle_sbend: uid 452, ports [], aliases [], 0 polygons, 3 references

get_bundle_from_waypoints

While get_bundle routes bundles of ports automatically, you can also use get_bundle_from_waypoints to manually specify the route waypoints.

You can think of get_bundle_from_waypoints as a manual version of get_bundle

[36]:
import numpy as np
import gdsfactory as gf


@gf.cell
def test_connect_bundle_waypoints():
    """Connect bundle of ports with bundle of routes following a list of waypoints."""
    ys1 = np.array([0, 5, 10, 15, 30, 40, 50, 60]) + 0.0
    ys2 = np.array([0, 10, 20, 30, 70, 90, 110, 120]) + 500.0
    N = ys1.size

    ports1 = [
        Port(name=f"A_{i}", midpoint=(0, ys1[i]), width=0.5, orientation=0)
        for i in range(N)
    ]
    ports2 = [
        Port(
            name=f"B_{i}",
            midpoint=(500, ys2[i]),
            width=0.5,
            orientation=180,
        )
        for i in range(N)
    ]

    p0 = ports1[0].position

    c = gf.Component("B")
    c.add_ports(ports1)
    c.add_ports(ports2)
    waypoints = [
        p0 + (200, 0),
        p0 + (200, -200),
        p0 + (400, -200),
        (p0[0] + 400, ports2[0].y),
    ]

    routes = gf.routing.get_bundle_from_waypoints(ports1, ports2, waypoints)
    lengths = {}
    for i, route in enumerate(routes):
        c.add(route.references)
        lengths[i] = route.length

    return c


cell = test_connect_bundle_waypoints()
cell
../_images/notebooks_04_routing_48_0.png
[36]:
test_connect_bundle_waypoints: uid 456, ports ['A_0', 'A_1', 'A_2', 'A_3', 'A_4', 'A_5', 'A_6', 'A_7', 'B_0', 'B_1', 'B_2', 'B_3', 'B_4', 'B_5', 'B_6', 'B_7'], aliases [], 0 polygons, 72 references
[37]:
import numpy as np
import gdsfactory as gf

c = gf.Component()
r = c << gf.c.array(component=gf.c.straight, rows=2, columns=1, spacing=(0, 20))

r.movex(60)
r.movey(40)

lt = c << gf.c.straight(length=15)
lb = c << gf.c.straight(length=5)
lt.movey(5)

ports1 = lt.get_ports_list(orientation=0) + lb.get_ports_list(orientation=0)
ports2 = r.get_ports_list(orientation=180)


dx = 20
p0 = ports1[0].midpoint + (dx, 0)
p1 = (ports1[0].midpoint[0] + dx, ports2[0].midpoint[1])
waypoints = (p0, p1)

routes = gf.routing.get_bundle_from_waypoints(ports1, ports2, waypoints=waypoints)
for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_49_0.png
[37]:
Unnamed_1c739cf1: uid 487, ports [], aliases [], 0 polygons, 13 references

get_bundle_path_length_match

Sometimes you need to set up a route a bundle of ports that need to keep the same lengths

[38]:
import gdsfactory as gf

c = gf.Component("path_length_match_sample")
dy = 2000.0
xs1 = [-500, -300, -100, -90, -80, -55, -35, 200, 210, 240, 500, 650]

pitch = 100.0
N = len(xs1)
xs2 = [-20 + i * pitch for i in range(N)]

a1 = 90
a2 = a1 + 180

ports1 = [gf.Port(f"top_{i}", (xs1[i], 0), 0.5, a1) for i in range(N)]
ports2 = [gf.Port(f"bottom_{i}", (xs2[i], dy), 0.5, a2) for i in range(N)]

routes = gf.routing.get_bundle_path_length_match(ports1, ports2)

for route in routes:
    c.add(route.references)
    print(route.length)
c
2660.794
2660.794
2660.794
2660.794
2660.794
2660.794
2660.794
2660.794
2660.794
2660.794
2660.794
2660.794
../_images/notebooks_04_routing_51_1.png
[38]:
path_length_match_sample: uid 499, ports [], aliases [], 0 polygons, 130 references

Add extra length

You can also add some extra length to all the routes

[39]:
import gdsfactory as gf

c = gf.Component("path_length_match_sample")

dy = 2000.0
xs1 = [-500, -300, -100, -90, -80, -55, -35, 200, 210, 240, 500, 650]

pitch = 100.0
N = len(xs1)
xs2 = [-20 + i * pitch for i in range(N)]

a1 = 90
a2 = a1 + 180

ports1 = [gf.Port(f"top_{i}", (xs1[i], 0), 0.5, a1) for i in range(N)]
ports2 = [gf.Port(f"bot_{i}", (xs2[i], dy), 0.5, a2) for i in range(N)]

routes = gf.routing.get_bundle_path_length_match(ports1, ports2, extra_length=44)
for route in routes:
    c.add(route.references)
    print(route.length)
c
2704.794
2704.794
2704.794
2704.794
2704.794
2704.794
2704.794
2704.794
2704.794
2704.794
2704.794
2704.794
../_images/notebooks_04_routing_53_1.png
[39]:
path_length_match_sample: uid 546, ports [], aliases [], 0 polygons, 132 references

increase number of loops

You can also increase the number of loops

[40]:
c = gf.Component("path_length_match_sample")

dy = 2000.0
xs1 = [-500, -300, -100, -90, -80, -55, -35, 200, 210, 240, 500, 650]

pitch = 200.0
N = len(xs1)
xs2 = [-20 + i * pitch for i in range(N)]

a1 = 90
a2 = a1 + 180

ports1 = [gf.Port(f"top_{i}", (xs1[i], 0), 0.5, a1) for i in range(N)]
ports2 = [gf.Port(f"bot_{i}", (xs2[i], dy), 0.5, a2) for i in range(N)]

routes = gf.routing.get_bundle_path_length_match(
    ports1, ports2, nb_loops=2, auto_widen=False
)
for route in routes:
    c.add(route.references)
    print(route.length)
c
3587.99
3587.99
3587.99
3587.99
3587.99
3587.99
3587.99
3587.99
3587.99
3587.99
3587.99
3587.99
../_images/notebooks_04_routing_55_1.png
[40]:
path_length_match_sample: uid 594, ports [], aliases [], 0 polygons, 200 references
[41]:
# Problem, sometimes when you do path length matching you need to increase the separation
import gdsfactory as gf

c = gf.Component()
c1 = c << gf.c.straight_array(spacing=90)
c2 = c << gf.c.straight_array(spacing=5)
c2.movex(200)
c1.y = 0
c2.y = 0

routes = gf.routing.get_bundle_path_length_match(
    c1.get_ports_list(orientation=0),
    c2.get_ports_list(orientation=180),
    end_straight_offset=0,
    start_straight=0,
    separation=30,
    radius=5,
)

for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_56_0.png
[41]:
Unnamed_b8aa8fd4: uid 640, ports [], aliases [], 0 polygons, 42 references
[42]:
# Solution: increase separation
import gdsfactory as gf

c = gf.Component()
c1 = c << gf.c.straight_array(spacing=90)
c2 = c << gf.c.straight_array(spacing=5)
c2.movex(200)
c1.y = 0
c2.y = 0

routes = gf.routing.get_bundle_path_length_match(
    c1.get_ports_list(orientation=0),
    c2.get_ports_list(orientation=180),
    end_straight_offset=0,
    start_straight=0,
    separation=80,  # increased
    radius=5,
)

for route in routes:
    c.add(route.references)
c
../_images/notebooks_04_routing_57_0.png
[42]:
Unnamed_c22a5c08: uid 654, ports [], aliases [], 0 polygons, 42 references

Route to IO (Pads, grating couplers …)

Route to electrical pads

[43]:
import gdsfactory as gf

mzi = gf.components.straight_heater_metal(length=30)
mzi
../_images/notebooks_04_routing_59_0.png
[43]:
straight_heater_metal_u_c4123e83: uid 673, ports ['o1', 'o2', 'e1', 'e2'], aliases [], 0 polygons, 3 references
[44]:
import gdsfactory as gf

mzi = gf.components.mzi_phase_shifter(
    length_x=30, straight_x_top=gf.c.straight_heater_metal_90_90
)
gf.routing.add_electrical_pads_top(component=mzi)
../_images/notebooks_04_routing_60_0.png
[44]:
add_electrical_pads_top_1afc9488: uid 708, ports ['o1', 'o2'], aliases [], 0 polygons, 4 references
[45]:
import gdsfactory as gf

hr = gf.components.straight_heater_metal()
cc = gf.routing.add_electrical_pads_shortest(component=hr)
cc
../_images/notebooks_04_routing_61_0.png
[45]:
add_electrical_pads_sho_d15955aa: uid 730, ports ['o1', 'o2'], aliases [], 0 polygons, 5 references
[46]:
# Problem: Sometimes the shortest path does not work well
import gdsfactory as gf

c = gf.components.mzi_phase_shifter(length_x=250)
cc = gf.routing.add_electrical_pads_shortest(component=c)
cc
../_images/notebooks_04_routing_62_0.png
[46]:
add_electrical_pads_sho_22a500a9: uid 761, ports ['o1', 'o2'], aliases [], 0 polygons, 5 references
[47]:
# Solution: you can use define the pads separate and route metal lines to them

c = gf.Component("mzi_with_pads")
c1 = c << gf.components.mzi_phase_shifter(length_x=70)
c2 = c << gf.components.pad_array(columns=2)

c2.ymin = c1.ymax + 20
c2.x = 0
c1.x = 0
c
../_images/notebooks_04_routing_63_0.png
[47]:
mzi_with_pads: uid 768, ports [], aliases [], 0 polygons, 2 references
[48]:
c = gf.Component("mzi_with_pads")
c1 = c << gf.components.mzi_phase_shifter(
    straight_x_top=gf.c.straight_heater_metal_90_90, length_x=70
)
c2 = c << gf.components.pad_array(columns=2)

c2.ymin = c1.ymax + 20
c2.x = 0
c1.x = 0

ports1 = c1.get_ports_list(width=11)
ports2 = c2.get_ports_list()

routes = gf.routing.get_bundle(
    ports1=ports1,
    ports2=ports2,
    cross_section=gf.cross_section.metal1,
    width=5,
    bend_factory=gf.c.wire_corner,
)
for route in routes:
    c.add(route.references)

c
../_images/notebooks_04_routing_64_0.png
[48]:
mzi_with_pads: uid 797, ports [], aliases [], 0 polygons, 12 references

Route to Fiber Array

Routing allows you to define routes to optical or electrical IO (grating couplers or electrical pads)

[49]:
import numpy as np
import gdsfactory as gf
from gdsfactory import LAYER
from gdsfactory import Port


@gf.cell
def big_device(w=400.0, h=400.0, N=16, port_pitch=15.0, layer=LAYER.WG, wg_width=0.5):
    """big component with N ports on each side"""
    component = gf.Component()
    p0 = np.array((0, 0))
    dx = w / 2
    dy = h / 2

    points = [[dx, dy], [dx, -dy], [-dx, -dy], [-dx, dy]]
    component.add_polygon(points, layer=layer)
    port_params = {"layer": layer, "width": wg_width}
    for i in range(N):
        port = Port(
            name="W{}".format(i),
            midpoint=p0 + (-dx, (i - N / 2) * port_pitch),
            orientation=180,
            **port_params,
        )
        component.add_port(port)

    for i in range(N):
        port = Port(
            name="E{}".format(i),
            midpoint=p0 + (dx, (i - N / 2) * port_pitch),
            orientation=0,
            **port_params,
        )
        component.add_port(port)

    for i in range(N):
        port = Port(
            name="N{}".format(i),
            midpoint=p0 + ((i - N / 2) * port_pitch, dy),
            orientation=90,
            **port_params,
        )
        component.add_port(port)

    for i in range(N):
        port = Port(
            name="S{}".format(i),
            midpoint=p0 + ((i - N / 2) * port_pitch, -dy),
            orientation=-90,
            **port_params,
        )
        component.add_port(port)
    return component


component = big_device(N=10)
c = gf.routing.add_fiber_array(component=component, radius=10.0, fanout_length=60.0)
c
../_images/notebooks_04_routing_66_0.png
[49]:
add_fiber_array_compone_13e6960e: uid 834, ports ['vertical_te_00', 'vertical_te_01', 'vertical_te_02', 'vertical_te_03', 'vertical_te_04', 'vertical_te_05', 'vertical_te_06', 'vertical_te_07', 'vertical_te_08', 'vertical_te_09', 'vertical_te_010', 'vertical_te_011', 'vertical_te_012', 'vertical_te_013', 'vertical_te_014', 'vertical_te_015', 'vertical_te_016', 'vertical_te_017', 'vertical_te_018', 'vertical_te_019', 'vertical_te_020', 'vertical_te_021', 'vertical_te_022', 'vertical_te_023', 'vertical_te_024', 'vertical_te_025', 'vertical_te_026', 'vertical_te_027', 'vertical_te_028', 'vertical_te_029', 'vertical_te_030', 'vertical_te_031', 'vertical_te_032', 'vertical_te_033', 'vertical_te_034', 'vertical_te_035', 'vertical_te_036', 'vertical_te_037', 'vertical_te_038', 'vertical_te_039'], aliases [], 0 polygons, 364 references
[50]:
import gdsfactory as gf

c = gf.components.ring_double(width=0.8)
cc = gf.routing.add_fiber_array(component=c, taper_length=150)
cc
../_images/notebooks_04_routing_67_0.png
[50]:
add_fiber_array_compone_cc13864a: uid 968, ports ['vertical_te_00', 'vertical_te_01', 'vertical_te_02', 'vertical_te_03'], aliases [], 0 polygons, 50 references
[51]:
cc.get_settings()
[51]:
{'function_name': 'add_fiber_array',
 'info': {},
 'module': 'gdsfactory.routing.add_fiber_array',
 'name': 'add_fiber_array_compone_cc13864a',
 'settings': {'contains': {'function_name': 'ring_double',
   'info': {},
   'module': 'gdsfactory.components.ring_double',
   'name': 'ring_double_width800n',
   'settings': {'gap': 0.2,
    'radius': 10,
    'length_x': 0.01,
    'length_y': 0.01,
    'bend': None,
    'width': 0.8}},
  'gc_port_name': 'o1',
  'gc_port_labels': None,
  'component_name': None,
  'layer_label': [66, 0],
  'taper_length': 150}}
[52]:
cc.pprint
{'function_name': 'add_fiber_array',
 'info': {},
 'module': 'gdsfactory.routing.add_fiber_array',
 'name': 'add_fiber_array_compone_cc13864a',
 'settings': {'component_name': None,
              'contains': {'function_name': 'ring_double',
                           'info': {},
                           'module': 'gdsfactory.components.ring_double',
                           'name': 'ring_double_width800n',
                           'settings': {'bend': None,
                                        'gap': 0.2,
                                        'length_x': 0.01,
                                        'length_y': 0.01,
                                        'radius': 10,
                                        'width': 0.8}},
              'gc_port_labels': None,
              'gc_port_name': 'o1',
              'layer_label': [66, 0],
              'taper_length': 150}}

You can also mix and match TE and TM grating couplers

[53]:
c = gf.components.mzi_phase_shifter()
gcte = gf.components.grating_coupler_te
gctm = gf.components.grating_coupler_tm

cc = gf.routing.add_fiber_array(
    component=c,
    optical_routing_type=2,
    grating_coupler=[gctm, gcte, gctm, gcte],
    radius=20,
)
cc
../_images/notebooks_04_routing_71_0.png
[53]:
add_fiber_array_compone_b335dd31: uid 1018, ports ['e1', 'e2', 'vertical_tm_00', 'vertical_te_01'], aliases [], 0 polygons, 32 references

Route to fiber single

[54]:
import gdsfactory as gf

c = gf.components.ring_single()
cc = gf.routing.add_fiber_single(component=c)
cc
../_images/notebooks_04_routing_73_0.png
[54]:
add_fiber_single_compon_234e7bbe: uid 1045, ports ['vertical_te_00', 'vertical_te_1'], aliases [], 0 polygons, 8 references
[55]:
import gdsfactory as gf

c = gf.components.ring_single()
cc = gf.routing.add_fiber_single(component=c, with_loopback=False)
cc
../_images/notebooks_04_routing_74_0.png
[55]:
add_fiber_single_compon_dcd0488d: uid 1066, ports ['vertical_te_00', 'vertical_te_1'], aliases [], 0 polygons, 5 references
[56]:
c = gf.components.mmi2x2()
cc = gf.routing.add_fiber_single(component=c, with_loopback=False)
cc
../_images/notebooks_04_routing_75_0.png
[56]:
add_fiber_single_compon_21e727e0: uid 1081, ports ['vertical_te_00', 'vertical_te_01', 'vertical_te_1', 'vertical_te_2'], aliases [], 0 polygons, 25 references
[57]:
c = gf.components.mmi1x2()
cc = gf.routing.add_fiber_single(component=c, with_loopback=False, fiber_spacing=150)
cc
../_images/notebooks_04_routing_76_0.png
[57]:
add_fiber_single_compon_c798bac4: uid 1098, ports ['vertical_te_00', 'vertical_te_1', 'vertical_te_2'], aliases [], 0 polygons, 15 references
[58]:
c = gf.components.mmi1x2()
cc = gf.routing.add_fiber_single(component=c, with_loopback=False, fiber_spacing=50)
cc
../_images/notebooks_04_routing_77_0.png
[58]:
add_fiber_single_compon_0f7b69e6: uid 1116, ports ['vertical_te_00', 'vertical_te_1', 'vertical_te_2'], aliases [], 0 polygons, 15 references
[59]:
c = gf.components.crossing()
cc = gf.routing.add_fiber_single(component=c, with_loopback=False)
cc
../_images/notebooks_04_routing_78_0.png
[59]:
add_fiber_single_compon_ed172673: uid 1134, ports ['vertical_te_00', 'vertical_te_1', 'vertical_te_2', 'vertical_te_3'], aliases [], 0 polygons, 23 references
[60]:
c = gf.components.cross(length=200, width=2)
cc = gf.routing.add_fiber_single(component=c, with_loopback=False)
cc
../_images/notebooks_04_routing_79_0.png
[60]:
add_fiber_single_compon_59c5c432: uid 1155, ports ['vertical_te_00', 'vertical_te_1', 'vertical_te_2', 'vertical_te_3'], aliases [], 0 polygons, 23 references
[61]:
c = gf.components.spiral()
cc = gf.routing.add_fiber_single(component=c, with_loopback=False)
cc
../_images/notebooks_04_routing_80_0.png
[61]:
add_fiber_single_compon_9b2923c1: uid 1176, ports ['vertical_te_0', 'vertical_te_1'], aliases [], 0 polygons, 3 references
[ ]: