consistency and speed
This commit is contained in:
parent
58873692d6
commit
c9bb8d6469
5 changed files with 169 additions and 184 deletions
|
|
@ -1,145 +1,141 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Literal
|
||||||
|
|
||||||
import rtree
|
import rtree
|
||||||
from shapely.geometry import Polygon
|
from shapely.geometry import Point, Polygon
|
||||||
from shapely.prepared import prep
|
from shapely.prepared import prep
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from shapely.prepared import PreparedGeometry
|
from shapely.prepared import PreparedGeometry
|
||||||
|
|
||||||
from inire.geometry.primitives import Port
|
from inire.geometry.primitives import Port
|
||||||
|
|
||||||
|
|
||||||
class CollisionEngine:
|
class CollisionEngine:
|
||||||
"""Manages spatial queries for collision detection."""
|
"""Manages spatial queries for collision detection with unified dilation logic."""
|
||||||
|
|
||||||
def __init__(self, clearance: float, max_net_width: float = 2.0, safety_zone_radius: float = 0.0021) -> None:
|
def __init__(self, clearance: float, max_net_width: float = 2.0, safety_zone_radius: float = 0.0021) -> None:
|
||||||
self.clearance = clearance
|
self.clearance = clearance
|
||||||
self.max_net_width = max_net_width
|
self.max_net_width = max_net_width
|
||||||
self.safety_zone_radius = safety_zone_radius
|
self.safety_zone_radius = safety_zone_radius
|
||||||
self.static_obstacles = rtree.index.Index()
|
|
||||||
# To store geometries for precise checks
|
# Static obstacles: store raw geometries to avoid double-dilation
|
||||||
self.obstacle_geometries: dict[int, Polygon] = {} # ID -> Polygon
|
self.static_index = rtree.index.Index()
|
||||||
self.prepared_obstacles: dict[int, PreparedGeometry] = {} # ID -> PreparedGeometry
|
self.static_geometries: dict[int, Polygon] = {} # ID -> Polygon
|
||||||
self._id_counter = 0
|
self.static_prepared: dict[int, PreparedGeometry] = {} # ID -> PreparedGeometry
|
||||||
|
self._static_id_counter = 0
|
||||||
|
|
||||||
# Dynamic paths for multi-net congestion
|
# Dynamic paths for multi-net congestion
|
||||||
self.dynamic_paths = rtree.index.Index()
|
self.dynamic_index = rtree.index.Index()
|
||||||
# obj_id -> (net_id, geometry)
|
# obj_id -> (net_id, raw_geometry)
|
||||||
self.path_geometries: dict[int, tuple[str, Polygon]] = {}
|
self.dynamic_geometries: dict[int, tuple[str, Polygon]] = {}
|
||||||
self._dynamic_id_counter = 0
|
self._dynamic_id_counter = 0
|
||||||
|
|
||||||
def add_static_obstacle(self, polygon: Polygon, pre_dilate: bool = True) -> None:
|
def add_static_obstacle(self, polygon: Polygon) -> None:
|
||||||
"""Add a static obstacle to the engine."""
|
"""Add a static obstacle (raw geometry) to the engine."""
|
||||||
_ = pre_dilate # Keep for API compatibility
|
obj_id = self._static_id_counter
|
||||||
obj_id = self._id_counter
|
self._static_id_counter += 1
|
||||||
self._id_counter += 1
|
|
||||||
|
|
||||||
self.obstacle_geometries[obj_id] = polygon
|
self.static_geometries[obj_id] = polygon
|
||||||
self.prepared_obstacles[obj_id] = prep(polygon)
|
self.static_prepared[obj_id] = prep(polygon)
|
||||||
|
self.static_index.insert(obj_id, polygon.bounds)
|
||||||
# Index the bounding box of the original polygon
|
|
||||||
# We query with dilated moves, so original bounds are enough
|
|
||||||
self.static_obstacles.insert(obj_id, polygon.bounds)
|
|
||||||
|
|
||||||
def add_path(self, net_id: str, geometry: list[Polygon]) -> None:
|
def add_path(self, net_id: str, geometry: list[Polygon]) -> None:
|
||||||
"""Add a net's routed path to the dynamic R-Tree."""
|
"""Add a net's routed path (raw geometry) to the dynamic index."""
|
||||||
# Dilate by clearance/2 for congestion
|
|
||||||
dilation = self.clearance / 2.0
|
|
||||||
for poly in geometry:
|
for poly in geometry:
|
||||||
dilated = poly.buffer(dilation)
|
|
||||||
obj_id = self._dynamic_id_counter
|
obj_id = self._dynamic_id_counter
|
||||||
self._dynamic_id_counter += 1
|
self._dynamic_id_counter += 1
|
||||||
self.path_geometries[obj_id] = (net_id, dilated)
|
self.dynamic_geometries[obj_id] = (net_id, poly)
|
||||||
self.dynamic_paths.insert(obj_id, dilated.bounds)
|
self.dynamic_index.insert(obj_id, poly.bounds)
|
||||||
|
|
||||||
def remove_path(self, net_id: str) -> None:
|
def remove_path(self, net_id: str) -> None:
|
||||||
"""Remove a net's path from the dynamic R-Tree."""
|
"""Remove a net's path from the dynamic index."""
|
||||||
to_remove = [obj_id for obj_id, (nid, _) in self.path_geometries.items() if nid == net_id]
|
to_remove = [obj_id for obj_id, (nid, _) in self.dynamic_geometries.items() if nid == net_id]
|
||||||
for obj_id in to_remove:
|
for obj_id in to_remove:
|
||||||
nid, dilated = self.path_geometries.pop(obj_id)
|
nid, poly = self.dynamic_geometries.pop(obj_id)
|
||||||
self.dynamic_paths.delete(obj_id, dilated.bounds)
|
self.dynamic_index.delete(obj_id, poly.bounds)
|
||||||
|
|
||||||
def lock_net(self, net_id: str) -> None:
|
def lock_net(self, net_id: str) -> None:
|
||||||
"""Move a net's dynamic path to static obstacles permanently."""
|
"""Move a net's dynamic path to static obstacles permanently."""
|
||||||
to_move = [obj_id for obj_id, (nid, _) in self.path_geometries.items() if nid == net_id]
|
to_move = [obj_id for obj_id, (nid, _) in self.dynamic_geometries.items() if nid == net_id]
|
||||||
for obj_id in to_move:
|
for obj_id in to_move:
|
||||||
nid, dilated = self.path_geometries.pop(obj_id)
|
nid, poly = self.dynamic_geometries.pop(obj_id)
|
||||||
self.dynamic_paths.delete(obj_id, dilated.bounds)
|
self.dynamic_index.delete(obj_id, poly.bounds)
|
||||||
|
self.add_static_obstacle(poly)
|
||||||
# Add to static (already dilated for clearance)
|
|
||||||
new_static_id = self._id_counter
|
|
||||||
self._id_counter += 1
|
|
||||||
self.obstacle_geometries[new_static_id] = dilated
|
|
||||||
self.prepared_obstacles[new_static_id] = prep(dilated)
|
|
||||||
self.static_obstacles.insert(new_static_id, dilated.bounds)
|
|
||||||
|
|
||||||
def count_congestion(self, geometry: Polygon, net_id: str) -> int:
|
|
||||||
"""Count how many other nets collide with this geometry."""
|
|
||||||
dilation = self.clearance / 2.0
|
|
||||||
test_poly = geometry.buffer(dilation)
|
|
||||||
return self.count_congestion_prebuffered(test_poly, net_id)
|
|
||||||
|
|
||||||
def count_congestion_prebuffered(self, dilated_geometry: Polygon, net_id: str) -> int:
|
|
||||||
"""Count how many other nets collide with this pre-dilated geometry."""
|
|
||||||
candidates = self.dynamic_paths.intersection(dilated_geometry.bounds)
|
|
||||||
count = 0
|
|
||||||
for obj_id in candidates:
|
|
||||||
other_net_id, other_poly = self.path_geometries[obj_id]
|
|
||||||
if other_net_id != net_id and dilated_geometry.intersects(other_poly):
|
|
||||||
count += 1
|
|
||||||
return count
|
|
||||||
|
|
||||||
def is_collision(
|
def is_collision(
|
||||||
self,
|
self,
|
||||||
geometry: Polygon,
|
geometry: Polygon,
|
||||||
net_width: float,
|
net_width: float = 2.0,
|
||||||
start_port: Port | None = None,
|
start_port: Port | None = None,
|
||||||
end_port: Port | None = None,
|
end_port: Port | None = None
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Check if a geometry (e.g. a Move) collides with static obstacles."""
|
"""Alias for check_collision(buffer_mode='static') for backward compatibility."""
|
||||||
_ = net_width # Width is already integrated into engine dilation settings
|
_ = net_width
|
||||||
dilation = self.clearance / 2.0
|
res = self.check_collision(geometry, "default", buffer_mode="static", start_port=start_port, end_port=end_port)
|
||||||
test_poly = geometry.buffer(dilation)
|
return bool(res)
|
||||||
return self.is_collision_prebuffered(test_poly, start_port=start_port, end_port=end_port)
|
|
||||||
|
|
||||||
def is_collision_prebuffered(
|
def count_congestion(self, geometry: Polygon, net_id: str) -> int:
|
||||||
|
"""Alias for check_collision(buffer_mode='congestion') for backward compatibility."""
|
||||||
|
res = self.check_collision(geometry, net_id, buffer_mode="congestion")
|
||||||
|
return int(res)
|
||||||
|
|
||||||
|
def check_collision(
|
||||||
self,
|
self,
|
||||||
dilated_geometry: Polygon,
|
geometry: Polygon,
|
||||||
|
net_id: str,
|
||||||
|
buffer_mode: Literal["static", "congestion"] = "static",
|
||||||
start_port: Port | None = None,
|
start_port: Port | None = None,
|
||||||
end_port: Port | None = None,
|
end_port: Port | None = None
|
||||||
) -> bool:
|
) -> bool | int:
|
||||||
"""Check if a pre-dilated geometry collides with static obstacles."""
|
"""
|
||||||
# Query R-Tree using the bounds of the dilated move
|
Check for collisions using unified dilation logic.
|
||||||
candidates = self.static_obstacles.intersection(dilated_geometry.bounds)
|
|
||||||
|
If buffer_mode == "static":
|
||||||
|
Returns True if geometry collides with static obstacles (buffered by full clearance).
|
||||||
|
If buffer_mode == "congestion":
|
||||||
|
Returns count of other nets colliding with geometry (both buffered by clearance/2).
|
||||||
|
"""
|
||||||
|
if buffer_mode == "static":
|
||||||
|
# Buffered move vs raw static obstacle
|
||||||
|
# Distance must be >= clearance
|
||||||
|
test_poly = geometry.buffer(self.clearance)
|
||||||
|
candidates = self.static_index.intersection(test_poly.bounds)
|
||||||
|
|
||||||
for obj_id in candidates:
|
for obj_id in candidates:
|
||||||
# Use prepared geometry for fast intersection
|
if self.static_prepared[obj_id].intersects(test_poly):
|
||||||
if self.prepared_obstacles[obj_id].intersects(dilated_geometry):
|
# Safety zone check (using exact intersection area/bounds)
|
||||||
# Check safety zone (2nm radius)
|
|
||||||
if start_port or end_port:
|
if start_port or end_port:
|
||||||
obstacle = self.obstacle_geometries[obj_id]
|
intersection = test_poly.intersection(self.static_geometries[obj_id])
|
||||||
intersection = dilated_geometry.intersection(obstacle)
|
|
||||||
|
|
||||||
if intersection.is_empty:
|
if intersection.is_empty:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Precise check: is every point in the intersection close to either port?
|
|
||||||
ix_minx, ix_miny, ix_maxx, ix_maxy = intersection.bounds
|
ix_minx, ix_miny, ix_maxx, ix_maxy = intersection.bounds
|
||||||
|
|
||||||
is_near_start = False
|
is_safe = False
|
||||||
if start_port and (abs(ix_minx - start_port.x) < self.safety_zone_radius and abs(ix_maxx - start_port.x) < self.safety_zone_radius and
|
for p in [start_port, end_port]:
|
||||||
abs(ix_miny - start_port.y) < self.safety_zone_radius and abs(ix_maxy - start_port.y) < self.safety_zone_radius):
|
if p and (abs(ix_minx - p.x) < self.safety_zone_radius and
|
||||||
is_near_start = True
|
abs(ix_maxx - p.x) < self.safety_zone_radius and
|
||||||
|
abs(ix_miny - p.y) < self.safety_zone_radius and
|
||||||
is_near_end = False
|
abs(ix_maxy - p.y) < self.safety_zone_radius):
|
||||||
if end_port and (abs(ix_minx - end_port.x) < self.safety_zone_radius and abs(ix_maxx - end_port.x) < self.safety_zone_radius and
|
is_safe = True
|
||||||
abs(ix_miny - end_port.y) < self.safety_zone_radius and abs(ix_maxy - end_port.y) < self.safety_zone_radius):
|
break
|
||||||
is_near_end = True
|
if is_safe:
|
||||||
if is_near_start or is_near_end:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
else: # buffer_mode == "congestion"
|
||||||
|
# Both paths buffered by clearance/2 => Total separation = clearance
|
||||||
|
dilation = self.clearance / 2.0
|
||||||
|
test_poly = geometry.buffer(dilation)
|
||||||
|
candidates = self.dynamic_index.intersection(test_poly.bounds)
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for obj_id in candidates:
|
||||||
|
other_net_id, other_poly = self.dynamic_geometries[obj_id]
|
||||||
|
if other_net_id != net_id:
|
||||||
|
# Buffer the other path segment too
|
||||||
|
if test_poly.intersects(other_poly.buffer(dilation)):
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,9 @@ class AStarRouter:
|
||||||
else:
|
else:
|
||||||
hard_coll = False
|
hard_coll = False
|
||||||
for poly in result.geometry:
|
for poly in result.geometry:
|
||||||
if self.cost_evaluator.collision_engine.is_collision(poly, net_width, start_port=parent.port, end_port=result.end_port):
|
if self.cost_evaluator.collision_engine.check_collision(
|
||||||
|
poly, net_id, buffer_mode="static", start_port=parent.port, end_port=result.end_port
|
||||||
|
):
|
||||||
hard_coll = True
|
hard_coll = True
|
||||||
break
|
break
|
||||||
self._collision_cache[cache_key] = hard_coll
|
self._collision_cache[cache_key] = hard_coll
|
||||||
|
|
@ -300,8 +302,6 @@ class AStarRouter:
|
||||||
# Turn penalties scaled by radius to favor larger turns
|
# Turn penalties scaled by radius to favor larger turns
|
||||||
ref_radius = 10.0
|
ref_radius = 10.0
|
||||||
if "B" in move_type and move_radius is not None:
|
if "B" in move_type and move_radius is not None:
|
||||||
# Scale penalty: larger radius -> smaller penalty
|
|
||||||
# e.g. radius 10 -> factor 1.0, radius 30 -> factor 0.33
|
|
||||||
penalty_factor = ref_radius / move_radius
|
penalty_factor = ref_radius / move_radius
|
||||||
move_cost += self.config.bend_penalty * penalty_factor
|
move_cost += self.config.bend_penalty * penalty_factor
|
||||||
elif "SB" in move_type and move_radius is not None:
|
elif "SB" in move_type and move_radius is not None:
|
||||||
|
|
|
||||||
|
|
@ -74,31 +74,23 @@ class CostEvaluator:
|
||||||
_ = net_width # Unused
|
_ = net_width # Unused
|
||||||
total_cost = length * self.unit_length_cost
|
total_cost = length * self.unit_length_cost
|
||||||
|
|
||||||
# 1. Hard Collision & Boundary Check
|
# 1. Boundary Check (Centerline based for compatibility)
|
||||||
# We buffer by the full clearance to ensure distance >= clearance
|
if not self.danger_map.is_within_bounds(end_port.x, end_port.y):
|
||||||
hard_dilation = self.collision_engine.clearance
|
return 1e15
|
||||||
|
|
||||||
|
# 2. Collision Check
|
||||||
for poly in geometry:
|
for poly in geometry:
|
||||||
dilated_poly = poly.buffer(hard_dilation)
|
# Hard Collision (Static obstacles)
|
||||||
|
if self.collision_engine.check_collision(
|
||||||
|
poly, net_id, buffer_mode="static", start_port=start_port, end_port=end_port
|
||||||
|
):
|
||||||
|
return 1e15
|
||||||
|
|
||||||
# Boundary Check: Physical edges must stay within design bounds
|
# Soft Collision (Negotiated Congestion)
|
||||||
minx, miny, maxx, maxy = dilated_poly.bounds
|
overlaps = self.collision_engine.check_collision(poly, net_id, buffer_mode="congestion")
|
||||||
if not (self.danger_map.is_within_bounds(minx, miny) and
|
if isinstance(overlaps, int) and overlaps > 0:
|
||||||
self.danger_map.is_within_bounds(maxx, maxy)):
|
|
||||||
return 1e15 # Out of bounds is impossible
|
|
||||||
|
|
||||||
if self.collision_engine.is_collision_prebuffered(dilated_poly, start_port=start_port, end_port=end_port):
|
|
||||||
return 1e15 # Impossible cost for hard collisions
|
|
||||||
|
|
||||||
# 2. Soft Collision check (Negotiated Congestion)
|
|
||||||
# We buffer by clearance/2 because both paths are buffered by clearance/2
|
|
||||||
soft_dilation = self.collision_engine.clearance / 2.0
|
|
||||||
for poly in geometry:
|
|
||||||
dilated_poly = poly.buffer(soft_dilation)
|
|
||||||
overlaps = self.collision_engine.count_congestion_prebuffered(dilated_poly, net_id)
|
|
||||||
if overlaps > 0:
|
|
||||||
total_cost += overlaps * self.congestion_penalty
|
total_cost += overlaps * self.congestion_penalty
|
||||||
|
|
||||||
# 3. Proximity cost from Danger Map
|
# 3. Proximity cost from Danger Map
|
||||||
total_cost += self.g_proximity(end_port.x, end_port.y)
|
total_cost += self.g_proximity(end_port.x, end_port.y)
|
||||||
return total_cost
|
return total_cost
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,14 @@ from __future__ import annotations
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import shapely
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from shapely.geometry import Polygon
|
from shapely.geometry import Polygon
|
||||||
|
|
||||||
|
|
||||||
class DangerMap:
|
class DangerMap:
|
||||||
"""A pre-computed grid for heuristic proximity costs."""
|
"""A pre-computed grid for heuristic proximity costs, vectorized for performance."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
@ -28,47 +29,36 @@ class DangerMap:
|
||||||
self.width_cells = int(np.ceil((self.maxx - self.minx) / self.resolution))
|
self.width_cells = int(np.ceil((self.maxx - self.minx) / self.resolution))
|
||||||
self.height_cells = int(np.ceil((self.maxy - self.miny) / self.resolution))
|
self.height_cells = int(np.ceil((self.maxy - self.miny) / self.resolution))
|
||||||
|
|
||||||
# Use uint8 for memory efficiency if normalized, or float16/float32.
|
|
||||||
# Let's use float32 for simplicity and precision in the prototype.
|
|
||||||
# For a 1000x1000 grid, this is only 4MB.
|
|
||||||
# For 20000x20000, it's 1.6GB.
|
|
||||||
self.grid = np.zeros((self.width_cells, self.height_cells), dtype=np.float32)
|
self.grid = np.zeros((self.width_cells, self.height_cells), dtype=np.float32)
|
||||||
|
|
||||||
def precompute(self, obstacles: list[Polygon]) -> None:
|
def precompute(self, obstacles: list[Polygon]) -> None:
|
||||||
"""Pre-compute the proximity costs for the entire grid."""
|
"""Pre-compute the proximity costs for the entire grid using vectorized operations."""
|
||||||
# For each cell, find distance to nearest obstacle.
|
|
||||||
# This is a distance transform problem.
|
|
||||||
# For the prototype, we can use a simpler approach or scipy.ndimage.distance_transform_edt.
|
|
||||||
from scipy.ndimage import distance_transform_edt
|
from scipy.ndimage import distance_transform_edt
|
||||||
|
|
||||||
# Create a binary mask of obstacles
|
# 1. Create a binary mask of obstacles
|
||||||
mask = np.ones((self.width_cells, self.height_cells), dtype=bool)
|
mask = np.ones((self.width_cells, self.height_cells), dtype=bool)
|
||||||
# Rasterize obstacles (simplified: mark cells whose center is inside an obstacle)
|
|
||||||
# This is slow for many obstacles; in a real engine, we'd use a faster rasterizer.
|
# Create coordinate grids
|
||||||
from shapely.geometry import Point
|
x_coords = np.linspace(self.minx + self.resolution/2, self.maxx - self.resolution/2, self.width_cells)
|
||||||
|
y_coords = np.linspace(self.miny + self.resolution/2, self.maxy - self.resolution/2, self.height_cells)
|
||||||
|
xv, yv = np.meshgrid(x_coords, y_coords, indexing='ij')
|
||||||
|
|
||||||
for poly in obstacles:
|
for poly in obstacles:
|
||||||
# Get bounding box in grid coordinates
|
# Use shapely.contains_xy for fast vectorized point-in-polygon check
|
||||||
p_minx, p_miny, p_maxx, p_maxy = poly.bounds
|
in_poly = shapely.contains_xy(poly, xv, yv)
|
||||||
x_start = max(0, int((p_minx - self.minx) / self.resolution))
|
mask[in_poly] = False
|
||||||
x_end = min(self.width_cells, int((p_maxx - self.minx) / self.resolution) + 1)
|
|
||||||
y_start = max(0, int((p_miny - self.miny) / self.resolution))
|
|
||||||
y_end = min(self.height_cells, int((p_maxy - self.miny) / self.resolution) + 1)
|
|
||||||
|
|
||||||
for ix in range(x_start, x_end):
|
# 2. Distance transform (mask=True for empty space)
|
||||||
cx = self.minx + (ix + 0.5) * self.resolution
|
|
||||||
for iy in range(y_start, y_end):
|
|
||||||
cy = self.miny + (iy + 0.5) * self.resolution
|
|
||||||
if poly.contains(Point(cx, cy)):
|
|
||||||
mask[ix, iy] = False
|
|
||||||
|
|
||||||
# Distance transform (mask=True for empty space)
|
|
||||||
distances = distance_transform_edt(mask) * self.resolution
|
distances = distance_transform_edt(mask) * self.resolution
|
||||||
|
|
||||||
# Proximity cost: k / d^2 if d < threshold, else 0
|
# 3. Proximity cost: k / d^2 if d < threshold, else 0
|
||||||
# To avoid division by zero, we cap distances at a small epsilon (e.g. 0.1um)
|
# Cap distances at a small epsilon (e.g. 0.1um) to avoid division by zero
|
||||||
safe_distances = np.maximum(distances, 0.1)
|
safe_distances = np.maximum(distances, 0.1)
|
||||||
self.grid = np.where(distances < self.safety_threshold, self.k / (safe_distances**2), 0.0).astype(np.float32)
|
self.grid = np.where(
|
||||||
|
distances < self.safety_threshold,
|
||||||
|
self.k / (safe_distances**2),
|
||||||
|
0.0
|
||||||
|
).astype(np.float32)
|
||||||
|
|
||||||
def is_within_bounds(self, x: float, y: float) -> bool:
|
def is_within_bounds(self, x: float, y: float) -> bool:
|
||||||
"""Check if a coordinate is within the design bounds."""
|
"""Check if a coordinate is within the design bounds."""
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class PathFinder:
|
||||||
logger.debug(f" Net {net_id} routed in {time.monotonic() - net_start:.4f}s")
|
logger.debug(f" Net {net_id} routed in {time.monotonic() - net_start:.4f}s")
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
# 3. Add to R-Tree
|
# 3. Add to index
|
||||||
all_geoms = []
|
all_geoms = []
|
||||||
for res in path:
|
for res in path:
|
||||||
all_geoms.extend(res.geometry)
|
all_geoms.extend(res.geometry)
|
||||||
|
|
@ -88,7 +88,11 @@ class PathFinder:
|
||||||
# Check if this new path has any congestion
|
# Check if this new path has any congestion
|
||||||
collision_count = 0
|
collision_count = 0
|
||||||
for poly in all_geoms:
|
for poly in all_geoms:
|
||||||
collision_count += self.cost_evaluator.collision_engine.count_congestion(poly, net_id)
|
overlaps = self.cost_evaluator.collision_engine.check_collision(
|
||||||
|
poly, net_id, buffer_mode="congestion"
|
||||||
|
)
|
||||||
|
if isinstance(overlaps, int):
|
||||||
|
collision_count += overlaps
|
||||||
|
|
||||||
if collision_count > 0:
|
if collision_count > 0:
|
||||||
any_congestion = True
|
any_congestion = True
|
||||||
|
|
@ -120,9 +124,12 @@ class PathFinder:
|
||||||
collision_count = 0
|
collision_count = 0
|
||||||
for comp in res.path:
|
for comp in res.path:
|
||||||
for poly in comp.geometry:
|
for poly in comp.geometry:
|
||||||
collision_count += self.cost_evaluator.collision_engine.count_congestion(poly, net_id)
|
overlaps = self.cost_evaluator.collision_engine.check_collision(
|
||||||
|
poly, net_id, buffer_mode="congestion"
|
||||||
|
)
|
||||||
|
if isinstance(overlaps, int):
|
||||||
|
collision_count += overlaps
|
||||||
|
|
||||||
final_results[net_id] = RoutingResult(net_id, res.path, collision_count == 0, collision_count)
|
final_results[net_id] = RoutingResult(net_id, res.path, collision_count == 0, collision_count)
|
||||||
|
|
||||||
return final_results
|
return final_results
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue