Refactoring, primary goal is to centralize the definition of PCellDeclarationHelper in Python

This commit is contained in:
Matthias Koefferlein 2023-03-10 23:22:30 +01:00
parent 8b7e2b2b40
commit 068849a634
10 changed files with 621 additions and 366 deletions

View File

@ -8,6 +8,10 @@
<doc>@class [db] PCellDeclarationHelper &lt; PCellDeclaration
@brief A helper class to simplify the declaration of a PCell (Python version)
NOTE: in the following, "pya" can be replaced by "klayout.db" which is
the canonical module and the preferred way of addressing the
external Python library.
This class provides adds some convenience to the PCell declaration based
on PCellDeclaration. PCellDeclaration is a C++ object which is less
convenient to use than a Ruby-based approach. In particular this class
@ -252,314 +256,7 @@ This method must return a \\Trans object. The default implementation returns a u
<interpreter>python</interpreter>
<dsl-interpreter-name/>
<text>
import pya
class _PCellDeclarationHelperLayerDescriptor(object):
"""
A descriptor object which translates the PCell parameters into class attributes
"""
def __init__(self, param_index):
self.param_index = param_index
def __get__(self, obj, type = None):
return obj._layers[self.param_index]
def __set__(self, obj, value):
raise AttributeError("can't change layer attribute")
class _PCellDeclarationHelperParameterDescriptor(object):
"""
A descriptor object which translates the PCell parameters into class attributes
In some cases (i.e. can_convert_from_shape), these placeholders are not
connected to real parameters (obj._param_values is None). In this case,
the descriptor acts as a value holder (self.value)
"""
def __init__(self, param_index, param_name):
self.param_index = param_index
self.param_name = param_name
self.value = None
def __get__(self, obj, type = None):
if obj._param_values:
return obj._param_values[self.param_index]
elif obj._param_states:
return obj._param_states.parameter(self.param_name)
else:
return self.value
def __set__(self, obj, value):
if obj._param_values:
obj._param_values[self.param_index] = value
else:
self.value = value
class _PCellDeclarationHelper(pya.PCellDeclaration):
"""
A helper class that somewhat simplifies the implementation
of a PCell
"""
def __init__(self):
"""
initialize this instance
"""
# "private" attributes
self._param_decls = []
self._param_values = None
self._param_states = None
self._layer_param_index = []
self._layers = []
# public attributes
self.layout = None
self.shape = None
self.layer = None
self.cell = None
def param(self, name, value_type, description, hidden = False, readonly = False, unit = None, default = None, choices = None):
"""
Defines a parameter
name -&gt; the short name of the parameter
type -&gt; the type of the parameter
description -&gt; the description text
named parameters
hidden -&gt; (boolean) true, if the parameter is not shown in the dialog
readonly -&gt; (boolean) true, if the parameter cannot be edited
unit -&gt; the unit string
default -&gt; the default value
choices -&gt; ([ [ d, v ], ...) choice descriptions/value for choice type
this method defines accessor methods for the parameters
{name} -&gt; read accessor
set_{name} -&gt; write accessor ({name}= does not work because the
Ruby confuses that method with variables)
{name}_layer -&gt; read accessor for the layer index for TypeLayer parameters
"""
# create accessor methods for the parameters
param_index = len(self._param_decls)
setattr(type(self), name, _PCellDeclarationHelperParameterDescriptor(param_index, name))
if value_type == type(self).TypeLayer:
setattr(type(self), name + "_layer", _PCellDeclarationHelperLayerDescriptor(len(self._layer_param_index)))
self._layer_param_index.append(param_index)
# store the parameter declarations
pdecl = pya.PCellParameterDeclaration(name, value_type, description)
self._param_decls.append(pdecl)
# set additional attributes of the parameters
pdecl.hidden = hidden
pdecl.readonly = readonly
if not (default is None):
pdecl.default = default
if not (unit is None):
pdecl.unit = unit
if not (choices is None):
if not isinstance(choices, list) and not isinstance(choices, tuple):
raise "choices value must be an list/tuple of two-element arrays (description, value)"
for c in choices:
if (not isinstance(choices, list) and not isinstance(choices, tuple)) or len(c) != 2:
raise "choices value must be an list/tuple of two-element arrays (description, value)"
pdecl.add_choice(c[0],c[1])
# return the declaration object for further operations
return pdecl
def display_text(self, parameters):
"""
implementation of display_text
"""
self._param_values = parameters
try:
text = self.display_text_impl()
finally:
self._param_values = None
return text
def get_parameters(self):
"""
gets the parameters
"""
return self._param_decls
def get_values(self):
"""
gets the temporary parameter values
"""
v = self._param_values
self._param_values = None
return v
def init_values(self, values = None, layers = None, states = None):
"""
initializes the temporary parameter values
"values" are the original values. If "None" is given, the
default values will be used.
"layers" are the layer indexes corresponding to the layer
parameters.
"""
self._param_values = None
self._param_states = None
if states:
self._param_states = states
elif not values:
self._param_values = []
for pd in self._param_decls:
self._param_values.append(pd.default)
else:
self._param_values = values
self._layers = layers
def finish(self):
"""
Needs to be called at the end of an implementation
"""
self._param_values = None
self._param_states = None
self._layers = None
self._cell = None
self._layout = None
self._layer = None
self._shape = None
def get_layers(self, parameters):
"""
gets the layer definitions
"""
layers = []
for i in self._layer_param_index:
layers.append(parameters[i])
return layers
def callback(self, layout, name, states):
"""
callback (change state on parameter change)
"""
self.init_values(states = states)
self.layout = layout
try:
self.callback_impl(name)
finally:
self.finish()
def coerce_parameters(self, layout, parameters):
"""
coerce parameters (make consistent)
"""
self.init_values(parameters)
self.layout = layout
try:
self.coerce_parameters_impl()
parameters = self.get_values()
finally:
self.finish()
return parameters
def produce(self, layout, layers, parameters, cell):
"""
produces the layout
"""
self.init_values(parameters, layers)
self.cell = cell
self.layout = layout
try:
self.produce_impl()
finally:
self.finish()
def can_create_from_shape(self, layout, shape, layer):
"""
produce a helper for can_create_from_shape
"""
self.layout = layout
self.shape = shape
self.layer = layer
try:
ret = self.can_create_from_shape_impl()
finally:
self.finish()
return ret
def transformation_from_shape(self, layout, shape, layer):
"""
produce a helper for parameters_from_shape
"""
self.layout = layout
self.shape = shape
self.layer = layer
try:
t = self.transformation_from_shape_impl()
finally:
self.finish()
return t
def parameters_from_shape(self, layout, shape, layer):
"""
produce a helper for parameters_from_shape
with this helper, the implementation can use the parameter setters
"""
self.init_values()
self.layout = layout
self.shape = shape
self.layer = layer
try:
self.parameters_from_shape_impl()
param = self.get_values()
finally:
self.finish()
return param
def display_text_impl(self):
"""
default implementation
"""
return ""
def coerce_parameters_impl(self):
"""
default implementation
"""
pass
def callback_impl(self, name):
"""
default implementation
"""
pass
def produce_impl(self):
"""
default implementation
"""
pass
def can_create_from_shape_impl(self):
"""
default implementation
"""
return False
def parameters_from_shape_impl(self):
"""
default implementation
"""
pass
def transformation_from_shape_impl(self):
"""
default implementation
"""
return pya.Trans()
# import the Type... constants from PCellParameterDeclaration
for k in dir(pya.PCellParameterDeclaration):
if k.startswith("Type"):
setattr(_PCellDeclarationHelper, k, getattr(pya.PCellParameterDeclaration, k))
# Inject the PCellDeclarationHelper into pya module for consistency:
setattr(pya, "PCellDeclarationHelper", _PCellDeclarationHelper)
# No code provided here. This macro is supplied to provide the documentation.
# The basic code is located in klayout.db.pcell_declaration_helper now.
</text>
</klayout-macro>

View File

@ -1,20 +1,31 @@
import functools
from typing import Type
import klayout.dbcore
from klayout.dbcore import *
from klayout.db.pcell_declaration_helper import PCellDeclarationHelper
import sys
from ..dbcore import __all__
from ..dbcore import *
__all__ = klayout.dbcore.__all__ + ["PCellDeclarationHelper"] # type: ignore
from .pcell_declaration_helper import *
# establish the PCellDeclarationHelper using the mixin provided by _pcell_declaration_helper
class PCellDeclarationHelper(_PCellDeclarationHelperMixin, PCellDeclaration):
def __init__(self):
super().__init__()
def _make_parameter_declaration(self, name, value_type, description):
return PCellParameterDeclaration(name, value_type, description)
def _make_default_trans(self):
return Trans()
# import the Type... constants from PCellParameterDeclaration
for k in dir(PCellParameterDeclaration):
if k.startswith("Type"):
setattr(PCellDeclarationHelper, k, getattr(PCellParameterDeclaration, k))
# If class has from_s, to_s, and assign, use them to
# enable serialization.
for name, cls in klayout.dbcore.__dict__.items():
if not isinstance(cls, type):
continue
if hasattr(cls, 'from_s') and hasattr(cls, 'to_s') and hasattr(cls, 'assign'):
cls.__getstate__ = cls.to_s # type: ignore
def _setstate(self, str):
cls = self.__class__
self.assign(cls.from_s(str))
cls.__setstate__ = _setstate # type: ignore
for name in __all__:
cls = globals()[name]
if hasattr(cls, 'from_s') and hasattr(cls, 'to_s') and hasattr(cls, 'assign'):
cls.__getstate__ = cls.to_s # type: ignore
def _setstate(self, str):
cls = self.__class__
self.assign(cls.from_s(str))
cls.__setstate__ = _setstate # type: ignore

View File

@ -1,5 +1,3 @@
from klayout.db import Trans, PCellDeclaration, PCellParameterDeclaration
class _PCellDeclarationHelperLayerDescriptor(object):
"""
@ -43,16 +41,17 @@ class _PCellDeclarationHelperParameterDescriptor(object):
else:
self.value = value
class _PCellDeclarationHelper(PCellDeclaration):
class _PCellDeclarationHelperMixin:
"""
A helper class that somewhat simplifies the implementation
of a PCell
A mixin class that somewhat simplifies the implementation of a PCell
Needed to build PCellDeclarationHelper
"""
def __init__(self):
def __init__(self, *args, **kwargs):
"""
initialize this instance
initializes this instance
"""
super().__init__(*args, **kwargs)
# "private" attributes
self._param_decls = []
self._param_values = None
@ -93,7 +92,7 @@ class _PCellDeclarationHelper(PCellDeclaration):
self._layer_param_index.append(param_index)
# store the parameter declarations
pdecl = PCellParameterDeclaration(name, value_type, description)
pdecl = self._make_parameter_declaration(name, value_type, description)
self._param_decls.append(pdecl)
# set additional attributes of the parameters
@ -116,24 +115,33 @@ class _PCellDeclarationHelper(PCellDeclaration):
def display_text(self, parameters):
"""
implementation of display_text
Reimplementation of PCellDeclaration.display_text
This function delegates the implementation to self.display_text_impl
after configuring the PCellDeclaration object.
"""
self._param_values = parameters
try:
text = self.display_text_impl()
finally:
self._param_values = None
self.finish()
return text
def get_parameters(self):
"""
gets the parameters
Reimplementation of PCellDeclaration.get_parameters
This function uses the collected parameters to feed the
PCell declaration.
"""
return self._param_decls
def get_values(self):
"""
gets the temporary parameter values
Gets the temporary parameter values used for the current evaluation
Call this function to get the a current parameter values. This
is an array of variants in the order the parameters are declared.
"""
v = self._param_values
self._param_values = None
@ -141,7 +149,8 @@ class _PCellDeclarationHelper(PCellDeclaration):
def init_values(self, values = None, layers = None, states = None):
"""
initializes the temporary parameter values
initializes the temporary parameter values for the current evaluation
"values" are the original values. If "None" is given, the
default values will be used.
"layers" are the layer indexes corresponding to the layer
@ -161,7 +170,7 @@ class _PCellDeclarationHelper(PCellDeclaration):
def finish(self):
"""
Needs to be called at the end of an implementation
Is called at the end of an implementation of a PCellDeclaration method
"""
self._param_values = None
self._param_states = None
@ -173,7 +182,9 @@ class _PCellDeclarationHelper(PCellDeclaration):
def get_layers(self, parameters):
"""
gets the layer definitions
Reimplements PCellDeclaration.get_layers.
Gets the layer definitions from all layer parameters.
"""
layers = []
for i in self._layer_param_index:
@ -182,7 +193,10 @@ class _PCellDeclarationHelper(PCellDeclaration):
def callback(self, layout, name, states):
"""
callback (change state on parameter change)
Reimplements PCellDeclaration.callback (change state on parameter change)
The function delegates the implementation to callback_impl
after updating the state of this object with the current parameters.
"""
self.init_values(states = states)
self.layout = layout
@ -193,7 +207,10 @@ class _PCellDeclarationHelper(PCellDeclaration):
def coerce_parameters(self, layout, parameters):
"""
coerce parameters (make consistent)
Reimplements PCellDeclaration.coerce parameters (make consistent)
The function delegates the implementation to coerce_parameters_impl
after updating the state of this object with the current parameters.
"""
self.init_values(parameters)
self.layout = layout
@ -206,7 +223,10 @@ class _PCellDeclarationHelper(PCellDeclaration):
def produce(self, layout, layers, parameters, cell):
"""
produces the layout
Reimplements PCellDeclaration.produce (produces the layout)
The function delegates the implementation to produce_impl
after updating the state of this object with the current parameters.
"""
self.init_values(parameters, layers)
self.cell = cell
@ -218,7 +238,10 @@ class _PCellDeclarationHelper(PCellDeclaration):
def can_create_from_shape(self, layout, shape, layer):
"""
produce a helper for can_create_from_shape
Reimplements PCellDeclaration.can_create_from_shape
The function delegates the implementation to can_create_from_shape_impl
after updating the state of this object with the current parameters.
"""
self.layout = layout
self.shape = shape
@ -231,21 +254,28 @@ class _PCellDeclarationHelper(PCellDeclaration):
def transformation_from_shape(self, layout, shape, layer):
"""
produce a helper for parameters_from_shape
Reimplements PCellDeclaration.transformation_from_shape
The function delegates the implementation to transformation_from_shape_impl
after updating the state of this object with the current parameters.
"""
self.layout = layout
self.shape = shape
self.layer = layer
try:
t = self.transformation_from_shape_impl()
if t is None:
t = self._make_default_trans()
finally:
self.finish()
return t
def parameters_from_shape(self, layout, shape, layer):
"""
produce a helper for parameters_from_shape
with this helper, the implementation can use the parameter setters
Reimplements PCellDeclaration.parameters_from_shape
The function delegates the implementation to parameters_from_shape_impl
after updating the state of this object with the current parameters.
"""
self.init_values()
self.layout = layout
@ -298,13 +328,10 @@ class _PCellDeclarationHelper(PCellDeclaration):
"""
default implementation
"""
return Trans()
return None
# import the Type... constants from PCellParameterDeclaration
for k in dir(PCellParameterDeclaration):
if k.startswith("Type"):
setattr(_PCellDeclarationHelper, k, getattr(PCellParameterDeclaration, k))
# Inject the PCellDeclarationHelper into module for consistency:
PCellDeclarationHelper = _PCellDeclarationHelper
__all__ = [ "_PCellDeclarationHelperLayerDescriptor",
"_PCellDeclarationHelperParameterDescriptor",
"_PCellDeclarationHelperMixin" ]

View File

@ -1,6 +1,6 @@
import klayout.dbcore # enables stream reader plugins
import klayout.laycore
from klayout.laycore import *
from ..dbcore import Layout # enables stream reader plugins
from ..laycore import __all__
from ..laycore import *
__all__ = klayout.laycore.__all__

View File

@ -1,4 +1,4 @@
import klayout.libcore
from klayout.libcore import *
__all__ = klayout.libcore.__all__
from ..libcore import __all__
from ..libcore import *

View File

@ -1,4 +1,4 @@
import klayout.rdbcore
from klayout.rdbcore import *
__all__ = klayout.rdbcore.__all__
from ..rdbcore import __all__
from ..rdbcore import *

View File

@ -1,4 +1,4 @@
import klayout.tlcore
from klayout.tlcore import *
__all__ = klayout.tlcore.__all__
from ..tlcore import __all__
from ..tlcore import *

View File

@ -2,3 +2,20 @@
# pull everything from the generic klayout.pya package into pya
from klayout.pya import __all__
from klayout.pya import *
from klayout.db.pcell_declaration_helper import *
# establish the PCellDeclarationHelper using the mixin provided by _pcell_declaration_helper
class PCellDeclarationHelper(_PCellDeclarationHelperMixin, PCellDeclaration):
def __init__(self):
super().__init__()
def _make_parameter_declaration(self, name, value_type, description):
return PCellParameterDeclaration(name, value_type, description)
def _make_default_trans(self):
return Trans()
# import the Type... constants from PCellParameterDeclaration
for k in dir(PCellParameterDeclaration):
if k.startswith("Type"):
setattr(PCellDeclarationHelper, k, getattr(PCellParameterDeclaration, k))

View File

@ -90,6 +90,7 @@ PYMODTEST (bridge, "bridge.py")
PYMODTEST (import_tl, "import_tl.py")
PYMODTEST (import_db, "import_db.py")
PYMODTEST (klayout_db_tests, "klayout_db_tests.py")
PYMODTEST (import_rdb, "import_rdb.py")
PYMODTEST (import_lay, "import_lay.py")
PYMODTEST (import_lib, "import_lib.py")

502
testdata/pymod/klayout_db_tests.py vendored Normal file
View File

@ -0,0 +1,502 @@
# KLayout Layout Viewer
# Copyright (C) 2006-2023 Matthias Koefferlein
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import klayout.db
import unittest
import sys
class BoxPCell(klayout.db.PCellDeclaration):
def display_text(self, parameters):
# provide a descriptive text for the cell
return "Box(L=" + str(parameters[0]) + ",W=" + ('%.3f' % parameters[1]) + ",H=" + ('%.3f' % parameters[2]) + ")"
def get_parameters(self):
# prepare a set of parameter declarations
param = []
param.append(klayout.db.PCellParameterDeclaration("l", klayout.db.PCellParameterDeclaration.TypeLayer, "Layer", klayout.db.LayerInfo(0, 0)))
param.append(klayout.db.PCellParameterDeclaration("w", klayout.db.PCellParameterDeclaration.TypeDouble, "Width", 1.0))
param.append(klayout.db.PCellParameterDeclaration("h", klayout.db.PCellParameterDeclaration.TypeDouble, "Height", 1.0))
return param
def get_layers(self, parameters):
return [ parameters[0] ]
def produce(self, layout, layers, parameters, cell):
dbu = layout.dbu
# fetch the parameters
l = parameters[0]
w = parameters[1] / layout.dbu
h = parameters[2] / layout.dbu
# create the shape
cell.shapes(layers[0]).insert(klayout.db.Box(-w / 2, -h / 2, w / 2, h / 2))
def can_create_from_shape(self, layout, shape, layer):
return shape.is_box()
def transformation_from_shape(self, layout, shape, layer):
return klayout.db.Trans(shape.box.center() - klayout.db.Point())
def parameters_from_shape(self, layout, shape, layer):
return [ layout.get_info(layer), shape.box.width() * layout.dbu, shape.box.height() * layout.dbu ]
class PCellTestLib(klayout.db.Library):
def __init__(self):
# set the description
self.description = "PCell test lib"
# create the PCell declarations
self.layout().register_pcell("Box", BoxPCell())
sb_index = self.layout().add_cell("StaticBox")
l10 = self.layout().insert_layer(klayout.db.LayerInfo(10, 0))
sb_cell = self.layout().cell(sb_index)
sb_cell.shapes(l10).insert(klayout.db.Box(0, 0, 100, 200))
# register us with the name "MyLib"
self.register("PCellTestLib")
# A PCell based on the declaration helper
class BoxPCell2(klayout.db.PCellDeclarationHelper):
def __init__(self):
super(BoxPCell2, self).__init__()
self.param("layer", self.TypeLayer, "Layer", default = klayout.db.LayerInfo(0, 0))
self.param("width", self.TypeDouble, "Width", default = 1.0)
self.param("height", self.TypeDouble, "Height", default = 1.0)
def display_text_impl(self):
# provide a descriptive text for the cell
return "Box2(L=" + str(self.layer) + ",W=" + ('%.3f' % self.width) + ",H=" + ('%.3f' % self.height) + ")"
def wants_lazy_evaluation(self):
return True
def produce_impl(self):
dbu = self.layout.dbu
# fetch the parameters
l = self.layer_layer
w = self.width / self.layout.dbu
h = self.height / self.layout.dbu
# create the shape
self.cell.shapes(l).insert(klayout.db.Box(-w / 2, -h / 2, w / 2, h / 2))
def can_create_from_shape_impl(self):
return self.shape.is_box()
def transformation_from_shape_impl(self):
return klayout.db.Trans(self.shape.box.center() - klayout.db.Point())
def parameters_from_shape_impl(self):
self.layer = self.layout.get_info(self.layer)
self.width = self.shape.box.width() * self.layout.dbu
self.height = self.shape.box.height() * self.layout.dbu
class PCellTestLib2(klayout.db.Library):
def __init__(self):
# set the description
self.description = "PCell test lib2"
# create the PCell declarations
self.layout().register_pcell("Box2", BoxPCell2())
# register us with the name "MyLib"
self.register("PCellTestLib2")
def inspect_LayerInfo(self):
return "<" + str(self) + ">"
klayout.db.LayerInfo.__repr__ = inspect_LayerInfo
def find_layer(ly, lp):
for li in ly.layer_indices():
if str(ly.get_info(li)) == lp:
return li
return None
def nh(h):
"""
Returns a normalized hash representation
"""
v = []
for k in sorted(h):
v.append(repr(k) + ": " + repr(h[k]))
return "{" + (", ".join(v)) + "}"
class DBPCellTests(unittest.TestCase):
def test_1(self):
# instantiate and register the library
tl = PCellTestLib()
ly = klayout.db.Layout(True)
ly.dbu = 0.01
li1 = find_layer(ly, "1/0")
self.assertEqual(li1 == None, True)
ci1 = ly.add_cell("c1")
c1 = ly.cell(ci1)
lib = klayout.db.Library.library_by_name("NoLib")
self.assertEqual(lib == None, True)
lib = klayout.db.Library.library_by_name("PCellTestLib")
self.assertEqual(lib != None, True)
pcell_decl = lib.layout().pcell_declaration("x")
self.assertEqual(pcell_decl == None, True)
pcell_decl = lib.layout().pcell_declaration("Box")
self.assertEqual(pcell_decl != None, True)
pcell_decl_id = lib.layout().pcell_id("Box")
self.assertEqual(pcell_decl.id(), pcell_decl_id)
self.assertEqual(":".join(lib.layout().pcell_names()), "Box")
self.assertEqual(lib.layout().pcell_ids(), [ pcell_decl_id ])
self.assertEqual(lib.layout().pcell_declaration(pcell_decl_id).id(), pcell_decl_id)
param = [ klayout.db.LayerInfo(1, 0) ] # rest is filled with defaults
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl_id, param)
pcell_var = ly.cell(pcell_var_id)
pcell_inst = c1.insert(klayout.db.CellInstArray(pcell_var_id, klayout.db.Trans()))
self.assertEqual(pcell_var.layout().__repr__(), ly.__repr__())
self.assertEqual(pcell_var.library().__repr__(), lib.__repr__())
self.assertEqual(pcell_var.is_pcell_variant(), True)
self.assertEqual(pcell_var.display_title(), "PCellTestLib.Box(L=1/0,W=1.000,H=1.000)")
self.assertEqual(pcell_var.basic_name(), "Box")
self.assertEqual(pcell_var.pcell_declaration().wants_lazy_evaluation(), False)
self.assertEqual(c1.is_pcell_variant(), False)
self.assertEqual(c1.is_pcell_variant(pcell_inst), True)
self.assertEqual(pcell_var.pcell_id(), pcell_decl_id)
self.assertEqual(pcell_var.pcell_library().__repr__(), lib.__repr__())
self.assertEqual(pcell_var.pcell_parameters().__repr__(), "[<1/0>, 1.0, 1.0]")
self.assertEqual(nh(pcell_var.pcell_parameters_by_name()), "{'h': 1.0, 'l': <1/0>, 'w': 1.0}")
self.assertEqual(pcell_var.pcell_parameter("h").__repr__(), "1.0")
self.assertEqual(c1.pcell_parameters(pcell_inst).__repr__(), "[<1/0>, 1.0, 1.0]")
self.assertEqual(nh(c1.pcell_parameters_by_name(pcell_inst)), "{'h': 1.0, 'l': <1/0>, 'w': 1.0}")
self.assertEqual(c1.pcell_parameter(pcell_inst, "h").__repr__(), "1.0")
self.assertEqual(nh(pcell_inst.pcell_parameters_by_name()), "{'h': 1.0, 'l': <1/0>, 'w': 1.0}")
self.assertEqual(pcell_inst["h"].__repr__(), "1.0")
self.assertEqual(pcell_inst["i"].__repr__(), "None")
self.assertEqual(pcell_inst.pcell_parameter("h").__repr__(), "1.0")
self.assertEqual(pcell_var.pcell_declaration().__repr__(), pcell_decl.__repr__())
self.assertEqual(c1.pcell_declaration(pcell_inst).__repr__(), pcell_decl.__repr__())
self.assertEqual(pcell_inst.pcell_declaration().__repr__(), pcell_decl.__repr__())
pcell_inst.change_pcell_parameter("h", 2.0)
self.assertEqual(nh(pcell_inst.pcell_parameters_by_name()), "{'h': 2.0, 'l': <1/0>, 'w': 1.0}")
pcell_inst.set_property("abc", "a property")
self.assertEqual(pcell_inst.property("abc").__repr__(), "'a property'")
c1.clear()
param = [ klayout.db.LayerInfo(1, 0), 5.0, 10.0 ]
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl_id, param)
pcell_var = ly.cell(pcell_var_id)
pcell_inst = c1.insert(klayout.db.CellInstArray(pcell_var_id, klayout.db.Trans()))
self.assertEqual(pcell_var.layout().__repr__(), ly.__repr__())
self.assertEqual(pcell_var.library().__repr__(), lib.__repr__())
self.assertEqual(pcell_var.is_pcell_variant(), True)
self.assertEqual(pcell_var.display_title(), "PCellTestLib.Box(L=1/0,W=5.000,H=10.000)")
self.assertEqual(pcell_var.basic_name(), "Box")
self.assertEqual(c1.is_pcell_variant(), False)
self.assertEqual(c1.is_pcell_variant(pcell_inst), True)
self.assertEqual(pcell_var.pcell_id(), pcell_decl_id)
self.assertEqual(pcell_var.pcell_library().__repr__(), lib.__repr__())
self.assertEqual(pcell_var.pcell_parameters().__repr__(), "[<1/0>, 5.0, 10.0]")
self.assertEqual(c1.pcell_parameters(pcell_inst).__repr__(), "[<1/0>, 5.0, 10.0]")
self.assertEqual(pcell_inst.pcell_parameters().__repr__(), "[<1/0>, 5.0, 10.0]")
self.assertEqual(pcell_var.pcell_declaration().__repr__(), pcell_decl.__repr__())
self.assertEqual(c1.pcell_declaration(pcell_inst).__repr__(), pcell_decl.__repr__())
li1 = find_layer(ly, "1/0")
self.assertEqual(li1 != None, True)
self.assertEqual(ly.is_valid_layer(li1), True)
self.assertEqual(str(ly.get_info(li1)), "1/0")
lib_proxy_id = ly.add_lib_cell(lib, lib.layout().cell_by_name("StaticBox"))
lib_proxy = ly.cell(lib_proxy_id)
self.assertEqual(lib_proxy.display_title(), "PCellTestLib.StaticBox")
self.assertEqual(lib_proxy.basic_name(), "StaticBox")
self.assertEqual(lib_proxy.layout().__repr__(), ly.__repr__())
self.assertEqual(lib_proxy.library().__repr__(), lib.__repr__())
self.assertEqual(lib_proxy.is_pcell_variant(), False)
self.assertEqual(lib.layout().cell(lib.layout().cell_by_name("StaticBox")).library().__repr__(), "None")
li2 = find_layer(ly, "10/0")
self.assertEqual(li2 != None, True)
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-250,-500;250,500)")
self.assertEqual(ly.begin_shapes(lib_proxy.cell_index(), li2).shape().__str__(), "box (0,0;10,20)")
param = { "w": 1, "h": 2 }
c1.change_pcell_parameters(pcell_inst, param)
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-50,-100;50,100)")
param = [ klayout.db.LayerInfo(1, 0), 5.0, 5.0 ]
c1.change_pcell_parameters(pcell_inst, param)
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-250,-250;250,250)")
pcell_inst.change_pcell_parameters({ "w": 2.0, "h": 10.0 })
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-100,-500;100,500)")
pcell_inst.change_pcell_parameters([ klayout.db.LayerInfo(1, 0), 5.0, 5.0 ])
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-250,-250;250,250)")
pcell_inst.change_pcell_parameter("w", 5.0)
pcell_inst.change_pcell_parameter("h", 1.0)
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-250,-50;250,50)")
c1.change_pcell_parameter(pcell_inst, "w", 10.0)
c1.change_pcell_parameter(pcell_inst, "h", 2.0)
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-500,-100;500,100)")
self.assertEqual(ly.cell(pcell_inst.cell_index).is_pcell_variant(), True)
self.assertEqual(pcell_inst.is_pcell(), True)
new_id = ly.convert_cell_to_static(pcell_inst.cell_index)
self.assertEqual(new_id == pcell_inst.cell_index, False)
self.assertEqual(ly.cell(new_id).is_pcell_variant(), False)
param = [ klayout.db.LayerInfo(1, 0), 5.0, 5.0 ]
c1.change_pcell_parameters(pcell_inst, param)
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-250,-250;250,250)")
pcell_inst.cell_index = new_id
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-500,-100;500,100)")
l10 = ly.layer(10, 0)
c1.shapes(l10).insert(klayout.db.Box(0, 10, 100, 210))
l11 = ly.layer(11, 0)
c1.shapes(l11).insert(klayout.db.Text("hello", klayout.db.Trans()))
self.assertEqual(pcell_decl.can_create_from_shape(ly, ly.begin_shapes(c1.cell_index(), l11).shape(), l10), False)
self.assertEqual(pcell_decl.can_create_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10), True)
self.assertEqual(repr(pcell_decl.parameters_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10)), "[<10/0>, 1.0, 2.0]")
self.assertEqual(str(pcell_decl.transformation_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10)), "r0 50,110")
def test_1a(self):
if not "PCellDeclarationHelper" in klayout.db.__dict__:
return
# instantiate and register the library
tl = PCellTestLib2()
ly = klayout.db.Layout(True)
ly.dbu = 0.01
li1 = find_layer(ly, "1/0")
self.assertEqual(li1 == None, True)
ci1 = ly.add_cell("c1")
c1 = ly.cell(ci1)
lib = klayout.db.Library.library_by_name("PCellTestLib2")
self.assertEqual(lib != None, True)
pcell_decl = lib.layout().pcell_declaration("Box2")
param = [ klayout.db.LayerInfo(1, 0) ] # rest is filled with defaults
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl.id(), param)
pcell_var = ly.cell(pcell_var_id)
pcell_inst = c1.insert(klayout.db.CellInstArray(pcell_var_id, klayout.db.Trans()))
self.assertEqual(pcell_var.basic_name(), "Box2")
self.assertEqual(pcell_var.pcell_parameters().__repr__(), "[<1/0>, 1.0, 1.0]")
self.assertEqual(pcell_var.display_title(), "PCellTestLib2.Box2(L=1/0,W=1.000,H=1.000)")
self.assertEqual(nh(pcell_var.pcell_parameters_by_name()), "{'height': 1.0, 'layer': <1/0>, 'width': 1.0}")
self.assertEqual(pcell_var.pcell_parameter("height").__repr__(), "1.0")
self.assertEqual(c1.pcell_parameters(pcell_inst).__repr__(), "[<1/0>, 1.0, 1.0]")
self.assertEqual(nh(c1.pcell_parameters_by_name(pcell_inst)), "{'height': 1.0, 'layer': <1/0>, 'width': 1.0}")
self.assertEqual(c1.pcell_parameter(pcell_inst, "height").__repr__(), "1.0")
self.assertEqual(nh(pcell_inst.pcell_parameters_by_name()), "{'height': 1.0, 'layer': <1/0>, 'width': 1.0}")
self.assertEqual(pcell_inst["height"].__repr__(), "1.0")
self.assertEqual(pcell_inst.pcell_parameter("height").__repr__(), "1.0")
self.assertEqual(pcell_var.pcell_declaration().__repr__(), pcell_decl.__repr__())
self.assertEqual(c1.pcell_declaration(pcell_inst).__repr__(), pcell_decl.__repr__())
self.assertEqual(pcell_inst.pcell_declaration().__repr__(), pcell_decl.__repr__())
self.assertEqual(pcell_decl.wants_lazy_evaluation(), True)
li1 = find_layer(ly, "1/0")
self.assertEqual(li1 == None, False)
pcell_inst.change_pcell_parameter("height", 2.0)
self.assertEqual(nh(pcell_inst.pcell_parameters_by_name()), "{'height': 2.0, 'layer': <1/0>, 'width': 1.0}")
self.assertEqual(ly.begin_shapes(c1.cell_index(), li1).shape().__str__(), "box (-50,-100;50,100)")
param = { "layer": klayout.db.LayerInfo(2, 0), "width": 2, "height": 1 }
li2 = ly.layer(2, 0)
c1.change_pcell_parameters(pcell_inst, param)
self.assertEqual(ly.begin_shapes(c1.cell_index(), li2).shape().__str__(), "box (-100,-50;100,50)")
l10 = ly.layer(10, 0)
c1.shapes(l10).insert(klayout.db.Box(0, 10, 100, 210))
l11 = ly.layer(11, 0)
c1.shapes(l11).insert(klayout.db.Text("hello", klayout.db.Trans()))
self.assertEqual(pcell_decl.can_create_from_shape(ly, ly.begin_shapes(c1.cell_index(), l11).shape(), l10), False)
self.assertEqual(pcell_decl.can_create_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10), True)
self.assertEqual(repr(pcell_decl.parameters_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10)), "[<10/0>, 1.0, 2.0]")
self.assertEqual(str(pcell_decl.transformation_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10)), "r0 50,110")
def test_2(self):
# instantiate and register the library
tl = PCellTestLib()
ly = klayout.db.Layout(True)
ly.dbu = 0.01
ci1 = ly.add_cell("c1")
c1 = ly.cell(ci1)
lib = klayout.db.Library.library_by_name("PCellTestLib")
pcell_decl_id = lib.layout().pcell_id("Box")
param = [ klayout.db.LayerInfo(1, 0), 10.0, 2.0 ]
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl_id, param)
pcell_var = ly.cell(pcell_var_id)
pcell_inst = c1.insert(klayout.db.CellInstArray(pcell_var_id, klayout.db.Trans()))
li1 = find_layer(ly, "1/0")
self.assertEqual(li1 != None, True)
self.assertEqual(ly.is_valid_layer(li1), True)
self.assertEqual(str(ly.get_info(li1)), "1/0")
self.assertEqual(pcell_inst.is_pcell(), True)
self.assertEqual(ly.begin_shapes(pcell_inst.cell_index, li1).shape().__str__(), "box (-500,-100;500,100)")
pcell_inst.convert_to_static()
self.assertEqual(pcell_inst.is_pcell(), False)
self.assertEqual(ly.begin_shapes(pcell_inst.cell_index, li1).shape().__str__(), "box (-500,-100;500,100)")
pcell_inst.convert_to_static()
self.assertEqual(pcell_inst.is_pcell(), False)
self.assertEqual(ly.begin_shapes(pcell_inst.cell_index, li1).shape().__str__(), "box (-500,-100;500,100)")
def test_3(self):
# instantiate and register the library
tl = PCellTestLib()
ly = klayout.db.Layout(True)
ly.dbu = 0.01
c1 = ly.create_cell("c1")
lib = klayout.db.Library.library_by_name("PCellTestLib")
pcell_decl_id = lib.layout().pcell_id("Box")
param = { "w": 4.0, "h": 8.0, "l": klayout.db.LayerInfo(1, 0) }
pcell_var_id = ly.add_pcell_variant(lib, pcell_decl_id, param)
pcell_var = ly.cell(pcell_var_id)
pcell_inst = c1.insert(klayout.db.CellInstArray(pcell_var_id, klayout.db.Trans()))
self.assertEqual(ly.begin_shapes(c1.cell_index(), ly.layer(1, 0)).shape().__str__(), "box (-200,-400;200,400)")
def test_4(self):
# instantiate and register the library
tl = PCellTestLib()
lib = klayout.db.Library.library_by_name("PCellTestLib")
pcell_decl_id = lib.layout().pcell_id("Box")
param = { "w": 4.0, "h": 8.0, "l": klayout.db.LayerInfo(1, 0) }
pcell_var_id = lib.layout().add_pcell_variant(pcell_decl_id, param)
self.assertEqual(lib.layout().begin_shapes(pcell_var_id, lib.layout().layer(1, 0)).shape().__str__(), "box (-2000,-4000;2000,4000)")
def test_5(self):
# instantiate and register the library
tl = PCellTestLib()
lib = klayout.db.Library.library_by_name("PCellTestLib")
pcell_decl_id = lib.layout().pcell_id("Box")
param = { "w": 3.0, "h": 7.0, "l": klayout.db.LayerInfo(2, 0) }
pcell_var_id = lib.layout().add_pcell_variant(pcell_decl_id, param)
self.assertEqual(lib.layout().begin_shapes(pcell_var_id, lib.layout().layer(2, 0)).shape().__str__(), "box (-1500,-3500;1500,3500)")
def test_6(self):
# instantiate and register the library
tl = PCellTestLib()
lib = klayout.db.Library.library_by_name("PCellTestLib")
param = { "w": 3.0, "h": 8.0, "l": klayout.db.LayerInfo(3, 0) }
pcell_var = lib.layout().create_cell("Box", param)
self.assertEqual(lib.layout().begin_shapes(pcell_var.cell_index(), lib.layout().layer(3, 0)).shape().__str__(), "box (-1500,-4000;1500,4000)")
def test_7(self):
# instantiate and register the library
tl = PCellTestLib()
ly = klayout.db.Layout(True)
ly.dbu = 0.01
param = { "w": 4.0, "h": 8.0, "l": klayout.db.LayerInfo(4, 0) }
cell = ly.create_cell("Box", "PCellTestLib", param)
self.assertEqual(ly.begin_shapes(cell, ly.layer(4, 0)).shape().__str__(), "box (-200,-400;200,400)")
def test_8(self):
# instantiate and register the library
tl = PCellTestLib()
lib = klayout.db.Library.library_by_name("PCellTestLib")
ly = klayout.db.Layout(True)
ly.dbu = 0.01
param = { "w": 2.0, "h": 6.0, "l": klayout.db.LayerInfo(5, 0) }
pcell_var = lib.layout().create_cell("Box", param)
pcell_var.name = "BOXVAR"
cell = ly.create_cell("BOXVAR", "PCellTestLib")
self.assertEqual(cell.begin_shapes_rec(ly.layer(5, 0)).shape().__str__(), "box (-100,-300;100,300)")
# run unit tests
if __name__ == '__main__':
suite = unittest.TestLoader().loadTestsFromTestCase(DBPCellTests)
if not unittest.TextTestRunner(verbosity = 1).run(suite).wasSuccessful():
sys.exit(1)