def generate_JJ(self): self.JJ_params['x'] = self.Coaxmon1.center[0] + (self.Coaxmon1.R4+self.JJ_params['indent'])*np.cos(self.angle) if self.Coaxmon1.center[0] != self.Coaxmon2.center[0]: self.JJ_params['y'] = self.Coaxmon1.center[1] + (self.Coaxmon1.R4+ self.JJ_params['indent'])*np.sin(self.angle)+(self.core/2+self.gap/2) else: self.JJ_params['y'] = self.Coaxmon1.center[1] + (self.Coaxmon1.R4 + self.JJ_params['indent']) * np.sin(self.angle) # print(self.angle) self.JJ = JJ4q.JJ_1(self.JJ_params['x'], self.JJ_params['y'], self.JJ_params['a1'], self.JJ_params['a2'], ) result = self.JJ.generate_JJ() result = gdspy.boolean(result, result, 'or', layer=self.JJ_layer) angle = self.JJ_params['angle_JJ'] result.rotate(angle, (self.JJ_params['x'], self.JJ_params['y'])) indent = 1 rect1 = gdspy.Rectangle((self.JJ_params['x'] - self.JJ.contact_pad_a / 2, self.JJ_params['y'] +indent), (self.JJ_params['x'] + self.JJ.contact_pad_a / 2, self.JJ_params['y'] - self.JJ.contact_pad_b+indent), layer=6) rect2 = gdspy.Rectangle((self.JJ.x_end - self.JJ.contact_pad_a / 2, self.JJ.y_end - 1), (self.JJ.x_end + self.JJ.contact_pad_a /2 , self.JJ.y_end - self.JJ.contact_pad_b - indent), layer=6) if self.Coaxmon1.center[0] != self.Coaxmon2.center[0]: poly1 = gdspy.Polygon([(self.JJ_params['x'] - self.JJ.contact_pad_a / 2, self.JJ_params['y'] +indent), (self.JJ_params['x'] - self.JJ.contact_pad_a / 2, self.JJ_params['y'] + indent-self.JJ.contact_pad_b), (self.JJ_params['x'] - self.JJ.contact_pad_a-indent,self.Coaxmon1.center[1]-self.core/2), (self.JJ_params['x'] - self.JJ.contact_pad_a-indent,self.Coaxmon1.center[1]+self.core/2) ]) poly2 = gdspy.Polygon([(self.JJ.x_end + self.JJ.contact_pad_a / 2, self.JJ.y_end -indent-self.JJ.contact_pad_b), (self.JJ.x_end + self.JJ.contact_pad_a / 2, self.JJ.y_end - indent ), (self.JJ.x_end + self.JJ.contact_pad_a + indent, self.Coaxmon1.center[1] + self.core / 2), (self.JJ.x_end + self.JJ.contact_pad_a + indent, self.Coaxmon1.center[1] - self.core / 2) ]) else: poly1 = [] poly2 = [] rect = gdspy.boolean(rect1,[rect2,poly1,poly2], 'or', layer=6) rect.rotate(angle, (self.JJ_params['x'], self.JJ_params['y'])) to_remove = gdspy.Polygon(self.JJ.points_to_remove,layer=self.layer_to_remove) to_remove.rotate(angle, (self.JJ_params['x'], self.JJ_params['y'])) return result, rect , to_remove
def finish_him(self): self.result.add( gdspy.boolean( self.total_cell.get_polygons(by_spec=True)[(self.total_layer, 0)], self.cell_to_remove.get_polygons(by_spec=True)[(2, 0)], 'not', layer=self.total_layer)) self.result.add( gdspy.boolean( self.total_cell.get_polygons(by_spec=True)[(self.total_layer, 0)], self.cell_to_remove.get_polygons(by_spec=True)[(2, 0)], 'not', layer=self.total_layer))
def generate_JJ(self): #cheap Manhatten style reach = 32 result = gdspy.Rectangle((self.center[0]-self.b_g/2,self.center[1]+self.h/2-self.b_w/3+self.JJ_params['a1']/2),(self.center[0]-self.b_g/2+reach,self.center[1]+self.h/2-self.b_w/3-self.JJ_params['a1']/2)) result = gdspy.boolean(result,gdspy.Rectangle((self.center[0]-self.b_g/2,self.center[1]+self.h/2-2*self.b_w/3+self.JJ_params['a1']/2),(self.center[0]-self.b_g/2+reach,self.center[1]+self.h/2-2*self.b_w/3-self.JJ_params['a1']/2)) ,'or') result = gdspy.boolean(result,gdspy.Rectangle((self.center[0]+self.b_g/2,self.center[1]+self.h/2-2*self.b_w),(self.center[0]+self.b_g/2+self.JJ_params['a2'],self.center[1]+self.h/2-2*self.b_w+reach)), 'or') result = gdspy.boolean(result, result, 'or', layer=self.layer_configuration.jj_layer) angle = self.JJ_params['angle_JJ'] result.rotate(angle, (self.JJ_coordinates[0], self.JJ_coordinates[1])) return result
def render_text(text, size=None, position=(0, 0), font_prop=None, tolerance=0.1): path = TextPath(position, text, size=size, prop=font_prop) polys = [] xmax = position[0] for points, code in path.iter_segments(): if code == path.MOVETO: c = gdspy.Curve(*points, tolerance=tolerance) elif code == path.LINETO: c.L(*points) elif code == path.CURVE3: c.Q(*points) elif code == path.CURVE4: c.C(*points) elif code == path.CLOSEPOLY: poly = c.get_points() if poly.size > 0: if poly[:, 0].min() < xmax: i = len(polys) - 1 while i >= 0: if gdspy.inside(poly[:1], [polys[i]], precision=0.1 * tolerance)[0]: p = polys.pop(i) poly = gdspy.boolean( [p], [poly], "xor", precision=0.1 * tolerance, max_points=0, ).polygons[0] break elif gdspy.inside(polys[i][:1], [poly], precision=0.1 * tolerance)[0]: p = polys.pop(i) poly = gdspy.boolean( [p], [poly], "xor", precision=0.1 * tolerance, max_points=0, ).polygons[0] i -= 1 xmax = max(xmax, poly[:, 0].max()) polys.append(poly) return polys
def ten(self, X=0, Y=0): points2 = [(X - 36, Y - 20.5), (X - 36, Y - 160.5), (X - 124, Y - 160.5), (X - 124, Y - 148.5), (X - 182, Y - 148.5), (X - 182, Y - 244.5), (X - 124, Y - 244.5), (X - 124, Y - 232.5), (X - 36, Y - 232.5), (X - 36, Y - 318.5), (X - 9, Y - 318.5), (X - 9, Y - 314.5), (X - 11, Y - 314.5), (X - 11, Y - 312.5), (X - 7, Y - 312.5), (X - 7, Y - 318.5), (X + 7, Y - 318.5), (X + 7, Y - 312.5), (X + 11, Y - 312.5), (X + 11, Y - 314.5), (X + 9, Y - 314.5), (X + 9, Y - 318.5), (X + 36, Y - 318.5), (X + 36, Y - 232.5), (X + 124, Y - 232.5), (X + 124, Y - 244.5), (X + 182, Y - 244.5), (X + 182, Y - 148.5), (X + 124, Y - 148.5), (X + 124, Y - 160.5), (X + 36, Y - 160.5), (X + 36, Y - 20.5)] points3 = [(X - 12, Y - 44.5), (X - 12, Y - 184.5), (X - 148, Y - 184.5), (X - 148, Y - 180.5), (X - 172, Y - 180.5), (X - 172, Y - 212.5), (X - 148, Y - 212.5), (X - 148, Y - 208.5), (X - 12, Y - 208.5), (X - 12, Y - 300.5), (X - 1.5, Y - 300.5), (X - 1.5, Y - 306.5), (X + 2.5, Y - 306.5), (X + 2.5, Y - 304.5), (X + 0.5, Y - 304.5), (X + 0.5, Y - 300.5), (X + 12, Y - 300.5), (X + 12, Y - 208.5), (X + 148, Y - 208.5), (X + 148, Y - 212.5), (X + 172, Y - 212.5), (X + 172, Y - 180.5), (X + 148, Y - 180.5), (X + 148, Y - 184.5), (X + 12, Y - 184.5), (X + 12, Y - 44.5)] poly2 = gs.Polygon(points2) poly3 = gs.Polygon(points3) cap_fin = gs.boolean(poly2, poly3, "not") self.cell.add(cap_fin)
def _subtract_holes_from_ground( self, diff_holes_cell) -> Union[gdspy.library.Cell, None]: """Get reference to ground cell and then subtract the holes from ground. Place the difference into a new cell, which will eventually be added under Top. Args: diff_holes_cell ([type]): Cell which contains all the holes. Returns: Union[gdspy.library.Cell, None]: If worked, the new cell with cheesed ground, otherwise, None. """ # Still need to 'not' with Top_main_1 (ground) top_chip_layer_name = f'TOP_{self.chip_name}_{self.layer}' if top_chip_layer_name in self.lib.cells.keys(): ground_cell = self.lib.cells[top_chip_layer_name] ground_cheese = gdspy.boolean(ground_cell.get_polygons(), diff_holes_cell.get_polygonsets(), 'not', max_points=self.max_points, precision=self.precision, layer=self.layer, datatype=self.datatype_cheese) ground_cheese_cell_name = (f'TOP_{self.chip_name}_{self.layer}' f'_Cheese_{self.datatype_cheese}') ground_cheese_cell = self.lib.new_cell(ground_cheese_cell_name, overwrite_duplicate=True) return ground_cheese_cell.add(ground_cheese) self.logger.warning( f'The cell:{top_chip_layer_name} was not found in self.lib. ' f'Cheesing not implemented.') return None
def generate_JJ(self): self.JJ = squid3JJ.JJ_2(self.JJ_coordinates[0], self.JJ_coordinates[1], self.JJ_params['a1'], self.JJ_params['jj1_width'], self.JJ_params['jj1_height'], self.JJ_params['jj2_width'], self.JJ_params['jj2_height'], self.JJ_params['jj3_width'], self.JJ_params['jj3_height'], self.JJ_params['c2'], add_JJ=self.third_JJ, hole_in_squid_pad=self.hole_in_squid_pad) result = self.JJ.generate_jj() result = gdspy.boolean(result, result, 'or', layer=self.layer_configuration.jj_layer) angle = self.JJ_params['angle_JJ'] if self.JJ_pad_connection_shift: connection_shift = self.JJ.contact_pad_b_outer / 2 else: connection_shift = 0 result.rotate(angle, (self.JJ_coordinates[0], self.JJ_coordinates[1])) rect = gdspy.Rectangle( (self.JJ_coordinates[0] - self.JJ.contact_pad_a_outer / 2, self.JJ_coordinates[1] + connection_shift + self.JJ.contact_pad_b_outer), (self.JJ_coordinates[0] + self.JJ.contact_pad_a_outer / 2, self.JJ_coordinates[1] + connection_shift - self.JJ.contact_pad_b_outer), layer=self.layer_configuration.total_layer) rect.rotate(angle, (self.JJ_coordinates[0], self.JJ_coordinates[1])) return result, rect
def Heater(l_heater2): # define heater w_heater = 3 w_port = 10 w_wire = w_port * 2 cell = lib.new_cell('heater') # add middle long rect heater = gdspy.Rectangle((0, w_heater / 2), (l_heater2, -w_heater / 2), **layer_heater) cell.add(heater) # add left rect and wire heater = gdspy.Rectangle((-w_port / 2, w_port / 2), (w_port / 2, -w_port / 2), **layer_heater) cell.add(heater) heater = gdspy.Rectangle((-w_wire / 2, w_wire / 2), (w_wire / 2, -w_wire / 2), **layer_wire) cell.add(heater) # add right rect and wire heater = gdspy.Rectangle((l_heater2 - w_port / 2, w_port / 2), (l_heater2 + w_port / 2, -w_port / 2), **layer_heater) cell.add(heater) heater = gdspy.Rectangle((l_heater2 - w_wire / 2, w_wire / 2), (l_heater2 + w_wire / 2, -w_wire / 2), **layer_wire) cell.add(heater) path1 = gdspy.Path(w_wg) path1.segment(l_heater2) path2 = gdspy.Path(w_wg_cld) path2.segment(l_heater2) cell.add(gdspy.boolean(path2, path1, 'xor', **layer_FETCH_COR)) return cell
def add_bandages(self): bandage_to_island = gdspy.Rectangle( (self.JJ_coordinates[0] - self.JJ.contact_pad_a_outer / 4, self.JJ_coordinates[1] + self.JJ.contact_pad_b_outer / 2), (self.JJ_coordinates[0] + self.JJ.contact_pad_a_outer / 4, self.JJ_coordinates[1] - 3 * self.JJ.contact_pad_b_outer / 4), layer=self.layer_configuration.bandages_layer) bandage_to_ground = gdspy.Rectangle( (self.JJ.rect2[0] - self.JJ.rect_size_a / 4, self.JJ.rect2[1] - self.JJ.rect_size_b / 4), (self.JJ.rect2[0] + self.JJ.rect_size_a / 4, self.JJ.rect2[1] - 5 * self.JJ.rect_size_b / 4), layer=self.layer_configuration.bandages_layer) bandage_to_fluxline = gdspy.Rectangle( (self.JJ.rect1[0] - self.JJ.rect_size_a / 4, self.JJ.rect1[1] - self.JJ.rect_size_b / 4), (self.JJ.rect1[0] + self.JJ.rect_size_a / 4, self.JJ.rect1[1] - 5 * self.JJ.rect_size_b / 4), layer=self.layer_configuration.bandages_layer) bandages = gdspy.boolean(bandage_to_island, [bandage_to_fluxline, bandage_to_ground], 'or', layer=self.layer_configuration.bandages_layer) return bandages
def unite(self, entities, keep_originals=True): blank_entity = entities.pop(0) blank_polygon = self.gds_object_instances.pop(blank_entity.name) self.cell = self.gds_cells[blank_entity.body.name] self.cell.polygons.remove(blank_polygon) tool_polygons = [] for tool_entity in entities: tool_polygon = self.gds_object_instances[tool_entity.name] if isinstance(tool_polygon, gdspy.PolygonSet): for polygon in tool_polygon.polygons: tool_polygons.append(polygon) else: tool_polygons.append(tool_polygon) #2 unite operation tool_polygon_set = gdspy.PolygonSet(tool_polygons, layer=blank_entity.layer) united = gdspy.boolean(blank_polygon, tool_polygon_set, 'or', precision=TOLERANCE, max_points=0, layer=blank_entity.layer) self.gds_object_instances[blank_entity.name] = united self.cell.add(united) return blank_entity
def render(self): # first the two contacts contact_1 = gdspy.Rectangle( (-self.padsize / 2, self.length / 2 - self.padsize / 2), (self.padsize / 2, self.length / 2 + self.padsize / 2)) contact_2 = gdspy.Rectangle( (-self.padsize / 2, -self.length / 2 - self.padsize / 2), (self.padsize / 2, -self.length / 2 + self.padsize / 2)) contacts = gdspy.boolean( contact_1, contact_2, 'or', layer=self.layer_configuration.airbridges_pad_layer) # add restricted area for holes restricted_area = gdspy.Rectangle( (-self.padsize / 2, -self.length / 2 - self.padsize / 2), (self.padsize / 2, self.length / 2 + self.padsize / 2)) # now the bridge itself bridge = gdspy.Rectangle( (-self.width / 2, -self.length / 2), (self.width / 2, self.length / 2), layer=self.layer_configuration.airbridges_layer) return { 'airbridges_pad_layer': [contacts], 'airbridges_layer': [bridge], 'restrict': (restricted_area, ) }
def plg_bool(plgsa,plgsb,operation,**kwargs): '''take two poly_lists and perform layer-wise boolean operations. This is not grade-A code. It doesn't support subtracting a contained object.''' if not isinstance(plgsa,poly_list) or not isinstance(plgsb,poly_list): print 'both objects need to be poly lists!' pass if operation == 'int': f = lambda a,b: a and b elif operation == 'union': f = lambda a,b: a or b elif operation == 'sub': f = lambda a, b: a and not b out_plgs = poly_list() for layer in plgsa: if plgsb.has_key(layer): # print 'found two in the same layer' # print 'a:', plgsa[layer] # print 'b:', plgsb[layer] ret = gdspy.boolean([plgsa[layer], plgsb[layer]], f, eps=1e-10) # print 'ret:', ret ret.layers = [layer,]*len(ret.polygons) ret.datatypes = [0,]*len(ret.polygons) # print 'ret:', ret out_plgs[layer] = ret return out_plgs
def render(self): """ Draws edge g metallization on chip :return: """ contact_window = gdspy.Rectangle( np.asarray(self.position) - self.window_size / 2, np.asarray(self.position) + self.window_size / 2, layer=self.layer_configuration.airbridges_pad_layer) squares = None for sqc in ((1, 1), (1, -1), (-1, 1), (-1, -1)): sqc = gdspy.Rectangle( np.asarray(self.position) + np.asarray(sqc) * (self.gap / 2 + self.square_size), np.asarray(self.position) + np.asarray(sqc) * self.gap / 2) squares = gdspy.boolean(squares, sqc, 'or', layer=self.layer_configuration.total_layer) return { 'positive': squares, 'restrict': contact_window, 'airbridges_pads': contact_window }
def markers_photo_litho(center_coords, chip_side_x, chip_side_y, w, l_x, l_y, layer_align): """ Generates photo-litho markers that are cross hairs on the wafer edges with desired width and length INPUT: array with center position of the chips, chip dimensions OUTPUT: dicing markers elements """ elements = [] # positions of photo-litho alignment markers positions = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 ] for j in positions: elements.append( crosshair(center_coords[j][0], center_coords[j][1], w, l_x, l_y, layer_align)) markers = gdspy.boolean(elements, None, 'or', precision=0.001, max_points=199, layer=layer_align) return markers
def bwToGDS(Width, Height, bwArray, layer, datatype): #Converts a black and white """ Args: Width: The width of the bmp in the GDSPY base units (1 unit = 1um by default) Height: The height of the bmp in the GDSPY base unit (1 unit = 1um by default) bwArray: The black and white image for layout layer: Layer the BMP should be placed on in the GDS Layout datatype: The datatype of the bmp when placed in the GDS Layout Returns: A GDSPY polygonset """ black = 0 #We assume that the material is already converted to a black only version. YRes = len(bwArray) XRes = len(bwArray[0]) pX = Width / XRes pY = Height / YRes rects = [] for i in range(len(bwArray)): for j in range(len(bwArray[0])): if bwArray[i][j] == black: rects.append( gdspy.Rectangle((pX * j, -pY * i), (pX * (j + 1), -pY * (i + 1)), layer, datatype)) patt = None try: patt = gdspy.boolean(rects[0], rects[1:], "or", max_points=0) print("Polygonset Successfully Generated") except: print("Polygonset is Empty! Check black value and try again") None return patt
def render(self): if self.cross_type=='Navigation': filename = "\/navigation_cross.gds" cell_name = 'Navigation_cross' elif self.cross_type=='Cut': filename = "\cut_cross.gds" cell_name = 'Cut_cross' cross_positive = gdspy.GdsLibrary().read_gds(infile=self.path_mask + filename).cells[cell_name].remove_polygons(lambda pts,layer, datatype: layer != self.layers_configuration['total']) cross_negative = gdspy.GdsLibrary().read_gds(infile=self.path_mask + filename).cells[cell_name].remove_polygons(lambda pts,layer, datatype: layer != self.layers_configuration['inverted']) for elements_layer in [cross_negative.polygons,cross_positive.polygons]: for element in elements_layer: element.translate(self.cross_position[0],self.cross_position[1]) restricted_object=gdspy.boolean(cross_negative.get_polygons(),cross_positive.get_polygons(),'or') return {'positive': cross_positive, 'inverted': cross_negative, 'restrict': restricted_object}
def markers_ebeam_litho1(center_coords, chip_side_x, chip_side_y, layer_align): """ Generates "crosshair" markers at the corners of each chip for alignment of the dicing saw and dots following that fo alignment of ebeam INPUT: array with center position of the chips, chip dimensions OUTPUT: dicing markers elements """ elements = [] w = 5 # width of the cross hair on the corner of a chip l_x = 100 # length of the cross hair l_y = 100 for c in center_coords: print(c) print(type(c)) rv = gdspy.Rectangle( (c[0] - chip_side_x / 2 - w / 2, c[1] + chip_side_y / 2 - l_y / 2), (c[0] - chip_side_x / 2 + w / 2, c[1] + chip_side_y / 2 + l_y / 2), layer=layer_align) rh = gdspy.Rectangle( (c[0] - chip_side_x / 2 - l_x / 2, c[1] + chip_side_y / 2 - w / 2), (c[0] - chip_side_x / 2 + l_x / 2, c[1] + chip_side_y / 2 + w / 2), layer=layer_align) crosshair = gdspy.boolean( rv, rh, 'or', layer=layer_align) #making a cross hair on the edge of a chip elements.append(crosshair) return elements
def text_rect(bounding_box, box_length, box_width, txt_label, txt_height, layer, rect_shift, extra_text=None): """creates a bounding rectangle with text subtracted from it. returns a cell. """ # get the bounding box # add a 1um offset from the rest of the pattern offset = box_length / 2 + 1 xshift, yshift = rect_shift print(xshift) test1 = offset + xshift # add the text vtext = gdspy.Text(txt_label, txt_height, (offset + xshift, box_width / 2 + yshift), horizontal=False, **layer) # first subtract the interior rectangle from the exterior rectangle sub = gdspy.boolean(bounding_box, vtext, 'not') return sub
def bounding_rectangle(pattern_w, pattern_l, padding, layer, rect_shift, clearance, opt_litho_layer): """ creates a rectangle of a predefined width bounding the pattern. Rect shift is a tuple of coordinates specifying the offset of the rectangle """ print('pattern_w :' + str(pattern_w)) print('pattern_l: ' + str(pattern_l)) print('padding: ' + str(padding)) print('layer: ' + str(layer)) print('rect shift: ' + str(rect_shift)) xshift, yshift = rect_shift interior_rectangle = gdspy.Rectangle( (-pattern_l / 2 + xshift, -pattern_w / 2 + yshift), (pattern_l / 2 + xshift, pattern_w / 2 + yshift), **layer) exterior_rectangle = gdspy.Rectangle( (-pattern_l / 2 - padding + xshift, -pattern_w / 2 - padding + yshift), (pattern_l / 2 + padding + xshift, pattern_w / 2 + padding + yshift), layer) # define the rectangle used for optical lithography opt_rectangle = gdspy.Rectangle( (-pattern_l / 2 - padding + xshift + clearance, -pattern_w / 2 - padding + yshift + clearance), (pattern_l / 2 + padding + xshift - clearance, pattern_w / 2 + padding + yshift - clearance), **opt_litho_layer) # subtract the interior rectangle from the exterior rectangle sub = gdspy.boolean(exterior_rectangle, interior_rectangle, 'not') return sub, pattern_l, opt_rectangle
def gdspy_shift_size(polyset, shift, layer): new_polygons = [] for ps in polyset.polygons: new_ps = [] N = len(ps) for idx in range(N): vec = ps[idx] - ps[idx - 1] vec_len = np.linalg.norm(vec) vec_t = vec / vec_len vec_n = np.array([vec_t[1], -vec_t[0]]) new_ps.extend( (ps[idx - 1] + vec_n * shift, ps[idx] + vec_n * shift)) new_polygons.append(gdspy.Polygon(np.array(new_ps))) #union_shape = polyset union_shape = None for poly in new_polygons: union_shape = gdspy.boolean(union_shape, poly, 'or', max_points=0, layer=layer) return union_shape
def _subtract_keepout_from_hole_grid( self, gather_holes_cell: gdspy.library.Cell) -> gdspy.library.Cell: """Given a cell with all the holes, subtract the keepout region. Then return a new cell with the result. Args: gather_holes_cell (gdspy.library.Cell): Holds a grid of all the holes for cheesing. Returns: gdspy.library.Cell: Newly created cell that holds the difference of holes minus the keep=out region. """ # subtact the keepout, note, Based on user options, # the keepout (no_cheese) cell may not be in self.lib. temp_keepout_chip_layer_cell = f'temp_keepout_{self.chip_name}_{self.layer}' temp_keepout_cell = self.lib.new_cell(temp_keepout_chip_layer_cell, overwrite_duplicate=True) temp_keepout_cell.add(self.nocheese_gds) diff_holes = gdspy.boolean(gather_holes_cell.get_polygonsets(), temp_keepout_cell.get_polygonsets(), 'not', max_points=self.max_points, precision=self.precision, layer=self.layer, datatype=self.datatype_cheese + 1) diff_holes_cell_name = f'TOP_{self.chip_name}_{self.layer}_Cheese_diff' diff_holes_cell = self.lib.new_cell(diff_holes_cell_name, overwrite_duplicate=True) diff_holes_cell.add(diff_holes) self.lib.remove(temp_keepout_chip_layer_cell) return diff_holes_cell
def finish_him(self): _tmp = gdspy.boolean( self.total_cell.get_polygons(by_spec=True)[(self.total_layer, 0)], self.cell_to_remove.get_polygons(by_spec=True)[(0, 0)], 'not', layer=self.total_layer) self.result.add(_tmp) # self.total_cell.add(gdspy.boolean(self.total_cell.get_polygons(by_spec=True)[(self.total_layer, 0)], # self.cell_to_remove.get_polygons(by_spec=True)[(0, 0)], 'not', # layer=self.total_layer)) for each_two_rects in self.two_small_rectangles_list: _tmp = gdspy.boolean(_tmp, each_two_rects, 'or') self.result.add(_tmp)
def union(one, two, layer = 1, datatype = 1): ### Makes a mask from two objects of the type: ### Polygon, PolygonSet, CellReference, CellArray, ### or an array-like[N][2] of vertices of a polygon. spec = {'layer': layer, 'datatype': datatype} # Apply boolean operation and add boolean to cell return gdspy.boolean([one, two], lambda one, two: one or two, **spec)
def test_notempty(): name = 'cr_notempty' c = gdspy.Cell(name) ref = gdspy.CellReference(name, (1, -1), 90, 2, True) ref.translate(-1, 1) c.add(gdspy.Rectangle((0, 0), (1, 2), 2, 3)) assert ref.area() == 8 assert ref.area(True) == {(2, 3): 8} err = numpy.array(((0, 0), (4, 2))) - ref.get_bounding_box() assert numpy.max(numpy.abs(err)) < 1e-15 assert ref.origin[0] == ref.origin[1] == 0 r = gdspy.boolean(ref.get_polygons(), gdspy.Rectangle((0, 0), (4, 2)), 'xor', 1e-6, 0) assert r is None d = ref.get_polygons(True) assert len(d.keys()) == 1 r = gdspy.boolean(d[(2, 3)], gdspy.Rectangle((0, 0), (4, 2)), 'xor', 1e-6, 0) assert r is None
def generate_coupler(self,coordinate,r_init,r_final,rect_end): #to fix bug bug=5 result = gdspy.Round(coordinate, r_init, r_final, initial_angle=(self.arc_start) * np.pi, final_angle=(self.arc_finish) * np.pi) rect = gdspy.Rectangle((coordinate[0]+r_final-bug,coordinate[1]-self.w/2),(coordinate[0]+rect_end+bug, coordinate[1]+self.w/2)) rect.rotate(self.phi*np.pi, coordinate) return gdspy.boolean(result,rect, 'or')
def generate_qubit(self): ground = gdspy.Round(self.center, self.outer_ground, self.R4, initial_angle=0, final_angle=2*np.pi) restricted_area = gdspy.Round(self.center, self.outer_ground, layer=self.restricted_area_layer) core = gdspy.Round(self.center, self.R1, inner_radius=0, initial_angle=0, final_angle=2*np.pi) result = gdspy.boolean(ground, core, 'or', layer=self.total_layer) if len(self.Couplers) != 0: for Coupler in self.Couplers: if Coupler.grounded == True: result = gdspy.boolean(Coupler.generate_coupler(self.center, self.R2, self.outer_ground,self.R4 ), result, 'or', layer=self.total_layer) else: result = gdspy.boolean(Coupler.generate_coupler(self.center, self.R2, self.R3, self.R4), result, 'or', layer=self.total_layer) self.JJ_coordinates = (self.center[0] + self.R1*np.cos(self.JJ_params['angle_qubit']), self.center[1] + self.R1*np.sin(self.JJ_params['angle_qubit'])) JJ,rect = self.generate_JJ() result = gdspy.boolean(result, rect, 'or') # self.AB1_coordinates = coordinates(self.center.x, self.center.y + self.R4) # self.AB2_coordinates = coordinates(self.center.x, self.center.y - self.outer_ground) return result, restricted_area, JJ
def connect_zShape(cell, pos_start, pos_end, len_port=5, w_wg=0.5, w_etch=3, radius=10, type_layout='positive'): path1 = connect_zShape_(pos_start, pos_end, len_port, w_wg, radius=radius, **ld_wg) path2 = connect_zShape_(pos_start, pos_end, len_port, w_wg+2*w_etch, radius=radius, **ld_cld) if type_layout=='positive': cell.add([path1, path2]) if type_layout=='negative': cell.add(gdspy.boolean(path2, path1, 'xor', **ld_fulletch)) return cell
def render(self): """ Draws edge g metallization on chip :return: """ edge = 600 # fundamental constant - edge length r1 = gdspy.Rectangle((0, 0), (self.chip_geometry.sample_horizontal_size, self.chip_geometry.sample_vertical_size)) r2 = gdspy.Rectangle((edge, edge), (self.chip_geometry.sample_horizontal_size - edge, self.chip_geometry.sample_vertical_size - edge)) result = gdspy.boolean(r1, r2, 'not') for pad in self.pads.items(): pad = pad.get() to_bool = gdspy.Rectangle(pad['positive'].get_bounding_box()[0].tolist(), pad['positive'].get_bounding_box()[1].tolist()) result = gdspy.boolean(result, to_bool, 'not') result_restricted = gdspy.boolean(result, result, 'or', layer=self.layer_configuration.restricted_area_layer) return {'positive': result, 'restrict': result_restricted}
def alignment_mark(xw, xl, layer): """ creates a cross alignment mark geometry, by taking the union of 2 rectangles. """ horizontal = gdspy.Rectangle((-xl / 2, -xw / 2), (xl / 2, xw / 2), **layer) vertical = gdspy.Rectangle((-xw / 2, -xl / 2), (xw / 2, xl / 2), **layer) cross = gdspy.boolean(horizontal, vertical, 'or', **layer) return cross
def generate_fluxline_end(self,end): JJ = end['JJ'] length = end['length'] width = end['width'] point1 = JJ.rect1 point2 = JJ.rect2 result = None # rect_to_remove = gdspy.Rectangle((), # ()) for point in [point1,point2]: line = gdspy.Rectangle((point[0]-width/2, point[1]), (point[0]+width/2, point[1]-length)) result = gdspy.boolean(line, result, 'or', layer=6) # result = gdspy.boolean(line1,line2,'or',layer=self.total_layer) path1 = gdspy.Polygon([(point1[0]+width/2, point1[1]-length),(point1[0]-width/2, point1[1]-length), (self.end[0]+(self.core/2+self.gap+width)*np.cos(self.angle+np.pi/2), self.end[1]+(self.core/2+self.gap+width)*np.sin(self.angle+np.pi/2)), (self.end[0] + (self.core / 2 + self.gap ) * np.cos(self.angle +np.pi/2), self.end[1] + (self.core / 2 + self.gap ) * np.sin(self.angle +np.pi/2))]) result = gdspy.boolean(path1,result, 'or', layer=6) path2 = gdspy.Polygon([(point2[0] + width / 2, point2[1] - length),(point2[0] - width / 2, point2[1] - length), (self.end[0] +(self.core / 2)*np.cos(self.angle+np.pi/2),self.end[1]+( self.core / 2)*np.sin(self.angle+np.pi/2)), (self.end[0] + self.core / 2 *np.cos(self.angle+3*np.pi/2),self.end[1]+( self.core / 2)*np.sin(self.angle+3*np.pi/2))]) result = gdspy.boolean(path2, result, 'or', layer=6) # if end['type'] != 'coupler': restricted_area = gdspy.Polygon([(point1[0] - width / 2, point1[1]), (point2[0] + width / 2 + self.gap, point2[1]), (point2[0] + width / 2 + self.gap, point2[1] - length), (self.end[0] + (self.core / 2 + self.gap) * np.cos( self.angle + 3 * np.pi / 2), self.end[1] + (self.core / 2 + self.gap) * np.sin( self.angle + 3 * np.pi / 2)), (self.end[0] + (self.core / 2 + self.gap + width) * np.cos( self.angle + np.pi / 2), self.end[1] + (self.core / 2 + self.gap + width) * np.sin( self.angle + np.pi / 2)), (point1[0] - width / 2, point1[1] - length)], layer=self.rectricted_area_layer) # else: # return result,restricted_area,restricted_area
def generateRings(lib, centerWidth=2200, centerHeight=2200, ringWidth=[750, 450, 270, 162, 97, 58], traceWidth=0, traceSpacing=0, name="RINGS", layer=1, offset=[0, 0]): rings = lib.new_cell(name) for i in range(len(ringWidth)): outerHalfWidth = centerWidth / 2 + np.sum(ringWidth[0:i + 1]) + np.sum( ringWidth[0:i]) innerHalfWidth = centerWidth / 2 + 2 * np.sum(ringWidth[0:i]) outerHalfHeight = centerHeight / 2 + np.sum( ringWidth[0:i + 1]) + np.sum(ringWidth[0:i]) innerHalfHeight = centerHeight / 2 + 2 * np.sum(ringWidth[0:i]) outerPoints = np.array(offset) + np.array([[ -outerHalfWidth, -outerHalfHeight ], [-outerHalfWidth, outerHalfHeight], [ outerHalfWidth, outerHalfHeight ], [outerHalfWidth, -outerHalfHeight]]) innerPoints = np.array(offset) + np.array([[ -innerHalfWidth, -innerHalfHeight ], [-innerHalfWidth, innerHalfHeight], [ innerHalfWidth, innerHalfHeight ], [innerHalfWidth, -innerHalfHeight]]) cutoutPoints = np.array(offset) + np.array( [[-innerHalfWidth, traceSpacing + traceWidth / 2], [-outerHalfWidth, traceSpacing + traceWidth / 2], [-outerHalfWidth, -traceSpacing - traceWidth / 2], [-innerHalfWidth, -traceSpacing - traceWidth / 2]]) ringOuter = gdspy.Polygon(outerPoints) ringInner = gdspy.Polygon(innerPoints) ringCutout = gdspy.Polygon(cutoutPoints) ring = gdspy.boolean(ringOuter, ringInner, 'not') ring = gdspy.boolean(ring, ringCutout, 'not', layer=layer) rings.add(ring) return rings
def useAsMask(self, groundPlane = None): ### Generates metal from groundPlane using CPW as a negative mask ### Make a fake 1000x1000 at origin ground plane if none given ### Returns resulting object spec = self.spec if groundPlane is None: groundPlane = gdspy.Rectangle((-500, -500), (500, 500), 0) # Create a PolygonSet from CPW path cpwPolySet = self.makePolySet(self.path) # Apply boolean operation return gdspy.boolean([groundPlane, cpwPolySet], lambda groundPlane, cpwPolySet: groundPlane and not cpwPolySet, **spec)
def mask(subtractThis, addThis = None, layer = 1, datatype = 1): ### Makes a mask from two objects of the type: ### Polygon, PolygonSet, CellReference, CellArray, ### or an array-like[N][2] of vertices of a polygon. ### cell - specify which GDS cell to add mask to ### Make a fake 1000x1000 at origin ground plane if addThis not given spec = {'layer': layer, 'datatype': datatype} if addThis is None: addThis = gdspy.Rectangle((-500, -500), (500, 500), 0) # Apply boolean operation and add boolean to cell return gdspy.boolean([addThis, subtractThis], lambda addThis, subtractThis: addThis and not subtractThis, **spec)
# P4=gdspy.Rectangle(7, (2.5, -2.5), ((100+2.5), -(100+2.5))) align_cell.add(gdspy.Rectangle(7, (2.5, 2.5), (100+2.5, 100+2.5))) align_cell.add(gdspy.Rectangle(7, (-2.5, 2.5), (-(100+2.5), (100+2.5)))) align_cell.add(gdspy.Rectangle(7, (-2.5, -2.5), (-(100+2.5), -(100+2.5)))) align_cell.add(gdspy.Rectangle(7, (2.5, -2.5), ((100+2.5), -(100+2.5)))) align_cell2.add(gdspy.Rectangle(7, (-(100+2.5), -(100+2.5)), ((100+2.5), (100+2.5)))) #primitives.append(gdspy.PolygonSet(0, [P1.points ,P2.points,P3.points,P4.points])) #primitives.append(gdspy.PolygonSet(0,B.points)) primitives = [gdspy.CellReference(align_cell, origin=(0, 0)), gdspy.CellReference(align_cell2, origin=(0, 0))] subtraction = lambda p1, p2: p2 and not p1 bool_cell = gdspy.Cell('Bool_Alignment_Marks') bool_cell.add(gdspy.boolean(1, primitives, subtraction, max_points=199)) name = os.path.abspath(os.path.dirname(os.sys.argv[0])) + os.sep + 'bool_align_mark' ## Output the layout to a GDSII file (default to all created cells). ## Set the units we used to micrometers and the precision to nanometers. gdspy.gds_print(name + '.gds', unit=1.0e-6, precision=1.0e-9) print('Sample gds file saved: ' + name + '.gds') ## Save an image of the boolean cell in a png file. Resolution refers ## to the number of pixels per unit in the layout. Resolution changed from 4 ## to 1 to avoid "malloc_error" #gdspy.gds_image([resonator_cell], image_name=name, resolution=1, antialias=4) #comment out save as png for speed
def openGapFillet(self, distance, gapType, filletRadius = 10, direction = None): ### Draw a straight CPW gap segment in the direction following the last path with a fillet ### Gap type is either 'beg' or 'end' to specify where fillet goes gap = self.gap width = self.width spec = self.spec if gapType == 'beg': if direction is None: direction = self.initalDirection if gapType == 'end': if direction is None: direction = self.path.direction cellName = self.cellName x = self.path.x y = self.path.y if direction == '+x': theta = 0 elif direction == '+y': theta = pi/2 elif direction == '-y': theta = 3*pi/2 elif direction == '-x': theta = pi else: theta = direction points = self.rectPathPivot(x, y, 0, distance, width + 2*gap, theta) gapPoly = gdspy.Polygon(points, **spec) gapPoly.fillet(filletRadius) if gapType == 'beg': points = self.rectPathPivot(x, y, distance - filletRadius, distance, width + 2*gap, theta) elif gapType == 'end': points = self.rectPathPivot(x, y, 0, filletRadius, width + 2*gap, theta) gapPolyNoFillet = gdspy.Polygon(points, **spec) union = gdspy.boolean([gapPolyNoFillet, gapPoly], lambda gpnf, gp: gpnf or gp, **spec) # Correct for path offset if direction == '+x': self.path.x = x + distance self.path.y = y elif direction == '+y': self.path.x = x self.path.y = y + distance elif direction == '-y': self.path.x = x self.path.y = y - distance elif direction == '-x': self.path.x = x - distance self.path.y = y else: self.path.x = x + sin(theta)*distance self.path.y = y + cos(theta)*distance # Inner fillet position definitions if gapType == 'beg': x = self.path.x y = self.path.y elif gapType == 'end': x = x - cos(theta)*(2*filletRadius) y = y - sin(theta)*(2*filletRadius) # Create masks for fillet and so subtraction points = self.rectPathPivot(x, y, 0, 2*filletRadius, width, theta) rectMask = gdspy.Polygon(points, **spec) rectMask.fillet(filletRadius) if gapType == 'beg': points = self.rectPathPivot(x, y, 0, filletRadius, width, theta) elif gapType == 'end': points = self.rectPathPivot(x, y, filletRadius, 2*filletRadius, width, theta) rect = gdspy.Polygon(points, **spec) subtraction = gdspy.boolean([rectMask, rect], lambda rem, re: re and not rem, **spec) # Add polygons to cell cellName.add(subtraction) cellName.add(union)
def Draw_Sensor(Sensor_Number, Through_Line_Layer = 1, Resonator_Trace_Layer = 2, Pillar_Layer = 3): ''' This Function draws a sensor. It positions the resonators according to the mask definition input files. Its also draws the pillars outside of the bounding box of the resonators, the sensor die cut lines and the through line. Origin of the Sensor is the lower left corner. ''' #For Test Only! Remove when deployed #gdspy.Cell.cell_dict = {} # Obtain Sensor Parameters Sensor_Params = Mask_DB.Get_Mask_Data("SELECT X_Length, Y_Length, Cut_Line_Width, Pillar_Diameter, Pillar_Grid_Spacing, Pillar_Clearance, Through_Line_Type, Through_Line_Width, Through_Line_Turn_Radius,Through_Line_Gap, Through_Line_Edge_Offset FROM Sensors WHERE sensor_id = " + str(Sensor_Number),'all') #sql_cmd = "SELECT X_Length, Y_Length, Cut_Line_Width, Pillar_Diameter, Pillar_Grid_Spacing, Pillar_Clearance, Through_Line_Type, Through_Line_Width, Through_Line_Turn_Radius,Through_Line_Gap, Through_Line_Edge_Offset FROM Sensors WHERE Sensor = " + str(Sensor_Number) #Cursor.execute(sql_cmd) #Sensor_Params = Cursor.fetchall() #There should only be one sensor returned from database. We make sure of this. if len(Sensor_Params) > 1: print("Something is wrong. There are more than one senor #" + str(Sensor_Number) + ". Using the first one pulled.") Pillar_Grid_Spacing_Reduction_Factor = 3.0 X_Length, Y_Length, Cut_Line_Width, Pillar_Diameter, Pillar_Grid_Spacing, Pillar_Clearance, Through_Line_Type, Through_Line_Width, Through_Line_Turn_Radius,Through_Line_Gap, Through_Line_Edge_Offset = Sensor_Params[0] Pillar_Radius = Pillar_Diameter/2 Pillar_Grid_Spacing = Pillar_Grid_Spacing*Pillar_Grid_Spacing_Reduction_Factor #Initialize Sensor Cell sensor_cell_name = 'SENSOR_'+str(Sensor_Number) sensor_cell = gdspy.Cell(sensor_cell_name,exclude_from_global=False) # We draw the cut lines by boolean operation on two squares defining the die outline poly1 = gdspy.Rectangle(current_sensor_origin, current_sensor_origin+ np.array([X_Length, Y_Length]), layer=Resonator_Trace_Layer) poly2 = gdspy.Rectangle(current_sensor_origin + np.array([Cut_Line_Width, Cut_Line_Width]), current_sensor_origin+np.array([X_Length-Cut_Line_Width, Y_Length-Cut_Line_Width]), layer=Resonator_Trace_Layer) #poly1 = gdspy.Rectangle(Resonator_Trace_Layer, (0, 0), (X_Length, Y_Length)) #poly2 = gdspy.Rectangle(Resonator_Trace_Layer, (Cut_Line_Width, Cut_Line_Width), (X_Length-Cut_Line_Width, Y_Length-Cut_Line_Width)) primitives = [poly2,poly1] subtraction = lambda p1, p2: p2 and not p1 mask_cell.add(gdspy.boolean( primitives, subtraction, max_points=199,layer=Resonator_Trace_Layer)) # # # #Bare sensor cell is sensor cell without text and cutlines #bare_sensor_cell_name = 'BARE_'+sensor_cell_name #bare_sensor_cell = gdspy.Cell(bare_sensor_cell_name,exclude_from_global=False) # # # # # # # # # #Add the name of the sensor in the lower left of the die Text_Offset = 100 #sensor_cell.add(gdspy.Text(Resonator_Trace_Layer, 'S' + str(Sensor_Number), 300, (Cut_Line_Width+Text_Offset, Cut_Line_Width+Text_Offset))) text_cell.add(gdspy.Text('S' + str(Sensor_Number), 300, tuple(current_sensor_origin + np.array([Cut_Line_Width+Text_Offset, Cut_Line_Width+Text_Offset])), layer=Resonator_Trace_Layer)) # # # ## #Draw Throughline #assuming L shape Though_Line_Trace = gdspy.Path(Through_Line_Width, (X_Length/2, 0)) Though_Line_Trace.segment( Through_Line_Edge_Offset + Through_Line_Width/2 - Through_Line_Turn_Radius, '+y', layer=Through_Line_Layer) Though_Line_Trace.turn(Through_Line_Turn_Radius, 'l',layer=Through_Line_Layer) Though_Line_Trace.segment((X_Length/2)-Through_Line_Edge_Offset-Through_Line_Width/2 - 2*Through_Line_Turn_Radius, '-x', layer=Through_Line_Layer) Though_Line_Trace.turn(Through_Line_Turn_Radius, 'r', layer=Through_Line_Layer) Though_Line_Trace.segment(Y_Length-2*Through_Line_Edge_Offset-Through_Line_Width - 2*Through_Line_Turn_Radius, '+y',layer=Through_Line_Layer) _res_x_zero = Though_Line_Trace.x Though_Line_Trace.turn( Through_Line_Turn_Radius, 'r',layer=Through_Line_Layer) _res_y_zero = Though_Line_Trace.y Though_Line_Trace.segment( (X_Length/2)-Through_Line_Edge_Offset-Through_Line_Width/2 - 2*Through_Line_Turn_Radius, '+x',layer=Through_Line_Layer) Though_Line_Trace.turn( Through_Line_Turn_Radius, 'l',layer=Through_Line_Layer) Though_Line_Trace.segment(Through_Line_Edge_Offset + Through_Line_Width/2 - Through_Line_Turn_Radius, '+y',layer=Through_Line_Layer) sensor_cell.add(Though_Line_Trace) #This is a test rectange to check the through line placement #sensor_cell.add(gdspy.Rectangle(Resonator_Trace_Layer, (Through_Line_Edge_Offset, Through_Line_Edge_Offset), (Through_Line_Edge_Offset+Through_Line_Width/2 + Through_Line_Turn_Radius, Through_Line_Edge_Offset+Through_Line_Width/2 + Through_Line_Turn_Radius))) #Starting position of first resonator _res_x_zero += (Through_Line_Width/2) _res_y_zero += (-Through_Line_Width/2) _cur_Res_y_pos = _res_y_zero Res_IDs = Mask_DB.Get_Mask_Data("SELECT resonator_id, Coupler_Zone, Head_Space, Design_Freq,Design_Q FROM Resonators WHERE sensor_id = " + str(Sensor_Number),'all') #sql_cmd = "SELECT Design_Freq, Width, Design_Q, Head_Space, Coupler_Zone, Meander_Pitch, Meander_Zone FROM Resonators WHERE Sensor = " + str(Sensor_Number) #Cursor.execute(sql_cmd) #Res_IDs = Cursor.fetchall() #For boolean subtraction, however does not work due to insufficient memory # #Initialize Resonator Mask Cell, which will contain rectangles positioned over the resonators # res_mask_cell_name = 'S'+str(Sensor_Number)+'ResMask' # res_mask_cell = gdspy.Cell(res_mask_cell_name, exclude_from_global=True) _pillar_x_index = (Pillar_Grid_Spacing+Pillar_Diameter)/2 _pillar_y_index = Y_Length - (Pillar_Grid_Spacing+Pillar_Diameter)/2 Res_Number = 1 for ID in Res_IDs: resonator_id,Coupler_Zone, Head_Space,Design_Freq,Design_Q = ID Resonator_Name = 'F' + str(Design_Freq) + ' Qc' + str(int(Design_Q)) + ' R' + str(resonator_id) resonator_cell,_y_initial = Draw_Resonator(Resonator_Name=Resonator_Name,Resonator_ID=resonator_id, Resonator_Trace_Layer = Resonator_Trace_Layer, Pillar_Layer = Pillar_Layer, Y_Pitch_Tight = True, X_Pitch_Tight = True,Update_DB = True) _cur_Res_x_pos = Coupler_Zone + _res_x_zero _cur_Res_y_pos += -Head_Space-_y_initial #global current_resonator_origin current_resonator_origin = np.array([_cur_Res_x_pos, _cur_Res_y_pos]) resonator_origin = str(current_resonator_origin.tolist()).replace(',',';') Mask_DB.Update_Computed_Parameters(resonator_id, {"Resonator_Origin":'"'+resonator_origin+'"'}) _curr_Res_ref = gdspy.CellReference(resonator_cell, tuple(current_resonator_origin)) sensor_cell.add(_curr_Res_ref) #add resonator label text text_location = current_sensor_origin + current_resonator_origin + resonator_label_location text_cell.add(gdspy.Text( Resonator_Name, 3*Pillar_Radius, tuple(text_location), layer=Resonator_Trace_Layer)) _bounding_box_edges = resonator_cell.get_bounding_box() + np.array([[_cur_Res_x_pos, _cur_Res_y_pos],[_cur_Res_x_pos, _cur_Res_y_pos]]) _total_y_distance = _bounding_box_edges[1][1] - _bounding_box_edges[0][1] _cur_Res_y_pos += -_total_y_distance while _pillar_y_index >= _cur_Res_y_pos: while _pillar_x_index <= X_Length - Pillar_Radius: if not in_rectangle((_pillar_x_index, _pillar_y_index),_bounding_box_edges,Pillar_Radius):#Pillar_Radius+Pillar_Grid_Spacing): sensor_cell.add(gdspy.Round((_pillar_x_index, _pillar_y_index), Pillar_Radius,number_of_points=60, layer=Pillar_Layer)) _pillar_x_index += Pillar_Grid_Spacing _pillar_x_index = (Pillar_Grid_Spacing+Pillar_Diameter)/2 _pillar_y_index -= Pillar_Grid_Spacing Res_Number += 1 while _pillar_y_index >= Pillar_Radius: while _pillar_x_index <= X_Length - Pillar_Radius: sensor_cell.add(gdspy.Round((_pillar_x_index, _pillar_y_index), Pillar_Radius,number_of_points=60, layer=Pillar_Layer)) _pillar_x_index += Pillar_Grid_Spacing _pillar_x_index = (Pillar_Grid_Spacing+Pillar_Diameter)/2 _pillar_y_index -= Pillar_Grid_Spacing #bare_sensor_cell_ref = gdspy.CellReference(bare_sensor_cell, (0,0)) #sensor_cell.add(bare_sensor_cell_ref) Sensor_Dict = {"Through_Line_Metal_Area":Though_Line_Trace.area(),"Through_Line_Length":Though_Line_Trace.length, "Sensor_Pillar_Area": sensor_cell.area(by_spec=True)[(Pillar_Layer,0)], "sensor_id":Sensor_Number, "Sensor_Cell_Name":'"'+sensor_cell_name+'"' } for ID in Res_IDs: resonator_id = ID[0] Mask_DB.Update_Computed_Parameters(resonator_id, Sensor_Dict) return sensor_cell
def draw_alignment(Inner_Cross_Layer, Outer_Cross_Layer,Inner_Cross_Thickness,Outer_Cross_Thickness,Outer_Cross_Width, Guidelines = True, Outer_Only = False, *arg): """ Inner_Cross_Thickness is line width of cross Outer_Cross_Thickness is line width of outer cross Outer_Cross_Width is overall width and height of cross Guidelines = True -- draws stepped guidlines which point to alignment cross Outer_Only = True -- draws only the outer features on each layer: Inner_Cross_Layer, Outer_Cross_Layer if Outer_Cross_Thickness == 0 only the inner features are drawn on each layer: Inner_Cross_Layer, Outer_Cross_Layer """ Inner_Cross_Thickness = float(Inner_Cross_Thickness) Outer_Cross_Thickness = float(Outer_Cross_Thickness) Outer_Cross_Width = float(Outer_Cross_Width) if arg is not (): ang = arg[0] else: ang = 0 def draw_cross(Layer,cross_line_width,cross_width): """cross_line_width is line width of cross cross_width is Overall width and height of cross """ P_1 = gdspy.Path(cross_line_width, (-cross_width/2, 0)) P_1.segment(cross_width, '+x', layer = Layer) P_2 = gdspy.Path(cross_line_width, (0,-cross_width/2)) P_2.segment((cross_width/2)-(cross_line_width/2), '+y', layer = Layer) P_3 = gdspy.Path(cross_line_width, (P_2.x,P_2.y+cross_line_width)) P_3.segment((cross_width/2)-(cross_line_width/2), '+y', layer = Layer) return P_1.polygons+P_2.polygons+ P_3.polygons def draw_guide_lines(Layer,Line_Width_Start,Length,Steps): polygons = [] for i in xrange(1,Steps+1): polygons.append(gdspy.Path(Line_Width_Start*i,(Outer_Cross_Width/2 + Inner_Cross_Thickness +(i-1)*(Length/Steps),0)).segment(Length/Steps,'+x',layer = Layer).polygons) for i in xrange(1,Steps+1): polygons.append(gdspy.Path(Line_Width_Start*i,(-(Outer_Cross_Width/2 + Inner_Cross_Thickness +(i-1)*(Length/Steps)),0)).segment(Length/Steps,'-x', layer = Layer).polygons) return reduce(lambda x, y: x+y,polygons) cross_line_width = Inner_Cross_Thickness #line width of cross cross_width = Outer_Cross_Width - 2*Outer_Cross_Thickness # overall width and height of cross i = draw_cross(Inner_Cross_Layer,cross_line_width,cross_width) inner_cross_poly_set = gdspy.PolygonSet(i, layer =Inner_Cross_Layer ).rotate(ang, center=(0, 0)) if Outer_Cross_Thickness==0: o = draw_cross(Outer_Cross_Layer,cross_line_width,cross_width) outer_cross_poly_set = gdspy.PolygonSet(o, layer =Outer_Cross_Layer ).rotate(ang, center=(0, 0)) else: o = draw_cross(Outer_Cross_Layer,3*cross_line_width,Outer_Cross_Width) primitives = [gdspy.PolygonSet(i, layer = 0),gdspy.PolygonSet( o, layer = 0)] subtraction = lambda p1, p2: p2 and not p1 outer_cross_poly_set = gdspy.boolean( primitives, subtraction, max_points=199,layer =Outer_Cross_Layer).rotate(ang, center=(0, 0)) if Outer_Only == True: inner_cross_poly_set = gdspy.boolean( primitives, subtraction, max_points=199,layer =Inner_Cross_Layer ).rotate(ang, center=(0, 0)) poly_set_list = [inner_cross_poly_set, outer_cross_poly_set] if Guidelines == True: guide_line_length = 5*Outer_Cross_Width num_steps = 3 gi = draw_guide_lines(Inner_Cross_Layer,Inner_Cross_Thickness,guide_line_length,num_steps) inner_guide_line_poly_set = gdspy.PolygonSet( gi, layer =Inner_Cross_Layer ) if Outer_Cross_Thickness==0: go = draw_guide_lines(Outer_Cross_Layer,Inner_Cross_Thickness,guide_line_length,num_steps) outer_guide_line_poly_set = gdspy.PolygonSet( go, layer =Outer_Cross_Layer ) else: go = draw_guide_lines(Outer_Cross_Layer,2*Outer_Cross_Thickness+Inner_Cross_Thickness ,guide_line_length,num_steps) primitives = [gdspy.PolygonSet(gi, layer = 0),gdspy.PolygonSet( go, layer = 0 )] subtraction = lambda p1, p2: p2 and not p1 outer_guide_line_poly_set = gdspy.boolean(primitives, subtraction, max_points=199, layer =Inner_Cross_Layer ) if Outer_Only == True: inner_guide_line_poly_set = gdspy.boolean(primitives, subtraction, max_points=199, layer =Inner_Cross_Layer ) poly_set_list.append(inner_guide_line_poly_set) poly_set_list.append(outer_guide_line_poly_set) align_cell = gdspy.Cell('Alignment_Mark', exclude_from_global=True) align_cell.add(poly_set_list) return align_cell
"6,5 -2,2 0,5 2,2 7,0 0,11 3,0 0,-18 -2,-2", ] paths = [] for s in spaths: p = [np.array(v.split(','), dtype='f8') for v in s.split(' ')] p = [np.sum(p[:i + 1], 0) for i in range(len(p))] p = [np.array((v[0], H - v[1])) for v in p] paths.append(p) invcells = [] for i, p in enumerate(paths): r = gdspy.Rectangle((0, 0), (W, H), layer=0) pol = gdspy.boolean([r, p], lambda r, p: r and not p, layer=WGTEXT) c = gdspy.Cell("ifont_%1d" % i, exclude_from_global=True) c.add(pol) invcells.append(c) invtextid = 0 def invtext(text, height=12): global invtextid c = gdspy.Cell("invtext_%04d" % int(invtextid), exclude_from_global=True) invtextid += 1 mag = float(height) / H for i, l in enumerate(text): if l not in "0123456789": raise TypeError("DONT KNOW HOW TO MAKE LETTERS \"%s\"." % l) c.add(gdspy.CellReference(
import gdspy bool_cell = gdspy.Cell('BOOLEAN') liPolygon = [] liUnitDummy = [] yPitch=6 for i in range(4): yMove = i*yPitch liUnitDummy.append(gdspy.Rectangle(51, (0, yMove+3), (18, yMove)).points) frame_poly = gdspy.Rectangle(11,(-10,30),(30,-10)).points frame_poly_list = [frame_poly] #liPolygon.append(liUnitDummy) subtraction = lambda p1, p2: p2 and not p1 bool_cell.add(gdspy.boolean(1,[gdspy.PolygonSet(0,liUnitDummy), gdspy.PolygonSet(0,frame_poly_list)], subtraction, max_points=199)) #bool_cell.add(gdspy.PolygonSet(1, liUnitDummy[0]+liUnitDummy[1]+liUnitDummy[2]+liUnitDummy[3])) #bool_cell.add(liUnitDummy) name = os.path.abspath(os.path.dirname(os.sys.argv[0])) + os.sep +\ 'gdspy-autodummy' gdspy.gds_print(name + '.gds', unit=1.0e-6, precision=1.0e-9) gdspy.LayoutViewer()
## We create a PolygonSet that contains both our path segments and ## ring, and then append it to our list of operands. primitives.append(gdspy.PolygonSet(bool_path.polygons + ring.polygons)) ## The list of operands contains 2 polygon sets. We will subtract the ## 1st (narrower) from the 2nd (wider). For that we need to define a ## function that receives 2 integers (each representing an operand) and ## returns the operation we want executed. Here we use a lambda ## expression to do so. subtraction = lambda p1, p2: p2 and not p1 ## We perform the operation, put the resulting polygons in layer 1, and ## add to our boolean cell. bool_cell.add(gdspy.boolean(primitives, subtraction, max_points=199, layer=1)) ## ------------------------------------------------------------------ ## ## POLYGON OFFSET ## ------------------------------------------------------------------ ## ## Polygon offset (inset and outset) can be used, for instance, to ## define safety margins around shapes. spec = {'layer': 7} path4 = gdspy.Path(0.5, (21, -5)).segment(3, '+x', **spec)\ .turn(4, 'r', **spec).turn(4, 'rr', **spec)\ .segment(3, **spec) path_cell.add(path4)
def Draw_Sensor(Sensor_Number, Through_Line_Layer = 1, Resonator_Trace_Layer = 2, Pillar_Layer = 3): ''' This Function draws a sensor. It positions the resonators according to the mask definition input files. Its also draws the pillars outside of the bounding box of the resonators, the sensor die cut lines and the through line. Origin of the Sensor is the lower left corner. ''' #For Test Only! Remove when deployed #gdspy.Cell.cell_dict = {} # Obtain Sensor Parameters Sensor_Params = Mask_DB.Get_Mask_Data("SELECT X_Length, Y_Length, Cut_Line_Width, Pillar_Diameter, Pillar_Grid_Spacing, Pillar_Clearance, Through_Line_Type, Through_Line_Width, Through_Line_Turn_Radius,Through_Line_Gap, Through_Line_Edge_Offset FROM Sensors WHERE sensor_id = " + str(Sensor_Number),'all') #sql_cmd = "SELECT X_Length, Y_Length, Cut_Line_Width, Pillar_Diameter, Pillar_Grid_Spacing, Pillar_Clearance, Through_Line_Type, Through_Line_Width, Through_Line_Turn_Radius,Through_Line_Gap, Through_Line_Edge_Offset FROM Sensors WHERE Sensor = " + str(Sensor_Number) #Cursor.execute(sql_cmd) #Sensor_Params = Cursor.fetchall() #There should only be one sensor returned from database. We make sure of this. if len(Sensor_Params) > 1: print("Something is wrong. There are more than one senor #" + str(Sensor_Number) + ". Using the first one pulled.") Pillar_Grid_Spacing_Reduction_Factor = 3.0 X_Length, Y_Length, Cut_Line_Width, Pillar_Diameter, Pillar_Grid_Spacing, Pillar_Clearance, Through_Line_Type, Through_Line_Width, Through_Line_Turn_Radius,Through_Line_Gap, Through_Line_Edge_Offset = Sensor_Params[0] Pillar_Radius = Pillar_Diameter/2 Pillar_Grid_Spacing = Pillar_Grid_Spacing*Pillar_Grid_Spacing_Reduction_Factor #Initialize Sensor Cell sensor_cell_name = 'SENSOR_'+str(Sensor_Number) sensor_cell = gdspy.Cell(sensor_cell_name,exclude_from_global=True) # We draw the cut lines by boolean operation on two squares defining the die outline poly1 = gdspy.Rectangle(Resonator_Trace_Layer, (0, 0), (X_Length, Y_Length)) poly2 = gdspy.Rectangle(Resonator_Trace_Layer, (Cut_Line_Width, Cut_Line_Width), (X_Length-Cut_Line_Width, Y_Length-Cut_Line_Width)) primitives = [poly2,poly1] subtraction = lambda p1, p2: p2 and not p1 sensor_cell.add(gdspy.boolean(Resonator_Trace_Layer, primitives, subtraction, max_points=199)) #Bare sensor cell is sensor cell without text and cutlines bare_sensor_cell_name = 'BARE_'+sensor_cell_name bare_sensor_cell = gdspy.Cell(bare_sensor_cell_name,exclude_from_global=False) #Add the name of the sensor in the lower left of the die Text_Offset = 100 sensor_cell.add(gdspy.Text(Resonator_Trace_Layer, 'S' + str(Sensor_Number), 300, (Cut_Line_Width+Text_Offset, Cut_Line_Width+Text_Offset))) #Draw Throughline #assuming L shape Though_Line_Trace = gdspy.Path(Through_Line_Width, (X_Length/2, 0)) Though_Line_Trace.segment(Through_Line_Layer, Through_Line_Edge_Offset + Through_Line_Width/2 - Through_Line_Turn_Radius, '+y') Though_Line_Trace.turn(Through_Line_Layer, Through_Line_Turn_Radius, 'l') Though_Line_Trace.segment(Through_Line_Layer, (X_Length/2)-Through_Line_Edge_Offset-Through_Line_Width/2 - 2*Through_Line_Turn_Radius, '-x') Though_Line_Trace.turn(Through_Line_Layer, Through_Line_Turn_Radius, 'r') Though_Line_Trace.segment(Through_Line_Layer, Y_Length-2*Through_Line_Edge_Offset-Through_Line_Width - 2*Through_Line_Turn_Radius, '+y') _res_x_zero = Though_Line_Trace.x Though_Line_Trace.turn(Through_Line_Layer, Through_Line_Turn_Radius, 'r') _res_y_zero = Though_Line_Trace.y Though_Line_Trace.segment(Through_Line_Layer, (X_Length/2)-Through_Line_Edge_Offset-Through_Line_Width/2 - 2*Through_Line_Turn_Radius, '+x') Though_Line_Trace.turn(Through_Line_Layer, Through_Line_Turn_Radius, 'l') Though_Line_Trace.segment(Through_Line_Layer, Through_Line_Edge_Offset + Through_Line_Width/2 - Through_Line_Turn_Radius, '+y') bare_sensor_cell.add(Though_Line_Trace) #This is a test rectange to check the through line placement #sensor_cell.add(gdspy.Rectangle(Resonator_Trace_Layer, (Through_Line_Edge_Offset, Through_Line_Edge_Offset), (Through_Line_Edge_Offset+Through_Line_Width/2 + Through_Line_Turn_Radius, Through_Line_Edge_Offset+Through_Line_Width/2 + Through_Line_Turn_Radius))) #Starting position of first resonator _res_x_zero += (Through_Line_Width/2) _res_y_zero += (-Through_Line_Width/2) _cur_Res_y_pos = _res_y_zero Res_IDs = Mask_DB.Get_Mask_Data("SELECT resonator_id, Coupler_Zone, Head_Space, Design_Freq,Design_Q FROM Resonators WHERE sensor_id = " + str(Sensor_Number),'all') #sql_cmd = "SELECT Design_Freq, Width, Design_Q, Head_Space, Coupler_Zone, Meander_Pitch, Meander_Zone FROM Resonators WHERE Sensor = " + str(Sensor_Number) #Cursor.execute(sql_cmd) #Res_IDs = Cursor.fetchall() #For boolean subtraction, however does not work due to insufficient memory # #Initialize Resonator Mask Cell, which will contain rectangles positioned over the resonators # res_mask_cell_name = 'S'+str(Sensor_Number)+'ResMask' # res_mask_cell = gdspy.Cell(res_mask_cell_name, exclude_from_global=True) _pillar_x_index = (Pillar_Grid_Spacing+Pillar_Diameter)/2 _pillar_y_index = Y_Length - (Pillar_Grid_Spacing+Pillar_Diameter)/2 Res_Number = 1 for ID in Res_IDs: resonator_id,Coupler_Zone, Head_Space,Design_Freq,Design_Q = ID Resonator_Name = 'F' + str(Design_Freq) + ' Qc' + str(int(Design_Q)) + ' R' + str(resonator_id) #Resonator_Name = 'S' + str(Sensor_Number) + 'R' + str(Res_Number) + '#' + str(resonator_id) resonator_cell,_y_initial = Draw_Resonator(Resonator_Name=Resonator_Name,Resonator_ID=resonator_id, Resonator_Trace_Layer = Resonator_Trace_Layer, Pillar_Layer = Pillar_Layer, Y_Pitch_Tight = True, X_Pitch_Tight = True,Update_DB = True) _cur_Res_x_pos = Coupler_Zone + _res_x_zero _cur_Res_y_pos += -Head_Space-_y_initial _curr_Res_ref = gdspy.CellReference(resonator_cell, (_cur_Res_x_pos, _cur_Res_y_pos)) bare_sensor_cell.add(_curr_Res_ref) _bounding_box_edges = resonator_cell.get_bounding_box() + np.array([[_cur_Res_x_pos, _cur_Res_y_pos],[_cur_Res_x_pos, _cur_Res_y_pos]]) #sensor_cell.add( gdspy.Rectangle(Through_Line_Layer, (_bounding_box_edges[0][0], _bounding_box_edges[0][1]), (_bounding_box_edges[1][0], _bounding_box_edges[1][1]))) #For boolean subtraction, however does not work due to insufficient memory #res_mask_cell.add(gdspy.Rectangle(Through_Line_Layer, (_bounding_box_edges[0][0], _bounding_box_edges[0][1]), (_bounding_box_edges[1][0], _bounding_box_edges[1][1]))) #_total_x_distance = _bounding_box_edges[1][0] - _bounding_box_edges[0][0] #not used currently _total_y_distance = _bounding_box_edges[1][1] - _bounding_box_edges[0][1] _cur_Res_y_pos += -_total_y_distance while _pillar_y_index >= _cur_Res_y_pos: while _pillar_x_index <= X_Length - Pillar_Radius: if not in_rectangle((_pillar_x_index, _pillar_y_index),_bounding_box_edges,Pillar_Radius):#Pillar_Radius+Pillar_Grid_Spacing): bare_sensor_cell.add(gdspy.Round(Pillar_Layer, (_pillar_x_index, _pillar_y_index), Pillar_Radius,number_of_points=60)) _pillar_x_index += Pillar_Grid_Spacing _pillar_x_index = (Pillar_Grid_Spacing+Pillar_Diameter)/2 _pillar_y_index -= Pillar_Grid_Spacing Res_Number += 1 while _pillar_y_index >= Pillar_Radius: while _pillar_x_index <= X_Length - Pillar_Radius: bare_sensor_cell.add(gdspy.Round(Pillar_Layer, (_pillar_x_index, _pillar_y_index), Pillar_Radius,number_of_points=60)) _pillar_x_index += Pillar_Grid_Spacing _pillar_x_index = (Pillar_Grid_Spacing+Pillar_Diameter)/2 _pillar_y_index -= Pillar_Grid_Spacing bare_sensor_cell_ref = gdspy.CellReference(bare_sensor_cell, (0,0)) sensor_cell.add(bare_sensor_cell_ref) Sensor_Dict = {"Through_Line_Metal_Area":Though_Line_Trace.area(),"Through_Line_Length":Though_Line_Trace.length, "Sensor_Pillar_Area": sensor_cell.area(by_layer=True)[Pillar_Layer], "sensor_id":Sensor_Number, "Sensor_Cell_Name":'"'+bare_sensor_cell_name+'"'} for ID in Res_IDs: resonator_id = ID[0] Mask_DB.Update_Computed_Parameters(resonator_id, Sensor_Dict) #Insufficuent Memory to perform boolean subtraction below # #Initialize Pillar Cell, which will contain a grid of pillars for the sensor # pillar_cell_name = 'S'+str(Sensor_Number)+'Pillars' # pillar_cell = gdspy.Cell(pillar_cell_name, exclude_from_global=True) # pillar_cell.add(gdspy.Round(Pillar_Layer, (0, 0), Pillar_Radius)) # #Perform boolean subtraction of resonator mask rectangels # num_pillars_x = (X_Length-Pillar_Grid_Spacing-2*Pillar_Radius)%Pillar_Grid_Spacing # num_pillars_y = (Y_Length-Pillar_Grid_Spacing-2*Pillar_Radius)%Pillar_Grid_Spacing # primitives = [gdspy.CellReference(res_mask_cell),gdspy.CellArray(pillar_cell, int(num_pillars_x), int(num_pillars_y), (Pillar_Grid_Spacing, Pillar_Grid_Spacing) ,(Pillar_Grid_Spacing/2 + Pillar_Radius, Pillar_Grid_Spacing/2 + Pillar_Radius),magnification=1)] # sensor_cell.add(gdspy.boolean(Pillar_Layer, primitives, sub, max_points=199)) # name = os.path.abspath(os.path.dirname(os.sys.argv[0])) + os.sep + 'single_sensor' # ## Output the layout to a GDSII file (default to all created cells). # ## Set the units we used to micrometers and the precision to nanometers. # gdspy.gds_print(name + '.gds', unit=1.0e-6, precision=1.0e-9) # print('Sample gds file saved: ' + name + '.gds') # ## Save an image of the boolean cell in a png file. Resolution refers # ## to the number of pixels per unit in the layout. Resolution changed from 4 # ## to 1 to avoid "malloc_error" # #gdspy.gds_image([resonator_cell], image_name=name, resolution=1, antialias=4) # #comment out save as png for speed # #print('Image of the boolean cell saved: ' + name + '.png') # ## Import the file we just created, and extract the cell 'POLYGONS'. To # ## avoid naming conflict, we will rename all cells. # #gdsii = gdspy.GdsImport(name + '.gds', rename={sensor_cell_name:'IMPORT_SENSOR'}) # ## Now we extract the cells we want to actually include in our current # ## structure. Note that the referenced cells will be automatically # ## extracted as well. # #gdsii.extract(sensor_cell_name) # ## View the layout using a GUI. Full description of the controls can # ## be found in the online help at http://gdspy.sourceforge.net/ # gdspy.LayoutViewer(colors=[None] * 64) # for outlined polygons # #gdspy.LayoutViewer() return sensor_cell