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)
Steps Bundle Route
add_bundle_manual with steps= is the relative-coordinate sibling of
add_bundle_manual with corners= — the shape is given as relative steps
(dx/dy) anchored at the bundle exit (post-fan-in convergence
point). Use this form when you want a recipe that doesn't depend on
absolute coordinates — handy if you reuse the same routing pattern
across different placements.
The first and last steps may be diagonal (both dx and dy); the
router splits the diagonal into an axis-aligned L by going parallel
to the port direction first. Below the first step is {dx: 200, dy:
-40} — the diagonal gets split into a 40 µm south leg followed by a
200 µm east leg, putting the bundle on the y=350 rail without
requiring an explicit south corner.
Same fan_in={"type": "manhattan", "start_straight": 30.0} is
forwarded so the fan-in clears the pads on both ends.
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,
steps=[
# diagonal first step: 200 µm east + 40 µm south of the bundle exit
# (the diagonal is split into south-then-east by the router)
{"dx": 200, "dy": -40},
{"dy": -200}, # south leg at x=400
{"dx": -200}, # west leg at y=150 back to x=200
],
spacing=20,
straight=partial(gf.c.straight, cross_section="metal_routing"),
bend=partial(gf.c.wire_corner, cross_section="metal_routing"),
fan_in={"type": "manhattan", "start_straight": 30.0},
)
dr.util.show_cell(c)
Next steps
- For more bundle strategies (s-bend, l-bend) and a side-by-side comparison, see Bundle Routing Comparison.
- The single-wire versions of these routers are documented in Corners Routing and Steps Routing.
- Need obstacle avoidance without specifying the shape? See A* Bundle Routing.