diff --git a/poetry.lock b/poetry.lock index e96a935..0fbb7ed 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2553,20 +2553,20 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-extra-types" -version = "2.4.1" +version = "2.5.0" description = "Extra Pydantic types." optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_extra_types-2.4.1-py3-none-any.whl", hash = "sha256:b3cec735e471b1234a1cc05a680dc080836bab6970cab40d60dcade97fe68f5d"}, - {file = "pydantic_extra_types-2.4.1.tar.gz", hash = "sha256:63314096ca57bc1575d988d1a770d73af76aaebe684140f24333b60af4134a2c"}, + {file = "pydantic_extra_types-2.5.0-py3-none-any.whl", hash = "sha256:7346873019cac32061b471adf2cdac711664ddb7a6ede04219bed2da34888c4d"}, + {file = "pydantic_extra_types-2.5.0.tar.gz", hash = "sha256:46b85240093dc63ad4a8f3cab49e03d76ae0577e4f99e2bbff7d32f99d009bf9"}, ] [package.dependencies] pydantic = ">=2.5.2" [package.extras] -all = ["phonenumbers (>=8,<9)", "pycountry (>=23,<24)", "python-ulid (>=1,<2)"] +all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23,<24)", "python-ulid (>=1,<2)"] [[package]] name = "pydantic-settings" @@ -2696,6 +2696,20 @@ tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-datadir" +version = "1.5.0" +description = "pytest plugin for test data directories and files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-datadir-1.5.0.tar.gz", hash = "sha256:1617ed92f9afda0c877e4eac91904b5f779d24ba8f5e438752e3ae39d8d2ee3f"}, + {file = "pytest_datadir-1.5.0-py3-none-any.whl", hash = "sha256:34adf361bcc7b37961bbc1dfa8d25a4829e778bab461703c38a5c50ca9c36dc8"}, +] + +[package.dependencies] +pytest = ">=5.0" + [[package]] name = "pytest-mock" version = "3.12.0" @@ -2713,6 +2727,28 @@ pytest = ">=5.0" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[package]] +name = "pytest-regressions" +version = "2.5.0" +description = "Easy to use fixtures to write regression tests." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-regressions-2.5.0.tar.gz", hash = "sha256:818c7884c1cff3babf89eebb02cbc27b307856b1985427c24d52cb812a106fd9"}, + {file = "pytest_regressions-2.5.0-py3-none-any.whl", hash = "sha256:8c4e5c4037325fdb0825bc1fdcb75e17e03adf3407049f0cb704bb996d496255"}, +] + +[package.dependencies] +pytest = ">=6.2.0" +pytest-datadir = ">=1.2.0" +pyyaml = "*" + +[package.extras] +dataframe = ["numpy", "pandas"] +dev = ["matplotlib", "mypy", "numpy", "pandas", "pillow", "pre-commit", "restructuredtext-lint", "tox"] +image = ["numpy", "pillow"] +num = ["numpy", "pandas"] + [[package]] name = "python-dateutil" version = "2.8.2" @@ -3669,13 +3705,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "tifffile" -version = "2023.12.9" +version = "2024.1.30" description = "Read and write TIFF files" optional = false python-versions = ">=3.9" files = [ - {file = "tifffile-2023.12.9-py3-none-any.whl", hash = "sha256:9b066e4b1a900891ea42ffd33dab8ba34c537935618b9893ddef42d7d422692f"}, - {file = "tifffile-2023.12.9.tar.gz", hash = "sha256:9dd1da91180a6453018a241ff219e1905f169384355cd89c9ef4034c1b46cdb8"}, + {file = "tifffile-2024.1.30-py3-none-any.whl", hash = "sha256:40cb48f661acdfea16cb00dc8941bd642b8eb5c59bca6de6a54091bee9ee2699"}, + {file = "tifffile-2024.1.30.tar.gz", hash = "sha256:66cf1fbc3fee8f87670ffd415c1e758ea1779376bdfaa9d0dc6ce634e6bc52ea"}, ] [package.dependencies] @@ -3951,5 +3987,5 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] [metadata] lock-version = "2.0" -python-versions = ">=3.10, <=3.12" -content-hash = "ad1266e8b08dd76a18e26005b1264f2376ee81aa47ce064724f480182d42a4c4" +python-versions = ">=3.10, <=3.12.1" +content-hash = "63b47dd244477b678401047f8ef28b3194f11c9cd58994f31b17761c71b5d9d8" diff --git a/pyproject.toml b/pyproject.toml index fae7843..9b13dee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ classifiers = [ ] [tool.poetry.dependencies] -python = ">=3.10, <=3.12" +python = ">=3.10, <=3.12.1" gdsfactory = "7.10.5" [build-system] @@ -28,6 +28,7 @@ pylint = ">=2.16.0" pylint-exit = "^1.2.0" pytest = ">=7.2.2" pytest-mock = ">=3.10.0" +pytest-regressions = ">=2.5.0" mypy = "^1.7.1" [tool.poetry.group.docs] @@ -44,5 +45,8 @@ sphinx-copybutton = "^0.5.1" sphinx-last-updated-by-git = "^0.3.5" [[tool.mypy.overrides]] -module=["h5py"] +module=["gdsfactory.*"] ignore_missing_imports = true + +[tool.pytest.ini_options] +filterwarnings = ["ignore::DeprecationWarning"] diff --git a/src/qutegds/__init__.py b/src/qutegds/__init__.py index fe37047..6410a17 100644 --- a/src/qutegds/__init__.py +++ b/src/qutegds/__init__.py @@ -1,5 +1,23 @@ """qutegds module.""" - import importlib.metadata as im +import sys __version__ = im.version(__package__) + +import gdsfactory as gf +from gdsfactory.generic_tech import get_generic_pdk +from gdsfactory.get_factories import get_cells + +from qutegds.components import * + +generic_pdk = get_generic_pdk() +cells = get_cells(sys.modules[__name__]) +qute_pdk = gf.Pdk( + name="qute", + cells=cells, + base_pdk=generic_pdk, + layers=generic_pdk.layers, + layer_views=generic_pdk.layer_views, + cross_sections=generic_pdk.cross_sections, +) +qute_pdk.activate() diff --git a/src/qutegds/components.py b/src/qutegds/components.py new file mode 100644 index 0000000..0326621 --- /dev/null +++ b/src/qutegds/components.py @@ -0,0 +1,100 @@ +"""List of coplanar waveguide elements.""" +from functools import partial + +import gdsfactory as gf +from gdsfactory import Component +from gdsfactory.typings import ComponentFactory, ComponentSpec + +from qutegds.geometry import subtract + +WIDTH = 6 +GAP = 3 +WIDTH_PAD = 350 +GAP_PAD = 70 +SPACE_PAD = 10 + + +@gf.cell() +def cpw( + component_name: str = "straight", gap: float = GAP, width: float = WIDTH, **kwargs +) -> Component: + """ + Return simple coplanar waveguide from single component. + + By default, returns negative mask of the CPW trace. + + Args: + component_name (str): name of the component to be used + width (float): width of the central CPW trace + gap (float): space in um between the CPW trace and ground + """ + cpw = gf.Component() + outer = gf.get_component(component_name, width=width + 2 * gap, **kwargs) + inner = gf.get_component(component_name, width=width, **kwargs) + _ = cpw << subtract(outer, inner) + cpw.add_ports(outer.ports) + return cpw + + +snake = partial(cpw, component_name="delay_snake") + + +@gf.cell() +def straight_taper( + straight: ComponentSpec = gf.components.straight, + taper: ComponentFactory = gf.components.taper, +) -> Component: + """Return straight section connected with taper.""" + st = gf.get_component(straight) + return gf.add_tapers( + st, + taper=taper, + ports=[st["o1"]], + ) + + +@gf.cell() +def rf_port( + width1: float = WIDTH, + width2: float = WIDTH_PAD, + gap1: float = GAP, + gap2: float = GAP_PAD, + len_taper: float = 200, + len_rect: float = 100, + space_pad=SPACE_PAD, + **kwargs, +) -> Component: + """Return rf port.""" + cpw = gf.Component() + straight = partial(gf.components.straight, **kwargs) + taper = partial(gf.components.taper, length=len_taper, **kwargs) + + outer = straight_taper( + straight=partial( + straight, length=len_rect + space_pad, width=width2 + 2 * gap2 + ), + taper=partial(taper, width1=width1 + 2 * gap1, width2=width2 + 2 * gap2), + ) + inner = straight_taper( + straight=partial(straight, length=len_rect, width=width2), + taper=partial(taper, width1=width1, width2=width2), + ) + _ = cpw << subtract(outer, inner) + cpw.add_ports(outer.ports) + return cpw + + +@gf.cell() +def cpw_with_ports( + gap=GAP, width=WIDTH, length=1000, straight=cpw, launcher=rf_port +) -> Component: + """CPW with ports at extremities.""" + launch = launcher(gap1=gap, width1=width) + c = gf.Component() + lref = c << launch + lref2 = c << launch + stref = c << straight(gap=gap, width=width, length=length) + + lref.connect("o1", stref.ports["o2"]) + lref2.connect("o1", stref.ports["o1"]) + return c diff --git a/src/qutegds/geometry.py b/src/qutegds/geometry.py new file mode 100644 index 0000000..a148927 --- /dev/null +++ b/src/qutegds/geometry.py @@ -0,0 +1,6 @@ +"""Geometry related functions.""" +from functools import partial + +import gdsfactory as gf + +subtract = partial(gf.geometry.boolean, operation="not") diff --git a/tests/test_components.py b/tests/test_components.py new file mode 100644 index 0000000..aa56698 --- /dev/null +++ b/tests/test_components.py @@ -0,0 +1,44 @@ +""" +This code tests all your cells in the PDK. + +it will test 3 things: + +1. difftest: will test the GDS geometry of a new GDS compared to a reference. Thanks to Klayout fast booleans.add() +2. settings test: will compare the settings in YAML with a reference YAML file.add() +3. ensure ports are on grid, to avoid port snapping errors that can create 1nm gaps later on when you build circuits. + +""" +import pathlib + +import pytest +from gdsfactory.component import Component +from pytest_regressions.data_regression import DataRegressionFixture + +from qutegds import cells + +skip_test = {"subtract"} +cell_names = set(cells.keys()) - set(skip_test) +dirpath = pathlib.Path(__file__).absolute().parent / "gds_ref" + + +@pytest.fixture(params=cell_names, scope="function") +def component(request) -> Component: + """Return requested component.""" + return cells[request.param]() + + +# def test_pdk_gds(component: Component) -> None: +# """Avoid regressions in GDS geometry shapes and layers.""" +# difftest(component, dirpath=dirpath) + + +def test_pdk_settings( + component: Component, data_regression: DataRegressionFixture +) -> None: + """Avoid regressions when exporting settings.""" + data_regression.check(component.to_dict()) + + +def test_assert_ports_on_grid(component: Component): + """Check if ports are aligned to grid.""" + component.assert_ports_on_grid() diff --git a/tests/test_components/test_pdk_settings_cpw_.yml b/tests/test_components/test_pdk_settings_cpw_.yml new file mode 100644 index 0000000..69849f0 --- /dev/null +++ b/tests/test_components/test_pdk_settings_cpw_.yml @@ -0,0 +1,8 @@ +function: cpw +info: {} +module: qutegds.components +name: cpw +settings: + component_name: straight + gap: 3 + width: 6 diff --git a/tests/test_components/test_pdk_settings_cpw_with_ports_.yml b/tests/test_components/test_pdk_settings_cpw_with_ports_.yml new file mode 100644 index 0000000..dbed8c7 --- /dev/null +++ b/tests/test_components/test_pdk_settings_cpw_with_ports_.yml @@ -0,0 +1,12 @@ +function: cpw_with_ports +info: {} +module: qutegds.components +name: cpw_with_ports +settings: + gap: 3 + launcher: + function: rf_port + length: 1000 + straight: + function: cpw + width: 6 diff --git a/tests/test_components/test_pdk_settings_rf_port_.yml b/tests/test_components/test_pdk_settings_rf_port_.yml new file mode 100644 index 0000000..6528337 --- /dev/null +++ b/tests/test_components/test_pdk_settings_rf_port_.yml @@ -0,0 +1,12 @@ +function: rf_port +info: {} +module: qutegds.components +name: rf_port +settings: + gap1: 3 + gap2: 70 + len_rect: 100 + len_taper: 200 + space_pad: 10 + width1: 6 + width2: 350 diff --git a/tests/test_components/test_pdk_settings_snake_.yml b/tests/test_components/test_pdk_settings_snake_.yml new file mode 100644 index 0000000..c5cb073 --- /dev/null +++ b/tests/test_components/test_pdk_settings_snake_.yml @@ -0,0 +1,8 @@ +function: cpw +info: {} +module: qutegds.components +name: cpw_component_namedelay_snake +settings: + component_name: delay_snake + gap: 3 + width: 6 diff --git a/tests/test_components/test_pdk_settings_straight_taper_.yml b/tests/test_components/test_pdk_settings_straight_taper_.yml new file mode 100644 index 0000000..9fee485 --- /dev/null +++ b/tests/test_components/test_pdk_settings_straight_taper_.yml @@ -0,0 +1,15 @@ +function: straight_taper +info: + length: 10.0 + route_info_length: 10.0 + route_info_type: xs_sc + route_info_weight: 10.0 + route_info_xs_sc_length: 10.0 + width: 0.5 +module: qutegds.components +name: straight_taper +settings: + straight: + function: straight + taper: + function: taper