Skip to content

Array Routing

This notebook shows how to wire arrayed instances — add_ref(rows=N, columns=M, ...) placements that show up whenever you have grids of pads, probe arrays, or fan-outs. The routing APIs are the same ones covered in the single-routing and bundle-routing tutorials; the unique piece here is the port indexing that lets you address individual elements of an array.

gdsfactory.Component.add_ref(columns=N, rows=M, ...) places a component as a 2-D array. Ports on individual elements are addressed with instance.ports[port_name, col, row] (kfactory's array port indexing). Promoting those array ports onto the parent component lets the standard bundle routers consume them as flat lists.

We use the same two-row pad layout to demonstrate three bundle routing approaches in turn — A*, corners, and steps — so you can see how to feed array-indexed ports into each.

Imports

from functools import partial

import gdsfactory as gf
from gdsfactory.gpdk import PDK

import gdsfactoryplus as gfp
dr = gfp.routing.doroutes

PDK.activate()

Two Rows of Pads

Two arrays of three pads, separated vertically by 500 µm. The top row exposes its south-facing ports as e1..e3; the bottom row exposes its north-facing ports as e4..e6. Promoting the array ports onto the parent component lets the bundle routers consume them as flat lists.

def pad_arrays() -> gf.Component:
    """Two rows of 3 pads, 200 µm pitch, 500 µm vertical separation."""
    c = gf.Component()
    pad = PDK.get_component("pad")

    top = c.add_ref(pad, name="top", columns=3, rows=1, column_pitch=200, row_pitch=0)
    top.move((0, 500))
    bot = c.add_ref(pad, name="bot", columns=3, rows=1, column_pitch=200, row_pitch=0)

    # Promote each array element's port onto the parent component using
    # gdsfactory's array-port indexing: ports[name, col, row].
    for i in range(3):
        c.add_port(f"e{i + 1}", port=top.ports["e4", i, 0])  # top row, south face
        c.add_port(f"e{i + 4}", port=bot.ports["e2", i, 0])  # bot row, north face

    return c

A* Bundle Route

We let add_bundle_astar find a path that avoids the pad layer. For how the algorithm picks routes (and how grid_unit and layers control the search), see A* Bundle Routing.

Here we route on the metal-routing cross-section using wire_corner for the bends — the natural choice for electrical traces, where bend losses aren't a concern and a sharp corner is the most compact turn possible.

wire_corner has a hard 90° body, and on a 200 µm pad pitch a corner landing right at the pad face would clip into the neighbouring pads. We push the comb turn out with fan_in={"type": "manhattan", "start_straight": 30.0} (30 µm of extra leading straight per wire, applied to both fan-ins) so the bends sit clear of the pad array.

c = pad_arrays()
ports1 = [p for p in c.ports if p.name in ["e1", "e2", "e3"]]
ports2 = [p for p in c.ports if p.name in ["e4", "e5", "e6"]]

dr.add_bundle_astar(
    component=c,
    ports1=ports1,
    ports2=ports2,
    straight=partial(PDK.get_component, "straight", cross_section="metal_routing"),
    bend=partial(PDK.get_component, "wire_corner", cross_section="metal_routing"),
    layers=[(49, 0)],  # pad metal layer — treated as an obstacle by A*
    grid_unit=2500,
    spacing=20,
    fan_in={"type": "manhattan", "start_straight": 30.0},
)

dr.util.show_cell(c)
API key for organization 'GDSFactory' found.

Corners Bundle Route

Same array layout, now using add_bundle_manual to specify the exact path through four absolute corner points. For the full corners / steps API and when to choose each, see Bundle From Corners.

The four corners below produce a Z-shaped detour around the center of the layout: south leg from the top pads, east leg at y=350, south leg at x=400, west leg at y=150, then a final south leg into the bottom pads.

We pass fan_in={"type": "manhattan", "start_straight": 30.0} for the same reason as the A* route above — a 30 µm leading straight on each wire keeps the comb turn clear of the pad faces.

c = pad_arrays()
ports1 = [p for p in c.ports if p.name in ["e1", "e2", "e3"]]
ports2 = [p for p in c.ports if p.name in ["e4", "e5", "e6"]]

dr.add_bundle_manual(
    component=c,
    ports1=ports1,
    ports2=ports2,
    corners=[
        (200, 350),  # turn east at y=350
        (400, 350),  # turn south at x=400
        (400, 150),  # turn west at y=150
        (200, 150),  # turn south at x=200
    ],
    spacing=20,
    straight=partial(gf.c.straight, cross_section="metal_routing"),
    bend=partial(gf.c.wire_corner),
    fan_in={"type": "manhattan", "start_straight": 30.0},
)

dr.util.show_cell(c)