Source code for semi_cr.core.lab.gdsfile

import sys
from collections.abc import Iterator

import gdspy

from semi_cr.assets import get_sample_gds_path


[docs] def is_gate_polygon(polygon: gdspy.Polygon) -> bool: return polygon.properties.get(1) is not None
[docs] class GDSGate: name: str polygon: list[tuple[float, float]] def __init__(self, name: str, polygon: list[tuple[float, float]]): self.name = name self.polygon = polygon def __repr__(self): return f"<Gate: {self.name}, {len(self.polygon)} vertices>"
[docs] def sort_gate_names(names: list[str]) -> list[str]: """Sort gate names: measurement plungers, numbered plungers, barriers, sensors. Examples: >>> sort_gate_names(['B2', 'P1', 'PN', 'B1', 'P2', 'PE', 'S1']) ['PN', 'PE', 'P1', 'P2', 'B1', 'B2', 'S1'] >>> sort_gate_names(['P10', 'P2', 'P1']) ['P1', 'P2', 'P10'] """ MEASUREMENT_PLUNGERS = ["PN", "PE", "PS", "PW"] # Fixed order def sort_key(name: str) -> tuple: # Measurement plungers first (group 0) if name in MEASUREMENT_PLUNGERS: return (0, MEASUREMENT_PLUNGERS.index(name), 0) # Numbered plungers (group 1) if name.startswith("P") and name[1:].isdigit(): return (1, int(name[1:]), 0) # Barriers (group 2) if name.startswith("B") and name[1:].isdigit(): return (2, int(name[1:]), 0) # Sensors (group 3) if name.startswith("S") and name[1:].isdigit(): return (3, int(name[1:]), 0) # Unknown gates last (group 4), alphabetically return (4, 0, name) return sorted(names, key=sort_key)
[docs] class GDSFile: """Loads a GDS2 file, and can output the polygons in a neutral format TODO: Improve validation (and write test cases) - gdspy.Polygon may include more than one actual polygon, enforce that properly (and write a test case) - We do not allow non-unique gate names. """ _gdsii: gdspy.GdsLibrary def __init__(self, path: str | None = None): self.path = path or get_sample_gds_path() self._gdsii = gdspy.GdsLibrary(infile=self.path)
[docs] def get_gates(self) -> Iterator[GDSGate]: for poly in filter(is_gate_polygon, self._gdsii.cells["main_fo"].polygons): name = poly.properties[1] assert len(poly.polygons) == 1 coords = poly.polygons[0] points = [] for x in coords: points.append(tuple(x)) yield GDSGate(name, points)
[docs] def get_gate_names(self) -> list[str]: """Return gate names in domain-appropriate order. Order: measurement plungers (PN, PE, PS, PW) -> numbered plungers -> barriers -> sensors """ names = [gate.name for gate in self.get_gates()] return sort_gate_names(names)
[docs] def get_bbox(self) -> tuple[tuple[float, float], tuple[float, float]]: xmin, ymin = sys.float_info.max, sys.float_info.max xmax, ymax = sys.float_info.min, sys.float_info.min for gate in self.get_gates(): for x, y in gate.polygon: xmin = min(xmin, x) xmax = max(xmax, x) ymin = min(ymin, y) ymax = max(ymax, y) return ((xmin, ymin), (xmax, ymax))
if __name__ == "__main__": g = GDSFile() for gate in g.get_gates(): print(gate)