masque/masque/test/test_gdsii_lazy.py

230 lines
7.1 KiB
Python

from pathlib import Path
import numpy
import pytest
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
def _make_lazy_port_library() -> Library:
lib = Library()
leaf = Pattern()
leaf.label(layer=(10, 0), string='A:type1 0', offset=(5, 0))
lib['leaf'] = leaf
child = Pattern()
child.ref('leaf', offset=(10, 20), rotation=numpy.pi / 2)
lib['child'] = child
top = Pattern()
top.ref('child', offset=(100, 200))
lib['top'] = top
return lib
def test_gdsii_lazy_source_exposes_order_and_graph_without_materializing(tmp_path: Path) -> None:
gds_file = tmp_path / 'lazy_source.gds'
src = _make_lazy_port_library()
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-lazy')
lib, info = gdsii_lazy.readfile(gds_file)
assert info['name'] == 'classic-lazy'
assert lib.source_order() == ('leaf', 'child', 'top')
assert lib.child_graph(dangling='ignore') == {
'leaf': set(),
'child': {'leaf'},
'top': {'child'},
}
assert not lib._cache
child = lib['child']
assert list(child.refs.keys()) == ['leaf']
assert set(lib._cache) == {'child'}
def test_gdsii_lazy_ports_view_keeps_raw_source_unmodified(tmp_path: Path) -> None:
gds_file = tmp_path / 'lazy_ports.gds'
src = _make_lazy_port_library()
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-ports')
raw, _ = gdsii_lazy.readfile(gds_file)
processed = raw.with_ports_from_data(layers=[(10, 0)], max_depth=2)
top = processed['top']
assert set(top.ports) == {'A'}
assert_allclose(top.ports['A'].offset, [110, 225], atol=1e-10)
assert not raw._cache
raw_top = raw['top']
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()
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-overlay')
raw, _ = gdsii_lazy.readfile(gds_file)
processed = raw.with_ports_from_data(layers=[(10, 0)], max_depth=2)
overlay = OverlayLibrary()
overlay.add_source(processed)
assert not raw._cache
assert not processed._cache
abstract = overlay.abstract('top')
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()
rename_map = overlay.add_source(
src,
rename_theirs=lambda _lib, name: f'mapped_{name}',
rename_when='always',
)
assert rename_map == {
'leaf': 'mapped_leaf',
'child': 'mapped_child',
'top': 'mapped_top',
}
assert tuple(overlay.keys()) == ('mapped_leaf', 'mapped_child', 'mapped_top')
assert 'mapped_leaf' in overlay['mapped_child'].refs
def test_gdsii_lazy_overlay_add_source_rename_when_validation() -> None:
src = _make_lazy_port_library()
with pytest.raises(TypeError, match='rename_theirs'):
OverlayLibrary().add_source(src, rename_when='always')
with pytest.raises(ValueError, match='rename mode'):
OverlayLibrary().add_source(src, rename_when='sometimes') # type: ignore[arg-type]
def test_gdsii_lazy_processed_write_roundtrips_without_explicit_units(tmp_path: Path) -> None:
gds_file = tmp_path / 'lazy_roundtrip.gds'
src = _make_lazy_port_library()
gdsii.writefile(src, gds_file, meters_per_unit=1e-9, library_name='classic-roundtrip')
raw, _ = gdsii_lazy.readfile(gds_file)
processed = raw.with_ports_from_data(layers=[(10, 0)], max_depth=2)
out_file = tmp_path / 'lazy_roundtrip_out.gds'
gdsii_lazy.writefile(processed, out_file)
assert out_file.read_bytes() == gds_file.read_bytes()
def test_gdsii_removed_closure_based_lazy_loader() -> None:
assert not hasattr(gdsii, 'load_library')
assert not hasattr(gdsii, 'load_libraryfile')