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 doroutes as dr
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.dmove((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 'DoPlayDo' found.
Corners Bundle Route
Same array layout, now using [add_bundle_manual][doroutes.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][doroutes.add_bundle_manual] with steps= is the relative-coordinate sibling of
[add_bundle_manual][doroutes.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.