Forum Replies Created
-
AuthorPosts
-
Ronald
KeymasterDear dnortheast,
If no pins are placed in a new cell Nazca will place two default pins, i.e. input ‘a0’ at (0, 0, 180) and output ‘b0’ at (0, 0, 0) in the cell’s coordinates, so the cell can be connected.
The pins you placed are on pins in the original cells (wg1strt and wg2strt) rather than the instances of those cells in your “pulley” cell, i.e. the result of their placement via a put(). So those pins in your example appear at the coordinates as they are in the original cell rather than their instance location in your new “pulley” cell, where you want them. The solution is as follows:
# old (incorrect) wg2strt.put() nd.Pin(name='a0', pin=wg1strt.pin['b0']).put() # new (correct) an_instance_of_the_cell_wg2strt = wg2strt.put() nd.Pin(name='a0', pin=an_instance_of_the_cell_wg2strt.pin['b0']).put()See also this post about cells and instances
Ronald
Ronald
KeymasterDear Cameron,
A correct observation; You can play with x, y, and w at the same time to get what you need.
For the geometrical path length of a parametric curve along (x, y) some calculus is required:
length = integral [ sqrt (x(t)^2 + y(t)^2) * dt ], t from t=0 to t=1
Analytically you may have the integral already, then that would be the most accurate solution. Otherwise take the sum of the line segments for a set of finite dt sections for t along [0, 1], where you need to set a criterion that ensures you obtain a sufficiently close approximation of the length. Perhaps scipy.integrate is of use.
Ronald
Ronald
KeymasterDear Cameron,
If I interpret your question correctly, i.e. that you want to modulate the edge of the waveguide, there are a few ways to get a sin-shaped waveguide edge.
The most flexible is to define a parametric curve in x(t), y(t), and w(t), where in your case you exploit w(t). See also Free form curves, which discusses arbitrary parametric curves.
import numpy as np import nazca as nd xs = nd.add_xsection(name='myXS') xs.width = 5.0 nd.add_layer(name='myLay1', layer=(5, 0)) nd.add_layer2xsection(xsection='myXS', layer='myLay1') # create functions x, y, w for the parametric waveguide: def x(t, wavelength, periods, **kwargs): """X as function of t and free parameters.""" return periods * wavelength * t def y(t, **kwargs): """Y as function of t and free parameters.""" return 0 def w(t, width1=None, width2=None, amplitude=1.0, periods=10, **kwargs): """Width as function of t, width1 and width2 and free parameters.""" return width1 + amplitude * np.sin(t * periods * np.pi) # create the new parametric function using the template Tp_viper(): params = {'wavelength': 2.0, 'amplitude': 0.5, 'periods': 50}Â # assign *all* free params used in functions x, y, and w. sin_edge = nd.Tp_viper(x, y, w, xs='myXS', **params) # put waveguides: sin_edge().put(0) sin_edge(width1=10.0, wavelength=4.0, amplitude=4.0, N=2000).put(0, 15) nd.export_gds()The result is the following waveguides:
The second solution is to define a perturbation of the edge of a straight waveguide element. This hasn’t been an actively in use option, but I dusted it off for nazca-0.5.10 to work in the following way (not using a sin but 2*t for simplicity). The edge1 function in t adds to the normal width of the guide, but it becomes absolute when using width=1.0.
import nazca as nd s = nd.strt(length=10, edge1=lambda t: 2*t + 1.0, edgepoints=100, layer=1).put(0) nd.export_gds()The above would work the same for straight interconnects.
Lastly, you can draw your desired waveguide directly as a polygon:
import nazca as nd points = [(x1, y1), (x2, y2), ...] # your shape nd.Polygon(points=points, layer=1).put(0) nd.export_gds()You ideally would put this in a cell and add pins to it as described in Make and put a Cell.
Ronald
14 March 2020 at 17:30 in reply to: How to pass down pcell arguments in the replaceCells function ? #6042Ronald
KeymasterDear Cecil,
If I understand your new question correctly you want to add an extra parameter, here width_output, on your side to the whitebox in addition to the blackbox parameters. Such a parameter should not affect any pin positions or bbox size. I can not image a direct use case, unless you do something like porting the same black box to two different processes internally. You can probably use the partial method from Pythons functools module. It basically applies, in the example below, the width_output parameter before feeding the white_pcell into the replacement dictionary. After partial(white_pcell, width_output) only the blackbox parameters are left. Referring to the example code in my reply above above this looks like
from functools import partial ... def white_pcell(width_wg, offset, radius, width_output): # do other things ... d = {'blackbox': partial(white_pcell, width_output)}Ronald
Ronald
KeymasterWhat would we be without garbagebags?
Thanks for updating with the solution.
Romald
Ronald
KeymasterDear Paul,
That is an interesting example. Only the last cell in the loop makes it to the final gds layout, because the rebuild reuses the normal export_gds() code base that by default clears all cells at the end. Two ways forward:
Solution 1:
In a call directly to export_gds() the clear setting can be changed by passing
clear=False. The rebuild() does not have to clear keyword yet up and including 0.5.9. You can add it though by going into Nazca module layout.py and in def rebuild() add the lineexport.clear = False, somewhere before the lineexport.generate_layout(cell).Solution 2:
Another way is to use the code to reconstruct cellA and not clearing any cells. In the example below I use nazca-0.5.9 to make use of the custom suffix option and some variable name improvements. Note that each new cell needs a new unique name. Below that is done using the layer number as a suffix.
import nazca as nd with nd.Cell(name='CellA') as cellA: nd.strt(length=2, width=2, layer=0).put() grid_points = [(0, 0), (4, 0), (0, 4), (4, 4)] def custom_rebuild(cell, layermap, suffix="_new"): ly = nd.layout(layermap=layermap, suffix=suffix) for params in nd.cell_iter(cell): if params.cell_start: if params.cell_open: ly.cell_open(params) ly.add_polygons(params) ly.add_polylines(params) ly.add_annotations(params) ly.add_instances(params) elif params.cell_close: ly.cell_close(params) return ly.topcell for i, (x,y) in enumerate(grid_points): new_cell = custom_rebuild(cellA, layermap={0:i}, suffix=f"_{i}") new_cell.put(x,y) nd.export_gds()Ronald
Ronald
KeymasterDear Paul,
If you would like to map layers upon export to gds you can simply do this:
import nazca as nd with nd.Cell(name='CellA') as cellA: nd.strt(length=10, width=5, layer=1).put() nd.bend(radius=20, width=2, angle=90, layer=2).put() nd.export_gds(topcells=cellA, layermap={1: 3, 2: 4}, layermapmode='all')The (hiding) rebuild() function was a special case of the cell_iter() in 0.5.7. The cell_iter() has many use cases and it requires several dedicated tutorials to cover it. These will be posted in the near future. But for layermapping you can use the example above, no need for the cell_iter().
Ronald
Ronald
KeymasterDear ale35,
The best way to get rid of warnings is to solve the underlying reason or, second best, redirecting them to a log file so they do not scroll over the screen but are awaiting review on disk. For the latter see logging.
To resolve the cell rename warning avoid recreating a cell more than once, i.e. do not do the following:
import nazca as nd def createA(): with nd.Cell('test') as C: pass return C createA() createA() # causes a renaming warning createA() # causes a renaming warningbut instead do
import nazca as nd def createA(): with nd.Cell('test') as C: pass return C A = createA() A.put() A.put() A.put()Another way to resolve these warnings is to use the @hashme decorator. It checks on cell name reuse and returns the existing cell rather then recreating it under a new name:
import nazca as nd @nd.bb_util.hashme('test') def createA(): with nd.Cell() as C: pass return C createA() createA() # no more renaming warning createA() # no more renaming warningRonald
29 February 2020 at 18:50 in reply to: How to pass down pcell arguments in the replaceCells function ? #6025Ronald
KeymasterDear Cecil,
Your approach is correct. What is missing is that you need to fill the cell’s ‘parameter’ attribute with a dict like {parametername : value}. The parametername is expressed as a string.
This parameter attribute is automatically filled by the @hashme decorator, which I added in the example below to your ‘blackbox’ in combination with an autobbox=True. To give the autobbox dimension in your case with only two pins, I added the userbbox. The autobbox finds the boundaries and calls the correct put_boundingbox() function internally, so that explicit call can be deleted.
In all it adds a boundingbox and all the blackbox function parameters as annotation to the gds. Those parameters are read back in during the pcell replacement to generate the parametric white cell. I also added some keywords to your calls to make the code more robust. Additionally, I added version info to your cell via the ‘version’ attribute of the cell, which adds a extra information to the black cell, again as annotation.
This pcell flow can be used for IP protection and there are several more elements to this flow to make this pcell IP-replacement work in basically any situation. If you know your pcell names you can simply look for them in the gds, but Nazca can also detect “unknown” pcells, as well as tempering with black boxes to make the whole process 100% reliable. All these features require a somewhat longer tutorial.
import nazca as nd version = {'owner': 'cecil'} def some_calculation(offset, radius): return 2.0 + offset + 0.5*radius @nd.bb_util.hashme('blackbox') def blackbox(width_wg, offset, radius): with nd.Cell() as C: C.version = version length = some_calculation(offset, radius) C.userbbox = [(0, -1.5*offset), (length, -1.5*offset), (length, 1.5*offset), (0, 1.5*offset)] C.autobbox = True nd.Pin('a0').put(0, 0, 180) nd.Pin('b0').put(length, offset) nd.put_stub() return C def white_pcell(width_wg=2, offset=100, radius=500, layer=1): with nd.Cell(name='white_pcell', cnt=True) as C: length = some_calculation(offset, radius) nd.strt(length=length, width=width_wg).put(0) # do some stuff to create the pattern # same pin placement as above return C nd.strt(width=2, length=200, layer=1).put(0) blackbox(width_wg=2, offset=150, radius=1000).put() blackbox(width_wg=2, offset=20, radius=500).put() nd.export_gds(filename='test') # Import and conversion black to white: d = {'blackbox': white_pcell} nd.replaceCells(gdsin='test.gds', gdsout='testwhite.gds', PcellFunctionMap=d)Ronald
Ronald
KeymasterDear ale35,
The tutorial on hashme may be of help.
All cells generated by hashed function calls have the _$#### hash suffix as unique identifier.
In newer Nazca versions (0.5.7 and up) the hashme=True has become optional in de Cell() call.The hashme decorator does not only check for the cell name, but for the full call profile and it generates a unique cell name based on that (with the hash). You can also manage cell names yourself and simply send or generate a unique cell name every time you call your cell generating function. Hashme does all the work for you in one line with a few characters in case of parametrized cells.
Ronald
2 February 2020 at 12:20 in reply to: straight connection with sbends vs. diagnonal connection of the sbend #5983Ronald
KeymasterDear Daneel,
Which Nazca version are you using? I can not reproduce the asymmetric result. Would you have a short piece of code generating the guides like in the picture you attached?
The sbend_p2p() has by default a fall-back option (keyword bsb=True) where it draws a bend_straight_bend_p2p() interconnect if for some reason the sbend did not return a valid solution. This is what the left interconnect in your picture looks like. If you set bsb=False Nazca will connect the points with a straight line in the error layer. These events are also recorded in the Nazca logfile, which you can activate like
import nazca as nd nd.logfile(__file__)By default, the horizontal line of the sbend is in the direction of pin1. This horizontal orientation can be set/changed via the ref keyword if needed. The sbend_p2p will automatically add extra bends in the pin1 start and pin2 en points to accommodate the sbend_p2p under the ref condition, e.g. ref = (0, 0, 20) to place the sbend straight section under 20 degree w.r.t. the local x-axis. Setting ref to pin2 will use pin2 rather than pin1 as reference.
Ronald
29 January 2020 at 14:05 in reply to: straight connection with sbends vs. diagnonal connection of the sbend #5979Ronald
KeymasterDear Daneel,
Would you have a picture to clarify the question?
As a general remark: The point-to-point interconnect sbend_p2p(pin1, pin2) takes pin1 as the direction of horizontal part of the sbend and ignores the angle of pin2. If pin2 has a wrong angle to connect to the sbend output smoothly, this will show up as an angle error in the pin2pin DRC. You can switch the DRC on using
nazca.pin2pin_drc_on(). By default the sbend will add an extra bend to pin2 to ensure a smooth connection.Ronald
Ronald
KeymasterSee also export cells into an existing gds.
Ronald
KeymasterDear Shayan,
It seems the memory is not cleared between runs. It then looks to Nazca like you are defining the same layer twice. If you work in Spyder you can tag the option:
“Tools/Preferences/Run/Remove all variables before execution”.You can also remove all layers explicitly in Nazca using
import nazca as nd nd.clear_layers()However, better is to clear the memory between runs to avoid similar issues.
If on another occasion you actually want to overwrite an existing layer you can use the overwrite keyword since nazca-0.5.7:
import nazca as nd nd.add_layer(name='layer1', layer=1) nd.add_layer(name='layer1', layer=1, overwrite=True)Ronald
Ronald
KeymasterDear Maziyarm,
You can generate the pin layer with name/identifier ‘bb_pin’ explicitly before using a pin.
import nazca as nd nd.add_layer(name='bb_pin', layer=(100, 0))If you use a pin before creating the pin layer the ‘bb_pin’ layer is created automatically according to the setting in default_layers in cfg.py:
default_layers = { 'bb_pin': (1001, 0), # layer for the pin symbol (arrow) 'bb_pin_text': (1002, 0), # layer for pin name annotation }If the pin layer was set already you can change (overwrite) it:
nd.add_layer(name='bb_pin', layer=(100, 0), overwrite=True)—
Some more options to change the pin layer and/or shape:You can set the pin layer when you use put_stub() for those pins put by put_stub():
nd.put_stub(layer=(100,0))With respect to pins in an interconnect, you can initialize a pinstyle (see cfg.py for details) per interconnect and change layer, shape, size and other pin properties:
nd.add_layer(name='my_pin', layer=(300, 0)) my_style = { 'shape': 'arrow_full', 'size': 2.5, 'layer': 'my_pin', } nd.set_pinstyle(name='fancy', styledict=my_style) ic = nd.interconnects.Interconnect(pinstyle='fancy') ic.strt().put(10) nd.export_gds()Ronald
13 January 2020 at 12:03 in reply to: Use of template function that generates cell from PDK example #5961Ronald
KeymasterDear Alex,
The template functions are based so-called “closures”, where a closure is a function that returns a function while storing the variable status at time of function creation.
In Nazca we try to identify these closures with the Tp_ prefix in the function name. Initially, the idea was to create these Tp_ functions for building blocks and reuse the Tp_ functions across foundries. When dealing with 10s of PDKs, however, it becomes a less ideal to reuse these building block templates, because updating a function template for one PDK would not be disconnected from other PDKs. In Nazca we prefer, in contrast, a workflow where PDKs are updated independently from each other. The specific PDK building block template functions should be considered as examples that you hardcopy and adapt for your specific use case.
def Tp_test(a='hello'): b = "Monday" def say_stuff(a=a, b=b, c=3): message = f"I say {a} to {b} {c} times" return message return say_stuff # Create and initialize a function using the template: F1 = Tp_test() F2 = Tp_test(a='good day') # Use the function like any other normal function: print( F1() ) print( F1(b='Tuesday') ) print( F1(b='Tuesday', c=5) ) print( F2() ) print( F2(b='Wednesday') ) # output: I say hello to Monday 3 times I say hello to Tuesday 3 times I say hello to Tuesday 5 times I say good day to Monday 3 times I say good day to Wednesday 3 timesFor creating GDS cells, PDK template functions all return a function (like F1 and F2 above) which on its turn returns a Cell object. In the example above the closures F1 and F2 return a string object that can be printed. If F1 and F2 return a Cell, you can put it. Parameter a and b can be used to initialize your closure, in the case of a design with e.g. geometry or xsection settings.
Ronald
Ronald
KeymasterDear Eric,
Klayout libraries as you describe should be no more than a link to a gds file(s):
https://www.klayout.de/0.22/doc/about/about_libraries.htmlHence, if you store gds files in “~/.klayout/libraries” they will end up in the Klayout library repositories with all the top cells as available library cells (via the “Instance” menu item). You have to restart Klayout as far as I am aware to see updates in a library file in Klayout. The cells of your present mask (the active layout in Klayout) will show up under the “Local” library (all cells, not only top cells as in the stored library case). If this is not addressing your issue, can you provide a more detailed example?
Ronald
Ronald
KeymasterDear Ale35,
With deleting the variable space, do you mean checking the option (Spyder 3.3.6):
Spyder-menu/Tools/PreferenceRun/General settings/Remove all variable before execution
This is also discussed with a screenshot in the following post
https://stackoverflow.com/questions/45853595/spyder-clear-variable-explorer-along-with-variables-from-memoryDo you reuse the python console rather than spawning new ones? In the latter case consoles may stick around in memory, but I haven’t looked into that explicitly.
If the above does not resolve the issue you may also try to delete some Nazca arrays explicitly in your code and rely on Python’s garbage collection.
import nazca as nd nd.clear_layout() nd.clear_layers() nd.clear_xsections()Ronald
Ronald
KeymasterDear Miakatt,
For the sake of completeness, you could do the full inversion in Nazca and I created a tutorial which reproduces roughly the Inverted MMI in the picture you posted.
Still, I would prefer to keep the waveguide and trench-overlay layers during the design process and do a post-design mask boolean operation in Klayout, i.e. do “Edit / Layer / Boolen operation” on the original waveguide and trench layer, or script that operation.
Ronald
Ronald
KeymasterDear Daneel,
The first cell you create after evoking the @hashme will get the name created by hashme.
You can simply nest your ‘text’ cell inside the ‘rectangle’ cell to get what you need:
import nazca as nd import nazca.geometries as geom @nd.bb_util.hashme('rectangle', 'length', 'width') def rectangles(length=80, width=80): with nd.Cell() as rectangle: with nd.Cell() as text: nd.text('text', height=30, layer='Label', align='cc').put(0) rect=geom.box(length=length, width=width) nd.Polygon(points=rect).put(0, 0) text.put(100, 0) return rectangle rectangles().put(0) rectangles().put(0) nd.export_gds()If all you ultimately want to do here is to get access the cell name to print it you have other options too. You can use the Cell attribute cell_name and get rid of the second explicit cell altogether:
@nd.bb_util.hashme('rectangle', 'length', 'width') def rectangles(length=80, width=80): with nd.Cell() as rectangle: text = nd.text(text=rectangle.cell_name[:-6], height=30, layer='Label', align='cc') rect=geom.box(length=length, width=width) nd.Polygon(points=rect).put(0, 0) text.put(100, 0) return rectangleA last alternative is to use a bounding box:
@nd.bb_util.hashme('rectangle', 'length', 'width') def rectangles(length=80, width=80): with nd.Cell() as rectangle: rectangle.autobbox = True rect=geom.box(length=length, width=width) nd.Polygon(points=rect).put(0, 0) return rectangleRonald
-
AuthorPosts