masque/masque/test/test_gdsii.py

80 lines
2.5 KiB
Python
Raw Normal View History

2026-02-15 12:36:13 -08:00
from pathlib import Path
2026-02-16 17:58:34 -08:00
from typing import cast
import numpy
import pytest
from numpy.testing import assert_equal, assert_allclose
from ..error import LibraryError
from ..pattern import Pattern
from ..library import Library
from ..file import gdsii
2026-02-16 17:58:34 -08:00
from ..shapes import Path as MPath, Polygon
2026-02-15 12:36:13 -08:00
def test_gdsii_roundtrip(tmp_path: Path) -> None:
lib = Library()
2026-02-15 12:36:13 -08:00
# Simple polygon cell
pat1 = Pattern()
pat1.polygon((1, 0), vertices=[[0, 0], [10, 0], [10, 10], [0, 10]])
lib["poly_cell"] = pat1
2026-02-15 12:36:13 -08:00
# Path cell
pat2 = Pattern()
pat2.path((2, 5), vertices=[[0, 0], [100, 0]], width=10)
lib["path_cell"] = pat2
2026-02-15 12:36:13 -08:00
# Cell with Ref
pat3 = Pattern()
2026-02-15 12:36:13 -08:00
pat3.ref("poly_cell", offset=(50, 50), rotation=numpy.pi / 2)
lib["ref_cell"] = pat3
2026-02-15 12:36:13 -08:00
gds_file = tmp_path / "test.gds"
gdsii.writefile(lib, gds_file, meters_per_unit=1e-9)
2026-02-15 12:36:13 -08:00
read_lib, info = gdsii.readfile(gds_file)
2026-02-15 12:36:13 -08:00
assert "poly_cell" in read_lib
assert "path_cell" in read_lib
assert "ref_cell" in read_lib
2026-02-15 12:36:13 -08:00
# Check polygon
2026-02-16 17:58:34 -08:00
read_poly = cast("Polygon", read_lib["poly_cell"].shapes[(1, 0)][0])
# GDSII closes polygons, so it might have an extra vertex or different order
assert len(read_poly.vertices) >= 4
# Check bounds as a proxy for geometry correctness
assert_equal(read_lib["poly_cell"].get_bounds(), [[0, 0], [10, 10]])
2026-02-15 12:36:13 -08:00
# Check path
2026-02-16 17:58:34 -08:00
read_path = cast("MPath", read_lib["path_cell"].shapes[(2, 5)][0])
assert isinstance(read_path, MPath)
assert read_path.width == 10
assert_equal(read_path.vertices, [[0, 0], [100, 0]])
2026-02-15 12:36:13 -08:00
# Check Ref
read_ref = read_lib["ref_cell"].refs["poly_cell"][0]
assert_equal(read_ref.offset, [50, 50])
2026-02-15 12:36:13 -08:00
assert_allclose(read_ref.rotation, numpy.pi / 2, atol=1e-5)
2026-02-15 12:36:13 -08:00
def test_gdsii_annotations(tmp_path: Path) -> None:
lib = Library()
pat = Pattern()
# GDS only supports integer keys in range [1, 126] for properties
pat.polygon((1, 0), vertices=[[0, 0], [1, 0], [1, 1]], annotations={"1": ["hello"]})
lib["cell"] = pat
2026-02-15 12:36:13 -08:00
gds_file = tmp_path / "test_ann.gds"
gdsii.writefile(lib, gds_file, meters_per_unit=1e-9)
2026-02-15 12:36:13 -08:00
read_lib, _ = gdsii.readfile(gds_file)
read_ann = read_lib["cell"].shapes[(1, 0)][0].annotations
2026-02-16 17:58:34 -08:00
assert read_ann is not None
assert read_ann["1"] == ["hello"]
def test_gdsii_check_valid_names_validates_generator_lengths() -> None:
names = (name for name in ("a" * 40,))
with pytest.raises(LibraryError, match="invalid names"):
gdsii.check_valid_names(names)