It looks like you're new here. If you want to get involved, click one of these buttons!
I am trying to boolean some large-vertex-count polygons with some other large-vertex-count polygons, and am getting memory allocation errors.
To reproduce the problem on a simpler script I wrote the following. Note my observations in the comments on the 7th line.
layout_view = RBA::Application.instance.main_window.current_view
layout = layout_view.active_cellview.layout
out_cell = layout_view.active_cellview.cell
out_layer = layout.layer(1,0) # Use this if you're not sure if there are any shapes on that layer in the current view, and don't mind creating the layer if it doesn't exist
dbu = layout.dbu
# Number of points along the base of the inner polygon.
# If this is 10**5 it works and runs in about 0.5 seconds.
# If this is 10**6 it works and runs in about 5 seconds.
# If this is 10**7 it hits an error:.
# "Caught the following exception: bad allocation in EdgeProcessor::boolean_p2p (Class RuntimeError). Press 'Ok' to continue and 'Cancel' to stop in the debugger"
# If this is 10**8 it doesn't return anything even after waiting 10 minutes (it just hangs), and doesn't complain about any memory allocation errors (though, perhaps I didn't wait long enough).
n = 10**5
# Create the inner polygon (a rectangle with many jagged-edge points along the base)
pts_inner = []
x_max = n*0.001
y_max = 10
pts_inner.push(RBA::Point.new(x_max/dbu,y_max/dbu))
pts_inner.push(RBA::Point.new(0.0/dbu,y_max/dbu))
curr_x = 0
curr_y = 0
n.times { |i|
pts_inner.push(RBA::Point.new(curr_x/dbu,curr_y/dbu))
curr_x += 0.001
if i % 2 == 0
curr_y = 1
else
curr_y = 0
end
}
poly_inner = RBA::Polygon.new(pts_inner)
# Create the outer polygon (a box)
extra = 1
left = -extra
bottom = -extra
right = x_max + extra
top = y_max + extra
poly_outer = RBA::Polygon.new(RBA::Box.new(left/dbu,bottom/dbu,right/dbu,top/dbu))
ep = RBA::EdgeProcessor::new
outer_minus_inner = ep.boolean_p2p([poly_outer],[poly_inner], RBA::EdgeProcessor::ModeANotB, false, false)
outer_minus_inner.each { |p|
out_cell.shapes(out_layer).insert(p)
}
Now, I searched on this forum and as I understand it: If I'm using 32-bit windows version, I have some sort of 2 GB limit on filesize. Not sure how this relates to memory consumption by a script though. Nonetheless, I'm now using 64-bit and get the same error. The previous posts suggest 64-bit has no KLayout-imposed limit, but just a hardware limit based on the RAM for whatever computer you are using.
So, I need to use a computer with more memory or I need to do something more clever. The former is not an option so let's look at the latter. One forum post suggested I could write some script to "tile" it and do it piecemeal, however I'm not sure how I could do that. Could you perhaps offer an example with the above script, showing how to tile it?
Thanks!
Comments
Hi David,
that appears to be some extreme case - maybe the formation of the complex hole inside the polygon is requiring more memory than expected.
In general, a lot of memory is consumed by the point array which is built before the polygon is made of that. You can try to release that memory by releasing the memory and forcing a garbage collect, but that appears not to have an effect on the process size. Maybe memory fragmentation is happening here.
64bit should be one solution, but you need to make sure you run the 64bit executable. 64bit can run 32bit executables as well in WoW mode, but they will be subject to the same memory restrictions. There is a hardware limit of course too, so if you want to go even further, you should look for a more general solution.
Looking at the output of your script I see a regular structure. That suggests, you could simplify the problem by producing slices (one slice per tooth) and putting them together later. That surely is more efficient than operations on big polygons.
Matthias
Thanks Matthias.
Yes I admit I am pushing your excellent program to the limits, and I'm surprised it even makes it this far, because that is a heavy requirement!
Definitely running 64 bit. I uninstalled 32-bit.
Yes it's true that in this script I have a regular structure but this was only a short example to illustrate the question -- my actual script has very complicated shapes and difficult or impossible to split up in such a way. Here I am mostly trying to understand the limits so I can work around them.
I will mention that anything the script tries to do, before getting to the EdgeProcessor part, even with all those points in memory, still runs really quickly. It is only when it hits the EdgeProcessor line that it struggles if the input and/or output polygons have too many points.
If there were a way to split both inner and outer polygons - say, split them with vertical lines along the x = 10 um, 20 um, 30 um,... and get an array of polygons. Then operate on the x = 0..10 um polygons, then on the 10..20 um polygons, then on the 20..30 um polygons, etc.... That could be a nice solution. However I fail to find a way to split the a polygon along, say, x = 10um line and return a handle to the two resultant polygons. On L-Edit they have a function for this (splitting along a vertical or horizontal line) so I mention that only because I am familiar with it, but there may be better approaches. Any simple way to do that in Ruby code? If not maybe I just need to buy and install a ton of memory.
Never mind. Figured it out.
I coded up the following "slicer" (1D tiling function) pasted below. Not the prettiest code but it works. There are a few limitations of the code though these can be fixed. First, it has problems if the slice locations v fall outside of the original polygon. Second, there are a few strange cases where it may not work. At any rate, this is good enough for me.
Here is some example code which calls the .rb file pasted later:
And here is the .rb file itself:
Hi David,
thank you for that example. I have not figured out yet what causes the memory issue. Basically a 64bit machine should be able to address much more memory, but first, a 64bit binary also requires more (because pointers will be 8 byte instead of 4 for example) and second, there is always a limitation, at least at physical memory + swap.
I somewhat think the problem is related to the big hole. When you use smaller boxes (i.e. horizontal slices) as the background for the subtraction and repeat the step with every slice it may work - but I have not tried that yet.
Your latest code uses some interesting Ruby techniques and frankly I have not figured out how it works on first glance - but I'll study it :-)
Thanks,
Matthias