parametric.route_auto
route_auto( *, port1: tuple[photonforge.Reference, str] | tuple[photonforge.Reference, str, int] | collections.abc.Sequence[ tuple[photonforge.Reference, str] | tuple[photonforge.Reference, str, int] ] | None = None, port2: tuple[photonforge.Reference, str] | tuple[photonforge.Reference, str, int] | collections.abc.Sequence[ tuple[photonforge.Reference, str] | tuple[photonforge.Reference, str, int] ] | None = None, radius: Annotated[float, exclusiveMinimum=0, units='μm'] | None = None, obstacles: collections.abc.Sequence[ photonforge.Rectangle | photonforge.Circle | photonforge.Polygon | photonforge.Path | photonforge.Component | photonforge.Reference ] | photonforge.Component | photonforge.Reference | None = None, include_port_references_as_obstacles: bool | None = None, collision_layers: collections.abc.Sequence[ Annotated[str | tuple[int, int], _] ] | None = None, collision_offset: Annotated[float, minimum=0, units='μm'] | None = None, straight_kwargs: Annotated[dict[str, Any], _] | None = None, s_bend_kwargs: Annotated[dict[str, Any], _] | None = None, bend: photonforge.Component | None = None, bend45: photonforge.Component | None = None, cross: photonforge.Component | None = None, propagation_cost: Annotated[float, minimum=0, units='dB/μm'] | None = None, bend_cost: Annotated[float, minimum=0, units='dB'] | None = None, bend45_cost: Annotated[float, minimum=0, units='dB'] | None = None, cross_cost: Annotated[float, minimum=0, units='dB'] | None = None, cross_space_cost: Annotated[float, minimum=0, units='dB/μm'] | None = None, congestion_cost: Annotated[float, minimum=0, units='dB/μm'] | None = None, congestion_radius: Annotated[int, minimum=0] | None = None, allow_diagonals: bool | None = None, allow_s_bend: bool | None = None, collapse_s_bends: bool | None = None, max_crossings: int | None = None, grid_size: Annotated[float, exclusiveMinimum=0, units='μm'] | None = None, search_margin: Annotated[float, minimum=0, units='μm'] | None = None, net_reorder: bool | None = None, allow_partial: bool | None = None, max_iterations: int | None = None, max_reroute_rounds: Annotated[int, minimum=0] | None = None, reroute_history_cost: Annotated[float, minimum=0] | None = None, matching: collections.abc.Sequence[photonforge.PathLengthMatching] | None = None, show_progress: bool | None = None, diagnostics: bool | None = None, technology: photonforge.Technology | None = None, name: str | None = None, route_model: photonforge.Model | None = None, bundle_model: photonforge.Model | None = None,) -> photonforge.ComponentAutomatic optical routing between reference ports.
The automatic router performs grid-based multi-net routing. Ports must be specified as reference-port tuples so device-aware access preprocessing can use the surrounding component geometry.
Args
- port1: First port for single-net routing. The port must be specified as a tuple including a
Reference, the port name, and the repetition index (optional, only for array references). For bundle routing, a sequence of ports can be used. - port2: Second port for single-net routing, or sequence of ports for bundle routing (with the same length as
port1). - radius: Radius used to generate default bends and S-bend sections.
- obstacles: Additional routing obstacles. Use a sequence of 2D structures, or a
ComponentorReference. - include_port_references_as_obstacles: If
True, add the references used by routed ports toobstacles. IfNone, defaults toTrue. - collision_layers: Layers used for route-section collision stamps and for collecting obstacle polygons from a component or reference. An empty sequence uses all layers. Defaults to routing waveguide layers only.
- collision_offset: Offset applied to route collision layers.
- straight_kwargs: Keyword arguments for
straight. - s_bend_kwargs: Keyword arguments for
s_bend. - bend: 90° bend to be used for routing.
- bend45: Optional 45° bend to be used for routing. If unset and
allow_diagonalsis enabled, it is derived frombendwhenbendis parametric and takes anangle, so the section recipe is shared; otherwise a default 45° bend is built. - cross: Optional crossing to be used for routing.
- propagation_cost: Straight section cost (per μm). Defaults to 1e-5.
- bend_cost: Cost of a 90° bend. Defaults to
2.1 * radius * propagation_cost(5% penalty on the Manhattan distance). - bend45_cost: Cost of a 45° bend. Defaults to
0.51 * bend_cost. - cross_cost: Cost of a crossing. Defaults to twice the
propagation_costapplied to the crossing length. - cross_space_cost: Cost multiplier for nearby crossing spacing. Defaults to
propagation_cost. - congestion_cost: Cost multiplier for nearby routed occupancy. Defaults to
propagation_cost. - congestion_radius: Congestion search radius in grid cells. Defaults to 1.
- allow_diagonals: Controls whether 45° bends are allowed. If
None, defaults toTruewhenbend45is provided, andFalseotherwise. - allow_s_bend: Controls whether the router may use S bends to reach an aligned goal directly. Port access for grid alignment use S bends regardless of this flag. If
None, defaults toFalse. - collapse_s_bends: Controls post-routing removal of unnecessary access S bends. If
None, defaults toTrue. - max_crossings: Maximum crossings per net. Use a negative value for no explicit limit. Defaults to -1.
- grid_size: Router grid size. If
None, defaults to half the resolved routing radius, snapped to 100 layout grid units. See the important notes below. - search_margin: Extra search margin around the routing bounds. If
None, uses an automatic margin based on endpoint separation and bend size. - net_reorder: Allow net reordering before routing. If
None, defaults toTrue. - allow_partial: If
False, raise when any net fails. IfNone, defaults toFalse. - max_iterations: Maximum search iterations per route. If
None, defaults to 500000. - max_reroute_rounds: Maximum route conflict-recovery rounds. If
None, defaults to the number of nets clipped to [3; 16]. - reroute_history_cost: History cost scale used in bundle rerouting. If
None, defaults to 5. - matching: Path-length matching groups. See notes below.
- show_progress: If
True, show routing progress for long-running routes. IfNone, defaults toTrue. - diagnostics: If
True, JSON-encoded diagnostic information from the routing algorithm is stored incomponent.properties.route_auto. IfNone, defaults toFalse. - technology: Component technology. If
None, the default technology is used. - name: Component name.
- route_model: Model to be used with each route sub-component. If
NoneaCircuitModelis used. - bundle_model: Model to be used with the top-level route component. If
NoneaCircuitModelis used.
Important
The grid_size controls the search-space discretization. A
smaller size allows routing in tighter spaces but increases
execution time. A larger size is faster but may miss routes that a
finer grid can find.
For correct collision avoidance, grid_size should be at least
the routed waveguide core width. The
collision_offset adds separation in whole grid-cell increments,
so the center-to-center pitch can be larger than
grid_size + collision_offset.
Note
The costs of S bends are based on the computed length multiplied by the propagation cost.
Path-length matching groups are each a PathLengthMatching
instance. Net indices follow the port order (an empty nets means
every net) and must not repeat across groups. After routing, the
(effective) path lengths in each group are equalized by splicing
displacement-neutral meanders into the routed nets, or, for ordered
dense fans under bends="equal", by rebuilding the group with a
uniform 4-bend ride-bus construction. Access regions, endpoints, and
nets outside the groups are never modified, and no colliding
geometry is ever emitted. One report per group is stored as JSON in
component.properties.path_length_matching.
Returns
Component with emitted route geometry.
Example
>>> component1 = pf.Component("1").add("WG_CORE", pf.Rectangle(size=(8, 12)))>>> component1.add_port(... [... pf.Port((4, 0), 180, "Strip"),... pf.Port((4, 4), 180, "Strip"),... pf.Port((4, -4), 180, "Strip"),... ]... )['P0', 'P1', 'P2']>>> component2 = pf.Component("2").add("WG_CORE", pf.Rectangle(size=(12, 4)))>>> component2.add_port(... [... pf.Port((-4, -2), 90, "Strip"),... pf.Port((4, 2), -90, "Strip"),... pf.Port((4, -2), 90, "Strip"),... ]... )['P0', 'P1', 'P2']>>> # Bundle routing with collision avoidance>>> routed = pf.Component("ROUTED").add("METAL", pf.Rectangle((0, -40), (15, 10)))>>> ref0 = routed.add_reference(component1).translate((-50, -10))>>> ref1 = routed.add_reference(component2).translate((50, 10))>>> route = pf.parametric.route_auto(... port1=[(ref0, "P0"), (ref0, "P1"), (ref0, "P2")],... port2=[(ref1, "P0"), (ref1, "P1"), (ref1, "P2")],... obstacles=routed,... collision_layers=["METAL", "WG_CORE"],... radius=10,... )>>> routed.add(route)Component(name="ROUTED", ...)>>> # Routing through a crossing component>>> routed_x = pf.Component("ROUTED_CROSSING").add(... "METAL", pf.Rectangle((0, -40), (15, 10))... )>>> ref0 = routed_x.add_reference(component1).translate((-50, -10))>>> ref1 = routed_x.add_reference(component2).translate((50, 10))>>> crossing = pf.parametric.crossing(port_spec="Strip", arm_length=2, added_width=0.5)>>> route_with_crossing = pf.parametric.route_auto(... port1=[(ref0, "P0"), (ref0, "P1"), (ref0, "P2")],... port2=[(ref1, "P0"), (ref1, "P2"), (ref1, "P1")],... cross=crossing,... obstacles=routed_x,... collision_layers=["METAL", "WG_CORE"],... radius=10,... )>>> routed_x.add(route_with_crossing)Component(name="ROUTED_CROSSING", ...)>>> layout = pf.Component().add(pf.Reference(routed), pf.Reference(routed_x, (150, 0)))