Home Forums Nazca Clipping check, distance and length for interconnects

Viewing 6 posts - 1 through 6 (of 6 total)
  • Author
    Posts
  • #6061
    iv_tern
    Member

    Hello all,

    Had a bunch of questions about interconnects and sanity checks, that I have lumped together, apologies if this poses difficulty.

    I was wondering if there is any way of checking whether an interconnect clips onto a pre-existing feature? I tried playing around with clipper, but it seemed to always return an empty list (have to confirm this, as I can’t use my nazca machine for a day or two). Similarly, is there an implemented way of checking the minimum distance between two features? Finally, is there a way to check the overall length of an interconnect?

     

     

    #6063
    Ronald
    Keymaster

    Dear iv_tern,

    When you place elements (cells) Nazca checks on all of the pins of the element if they are closer than a small value epsilon (typically < 1 nm) from an existing pin in the parent cell. In that case Nazca internally creates a connection between all matching pins for a complete circuit netlist. The connections are validated against a number of conditions, like the pin width, xsection and symmetry. Note this is on pins, not an arbitrary feature of the polygons or polylines; It is about circuit connectivity in this context, not geometrical drawings.

    The same feature can be used for snapping (useful in a drag & drop environment) by using a larger epsilon and let the placement follow the existing pin if close enough and if all the other required pin properties (apart from spatial proximity) match according to a customizable set of rules. It needs some small adaptations in the code to be able to switch from proximity based in-place connections to snapping connections depending on the situation (script vs mouse).


    The distance between two pins can be obtained as follows

    x, y, a = nazca.diff(pin1, pin2)

    Some other features in this context may be useful, e.g. getting the position in between two pins:

    x, y, a = nazca.midpoint(pin1, pin2)
    pin = nazca.midpointer(pin1, pin2)


    The geometrical length of interconnects can be obtained by starting a “trace”:

    import nazca as nd
    ic = nd.interconnects.Interconnect(width=1.0, radius=100)
    
    nd.trace.trace_start()
    ic.strt(length=200).put(0)
    ic.sbend(offset=100).put()
    ic.bend(angle=45).put()
    nd.trace.trace_stop()
    length = nd.trace.trace_length()
    print(length)

    The trace does not check if elements are connected.

    Instead of the trace method you can use the pathfinder, which returns all connected paths and their geometrical lengths, starting in the pin provided to the “start” keyword.

    import nazca as nd
    ic = nd.interconnects.Interconnect(width=1.0, radius=100)
    
    e1 = ic.strt(length=200).put(0)
    ic.sbend(offset=100).put()
    ic.bend(angle=45).put()
    nd.pathfinder(start=e1.pin['a0'])

    Note that some parametric interconnect elements may not have a path length yet.

    Ronald

    #6075
    iv_tern
    Member

    Thank you for your input!

    One final question, if that’s ok – is there any way to get the coordinates of all the points in an interconnect (i.e. where in the object properties as those stores?) I.e. doing the reverse of the tutorial example of placing a polygon, where we give a list of points and then use nd.Polygon(points=pts, layer=’layer’).put(coords).

    Let me know if this isn’t clear, and I can try to elaborate.

    #6084
    Ronald
    Keymaster

    Dear iv_tern,

    The properties are accessible through the Cell objects, which are themselves in a tree structure.

    If you want to see the polygons (if I interpret your question well) you can iterate over all cells under a specific cell and in each cell iterate over all polygons. The method cell_iter() is available for that as demonstrated in the example below.

    import nazca as nd
    import nazca.demofab as demo
    
    cell_1 = demo.deep.strt(length=10, width=5)
    
    print("points in original polygon:")
    for NT in nd.cell_iter(cell_1):  # NT is a named tuple object
        if NT.cell_start:
            print(f"\ncell: '{NT.cell.cell_name}'")
            for i, (pgon, points, bbox) in enumerate(NT.iters['polygon']):
                print(f"{i}: points: {pgon.points}, layer: '{pgon.layer}'")

    In the line (pgon, points, bbox), the “pgon” refers to the original polygon object, the “points” to the polygon translated w.r.t. the top cell provided to cell_iter, and “bbox” the translated bounding box.

    Ronald

    #6090
    iv_tern
    Member

    Dear Ronald, thank you very much for your prompt response – this is almost what I’m looking for.

    If I run the following code:

    import nazca as nd
    
    nd.clear_layers()
    nd.clear_layout()
    
    toy_points1=[(0,0), (0,7), (10, 14), (0,25)]
    toy_points2=[(0,0), (0,15), (10, 14), (0,20)]
    
    with nd.Cell('toy_poly1') as tp1:
        toy_polygon1=nd.Polygon(points=toy_points1, layer=1).put(0)
        nd.Pin('a0').put(0, 0, 180)
        nd.Pin('b0').put(10, 2.5, 0)
    
    with nd.Cell('toy_poly2') as tp2:
        toy_polygon2=nd.Polygon(points=toy_points2, layer=1).put(0)
        nd.Pin('a0').put(0, 0, 180)
        nd.Pin('b0').put(10, 2.5, 0)
    
    p1 = tp1.put(0,0,0)
    p2 = tp2.put(40,40,90)
    
    print("points in original polygon:")
    for NT in nd.cell_iter(p1):  # NT is a named tuple object
        if NT.cell_start:
            print(f"\ncell: '{NT.cell.cell_name}'")
            for i, (pgon, points, bbox) in enumerate(NT.iters['polygon']):
                print(f"{i}: points: {pgon.points}, layer: '{pgon.layer}'")
    
    print("points in original polygon:")
    for NT in nd.cell_iter(p2):  # NT is a named tuple objec
        if NT.cell_start:
            print(f"\ncell: '{NT.cell.cell_name}'")
            for i, (pgon, points, bbox) in enumerate(NT.iters['polygon']):
                print(f"{i}: points: {pgon.points}, layer: '{pgon.layer}'")

    I get this output:

    points in original polygon:
    
    cell: 'toy_poly1'
    0: points: [(0, 0), (0, 7), (10, 14), (0, 25)], layer: '1/0/None'
    points in original polygon:
    
    cell: 'toy_poly2'
    0: points: [(0, 0), (0, 15), (10, 14), (0, 20)], layer: '1/0/None'

    Whereas I’m looking for the output, that takes into account the fact that p2 has been .put at 40,40 and rotated 90. I.e. I’m interested in the points with the origin of the entire mask, as opposed to the origin of the top cell, if this makes sense.

    Regards,
    iv_tern

    #6091
    Ronald
    Keymaster

    Dear iv_tern,

    print points rather than pgon.points for the polygon coordinates with respect to the first instantiated parent cell, which maximaly goes up to the cell provided to cell_iter(), the “topcell” in this context.

    To get the polygon with respect to the topcell, regardless of any cell instantiation, flatten the hierarchy with flat=True or hierarchy='flat'.

    In your case you are looking for the position with respect to the “nazca” top cell, which is always created as a blank canvas when importing nazca.

    – nazca
    .. – tp1
    …. – polygon1
    .. – tp2
    …. – polygon2

    That “nazca” topcell can be accessed as nd.cfg.defaultcell and you get:

    for NT in nd.cell_iter(nd.cfg.defaultcell, flat=True):
        if NT.cell_start:
            print(f"\ncell: '{NT.cell.cell_name}'")
            for i, (pgon, points, bbox) in enumerate(NT.iters['polygon']):
                print(f"{i}: points: {points}, layer: '{pgon.layer}'")

    output:

    cell: 'nazca'
    
    cell: 'toy_poly2'
    0: points: [(40.0, 40.0), (25.0, 40.0), (26.0, 50.0), (20.0, 40.0)], layer: '1/0/None'
    
    cell: 'toy_poly1'
    0: points: [(0, 0), (0, 7), (10, 14), (0, 25)], layer: '1/0/None'

    Alternatively put the design in a custom topcell:

    nd.with(name='topcell') as top:
        p1 = tp1.put(0, 0, 0)
        p2 = tp2.put(40, 40, 90)

    and use nd.cell_iter(cell=top, flat=True) to get the coordinates as before and nd.export_gds(top) in case you export cell “top” to gds.

    Ronald

    • This reply was modified 3 months, 1 week ago by Ronald. Reason: Replaced nd.cfg.cells[1] by nd.cfg.defaultcell. Apart from that, note the nd.clear_layer() call created an extra nazca topcell, hence the cells[1] in the earlier exampler rather than cells[0]. In nazca0.5.11 that has been corrected and a cells[0] would be required if using the cells reference
Viewing 6 posts - 1 through 6 (of 6 total)
  • You must be logged in to reply to this topic.