[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
|
from ...utils import apply_transforms
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Iterator, Sequence
|
from collections.abc import Iterator, Mapping, Sequence
|
||||||
|
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
from ...pattern import Pattern
|
from ...pattern import Pattern
|
||||||
|
from ...ports import Port
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -284,12 +285,28 @@ class GdsLibrarySource(ILibraryView):
|
||||||
layers: Sequence[tuple[int, int] | int],
|
layers: Sequence[tuple[int, int] | int],
|
||||||
max_depth: int = 0,
|
max_depth: int = 0,
|
||||||
skip_subcells: bool = True,
|
skip_subcells: bool = True,
|
||||||
|
ports: Mapping[str, Mapping[str, Port]] | None = None,
|
||||||
|
replace: bool = False,
|
||||||
) -> PortsLibraryView:
|
) -> PortsLibraryView:
|
||||||
return PortsLibraryView(
|
return PortsLibraryView(
|
||||||
self,
|
self,
|
||||||
layers=layers,
|
layers=layers,
|
||||||
max_depth=max_depth,
|
max_depth=max_depth,
|
||||||
skip_subcells=skip_subcells,
|
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(
|
def find_refs_local(
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,13 @@ from ...library import (
|
||||||
from ...utils import apply_transforms
|
from ...utils import apply_transforms
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Iterator, Sequence
|
from collections.abc import Iterator, Mapping, Sequence
|
||||||
|
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
import pyarrow
|
import pyarrow
|
||||||
|
|
||||||
from ...pattern import Pattern
|
from ...pattern import Pattern
|
||||||
|
from ...ports import Port
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
@ -407,12 +408,28 @@ class ArrowLibrary(ILibraryView):
|
||||||
layers: Sequence[tuple[int, int] | int],
|
layers: Sequence[tuple[int, int] | int],
|
||||||
max_depth: int = 0,
|
max_depth: int = 0,
|
||||||
skip_subcells: bool = True,
|
skip_subcells: bool = True,
|
||||||
|
ports: Mapping[str, Mapping[str, Port]] | None = None,
|
||||||
|
replace: bool = False,
|
||||||
) -> PortsLibraryView:
|
) -> PortsLibraryView:
|
||||||
return PortsLibraryView(
|
return PortsLibraryView(
|
||||||
self,
|
self,
|
||||||
layers=layers,
|
layers=layers,
|
||||||
max_depth=max_depth,
|
max_depth=max_depth,
|
||||||
skip_subcells=skip_subcells,
|
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:
|
def close(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
|
|
||||||
|
from ..ports import Port
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class _SourceLayer:
|
class _SourceLayer:
|
||||||
|
|
@ -47,7 +49,7 @@ def _materialize_detached_pattern(view: ILibraryView, name: str) -> Pattern:
|
||||||
|
|
||||||
class PortsLibraryView(ILibraryView):
|
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
|
The wrapped source remains untouched; this view owns a separate processed
|
||||||
cache so direct-copy workflows can continue to use the raw source view.
|
cache so direct-copy workflows can continue to use the raw source view.
|
||||||
|
|
@ -61,14 +63,21 @@ class PortsLibraryView(ILibraryView):
|
||||||
self,
|
self,
|
||||||
source: ILibraryView,
|
source: ILibraryView,
|
||||||
*,
|
*,
|
||||||
layers: Sequence[layer_t],
|
layers: Sequence[layer_t] = (),
|
||||||
max_depth: int = 0,
|
max_depth: int = 0,
|
||||||
skip_subcells: bool = True,
|
skip_subcells: bool = True,
|
||||||
|
ports: Mapping[str, Mapping[str, Port]] | None = None,
|
||||||
|
replace: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._source = source
|
self._source = source
|
||||||
self._layers = tuple(layers)
|
self._layers = tuple(layers)
|
||||||
self._max_depth = max_depth
|
self._max_depth = max_depth
|
||||||
self._skip_subcells = skip_subcells
|
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._cache: dict[str, Pattern] = {}
|
||||||
self._lookups_in_progress: list[str] = []
|
self._lookups_in_progress: list[str] = []
|
||||||
if hasattr(source, 'library_info'):
|
if hasattr(source, 'library_info'):
|
||||||
|
|
@ -103,14 +112,21 @@ class PortsLibraryView(ILibraryView):
|
||||||
self._lookups_in_progress.append(name)
|
self._lookups_in_progress.append(name)
|
||||||
try:
|
try:
|
||||||
pat = _materialize_detached_pattern(self._source, name)
|
pat = _materialize_detached_pattern(self._source, name)
|
||||||
pat = data_to_ports(
|
if self._layers:
|
||||||
layers=self._layers,
|
pat = data_to_ports(
|
||||||
library=self,
|
layers=self._layers,
|
||||||
pattern=pat,
|
library=self,
|
||||||
name=name,
|
pattern=pat,
|
||||||
max_depth=self._max_depth,
|
name=name,
|
||||||
skip_subcells=self._skip_subcells,
|
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:
|
finally:
|
||||||
self._lookups_in_progress.pop()
|
self._lookups_in_progress.pop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from numpy.testing import assert_allclose
|
||||||
from ..file import gdsii
|
from ..file import gdsii
|
||||||
from ..file.gdsii import lazy as gdsii_lazy
|
from ..file.gdsii import lazy as gdsii_lazy
|
||||||
from ..pattern import Pattern
|
from ..pattern import Pattern
|
||||||
|
from ..ports import Port
|
||||||
from ..library import Library, OverlayLibrary
|
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
|
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:
|
def test_gdsii_lazy_overlay_add_source_stays_lazy_for_processed_view(tmp_path: Path) -> None:
|
||||||
gds_file = tmp_path / 'lazy_overlay.gds'
|
gds_file = tmp_path / 'lazy_overlay.gds'
|
||||||
src = _make_lazy_port_library()
|
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'}
|
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:
|
def test_gdsii_lazy_overlay_add_source_can_rename_every_source_cell() -> None:
|
||||||
src = _make_lazy_port_library()
|
src = _make_lazy_port_library()
|
||||||
overlay = OverlayLibrary()
|
overlay = OverlayLibrary()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue