Convenience function to create a "pcell_variant" from a script

To make building PCell variants from Ruby scripts easier, I found that a lot of the boilerplate code could be factored out to make the actual generator code more compact and easy to read. It also calls the coerce_parameters callback to make sure the given parameters are always consistent.

Here is the code:

##
# Creates a variant of a PCell based on the given parameters.
#
# @param lib_cell_name [String] Name of the PCell, with its library, of which a variant is being made.
# @param params [{various}] Parameters for the PCell in a hash.
# @param layout [RBA::Layout] Layout to which the new PCell variant will be added.

def self.pcell_variant(lib_cell_name, params, layout)
  libname, cellname = lib_cell_name.split(".")

  # first try to get the pcell from the current layout (see http://klayout.de/forum/comments.php?DiscussionID=404&page=1#Item_7)
  pcell_decl = layout.pcell_declaration(cellname)

  if pcell_decl.nil? # ...and if it doesn't exist, get the library first.
    lib = RBA::Library.library_by_name(libname)
    lib || raise("Unknown PCell library '#{libname}'")

    # find the pcell within the lib
    pcell_decl = lib.layout.pcell_declaration(cellname)
    pcell_decl || raise("Unknown PCell '#{cellname}'")
  end

  unused_params = params.dup
  pv_indices = {}
  # build a param array from the defaults
  pv = pcell_decl.get_parameters.collect do |p|
    pv_indices[p.name.to_sym] = pv_indices.size # also record the index for each name
    p.default
  end

  # call coerce_parameters to make sure the parameters are consistent
  pv = pcell_decl.coerce_parameters(layout, pv)

  # now update, step-by-step, the pv with the given parameters and 
  # call coerce_parameters for each change to remain consistent
  params.each do |k, v|
    raise("PCell #{lib_cell_name}: Encountered unsupported parameter #{k}") if !pv_indices.include? k
    pv[pv_indices[k]] = v
    pv = pcell_decl.coerce_parameters(layout, pv)
  end

  # create a PCell variant cell
  if lib.nil?
    pcell_var = layout.add_pcell_variant(pcell_decl.id, pv)
  else
    pcell_var = layout.add_pcell_variant(lib, pcell_decl.id, pv)
  end
end

Here is an example of how it can be used:

ly = RBA::Application.instance.main_window.create_layout(1).layout
top = ly.add_cell("top")
layer = RBA::LayerInfo.new(2, 0)
pcv = pcell_variant('Basic.TEXT', {text: "PCELL_VARIANT", layer: layer}, ly)
trans = RBA::Trans::new(2000, 1000)
ly.cell(top).insert(RBA::CellInstArray::new(pcv, trans))

For some reason it doesn't seem to work properly with the Basic.CIRCLE PCell (I believe it is a problem of the example code rather than the pcell_variant method itself), so any ideas for improvements are more than welcome!

Regards, Chris

Comments

  • edited November -1

    Hi Chris,

    thanks for providing this code.

    As I mentioned in the [http://klayout.de/forum/comments.php?DiscussionID=630&page=1#Item_6](other entry), there will be a very similar method in 0.24.

    I used the following sample code:

    mw = RBA::Application.instance.main_window
    ly = mw.create_layout(1).layout
    top = ly.add_cell("top")
    layer = RBA::LayerInfo.new(2, 0)
    pcv = pcell_variant('Basic.CIRCLE', {actual_radius: 10.0, layer: layer}, ly)
    trans = RBA::Trans::new(2000, 1000)
    ly.cell(top).insert(RBA::CellInstArray::new(pcv, trans))
    
    lv = mw.current_view
    lv.select_cell(top, 0)
    lv.add_missing_layers
    lv.zoom_fit
    

    The last four lines just configure the view to shown all layers and select the top cell. Th "pcell_variant" method also works with Basic.CIRCLE. But the required radius parameter is "actual_radius". "radius" and "handle" provide alternative inputs for the dialog and the handle which can be moved to adjust the radius graphically. Basic.CIRCLE was not really designed for programmatic instantiation, so that is somewhat special with this cell.

    Matthias

  • edited November -1

    Hi Matthias,

    Thanks for the hint regarding the actual_radius, that got the Basic.CIRCLE working. Also good to know about the add_missing_layers method, I guess it is good practice to always call it at the end of a layout generating script?

    Regards, Chris

  • edited May 2015

    Hi Chris,

    I'd recommend putting these lines at the end of the generation script. Specifically "add_missing_layers" is a good idea. That scheme saves some mouse clicks and doesn't scare away inexperienced users :-)

    Matthias

Sign In or Register to comment.