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)