Round_path PCell in python, create_cell

edited November 2015 in Layout

Hi Matthias,

I would like to use a guiding shape (path) to create a round_path.

In a Python script, I tried as follows:

  • create a path, and shape, in my layout
  • do a pcell_decl for the Basic.ROUND_PATH PCell
  • get the PCell to take the guiding parameters from the shape, by trying to trigger "parameters_from_shape_impl" and "transformation_from_shape_impl"
  • instantiate the PCell

It's the last part I can't figure out how to do.

pcell_decl.can_create_from_shape(ly, shape, SiLayer) return True.

Then trying to get the parameters to update:

a = pcell_decl.parameters_from_shape(ly, shape, SiLayer)
b = pcell_decl.transformation_from_shape(ly, shape, SiLayer)
pcell_decl.produce(ly, SiLayer, a, cell)

But that doesn't work...

I then tried as follows:

  • create a path: wg_path = pya.Path(a1, w)
  • do a pcell_decl for the Basic.ROUND_PATH PCell
  • include the path in param = { "npoints": npoints, "radius": radius, "path": wg_path, "layer": SiLayer_name }
  • get the PCell to take the guiding parameters from the shape
  • instantiate the PCell

But then I get a completely empty layout...

import pya
import numpy as n

cell = pya.Application.instance().main_window().current_view().active_cellview().cell
ly = pya.Application.instance().main_window().current_view().active_cellview().layout() 

# clean all cells within "cell"
ly.prune_subcells(cell.cell_index(), 10)

SiLayer_name = pya.LayerInfo(1, 0)
SiLayer = cell.layout().layer(SiLayer_name)

# Create paths for waveguides
points = [ [0, 1], [10,1], [10, 127], [0,127] ] 
w=0.5
radius = 10

dbu = 1 / cell.layout().dbu
points=n.array(points)*dbu
w*=dbu

a1 = []
for p in points:
  a1.append (pya.Point(p[0], p[1]))

wg_path = pya.Path(a1, w)
print wg_path 

shape = cell.shapes(SiLayer).insert(wg_path)

# for the basic library
lib = pya.Library.library_by_name("Basic")
if lib == None:
   raise Exception("Unknown lib 'Basic'")

pcell_decl = lib.layout().pcell_declaration("ROUND_PATH");
if pcell_decl == None:
   raise Exception("Unknown PCell 'ROUND_PATH'")

npoints = 400
param = { "npoints": npoints, "radius": radius,  "layer": SiLayer_name }
#param = { "npoints": npoints, "radius": radius, "path": wg_path, "layer": SiLayer_name }
pv = []
for p in pcell_decl.get_parameters():
   print p.name
   print p.default
   if p.name in param:
     pv.append(param[p.name])
   else:
     pv.append(p.default)

#pcell_decl.coerce_parameters(ly, pv)

#if pcell_decl.can_create_from_shape(ly, shape, SiLayer):
  # trigger "parameters_from_shape_impl" and "transformation_from_shape_impl"
  #pcell_decl.shape = shape
#  a = pcell_decl.parameters_from_shape(ly, shape, SiLayer)
#  b = pcell_decl.transformation_from_shape(ly, shape, SiLayer)
#  pcell_decl.produce(ly, SiLayer, a, cell)

# create the layout, presumably to trigger "produce_impl"
pcell_index = ( ly.add_pcell_variant(lib, pcell_decl.id(), pv) )

# Configure the cell location
t = pya.Trans(pya.Trans.R0, 0 * dbu, 0 * dbu) 

# Place the PCell
cell.insert(pya.CellInstArray(pcell_index, t))

I also notice that manually editing the GUIDING_SHAPES points in the GUI causes KLayout to crash.

Finally, the function "create_cell" for PCells, which you mention here – http://klayout.de/forum/comments.php?DiscussionID=630, and pcell_variant (http://klayout.de/forum/comments.php?DiscussionID=632) – is there a way to use this for what I'm trying to do?

I would sure like to simplify the PCell instantiation... Do you have an example of how to use this? There are several discussion threads that show the complicated way (as in the code above).

Thank you very much.

Comments

  • edited November 2015

    I noticed that the ROUND_PATH expects a DPath object instead of Path. And also I had to take out the dbu part (looks like the path units are in microns already)

    So I tried that instead:

    a1 = []
    for p in points:
      a1.append (pya.DPoint(p[0], p[1]))
    wg_path = pya.DPath(a1, w)
    
    inst = cell.insert(pya.CellInstArray(pcell_index, t))
    cell.change_pcell_parameter(inst,"path", wg_path )
    

    And it worked!

    I still wouldn't mind knowing how to:

    • trigger the PCell to use guiding shapes, via script
    • use the create_cell function to instantiate a new PCell
  • edited November -1

    Hi Lukas,

    the second approach is the valid one, yes.

    In general, the parameters_from_shape and transformation_from_shape methods are provided for PCell implementors to supply a recipe to the application about how to turn a shape into a specific PCell. It takes "D" type shape objects since PCell parameters should be independent of the database unit in general.

    Calling it from client code (instantiating a PCell) is possible, but it's probably easier to use the direct approach you found.

    Sorry for that lack of documentation.

    Best regards,

    Matthias

  • edited November 2015

    Hi Matthias,

    I am able to get the create_cell and cell.insert approach for PCell instantiation to work on:

    • my PCell (based on your example, Circle),
    • Basic.TEXT
    • Basic.ROUND_PATH

    But it doesn't work for:

    • Basic.CIRCLE
    • Basic.DONUT

    Specifically, I get the following output for these two problematic PCells:

    CIRCLE:

    Parameter: npoints, Value: 64

    Parameter: radius, Value: 10.1

    Parameter: layer, Value: 1/0

    Parameter: handle, Value: -0.1,0

    Parameter: actual_radius, Value: 0.0

    DONUT:

    Parameter: radius1, Value: 10.25

    Parameter: layer, Value: 1/0

    Parameter: radius2, Value: 0.2

    Parameter: actual_radius2, Value: 0.0

    Parameter: actual_radius1, Value: 0.0

    Parameter: handle1, Value: -0.1,0

    Parameter: handle2, Value: -0.2,0

    Parameter: npoints, Value: 64

    What are the variables "handle"? Do I need to set them? They aren't exposed in the GUI. And isn't "actual_radius" set by your script after entering radius?

    Here is a screenshot showing the cells that work, and those that don't. Notice the cell name is missing the correct radius:

    https://www.dropbox.com/s/zqnldh6jfz0bhql/Screenshot%202015-11-10%2021.32.46.png?dl=1

    Here is the Python code:

        # Python script
    
        # Script to test KLayout's "Basic" PCell library components
        # by Lukas Chrostowski, 2015/11
    
        import pya
    
    
        def PCell_get_parameter_list ( cell_name, library_name ):
          # function to list all the parameters & defaults for a PCell
          # example usage:
          # PCell_get_parameter_list("CIRCLE", "Basic")
    
          print "* def PCell_get_parameter_list ( %s, %s): " % ( cell_name, library_name )
    
          lib = pya.Library.library_by_name(library_name)
          if lib == None:
            raise Exception("Unknown lib '%s'" % library_name)
    
          pcell_decl = lib.layout().pcell_declaration(cell_name);
          if pcell_decl == None:
             raise Exception("Unknown PCell '%s'" % cell_name)
    
          type2s = ['TypeBoolean', 'TypeDouble', 'TypeInt', 'TypeLayer', 'TypeList', 'TypeNone', 'TypeShape', 'TypeString']
    
          for p in pcell_decl.get_parameters():
            if ~p.readonly:
              print "Name: %s, %s, unit: %s, default: %s, description: %s%s" % \
                (p.name, type2s[p.type], p.unit, p.default, p.description, ", hidden" if p.hidden else ".")
    
        def PCell_get_parameters ( pcell ):
          # function to list the values for all parameters for an intantiated PCell
          # example usage:
          # ly = pya.Application.instance().main_window().current_view().active_cellview().layout() 
          # pcell = ly.create_cell("CIRCLE", "Basic", { "radius": 10, "layer": pya.LayerInfo(1, 0) } )
          # PCell_get_parameters( pcell )
    
          print "* def PCell_get_parameters ( %s ):" % pcell 
          print pcell.pcell_parameters()
    
          params = pcell.pcell_parameters_by_name()
    
          for param in params.keys():
            print "Parameter: %s, Value: %s" % (param, params[param])
    
    
    
        cell = pya.Application.instance().main_window().current_view().active_cellview().cell
        ly = pya.Application.instance().main_window().current_view().active_cellview().layout() 
    
        # clean all cells within "cell"
        ly.prune_subcells(cell.cell_index(), 10)
    
        layer = pya.LayerInfo(1, 0)
    
        PCell_get_parameter_list("CIRCLE", "Basic")
        pcell = ly.create_cell("CIRCLE", "Basic", { "radius": 10.1, "layer": layer } )
        t = pya.Trans(0, 0)
        cell.insert(pya.CellInstArray(pcell.cell_index(), t))
        PCell_get_parameters ( pcell )
    
    
        PCell_get_parameter_list("DONUT", "Basic")
        pcell = ly.create_cell("DONUT", "Basic", { "radius1": 9.75, "radius1": 10.25, "layer": layer } )
        cell.insert(pya.CellInstArray(pcell.cell_index(), t))
        PCell_get_parameters ( pcell )
    
    
        PCell_get_parameter_list("TEXT", "Basic")
        pcell = ly.create_cell("TEXT", "Basic", { "text": "KLayout + Python = fun!", "layer": layer } )
        cell.insert(pya.CellInstArray(pcell.cell_index(), t))
        PCell_get_parameters ( pcell )
    
    
        PCell_get_parameter_list("ROUND_PATH", "Basic")
        points=n.array([ [0,0], [10,0], [10,10] ])
        a1 = []
        for p in points:
          a1.append (pya.DPoint(p[0], p[1]))
        wg_path = pya.DPath(a1, w)
        param = { "npoints": 100, "radius": 4, "path": wg_path, "layer": layer }
        pcell = ly.create_cell("ROUND_PATH", "Basic", param )
        cell.insert(pya.CellInstArray(pcell.cell_index(), t))
        PCell_get_parameters ( pcell )
    
  • edited November -1

    One more attempt to fix this, using coerce_parameters. The documentation states that "The reimplementation may modify the parameters in a way that they are usable for the produce method", so I hoped it would fix the parameters...

    pcell_decl = pcell.pcell_declaration()
    pcell_decl.get_parameters()
    pcell_decl.coerce_parameters(ly, pcell_decl.get_parameters())
    PCell_get_parameters ( pcell )
    
  • edited November -1

    Hi Lukas,

    the design of the Basic library was not to provide reusable components but rather drawing features, so the actual implementation is less straightforward than their simple functionality might suggest.

    I can explain that for a circle: the actual parameters used for generating the circle are "actual_radius", "layer" and "npoints". "radius" and "handle" are auxiliary parameters which act as two independent ways of specifying the radius: numerically or graphically. The handle is the little dot you can drag to set the radius (a "guiding shape"), the radius parameter is presented on the PCell's parameter page. Both parameters are basically alternatives and each one has to be synchronized with the other one. That is what "coerce_parameters" does: by comparing against the actual radius, it is able to determine which parameter was changed and will synchronize the other one.

    KLayout will call "coerce_parameters" on every PCell when some parameter has changed to give the PCell implementation a chance to establish a valid and consistent parameter set ("coerce" it into a consistent state). That's a general feature, but not required for you to use the PCell generator code. Just set "actual_radius", "npoints" and "layer" as the basic parameters.

    This is not the first time I get a question about this. Frankly, I wonder why people like to spend effort in understanding this concept. Personally, I'd prefer to code the few lines that are required to produce a circle myself. Many Basic PCells have some built in magic which is supposed to provide user convenience. This will interfere with your attempts to harvest their layout.

    But I basically understand the desire to use Basic.TEXT and maybe Basic.ROUND_PATH. These are the two PCells contributing considerable functionality within their implementation code (although that's not more than an equivalent of a few hundred lines of code). If required, I'd rather expose their functionality as part of the standard API than to encourage using PCells as pure layout generators.

    Best regards,

    Matthias

  • edited November -1

    Thanks Matthias. My main objective was to understand how to call the layout generation function from PCells in general (including my own). I agree that the circle is simple so make more sense to code in yourself.

    Yes, the ROUND_PATH functionality is useful. However, I would love to be able to extend it, e.g., adding Bezier curves. So I would love to see your implementation to build upon it.

  • edited November 2015

    How do you get the width of the path from the ROUND_PATH PCell?

    Here is the list returned for the PCell:

    • def PCell_get_parameter_list ( ROUND_PATH, Basic):

    • Name: layer, TypeList, unit: , default: None, description: Layer.

    • Name: radius, TypeDouble, unit: micron, default: 0.1, description: Radius.
    • Name: path, TypeNone, unit: , default: (0,0;0.2,0;0.2,0.2) w=0.1 bx=0 ex=0 r=false, description:
    • Name: npoints, TypeBoolean, unit: , default: 64, description: Number of points / full circle..
  • edited November -1

    Hi Lukas,

    the width of the path is taken from the path itself. The PCell takes this path and modifies the spine points by inserting new points where required. The remaining parameters of the path are not touched.

    Best regards,

    Matthias

  • edited November -1

    I didn't notice, but the "path" is actually a DPath object, which includes the parameter "w". So I can read this in order to write an "undo" ROUND_PATH function.

Sign In or Register to comment.