Duplicate PCell with changing parameters (for Guard Rings)

edited March 2016 in Ruby Scripting
Hello all,

I have some minor experience in KLayout, but sadly none in Ruby. I want to create a macro (Ruby or Python, there are just many more examples for Ruby in KLayout) that takes a selected PCell STROKED_POLYGON (a BOX that I convert to PCell and then adjust width and radius for rounded corners), duplicates it, changes the parameters from some list (ideally reading from a simple csv-file). The way to get there isn't important for me. I just want to draw a first "template" structure and then automate the replication.

So far I haven't been able to produce a PCell copy of a STROKED_POLYGON that actually show up in the layout.
It seems to be not that complicated, but I'd be very happy about some pointers or examples for duplication / STROKED_POLYGON creation. Especially since there are two selectable entities (the original BOX and the STROKED_POLYGON PCell with the parameters) and I have no idea how to copy both.

Thanks a lot for any help :)
Johannes

Comments

  • edited March 2016

    The easiest way I think is to use "PCell array generator.lym", which is part of TRT. Once you've installed TRT per the instructions at that link, reopen KLayout. Open a new layout. Click the menu at the top and then Cells > PCell array.*

    There is an example spreadsheet file in the same "Cells" subfolder, that prints "KLAYOUT RULES" three times, three different magnifications. Direct the dialog to point to that file and choose your other parameters then click OK to see the result.

    It won't copy an existing PCell and modify its parameters, it will generate new PCells with new parameters specified by a spreadsheet. (Those PCells can be built-in ones or your own custom ones.) So it is not exactly what you asked for, but based on your description I think this would be an adequate solution. You would specify the STROKED_POLYGON parameters (like the points, the radius, the layer, ...) in the Excel sheet and it would draw all of them for you. Let us know if it works for you.

    Two things I'd like to add in the future is arbitrary placement (at specified xy locations instead of just an array configuration) and a built-in way to explore available PCell parameters (right now you use the separate script "Get PCell parameters.lym").

    Caveat: That script has not undergone rigorous testing, so if you find problems let me know.

    David

    *It's not a true array, it's just some instances at some xy locations.

  • edited March 2016
    Hi David and thank you for your tips. In fact I have tried your macro already and only started my own efforts after not getting the desired result from it.
    Trying it right now again, but I'm inputting some incorrect parameters it seems. The data in the excel sheet I use for testing is as follows:

    layer width radius shape npoints
    LayerInfo.new(1,0) 10 80 (-876,-876;-876,876;876,876;876,-876) 64
    LayerInfo.new(1,0) 10 92 (-888,-888;-888,888;888,888;888,-888) 64
    LayerInfo.new(1,0) 10 104 (-900,-900;-900,900;900,900;900,-900) 64

    As macro parameters I use STROKED_POLYGON row-wise, 3 rows, 1 column and 0 for all vector values (since I want concentric rings).

    There are a few problems with this though.

    1. It tells me that there are too many rows in my spreadsheet "(excluding header rows)" and that only the first 3 rows will be processed. There are exactly 3 though...

    2. I get several errors like this:
    (eval):1: syntax error, unexpected ',', expecting ')'
    (-876,-876;-876,876;876,876;876,-876)

    Could you show me an example to generate a very simple STROKED_POLYGON array?

    Thank you!

    PS: And how do I use the currently active layer instead of creating a new one? I have found no "LayerInfo.active" method or something like it in the documentation or examples.
  • edited March 2016

    Hi,

    You get the objects for the active layer (actually: the active layer view) this way:

    view = RBA::LayoutView::current
    l = view.current_layer
    
    current_cv = view.cellview(l.current.cellview)
    current_layout = cv.layout
    current_cell = cv.cell
    current_layer_index = l.current.layer_index
    

    Matthias

  • edited November -1

    Thanks for the test case. I haven't tried to use it with STROKED_POLYGON, or any PCell where you specify vertices for that matter.

    Unfortunately I'm on vacation without access to my home computer (or any computer where I can install KLayout) for a week or so, but will look at this after I return. In the meantime feel free to look in to the source (I know you said you didn't have Ruby experience, but take a look anyway, it's relatively intuitive.

    Regarding #1, I think the problem is that you are "putting zeros for everything" -- I assume if you put nonzero values then it would work.. does it? If not, try removing the header row (even though you shouldn't, based on the error message).

    Regarding #2, I think you may have the wrong format (meaning, it might not be (,;,; ... ). There might be different separators, or it might need spaces, or something. Having never used it for a PCell where you specify vertices, I'm not sure. Maybe try to "reverse engineer" a hand-drawn STROKED_POLYGON by examining it using the IDE...

    David

  • edited April 2016
    @Matthias:
    1) Thanks - but how do I use it when creating a new PCell? Putting l.current.layer_index in the param array (instead of "layer" => RBA::LayerInfo::new(1, 0)) doesn't work and leaving the layer parameter empty leads to an invisible PCell. I just want to tell KLayout that it shouldn't create a new layer and instead put new things into the current view/layer.
    2) Could you please tell me how to create a STROKED_POLYGON using Ruby? I'm looking at the source code trying to find the right spot... Experimenting with "shape" => [dpolygon:(-500,-500;-500,500;500,500;500,-500)] hasen't worked so far.
    3) How can I simply execute the Edit -> Duplicate command by code? Shouldn't be that hard, yet I haven't succeeded.
    Update:
    Using
    doit = RBA::Application.instance.main_window.menu.action("edit_menu.duplicate").trigger
    I was able to execute the duplicate command. When inside of a transaction the .commit throws an error, but the duplication is still successful (without the ability to undo). Do I have to "close" the menu?
    Internal error: dbManager.cc:129 m_opened was not true in LayoutView::commit
    C:/Users/.../KLayout/macros/Dup.lym:28:in `commit'
    Update 2:
    How can I alter the parameters of the box that defines a STROKED_POLYGON size? I find only two polygons programatically, the structure that is actually visible (the one that has the radius and width applied) and the one that is the "inner" part of the PCell (but these parameters aren't editable by code for me :( ).

    Thanks for your support!

    @David:
    1) Removing the header didn't change anything, using 1 instead of 0 neither (I only used 0 as the displacement vector, so I guess it should work with 0 too anyway).
    2) Thanks for the tip. I did find some more info, but haven't achieved my goal. I hope Matthias can help with that.

    Cheers!
  • edited April 2016

    Hi,

    Regarding 1.) for the PCell parameter you will need the RBA::LayerInfo object. You can get this like this:

    view = RBA::LayoutView::current
    l = view.current_layer
    
    current_cv = view.cellview(l.current.cellview)
    current_layout = cv.layout
    current_cell = cv.cell
    current_layer_index = l.current.layer_index
    # Fetch the LayerInfo object for the current layer:
    current_layer_info = current_layout.get_info(current_layer_index)
    

    Regarding 2.) here is some code:

    view = RBA::LayoutView::current
    
    # Gets the current layer, the corresponding CellView object and
    # the LayerInfo object:
    layer = view.current_layer.current
    cv = view.cellview(layer.cellview)
    layer_info = cv.layout.get_info(layer.layer_index)
    
    # Locates the STROKED_POLYGON PCell in the Basic library
    basic_lib = RBA::Library::library_by_name("Basic")
    stroked_polygon_pcell_id = basic_lib.layout.pcell_id("STROKED_POLYGON")
    
    # A set of points to build a polygon from (in µm units)
    points = [ 
      RBA::DPoint::new(1.0, 1.5),
      RBA::DPoint::new(5.0, 2.5),
      RBA::DPoint::new(4.0, 5.5),
      RBA::DPoint::new(0.5, 4.0)
    ]
    
    # Sets up a parameter hash
    param = {
      "shape" => RBA::DPolygon::new(points),  # The basic shape
      "layer" => layer_info,                  # The target layer
      "width" => 0.1,                         # Width
      "radius" => 0.2,                        # Rounding radius
      "npoints" => 64                         # Number of points / circle
    }
    
    # Creates the new PCell variant (a cell representing the PCell with the given parameters)
    stroked_polygon_pcell_variant = cv.layout.add_pcell_variant(basic_lib, stroked_polygon_pcell_id, param)
    
    # Places the PCell rotated by 90 degree, unmirrored and at 500,0 (DBU)
    trans = RBA::Trans::new(1, false, 500, 0)
    inst = RBA::CellInstArray::new(stroked_polygon_pcell_variant, trans)
    cv.cell.insert(inst)
    

    Regarding 3.) I don't have code and the true functionality is not easy to emulate. It requires accessing the selection, duplicating the shapes and selecting them again. Possible, but tedious. Maybe you should stop thinking that you can basically rewrite editing operations in Ruby. That's simply not true and I don't know how I raised this expectation. AFAIK there is no claim regarding that. The Ruby API is a low-level interface and does not cover higher level functionality of the application. So when you want to emulate any functionality you'll basically have to rewrite it.

    The Ruby API is good for generating layout and if that is what you are trying to do, I would recommend an entirely different approach: create a layout object, add some layers and build your layout by adding cells and shapes and instances. You don't need PCells for this - they are basically editing aids. When you code a layout generator, it's far easier to use the primitives directly. Polygons already provide some methods for manipulation and if you need more features you will probably find them in the mighty RBA::Region class. In fact, the core code of the STROKED_POLYGON PCell is this:

    # input parameters are: 
    #  r_dbu: radius in database units
    #  w_dbu: width in database units
    #  n: number of points
    #  poly: the polygon in database units
    #  cell: the cell where to output the stroked polygon
    #  layer: the index of the layer on which to output
    
    # creates the outer contour:
    
    reg_outer = RBA::Region::new(poly)
    reg_outer.size(w_dbu / 2)
    
    if r_dbu > 0
      r_inner = [ 0, r_dbu - w_dbu / 2 ].max
      r_outer = r_dbu + w_dbu / 2
      reg_outer.round_corners(r_inner, r_outer, n_eff)
    end
    
    # creates the inner contour:
    
    reg_inner = reg_outer.dup
    reg_inner.size(-w_dbu)
    
    # the output will be the difference between both:
    
    cell.shapes(layer).insert(reg_outer - reg_inner)
    

    Now compare this to the PCell instantiation code and tell me which one is easier to comprehend ...

    Matthias

  • edited November -1
    Hello Matthias,

    thank you very much again - my problem is now solved without the need for me to code anything new! :)
    The PCell array generator of The Red Toolbox works for STROKED_POLYGON if I specify the shape parameter like this:
    RBA::DPolygon::new([
    RBA::DPoint::new(-500.0, -500.0),
    RBA::DPoint::new(-500.0, 500.0),
    RBA::DPoint::new(500.0, 500.0),
    RBA::DPoint::new(500.0, -500.0)
    ])
    or for STROKED_BOX with:
    RBA::DBox::new(-2575.0,-2579.0,2575.0,2579.0)

    Regarding expectation management: I was looking to emulate program functionality since I thought of the Ruby API as macro development in the sense of simple Office or mouse-click macros (i.e. record an action, replay it). You didn't raise the expectation, I just developed the idea that it would be possible in a simple fashion. Now I have learned much about KLayout, so I'm glad that I save time and can use TRT to use KLayout in a more effective way.

    So a big thank you to Matthias and David!
    Hope this thread will help someone in the future, too :)

    Johannes
  • edited November -1
    Hi Matthias,

    along the same lines:
    how do you change PCELL parameters of an existing PCELL ? I iterate through all cells via
    .called_cells, and I detect a PCELL with .is_pcell_variant?, but I cannot seem to change
    that PCELL's parameters. (.cell.change_pcell_parameter needs INST, and I dont have that
    with .called_cells)

    Thanks.

    Thomas.
  • edited November -1

    Hi Thomas,

    "called_cells" will only give you the cells which are called, not how they are called. There is a basic difference between "if" and "how": "if" just tells you whether there is at least one instance and "how" tells you where the cell is placed and how many times it is placed. The PCell parameters are another part of the "how" since PCell's are considered special (parametrized) instances of a generic (parametrisable) cell.

    So you need to iterate the instances.

    A cell has instances of other cells which itself may have instances of other cells. You can iterate the instances of a cell using the "each_inst":

    cell = ... # a cell
    cell.each_inst do |inst|
      inst.cell         # -> The cell object of the child cell (the "if")
      inst.cplx_trans   # -> The transformation of the child cell (the "how")
      inst.is_pcell?    # -> Whether the instance is a PCell instance
      inst.change_pcell_parameter(name, value)    # changes the parameter with name "name" to "value"
    end
    

    If you need to find a pcell further down in the hierarchy you'll need to iterate recursively through the child cells as well. You can iterate the child cells (just the cells, not each instance) with "Cell#each_child_cell" which gives "cell indexes":

    cell = ... # a cell
    cell.each_child_cell do |ci|
      child_cell = cell.layout.cell(ci)    # -> The RBA::Cell object of the child cell
      ..
    end
    

    Matthias

Sign In or Register to comment.