Ruby example for Layer Boolean Operations

edited August 2009 in KLayout Support
I would like to automate a series boolean operations, but haven't been able to get past Ruby syntax errors. It would be very helpful if you can provide a simple example.
Thanks,
Mikel

Comments

  • edited November -1

    Hi Mikel,

    there are a lot of ways to write boolean functions. The most important application probably is to create new layers from existing ones, similar to what the menu functions do.

    This is an example piece of code that performs a boolean between two layers:

    # get the current layout and cell
    view = RBA::Application.instance.main_window.current_view
    cellview = view.cellview(view.active_cellview_index)
    layout = cellview.layout
    cell = cellview.cell
    
    # prepare a shape processor (can be reused for subsequent operations)
    processor = RBA::ShapeProcessor.new
    
    # the layers to process
    layer_a = 2
    layer_b = 3
    layer_out = 5
    
    # the actual processor call
    processor.boolean(layout, cell, layer_a, layout, cell, layer_b, cell.shapes(layer_out), 
                      RBA::EdgeProcessor::mode_and, true, true, true)
    

    Although this code works, it is somewhat tedious to use, because

    • The layers must be given as layer indices, not as layers or datatypes
    • The output layer is not completely cleared but rather the top level shapes are overwritten

    To simplify these tasks, I have created a simple helper class. An example for using that class can be found below:

    # ----------------------------------------------------------------
    # A boolean processor class
    
    class BoolProcessor
    
      def initialize(layout, cell)
    
        @layout = layout
        @cell = cell
    
        @proc = RBA::ShapeProcessor.new
    
        @temp_layers = []
    
      end
    
      # Locate a layer for input.
      # The layer is specified by a RBA::LayerInfo object which identifies the layer by 
      # a layer and datatype for example. If the layer does not exist, it is created.
      # This method returns a layer index which can be used as input for other functions.
      def input(lp)
    
        layer_index = nil
    
        # locate the layer if it already exists
        (0..(@layout.layers-1)).each do |i|
          if @layout.is_valid_layer?(i) && @layout.get_info(i).is_equivalent?(lp)
            layer_index = i
            break
          end
        end
    
        # create a new layer if it does not exist
        return layer_index || @layout.insert_layer(lp)
    
      end
    
      # Create a new temporary layer and remember for removal on "cleanup".
      # This method returns a layer index which can be used as input for other functions.
      def tmp
        lp = RBA::LayerInfo.new
        layer_index = @layout.insert_layer(lp)
        @temp_layers.push(layer_index)
        return layer_index
      end
    
      # Assign a layer as output.
      # Change the layer given by the layer index to the output layer specified
      # by a RBA::LayerInfo object. If the layer already exists, it is deleted.
      def output(lp, layer_index)
    
        li = nil
    
        # locate the layer if it already exists
        (0..(@layout.layers-1)).each do |i|
          if @layout.is_valid_layer?(i) && @layout.get_info(i).is_equivalent?(lp)
            li = i
            break
          end
        end
    
        # clear the layer if it already exists
        if li != layer_index
          if li
            @layout.delete_layer(li)
          end
          @layout.set_info(layer_index, lp)
        end
    
        # remove the layer from the list of temporary layers
        new_temp = []
        @temp_layers.each do |i|
          if i != layer_index
            new_temp.push(i)
          end
        end
        @temp_layers = new_temp
    
      end
    
      # Clean up all temporary layers
      def cleanup
        @temp_layers.each do |i|
          @layout.delete_layer(i)
        end
        @temp_layers = []
      end
    
      # Size a layer by the given value (in micron).
      # "input" is the input layer, given by a layer index.
      def size(input, d)
        res = tmp()
        @proc.size(@layout, @cell, input, @cell.shapes(res), to_dbu(d), 
                   2, true, true, true)
        return res
      end    
    
      # Size a layer by the given values anisotropically (in micron)
      # "input" is the input layer, given by a layer index.
      def size2(input, dx, dy)
        res = tmp()
        @proc.size(@layout, @cell, input, @cell.shapes(res), to_dbu(dx), to_dbu(dy), 
                   2, true, true, true)
        return res
      end    
    
      # OR two layers
      # "a" and "b" are the input layers, given by a layer index.
      def or(a, b)
        res = tmp()
        @proc.boolean(@layout, @cell, a, @layout, @cell, b, @cell.shapes(res), 
                      RBA::EdgeProcessor::mode_or, true, true, true)
        return res
      end    
    
      # XOR two layers
      # "a" and "b" are the input layers, given by a layer index.
      def xor(a, b)
        res = tmp()
        @proc.boolean(@layout, @cell, a, @layout, @cell, b, @cell.shapes(res), 
                      RBA::EdgeProcessor::mode_xor, true, true, true)
        return res
      end    
    
      # AND two layers
      # "a" and "b" are the input layers, given by a layer index.
      def and(a, b)
        res = tmp()
        @proc.boolean(@layout, @cell, a, @layout, @cell, b, @cell.shapes(res), 
                      RBA::EdgeProcessor::mode_and, true, true, true)
        return res
      end    
    
      # NOT two layers
      # "a" and "b" are the input layers, given by a layer index.
      def not(a, b)
        res = tmp()
        @proc.boolean(@layout, @cell, a, @layout, @cell, b, @cell.shapes(res), 
                      RBA::EdgeProcessor::mode_anotb, true, true, true)
        return res
      end    
    
      # Convert a value from micron to database units
      # "a" and "b" are the input layers, given by a layer index.
      def to_dbu(micron)
        return (0.5 + micron / @layout.dbu).floor()
      end
    
      # Run a procedure in the context of this object
      def run(&action)
        action.call(self)
        cleanup
      end
    
    private
      @layout
      @cell
      @proc
    
    end
    

    The following script employs that class to perform a simple set of boolean operations: it combines two layers, "2/0" for "active" and "3/0" for "poly" with a logical "and" to form the gate regions and combines "active" oversized by 10nm with "poly" to form gate end markers:

    view = RBA::Application.instance.main_window.current_view
    cv = view.cellview(view.active_cellview_index)
    
    BoolProcessor.new(cv.layout, cv.cell).run do |p|
    
      active = p.input(RBA::LayerInfo.new(2, 0))
      poly = p.input(RBA::LayerInfo.new(3, 0))
    
      gates = p.and(active, poly)
    
      p.output(RBA::LayerInfo.new(10, 0), gates)
    
      gate_ends = p.not(p.and(p.size(active, 0.01), poly), gates)
    
      p.output(RBA::LayerInfo.new(11, 0), gate_ends)
    
    end
    
    # Add layers that have been created so far to the layout view
    RBA::Application.instance.main_window.cm_lv_add_missing
    view.update_content
    

    Matthias

  • edited August 2009
    Hi Matthias,
    Can't say enough thanks. This is almost all the code I needed. Will be working with it this weekend.
    Amazing program, even more amazing support!
    Mike
  • edited November -1
    Hello Matthias,

    Thank you for the very nice tool.

    I have a layout with a top cell and many sub cells. I am using your code from this page to run several sizing and boolean operations on the layers. The code runs very well, but the results are returned to the top cell destroying the hierarchy of the layout.

    I've trying to modify the above code in order to return the results in child cell and not the top cell that I am executing the script, for days now.

    Tried two strategies (i) iterate through the cell, (ii) use @cell.each_child_cell as the output cell. Unfortunately none works, the first one will delete the layer and if I comment out the delete part then I am filling up with temp layers. The other case it is looking for a box that I am not sure how to define.

    Can you please give an example on how to change the output cell?

    Thank you,
    Michael
  • edited November 2015

    Hi Michael,

    thanks :-)

    In general, the code above is awfully old. Now there is the DRC feature which is much more convenient to use. Please see http://www.klayout.de/doc/manual/drc.html for details.

    But the DRC feature is also producing flat data. That is a general limitation and it's not easy to overcome.

    In general, hierarchical processing of layout is far from being simple. You will always find a solution for a specific case. But there is no general solution, just a variety of heuristic solutions.

    For example, a cell-local sizing will only work if the content of each cell is isolated and not interacting (touching, overlapping) with content from either a parent, child or sibling cell (direct or indirect ones). Only in that case you can code a simple operation to run over each cell individually. Except for rare cases this condition isn't met and this is usually when trouble starts. I'm not brave enough to deal with this topic, so my choice was to stay flat.

    But there is a general flow scheme which may help to make you independent from the implementation details of the processor: If you are producing a mask, then in general it does not matter (apart from data volume maybe) whether the mask data is flat or hierarchical. But while drawing a layout, the hierarchy is important since it allows you to partition your layout and reduce the manual efforts.

    So the solution is to draw your layout in a certain way and apply sizing and boolean operations in a final finishing step. Only then you convert the drawn layout into mask layout. For example you can use the DRC feature to do this. This process can be scripted, so it's no additional effort.

    This also opens the opportunity to apply complex operations such as reverse toning (see http://klayout.de/forum/comments.php?DiscussionID=762&page=1#Item_2 for example).

    Matthias

  • Matthias, If I run this code the manual way and use the heirachial way "flat" it runs slow. If I do the same thing using "semi-hierarchical" it runs fast (instant) and the file size is 100X smaller. Is there a way to run the above code in "semi-hierarchical" mode. Thanks

  • Thanks Scott

  • Hi Scott,

    this post is pretty outdated.

    I'd recommend using DRC for this purpose. For the above example:

    active = input(2, 0)
    poly = input(3, 0)
    
    gates = active & poly
    gates.output(10, 0)
    
    gate_ends = active.sized(10.nm) & gates
    gate_ends.output(11, 0)
    

    DRC supports a real hierarchical mode. Just add the following line to the file at the beginning:

    deep
    
    ...
    

    Matthias

Sign In or Register to comment.