can_create_from_shape_impl doesn't work in Python

edited November 2015 in Ruby Scripting

Hi Matthias,

I found this problem with Python PCells in both OSX and Windows 8.1 (I made myself a VirtualBox to help debug these things).

I can't get the "PCell from shape" protocol working for Python; it works for Ruby.

http://www.klayout.de/doc/programming/ruby_pcells.html

Specifically, I tried your example MyLib.Circle. I create a box, select it, and use the menu Convert to PCell. Then I look in the menu to find "MyLib.Circle".

When I run the Ruby script, it shows up in the menu.

When I run the Python script, to does not.

Thanks

Lukas

Comments

  • edited November -1

    Hi Lukas,

    yes, you're right. The Python's sample is not working as expected with respect to the shape to PCell translation.

    There is more than a single issue here. A workaround is to employ the "raw" methods, not the convenience methods (the ones with the _impl suffix). Fortunately the overhead is not much.

    Here is the working implementation for the "Circle" PCell:

    # These methods replace "can_create_from_shape_impl",
    # "transformation_from_shape_impl" and "parameters_from_shape_impl":
    
      def can_create_from_shape(self, layout, shape, layer):
        return shape.is_box() or shape.is_polygon() or shape.is_path()
    
      def transformation_from_shape(self, layout, shape, layer):
        return pya.Trans(shape.bbox().center())
    
      def parameters_from_shape(self, layout, shape, layer):
        self._param_values = []
        for pd in self._param_decls:
          self._param_values.append(pd.default)
        self.r = shape.bbox().width() * layout.dbu / 2
        self.l = layout.get_info(layer)
        return self._param_values  
    

    Thanks for mentioning this.

    Best regards,

    Matthias

  • edited November 2015

    Thank you. That part works, including for a Path shape.

    However, for the Circle above, it crashes when I move the Handle point... Can you also update the coerce function as well?

  • edited November -1

    Further problem.

    Does this problem also extend to "produce_impl"?

    What I'd like to do is query the PCell for geometry information derived during the PCell implementation, similar to described here: http://www.klayout.de/doc/programming/ruby_pcells.html.

    Specifically, I added to the PCell a Point location parameter:

    self.param("p1", self.TypeShape, "DPoint location of pin1", default = pya.Point(-10000, 0), hidden = True, readonly = True)
    

    Then I set this in the produce_impl code:

    p2c = pya.Point(0, y)
    self.set_p2 = p2c
    self.p2 = p2c
    

    and I try to access this value after instantiating the PCell in another PCell:
    pcell = ly.create_cell ( ... )
    instance = self.cell.insert(pya.CellInstArray(pcell.cell_index(), trans))
    p2 = pcell.pcell_parameters_by_name()['p2'] # Point, within the PCell's coordinates

    but I am only able to get back the default values.

    I tried putting my production code, where I use self.p2 or self.set_p2, in both the following functions, but that didn't help:

     def produce(self, layout, layers, parameters, cell):
     def produce_impl(self):
    
  • edited November 2015

    Hi Lukas,

    it does not apply to produce_impl. With the above patches the sample and handles work for me on Linux.

    But given the observations regarding random crashes (see http://klayout.de/forum/comments.php?DiscussionID=758&page=1#Item_2) I think everything is possible. So I'd suggest to let this topic settle until the reason for the other crash is found.

    Regarding the PCell-in-PCell instantiation issue: I wonder in general why people are so keen to do this. There is absolutely no advantage of instantiating a PCell inside another one. It's just awfully complex. A PCell is a editing feature and one cannot edit a PCell inside a PCell, so what's the use of that?

    Modularisation? I have discussed that topic in this forum several times. If one wants modularisation then why not doing that on code level?

    I mean this:

    # A function to create a layout for PCell A with parameters p1, p2, p3 ..
    def create_layout_A(layout, cell, p1, p2, p3 ...):
      ... create the PCell A layout 
    
    # A function to create a layout for PCell B using the code for PCell A inside this code
    def create_layout_B(layout, cell, q1, q2, q3 ...):
      create_layout_A(layout, cell, q2 * 2, q1 + q2, ...)
      ... and maybe create some layout for PCell B alone
    
    # PCell declaration for A using create_layout_A
    class PCellA(pya.PCellDeclaration):
      ...
      def produce_impl(self):
        create_layout_A(self.layout, self.cell, self.a, self.b, ...)
      ...
    
    # PCell declaration for B using create_layout_B
    class PCellB(pya.PCellDeclaration):
      ...
      def produce_impl(self):
        create_layout_B(self.layout, self.cell, self.x, self.y, ...)
      ...
    

    In other words: the PCells just provide the access points to the code generation functions. But the functions itself don't need PCell's - they don't even need usual cells as subcells. Instead of creating PCell instance you just call the function which creates the layout where you need it. Unless you benefit from the memory reduction effect of big instance arrays (for example for a memory array generator), creating instances within a PCell is hardly required. If you really need to do this, you'll find some sample code in various places in this forum.

    Best regards,

    Matthias

  • edited November -1

    Hi Matthias,

    Regarding your question why people want PCells-in-PCells:

    • I think of PCells as having two parts: a) the parameterized code, and b) a way to instantiate the layout using a GUI. I agree with you about the alternative which is to simply call the production code. I've explored this.

    • The memory reduction is one nice aspect of nested PCells, making for smaller file sizes.

    • It's nice to look in the cell listing to see the sub-cells being created; your PCell-PCell implementation nicely compacts the list so that if you have many copies of a sub-PCell with the same parameter set, only one is shown. I can use this to see which parameters are being used in the layout.

    • Since the PCell is an object, you can query it. e.g., as above, you can ask a sub-PCell about it's pin locations, bounding box, etc. Then the main PCell can perform routing / placement with this information. This could be done using functions (like above, create_layout_B), but then you need to have specific "return" parameters. But if you perform a modification to the sub-PCell (in the same code), it may be nice to query it after to see what parameters have changed. (however, one may argue that it's better to only draw the sub-PCell once...)

    • Finally, it seems very elegant to me. The user can look at the library in the GUI, and easily program the same thing (including within their own PCells). Again, I'm sure examples could made to use the production code instead of the PCell GUI code...

  • edited November -1

    Hi Lukas,

    Ok, I see I'm not convincing ... :-)

    Here is another forum discussion with some pointers about the topic of calling PCells inside other PCells: http://klayout.de/forum/comments.php?DiscussionID=701&page=1#Item_4.

    Regarding your question 7 days ago, I hope this explanation clarifies the question about parameters and why you cannot set parameters inside a PCell implementation persistently:

    The PCell class is not the PCell itself. In fact, the PCell class only provides the algorithms, not the data itself. The underlying PCellDeclaration class provides the PCell's definition parameters and the basic functions to implement the PCell (the design pattern employed here is the Strategy pattern).

    If "produce_impl" is called, the declaration class will carry some attributes which hold the parameters of the PCell variant to be produced. This is a convenience feature to simplify implementation, but I see it makes it difficult to keep apart the PCell object and the declaration object. After producing the layout, the parameters are discarded. So updating them won't have an effect. This is desired since layout production should not alter the parameter values.

    One exception is the "coerce_parameters_impl" method which is supposed to modify the parameter values in a way to make them "consistent". This method may modify parameter values and these modified values are stored back in the "real" PCell object. But beware that this method is only called when the parameters are changed manually, not if they are modified in a script.

    Best regards,

    Matthias

  • edited November -1

    Hi Matthias,

    Regarding getting post-implementation parameters from a PCell, given what you said, and as a work around, my solution is to search the cell's geometries, to find what I need.

    def find_PCell_pins(subcell):
      # find the location of the pins in the cell.
      # example usage:
      # ly = pya.Application.instance().main_window().current_view().active_cellview().layout() 
      # pcell = ly.create_cell("Waveguide_Bend", "SiEPIC", { } )
      # find_PCell_pins( pcell )
    
      LayerPinRecN = subcell.layout().layer(LayerPinRec)
      iter2 = subcell.begin_shapes_rec(LayerPinRecN)
      pins = {} # dictionary for pin information
      while not(iter2.at_end()):
        # Find text label for PinRec, to get the port numbers
        if iter2.shape().is_text():
          texto= iter2.shape().text.transformed(iter2.itrans())
          x = texto.x
          y = texto.y
          text = iter2.shape().text
          print( "PinRec label: %s at (%s, %s)" % (text, x, y) )
          pins[ str(text.string) + "_x" ] = x
          pins[ str(text.string) + "_y" ] = y
        iter2.next()
    
      return pins
    

    Regarding PCells-in-PCells, I'm sorry I'm not easily convinced on this. I've spent many years having production functions calling other productions functions, as you propose, and I am very much attracted to what you have (inadvertently?) allowed us to do here, for the reasons I list. Sorry I'm not convincing you either!

    regards,

    Lukas

  • edited December 2015

    Hi Lukas,

    I'm not saying it's not possible to use PCells inside PCells - if you want to place PCells inside others you can do so. I just feel it's more complicated this way.

    Regarding your workaround: It's a nice idea in fact.

    The background of the whole topic is that modifying parameters in produce_impl basically means to modify the inputs for production. The design is regarding produce_impl as a non-modifying operation - otherwise it would have to handle the implications of modifications within produce_impl ... that's basically entering an infinite loop of dependencies. So I ruled out that possibility by disallowing produce_impl to modify the parameters.

    The only function which is allowed to do so is coerce_parameters. But that function is called only on special occasions (when the PCell user interface is opened for example).

    I understand your requirement is to provide pin positions in addition to the layout. Passing them though layout is one way, but - caution: I'm coming back to my idea of code modularisation - you could provide a function for this computation and use it once in your production code and then in the client code which instantiates the PCell.

    You could even provide this function as a method of the PCellDeclaration class. Like this:

    class ChildPCellDeclaration(pya.PCellDeclarationHelper):
      ...
      def computePinsPositionsFromParameters(self, param):
        ...
      def produce_impl(self):
        ... 
    
    
    class ClientPCellDeclaration(pya.PCellDeclarationHelper):
      ...
      def produce_impl(self):
        ...
        child_param = ...
        child_pcell_decl = self.layout().pcell_declaration("CHILD_PCELL")
        pcell = self.layout().create_pcell_variant(child_pcell_decl.id(), child_param)
        instance = self.cell().insert(pya.CellInstArray(pcell.cell_index(), ...))
        ...
        pins = child_pcell_decl.computePinsPositionsFromParameters(child_param)
        ...
    

    This way you maintain the encapsulation of the code for the PCell inside the child PCell's declaration class and expose the add-on functionality (computation of pin locations) through methods added to it. In other words, you don't store the computed parameters, but you provide an interface to compute them when required.

    Best regards,

    Matthias

Sign In or Register to comment.