Merge many gds together

edited March 2011 in General
Hi,

I've two gds files and I want to merge them together. For this I created one blank layout and instantiating the top cells of these two gds files under that top cell in new layout. All this I want to do through ruby script, non-interactively.

Im running klayout -e -z -rm merge_gds.rb

Now I'm unable to copy cell from gds1 file and paste in the same cellview of new layout. Also how do I instantiate the cell at a particular location ?

Regards,
Acku

Comments

  • edited March 2011

    Hi Acku,

    merging of layouts is not the strength of RBA but it's possible (although probably slower than the native functionality).

    Here is a sample script that takes two layouts (a.gds and b.gds) and merges them. It illustrates how to solve the problem of merging the layer sets and the hierarchies. It can be extended to merge properties as well, but I omitted that for simplicity. In addition it assumes that the database units of the layouts is identical. If that is not the case, the second will be magnified or shrinked.

    In particular, copying the instances is somewhat tedious currently.

    Best regards,

    Matthias


    Here is the script:

    # Merge two layouts
    # This script merges two layouts (a.gds and b.gds) into another one (c.gds)
    # and creates a new top cell referencing both source layouts.
    
    # TODO:
    #   * Consider case of different DBU's (requires scaling)
    #   * Copy properties as well (requires property mapping)
    
    # Open the input layouts
    
    l1 = RBA::Layout.new
    l1.read("a.gds")
    
    l2 = RBA::Layout.new
    l2.read("b.gds")
    
    
    # Open the target layout
    
    l3 = RBA::Layout.new
    top = l3.add_cell("TOP")
    
    # Take the output DBU from l1
    l3.dbu = l1.dbu
    
    # Create a map of layer indices of layout 1 to 3 and 2 to 3.
    # lmap_1to3 will map a layer index from 1 to 3 and lmap_2to3 a layer index from 2 to 3.
    # Add the required layers to l3. 
    
    lmap_1to3 = {}
    lmap_2to3 = {}
    linfo_to_index = {}
    
    l1.layer_indices.each do |layer|
      linfo = l1.get_info(layer)
      l3layer = l3.insert_layer(linfo)
      lmap_1to3[layer] = l3layer;
      linfo_to_index[linfo.to_s] = l3layer
    end
    
    l2.layer_indices.each do |layer|
      linfo = l2.get_info(layer)
      l3layer = linfo_to_index[linfo.to_s]
      if !l3layer
        l3layer = l3.insert_layer(linfo)
      end
      lmap_2to3[layer] = l3layer;
    end
    
    # A utility function which copies one layout into another
    
    def copy_cells(lsrc, ltarget, lmap)
    
      citarget = nil
    
      # a map for the cell indices
      cmap = {}
    
      lsrc.each_cell_bottom_up do |cisrc|
    
        # create a new cell in the target layout and add to the cell index map
        csrc = lsrc.cell(cisrc)
        citarget = ltarget.add_cell(lsrc.cell_name(cisrc))
        ctarget = ltarget.cell(citarget)
        cmap[cisrc] = citarget
    
        # copy the shapes
        lsrc.layer_indices.each do |lisrc|
          shtarget = ctarget.shapes(lmap[lisrc])
          csrc.shapes(lisrc).each do |shape|
            newshape = shtarget.insert(shape) 
            shtarget.replace_prop_id(newshape, 0) # clear properties
          end
        end
    
        # translate and copy the instances
        csrc.each_inst do |inst|
    
          # get the instance object and create a new one with the new cell index
          i = inst.cell_inst
          trans = i.is_complex? ? i.cplx_trans : i.trans
          cinew = cmap[i.cell_index]
          if i.is_regular_array?
            ctarget.insert(RBA::CellInstArray.new(cinew, trans, i.a, i.b, i.na, i.nb))
          else
            ctarget.insert(RBA::CellInstArray.new(cinew, trans))
          end
    
        end
    
      end
    
      # Return the last cell's index which (because we iterated bottom up) is a
      # top cell
      return citarget
    
    end
    
    # Actually copy the layouts
    
    new_l1top = copy_cells(l1, l3, lmap_1to3)
    new_l2top = copy_cells(l2, l3, lmap_2to3)
    
    # Create the top level instances
    # (in that example at 0,1000 and 0,-1000 database units
    
    l3.cell(top).insert(RBA::CellInstArray.new(new_l1top, RBA::Trans.new(RBA::Point.new(0, -1000))))
    l3.cell(top).insert(RBA::CellInstArray.new(new_l2top, RBA::Trans.new(RBA::Point.new(0, 1000))))
    
    # write the output file
    
    l3.write("c.gds")
    
  • edited November -1
    Hi Matthias,

    Thanks a lot!

    The script is working fine for me and solved my purpose. I modified it to merge several hundreds of gds files in one file.

    Actually I was trying to follow the most intuitive approach of loading gds files in same layout, but this doesn't seem to work as some "m_layer_states not free" error was coming. Maybe this can be made to work by making the manual work automatic just like it happens (I'm not sure) while doing the same with GUI.

    Thank you so much again, as this example helped to learn a lot. It would be really beneficiary for users of RBA if some more such examples are added in the RBA Examples section.

    Regards,
    Acku
  • edited March 2011

    Hi Acku,

    Being able to load a file into an existing layout is actually a nice suggestion! Right now that use case is not supported and seeing those error message would not surprise me.

    I admit such a feature would be quite useful. I'll try to consider that for the next release.

    Best regards,

    Matthias

  • edited November -1
    Hi Matthias,

    The above code is really useful, but I need to copy layer properties as well. I tried to modify it to map the layer properties but somehow I couldn't make it work. However I able to handle gds files with different database units.

    Can you please guide me on how to update the copy_cells function above to copy the layer properties as well ?

    Regards
    Acku
  • edited November -1

    Hi Acku,

    there is nothing special with the properties. All you need to do is to create a mapping for the property id's and apply the new id's.

    Below is the new script.

    Best regards,

    Matthias

     # Merge two layouts
     # This script merges two layouts (a.gds and b.gds) into another one (c.gds)
     # and creates a new top cell referencing both source layouts.
    
     # TODO:
     #   * Consider case of different DBU's (requires scaling)
     #   * Copy properties as well (requires property mapping)
    
     # Open the input layouts
    
     l1 = RBA::Layout.new
     l1.read("a.gds")
    
     l2 = RBA::Layout.new
     l2.read("b.gds")
    
    
     # Open the target layout
    
     l3 = RBA::Layout.new
     top = l3.add_cell("TOP")
    
     # Take the output DBU from l1
     l3.dbu = l1.dbu
    
     # Create a map of layer indices of layout 1 to 3 and 2 to 3.
     # lmap_1to3 will map a layer index from 1 to 3 and lmap_2to3 a layer index from 2 to 3.
     # Add the required layers to l3. 
    
     lmap_1to3 = {}
     lmap_2to3 = {}
     linfo_to_index = {}
     pmap_1to3 = {}
     pmap_2to3 = {}
    
     l1.layer_indices.each do |layer|
       linfo = l1.get_info(layer)
       l3layer = l3.insert_layer(linfo)
       lmap_1to3[layer] = l3layer;
       linfo_to_index[linfo.to_s] = l3layer
     end
    
     l2.layer_indices.each do |layer|
       linfo = l2.get_info(layer)
       l3layer = linfo_to_index[linfo.to_s]
       if !l3layer
         l3layer = l3.insert_layer(linfo)
       end
       lmap_2to3[layer] = l3layer;
     end
    
     # A utility function which copies one layout into another
    
     def copy_cells(lsrc, ltarget, lmap, pmap)
    
       citarget = nil
    
       # a map for the cell indices
       cmap = {}
    
       lsrc.each_cell_bottom_up do |cisrc|
    
         # create a new cell in the target layout and add to the cell index map
         csrc = lsrc.cell(cisrc)
         citarget = ltarget.add_cell(lsrc.cell_name(cisrc))
         ctarget = ltarget.cell(citarget)
         cmap[cisrc] = citarget
    
         # copy the shapes
         lsrc.layer_indices.each do |lisrc|
           shtarget = ctarget.shapes(lmap[lisrc])
           csrc.shapes(lisrc).each do |shape|
    
             # property mapping 
             newpid = 0 # =no properties
             if shape.has_prop_id?
               newpid = pmap[shape.prop_id]
               if !newpid
                 newpid = ltarget.properties_id(lsrc.properties(shape.prop_id))
                 pmap[shape.prop_id] = newpid
               end
             end
    
             newshape = shtarget.insert(shape) 
             shtarget.replace_prop_id(newshape, newpid)
           end
         end
    
         # translate and copy the instances
         csrc.each_inst do |inst|
    
           # property mapping 
           newpid = 0 # =no properties
           if inst.has_prop_id?
             newpid = pmap[inst.prop_id]
             if !newpid
               newpid = ltarget.properties_id(lsrc.properties(inst.prop_id))
               pmap[inst.prop_id] = newpid
             end
           end
    
           # get the instance object and create a new one with the new cell index
           i = inst.cell_inst
           trans = i.is_complex? ? i.cplx_trans : i.trans
           cinew = cmap[i.cell_index]
           if i.is_regular_array?
             newinst = ctarget.insert(RBA::CellInstArray.new(cinew, trans, i.a, i.b, i.na, i.nb))
           else
             newinst = ctarget.insert(RBA::CellInstArray.new(cinew, trans))
           end
    
           # apply the new property id
           ctarget.replace_prop_id(newinst, newpid)
    
         end
    
       end
    
       # Return the last cell's index which (because we iterated bottom up) is a
       # top cell
       return citarget
    
     end
    
     # Actually copy the layouts
    
     new_l1top = copy_cells(l1, l3, lmap_1to3, pmap_1to3)
     new_l2top = copy_cells(l2, l3, lmap_2to3, pmap_2to3)
    
     # Create the top level instances
     # (in that example at 0,1000 and 0,-1000 database units
    
     l3.cell(top).insert(RBA::CellInstArray.new(new_l1top, RBA::Trans.new(RBA::Point.new(0, -1000))))
     l3.cell(top).insert(RBA::CellInstArray.new(new_l2top, RBA::Trans.new(RBA::Point.new(0, 1000))))
    
     # write the output file
    
     l3.write("c.gds")
    
  • edited November -1
    Hi Matthias,

    Thanks again. I couldn't figure out that this needs to be done for every instance as well. The code is working fine now!

    Thanks and Regards,

    Acku
  • edited March 2011

    You're welcome :-)

    I admit it's more tedious than it should be. I'll try to provide a better solution in the next release, like the one you already mentioned.

    BTW: if you need performance, the script can be set up somewhat more efficient by not merging both layouts into a new one but rather the second into the first.

    Best regards,

    Matthias

  • edited November -1
    Hi, Matthias,

    I want to merge 2 gds into 1 and flatten them.
    I use your first script of this discussion and add the flatten script in the end of file.
    But I get the error message as following (mergecell.rb is the file name of my script)

    ./mergecell.rb:112: [BUG] Segmentation fault
    ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]

    /usr/local/klayout/bin64/klayout.sh: line 66: 18798 Aborted ${KLAYOUT_HOME}/bin64/current/klayout ${ARGUMENTS} ${FILES}

    Could you give me some hints?
    The following is the RBA code.
    ----------------------------------------------------------------------------------
    # Merge two layouts
    # This script merges two layouts (a.gds and b.gds) into another one (c.gds)
    # and creates a new top cell referencing both source layouts.

    # TODO:
    # * Consider case of different DBU's (requires scaling)
    # * Copy properties as well (requires property mapping)

    # Open the input layouts

    l1 = RBA::Layout.new
    l1.read("a.gds")

    l2 = RBA::Layout.new
    l2.read("b.gds")


    # Open the target layout

    l3 = RBA::Layout.new
    top = l3.add_cell("TOP")

    # Take the output DBU from l1
    l3.dbu = l1.dbu

    # Create a map of layer indices of layout 1 to 3 and 2 to 3.
    # lmap_1to3 will map a layer index from 1 to 3 and lmap_2to3 a layer index from 2 to 3.
    # Add the required layers to l3.

    lmap_1to3 = {}
    lmap_2to3 = {}
    linfo_to_index = {}

    l1.layer_indices.each do |layer|
    linfo = l1.get_info(layer)
    l3layer = l3.insert_layer(linfo)
    lmap_1to3[layer] = l3layer;
    linfo_to_index[linfo.to_s] = l3layer
    end

    l2.layer_indices.each do |layer|
    linfo = l2.get_info(layer)
    l3layer = linfo_to_index[linfo.to_s]
    if !l3layer
    l3layer = l3.insert_layer(linfo)
    end
    lmap_2to3[layer] = l3layer;
    end

    # A utility function which copies one layout into another

    def copy_cells(lsrc, ltarget, lmap)

    citarget = nil

    # a map for the cell indices
    cmap = {}

    lsrc.each_cell_bottom_up do |cisrc|

    # create a new cell in the target layout and add to the cell index map
    csrc = lsrc.cell(cisrc)
    citarget = ltarget.add_cell(lsrc.cell_name(cisrc))
    ctarget = ltarget.cell(citarget)
    cmap[cisrc] = citarget

    # copy the shapes
    lsrc.layer_indices.each do |lisrc|
    shtarget = ctarget.shapes(lmap[lisrc])
    csrc.shapes(lisrc).each do |shape|
    newshape = shtarget.insert(shape)
    shtarget.replace_prop_id(newshape, 0) # clear properties
    end
    end

    # translate and copy the instances
    csrc.each_inst do |inst|

    # get the instance object and create a new one with the new cell index
    i = inst.cell_inst
    trans = i.is_complex? ? i.cplx_trans : i.trans
    cinew = cmap[i.cell_index]
    if i.is_regular_array?
    ctarget.insert(RBA::CellInstArray.new(cinew, trans, i.a, i.b, i.na, i.nb))
    else
    ctarget.insert(RBA::CellInstArray.new(cinew, trans))
    end

    end

    end

    # Return the last cell's index which (because we iterated bottom up) is a
    # top cell
    return citarget

    end

    # Actually copy the layouts

    new_l1top = copy_cells(l1, l3, lmap_1to3)
    new_l2top = copy_cells(l2, l3, lmap_2to3)

    # Create the top level instances
    # no off-set

    l3.cell(top).insert(RBA::CellInstArray.new(new_l1top, RBA::Trans.new(RBA::Point.new(0, 0))))
    l3.cell(top).insert(RBA::CellInstArray.new(new_l2top, RBA::Trans.new(RBA::Point.new(0, 0))))


    (0..(l3.cells-1)).each do |ci|
    if l3.cell_name(ci) == "TOP"
    l3.flatten(ci, 32, true)
    end
    end

    # write the output file
    l3.write("c.gds")

    ---------------------------------------------------------
    Regards,
    Canny
  • edited August 2012

    Hi Canny,

    I think the problem is caused because not every integer between 0 and l3.cells might be a valid cell index. The program should not segmentation fault, but I can't exclude that in every case.

    To find the cell called "TOP" better use

    l3.cell_by_name("TOP")
    

    that method gives you the index of the TOP cell, so you could simply write:

    l3.flatten(l3.cell_by_name("TOP"), 32, true)
    

    Best regards,

    Matthias

Sign In or Register to comment.