Problem instanciating PCell variants

Hi folks,

I have trouble getting PCells instantiated using Python. KLayout just crashed with wild Qt errors...

What I try to do is having a PCell which will add text markers to an array of PCells, which I at the moment instantiate by hand. So basically label arrays with "A B C....AA AB...".

Here's my code:

import pya
import math

from string import ascii_uppercase
from itertools import count, product, islice

def multiletters(seq):
for n in count(1):
    for s in product(seq, repeat=n):
    yield ''.join(s)

"""
This sample PCell implements a library called "MyLib" with a single PCell that
draws a circle. It demonstrates the basic implementation techniques for a PCell 
and how to use the "guiding shape" feature to implement a handle for the circle
radius.

NOTE: after changing the code, the macro needs to be rerun to install the new
implementation. The macro is also set to "auto run" to install the PCell 
when KLayout is run.
"""

class ArrayText(pya.PCellDeclarationHelper):
"""
The PCell declaration for the circle
"""

def __init__(self):

    # Important: initialize the super class
    super(ArrayText, self).__init__()

    # declare the parameters
    self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0))
    self.param("textsize", self.TypeInt, "Text Size", default = 30)
    self.param("ignore_j", self.TypeBoolean, "Ignore J", default = True)
    self.param("spread", self.TypeDouble, "Spread", default = 100)     
    self.param("n", self.TypeInt, "Number of points", default = 10)  
    self.param("mirror", self.TypeBoolean, "Mirror", default = False)   
    self.param("use_offset", self.TypeBoolean, "Use offset instead spread", default = False)      
    self.param("x_off", self.TypeDouble, "x offset", default = 160.)
    self.param("y_off", self.TypeDouble, "y offset", default = 0.)

def display_text_impl(self):
    # Provide a descriptive text for the cell
    return "Array Text (position...)"

def coerce_parameters_impl(self):

    # We employ coerce_parameters_impl to decide whether the handle or the 
    # numeric parameter has changed (by comparing against the effective 
    # radius ru) and set ru to the effective radius. We also update the 
    # numerical value or the shape, depending on which on has not changed.
    if self.n <= 0:
    self.n = 1

def can_create_from_shape_impl(self):
    # Implement the "Create PCell from shape" protocol: we can use any shape which 
    # has a finite bounding box
    return self.shape.is_box() or self.shape.is_polygon() or self.shape.is_path()

def parameters_from_shape_impl(self):
    # Implement the "Create PCell from shape" protocol: we set r and l from the shape's 
    # bounding box width and layer
    self.spread   = self.shape.bbox().width() * self.layout.dbu
    self.textsize = self.shape.bbox().width() * self.layout.dbu

def transformation_from_shape_impl(self):
    # Implement the "Create PCell from shape" protocol: we use the center of the shape's
    # bounding box to determine the transformation
    return pya.Trans(self.shape.bbox().center())

def produce_impl(self):
    # This is the main part of the implementation: create the layout
    characters = ascii_uppercase
    if self.ignore_j:
    characters = characters.replace("J","")
    try:  
    strings = list(islice(multiletters(characters), self.n))
    except GeneratorExit:
    pass



    basic_lib = pya.Library.library_by_name("Basic")
    txt_pcell = basic_lib.layout().pcell_declaration("TEXT")
    if basic_lib == None:
    print("BASIC LIB NOT FOUND")
    if txt_pcell == None:
    print("PCELL TEXT NOT FOUND")

    default_parameters_list = txt_pcell.get_parameters()  
    print("Got default parameter list. Type is: "+str(type(default_parameters_list)))
    print("Name\t\t\ŧdefault")
    for parameter in default_parameters_list:
    print(str(parameter.name)+"\t\t"+str(parameter.default))



    pcell_variants = []
    for string in strings:
    parameters = []
    new_parameters = { 'text':string,'layer':self.l_layer, 'mag':2.5 }    
    for param in default_parameters_list:
        if param.name in new_parameters.keys():
        print("Exchanged parameter "+str(param.name)+" default from "+str(param.default)+" to "+str(new_parameters[param.name])) 
        param.default = new_parameters[param.name]
        parameters.append(param)

    pcell_variant = self.layout.add_pcell_variant(basic_lib, txt_pcell.id(), parameters)
    pcell_variants.append(pcell_variant)


    t = pya.Trans(pya.Trans.r0(), self.x_off, self.y_off)
    new_cell = self.layout.create_cell("ArrayText")
    pcell_inst = new_cell.insert(pya.CellInstArray(pcell_variant, t))

Also the generator always raises an GeneratorExit Exception and a popup opens, which in my opinion shouldn't be the right behaviour as the GeneratorExit just stops the generator from iterating?

I'm thankful for any help and insight,
Cheers,
Matthias

PS: sorry, somehow the code markup doesn't work as I expected...

Comments

  • edited November -1

    To get code markup working, preface each line by (at least) four spaces. Not [code][/code]. Link

    This is most easily done by pasting into your favorite text editor, select all, press Tab to indent it by four spaces, then copy-paste here.

  • edited June 2016

    Hi,

    There is another Matthias around but it's not me :-)

    I had some trouble getting the code to work without the indents but I guess I got the idea.

    The code contains some bugs and here is my version. It requires KLayout 0.24.x or later. The main changes are in produce_impl. I tried to implement the positioning of the text labels assuming the parameters are given in micron units, but other things like proper scaling are missing:

    import pya
    import math
    
    from string import ascii_uppercase
    from itertools import count, product, islice
    
    def multiletters(seq):
      for n in count(1):
        for s in product(seq, repeat=n):
          yield ''.join(s)
    
    class ArrayText(pya.PCellDeclarationHelper):
      """
      The PCell declaration for the circle
      """
    
      def __init__(self):
    
        # Important: initialize the super class
        super(ArrayText, self).__init__()
    
        # declare the parameters
        self.param("l", self.TypeLayer, "Layer", default = pya.LayerInfo(1, 0))
        self.param("textsize", self.TypeInt, "Text Size", default = 30)
        self.param("ignore_j", self.TypeBoolean, "Ignore J", default = True)
        self.param("spread", self.TypeDouble, "Spread", default = 100)
        self.param("n", self.TypeInt, "Number of points", default = 10)
        self.param("mirror", self.TypeBoolean, "Mirror", default = False)
        self.param("use_offset", self.TypeBoolean, "Use offset instead spread", default = False)
        self.param("x_off", self.TypeDouble, "x offset", default = 160.)
        self.param("y_off", self.TypeDouble, "y offset", default = 0.)
    
      def display_text_impl(self):
        # Provide a descriptive text for the cell
        return "Array Text (position...)"
    
      def coerce_parameters_impl(self):
    
        # We employ coerce_parameters_impl to decide whether the handle or the
        # numeric parameter has changed (by comparing against the effective
        # radius ru) and set ru to the effective radius. We also update the
        # numerical value or the shape, depending on which on has not changed.
        if self.n <= 0:
          self.n = 1
    
      def can_create_from_shape_impl(self):
        # Implement the "Create PCell from shape" protocol: we can use any shape which
        # has a finite bounding box
        return self.shape.is_box() or self.shape.is_polygon() or self.shape.is_path()
    
      def parameters_from_shape_impl(self):
        # Implement the "Create PCell from shape" protocol: we set r and l from the shape's
        # bounding box width and layer
        self.spread = self.shape.bbox().width() * self.layout.dbu
        self.textsize = self.shape.bbox().width() * self.layout.dbu
    
      def transformation_from_shape_impl(self):
        # Implement the "Create PCell from shape" protocol: we use the center of the shape's
        # bounding box to determine the transformation
        return pya.Trans(self.shape.bbox().center())
    
      def produce_impl(self):
        # This is the main part of the implementation: create the layout
        characters = ascii_uppercase
        if self.ignore_j:
          characters = characters.replace("J","")
        try:
          strings = list(islice(multiletters(characters), self.n))
        except GeneratorExit:
          pass
    
        basic_lib = pya.Library.library_by_name("Basic")
        txt_pcell = basic_lib.layout().pcell_declaration("TEXT")
        if basic_lib == None:
          print("BASIC LIB NOT FOUND")
        if txt_pcell == None:
          print("PCELL TEXT NOT FOUND")
    
        # converts x_off, y_off and spread from µm to DBU:
        x = int(0.5 + self.x_off / self.layout.dbu)
        y = int(0.5 + self.y_off / self.layout.dbu)
        dx = int(0.5 + self.spread / self.layout.dbu)
    
        for string in strings:
    
          parameters = { "text": string, "layer": self.l, "mag": 2.5 }
          pcell_variant = self.layout.add_pcell_variant(basic_lib, txt_pcell.id(), parameters)
    
          t = pya.Trans(pya.Trans.R0, x, y)
          self.cell.insert(pya.CellInstArray(pcell_variant, t))
    
          x += dx
    
    
    class ARRAY_TEXT_LIBRARY(pya.Library):
      """
      The library where we will put the PCell into
      """
    
      def __init__(self):
    
        # Set the description
        self.description = "Array Text Library"
    
        # Create the PCell declarations
        self.layout().register_pcell("Array Text", ArrayText())
        #self.layout().register_pcell("Array Numbers", ArrayNumbers())
        # That would be the place to put in more PCells ...
    
        # Register us with the name "MyLib".
        # If a library with that name already existed, it will be replaced then.
        self.register("Array Text Library")
    
    
    #Instantiate and register the library
    ARRAY_TEXT_LIBRARY()
    

    I shall curse the god of evil programming who made the Python guys use exceptions for stopping a loop ... But I know my way around and made the debugger feature configurable: go to "Macro Development Setup" (the "wrench" button on the right of the macro IDE tool bar) and deselect "Ask whether to stop in debugger on exception" on the "Debugging" page.

    The first Matthias

  • edited November -1

    Thanks, it worked nicely with your code :)

Sign In or Register to comment.