[PortsLibraryView] now accepts overrides
This commit is contained in:
parent
520f37aa29
commit
7083edc7ca
4 changed files with 160 additions and 12 deletions
|
|
@ -42,11 +42,12 @@ from ...library import (
|
|||
from ...utils import apply_transforms
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterator, Sequence
|
||||
from collections.abc import Iterator, Mapping, Sequence
|
||||
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ...pattern import Pattern
|
||||
from ...ports import Port
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -284,12 +285,28 @@ class GdsLibrarySource(ILibraryView):
|
|||
layers: Sequence[tuple[int, int] | int],
|
||||
max_depth: int = 0,
|
||||
skip_subcells: bool = True,
|
||||
ports: Mapping[str, Mapping[str, Port]] | None = None,
|
||||
replace: bool = False,
|
||||
) -> PortsLibraryView:
|
||||
return PortsLibraryView(
|
||||
self,
|
||||
layers=layers,
|
||||
max_depth=max_depth,
|
||||
skip_subcells=skip_subcells,
|
||||
ports=ports,
|
||||
replace=replace,
|
||||
)
|
||||
|
||||
def with_port_overrides(
|
||||
self,
|
||||
ports: Mapping[str, Mapping[str, Port]],
|
||||
*,
|
||||
replace: bool = False,
|
||||
) -> PortsLibraryView:
|
||||
return PortsLibraryView(
|
||||
self,
|
||||
ports=ports,
|
||||
replace=replace,
|
||||
)
|
||||
|
||||
def find_refs_local(
|
||||
|
|
|
|||
|
|
@ -28,12 +28,13 @@ from ...library import (
|
|||
from ...utils import apply_transforms
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Iterator, Sequence
|
||||
from collections.abc import Iterator, Mapping, Sequence
|
||||
|
||||
from numpy.typing import NDArray
|
||||
import pyarrow
|
||||
|
||||
from ...pattern import Pattern
|
||||
from ...ports import Port
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -407,12 +408,28 @@ class ArrowLibrary(ILibraryView):
|
|||
layers: Sequence[tuple[int, int] | int],
|
||||
max_depth: int = 0,
|
||||
skip_subcells: bool = True,
|
||||
ports: Mapping[str, Mapping[str, Port]] | None = None,
|
||||
replace: bool = False,
|
||||
) -> PortsLibraryView:
|
||||
return PortsLibraryView(
|
||||
self,
|
||||
layers=layers,
|
||||
max_depth=max_depth,
|
||||
skip_subcells=skip_subcells,
|
||||
ports=ports,
|
||||
replace=replace,
|
||||
)
|
||||
|
||||
def with_port_overrides(
|
||||
self,
|
||||
ports: Mapping[str, Mapping[str, Port]],
|
||||
*,
|
||||
replace: bool = False,
|
||||
) -> PortsLibraryView:
|
||||
return PortsLibraryView(
|
||||
self,
|
||||
ports=ports,
|
||||
replace=replace,
|
||||
)
|
||||
|
||||
def close(self) -> None:
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ if TYPE_CHECKING:
|
|||
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from ..ports import Port
|
||||
|
||||
|
||||
@dataclass
|
||||
class _SourceLayer:
|
||||
|
|
@ -47,7 +49,7 @@ def _materialize_detached_pattern(view: ILibraryView, name: str) -> Pattern:
|
|||
|
||||
class PortsLibraryView(ILibraryView):
|
||||
"""
|
||||
Read-only view which imports ports into cells on first materialization.
|
||||
Read-only view which imports or applies ports on first materialization.
|
||||
|
||||
The wrapped source remains untouched; this view owns a separate processed
|
||||
cache so direct-copy workflows can continue to use the raw source view.
|
||||
|
|
@ -61,14 +63,21 @@ class PortsLibraryView(ILibraryView):
|
|||
self,
|
||||
source: ILibraryView,
|
||||
*,
|
||||
layers: Sequence[layer_t],
|
||||
layers: Sequence[layer_t] = (),
|
||||
max_depth: int = 0,
|
||||
skip_subcells: bool = True,
|
||||
ports: Mapping[str, Mapping[str, Port]] | None = None,
|
||||
replace: bool = False,
|
||||
) -> None:
|
||||
self._source = source
|
||||
self._layers = tuple(layers)
|
||||
self._max_depth = max_depth
|
||||
self._skip_subcells = skip_subcells
|
||||
self._ports = {
|
||||
name: copy.deepcopy(dict(cell_ports))
|
||||
for name, cell_ports in (ports or {}).items()
|
||||
}
|
||||
self._replace = replace
|
||||
self._cache: dict[str, Pattern] = {}
|
||||
self._lookups_in_progress: list[str] = []
|
||||
if hasattr(source, 'library_info'):
|
||||
|
|
@ -103,6 +112,7 @@ class PortsLibraryView(ILibraryView):
|
|||
self._lookups_in_progress.append(name)
|
||||
try:
|
||||
pat = _materialize_detached_pattern(self._source, name)
|
||||
if self._layers:
|
||||
pat = data_to_ports(
|
||||
layers=self._layers,
|
||||
library=self,
|
||||
|
|
@ -111,6 +121,12 @@ class PortsLibraryView(ILibraryView):
|
|||
max_depth=self._max_depth,
|
||||
skip_subcells=self._skip_subcells,
|
||||
)
|
||||
if name in self._ports:
|
||||
ports = copy.deepcopy(self._ports[name])
|
||||
if self._replace:
|
||||
pat.ports = ports
|
||||
else:
|
||||
pat.ports.update(ports)
|
||||
finally:
|
||||
self._lookups_in_progress.pop()
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from numpy.testing import assert_allclose
|
|||
from ..file import gdsii
|
||||
from ..file.gdsii import lazy as gdsii_lazy
|
||||
from ..pattern import Pattern
|
||||
from ..ports import Port
|
||||
from ..library import Library, OverlayLibrary
|
||||
|
||||
|
||||
|
|
@ -66,6 +67,80 @@ def test_gdsii_lazy_ports_view_keeps_raw_source_unmodified(tmp_path: Path) -> No
|
|||
assert not raw_top.ports
|
||||
|
||||
|
||||
def test_gdsii_lazy_port_overrides_without_data_stay_lazy(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'lazy_port_overrides.gds'
|
||||
src = _make_lazy_port_library()
|
||||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-overrides')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = raw.with_port_overrides({
|
||||
'top': {
|
||||
'P': Port((1, 2), rotation=0, ptype='wire'),
|
||||
},
|
||||
})
|
||||
|
||||
top = processed['top']
|
||||
assert set(top.ports) == {'P'}
|
||||
assert_allclose(top.ports['P'].offset, [1, 2], atol=1e-10)
|
||||
assert top.ports['P'].rotation == 0
|
||||
assert top.ports['P'].ptype == 'wire'
|
||||
assert not raw._cache
|
||||
|
||||
raw_top = raw['top']
|
||||
assert not raw_top.ports
|
||||
|
||||
|
||||
def test_gdsii_lazy_port_overrides_apply_after_extraction(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'lazy_ports_override_extracted.gds'
|
||||
src = _make_lazy_port_library()
|
||||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-override-extracted')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = raw.with_ports_from_data(
|
||||
layers=[(10, 0)],
|
||||
max_depth=2,
|
||||
ports={
|
||||
'top': {
|
||||
'A': Port((1, 2), rotation=numpy.pi, ptype='manual'),
|
||||
'B': Port((3, 4), rotation=None, ptype=None),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
top = processed['top']
|
||||
assert set(top.ports) == {'A', 'B'}
|
||||
assert_allclose(top.ports['A'].offset, [1, 2], atol=1e-10)
|
||||
assert top.ports['A'].rotation == numpy.pi
|
||||
assert top.ports['A'].ptype == 'manual'
|
||||
assert_allclose(top.ports['B'].offset, [3, 4], atol=1e-10)
|
||||
assert top.ports['B'].rotation is None
|
||||
assert top.ports['B'].ptype is None
|
||||
assert not raw._cache
|
||||
|
||||
|
||||
def test_gdsii_lazy_port_overrides_replace_extracted_ports(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'lazy_ports_replace.gds'
|
||||
src = _make_lazy_port_library()
|
||||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-replace-ports')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = raw.with_ports_from_data(
|
||||
layers=[(10, 0)],
|
||||
max_depth=2,
|
||||
ports={
|
||||
'top': {
|
||||
'B': Port((3, 4), rotation=None, ptype=None),
|
||||
},
|
||||
},
|
||||
replace=True,
|
||||
)
|
||||
|
||||
top = processed['top']
|
||||
assert set(top.ports) == {'B'}
|
||||
assert_allclose(top.ports['B'].offset, [3, 4], atol=1e-10)
|
||||
assert not raw._cache
|
||||
|
||||
|
||||
def test_gdsii_lazy_overlay_add_source_stays_lazy_for_processed_view(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'lazy_overlay.gds'
|
||||
src = _make_lazy_port_library()
|
||||
|
|
@ -84,6 +159,29 @@ def test_gdsii_lazy_overlay_add_source_stays_lazy_for_processed_view(tmp_path: P
|
|||
assert set(abstract.ports) == {'A'}
|
||||
|
||||
|
||||
def test_gdsii_lazy_overlay_add_source_sees_port_overrides(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'lazy_overlay_override.gds'
|
||||
src = _make_lazy_port_library()
|
||||
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-overlay-override')
|
||||
|
||||
raw, _ = gdsii_lazy.readfile(gds_file)
|
||||
processed = raw.with_port_overrides({
|
||||
'top': {
|
||||
'P': Port((1, 2), rotation=0, ptype='wire'),
|
||||
},
|
||||
})
|
||||
|
||||
overlay = OverlayLibrary()
|
||||
overlay.add_source(processed)
|
||||
|
||||
assert not raw._cache
|
||||
assert not processed._cache
|
||||
|
||||
abstract = overlay.abstract('top')
|
||||
assert set(abstract.ports) == {'P'}
|
||||
assert_allclose(abstract.ports['P'].offset, [1, 2], atol=1e-10)
|
||||
|
||||
|
||||
def test_gdsii_lazy_overlay_add_source_can_rename_every_source_cell() -> None:
|
||||
src = _make_lazy_port_library()
|
||||
overlay = OverlayLibrary()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue