Skip to content

Commit dd13c44

Browse files
FEAT: Reduce via by density (#1469)
* reduce vias by density * reduce vias by density * reduce vias by density * reduce vias by density grpc * reduce vias by density test * reduce vias by density test --------- Co-authored-by: Hui Zhou <[email protected]>
1 parent 5a86cd9 commit dd13c44

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

src/pyedb/dotnet/database/padstack.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,3 +1943,90 @@ def expand_cluster(point_idx, neighbors):
19431943
clusters[int(label)].append(padstack_ids[i])
19441944

19451945
return dict(clusters)
1946+
1947+
def reduce_via_by_density(
1948+
self, padstacks: List[int], cell_size_x: float = 1e-3, cell_size_y: float = 1e-3, delete: bool = False
1949+
) -> tuple[List[int], List[List[List[float]]]]:
1950+
"""
1951+
Reduce the number of vias by density. Keep only one via which is closest to the center of the cell. The cells
1952+
are automatically populated based on the input vias.
1953+
1954+
Parameters
1955+
----------
1956+
padstacks: List[int]
1957+
List of padstack ids to be reduced.
1958+
1959+
cell_size_x : float
1960+
Width of each grid cell (default is 1e-3).
1961+
1962+
cell_size_y : float
1963+
Height of each grid cell (default is 1e-3).
1964+
1965+
delete: bool
1966+
If True, delete vias that are not kept (default is False).
1967+
1968+
Returns
1969+
-------
1970+
List[int]
1971+
IDs of vias kept after reduction.
1972+
1973+
List[List[float]]
1974+
coordinates for grid lines (for plotting).
1975+
1976+
"""
1977+
to_keep = set()
1978+
1979+
all_instances = self.instances
1980+
positions = np.array([all_instances[_id].position for _id in padstacks])
1981+
1982+
x_coords, y_coords = positions[:, 0], positions[:, 1]
1983+
x_min, x_max = np.min(x_coords), np.max(x_coords)
1984+
y_min, y_max = np.min(y_coords), np.max(y_coords)
1985+
1986+
padstacks_array = np.array(padstacks)
1987+
cell_map = {} # {(cell_x, cell_y): [(id1, [x1, y1]), (id2, [x2, y2), ...]}
1988+
grid = []
1989+
1990+
for idx, pos in enumerate(positions):
1991+
i = int((pos[0] - x_min) // cell_size_x)
1992+
j = int((pos[1] - y_min) // cell_size_y)
1993+
cell_key = (i, j)
1994+
cell_map.setdefault(cell_key, []).append((padstacks_array[idx], pos))
1995+
1996+
for (i, j), items in cell_map.items():
1997+
# cell center
1998+
cell_x_min = x_min + i * cell_size_x
1999+
cell_y_min = y_min + j * cell_size_y
2000+
cell_x_mid = cell_x_min + 0.5 * cell_size_x
2001+
cell_y_mid = cell_y_min + 0.5 * cell_size_y
2002+
2003+
grid.append(
2004+
[
2005+
[
2006+
cell_x_min,
2007+
cell_x_min + cell_size_x,
2008+
cell_x_min + cell_size_x,
2009+
cell_x_min,
2010+
cell_x_min,
2011+
],
2012+
[
2013+
cell_y_min,
2014+
cell_y_min,
2015+
cell_y_min + cell_size_y,
2016+
cell_y_min + cell_size_y,
2017+
cell_y_min,
2018+
],
2019+
]
2020+
)
2021+
2022+
# Find closest via to cell center
2023+
distances = [np.linalg.norm(pos - [cell_x_mid, cell_y_mid]) for _, pos in items]
2024+
closest_idx = np.argmin(distances)
2025+
to_keep.add(items[closest_idx][0])
2026+
2027+
if delete:
2028+
to_delete = set(padstacks) - to_keep
2029+
for _id in to_delete:
2030+
all_instances[_id].delete()
2031+
2032+
return list(to_keep), grid

src/pyedb/grpc/database/padstacks.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,3 +1853,90 @@ def expand_cluster(point_idx, neighbors):
18531853
clusters[int(label)].append(padstack_ids[i])
18541854

18551855
return dict(clusters)
1856+
1857+
def reduce_via_by_density(
1858+
self, padstacks: List[int], cell_size_x: float = 1e-3, cell_size_y: float = 1e-3, delete: bool = False
1859+
) -> tuple[List[int], List[List[List[float]]]]:
1860+
"""
1861+
Reduce the number of vias by density. Keep only one via which is closest to the center of the cell. The cells
1862+
are automatically populated based on the input vias.
1863+
1864+
Parameters
1865+
----------
1866+
padstacks: List[int]
1867+
List of padstack ids to be reduced.
1868+
1869+
cell_size_x : float
1870+
Width of each grid cell (default is 1e-3).
1871+
1872+
cell_size_y : float
1873+
Height of each grid cell (default is 1e-3).
1874+
1875+
delete: bool
1876+
If True, delete vias that are not kept (default is False).
1877+
1878+
Returns
1879+
-------
1880+
List[int]
1881+
IDs of vias kept after reduction.
1882+
1883+
List[List[float]]
1884+
coordinates for grid lines (for plotting).
1885+
1886+
"""
1887+
to_keep = set()
1888+
1889+
all_instances = self.instances
1890+
positions = np.array([all_instances[_id].position for _id in padstacks])
1891+
1892+
x_coords, y_coords = positions[:, 0], positions[:, 1]
1893+
x_min, x_max = np.min(x_coords), np.max(x_coords)
1894+
y_min, y_max = np.min(y_coords), np.max(y_coords)
1895+
1896+
padstacks_array = np.array(padstacks)
1897+
cell_map = {} # {(cell_x, cell_y): [(id1, [x1, y1]), (id2, [x2, y2), ...]}
1898+
grid = []
1899+
1900+
for idx, pos in enumerate(positions):
1901+
i = int((pos[0] - x_min) // cell_size_x)
1902+
j = int((pos[1] - y_min) // cell_size_y)
1903+
cell_key = (i, j)
1904+
cell_map.setdefault(cell_key, []).append((padstacks_array[idx], pos))
1905+
1906+
for (i, j), items in cell_map.items():
1907+
# cell center
1908+
cell_x_min = x_min + i * cell_size_x
1909+
cell_y_min = y_min + j * cell_size_y
1910+
cell_x_mid = cell_x_min + 0.5 * cell_size_x
1911+
cell_y_mid = cell_y_min + 0.5 * cell_size_y
1912+
1913+
grid.append(
1914+
[
1915+
[
1916+
cell_x_min,
1917+
cell_x_min + cell_size_x,
1918+
cell_x_min + cell_size_x,
1919+
cell_x_min,
1920+
cell_x_min,
1921+
],
1922+
[
1923+
cell_y_min,
1924+
cell_y_min,
1925+
cell_y_min + cell_size_y,
1926+
cell_y_min + cell_size_y,
1927+
cell_y_min,
1928+
],
1929+
]
1930+
)
1931+
1932+
# Find closest via to cell center
1933+
distances = [np.linalg.norm(pos - [cell_x_mid, cell_y_mid]) for _, pos in items]
1934+
closest_idx = np.argmin(distances)
1935+
to_keep.add(items[closest_idx][0])
1936+
1937+
if delete:
1938+
to_delete = set(padstacks) - to_keep
1939+
for _id in to_delete:
1940+
all_instances[_id].delete()
1941+
1942+
return list(to_keep), grid

tests/system/test_edb_padstacks.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,27 @@ def test_dbscan(self, edb_examples):
561561
assert len(clusters1[0]) == 20
562562
assert len(clusters2) == 2
563563
assert len(clusters2[1]) == 21
564+
edbapp.close(terminate_rpc_session=False)
565+
566+
def test_reduce_via_by_density(self, edb_examples):
567+
source_path = edb_examples.example_models_path / "TEDB" / "merge_via_4layers.aedb"
568+
edbapp = edb_examples.load_edb(source_path)
569+
570+
inst = edbapp.padstacks.instances
571+
all_vias = {id_: i.position for id_, i in inst.items()}
572+
clusters = edbapp.padstacks.dbscan(all_vias, max_distance=2e-3, min_samples=3)
573+
574+
kept_2mm, grid_2mm = edbapp.padstacks.reduce_via_by_density(clusters[0], cell_size_x=2e-3, cell_size_y=2e-3)
575+
kept_5mm, grid_5mm = edbapp.padstacks.reduce_via_by_density(clusters[0], cell_size_x=5e-3, cell_size_y=5e-3)
576+
assert len(kept_2mm) == 8
577+
assert len(grid_2mm) == 8
578+
assert len(kept_5mm) == 1
579+
assert len(grid_5mm) == 1
580+
581+
_, _ = edbapp.padstacks.reduce_via_by_density(clusters[0], cell_size_x=5e-3, cell_size_y=5e-3, delete=True)
582+
_, _ = edbapp.padstacks.reduce_via_by_density(clusters[1], cell_size_x=5e-3, cell_size_y=5e-3, delete=True)
583+
assert len(edbapp.padstacks.instances) == 2
584+
edbapp.close(terminate_rpc_session=False)
564585

565586

566587
def _get_padstack_polygon_data(edb, padstack_instance: EDBPadstackInstance, layer_name: str) -> PolygonData:

0 commit comments

Comments
 (0)