mirror of https://github.com/KLayout/klayout.git
A few enhancements and unit tests for PCells
This commit adds unit tests for implementation helper-based PCells in Python and Ruby.
This commit is contained in:
parent
f6f75cd791
commit
6052a04429
|
|
@ -183,6 +183,13 @@ to create the \\LayerInfo object, i.e. like this:
|
|||
|
||||
The default implementation does nothing. All parameters not set in this method will receive their default value.
|
||||
|
||||
If you use a parameter called "layer" for example, the parameter getter will hide the
|
||||
"layer" argument. Use "_layer" for the argument in this case (same for "layout", "shape" or "cell):
|
||||
|
||||
@code
|
||||
set_layer layout.get_info(_layer)
|
||||
@/code
|
||||
|
||||
@method transformation_from_shape_impl
|
||||
|
||||
@brief Gets the initial PCell instance transformation when creating from a shape
|
||||
|
|
@ -230,6 +237,13 @@ module RBA
|
|||
|
||||
# provide accessors for the current layout and cell (for prod)
|
||||
attr_reader :layout, :cell, :shape, :layer
|
||||
|
||||
# provide fallback accessors in case of a name clash with a
|
||||
# parameter
|
||||
def _layer; @layer; end
|
||||
def _layout; @layout; end
|
||||
def _cell; @cell; end
|
||||
def _shape; @shape; end
|
||||
|
||||
# define a parameter
|
||||
# name -> the short name of the parameter
|
||||
|
|
@ -246,6 +260,12 @@ module RBA
|
|||
# set_{name} -> write accessor ({name}= does not work because the
|
||||
# Ruby confuses that method with variables)
|
||||
# {name}_layer -> read accessor for the layer index for TypeLayer parameters
|
||||
# in addition, fallback accessors are defined which can be used if
|
||||
# the parameter's name clashes with another name:
|
||||
# param_{name}
|
||||
# set_param_{name}
|
||||
# param_{name}_layer
|
||||
|
||||
def param(name, type, description, args = {})
|
||||
|
||||
# create accessor methods for the parameters
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class MyPCell(pya.PCellDeclarationHelper):
|
|||
def __init__(self):
|
||||
|
||||
# Important: initialize the super class
|
||||
super(Circle, self).__init__()
|
||||
super(MyPCell, self).__init__()
|
||||
|
||||
# your initialization: add parameters with name, type, description and
|
||||
# optional other values
|
||||
|
|
@ -431,10 +431,11 @@ class _PCellDeclarationHelper(pya.PCellDeclaration):
|
|||
self.shape = shape
|
||||
self.layer = layer
|
||||
self.parameters_from_shape_impl()
|
||||
param = self.get_values()
|
||||
self.layout = None
|
||||
self.shape = None
|
||||
self.layer = None
|
||||
return self.get_values()
|
||||
return param
|
||||
|
||||
def display_text_impl(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -52,20 +52,25 @@ class BoxPCell(pya.PCellDeclaration):
|
|||
|
||||
# create the shape
|
||||
cell.shapes(layers[0]).insert(pya.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 pya.Trans(shape.box.center() - pya.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(pya.Library):
|
||||
|
||||
boxpcell = None
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# set the description
|
||||
self.description = "PCell test lib"
|
||||
|
||||
# create the PCell declarations
|
||||
boxpcell = BoxPCell()
|
||||
self.layout().register_pcell("Box", boxpcell)
|
||||
self.layout().register_pcell("Box", BoxPCell())
|
||||
|
||||
sb_index = self.layout().add_cell("StaticBox")
|
||||
l10 = self.layout().insert_layer(pya.LayerInfo(10, 0))
|
||||
|
|
@ -75,6 +80,60 @@ class PCellTestLib(pya.Library):
|
|||
# register us with the name "MyLib"
|
||||
self.register("PCellTestLib")
|
||||
|
||||
|
||||
# A PCell based on the declaration helper
|
||||
|
||||
class BoxPCell2(pya.PCellDeclarationHelper):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
super(BoxPCell2, self).__init__()
|
||||
|
||||
self.param("layer", self.TypeLayer, "Layer", default = pya.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 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(pya.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 pya.Trans(self.shape.box.center() - pya.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(pya.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) + ">"
|
||||
|
||||
|
|
@ -233,6 +292,75 @@ class DBPCellTests(unittest.TestCase):
|
|||
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(pya.Box(0, 10, 100, 210))
|
||||
l11 = ly.layer(11, 0)
|
||||
c1.shapes(l11).insert(pya.Text("hello", pya.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):
|
||||
|
||||
# instantiate and register the library
|
||||
tl = PCellTestLib2()
|
||||
|
||||
ly = pya.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 = pya.Library.library_by_name("PCellTestLib2")
|
||||
self.assertEqual(lib != None, True)
|
||||
pcell_decl = lib.layout().pcell_declaration("Box2")
|
||||
|
||||
param = [ pya.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(pya.CellInstArray(pcell_var_id, pya.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__())
|
||||
|
||||
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": pya.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(pya.Box(0, 10, 100, 210))
|
||||
l11 = ly.layer(11, 0)
|
||||
c1.shapes(l11).insert(pya.Text("hello", pya.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
|
||||
|
|
|
|||
|
|
@ -61,6 +61,18 @@ class BoxPCell < RBA::PCellDeclaration
|
|||
|
||||
end
|
||||
|
||||
def can_create_from_shape(layout, shape, layer)
|
||||
return shape.is_box?
|
||||
end
|
||||
|
||||
def transformation_from_shape(layout, shape, layer)
|
||||
return RBA::Trans::new(shape.box.center - RBA::Point::new)
|
||||
end
|
||||
|
||||
def parameters_from_shape(layout, shape, layer)
|
||||
return [ layout.get_info(layer), shape.box.width * layout.dbu, shape.box.height * layout.dbu ]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class PCellTestLib < RBA::Library
|
||||
|
|
@ -85,6 +97,72 @@ class PCellTestLib < RBA::Library
|
|||
|
||||
end
|
||||
|
||||
# A PCell based on the declaration helper
|
||||
|
||||
class BoxPCell2 < RBA::PCellDeclarationHelper
|
||||
|
||||
def initialize
|
||||
|
||||
super()
|
||||
|
||||
param("layer", BoxPCell2::TypeLayer, "Layer", :default => RBA::LayerInfo::new(0, 0))
|
||||
param("width", BoxPCell2::TypeDouble, "Width", :default => 1.0)
|
||||
param("height", BoxPCell2::TypeDouble, "Height", :default => 1.0)
|
||||
|
||||
end
|
||||
|
||||
def display_text_impl
|
||||
# provide a descriptive text for the cell
|
||||
return "Box2(L=" + layer.to_s + ",W=" + ('%.3f' % width) + ",H=" + ('%.3f' % height) + ")"
|
||||
end
|
||||
|
||||
def produce_impl
|
||||
|
||||
# fetch the parameters
|
||||
l = layer_layer
|
||||
w = width / layout.dbu
|
||||
h = height / layout.dbu
|
||||
|
||||
# create the shape
|
||||
cell.shapes(l).insert(RBA::Box::new(-w / 2, -h / 2, w / 2, h / 2))
|
||||
|
||||
end
|
||||
|
||||
def can_create_from_shape_impl
|
||||
return self.shape.is_box?
|
||||
end
|
||||
|
||||
def transformation_from_shape_impl
|
||||
return RBA::Trans::new(shape.box.center - RBA::Point::new)
|
||||
end
|
||||
|
||||
def parameters_from_shape_impl
|
||||
# NOTE: because there is one parameter called "layer" already, we need to use
|
||||
# the "_layer" fallback to access the argument to this method
|
||||
set_layer(_layout.get_info(_layer))
|
||||
set_width(shape.box.width * _layout.dbu)
|
||||
set_height(shape.box.height * _layout.dbu)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class PCellTestLib2 < RBA::Library
|
||||
|
||||
def initialize
|
||||
|
||||
# set the description
|
||||
description = "PCell test lib2"
|
||||
|
||||
# create the PCell declarations
|
||||
layout.register_pcell("Box2", BoxPCell2::new)
|
||||
|
||||
# register us with the name "MyLib"
|
||||
self.register("PCellTestLib2")
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A helper for testing: provide an inspect method
|
||||
class RBA::LayerInfo
|
||||
def inspect
|
||||
|
|
@ -115,8 +193,7 @@ class DBPCell_TestClass < TestBase
|
|||
ly = RBA::Layout::new(true)
|
||||
ly.dbu = 0.01
|
||||
|
||||
li1 = ly.layer_indices.find { |li| ly.get_info(li).to_s == "1/0" }
|
||||
assert_equal(li1 == nil, true)
|
||||
li1 = ly.layer(1, 0)
|
||||
|
||||
ci1 = ly.add_cell("c1")
|
||||
c1 = ly.cell(ci1)
|
||||
|
|
@ -241,10 +318,84 @@ class DBPCell_TestClass < TestBase
|
|||
pcell_inst.cell_index = new_id
|
||||
assert_equal(ly.begin_shapes(c1.cell_index, li1).shape.to_s, "box (-500,-100;500,100)")
|
||||
|
||||
#ly.destroy
|
||||
l10 = ly.layer(10, 0)
|
||||
c1.shapes(l10).insert(RBA::Box::new(0, 10, 100, 210))
|
||||
l11 = ly.layer(11, 0)
|
||||
c1.shapes(l11).insert(RBA::Text::new("hello", RBA::Trans::new))
|
||||
assert_equal(pcell_decl.can_create_from_shape(ly, ly.begin_shapes(c1.cell_index(), l11).shape(), l10), false)
|
||||
assert_equal(pcell_decl.can_create_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10), true)
|
||||
assert_equal(pcell_decl.parameters_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10).inspect, "[<10/0>, 1.0, 2.0]")
|
||||
assert_equal(pcell_decl.transformation_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10).to_s, "r0 50,110")
|
||||
|
||||
ly.destroy
|
||||
|
||||
ensure
|
||||
#tl.delete
|
||||
tl.delete
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def test_1a
|
||||
|
||||
# instantiate and register the library
|
||||
tl = PCellTestLib2::new
|
||||
|
||||
begin
|
||||
|
||||
ly = RBA::Layout::new(true)
|
||||
ly.dbu = 0.01
|
||||
|
||||
ci1 = ly.add_cell("c1")
|
||||
c1 = ly.cell(ci1)
|
||||
|
||||
lib = RBA::Library.library_by_name("PCellTestLib2")
|
||||
assert_equal(lib != nil, true)
|
||||
pcell_decl = lib.layout().pcell_declaration("Box2")
|
||||
|
||||
param = [ RBA::LayerInfo::new(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(RBA::CellInstArray::new(pcell_var_id, RBA::Trans::new))
|
||||
assert_equal(pcell_var.basic_name, "Box2")
|
||||
assert_equal(pcell_var.pcell_parameters().inspect, "[<1/0>, 1.0, 1.0]")
|
||||
assert_equal(pcell_var.display_title(), "PCellTestLib2.Box2(L=1/0,W=1.000,H=1.000)")
|
||||
assert_equal(norm_hash(pcell_var.pcell_parameters_by_name()), "{\"height\"=>1.0, \"layer\"=><1/0>, \"width\"=>1.0}")
|
||||
assert_equal(pcell_var.pcell_parameter("height").inspect(), "1.0")
|
||||
assert_equal(c1.pcell_parameters(pcell_inst).inspect(), "[<1/0>, 1.0, 1.0]")
|
||||
assert_equal(norm_hash(c1.pcell_parameters_by_name(pcell_inst)), "{\"height\"=>1.0, \"layer\"=><1/0>, \"width\"=>1.0}")
|
||||
assert_equal(c1.pcell_parameter(pcell_inst, "height").inspect(), "1.0")
|
||||
assert_equal(norm_hash(pcell_inst.pcell_parameters_by_name()), "{\"height\"=>1.0, \"layer\"=><1/0>, \"width\"=>1.0}")
|
||||
assert_equal(pcell_inst["height"].inspect(), "1.0")
|
||||
assert_equal(pcell_inst.pcell_parameter("height").inspect(), "1.0")
|
||||
assert_equal(pcell_var.pcell_declaration().inspect(), pcell_decl.inspect)
|
||||
assert_equal(c1.pcell_declaration(pcell_inst).inspect(), pcell_decl.inspect)
|
||||
assert_equal(pcell_inst.pcell_declaration().inspect(), pcell_decl.inspect)
|
||||
|
||||
li1 = ly.layer(1, 0)
|
||||
assert_equal(li1 == nil, false)
|
||||
pcell_inst.change_pcell_parameter("height", 2.0)
|
||||
assert_equal(norm_hash(pcell_inst.pcell_parameters_by_name()), "{\"height\"=>2.0, \"layer\"=><1/0>, \"width\"=>1.0}")
|
||||
|
||||
assert_equal(ly.begin_shapes(c1.cell_index(), li1).shape().to_s, "box (-50,-100;50,100)")
|
||||
|
||||
param = { "layer" => RBA::LayerInfo::new(2, 0), "width" => 2, "height" => 1 }
|
||||
li2 = ly.layer(2, 0)
|
||||
c1.change_pcell_parameters(pcell_inst, param)
|
||||
assert_equal(ly.begin_shapes(c1.cell_index(), li2).shape().to_s, "box (-100,-50;100,50)")
|
||||
|
||||
l10 = ly.layer(10, 0)
|
||||
c1.shapes(l10).insert(RBA::Box::new(0, 10, 100, 210))
|
||||
l11 = ly.layer(11, 0)
|
||||
c1.shapes(l11).insert(RBA::Text::new("hello", RBA::Trans::new))
|
||||
assert_equal(pcell_decl.can_create_from_shape(ly, ly.begin_shapes(c1.cell_index(), l11).shape(), l10), false)
|
||||
assert_equal(pcell_decl.can_create_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10), true)
|
||||
assert_equal(pcell_decl.parameters_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10).inspect, "[<10/0>, 1.0, 2.0]")
|
||||
assert_equal(pcell_decl.transformation_from_shape(ly, ly.begin_shapes(c1.cell_index(), l10).shape(), l10).to_s, "r0 50,110")
|
||||
|
||||
ly.destroy
|
||||
|
||||
ensure
|
||||
tl.delete
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue