more bend-related work

This commit is contained in:
Jan Petykiewicz 2026-03-08 22:18:33 -07:00
commit 556241bae3
4 changed files with 92 additions and 2 deletions

25
DOCS.md
View file

@ -51,3 +51,28 @@ The `CostEvaluator` defines the "goodness" of a path.
- **Coordinates**: Micrometers (µm). - **Coordinates**: Micrometers (µm).
- **Grid Snapping**: The router internally operates on a **1nm** grid for final ports and a **1µm** lattice for expansion moves. - **Grid Snapping**: The router internally operates on a **1nm** grid for final ports and a **1µm** lattice for expansion moves.
- **Search Space**: Assumptions are optimized for design areas up to **20mm x 20mm**. - **Search Space**: Assumptions are optimized for design areas up to **20mm x 20mm**.
---
## 5. Best Practices & Tuning Advice
### Speed vs. Optimality
The `greedy_h_weight` is your primary lever for search performance.
- **`1.0`**: Dijkstra-like behavior. Guarantees the shortest path but is very slow.
- **`1.1` to `1.2`**: Recommended range. Balances wire length with fast convergence.
- **`> 1.5`**: Extremely fast "greedy" search. May produce zig-zags or suboptimal detours.
### Avoiding "Zig-Zags"
If the router produces many small bends instead of a long straight line:
1. Increase `bend_penalty` (e.g., set to `100.0` or higher).
2. Ensure `straight_lengths` includes larger values like `25.0` or `100.0`.
3. Decrease `greedy_h_weight` closer to `1.0`.
### Handling Congestion
In multi-net designs, if nets are overlapping:
1. Increase `congestion_penalty` in `CostEvaluator`.
2. Increase `max_iterations` in `PathFinder`.
3. If a solution is still not found, check if the `clearance` is physically possible given the design's narrowest bottlenecks.
### S-Bend Usage
Parametric S-bends are triggered by the `sbend_offsets` list. If you need a specific lateral shift (e.g., 5.86µm for a 45° switchover), add it to `sbend_offsets`. The router will only use an S-bend if it can reach a state that is exactly on the lattice or the target.

View file

@ -3,7 +3,6 @@ from shapely.geometry import Polygon
from inire.geometry.collision import CollisionEngine from inire.geometry.collision import CollisionEngine
from inire.geometry.primitives import Port from inire.geometry.primitives import Port
from inire.router.astar import AStarRouter from inire.router.astar import AStarRouter
from inire.router.config import CostConfig, RouterConfig
from inire.router.cost import CostEvaluator from inire.router.cost import CostEvaluator
from inire.router.danger_map import DangerMap from inire.router.danger_map import DangerMap
from inire.router.pathfinder import PathFinder from inire.router.pathfinder import PathFinder

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

View file

@ -1,7 +1,9 @@
import numpy as np
import pytest import pytest
from shapely.geometry import Point
from inire.geometry.components import Bend90, SBend, Straight from inire.geometry.components import Bend90, SBend, Straight
from inire.geometry.primitives import Port from inire.geometry.primitives import Port, rotate_port, translate_port
def test_straight_generation() -> None: def test_straight_generation() -> None:
@ -90,3 +92,67 @@ def test_sbend_collision_models() -> None:
res_arc = SBend.generate(start, offset, radius, width, collision_type="arc") res_arc = SBend.generate(start, offset, radius, width, collision_type="arc")
assert res_bbox.geometry[0].area > res_arc.geometry[0].area assert res_bbox.geometry[0].area > res_arc.geometry[0].area
def test_sbend_continuity() -> None:
# Verify SBend endpoints and continuity math
start = Port(10, 20, 90) # Starting facing up
offset = 4.0
radius = 20.0
width = 1.0
res = SBend.generate(start, offset, radius, width)
# Target orientation should be same as start
assert abs(res.end_port.orientation - 90.0) < 1e-6
# For a port at 90 deg, +offset is a shift in -x direction
assert abs(res.end_port.x - (10.0 - offset)) < 1e-6
# Geometry should be connected (unary_union results in 1 polygon)
assert len(res.geometry) == 1
assert res.geometry[0].is_valid
def test_arc_sagitta_precision() -> None:
# Verify that requested sagitta actually controls segment count
start = Port(0, 0, 0)
radius = 100.0 # Large radius to make sagitta significant
width = 2.0
# Coarse: 1um sagitta
res_coarse = Bend90.generate(start, radius, width, sagitta=1.0)
# Fine: 0.01um (10nm) sagitta
res_fine = Bend90.generate(start, radius, width, sagitta=0.01)
# Number of segments should be significantly higher for fine
# Exterior points = (segments + 1) * 2
pts_coarse = len(res_coarse.geometry[0].exterior.coords)
pts_fine = len(res_fine.geometry[0].exterior.coords)
assert pts_fine > pts_coarse * 2
def test_component_transform_invariance() -> None:
# Verify that generating at (0,0) then transforming
# is same as generating at the transformed port.
start0 = Port(0, 0, 0)
radius = 10.0
width = 2.0
res0 = Bend90.generate(start0, radius, width, direction="CCW")
# Transform: Translate (10, 10) then Rotate 90
dx, dy = 10.0, 5.0
angle = 90.0
# 1. Transform the generated geometry
p_end_transformed = rotate_port(translate_port(res0.end_port, dx, dy), angle)
# 2. Generate at transformed start
start_transformed = rotate_port(translate_port(start0, dx, dy), angle)
res_transformed = Bend90.generate(start_transformed, radius, width, direction="CCW")
assert abs(res_transformed.end_port.x - p_end_transformed.x) < 1e-6
assert abs(res_transformed.end_port.y - p_end_transformed.y) < 1e-6
assert abs(res_transformed.end_port.orientation - p_end_transformed.orientation) < 1e-6