Is there any API supporting generating bitmap out of GDS?

edited June 2014 in Ruby Scripting
Hi Guru, I want to generate a bitmap according to polygon patterns on GDS. Is there any scripting API for the purpose?
Thank you for your attention!
«1

Comments

  • edited 7:15AM
    Hi Matthias,

    I just searched through the forum tickets and found that you ever made the answers as follows:
    http://klayout.de/forum/comments.php?DiscussionID=327&page=1#Item_0
    Is it workable to my purpose?
    If yes, can you teach me how to set the pixels? Thru the width and height parameters?
  • edited 7:15AM

    Hello,

    the answer depends on your application. The approach is suitable to create bitmaps for visualization purposes. If you want to feed the bitmaps into some mask writer tool for example, you may require a higher quality output.

    You can set the pixel dimensions of your result bitmap using the width and height parameter of the final image production call:

    view.save_image("#{output_file}", width, height)
    

    Matthias

  • edited 7:15AM
    Hi Matthias,

    Yes. My application requires high quality output. I want to feed the bitmap (image) into some simulation engine.
    Can you teach me the way to achieve that?

    Thanks
  • edited June 2014

    Well, bitmaps for visualization do not reflect pattern densities - the focus is on showing "something". Hence a pixel is set if some shape is on that pixel, but the pixel does not say much about the density at this point.

    If you want a bitmap that reflects the density (that's probably a gray map) you have basically two choices:

    • Use a big image with heavy oversampling to detect the details
    • Create a real density map

    Explaining the second option is somewhat beyond a simple forum entry. You'll have to become familar with the DRC feature and Ruby scripting. A forum entry related to density maps is this one: http://klayout.de/forum/comments.php?DiscussionID=167.

    Matthias

  • edited 7:15AM
    Thanks Matthias. I'll try with your current instructions first.
    Is there any notable difference between the two choices? Runtime performance for example.
  • edited June 2014

    Hi,

    Yes, there will a major difference in runtime, but also in quality.

    The image approach will just deliver binary information and does not care much about precision - if there is a very small shape, you will see one bright pixel, or maybe two or maybe even four. So if you count the pixels and treat them as a physical property, i.e. in the sense of an area density, you will get a value of 1 to 4.

    The density map will deliver a true pattern density. So for a very small shape you will get a density value which is proportional to the covered part of the pixel. So you might get values far below 1 for very small shapes which is a better measure for the effect of a small shape. Or if a shape covers half of a pixel, you will get a value of 0.5 and not just a binary value of 0 or 1, which more precisely reflects the contribution of the shape to that pixel.

    But the price to pay for precision is CPU time: the density method is much slower than the imaging method, although it's difficult to tell numbers. It's possible to run the density map on multiple cores to mitigate the effect a little.

    Matthias

  • edited 7:15AM
    Hi Matthias,

    Quoted: "It's possible to run the density map on multiple cores to mitigate the effect a little."
    Did you mean that klayout supports distributed computation? How to trigger the parallel computing?

    Thanks
  • edited 7:15AM

    There is no global switch, but there is the "TilingProcessor" class which allows distributing the density computation to multiple cores. This class is not straightforward to use however and it cannot produce image files currently. But if you need CSV output for the density map for example, there is a way to do it.

    Matthias

  • edited 7:15AM
    Hi Matthias,

    One more question:
    If we use the method in http://klayout.de/forum/comments.php?DiscussionID=167., we also need to clean the shape-to-shape overlap first, right? The action was not addressed in the entry.

    Thanks
  • edited 7:15AM

    No, that's already done in the "ep.merge(..)" call.

    BTW: the code in the forum entry works, but there are more efficient methods in between. Specifically there is the "Region" class which is basically a collection of polygons and provides more efficient ways to do the clipping, merging and collection of the area. And there is the tiling processor which controls the process and offers multi-code support for example.

    The tiling processor directly interfaces with the image class, so the code is pretty lean:

    view = RBA::LayoutView::current
    cv = RBA::CellView::active
    cv || raise("No layout selected")
    
    ly = cv.layout
    cell = cv.cell
    
    tp = RBA::TilingProcessor::new
    
    # TODO: adjust tile size
    tp.tile_size(20.0, 20.0)
    
    img = RBA::Image::new
    tp.output("image", img)
    
    # TODO: adjust input layer (here: layer 10, datatype 0)
    tp.input("input_layer", ly, cell.cell_index, RBA::LayerInfo::new(10, 0))
    
    tp.queue(<<END)
    var bx = _tile.bbox;
    var a = input_layer.area(bx);
    _output(image, to_f(a) / to_f(bx.area))
    END
    
    # TODO: adjust number of cores (here: 2)
    tp.threads = 2
    
    tp.execute("Density map")
    
    # TODO: save data to a file etc.
    view.insert_image(img)
    

    Maybe that is a more convenient solution in your case.

    Matthias

  • edited 7:15AM
    Thanks Matthias. Your tool is amazingly powerful!
    So, the new method also takes the overlap clean into account?
    And, the device I need to tackle is composed of several layers by boolean. Do I need to make a new layer by perform boolean operations over the original layers first and then extract the density map from the "new layer"? I guess there is smarter way.
  • edited June 2014

    Hello,

    yes, the area function will take overlaps into account - it will merge the shapes prior to computing the area.

    You can embed boolean operations too. They will be executed on the tiles and on multiple cores which makes them more efficient:

    ...
    # TODO: adjust input layer (here: layer 10, datatype 0)
    tp.input("input_layer1", ly, cell.cell_index, RBA::LayerInfo::new(10, 0))
    # TODO: adjust input layer (here: layer 11, datatype 0)
    tp.input("input_layer2", ly, cell.cell_index, RBA::LayerInfo::new(11, 0))
    
    tp.queue(<<END)
    var bx = _tile.bbox;
    var a = (input_layer1 & input_layer2).area(bx);
    _output(image, to_f(a) / to_f(bx.area))
    END
    ...
    

    This sample code will compute a boolean AND between layer 10 and 11 (each datatype 0). The boolean operator is "&" which represents the boolean AND. There are more operations available: "+" (OR), "-" (NOT) and "^" (XOR). You can even do a sizing (biasing) but that involves clipping and collection of the neighborhood which is another topic not covered here.

    Just in case you're curious: The magic is inside the "tp.queue" argument. This is a script executed for each tile. The variables specified in "input" are Region objects http://www.klayout.de/doc/code/class_Region.html. The script itself is not Ruby but KLayout expression syntax http://www.klayout.de/doc/about/expressions.html.

    You find more information about the tiling processor concept in the description of that class: http://www.klayout.de/doc/code/class_TilingProcessor.html.

    Matthias

  • edited 7:15AM
    Matthias, awesome! Thank you!
  • edited June 2014
    Hi Matthias,

    If there were multiple top cells, should I execute multiple "tp.input"?
    like > tp.input("input_layer1", ly, cell.cell_index, RBA::LayerInfo::new(10, 0))

    If I should, can I assign all the cell into a same "input_layer", the first parameter of tp.input??

    Thanks
  • edited 7:15AM

    Hi,

    To execute the script on multiple cells, you will have to start with each cell.

    The sample script above takes the currently selected cell for input.

    You can iterator over all top cells this way:

    ly.each_top_cell do |tc_index|
    
      cell = ly.cell(tc_index)
    
      tp = RBA::TilingProcessor::new
      ...
    
    end
    

    Regards,

    Matthias

  • edited 7:15AM
    Hi Matthias,

    Would the followings work? Not creating new TilingProcessor object but just defining different layers for different cells.

    ...
    tp = RBA::TilingProcessor::new
    tp.input("input_layer1", ly, cell.cell_index1, RBA::LayerInfo::new(10, 0))
    tp.input("input_layer2", ly, cell.cell_index2, RBA::LayerInfo::new(10, 0))
    ...
    tp.queue(<<END)
    var bx = _tile.bbox;
    var a = (input_layer1 & input_layer2).area(bx);
    _output(image, to_f(a) / to_f(bx.area))
    END
    ...

    Thanks
  • edited 7:15AM

    Hi,

    I guess this works. There is no restriction about the origin of the layout - neither layout, cell or layer.

    I think it will take the common bounding box as the total area to cover. But I have not verified that yet.

    Matthias

  • edited August 2014
    Dear Matthias,

    In your way of "Creating a real density map", it can be deemed that "anti-aliasing" has been considered, right? It is equivalent to "anti-aliasing + down-sampling". And then we can directly generate a density map at wanted pixel size using your way and then feed it into simulation engine. Am I right?

    Thanks,
  • edited August 2014

    Hi,

    it is, if we have the same idea of "anti-aliasing". The density map will reflect the area covered by layout for each pixel. That is one possible way of defining some layout metrics on a coarser grid and it's easy to implement.

    It will also give you the right area coverage for very big pixels. But it's very difficult to tell whether the area coverage is an appropriate metric for your simulation. The nature of the input required depends on the kind of simulation, so I can't give a recommendation here without further knowledge about your application.

    Matthias

  • edited 7:15AM
    Thanks Matthias.
    My application is doing optical simulation of imaging whose input patterns were defined by GDS. To implement simulation, I need to downsample the pattern first to get the map. By your way, I can get the map at the desired pixel size but I am not sure whether the operation is sufficient in terms of accuracy.

    Thanks,
  • edited August 2014

    Hi,

    downsampling is always some approximation, but area density is probably the best you can do.

    A assume you want to use the grayscale value in the sense of some "transmission". But you need to be somewhat careful with the definition - under incoherent conditions the transmission scales intensity while under coherent conditions, the transmission scales the electric field and intensity is proportional to the square of that transmission.

    However, for practical application that difference probably does not matter much.

    Matthias

  • edited 7:15AM
    Dear Matthias,

    Just to confirm: "area density" is the "real density map" by the way you proposed as below, right?
    -------------------
    tp.queue(<<END)
    var bx = _tile.bbox;
    var a = input_layer.area(bx);
    _output(image, to_f(a) / to_f(bx.area))
    END
    -------------------

    Thanks a lot for your reminder of the difference between coherent and incoherent! I should keep this in mind.

    Best Regards,
  • edited August 2014

    Hi,

    That's right. That is the best that you can get with the scripting approach right now.

    Please note that the performance is not very good for small pixels, because every pixel is computed on its own. There is a routine inside KLayout which can render a polygon set to a grayscale map in an area density way using a single sweep. This method is much more efficient for small pixels and is also accurate, but there is not scripting interface to that method yet.

    Matthias

  • edited August 2014
    Thanks Matthias.
    I got your points.

    Best Regards,
  • edited 7:15AM
    Hi Matthias,

    As you commented loooooog ago, there is a method much more efficient but no scripting interface supported.
    May I ask that whether the quick method has scripting interface now?

    Thanks
  • edited 7:15AM

    Hi klfun,

    I'm sorry - polygon grayscale rendering is still not part of the scripting interface. I also think that in general scripting isn't the right approach for high throughput, if you're interested in that.

    Regards,

    Matthias

  • edited 7:15AM
    Thanks Matthias.
    Yes, I do need a way of high throughput. Could you please teach me how to use the non-scripting way then?
  • edited 7:15AM

    Hi klfun,

    For high throughput, there is no way around C++ coding.

    However, the internal API's are not well documented and not easy to use. Teaching you how to do this is beyond my commitment here - you have to study the code yourself.

    But even more important, the C++ code is subject to the terms of the GNU Public License (GPL). That means, when you pick code from there, include headers and even link against it, you are obliged to publish the derived work under GPL as well.

    Matthias

  • edited 7:15AM
    Thanks Matthias. Understood.
  • edited 7:15AM
    Dear Matthias,

    Can I get a hint about where to find the routine? I can find gsiDeclImg.cc, but not sure whether it is the right to start with.

    Thanks
Sign In or Register to comment.