Refresh library entries programatically

edited September 19 in KLayout Support

Hello,

I'm attempting to implement a PCell library which references itself to construct more complex PCells. In order to do that I need to populate the library without destroying it and rebuilding it since it would cause to unlink all existing PCells.

I achieved to do that by creating the library and using the Library reference instead of using the "self", but the library entries don't automatically refresh like it does when executing the regular self.register(xx).
I tried using self.refresh(), lib.register(xx) again, lib.refresh(), updating the QWidget associated with the libraries, and there's no "library.unregister()" function to try (actually nor "layout.unregister_pcell()" but there is "remove_technology". I guess library.delete() would be the equivalent).
The only solution I found was to switch the selected library view to the "Basic" one and then back.

Here's a sample:

import pya, os
import importlib.util as ilib

lv = pya.Application.instance().main_window().current_view()
wid = lv.widget()
widLib = lv.widget().libraries_frame()

def ezimport(filePath):
  # Makes it less verbose to import modules found from the executing fileName
  # → Script executed from a technology
  extFilePath = filePath.split("/")
  selfPath = os.path.join(os.path.dirname(__file__),*extFilePath)
  spec = ilib.spec_from_file_location(extFilePath[-1], selfPath)

  md = ilib.module_from_spec(spec)
  spec.loader.exec_module(md)
  return md

ent1 = ezimport("ent1_PCell.py")
ent2 = ezimport("ent2_PCell.py")

class NEW_LIB(pya.Library):

  def __init__(self):

    lib = pya.Library.library_by_name("NEW_LIB")
    if not(lib):
      # Set the description
      self.description = "New Library"
      self.register("NEW_LIB")

    self.lib = pya.Library.library_by_name("NEW_LIB")

    # Create the PCell declarations
    # Register PCells to the already existing NEW_LIB already exists
    self.lib.layout().register_pcell("ENTRY1", ent1.PCell1())
    self.lib.layout().register_pcell("ENTRY2", ent2.PCell2())

    self.lib.register("NEW_LIB") # Only registers internally, won't update the library view
    self.refresh() # Won't do a thing
    lib.refresh() # Won't do a thing

    wid.update() # Won't do a thing
    wid.repaint() # Won't do a thing
    widLib.update() # Won't do a thing
    widLib.repaint() # Won't do a thing

# Instantiate and register the library
NEW_LIB()

Thank you,
Gerard

Comments

  • edited September 19

    I found another way to solve it (through the intended way).
    Using self.register() by the end of the library class, and send self as a parameter for the PCell1 class (be it PCell2 is to be a child of PCell1). The child PCells would be registered inside the definition of the parent PCell class, so only the parent PCells are required to be registered explicitly in the library class.
    One thing I notice however is that the sub-cell name has the library repeated, and hiding the instanced PCell won't hide a standalone one.

    Here's a simplified full code:

    import pya
    
    class PCell2(pya.PCellDeclarationHelper):
      def __init__(self, lib):
        super(PCell2, self).__init__()
    
        self.lib = lib
    
      def display_text_impl(self):
        return "PCell2"
    
      def produce_impl(self):
        poly = pya.Polygon([
          pya.Point(300,0),
          pya.Point(600,0),
          pya.Point(500,200),
          pya.Point(300,200),
        ])
    
        self.cell.shapes(pya.LayerInfo(1)).insert(poly)
    
    
    class PCell1(pya.PCellDeclarationHelper):
    
      def __init__(self, lib):
        super(PCell1, self).__init__()
    
        self.lib = lib
        self.PCell2Id = self.lib.layout().register_pcell("PCell2", PCell2(lib))
    
      def display_text_impl(self):
        return "PCell1"
    
      def produce_impl(self):
    
        inst = self.layout.add_pcell_variant(self.lib, self.PCell2Id, [])
    
        poly = pya.Polygon([
          pya.Point(0,0),
          pya.Point(200,0),
          pya.Point(200,200),
          pya.Point(0,200),
        ])
    
        self.cell.shapes(pya.LayerInfo(1)).insert(poly)
    
        self.cell.insert(pya.CellInstArray(inst, pya.Trans()))
    
    
    class PCELL_LIB(pya.Library):
    
      def __init__(self):
        self.layout().register_pcell("PCell1", PCell1(self))
        self.register("PCELL_LIB")
    
    PCELL_LIB()
    
  • edited September 19

    Hi @Tabra,

    You can register multiple PCells per library.

    Why not just doing:

    class PCell2(pya.PCellDeclarationHelper):
    ...
    
    class PCell1(pya.PCellDeclarationHelper):
    
      def __init__(self, lib):
        super(PCell1, self).__init__()
    
        self.lib = lib
        self.PCell2Id = self.lib.layout().pcell_id("PCell2")
    ...
    
    class PCELL_LIB(pya.Library):
    
      def __init__(self):
        self.layout().register_pcell("PCell2", PCell2(self))   # needs to come first
        self.layout().register_pcell("PCell1", PCell1(self))
        self.register("PCELL_LIB")
    
    PCELL_LIB()
    

    So you just need to register in the proper order. If you delay the "pcell_id" calls to the point when you really need it (in produce_impl), you could even realize recursive dependencies. But I'd say that is bad style.

    Matthias

  • Hi Matthias,
    Right, this is the best approach. But an underlying issue remains: When converting the PCells to Static (or saving them in GDS to transfer to another software), the PCells inside another PCell and the same PCells outside the parent PCell are differentiated when they shouldn't.
    An example:

    In this example, "Spring" calls 2 PCells to build a custom shape by using them as puzzle pieces. You can see that the same PCell corner is being used but one outside the Spring PCell and the rest inside.

    Hiding the PCells inside the Spring will hide all instances of the PCell, but not outside the Spring, and viceversa.

    Converting the layout to static also reflects the fact that these 2 are fundamentally disconnected, when they shouldn't as the exact same reference is used.

    If I may add something, I would like to know if there's an easy way to name the PCells so when converting them to static they have a meaningful name.

    Thank you,

    Gerard

  • I don't know how the NNS_LIB.Spring PCell is made, but the references inside that PCell are different from the outside cell. You can tell from the name: the one is called NNS_LIB.SpringCorner the other one NNS_LIB.NNS_LIB.SpringCorner. That indicates that you created the SpringCorner inside the PCell with a library reference. That is not needed as the Spring PCell is already inside the library.

    I assume you did something like

    corner = layout().create_cell("SpringCorner", "NNS_LIB", parameters)
    

    This will create a library reference inside a cell that is already inside a library. KLayout's differentiates between local ("static") cells are library references ("proxies"). So when you instantiate a "Spring" PCell the corner effectively becomes a double proxy while outside the "SpringCorner" cell is a single proxy. That means these cells are different.

    I think in your code it's sufficient to write:

    corner = layout().create_cell("SpringCorner", parameters)
    

    as you are already local to "NNS_LIB".

    Rule of thumb: KLayout is not Virtuoso!

    Matthias

  • Wow, this is exactly what I wanted from the start.
    Thank you very much!!

    Gerard

Sign In or Register to comment.