Skip to content

Commit

Permalink
Merge branch 'main' into fix-electrical-route-bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastian-goeldi authored Feb 10, 2025
2 parents 5afda31 + 19691f9 commit 07f7fcf
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 61 deletions.
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

0 comments on commit 07f7fcf

Please sign in to comment.