KLayout / klayout

KLayout Main Sources
http://www.klayout.org
GNU General Public License v3.0
809 stars 206 forks source link

Something is wrong with the layer index under python? (no correct integer for python?) #263

Closed quaeritis closed 5 years ago

quaeritis commented 5 years ago

Hello,

I'm trying for a while to play around with the klayout python module. Unfortunately, there seems to be some problems.

What is the subject between self.l_layer and self.l.layer? self.l_layer and the return value of insert_layer sometimes seem to return a "wrong" integer value but deliver the right value when passed to a function?

layer_index here is 3 but the layer is created at 10. Should self.layout.layers not return an integer?

It sometimes seems to make a difference whether pya.LayerInfo or pya.LayerInfo.new is used, what is the difference?

Unfortunately I could not narrow down the problem yet.

import klayout.db as pya

# --------------------------------------------------------
#  Library definition

class TestPCell1(pya.PCellDeclarationHelper):

  def __init__(self):

    super(TestPCell1, self).__init__()

    self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0))
    self.param("r", self.TypeDouble, "Radius", default = 1000.0)
    self.param("n", self.TypeInt, "Number of points", default = 64)

  def display_text_impl(self):
    return "Circle (l=%s,r=%.12g,n=%d)" % (str(self.l), self.r, self.n)

  def produce_impl(self):
    print("self.l_layer:", self.l_layer)
    print("self.l.layer:", self.l.layer)
    self.cell.shapes(self.l_layer).insert(pya.DPolygon.ellipse(pya.DBox(-self.r, -self.r, self.r, self.r), self.n))

class TestPCell2(pya.PCellDeclarationHelper):

  def __init__(self):

    super(TestPCell2, self).__init__()

    self.param("size", self.TypeDouble, "Sample size", default = 70000.0)

  def display_text_impl(self):
    return "Box (size=%s)" %  self.size

  def produce_impl(self):
    layer_index = self.layout.insert_layer(pya.LayerInfo(10, 0, "silicon"))
    print("layer_index:", layer_index)
    print("self.layout.layers:", self.layout.layers)
    self.cell.shapes(layer_index).insert(pya.Box(0, 0, self.size, self.size))

class TestLib(pya.Library):

  def __init__(self):
    self.description = "Test library"
    self.layout().register_pcell("Circle", TestPCell1())
    self.layout().register_pcell("Box", TestPCell2())
    self.register("TestLib")

# create and register the library
lib = TestLib()

# --------------------------------------------------------
#  Client code

lay = pya.Layout()
top = lay.create_cell("TOP")

output_file = "test.gds"

para = { "l": pya.LayerInfo(1, 0) }

pcell = lay.create_cell("Circle", "TestLib", para)
trans = pya.DCplxTrans.new(1, 0, False, 0, 0)
top.insert(pya.DCellInstArray(pcell.cell_index(), trans))

para = { "l": pya.LayerInfo(2, 0), "r": 750.0 }

pcell = lay.create_cell("Circle", "TestLib", para)
trans = pya.DCplxTrans.new(1, 0, False, 0, 2000.0)
top.insert(pya.DCellInstArray(pcell.cell_index(), trans))

para = { "l": pya.LayerInfo(2, 0), "r": 500.0, "n": 16 }

pcell = lay.create_cell("Circle", "TestLib", para)
trans = pya.DCplxTrans.new(1, 0, False, 0, 3000.0)
top.insert(pya.DCellInstArray(pcell.cell_index(), trans))

para = { }
pcell = lay.create_cell("Box", "TestLib", para)
trans = pya.DCplxTrans.new(1, 0, False, 0, 0)
top.insert(pya.DCellInstArray(pcell.cell_index(), trans))

lay.write("bug.oas")

outbut:

self.l_layer: 0
self.l.layer: 1
self.l_layer: 2
self.l.layer: 2
self.l_layer: 2
self.l.layer: 2
layer_index: 3
self.layout.layers: <built-in method layers of Layout object at 0x7f7c97991df0>

Another example, here is a big box with smaller boxes in the middle to be generated that are ever on other layers. So far I've always had some problems with the layers, am I doing something wrong?

import klayout.db as pya

class BasicSample(pya.PCellDeclarationHelper):

  def __init__(self):

    super(BasicSample, self).__init__()

    #self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo.new(1, 0))
    self.param("size", self.TypeDouble, "Sample size", default = 7000)
    self.param("columns", self.TypeInt, "Number of columns", default = 3)
    self.param("lines", self.TypeInt, "Number of lines", default = 3)

  def produce_impl(self):
    layer_index = self.layout.insert_layer(pya.LayerInfo.new(1, 0, "silicon"))
    print("layer_index:", layer_index)
    self.cell.shapes(layer_index).insert(pya.Box(0, 0, self.size, self.size))
    frame_size = 800
    width = (self.size-2*frame_size)/self.columns
    height = (self.size-2*frame_size)/self.lines
    bx = 0 + frame_size
    by = 0 + frame_size
    tx = width + frame_size
    ty = height + frame_size
    #print("self.l_layer:", self.l_layer)
    #print("self.l.layer:", self.l.layer)
    #print("self.layout.layers:", self.layout.layers)

    #layer_index = self.l.layer+1
    #layer_index = layer_index + 1
    layer_index =  2
    print("i", layer_index)

    for line in range(1, self.lines+1):
        for column in range(1, self.columns+1):
            print("make a new box on new layer: index: %i, c%i%i, at cor %8.1f %8.1f %8.1f %8.1f, " % (layer_index,  line, column, bx, by, tx, ty))
            insert_layer_index = self.layout.insert_layer(pya.LayerInfo.new(layer_index, 0, "c" + str(line) + str(column) + "i" + str(layer_index)))
            print("insert index", insert_layer_index)
            self.cell.shapes(layer_index).insert(pya.Box(bx, by, tx, ty))
            layer_index = layer_index+1
            bx = bx + width
            tx = tx + width
        by = by + height
        ty = ty + height
        bx = bx - self.columns*width
        tx = tx - self.columns*width

class TestLib(pya.Library):

  def __init__(self):
    self.description = "BasicSample"
    self.layout().register_pcell("BasicSample", BasicSample())
    self.register("BasicSampleLib")

# create and register the library
lib = TestLib()

# --------------------------------------------------------
#  Client code

lay = pya.Layout()
top = lay.create_cell("TOP")

output_file = "test.oas"

#para = { "l": pya.LayerInfo(1, 0) }
para = { }
pcell = lay.create_cell("BasicSample", "BasicSampleLib", para)
trans = pya.DCplxTrans.new(1, 0, False, 0, 0)
top.insert(pya.DCellInstArray(pcell.cell_index(), trans))

lay.write(output_file)

best regards quaeritis

klayoutmatthias commented 5 years ago

Hi,

Here is some explanation:

"l" is a "LayerInfo". This object provides the description of the layout layer in the layout file. This is the "canonical" way of describing a layer. A GDS file describes a layer in terms of layer and datatype number. Hence "l" features a layer and datatype number. This layer number is what you get when you use "l.layer". Some formats (like DXF or CIF) use names instead of numbers, hence there also is a "name" attribute in "LayerInfo".

"l_layer" is the layer index (sorry for the ambiguity). Internally, KLayout keeps a list of layers and the layer index identifies a layer in this list. Typically there is a correspondence: when you loaded a layout, each layout layer corresponds to one layer in this list. The order isn't specified, so GDS layer 10 may correspond to layer index 3 or anything else. There may also be layout layers that don't correspond to layers in the layout file (temporary or internal layers).

All methods that access shape information work with layer indexes. "l.layer" isn't this index, but the format layer number that helps finding this layer in the actual layout. As PCell parameters, GDS layer/datatypes or layer names are used, because these are independent on the actual internal order of the layer list. "l_layer" is a convenience shortcut to the layer index.

"layout.layers" is the number of layers in the layout. This will also include hidden internal layers (e.g. the layer for the guiding shapes). "layout.layer_indexes" gives a list of all layer indexes of non-hidden layers.

So bottom line is you should declare a parameter (i.e. "l") with layer type. Then use "l_layer" (parameter name + "_layer") to get the internal layer index of this layer.

Matthias

quaeritis commented 5 years ago

Hi,

thank you first for the quick help.

my code now looks like this:

import klayout.db as pya

class BasicSample(pya.PCellDeclarationHelper):

  def __init__(self):

    super(BasicSample, self).__init__()

    self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(0, 0, "silicon"))
    self.param("size", self.TypeDouble, "Sample size", default = 7000)
    self.param("columns", self.TypeInt, "Number of columns", default = 3)
    self.param("lines", self.TypeInt, "Number of lines", default = 3)

  def produce_impl(self):
    print("self.l_layer:", self.l_layer)
    #start_layer = 0
    #layer_index = self.layout.layer(start_layer, 0, "silicon")
    #print("layer_index:", layer_index)

    self.cell.shapes(self.l_layer).insert(pya.Box(0, 0, self.size, self.size))
    frame_size = 800
    width = (self.size-2*frame_size)/self.columns
    height = (self.size-2*frame_size)/self.lines
    bx = 0 + frame_size
    by = 0 + frame_size
    tx = width + frame_size
    ty = height + frame_size
    index = self.l_layer+1
    #print("i", layer_index)

    for column in range(1, self.columns+1):
        for line in range(1, self.lines+1):
            layer_info = pya.LayerInfo(index, 0, "c%i%i_l%i" % (column, line, index))
            layer_index = self.layout.insert_layer(layer_info)
            #layer_index = self.layout.layer(index, 0, "c%i%i_l%i" % (column, line, index))

            print("make a new box on new layer: index: %i, c%i%i, at cor %8.1f %8.1f %8.1f %8.1f, " % (index, column, line, bx, by, tx, ty))
            print("layer_index", layer_index)
            print("is_valid_layer:", self.layout.is_valid_layer(index))
            print("is_valid_layer:", self.layout.is_valid_layer(layer_index))
            self.cell.shapes(layer_index).insert(pya.Box(bx, by, tx, ty))
            index = index+1
            by = by + height
            ty = ty + height

        bx = bx + width
        tx = tx + width
        by = by - self.lines*height
        ty = ty - self.lines*height

class TestLib(pya.Library):

  def __init__(self):
    self.description = "BasicSample"
    self.layout().register_pcell("BasicSample", BasicSample())
    self.register("BasicSampleLib")

# create and register the library
lib = TestLib()

# --------------------------------------------------------
#  Client code

lay = pya.Layout()
top = lay.create_cell("TOP")

output_file = "test.oas"

#para = { "l": pya.LayerInfo(1, 0) }
para = { }
pcell = lay.create_cell("BasicSample", "BasicSampleLib", para)
trans = pya.DCplxTrans.new(1, 0, False, 0, 0)
top.insert(pya.DCellInstArray(pcell.cell_index(), trans))

lay.write(output_file)

Unfortunately, this only works for default = pya.LayerInfo (0, 0," silicon ") and not for default = pya.LayerInfo (1, 0," silicon ") or others.

For me it was irritating that I got l_layer = 0 with pya.LayerInfo (1, 0).

I think it would be useful that the attempt not to generate valid layer would cause an exception or?

In the doc I have found different names: layer == format layer number layer_index == layer index index == layer index?

From self.l_layer can I get layer_index? layer_index I need for self.cell.shapes the doc says I get from layout.layer only "layer" and from insert_layer layer_index?

So I should work with layer_index if possible?

(sorry for my bad English)

best regards quaeritis

klayoutmatthias commented 5 years ago

To wrap this up:

If you define a parameter (say "x") and give it "TypeLayer", then you will have these options in your code:

Can I close this issue with this explanation?