[gdsii_arrow] misc correctness work
This commit is contained in:
parent
1dbf195251
commit
7487806318
2 changed files with 316 additions and 37 deletions
180
masque/test/test_gdsii_arrow.py
Normal file
180
masque/test/test_gdsii_arrow.py
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
from pathlib import Path
|
||||
|
||||
import numpy
|
||||
import pytest
|
||||
|
||||
pytest.importorskip('pyarrow')
|
||||
|
||||
from ..library import Library
|
||||
from ..pattern import Pattern
|
||||
from ..repetition import Grid
|
||||
from ..shapes import Path as MPath
|
||||
from ..file import gdsii, gdsii_arrow
|
||||
from ..file.gdsii_perf import write_fixture
|
||||
|
||||
|
||||
if not gdsii_arrow.is_available():
|
||||
pytest.skip('klamath_rs_ext shared library is not available', allow_module_level=True)
|
||||
|
||||
|
||||
def _annotations_key(annotations: dict[str, list[object]] | None) -> tuple[tuple[str, tuple[object, ...]], ...] | None:
|
||||
if annotations is None:
|
||||
return None
|
||||
return tuple(sorted((key, tuple(values)) for key, values in annotations.items()))
|
||||
|
||||
|
||||
def _coord_key(values: object) -> tuple[int, ...] | tuple[tuple[int, int], ...]:
|
||||
arr = numpy.rint(numpy.asarray(values, dtype=float)).astype(int)
|
||||
if arr.ndim == 1:
|
||||
return tuple(arr.tolist())
|
||||
return tuple(tuple(row.tolist()) for row in arr)
|
||||
|
||||
|
||||
def _shape_key(shape: object, layer: tuple[int, int]) -> list[tuple[object, ...]]:
|
||||
if isinstance(shape, MPath):
|
||||
cap_extensions = None if shape.cap_extensions is None else _coord_key(shape.cap_extensions)
|
||||
return [(
|
||||
'path',
|
||||
layer,
|
||||
_coord_key(shape.vertices),
|
||||
_coord_key(shape.offset),
|
||||
int(round(float(shape.width))),
|
||||
shape.cap.name,
|
||||
cap_extensions,
|
||||
_annotations_key(shape.annotations),
|
||||
)]
|
||||
|
||||
keys = []
|
||||
for poly in shape.to_polygons():
|
||||
keys.append((
|
||||
'polygon',
|
||||
layer,
|
||||
_coord_key(poly.vertices),
|
||||
_coord_key(poly.offset),
|
||||
_annotations_key(poly.annotations),
|
||||
))
|
||||
return keys
|
||||
|
||||
|
||||
def _ref_key(target: str, ref: object) -> tuple[object, ...]:
|
||||
repetition = None
|
||||
if ref.repetition is not None:
|
||||
repetition = (
|
||||
_coord_key(ref.repetition.a_vector),
|
||||
int(ref.repetition.a_count),
|
||||
_coord_key(ref.repetition.b_vector),
|
||||
int(ref.repetition.b_count),
|
||||
)
|
||||
return (
|
||||
target,
|
||||
_coord_key(ref.offset),
|
||||
round(float(ref.rotation), 8),
|
||||
round(float(ref.scale), 8),
|
||||
bool(ref.mirrored),
|
||||
repetition,
|
||||
_annotations_key(ref.annotations),
|
||||
)
|
||||
|
||||
|
||||
def _label_key(layer: tuple[int, int], label: object) -> tuple[object, ...]:
|
||||
return (
|
||||
layer,
|
||||
label.string,
|
||||
_coord_key(label.offset),
|
||||
_annotations_key(label.annotations),
|
||||
)
|
||||
|
||||
|
||||
def _pattern_summary(pattern: Pattern) -> dict[str, object]:
|
||||
shape_keys: list[tuple[object, ...]] = []
|
||||
for layer, shapes in pattern.shapes.items():
|
||||
for shape in shapes:
|
||||
shape_keys.extend(_shape_key(shape, layer))
|
||||
|
||||
ref_keys = [
|
||||
_ref_key(target, ref)
|
||||
for target, refs in pattern.refs.items()
|
||||
for ref in refs
|
||||
]
|
||||
|
||||
label_keys = [
|
||||
_label_key(layer, label)
|
||||
for layer, labels in pattern.labels.items()
|
||||
for label in labels
|
||||
]
|
||||
|
||||
return {
|
||||
'shapes': sorted(shape_keys),
|
||||
'refs': sorted(ref_keys),
|
||||
'labels': sorted(label_keys),
|
||||
}
|
||||
|
||||
|
||||
def _library_summary(lib: Library) -> dict[str, dict[str, object]]:
|
||||
return {name: _pattern_summary(pattern) for name, pattern in lib.items()}
|
||||
|
||||
|
||||
def _make_arrow_test_library() -> Library:
|
||||
lib = Library()
|
||||
|
||||
leaf = Pattern()
|
||||
leaf.polygon((1, 0), vertices=[[0, 0], [10, 0], [10, 10], [0, 10]], annotations={'1': ['leaf-poly']})
|
||||
leaf.polygon((1, 0), vertices=[[20, 0], [30, 0], [30, 10], [20, 10]])
|
||||
leaf.label((10, 0), string='LEAF', offset=(3, 4), annotations={'10': ['leaf-label']})
|
||||
lib['leaf'] = leaf
|
||||
|
||||
child = Pattern()
|
||||
child.path(
|
||||
(2, 0),
|
||||
vertices=[[0, 0], [15, 5], [30, 5]],
|
||||
width=6,
|
||||
cap=MPath.Cap.SquareCustom,
|
||||
cap_extensions=(2, 4),
|
||||
annotations={'2': ['child-path']},
|
||||
)
|
||||
child.label((11, 0), string='CHILD', offset=(7, 8), annotations={'11': ['child-label']})
|
||||
child.ref('leaf', offset=(100, 200), rotation=numpy.pi / 2, mirrored=True, scale=1.25, annotations={'12': ['child-ref']})
|
||||
lib['child'] = child
|
||||
|
||||
sibling = Pattern()
|
||||
sibling.polygon((3, 0), vertices=[[0, 0], [5, 0], [5, 6], [0, 6]])
|
||||
sibling.label((12, 0), string='SIB', offset=(1, 2), annotations={'13': ['sib-label']})
|
||||
sibling.ref(
|
||||
'leaf',
|
||||
offset=(-50, 60),
|
||||
repetition=Grid(a_vector=(20, 0), a_count=3, b_vector=(0, 30), b_count=2),
|
||||
annotations={'14': ['sib-ref']},
|
||||
)
|
||||
lib['sibling'] = sibling
|
||||
|
||||
top = Pattern()
|
||||
top.ref('child', offset=(500, 600), annotations={'15': ['top-child-ref']})
|
||||
top.ref('sibling', offset=(-100, 50), rotation=numpy.pi, annotations={'16': ['top-sibling-ref']})
|
||||
top.label((13, 0), string='TOP', offset=(0, 0), annotations={'17': ['top-label']})
|
||||
lib['top'] = top
|
||||
|
||||
return lib
|
||||
|
||||
|
||||
def test_gdsii_arrow_matches_gdsii_readfile(tmp_path: Path) -> None:
|
||||
lib = _make_arrow_test_library()
|
||||
gds_file = tmp_path / 'arrow_roundtrip.gds'
|
||||
gdsii.writefile(lib, gds_file, meters_per_unit=1e-9)
|
||||
|
||||
canonical_lib, canonical_info = gdsii.readfile(gds_file)
|
||||
arrow_lib, arrow_info = gdsii_arrow.readfile(gds_file)
|
||||
|
||||
assert canonical_info == arrow_info
|
||||
assert _library_summary(canonical_lib) == _library_summary(arrow_lib)
|
||||
|
||||
|
||||
def test_gdsii_arrow_reads_small_perf_fixture(tmp_path: Path) -> None:
|
||||
gds_file = tmp_path / 'many_cells_smoke.gds'
|
||||
manifest = write_fixture(gds_file, preset='many_cells', scale=0.001)
|
||||
|
||||
lib, info = gdsii_arrow.readfile(gds_file)
|
||||
|
||||
assert info['name'] == manifest.library_name
|
||||
assert len(lib) == manifest.cells
|
||||
assert 'TOP' in lib
|
||||
assert sum(len(refs) for refs in lib['TOP'].refs.values()) > 0
|
||||
Loading…
Add table
Add a link
Reference in a new issue