---
title: "Routing"
description: "Connect components with Route, ports, and bends."
canonical_url: "https://www.rosette.dev/docs/guides/routing"
markdown_url: "https://www.rosette.dev/docs/guides/routing.md"
source_url: "https://github.com/PreFab-Photonics/rosette/blob/f086d7670645fd36c05362d696d442a2b2e74850/www/content/docs/guides/routing.mdx"
docs_channel: "main"
docs_revision: "f086d7670645fd36c05362d696d442a2b2e74850"
---

# Routing

[`Route`](/docs/api-reference/Route) is Rosette's waypoint-based router.
You hand it a sequence of points (plus optional start and end
[`Port`](/docs/api-reference/Port)s) and it emits a waveguide polygon with
straight segments, bends at corners, and linear width transitions.

Route is not an auto-router. You supply the path; it fills in the geometry.

## A minimal route

Connect two ports with a simple S-shape:

```python
from rosette import Cell, Layer, Point, Port, Vector2
from rosette.io import write_gds
from rosette.routing import Route

# A simple source/sink pair of ports.
src = Port("out", Point(0,  0),  Vector2( 1, 0), width=0.5)
dst = Port("in",  Point(120, 30), Vector2(-1, 0), width=0.5)

route = Route(Layer(1, 0), width=0.5, bend_radius=5.0)
route.start_at_port(src)
route.to(60, src.position.y)   # horizontal run out of src
route.to(60, dst.position.y)   # vertical jog
route.end_at_port(dst)         # arrive along dst's axis

top = Cell("top")
top.add_ref(route.to_cell("wg").at(0, 0))
write_gds("output.gds", top)
```

A few things to notice:

* The [`Route`](/docs/api-reference/Route) constructor takes the layer,
  the default waveguide width, and the default bend radius. You can
  override the width or radius on a per-waypoint basis by passing
  `width=` / `bend_radius=` to `to()`.
* [`start_at_port`](/docs/api-reference/Route) and
  [`end_at_port`](/docs/api-reference/Route) use the port's outward
  direction and width, so you do not have to re-specify them.
* [`to_cell(name)`](/docs/api-reference/Route) returns a
  [`Cell`](/docs/api-reference/Cell) that you can place with `add_ref`
  like any other cell. The Cell exposes geometry, hierarchy, and ports only;
  route diagnostics stay on the Route.



> **Warning: Always add intermediate waypoints between ports**
>
> `start_at_port(a)` followed immediately by `end_at_port(b)` draws a
> single straight segment between `a` and `b`, ignoring their directions.
> For any bend, you need at least one `to(x, y)` in between so the route
> departs and arrives along each port's axis.



## Route diagnostics

`Route` exposes the generated centerline length, warnings, and typed bend
diagnostics directly:

```python
from rosette.routing import BendInfo, Route

length = route.path_length
warnings = route.warnings
bends: list[BendInfo] = route.bends

for bend in bends:
    print(bend.radius, bend.position, bend.requested_radius)
```

`to_cell()` returns only the route geometry as a public `Cell`. Rosette retains
the route diagnostics in a private sidecar so `run_checks()` and
`rosette-layout` JSON can preserve them without turning Cell into a general
metadata container. Bend-radius checks consume these route diagnostics, not
arbitrary Cell metadata.

## Manual waypoints (no ports)

If you do not have ports, use `start_at` and `end_at` with an explicit
angle in degrees:

```python
from rosette import Layer
from rosette.routing import Route

route = Route(Layer(1, 0), width=0.5, bend_radius=5.0)
route.start_at(0, 0, angle=0)      # head in +X
route.to(50, 0)
route.to(50, 30)
route.end_at(100, 30, angle=0)     # arrive heading +X
cell = route.to_cell("manual")
```

## Bend profiles

Every corner in a route is filleted by a bend. Rosette ships two profiles:

* **`"circular"`** (default): constant-radius arc. Predictable footprint,
  cheap to compute, fine for low-index-contrast platforms and design
  exploration.
* **`"euler"`**: a clothoid (Cornu-spiral) fillet whose curvature grows
  linearly with arc length, reaching a minimum radius at the midpoint
  equal to `bend_radius`. Lower-loss on high-index-contrast platforms,
  at the cost of a longer bend (roughly 2x the arc length of a circular
  bend at the same peak curvature).

```python
from rosette import Layer
from rosette.routing import Route

# High-index platform? Prefer Euler bends.
route = Route(
    Layer(1, 0),
    width=0.5,
    bend_radius=5.0,
    bend_profile="euler",
)
route.start_at(0, 0, angle=0)
route.to(50, 0)
route.to(50, 30)
route.end_at(100, 30, angle=0)
cell = route.to_cell("euler_route")
```



> **Note: Per-corner vs. whole-bend Euler**
>
> `Route(bend_profile="euler")` inserts an *isotropic clothoid fillet* at
> each corner. This keeps curvature bounded along an arbitrary path.
> It is **not** the same shape as the whole-S-bend clothoid used by
> [`rosette.components.sbend`](/docs/api-reference) with
> `bend_type="euler"`, which anisotropically rescales a clothoid to hit a
> prescribed `(length, offset)`. Use the component for a dedicated offset
> bend; use `Route` for routing.



## Tapers and variable width

When a waypoint changes the width, Rosette interpolates linearly across
the full segment ending at that waypoint. Place a preceding waypoint to
control where the transition starts:

```python
from rosette import Layer
from rosette.routing import Route

route = Route(Layer(1, 0), width=0.5, bend_radius=5.0)
route.start_at(0, 0, angle=0)
route.to(40, 0)              # stays at 0.5
route.to(50, 0, width=1.5)   # widens across this 10 um segment
route.to(100, 0)             # stays at 1.5
route.end_at(100, 0, angle=0)
cell = route.to_cell("taper_route")
```

Route has no separate taper-length or abrupt-transition control. For a
specialized profile, construct the taper geometry explicitly and route
the constant-width sections on either side.

## Component path metrics

Component functions also return geometry-only Cells. Use the companion metric
whose name identifies the optical path you mean:

```python
from rosette.components import (
    bragg_grating_length,
    crossing_through_length,
    directional_coupler_arm_length,
    mmi_through_length,
    ring_round_trip_length,
    sbend_path_length,
)
```

The available metrics are `sbend_path_length`, `mmi_through_length`,
`ring_round_trip_length`, `crossing_through_length`,
`directional_coupler_arm_length`, and `bragg_grating_length`. Not every
component has one generic path, so Cells do not carry a generic
`path_length` attribute.

## Common pitfalls



> **Warning: Bend radius too tight**
>
> If two waypoints are closer than `2 * bend_radius`, the router
> auto-reduces the radius at that corner and prints a warning to stderr
> when you call `to_cell`. Fix it by spacing the waypoints farther apart
> or lowering `bend_radius`.





> **Warning: Width mismatch at port connections**
>
> If your `Route` width is `0.5` but the port you connect to is `1.0`, the
> route inherits the port's width at the start and end, but the
> [design-checks](/docs/api-reference#run_checks) pass will still flag
> *other* width mismatches on connected ports. Run
> `uv run rosette check designs/foo.py` (or
> [`run_checks`](/docs/api-reference#run_checks)) to catch these.





> **Warning: Angle mismatch at port connections**
>
> Your last `to(...)` waypoint should land on the port's axis. For a port
> pointing `+X` at `(100, 5)`, the last waypoint before `end_at_port`
> should have the same `y`. Otherwise the route draws a short diagonal
> into the port and connectivity checks flag an angle mismatch.





> **Warning: Straight line between two ports**
>
> Always put at least one `to()` between `start_at_port` and
> `end_at_port`. The router doesn't synthesize turns on its own: two
> ports alone produce a straight diagonal between them, ignoring port
> directions.



## See also

* [`Route`](/docs/api-reference/Route): full constructor and method
  reference
* [`Port`](/docs/api-reference/Port): port geometry and connectivity
* [`PathCap`](/docs/api-reference/PathCap): control endpoint geometry for
  `add_path`
* [Core concepts](/docs/getting-started/core-concepts): the overall
  mental model
* [Cells and hierarchy](/docs/guides/cells-and-hierarchy): placing
  routed cells in a design