heitzmann / gdspy

Python module for creating GDSII stream files, usually CAD layouts.
Boost Software License 1.0
352 stars 128 forks source link

Issue when creating hole array, strange gap ! #96

Closed dorianherle closed 5 years ago

dorianherle commented 5 years ago

Hi !

I am creating hole arrays with gdspy. this is how I do it:

def hole_array(pos,diameter,period,Nx,Ny,name,layer_number, fill = True):
    """
    Creates a hole array (reference)
    --------------------------------
    Input: (pos,diameter,period,Nx,Ny,name,layer_number, fill = True)
    Output: hole_array_reference 

    pos: list, position of center of array (x,y)
    diameter: float, diameter of hole
    period: float, same distance between holes in x and y direction
    Nx: float, number of holes in x direction
    Ny: float, number of holes in y direction
    name: string, name of unit cell (there cannot be two same unit cell names)
    layer_number: int, from 0-255 
    fill, boolean, True: space between holes is filled (default), False: empty space between holes (experimental)
    """

    unit_cell = gdspy.Cell(name + '_hole_unit_cell') 

    ## --------------------------------------------------------------
    ## Position of the unit circle such that the array is centered
    ## --------------------------------------------------------------
    half_array_size_x = (Nx-1)/2*period #circle center to circle center
    half_array_size_y = (Ny-1)/2*period #circle center to circle center

    # Create circle 
    unit_circle = gdspy.Round((pos[0]-half_array_size_x,
                               pos[1]-half_array_size_y), diameter/2, layer=layer_number)

    # Get unit circle edge cordinnates
    p0, p1 = unit_circle.get_bounding_box()

    # Filling Space between Holes
    if fill == True:
        p0 = (p0[0] - (period-diameter)/2, p0[1] - (period-diameter)/2)
        p1 = (p1[0] + (period-diameter)/2, p1[1] + (period-diameter)/2)

    # Create Hole
    unit_hole = gdspy.fast_boolean(gdspy.Rectangle(p0,p1),unit_circle, 'not',layer=layer_number)

    # Adding Hole to Unit Cell
    unit_cell.add(unit_hole)

    # Create hole array references 
    ## Multiple references to an existing cell (unit_cell) in an array format.
    hole_array_reference = gdspy.CellArray(unit_cell, Nx, Ny, (period, period))

    return hole_array_reference 
def main():
    ## ------------------
    ## --   Geometry   --
    ## ------------------

    diameter = 300
    period = 600
    gap = 50

    N = 2
    ## --------------
    ## --Create GDS--
    ## --------------

    top_cell = gdspy.Cell('top_cell')

    # Layer 1

    hole_array_reference = hole_array(pos=(0,0),diameter=diameter+2*gap,period=period,Nx=N,Ny=N,name = "hole_array",layer_number=0)
    top_cell.add(hole_array_reference)

    # Save GDS
    ## Specify Unit
    gdspy.LayoutViewer()
    gdspy.write_gds('array.gds', unit=1.0e-9, precision=1.0e-12)

    # View GDS (if small, else open KLayout)
    #gdspy.LayoutViewer()

if __name__ == '__main__':
    main()

for N=2, I get: image

It looks good, but when you zoom onto a horizontal line: image

You can see that there is a gap: image

I don't see where this gap comes from and I would highly appreciate any help !!

heitzmann commented 5 years ago

@totyped If you print(p0, p1) you'll see that your coordinates do not line up. You have to be very careful when using floats for computation, because they have limited precision and accuracy. Also, using get_bounding_box will return different values depending on the precision you choose for the circles (due to the inherent polygonal approximation).

It's probably best to draw your unit cell centered (or starting at the origin) and use gdspy.CellArray to center the whole thing. For example:

def hole_array(pos, diameter, period, Nx, Ny, name, layer_number, fill=True):
    """
    Creates a hole array (reference)
    --------------------------------
    Input: (pos,diameter,period,Nx,Ny,name,layer_number, fill = True)
    Output: hole_array_reference 

    pos: list, position of center of array (x,y)
    diameter: float, diameter of hole
    period: float, same distance between holes in x and y direction
    Nx: float, number of holes in x direction
    Ny: float, number of holes in y direction
    name: string, name of unit cell (there cannot be two same unit cell names)
    layer_number: int, from 0-255 
    fill, boolean, True: space between holes is filled (default), False: empty space between holes (experimental)
    """

    unit_cell = gdspy.Cell(name + "_hole_unit_cell")

    # Create circle
    cell_poly = gdspy.Round((period / 2, period / 2), diameter / 2, layer=layer_number)

    # Filling Space between Holes
    if fill == True:
        cell_poly = gdspy.fast_boolean(
            gdspy.Rectangle((0, 0), (period, period)),
            cell_poly,
            "not",
            layer=layer_number,
        )

    # Adding Hole to Unit Cell
    unit_cell.add(cell_poly)

    # Create hole array references
    ## Multiple references to an existing cell (unit_cell) in an array format.
    center_x = (Nx - 1) / 2 * period
    center_y = (Ny - 1) / 2 * period
    hole_array_reference = gdspy.CellArray(
        unit_cell, Nx, Ny, (period, period), (center_x, center_y)
    )

    return hole_array_reference
dorianherle commented 5 years ago

Thank you so much! This works perfectly!

dorianherle commented 5 years ago

sorry, to bother, but I just noticed that the resulting hole array is actually not centered: image

Here is how I centered it: """ def hole_array(pos, diameter, period, Nx, Ny, name, layer_number, fill=True):

Creates a hole array (reference)
--------------------------------
Input: (pos,diameter,period,Nx,Ny,name,layer_number, fill = True)
Output: hole_array_reference 

pos: list, position of center of array (x,y)
diameter: float, diameter of hole
period: float, same distance between holes in x and y direction
Nx: float, number of holes in x direction
Ny: float, number of holes in y direction
name: string, name of unit cell (there cannot be two same unit cell names)
layer_number: int, from 0-255 
fill, boolean, True: space between holes is filled (default), False: empty space between holes (experimental)
"""

unit_cell = gdspy.Cell(name + "_hole_unit_cell")

# Create circle
cell_poly = gdspy.Round((0, 0), diameter / 2, layer=layer_number)

# Filling Space between Holes
if fill == True:
    cell_poly = gdspy.fast_boolean(
        gdspy.Rectangle((-period/2, -period/2), (period/2, period/2)),
        cell_poly,
        "not",
        layer=layer_number,
    )

# Adding Hole to Unit Cell
unit_cell.add(cell_poly)

# Create hole array references
## Multiple references to an existing cell (unit_cell) in an array format.
center_x = -(Nx - 1) / 2 * period # displacing the center 
center_y = -(Ny - 1) / 2 * period
hole_array_reference = gdspy.CellArray(
    unit_cell, Nx, Ny, (period, period), (center_x, center_y)
)

return hole_array_reference

image

dorianherle commented 5 years ago

Sorry, I accidentally reopened the issue. The post above was just for my understanding of the "origin" argument in the CellArray or CellReference function: It's not telling to put the center of the cell at this point, but rather to displace the cell by these values... right ?