Routing#

Optical and high speed RF ports have an orientation that routes need to follow to avoid sharp turns that produce reflections.

we have routing functions that route:

  • single route between 2 ports

    • get_route

    • get_route_from_steps

  • group of routes between 2 groups of ports using a river/bundle/bus router. At the moment it works only when all ports on each group have the same orientation.

    • get_bundle

    • get_bundle_from_steps

The most useful function is get_bundle which supports both single and groups of routes, and can also route with length matching, which ensures that all routes have the same length.

The biggest limitation is that it requires to have all the ports with the same orientation, for that you can use gf.routing.route_ports_to_side

from functools import partial

import gdsfactory as gf
from gdsfactory.cell import cell
from gdsfactory.component import Component
from gdsfactory.port import Port
c = gf.Component("sample_no_routes")
mmi1 = c << gf.components.mmi1x2()
mmi2 = c << gf.components.mmi1x2()
mmi2.move((100, 50))
c.plot()
2024-04-17 21:36:14.206 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/sample_no_routes.lyp'.
../_images/5f6e4ba0e4ba7da0799eb9af219c0c76be35654e43cdda6d971175169494b7cb.png

get_route#

get_route returns a Manhattan route between 2 ports

help(gf.routing.get_route)
Help on function get_route in module gdsfactory.routing.get_route:

get_route(input_port: 'Port', output_port: 'Port', bend: 'ComponentSpec' = <function bend_euler at 0x7f20a0a55870>, with_sbend: 'bool' = False, straight: 'ComponentSpec' = <function straight at 0x7f20a0a55090>, taper: 'ComponentSpec | None' = None, start_straight_length: 'float | None' = None, end_straight_length: 'float | None' = None, min_straight_length: 'float' = 0.01, auto_widen: 'bool' = False, auto_widen_minimum_length: 'float' = 100, taper_length: 'float' = 10, width_wide: 'float' = 2, cross_section: 'None | CrossSectionSpec | MultiCrossSectionAngleSpec' = 'xs_sc', **kwargs) -> 'Route'
    Returns a Manhattan Route between 2 ports.
    
    The references are straights, bends and tapers.
    `get_route` is an automatic version of `get_route_from_steps`.
    
    Args:
        input_port: start port.
        output_port: end port.
        bend: bend spec.
        with_sbend: add sbend in case there are routing errors.
        straight: straight spec.
        taper: taper spec.
        start_straight_length: length of starting straight.
        end_straight_length: length of end straight.
        min_straight_length: min length of straight for any intermediate segment.
        auto_widen: auto widen the straights.
        auto_widen_minimum_length: minimum length to auto widen.
        taper_length: length of taper.
        width_wide: width of the wider straight.
        cross_section: spec.
        kwargs: cross_section settings.
    
    
    .. plot::
        :include-source:
    
        import gdsfactory as gf
    
        c = gf.Component('sample_connect')
        mmi1 = c << gf.components.mmi1x2()
        mmi2 = c << gf.components.mmi1x2()
        mmi2.move((40, 20))
        route = gf.routing.get_route(mmi1.ports["o2"], mmi2.ports["o1"], radius=5)
        c.add(route.references)
        c.plot()
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.plot()
2024-04-17 21:36:14.435 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/sample_connect.lyp'.
../_images/a44b91e87d08d4e68a1fc4ad64d29b77a4f465738608a3c8f4e3c65551b46120.png
route
Route(references=[ComponentReference (parent Component "bend_euler_a459ac5d", ports ['o1', 'o2'], origin (69.99, 0.625), rotation 0.0, x_reflection False), ComponentReference (parent Component "bend_euler_a459ac5d", ports ['o1', 'o2'], origin (79.99, 40.0), rotation 90.0, x_reflection True), ComponentReference (parent Component "straight_8ab6b107", ports ['o1', 'o2'], origin (15.5, 0.625), rotation 0.0, x_reflection False), ComponentReference (parent Component "straight_aeae8524", ports ['o1', 'o2'], origin (79.99, 10.625), rotation 90.0, x_reflection False), ComponentReference (parent Component "straight_b69bcf2d", ports ['o1', 'o2'], origin (89.99, 50.0), rotation 0.0, x_reflection False)], labels=[], ports=({'name': 'o1', 'width': 0.5, 'center': [15.5, 0.625], 'orientation': 180.0, 'layer': [1, 0], 'port_type': 'optical'}, {'name': 'o2', 'width': 0.5, 'center': [90.0, 50.0], 'orientation': 0.0, 'layer': [1, 0], 'port_type': 'optical'}), length=117.149)

Problem: get_route with obstacles

sometimes there are obstacles that connect strip does not see!

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.plot()
2024-04-17 21:36:14.601 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/sample_problem.lyp'.
../_images/c90b2e2f9e1affa28be3613441d4c8fbebf4c358941f89d8c4ca3aab985d374a.png

Solutions:

  • specify the route steps

get_route_from_steps#

get_route_from_steps is a manual version of get_route where you can define only the new steps x or y together with increments dx or dy

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.plot()
2024-04-17 21:36:14.772 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/get_route_from_steps.lyp'.
../_images/f8f2a104b62f1a624acf9bc54c056e0cea10ef8773b08a7558b34d0a89d30f7c.png
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.plot()
2024-04-17 21:36:14.986 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/get_route_from_steps_shorter_syntax.lyp'.
../_images/f8f2a104b62f1a624acf9bc54c056e0cea10ef8773b08a7558b34d0a89d30f7c.png

You can also reduce the waveguide loss by tapering to wider waveguides for long routes.

c = gf.Component("sample_connect")
mmi1 = c << gf.components.mmi1x2()
mmi2 = c << gf.components.mmi1x2()
mmi2.move((200, 50))


route = gf.routing.get_route(
    mmi1.ports["o3"],
    mmi2.ports["o1"],
    auto_widen=True,
    width_wide=2,
    auto_widen_minimum_length=40,
    radius=30,
    cross_section=gf.cross_section.strip,
)
c.add(route.references)
c.plot()
2024-04-17 21:36:15.155 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/sample_connect$1.lyp'.
../_images/ff9b3b2c6bb8ccbb43b2ef748586f0c70bcbee8e0b1142fb5eb21f7a1a8734d6.png

get_bundle#

To route groups of ports avoiding waveguide collisions, you should use get_bundle instead of get_route.

get_bundle uses a river/bundle/bus router.

At the moment it works only when each group of ports have the same orientation.

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)]
layer = (1, 0)

right_ports = [
    gf.Port(f"R_{i}", center=(0, ys_right[i]), width=0.5, orientation=180, layer=layer)
    for i in range(N)
]
left_ports = [
    gf.Port(f"L_{i}", center=(-200, ys_left[i]), width=0.5, orientation=0, layer=layer)
    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_bundle_v2")
routes = gf.routing.get_bundle(
    left_ports,
    right_ports,
    sort_ports=True,
    start_straight_length=100,
    enforce_port_ordering=False,
)
for route in routes:
    c.add(route.references)
c.plot()
2024-04-17 21:36:15.368 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/connect_bundle_v2.lyp'.
../_images/8be6bd7ee93b8f73518c6da79db183fbfecddb72a54acb446be4c1a1a70bf13b.png
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)]
layer = (1, 0)

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

bot_ports = [
    gf.Port(
        f"bot_{i}",
        center=(xs_bottom[i], -300),
        width=0.5,
        orientation=90,
        layer=layer,
    )
    for i in range(N)
]

c = gf.Component(name="connect_bundle_separation")
routes = gf.routing.get_bundle(
    top_ports, bot_ports, separation=5.0, end_straight_length=100
)
for route in routes:
    c.add(route.references)

c.plot()
2024-04-17 21:36:15.565 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/connect_bundle_separation.lyp'.
../_images/e4cf18fc92ba0e52c66080fc399a21911e7bc53443e6b6ee245fef64fdbb0f49.png

get_bundle can also route bundles through corners

@cell
def test_connect_corner(N=6, config="A"):
    d = 10.0
    sep = 5.0
    top_cell = gf.Component()
    layer = (1, 0)

    if config in ["A", "B"]:
        a = 100.0
        ports_A_TR = [
            Port(
                f"A_TR_{i}",
                center=(d, a / 2 + i * sep),
                width=0.5,
                orientation=0,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_A_TL = [
            Port(
                f"A_TL_{i}",
                center=(-d, a / 2 + i * sep),
                width=0.5,
                orientation=180,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_A_BR = [
            Port(
                f"A_BR_{i}",
                center=(d, -a / 2 - i * sep),
                width=0.5,
                orientation=0,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_A_BL = [
            Port(
                f"A_BL_{i}",
                center=(-d, -a / 2 - i * sep),
                width=0.5,
                orientation=180,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_A = [ports_A_TR, ports_A_TL, ports_A_BR, ports_A_BL]

        ports_B_TR = [
            Port(
                f"B_TR_{i}",
                center=(a / 2 + i * sep, d),
                width=0.5,
                orientation=90,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_B_TL = [
            Port(
                f"B_TL_{i}",
                center=(-a / 2 - i * sep, d),
                width=0.5,
                orientation=90,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_B_BR = [
            Port(
                f"B_BR_{i}",
                center=(a / 2 + i * sep, -d),
                width=0.5,
                orientation=270,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_B_BL = [
            Port(
                f"B_BL_{i}",
                center=(-a / 2 - i * sep, -d),
                width=0.5,
                orientation=270,
                layer=layer,
            )
            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(
                f"A_TR_{i}",
                center=(a, d + i * sep),
                width=0.5,
                orientation=0,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_A_TL = [
            Port(
                f"A_TL_{i}",
                center=(-a, d + i * sep),
                width=0.5,
                orientation=180,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_A_BR = [
            Port(
                f"A_BR_{i}",
                center=(a, -d - i * sep),
                width=0.5,
                orientation=0,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_A_BL = [
            Port(
                f"A_BL_{i}",
                center=(-a, -d - i * sep),
                width=0.5,
                orientation=180,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_A = [ports_A_TR, ports_A_TL, ports_A_BR, ports_A_BL]

        ports_B_TR = [
            Port(
                f"B_TR_{i}",
                center=(d + i * sep, a),
                width=0.5,
                orientation=90,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_B_TL = [
            Port(
                f"B_TL_{i}",
                center=(-d - i * sep, a),
                width=0.5,
                orientation=90,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_B_BR = [
            Port(
                f"B_BR_{i}",
                center=(d + i * sep, -a),
                width=0.5,
                orientation=270,
                layer=layer,
            )
            for i in range(N)
        ]

        ports_B_BL = [
            Port(
                f"B_BL_{i}",
                center=(-d - i * sep, -a),
                width=0.5,
                orientation=270,
                layer=layer,
            )
            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.plot()
2024-04-17 21:36:15.843 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/test_connect_corner.lyp'.
../_images/7e5ad913110f0a983fe0cc71920ae3bd346d82b33ef2b532c88b5e19a83dd994.png
c = test_connect_corner(config="C")
c.plot()
2024-04-17 21:36:16.221 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/test_connect_corner_configC.lyp'.
../_images/8bcc8c19798b6c8e25809972c213b65ebcb226a420df59fd7eb450cdf79ccea7.png
@cell
def test_connect_bundle_udirect(dy=200, orientation=270, layer=(1, 0)):
    xs1 = [-100, -90, -80, -55, -35, 24, 0] + [200, 210, 240]
    axis = "X" if orientation 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}",
                center=(0, xs1[i]),
                width=0.5,
                orientation=orientation,
                layer=layer,
            )
            for i in range(N)
        ]

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

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

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

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

    return top_cell


c = test_connect_bundle_udirect()
c.plot()
2024-04-17 21:36:16.513 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/test_connect_bundle_udirect.lyp'.
../_images/9b2f28bd625dc0fe9a695a2ec320dd49063972eca4c0e58f478ab52290d9b205.png
@cell
def test_connect_bundle_u_indirect(dy=-200, orientation=180, layer=(1, 0)):
    xs1 = [-100, -90, -80, -55, -35] + [200, 210, 240]
    axis = "X" if orientation in [0, 180] else "Y"
    pitch = 10.0
    N = len(xs1)
    xs2 = [50 + i * pitch for i in range(N)]

    a1 = orientation
    a2 = a1 + 180

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

        ports2 = [
            Port(
                f"bot_{i}",
                center=(dy, xs2[i]),
                width=0.5,
                orientation=a2,
                layer=layer,
            )
            for i in range(N)
        ]

    else:
        ports1 = [
            Port(f"top_{i}", center=(xs1[i], 0), width=0.5, orientation=a1, layer=layer)
            for i in range(N)
        ]

        ports2 = [
            Port(
                f"bot_{i}",
                center=(xs2[i], dy),
                width=0.5,
                orientation=a2,
                layer=layer,
            )
            for i in range(N)
        ]

    top_cell = Component()
    routes = gf.routing.get_bundle(
        ports1,
        ports2,
        bend=gf.components.bend_euler,
        radius=5,
        enforce_port_ordering=False,
    )
    for route in routes:
        top_cell.add(route.references)

    return top_cell


c = test_connect_bundle_u_indirect(orientation=0)
c.plot()
2024-04-17 21:36:16.928 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/test_connect_bundle_u_indirect_orientation0.lyp'.
../_images/b21d1035871ca684c8a3f3439c8d6b49385690617ca4b13ef831ef93b2281273.png
@gf.cell
def test_north_to_south(layer=(1, 0)):
    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(f"top_{i}", center=(xs1[i], 0), width=0.5, orientation=a1, layer=layer)
        for i in range(N)
    ]

    ports2 = [
        gf.Port(f"bot_{i}", center=(xs2[i], dy), width=0.5, orientation=a2, layer=layer)
        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.plot()
2024-04-17 21:36:17.226 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/test_north_to_south.lyp'.
../_images/7bd94dcf2f236c719455dc539d040a2c506656dd6767eeac92245df6f4c0112a.png
@gf.cell
def demo_connect_bundle():
    """combines all the connect_bundle tests"""
    y = 400.0
    x = 500
    y0 = 900
    dy = 200.0
    c = gf.Component()
    for j, s in enumerate([-1, 1]):
        for i, orientation in enumerate([0, 90, 180, 270]):
            ci = test_connect_bundle_u_indirect(dy=s * dy, orientation=orientation)
            ref = ci.ref(position=(i * x, j * y))
            c.add(ref)

            ci = test_connect_bundle_udirect(dy=s * dy, orientation=orientation)
            ref = ci.ref(position=(i * x, j * y + y0))
            c.add(ref)

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

    return c


c = demo_connect_bundle()
c.plot()
2024-04-17 21:36:18.613 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/demo_connect_bundle.lyp'.
../_images/831ec5cf5c70f96374ac343be09a4c6a8a6f1978e8b763cfc33911c20c2ae1f0.png
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.plot()
2024-04-17 21:36:18.828 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/route_bend_5um.lyp'.
../_images/03be5e0da26527e83dfcfa51805658c071f438b6a62b93e643e00207b70a5f33.png
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.plot()
2024-04-17 21:36:19.028 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/electrical.lyp'.
../_images/c7b68d2422f55900ab52eb1f0656dbbba76fc49a72a69396c10a23e94496e992.png

Problem

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

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.plot()
2024-04-17 21:36:19.215 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/route_fail_1.lyp'.
../_images/88624e9f41e9d4f0f8dd5def4e2c8fd4c826570713bda72cb5a38a19bdcb572a.png
c = gf.Component("route_fail_v2")
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.plot()
2024-04-17 21:36:19.443 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/route_fail_v2.lyp'.
../_images/ffcac37d0e909f0520871e41ef97f43ad3a4dddb1a5fb8c05c492727c3d27a2e.png
c = gf.Component("route_fail_v3")
pitch = 2.0
ys_left = [0, 10, 20]
N = len(ys_left)
ys_right = [(i - N / 2) * pitch for i in range(N)]
layer = (1, 0)

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

for route in routes:
    c.add(route.references)
c.plot()
2024-04-17 21:36:19.738 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/route_fail_v3.lyp'.
../_images/3c309c5c0d28e1af6cc0343edba552f2059f297dc615df498736f8537558ed9f.png

Solution

Add Sbend routes using get_bundle_sbend

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),
    enforce_port_ordering=False,
)
for route in routes:
    c.add(route.references)
c.plot()
2024-04-17 21:36:19.941 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/route_solution_1_get_bundle_sbend.lyp'.
../_images/acbc3a99b9f440033f20e47fcf110445cfb9a483f1bdbf0dd6709fc867d52b22.png

You can also get_bundle adding with_sbend=True

c = gf.Component("route_solution_2_get_bundle")
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),
    with_sbend=True,
    enforce_port_ordering=False,
)
for route in routes:
    c.add(route.references)
c.plot()
2024-04-17 21:36:20.146 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/route_solution_2_get_bundle.lyp'.
../_images/acbc3a99b9f440033f20e47fcf110445cfb9a483f1bdbf0dd6709fc867d52b22.png

get_bundle with path_length_match#

Sometimes you need to route two groups of ports keeping the same route lengths.

c = gf.Component("path_length_match_routing")
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
layer = (1, 0)

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

routes = gf.routing.get_bundle(
    ports1,
    ports2,
    path_length_match_loops=1,
    path_length_match_modify_segment_i=-2,
    end_straight_length=800,
)

for route in routes:
    c.add(route.references)
    print(route.length)
c.plot()
2659.822
2659.822
2659.822
2659.822
2659.822
2659.822
2659.822
2659.822
2659.822
2659.822
2659.822
2659.822
2024-04-17 21:36:20.555 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/path_length_match_routing.lyp'.
../_images/e70de04e45a8da255ad2cb6670847898a799221b6685adbaa9c9432589fa57f1.png

path_length_match with extra length#

You can also add some extra length to all the routes

c = gf.Component("get_bundle_path_length_match_extra_length")

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
layer = (1, 0)

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

routes = gf.routing.get_bundle(
    ports1,
    ports2,
    path_length_match_extra_length=44,
    path_length_match_loops=2,
    end_straight_length=800,
)
for route in routes:
    c.add(route.references)
    print(route.length)
c.plot()
2862.37
2862.37
2862.37
2862.37
2862.37
2862.37
2862.37
2862.37
2862.37
2862.37
2862.37
2862.37
2024-04-17 21:36:20.957 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/get_bundle_path_length_match_extra_length.lyp'.
../_images/f272ca8f4f0a324e204637e899606581d4387a0bf8f4592882daba4d9852fb75.png

path length match with extra loops#

You can also increase the number of loops

c = gf.Component("get_route_path_length_match_nb_loops")

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
layer = (1, 0)

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

routes = gf.routing.get_bundle(
    ports1,
    ports2,
    path_length_match_loops=2,
    auto_widen=False,
    end_straight_length=800,
    separation=30,
)
for route in routes:
    c.add(route.references)
    print(route.length)
c.plot()
3586.37
3586.37
3586.37
3586.37
3586.37
3586.37
3586.37
3586.37
3586.37
3586.37
3586.37
3586.37
2024-04-17 21:36:21.413 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/get_route_path_length_match_nb_loops.lyp'.
../_images/792ee764f5b5cebb3d6338f94a248fa4db57ef7ced73d927563d995e8dd14b34.png

Sometimes you need to modify separation to ensure waveguides don’t overlap.

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

routes = gf.routing.get_bundle(
    c1.get_ports_list(orientation=0),
    c2.get_ports_list(orientation=180),
    end_straight_length=0,
    start_straight_length=0,
    separation=30,  # not enough
    radius=5,
    path_length_match_loops=1,
    enforce_port_ordering=False,
)

for route in routes:
    c.add(route.references)
c.plot()
2024-04-17 21:36:21.660 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/problem_path_length_match.lyp'.
../_images/bdeb5d4d127c806f2da0019ff0de1f21565a2568dbe5c2678bea24831a10ab5a.png
c = gf.Component("solution_path_length_match")
c1 = c << gf.components.straight_array(spacing=90)
c2 = c << gf.components.straight_array(spacing=5)
c2.movex(200)
c1.y = 0
c2.y = 0

routes = gf.routing.get_bundle(
    c1.get_ports_list(orientation=0),
    c2.get_ports_list(orientation=180),
    end_straight_length=0,
    start_straight_length=0,
    separation=80,  # increased
    path_length_match_loops=1,
    radius=5,
    enforce_port_ordering=False,
)

for route in routes:
    c.add(route.references)
c.plot()
2024-04-17 21:36:21.891 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/solution_path_length_match.lyp'.
../_images/767a6d1570bdeb655bacde3809145defc1f867c2659bc3000b9f800964210890.png

get bundle with different orientation ports#

When trying to route ports with different orientations you need to bring them to a common x or y

  1. Use route_ports_to_side to bring all the ports to a common angle orientation and x or y.

  2. Use get_bundle to connect to the other group of ports.

from gdsfactory.samples.big_device import big_device

c = gf.Component("sample_route")
c1 = c << big_device()
c2 = c << gf.components.grating_coupler_array(n=len(c1.ports), rotation=-90)

routes, ports = gf.routing.route_ports_to_side(c1.ports, side="south")
for route in routes:
    c.add(route.references)

c2.ymin = -600
c2.x = 0

routes = gf.routing.get_bundle(ports, c2.ports)
for route in routes:
    c.add(route.references)

c.plot()
2024-04-17 21:36:23.206 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/sample_route.lyp'.
../_images/2c5bbd920f25c4c18f5a1ef7ef297044eb629bc56cee8b7245ebfbb8a0d8600f.png

get_bundle_from_steps#

This is a manual version of get_bundle that is more convenient than defining the waypoints.

c = gf.Component("get_route_from_steps_sample")
w = gf.components.array(
    partial(gf.components.straight, layer=(2, 0)),
    rows=3,
    columns=1,
    spacing=(0, 50),
)

left = c << w
right = c << w
right.move((200, 100))
p1 = left.get_ports_list(orientation=0)
p2 = right.get_ports_list(orientation=180)

routes = gf.routing.get_bundle_from_steps(
    p1,
    p2,
    steps=[{"x": 150}],
)

for route in routes:
    c.add(route.references)

c.plot()
2024-04-17 21:36:23.385 | INFO     | gdsfactory.technology.layer_views:to_lyp:1018 - LayerViews written to '/tmp/gdsfactory/get_route_from_steps_sample.lyp'.
../_images/92dc5c4954b9b9d5aa8eb1a170cdd8e96f6a32aca5c5f30d97cdc2435231c56a.png