klayout
klayout copied to clipboard
Support off-grid Instances/Shapes
Hi Matthias,
I was wondering whether it would be possible, with reasonable amount of work, to support Instances and Shapes which would essentially amount to DInstance & DShapes. Essentially, it would boil down to postponing snapping until write of the Layout object.
The usecase would be the folllowing: In photonics if we want to go for minimal footprint, it can be very beneficial to use non-manhattan routing. The problem with it is if you are just using standard Instances&Cells, you will inevitably run into issues related to snapping because you are doing snapping twice (once when creating the cell and once when you position the instance). Both of the undesired snappings will cause the reference points being slightly off-grid.
I have a working example which just stores any Cell definitition in (python) memory until it is actually placed in a real KLayout Cell. Of course it would be nice to have it supported natively in KLayout, but I am also aware it might just not be compatible or would require extensible changes to core functionalities. In that case, feel free to just close this.
This is from an example through kfactory. So, I can attest that it would work ;)
(tagging @tvt173 , as he is the one way more experienced in this and I probably did a terrible job explaining the problem)
Best, Sebastian
Supporting floating point coordinate support is virtually impossible as all the algorithms act on integers.
What you can do is to use a very small DBU, like 1e-6nm, but that requires 64bit coordinates to be really useful. KLayout and also the Python module can basically be built with 64bit coordinate support. However that means double the memory footprint for coordinates and as this is not really needed in plain GDS applications, this option is turned off by default.
For Windows there is a 64bit coordinate build from the master tip here: https://www.klayout.org/downloads/master/windows/klayout-master-win64-c64-install.exe (disclaimer: binary not signed). For Linux, you can build yourself with the "-with-64bit-coord" option. The Python module does not have a build option, but that should be easy to enable.
Matthias
Thanks for raising the issue Sebastian and your quick response Matthias. I think this could be a viable option as far as connecting components by ports which are diagonal/off-grid, as long as the instances are not rotated. Some downstream tools are not happy with non-cardinal rotations of instances though, so that is why the flatten_invalid_references
solution in gdsfactory is triggered either in the case where an instance is placed off-grid or rotated by an amount not divisible by 90. And the solution we came up with is to absorb and flatten such instances into a new (unrotated, on-grid) wrapper cell. I think that maybe taking combining Matthias's approach with something like this might work well.
To be clear, this would essentially mean we have a very high "working precision" for the layout (for all intents and purposes, making it equivalent to using floating point numbers for the coordinates), and only when we write it we should pass the desired output dbu value as part of the SaveLayoutOptions
. If we follow the scheme above, even if we have applied this flattening to the off-angle rotating cells, they will be resolved at the high resolution at the time of the flatten operation, so they should be rounded to consistent values when outputting to the lower-precision output dbu value, agreed?
I just tried (with a scaling of 1E3, not 1E6 since it seems I didn't find the swap for compiling the python module for 64bit coords). It seems to (mostly) work, but there's exactly something happening that I feared would happen. If a connection point is exactly on the sub-grid where Point.x
is exactly on where the middle of two gridpoints would be, it can round in the wrong direction due to floating point precision error when adding an instance where the transformation center is not exactly in the center of the line. This works really well if there is no snapping involved. When scaling & snapping this of course propagates up.
After scale and snap by 1e3 (back to 1nm grid), it also creates cell variants which are off-grid test_virtual_test_scale_and_snap_snapped_1e-3.gds.gz:
For reference the pre-scaled one already has this problem on a 1e-6 scale (not snapped yet) test_virtual_test_scale_and_snap.gds.gz:
Can't really show the scale, because the ruler seems to not like being below 1E-5 ;)
As a reference I can give you the equivalent with the pure virtual cell (all in floating coordinates), even after snapping it still works, as expected:
test_virtual_test_scale_and_snap_virtual.gds.gz
I did the scaling & snapping with:
- set dbu
dbu = 1e-6
- scale & snap
ly.scale_and_snap(c._kdb_cell, 1_000, 1, 1)
- set dbu
dbu = 1e-3
Also very interesting is that after the scale & snap, there seems to be a point not being on grid in the 1e-3 case. I am a bit confused how that can happen.
I think for now it might be best to stick to the virtual approach, it seems more resilient to snapping errors
If you want to work with the ruler in pm precision you have to increase the number of digits here:
Scale and snap does not guarantee that cell instances are on grid. It guarantees only that the polygon vertexes are on-grid when seen from the top cell.
I therefore used a slightly different approach: divide by 1000 and set the DBU to 1e-3:
# ly.dbu is 1e-6
ly.scale_and_snap(ly.top_cell(), 1, 1, 1000)
ly.dbu = 0.001
It seems to work for me on your sample.
Matthias
I just came across this issue. Is that still valid?
It's still valid. But I fully understand the limitations. I have found ways around it to make it work with the current API. I think we can close the issue.