Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

raise warning with uncached cells #1381

Merged
merged 2 commits into from Mar 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 29 additions & 26 deletions gdsfactory/component.py
Expand Up @@ -55,6 +55,14 @@
Axis = Literal["x", "y"]


class UncachedComponentWarning(UserWarning):
pass


class UncachedComponentError(ValueError):
pass


class MutabilityError(ValueError):
pass

Expand Down Expand Up @@ -1649,6 +1657,7 @@ def _write_library(
None: do not try to resolve (at your own risk!)
flatten_invalid_refs: flattens component references which have invalid transformations.
max_points: Maximal number of vertices per polygon. Polygons with more vertices that this are automatically fractured.

Oasis settings:
compression_level: Level of compression for cells (between 0 and 9).
Setting to 0 will disable cell compression, 1 gives the best speed and 9, the best compression.
Expand Down Expand Up @@ -1684,6 +1693,18 @@ def _write_library(
write_settings = default_settings.copy(update=explicit_gds_settings)
oasis_settings = default_oasis_settings.copy(update=explicit_oas_settings)

for component in self.get_dependencies(recursive=True):
if not component._locked:
message = (
f"Component {component.name!r} was NOT properly locked. "
"You need to write it into a function that has the @cell decorator."
)
if write_settings.on_uncached_component == "warn":
warnings.warn(message, UncachedComponentWarning)

elif write_settings.on_uncached_component == "error":
raise UncachedComponentError(message)

if write_settings.flatten_invalid_refs:
top_cell = flatten_invalid_refs_recursive(self)
else:
Expand Down Expand Up @@ -1759,57 +1780,45 @@ def write_gds(
self,
gdspath: Optional[PathType] = None,
gdsdir: Optional[PathType] = None,
unit: Optional[float] = None,
precision: Optional[float] = None,
logging: bool = True,
on_duplicate_cell: Optional[str] = None,
flatten_invalid_refs: Optional[bool] = None,
max_points: Optional[int] = None,
**kwargs,
) -> Path:
"""Write component to GDS and returns gdspath.

Args:
gdspath: GDS file path to write to.
gdsdir: directory for the GDS file. Defaults to /tmp/randomFile/gdsfactory.

Keyword Args:
unit: unit size for objects in library. 1um by default.
precision: for dimensions in the library (m). 1nm by default.
logging: disable GDS path logging, for example for showing it in KLayout.
on_duplicate_cell: specify how to resolve duplicate-named cells. Choose one of the following:
"warn" (default): overwrite all duplicate cells with one of the duplicates (arbitrarily).
"error": throw a ValueError when attempting to write a gds with duplicate cells.
"overwrite": overwrite all duplicate cells with one of the duplicates, without warning.
on_uncached_component: Literal["warn", "error"] = "warn"
flatten_invalid_refs: flattens component references which have invalid transformations.
max_points: Maximal number of vertices per polygon.
Polygons with more vertices that this are automatically fractured.
"""

return self._write_library(
gdspath=gdspath,
gdsdir=gdsdir,
unit=unit,
precision=precision,
logging=logging,
on_duplicate_cell=on_duplicate_cell,
flatten_invalid_refs=flatten_invalid_refs,
max_points=max_points,
gdspath=gdspath, gdsdir=gdsdir, with_oasis=False, **kwargs
)

def write_oas(
self,
gdspath: Optional[PathType] = None,
gdsdir: Optional[PathType] = None,
unit: Optional[float] = None,
precision: Optional[float] = None,
logging: bool = True,
on_duplicate_cell: Optional[str] = "warn",
flatten_invalid_refs: Optional[bool] = None,
**kwargs,
) -> Path:
"""Write component to GDS and returns gdspath.

Args:
gdspath: GDS file path to write to.
gdsdir: directory for the GDS file. Defaults to /tmp/randomFile/gdsfactory.

Keyword Args:
unit: unit size for objects in library. 1um by default.
precision: for dimensions in the library (m). 1nm by default.
logging: disable GDS path logging, for example for showing it in KLayout.
Expand All @@ -1818,9 +1827,8 @@ def write_oas(
"error": throw a ValueError when attempting to write a gds with duplicate cells.
"overwrite": overwrite all duplicate cells with one of the duplicates, without warning.
None: do not try to resolve (at your own risk!)
on_uncached_component: Literal["warn", "error"] = "warn"
flatten_invalid_refs: flattens component references which have invalid transformations.

Keyword Args:
compression_level: Level of compression for cells (between 0 and 9).
Setting to 0 will disable cell compression, 1 gives the best speed and 9, the best compression.
detect_rectangles: Store rectangles in compressed format.
Expand All @@ -1833,12 +1841,7 @@ def write_oas(
return self._write_library(
gdspath=gdspath,
gdsdir=gdsdir,
unit=unit,
precision=precision,
logging=logging,
on_duplicate_cell=on_duplicate_cell,
with_oasis=True,
flatten_invalid_refs=flatten_invalid_refs,
**kwargs,
)

Expand Down
11 changes: 11 additions & 0 deletions gdsfactory/config.py
Expand Up @@ -14,6 +14,7 @@

from __future__ import annotations

import warnings
import sys
import io
import json
Expand Down Expand Up @@ -48,6 +49,16 @@
logger.remove()
logger.add(sink=sys.stderr, level="INFO")

showwarning_ = warnings.showwarning


def showwarning(message, *args, **kwargs):
logger.warning(message)
showwarning_(message, *args, **kwargs)


warnings.showwarning = showwarning


def print_version():
"""Print gdsfactory version and install directory."""
Expand Down
2 changes: 2 additions & 0 deletions gdsfactory/pdk.py
Expand Up @@ -5,6 +5,7 @@
import warnings
from functools import partial
from typing import Any, Callable, Optional, Union, Tuple
from typing_extensions import Literal

import numpy as np
from omegaconf import DictConfig
Expand Down Expand Up @@ -51,6 +52,7 @@
class GdsWriteSettings(BaseModel):
"""Settings to use when writing to GDS."""

on_uncached_component: Literal["warn", "error"] = "warn"
unit: float = Field(
default=1e-6,
description="The units of coordinates in the database. The default is 1e-6 (1 micron).",
Expand Down
56 changes: 56 additions & 0 deletions tests/test_uncached_component.py
@@ -0,0 +1,56 @@
from __future__ import annotations

import pytest

import gdsfactory as gf
from gdsfactory.component import UncachedComponentWarning, UncachedComponentError


@gf.cell
def dangerous_intermediate_cells(width=0.5):
"""Example that will show the dangers of using intermediate cells."""
c = gf.Component("safe")
c2 = gf.Component(
"dangerous"
) # This should be forbidden as it will create duplicated cells
c2 << gf.components.hline(width=width)
c << c2
return c


@gf.cell
def using_dangerous_intermediate_cells():
"""Example on how things can go wrong.

Here we try to create to lines with different widths
they end up with two duplicated cells and a name collision on the intermediate cell
"""
c = gf.Component()
c << dangerous_intermediate_cells(width=0.5)
r3 = c << dangerous_intermediate_cells(width=2)
r3.movey(5)
return c


def test_uncached_component_warning():
"""Ensures that an impossible route raises UncachedComponentWarning."""
c = using_dangerous_intermediate_cells()

with pytest.warns(UncachedComponentWarning):
c.write_gds()
return c


def test_uncached_component_error():
"""Ensures that an impossible route raises UncachedComponentError."""
c = using_dangerous_intermediate_cells()

with pytest.raises(UncachedComponentError):
c.write_gds(on_uncached_component="error")
return c


if __name__ == "__main__":
# c = test_uncached_component_warning()
c = test_uncached_component_error()
c.show(show_ports=True)