Merge all shapes of a certain layer of a cell

edited September 2015 in General

Hello Matthias,

I have a small problem. I want to merge all shapes of a certain layer of a cell. All shapes are already polygons. I am not sure how to do it, since you usually need an array of polygons for the merge command. The only way I know of to adress the shapes needed would be:

cell_name.each_shape(layer_name) do |shape|
  xxx
end 

But with this method I dont know how to proceed.

Another idea I tried was with the ShapeProcessor. It looks like:

processor = RBA::ShapeProcessor.new
processor.merge_to_polygon(cell_name.shapes(layer_name), 0, true, false)

The last command needs an array as argument. But the "cell.shapes()" command should give us a list of shapes according to the class documentation. Isn't a list of shapes equivalent to the required array?

Thanks for your help in advance!

Greetings,
Elias

Comments

  • edited November -1

    I found the simplest solution for my problem:

    processor = RBA::ShapeProcessor.new
    processor.merge(layout, cell_name, layer_name, shape-container, false ,0,false,true)
    

    The problem at hand is solved, but just out of curiosity, I would still like to know how to make the list of shapes ( gained by: cell_name.shapes(layer_name)) to an array to use it for the command stated in the previous comment.

    Thanks again,
    Elias

  • edited September 2015

    Hi Elias,

    You can turn a Shapes container into a Shape array with a loop:

    shape_array = []    # will become and array of Shape objects
    shapes = ...        # a Shapes object
    shapes.each { |s| shape_array << s }
    

    But this is not really efficient and when you have a hierarchy of cells, this loop will just convert one cell, not it's children.

    Actually ShapeProcessor is a bit limited in it's use as is it's brother EdgeProcessor. The reasoning behind EdgeProcessor and ShapeProcessor is that the former operates on raw objects (Polygon, Edge, ...) while the latter works on the more generic Shape objects. Both classes implement polygon boolean operations. They allow a great degree of control what you do, but they are a bit unhandy.

    I'd recommend using the newer Region and Edges objects which act as polygon and edge containers and support higher level operations through their methods. Both can be created from a real layout using the RecursiveShapeIterator which is a powerful tool to address the shapes from cell trees or subtrees:

    cell = ....    # a Cell which you want to take the polygons from
    layer = ....   # the layer index from which you want to take the polygons
    
    # creates a region containing all polygons from the cell's layer plus all the
    # polygons from it's children.
    region = RBA::Region::new(cell.begin_shapes_rec(layer))
    
    # merges the polygons (in-place: the region is filled with the merged polygons)
    region.merge
    
    # clears the input layer (in all cells) and puts the 
    # merged polygons back into the cell
    cell.layout.clear_layer(layer)
    cell.shapes(layer).insert(region)
    

    The Edges class allows handling of edges (the connecting lines of the polygon vertexes). There is a rich set of methods on these containers you may like to explore. In fact, the DRC engine is built around these two objects mainly and is implemented as a thin wrapper around these. In Region and Edges you have access to these methods without your code.

    If you want to script layout manipulations alone, the DRC feature may be easier to use because it wraps all the overhead of retrieving and storing polygons. Merging a layer in a DRC script looks like this:

    # Merges the polygons on layer 10, datatype 0:
    input(10).merged.output(10)
    

    Matthias

  • edited November -1

    Thank you Matthias for your thorough answer. I will look into these objects.

  • Hello,
    I know this is an old post but I am trying to perform operations such as merges.
    In python I have written the following:

    for lind in layout.layer_indexes():
    mergeReg = pya.Region(top.begin_shapes_rec(lind))
    mergeReg.merge
    top.layout.clear_layer(lint)
    top.shapes(lint).insert(mergeReg)

    I get the error :
    Caught the following exception:
    'builtin_function_or_method' object has no attribute 'clear_layer' (Class AttributeError)

    I've tried different paths to find this function but have not been successful. I'm sure I'm missing something.
    Any help would be appreciated.

  • there was a typo in my post "lint" should be "lind" it does not change the error

  • Hi hleduc,

    Comments in this forum are better formatted with Markdown. Python code specifically is nicer to read when placed in a fenced code block, by placing triple backticks ``` before and after the code block.

    The error is saying that the word before clear_layer is a buildin function or method. That's layout. I guess top is a Cell and layout a method of a Cell instance. Note that the API documentation is written with Ruby in mind. In Python you have to add parenthesis to call a buildin/function/method:

    for lind in layout.layer_indexes():
        mergeReg = pya.Region(top.begin_shapes_rec(lind))
        mergeReg.merge
        top.layout().clear_layer(lind)
        top.shapes(lind).insert(mergeReg)
    

    HTH

  • Thanks. I will learn mark down (do you use an editor?). I converted everything to ruby and it is working. I will go back and try the python version.

  • edited March 2021

    @hleduc
    I know that this is a bit old discussion, but wonder if you have tried the Python codes yet? I also have the same problem and it has made me miserable! :neutral:
    It actually doesn't show me any error. Everything sounds working fine, but after running the codes, it actually deletes all the objects in the layer!

  • edited March 2021

    You mean something like

    Caught the following exception:
    'builtin_function_or_method' object has no attribute 'clear_layer' (Class AttributeError)
    

    This is a Python beginner's error. It happens when you are trying to call a function and forget the brackets.

    b = pya.Box(0, 0, 100, 100)
    b.width   # WRONG. Gives you a "method object"
    b.width()   # CORRECT. Gives you the width
    

    Matthias

  • edited May 2022

    Hi,
    I have tried the following script to merge all shapes in a layer/cell :

        import klayout.db as db
        layout = db.Layout()
        fname="..."
        layout.read(fname)
        for ci in layout.each_top_cell():
          c=layout.cell(ci)
          for li in layout.layer_indexes():
            mergeReg = db.Region(c.begin_shapes_rec(li))
            mergeReg.merge()
            c.clear(li)
            c.shapes(li).insert(mergeReg)
    

    But it fails with following error:
    RuntimeError: std::bad_alloc in Shapes.insert

    The error disappears if I comment out the line
    c.clear(li)
    In this case the merged shapes are properly inserted but, of course, there is a duplication.

  • It's strange, the error should be less likely to happen if the keep the clear. But maybe that is a coincidence.

    There is a general problem here which is hierarchy. You will need not only to clear the layer from the cell c, but also in the child cells. However, they may be shared by other top cells which you may need layer, so you cannot do that in the general case.

    Because of that you cannot clear a layer before you finished all top cells. So you could do the following (not tested):

    work_layer = layout.layer()
    for li in layout.layer_indexes():
      for ci in layout.each_top_cell():
         c=layout.cell(ci)
         mergeReg = db.Region(c.begin_shapes_rec(li))
         mergeReg.merge()
         c.shapes(work_layer).insert(mergeReg)
       layout.clear_layer(li)   # clears all cells
       layout.swap_layers(li, work_layer)     # move work_layer in place of the layer li
    

    Maybe that solves you memory issue because the layer is cleared entirely. However, you need memory to hold the merged results from all top cells per layer.

    So basically there is a trade-off. I don't know how your layout is made, but the merge step will basically flatten the hierarchy. This is bound to use a lot of memory if there is a complex hierarchy (e.g. arrays). Hierarchical merge may be a solution, but that cannot account of multiple top cells. To use that you first had to create an artificial additional top cell. But the best solution depends on the details of you application.

    Or maybe you're using a 32bit binary which suffers from the 2G process size constraint?

    Matthias

  • edited May 2022

    Hi Matthias, the problem shouldn't be related to the cell hierarchy because all the top cells are fully flatten before the merging. I omitted the flattening part for shortness but here I am annexing the full script with the two input files of a test_case.

    usage:
    layout2fc.py -stack UMS_PH10.stack test_case.dxf

    The failing lines to merge cells are commented out starting from line 70.
    If you want to try this script you should install the RealThunder Branch of FreeCAD from here:
    https://github.com/realthunder/FreeCAD/releases.
    On ubuntu you may prefer the snap version (see https://snapcraft.io/freecad-realthunder) which is updated more frequently.
    I am bulding FC from source and I install it in /usr/local. In may case I am able to access FreeCAD python modules after adding /usr/local/lib to my PYTHONPATH. On the page https://snapcraft.io/freecad-realthunder I am reading that the snap version makes available a command (freecad-realthunder.pip) which installes python packages for user (not system-wide).

    Before using the script you should also define the FC Preferences for Importing dxf files as recommanded by RealThunder in this post https://github.com/realthunder/FreeCAD/issues/419.
    The window shown in this post is opened with the menu "Edit/Preferences/(Import-Export Tab)"

    The merging of layer shapes is very important because the pockets (holes in the dielectric layers) fail when there are overlapping 2D shapes. I have seen that layouts generated by RF EDA tools (like AWR MicroWave Office) quite often tend to generate such bad shapes.

  • edited May 2022

    I forgot to say that with this test case the scale factor, also available in the FC Preferences for Importing dxf files, should be set to 0.001 while the FC length unit should be set to mm (the default).
    Unfortunately the micron unit (used in the layout) is not available in FC.

  • Hi Mathias,
    I have tried anyway your solution and I have discovered that it works. But I do not understand why because, after flattening, there is only one cell left which is named "TOP". Here annexed the updated script.

  • Sorry, there was an error in the last uploaded script. This is the correct one

  • @wsteffe At least the good news is that there is a solution :)

    The "bad_alloc" message means the system is not able to give the requested memory to the application.

    I still do not understand the original issue. I tried your initial script with a variety of input layouts, but without any noticeable memory effect and no error.

    The merge operation is memory greedy in more than one way: because it flattens (in your case that appears not to be an issue) and because it may generate huge polygons. In the latter case memory peaking may happen due to memory fragmentation. But in order to trigger that you need really huge polygons with several million vertexes. And that would not create an issue on 64bit systems either.

    Matthias

Sign In or Register to comment.