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

Move VKCell state to TVCell #607

Merged
merged 2 commits into from
Feb 10, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/kfactory/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

if TYPE_CHECKING:
from .instance import ProtoInstance
from .kcell import AnyKCell
from .kcell import AnyKCell, BaseKCell
from .layout import KCLayout
from .port import ProtoPort

Expand All @@ -22,7 +22,7 @@
class LockedError(AttributeError):
"""Raised when a locked cell is being modified."""

def __init__(self, kcell: AnyKCell) -> None:
def __init__(self, kcell: AnyKCell | BaseKCell) -> None:
"""Throw _locked error."""
super().__init__(
f"{kcell.name!r} is locked and likely stored in cache. Modifications are "
Expand Down
4 changes: 2 additions & 2 deletions src/kfactory/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ def insert_into(
_cell_name = get_cell_name(_cell_name + clean_name(_trans_str))
if cell.kcl.layout_cell(_cell_name) is None:
_cell = KCell(kcl=self.cell.kcl, name=_cell_name) # self.cell.dup()
for layer, shapes in self.cell._shapes.items():
for layer, shapes in self.cell.shapes().items():
for shape in shapes.transform(_trans):
_cell.shapes(layer).insert(shape)
for inst in self.cell.insts:
Expand Down Expand Up @@ -782,7 +782,7 @@ def insert_into_flat(
trans = kdb.DCplxTrans()

if isinstance(self.cell, VKCell):
for layer, shapes in self.cell._shapes.items():
for layer, shapes in self.cell.shapes().items():
for shape in shapes.transform(trans * self.trans):
cell.shapes(layer).insert(shape)
for inst in self.cell.insts:
Expand Down
120 changes: 63 additions & 57 deletions src/kfactory/kcell.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,17 @@ def locked(self) -> bool:
@abstractmethod
def locked(self, value: bool) -> None: ...

@abstractmethod
def lock(self) -> None:
"""Lock the cell."""
...
self.locked = True

@property
@abstractmethod
def name(self) -> str | None: ...

@name.setter
@abstractmethod
def name(self, value: str) -> None: ...


class ProtoKCell(GeometricObject[TUnit], Generic[TUnit, TBaseCell], ABC):
Expand All @@ -188,12 +195,12 @@ def lock(self) -> None:
self._base.lock()

@property
@abstractmethod
def name(self) -> str | None: ...
def name(self) -> str | None:
return self._base.name

@name.setter
@abstractmethod
def name(self, value: str) -> None: ...
def name(self, value: str) -> None:
self._base.name = value

@abstractmethod
def dup(self) -> Self: ...
Expand Down Expand Up @@ -404,15 +411,24 @@ def locked(self) -> bool:
def locked(self, value: bool) -> None:
self.kdb_cell.locked = value

def lock(self) -> None:
self.kdb_cell.locked = True

def __repr__(self) -> str:
return f"{self.__class__.__name__}(name={self.kdb_cell.name})"

@property
def name(self) -> str:
return self.kdb_cell.name

@name.setter
def name(self, value: str) -> None:
if self.locked:
raise LockedError(self)
self.kdb_cell.name = value


class TVCell(BaseKCell):
_locked: bool = PrivateAttr(default=False)
shapes: dict[int, VShapes] = Field(default_factory=dict)
_name: str | None = PrivateAttr(default=None)

@property
def locked(self) -> bool:
Expand All @@ -422,8 +438,13 @@ def locked(self) -> bool:
def locked(self, value: bool) -> None:
self._locked = value

def lock(self) -> None:
self._locked = True
@property
def name(self) -> str | None:
return self._name

@name.setter
def name(self, value: str) -> None:
self._name = value


class ProtoTKCell(ProtoKCell[TUnit, TKCell], Generic[TUnit], ABC):
Expand Down Expand Up @@ -468,31 +489,23 @@ def __getitem__(self, key: int | str | None) -> ProtoPort[TUnit]:
"""Returns port from instance."""
...

@property
def name(self) -> str:
return self._base.name

@name.setter
def name(self, value: str) -> None:
self._base.name = value

def __hash__(self) -> int:
"""Hash the KCell."""
return hash(
(
self._base.kcl.library.name(),
self._base.kdb_cell.cell_index(),
)
)
return hash((self._base.kcl.library.name(), self._base.kdb_cell.cell_index()))

def __eq__(self, other: object) -> bool:
if not isinstance(other, ProtoTKCell):
return False
return self._base == other._base

@property
def name(self) -> str:
"""Name of the KCell."""
return self._base.kdb_cell.name

@name.setter
def name(self, value: str) -> None:
if self.locked:
raise LockedError(self)
self._base.kdb_cell.name = value

@property
def prop_id(self) -> int:
"""Gets the properties ID associated with the cell."""
Expand Down Expand Up @@ -2706,9 +2719,6 @@ def get_cross_section(
class VKCell(ProtoKCell[float, TVCell], UMGeometricObject):
"""Emulate `[klayout.db.Cell][klayout.db.Cell]`."""

_shapes: dict[int, VShapes]
_name: str | None

@overload
def __init__(self, *, base: TVCell) -> None: ...

Expand All @@ -2733,10 +2743,9 @@ def __init__(
) -> None:
from .layout import get_default_kcl

self._shapes = {}
if base is not None:
self._base = base
self._name = base.function_name
self.base.name = base.function_name
else:
kcl_ = kcl or get_default_kcl()
self._base = TVCell(
Expand All @@ -2745,18 +2754,17 @@ def __init__(
settings=KCellSettings(**(settings or {})),
vinsts=VInstances(),
)
self._name = name
self._base.name = name

def ibbox(self, layer: int | None = None) -> kdb.Box:
return self.dbbox(layer).to_itype(self.kcl.dbu)

def transform(
self,
trans: kdb.Trans | kdb.DTrans | kdb.ICplxTrans | kdb.DCplxTrans,
/,
self, trans: kdb.Trans | kdb.DTrans | kdb.ICplxTrans | kdb.DCplxTrans, /
) -> None:
for key, vshape in self._shapes.items():
self._shapes[key] = vshape.transform(trans)
_shapes = self.base.shapes
for key, vshape in _shapes.items():
_shapes[key] = vshape.transform(trans)

@property
def ports(self) -> DPorts:
Expand All @@ -2770,12 +2778,13 @@ def ports(self, new_ports: Iterable[ProtoPort[Any]]) -> None:
self._base.ports = [port.base for port in new_ports]

def dbbox(self, layer: int | LayerEnum | None = None) -> kdb.DBox:
layers_ = set(self._shapes.keys())
layers_ = set(self.shapes().keys())

layers = layers_ if layer is None else {layer} & layers_

box = kdb.DBox()
for _layer in layers:
if isinstance(_layer, LayerEnum):
_layer = _layer.layout.layer(_layer.layer, _layer.datatype)
box += self.shapes(_layer).bbox()

for vinst in self.insts:
Expand All @@ -2791,17 +2800,6 @@ def __getitem__(self, key: int | str | None) -> DPort:
def insts(self) -> VInstances:
return self._base.vinsts

@property
def name(self) -> str | None:
"""Name of the KCell."""
return self._name

@name.setter
def name(self, value: str) -> None:
if self.locked:
raise LockedError(self)
self._name = value

def dup(self) -> VKCell:
"""Copy the full cell.

Expand All @@ -2817,7 +2815,7 @@ def dup(self) -> VKCell:
c.settings = self.settings.model_copy()
c.settings_units = self.settings_units.model_copy()
c.info = self._base.info.model_copy()
for layer, shapes in self._shapes.items():
for layer, shapes in self.shapes().items():
for shape in shapes:
c.shapes(layer).insert(shape)

Expand Down Expand Up @@ -2965,12 +2963,20 @@ def create_vinst(self, cell: AnyKCell) -> VInstance:
self.vinsts.append(vi)
return vi

def shapes(self, layer: int | kdb.LayerInfo) -> VShapes:
@overload
def shapes(self, layer: None = ...) -> dict[int, VShapes]: ...
@overload
def shapes(self, layer: int | kdb.LayerInfo) -> VShapes: ...
def shapes(
self, layer: int | kdb.LayerInfo | None = None
) -> VShapes | dict[int, VShapes]:
if layer is None:
return self._base.shapes
if isinstance(layer, kdb.LayerInfo):
layer = self.kcl.layout.layer(layer)
if layer not in self._shapes:
self._shapes[layer] = VShapes(cell=self)
return self._shapes[layer]
if layer not in self._base.shapes:
self._base.shapes[layer] = VShapes(cell=self)
return self._base.shapes[layer]

def flatten(self) -> None:
if self.locked:
Expand Down
Loading