27 September 2021 at 11:21 #6576
First I would like to say thank you for all of your effort developing Nazca Design. It has been a tremendous help.
Moving on to my question:
I am trying to create some code that will search through a cell and collect all of the polygons in a certain layer from the cell and any child cells. Thanks to the “Inverted MMI” example, I have come close to finding away to do this:
import nazca as nd nd.clear_layers() nd.add_layer(name='tr', layer=2) with nd.Cell(instantiate=True, name='trench') as trench: nd.strt(length=5, width=5, layer='tr').put(0, 0) nd.strt(length=3, width=7, layer='tr').put() trenchInstance = trench.put(0, -10, 10) ## search the trench cell for polygons polygons =  for params in nd.cell_iter(trench, flat=False): for pgon, xy, bbox in params.iters['polygon']: if pgon.layer == 'tr': polygons.append(xy) ## draw the found polygons for polygon in polygons: nd.Polygon(points=polygon, layer='tr').put(0, 0) ## export gds nd.export_gds(filename='forum_problem.gds')
The only problem is that the polygons extracted from the “trench” cell are found in the nd.strt cells, and the coordinates in the polygons are in the scope of those cells, not the trench cell. Instead of lining up like the nd.strts do, the polygons are just stacked up at (0,0). I suppose that this is the expected behavior.
Is it possible to automatically convert the polygons into the scope of the parent cell? I could do this myself but I would need the xya and flip of the instances of the nd.strt cells, which I can’t find anywhere in the trench cell or the trenchInstance.
Thanks for your assistance.28 September 2021 at 15:20 #6585XaveerModerator
Before going into this, I would like to ask if you can explain why you’d want to do this. It might be that you try something that can better be done in a different way.
Xaveer29 September 2021 at 19:35 #6586
Generally, I am trying to use Boolean operations to define shapes with interior geometry that are otherwise difficult to build from primitives.
In this specific case, I have a die-level GDS that consists of an array of different PICs. Each PIC cell (by this I mean a nazca cell) consists of:
1. a “tile” cell, which has probe pads, test structures, and fabrication process monitors
2. a “device” cell, which is a photonic waveguide device like a laser, SOA, or MZM
3. some interconnect objects to connect the pads in the tile to the vias in the device
The tile cell is complicated but relatively uniform across all the PICs, with a few exceptions, mainly the waveguide in the device cell has a few layers that need to be Boolean subtracted (NOT operation) from a big square in the tile cell. It would be highly desirable to have a uniform p-cell for the tile that was reusable across the entire die and in later masks, and just use some Boolean operations to adapt the geometry inside the tile cell for the specific device that is with it inside the PIC cell.
Another reason would be to invert the polarity of pattern to adapt it for the specific photoresist tone.
Previously I have done these Boolean operations in klayout using a DRC script, but, at least for the way that I know how to do it in klayout, the Boolean operations affect the entire mask. This presents some difficulty, as there were eventually a lot of these operations. It became difficult to mentally imagine what the final outcome would be. Some PIC cells needed a different set of Boolean operations, and this required the creation of a bunch of virtual layers which were Booleaned and then added back into the physical layer. It was a big headache, and during the fabrication, there were many errors in the mask that resulted from this extra complexity.
Ideally, I envision a “p-cell”, which is basically a Python function with some design information as an input, which produces a Nazca cell and then a GDS cell, that is fully complete, so that either the python function or the resulting GDS cell can be transported from one mask layout to another without someone (me) having to chase it around and apply the correct klayout DRC script to it.
So to accomplish this, I have learned that the klayout Boolean algorithms are accessible as a Python library. They are very robust at resolving the specific issue of polygons with interior geometry and making them compatible with GDS. I can post a simple example of how to do this if you are interested in seeing it. This way a Python function that creates a Nazca cell can do Boolean operations and edit the polygons either before putting them into the cell, which works really nicely already, or after the polygon has been put into the cell but before the cell has been instanced, which is what I’m trying to do here and is more difficult.
The only issue left is that the Nazca polygon class has to be converted somehow into a klayout polygon class, which is where I run into a problem. In Nazca it is simple and often very helpful to make a many layers deep cell hierarchy, but for the Boolean operation to be successful, the piece of geometry in the “tile” cell (in my example it is just a square) and the geometry of the waveguide trench (which is hopefully arbitrary and maybe very complicated, like an MZM with all adiabatic bends) need to be simple polygons in the coordinate system of the PIC cell.
Open to any suggestions, even if the suggestion is that the idea is impractical.
Thanks.5 October 2021 at 23:30 #6592RonaldKeymaster
The example below should do what you described (extracting polygons and their position w.r.t. a top cell in a celltree).
The params is a namedtuple that contains both the translation and flip state of a cell instantiation with respect to its direct parent, as well as with respect to the top cell that is iterated over, i.e. “trench” in your example. The namedtuple keys to look for are transflip_loc and transflip_glob. Both are themselves a tuple of the form (Pointer, flipstate). Here “trans” stands for translation (in x, y, and a).
Because the iterator will initiate a loop on cell opening and again on cell closure, I added a check for some actions on cell_open is True only, in the example below, and save the xya position in the same manner as you did for the polygon points. Note that on cell closure the polygon iterator is an empty list, so in your initial example everything works just fine without checking for a cell_open condition.
import nazca as nd nd.clear_layers() nd.add_layer(name='tr', layer=2) with nd.Cell(instantiate=True, name='trench') as trench: nd.strt(length=5, width=5, layer='tr').put(0, 0) nd.strt(length=3, width=7, layer='tr').put() trenchInstance = trench.put(0, -10, 10) # search the trench cell for polygons polygons =  xya =  for params in nd.cell_iter(trench, flat=False): if params.cell_open: trans, flip = params.transflip_glob for pgon, xy, bbox in params.iters['polygon']: if pgon.layer == 'tr': polygons.append(xy) xya.append(trans.xya()) for polygon, xya in zip(polygons, xya): nd.Polygon(points=polygon, layer='tr').put(xya) nd.export_gds(filename='forum_problem.gds')
Ronald5 October 2021 at 23:43 #6593RonaldKeymaster
As a second option you can flatten the celltree during iteration. The “xy” in the polygon iterator will always return the coordinates with respect to the first instantiated parent, which is the top cell in the iteration, here “trench”, because flat=True. In the example below I now use params.cell_start instead of params.cell_open, as the former will be True on any cell processed (instantiated or not), whereas the latter is only True if the cell will be instantiated. Depending on your needs cell_open or cell_start could be more useful, but here cell_open will not do the job due to the flat=True, which results in an instantiate=False state. As explained in my previous post you could even omit the check for cell_start completely here.
import nazca as nd nd.clear_layers() nd.add_layer(name='tr', layer=2) with nd.Cell(instantiate=True, name='trench') as trench: nd.strt(length=5, width=5, layer='tr').put(0, 0) nd.strt(length=3, width=7, layer='tr').put() trenchInstance = trench.put(0, -10, 10) # search the trench cell for polygons polygons =  for params in nd.cell_iter(trench, flat=True): if params.cell_start: for pgon, xy, bbox in params.iters['polygon']: if pgon.layer == 'tr': polygons.append(xy) for polygon in polygons: nd.Polygon(points=polygon, layer='tr').put(0) nd.export_gds(filename='forum_problem.gds')
Ronald10 November 2021 at 04:11 #6617
Thank you very much for your help! I have implemented the second method as my cell hierarchy is pretty flat already. It works beautifully.
-garbage.24 February 2022 at 14:37 #6720
hi garbagebag, I’m interested in coding nazca and klayout with the same script.py. So far I was able to generate the gds with nazca and then manipulate on klayout within the same script. My question is, do you use those platform in Windows or Linux? I could make it work only on Linux but I’d prefer to use Windows.
Thanks2 March 2022 at 04:15 #6727
I am using Windows. Just to clarify, I do not manipulate the GDS using Klayout. I am using the Klayout python to edit raw polygons which are extracted from Nazca cells. The edited polygons are then put back into the cell, which is put into the top cell and generated as GDS by Nazca.
-Garbage3 March 2022 at 12:21 #6728
Are you using the Klayout python through the macro DRC window or do you use an external IDE such as Spyder and import the Klayout Python Library? I couldn’t make it work (import klayout library in spyder) in Windows, only under Linux.
Your approach of using the raw polygon extracted from Nazca is better as, correctly if I’m wrong, within the same script you generate the cells in Nazca and manipulate them with Klayout engine, then you make the gds
Lorenzo3 March 2022 at 16:24 #6729
Ok, I figured it out. I just needed to do a pip install klayout from Spyder. I was confused by an old post in the Klayout forum. So far the module works.
Can you please share an example on extracting the cell from nazca and edit with klayout module?
- This reply was modified 1 year, 7 months ago by lorenzo_btbw.
- You must be logged in to reply to this topic.