def bitmarker(nx, ny, d=1, layer=3): nA = 4 if (nx >= 32) or (ny >= 32): nA = 5 marker = A_marker(number_of_squares=nA, d=d, layer=layer) if nx > 0: markerBx = B_marker_bits(nx, d=d, layer=layer) marker = gp.fast_boolean(marker, markerBx, "or", layer=layer) if ny > 0: markerBy = B_marker_bits(ny, d=d, layer=layer).mirror((d, -d)) marker = gp.fast_boolean(marker, markerBy, "or", layer=layer) return marker
def m_wire(w, L, l=0.5, d=0.5, up=False, layer=1): p1 = [(-L / 2, 0), (L / 2, 0)] if up == False: p2 = [(0, 0), (-l * math.cos(math.pi / 3), -l * math.sin(math.pi / 3))] else: p2 = [(0, 0), (l * math.cos(math.pi / 3), l * math.sin(math.pi / 3))] arm1 = gp.PolyPath(p1, w, layer=layer) arm2 = gp.PolyPath(p2, w, layer=layer) arm3 = gp.PolyPath(p2, w, layer=layer).translate(-d, 0) arm4 = gp.PolyPath(p2, w, layer=layer).translate(d, 0) t = gp.fast_boolean(arm1, arm2, "or", layer=layer) n = gp.fast_boolean(arm3, arm4, "or", layer=layer) m = gp.fast_boolean(t, n, "or", layer=layer) return m
def build_mask(cell, wgt, final_layer=None, final_datatype=None): """ Builds the appropriate mask according to the resist specifications and fabrication type. Does this by applying a boolean 'XOR' or 'AND' operation on the waveguide and clad masks. Args: * **cell** (gdspy.Cell): Cell with components. Final mask is placed in this cell. * **wgt** (WaveguideTemplate): Waveguide template containing the resist information, and layers/datatypes for the waveguides and cladding. Keyword Args: * **final_layer** (int): layer to place the mask on (defaults to `wgt.clad_layer + 1`) * **final_datatype** (int): datatype to place the mask on (defaults to `0`) Returns: None """ fl = wgt.clad_layer + 1 if final_layer == None else final_layer fd = 0 if final_datatype == None else final_datatype polygons = cell.get_polygons(by_spec=True) try: pWG = polygons[(wgt.wg_layer, wgt.wg_datatype)] pCLAD = polygons[(wgt.clad_layer, wgt.clad_datatype)] except KeyError: print( "Warning! No objects written to layer/datatype specified by WaveguideTemplate" ) if wgt.resist == "+": cell.add( gdspy.fast_boolean( pWG, pCLAD, "xor", precision=0.001, max_points=199, layer=fl, datatype=fd, )) elif wgt.resist == "-": cell.add( gdspy.fast_boolean( pWG, pCLAD, "and", precision=0.001, max_points=199, layer=fl, datatype=fd, ))
def add_reservoir_to_shape(shape, x_width=2, y_height=0.5, position=(0, 0), layer=1, rotation=0): """ Takes a shape and adds a rectangle of specified width and height to some position :param shape: gp.PolygonSet :param x_width: in um :param y_height: in um :param position: (x, y) center of the rectangle :param layer: shapes are on layer 1 by default :param rotation: float in degrees :return: None """ reservoir = gp.Rectangle((-x_width / 2, -y_height / 2), (x_width / 2, y_height / 2), layer=layer).rotate(rotation * math.pi / 180) return gp.fast_boolean(shape, reservoir.translate(position[0], position[1]), 'or', layer=layer)
def build2(self,shape,cell): return cell.add(shape) for i in range(0,len(input_shape)): shape = gdspy.Polygon(input_shape[i],input_layer) output_bool = gdspy.fast_boolean(bool_shape,shape, mode, precision=1e-9, max_points=1000, layer=output_layer) return shape, output_bool
def _find_overlap(cls, geos1, geos2, layer=0): """Return a geometry on layer 0 that represents any overlap in geometries. """ geos1 = cls._union_of_geometries(geos1, layer) geos2 = cls._union_of_geometries(geos2, layer) return gdspy.fast_boolean(geos1, geos2, 'and', layer=layer)
def tessellate(cell, i=199, lyr=None): """ within a cell, an old layer is subtracted from the chip size, and is written to the new layer Args: cell - gdspy cell lyr - layer name Return: cell with inverted new layer and removed old layer """ mask = [] if lyr is None: layers = cell.get_polygons(by_spec=True) for l in layers: i = 199 while hole(cell, l): tessellate(cell, i, l) i = i - 1 return None polygons = cell.get_polygons(by_spec=True)[lyr] box_coord = cell.get_bounding_box() bbox = gdspy.Rectangle(box_coord[0], box_coord[1]) for p in polygons: mask.append(gdspy.Polygon(p)) cell.remove_polygons(lambda pts, layer, datatype: layer == lyr[0]) result = gdspy.fast_boolean(bbox, mask, 'and', max_points=i, layer=lyr[0]) # print(len(result.polygons)) cell.add(result)
def xor_polygons_phidl(A, B, hash_geom=True): """ Given two devices A and B, performs a layer-by-layer XOR diff between A and B, and returns polygons representing the differences between A and B. """ from phidl import Device import gdspy # first do a geometry hash to vastly speed up if they are equal if hash_geom and (A.hash_geometry() == B.hash_geometry()): return Device() D = Device() A_polys = A.get_polygons(by_spec=True) B_polys = B.get_polygons(by_spec=True) A_layers = A_polys.keys() B_layers = B_polys.keys() all_layers = set() all_layers.update(A_layers) all_layers.update(B_layers) for layer in all_layers: if (layer in A_layers) and (layer in B_layers): p = gdspy.fast_boolean(A_polys[layer], B_polys[layer], operation='xor', precision=0.001, max_points=4000, layer=layer[0], datatype=layer[1]) elif (layer in A_layers): p = A_polys[layer] elif (layer in B_layers): p = B_polys[layer] if p is not None: D.add_polygon(p, layer=layer) return D
def add_double_SmartWall(struct_list, l=5.5, delta_x=0, delta_y=-0.6, relative_y=-0.8, x_superposition=0.15, min_thickness=0.4, layer=2, zigzag=False, zigzag_d=0.3, zigzag_angle=30): if -relative_y < min_thickness + 0.3: min_thickness = max(0.2, -relative_y - 0.3) p1 = [(0, 0), (0, -min_thickness), (-2, -1.5), (-l, -1.5), (-l, 0), (0, 0)] p2 = [(-x_superposition, 0), (l, 0), (l, -2), (-0.5, -2), (-0.5, -1.6), (-x_superposition, 0)] if zigzag == True: p1 = straight_to_zigzag_line(p1, zigzag_d, zigzag_angle) p2 = straight_to_zigzag_line(p2, zigzag_d, zigzag_angle) SW1 = gp.Polygon(p1, layer=layer) SW1.translate(delta_x, delta_y) SW2 = gp.Polygon(p2, layer=layer) SW2.translate(delta_x, delta_y + relative_y) SW = gp.fast_boolean(SW1, SW2, "or", layer=layer) struct_list.append(SW) return struct_list
def braggGrating(period=.310, NG=400, waveguideWidth=0.5, dwidth=0.05, layerNumber=1): # Intialize cells name = 'braggCell=' + str(int(period * 1e3)) + '_NG=' + str( int(NG)) + '_dwidth=' + str(int(dwidth * 1e3)) braggCell = gdspy.Cell(name) # Calculate Parameters L = NG * period # Generate main waveguide waveguide = gdspy.Rectangle([-L / 2, waveguideWidth / 2 - dwidth / 2], [L / 2, -waveguideWidth / 2 + dwidth / 2], layer=layerNumber) # Add side strips strips = [] startRect = -L / 2 stopRect = -L / 2 + period / 2 for k in range(0, NG): strips.append( gdspy.Rectangle([startRect, waveguideWidth / 2 + dwidth / 2], [stopRect, -waveguideWidth / 2 - dwidth / 2], layer=layerNumber)) startRect = startRect + period stopRect = stopRect + period finalShape = gdspy.fast_boolean(waveguide, strips, 'or', layer=layerNumber) braggCell.add(finalShape) return braggCell
def _union_of_geometries(cls, geometries, layer): """Returns union of all geometries in list ``geometries``""" result = geometries[0] for geo in geometries[1:]: result = gdspy.fast_boolean(result, geo, 'or', layer=layer) return result
def _etch_target_helper(self): etch_layers = [] etch_targets = [] layer_target = [] for elay in self.layerstack.etch_layers: etch_layers.append( self.gds.get_polygons(by_spec=True, depth=None)[(elay.layer, elay.datatype)]) layer_target.append((elay.layer, elay.datatype)) for etar in self.layerstack.etch_targets: etch_targets.append( self.gds.get_polygons(by_spec=True, depth=None)[(etar.layer, etar.datatype)]) # Do this with gdspy! dont go back to Device. etch_layers, etch_targets new_polygons = [] for i in range(len(etch_targets)): p = gdspy.fast_boolean(operand1=etch_targets[i], operand2=etch_layers[i], operation='not', precision=1e-9, max_points=4000, layer=702, datatype=727) new_polygons.append(p) self._remove_etch_layers() for i in range(len(new_polygons)): self.gds.add_polygon(new_polygons[i], layer=layer_target[i])
def A_marker(number_of_squares=4, d=1, layer=3): square = gp.Rectangle((0, 0), (d, -d), layer=layer) marker = gp.Rectangle((0, 0), (d, -d), layer=layer) for _ in range(1, number_of_squares): square.translate(d, -d) marker = gp.fast_boolean(marker, square, "or", layer=layer) return marker
def generate_ground(self): x = self.g_w y = self.g_h z = self.center t = self.g_t ground1 = gdspy.Rectangle((z[0] - x / 2, z[1] - y / 2), (z[0] + x / 2, z[1] + y / 2)) ground2 = gdspy.Rectangle((z[0] - x / 2 + t, z[1] - y / 2 + t), (z[0] + x / 2 - t, z[1] + y / 2 - t)) ground = gdspy.fast_boolean(ground1, ground2, 'not') for key in self.remove_ground: factor = 1 if key == 'left': if self.remove_ground[key] != None: factor = self.remove_ground[key] ground = gdspy.fast_boolean( ground, gdspy.Rectangle( (z[0] - x / 2, z[1] - factor * y / 2 + t), (z[0] - x / 2 + t, z[1] + factor * y / 2 - t)), 'not') if key == 'right': if self.remove_ground[key] != None: factor = self.remove_ground[key] ground = gdspy.fast_boolean( ground, gdspy.Rectangle( (z[0] + x / 2, z[1] - factor * y / 2 + t), (z[0] + x / 2 - t, z[1] + factor * y / 2 - t)), 'not') if key == 'top': if self.remove_ground[key] != None: factor = self.remove_ground[key] ground = gdspy.fast_boolean( ground, gdspy.Rectangle( (z[0] - factor * x / 2 + t, z[1] + y / 2), (z[0] + factor * x / 2 - t, z[1] + y / 2 - t)), 'not') if key == 'bottom': if self.remove_ground[key] != None: factor = self.remove_ground[key] ground = gdspy.fast_boolean( ground, gdspy.Rectangle( (z[0] - factor * x / 2 + t, z[1] - y / 2), (z[0] + factor * x / 2 - t, z[1] - y / 2 + t)), 'not') return ground
def star(w, L, layer=1): p1 = [(-L * math.cos(math.pi / 3), L * math.sin(math.pi / 3)), (0, 0), (-L * math.cos(math.pi / 3), -L * math.sin(math.pi / 3))] p2 = [(0, 0), (L, 0)] arm1 = gp.PolyPath(p1, w, layer=layer) arm2 = gp.PolyPath(p2, w, layer=layer) star = gp.fast_boolean(arm1, arm2, "or", layer=layer) return star
def parallel_wire(w, L, distance=1, layer=1, orientation=0): """ Returns a gdspy shape with two parallel wires centered at origin """ w1 = single_wire(w, L, layer=layer, orientation=0).translate(0, distance / 2) w2 = single_wire(w, L, layer=layer, orientation=0).translate(0, -distance / 2) angle_rad = orientation * math.pi / 180 return gp.fast_boolean(w1, w2, 'or', layer=layer).rotate(angle_rad)
def barred_hexagon(w, L, layer=1): p1 = [(-L, 0), (-L * math.cos(math.pi / 3), L * math.sin(math.pi / 3)), (+L * math.cos(math.pi / 3), L * math.sin(math.pi / 3)), (L, 0), (L * math.cos(math.pi / 3), -L * math.sin(math.pi / 3)), (-L * math.cos(math.pi / 3), -L * math.sin(math.pi / 3)), (-L, 0)] p2 = [(-3 * L, 0), (3 * L, 0)] hexagon = gp.PolyPath(p1, w, layer=layer) bar = gp.PolyPath(p2, w, layer=layer) struct = gp.fast_boolean(hexagon, bar, "or", layer=layer) return struct
def t_wire(w, L, l=1, up=False, layer=1): p1 = [(-L / 2, 0), (L / 2, 0)] if up == False: p2 = [(0, 0), (-l * math.cos(math.pi / 3), -l * math.sin(math.pi / 3))] else: p2 = [(0, 0), (l * math.cos(math.pi / 3), l * math.sin(math.pi / 3))] arm1 = gp.PolyPath(p1, w, layer=layer) arm2 = gp.PolyPath(p2, w, layer=layer) t = gp.fast_boolean(arm1, arm2, "or", layer=layer) return t
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.fast_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.fast_boolean(d[(2, 3)], gdspy.Rectangle((0, 0), (4, 2)), 'xor', 1e-6, 0) assert r is None
def boolean(self,input_shape,bool_shape, input_layer=0, output_layer=0,mode='or'): if(mode != 'or' or mode != 'not' or mode != 'and'): raise Exception('mode should be either 'not', 'or', or 'and'') for i in range(0,len(input_shape)): shape = gdspy.Polygon(input_shape[i],input_layer) output_bool = gdspy.fast_boolean(bool_shape,shape, mode, precision=1e-9, max_points=1000, layer=output_layer) return shape, output_bool
def boolean(self,input_shape,bool_shape, input_layer=0, output_layer=0,mode='or'): # if(mode != str('or') or mode != str('not') or mode != str('and')): # raise Exception('mode should be either \'not\', \'or\', or \'and\'') for i in range(0,len(input_shape)): shape = gdspy.Polygon(input_shape[i],input_layer) bool_shape = gdspy.fast_boolean(bool_shape,shape,mode, precision=1e-9, max_points=1000, layer=output_layer) return bool_shape
def invert_layer(cell, lyr, keep=False): mask = [] polygons = cell.get_polygons(by_spec=True)[lyr] box_coord = cell.get_bounding_box() bbox = gdspy.Rectangle(box_coord[0], box_coord[1]) for p in polygons: mask.append(gdspy.Polygon(p)) cell.remove_polygons(lambda pts, layer, datatype: layer == lyr[0] ) if keep is False else None result = gdspy.fast_boolean(bbox, mask, 'not', layer=lyr[0]) return result
def hollow_box(size, border_width, layer=0): outer = gd.Rectangle((0, 0), size) inner = gd.Rectangle((border_width, border_width), (size[0] - border_width, size[1] - border_width)) hbox = gd.fast_boolean(outer, inner, 'not', layer=layer) ends = { 'A': (0, size[1] / 2), 'B': (size[0] / 2, 0), 'C': (size[0] / 2, size[1]), 'D': (size[0], size[1] / 2), 'CENTER': (size[0] / 2, size[1] / 2) } epsz = {'A': size[1], 'CENTER': None} return gtools.classes.GDStructure(hbox, ends, epsz)
def lattice_cutter(lattice, objectlist, mode = 'and', layer = 0): #===================================== # Cut a lattice up using fast_boolean \\ #========================================================================= # Arguments: lattice : output of lattice() function || # objectlist : list of objects that intersect lattice || # (optional) mode : what boolean operation to apply || # (optional) layer : layer to put resulting structure on || #========================================================================= if type(objectlist) is not type([]): objectlist = [objectlist] for i in objectlist: if i.compound: lattice = lattice_cutter(lattice, i.compound) lattice.structure = gd.fast_boolean(lattice.structure, i.structure, mode, layer = layer) return lattice
def __xor__(self, other): pts1, pts2 = [], [] for e in self.elements: s1 = e.shape.transform_copy(e.transformation) pts1.append(s1.points) for e in other.elements: s1 = e.shape.transform_copy(e.transformation) pts2.append(s1.points) if (len(pts1) > 0) and (len(pts2) > 0): p1 = gdspy.PolygonSet(polygons=pts1) p2 = gdspy.PolygonSet(polygons=pts2) ply = gdspy.fast_boolean(p1, p2, operation='not') elems = ElementList() for points in ply.polygons: elems += Polygon(shape=points, layer=self.layer) self.elements = elems return self
def shapely_to_gdspy( geom_shapely: Union[Polygon, MultiPolygon]) -> gdspy.Polygon: if isinstance(geom_shapely, Polygon): return shapely_to_gdspy_polygon(geom_shapely) elif isinstance(geom_shapely, MultiPolygon): polygon_gdspy = shapely_to_gdspy_polygon(geom_shapely[0]) for polygon_shapely in geom_shapely[1:]: polygon_gdspy_append = shapely_to_gdspy_polygon(polygon_shapely) polygon_gdspy = dataprep_cleanup_gdspy( gdspy.fast_boolean(polygon_gdspy, polygon_gdspy_append, 'or', max_points=MAX_POINTS, precision=GLOBAL_OPERATION_PRECISION), do_cleanup=GLOBAL_DO_CLEANUP) return polygon_gdspy else: raise TypeError( "input must be a Shapely Polygon or a Shapely MultiPolygon")
def compute_intersection(polygon_1, polygon_2): ''' Wrapper function around the gdspy module to take as input two polygons and return polygon_1-polygon_2 Explicit NOT operation is only performed if the bounding boxes of the two polygons do not intersect. ''' if check_bounding_box(polygon_1, polygon_2): gds_poly1 = gdspy.Polygon(polygon_1, 0) gds_poly2 = gdspy.Polygon(polygon_2, 0) gds_poly = gdspy.fast_boolean(gds_poly1, gds_poly2, 'not', layer=1) if gds_poly is None: return [] else: return gds_poly.polygons else: return [polygon_1]
def shapely_to_gdspy_polygon(polygon_shapely: Polygon) -> gdspy.Polygon: if not isinstance(polygon_shapely, Polygon): raise ValueError("input must be a Shapely Polygon") else: ext_coord_list = list(zip(*polygon_shapely.exterior.coords.xy)) polygon_gdspy = gdspy.Polygon(ext_coord_list) if len(polygon_shapely.interiors): for interior in polygon_shapely.interiors: int_coord_list = list(zip(*interior.coords.xy)) polygon_gdspy_int = gdspy.Polygon(int_coord_list) polygon_gdspy = dataprep_cleanup_gdspy( gdspy.fast_boolean(polygon_gdspy, polygon_gdspy_int, 'not', max_points=MAX_POINTS, precision=GLOBAL_OPERATION_PRECISION), do_cleanup=GLOBAL_DO_CLEANUP) else: pass return polygon_gdspy
def boxOutline(): # initialize cell outlineCell = gdspy.Cell('outline') # define an outer box outerBox = gdspy.Rectangle([-outerBoxWidth / 2, -outerBoxWidth / 2], [outerBoxWidth / 2, outerBoxWidth / 2], layer=layerNumber) # define an inner box innerBox = gdspy.Rectangle([-innerBoxWidth / 2, -innerBoxWidth / 2], [innerBoxWidth / 2, innerBoxWidth / 2], layer=layerNumber) # now subtract the two outline = gdspy.fast_boolean(outerBox, innerBox, 'xor', layer=layerNumber) # update the cell outlineCell.add(outline) # return the cell return outlineCell
def flatten(objectlist, endpoints, endpoint_dims, layer = 0): #=========================== # FLatten a list of objects \\ #========================================================================= # Flattening will cause all objects in the objectlist to be placed in || # one single layer and remove boundaries between them if there are any. || # All layer information will become lost! If you just want to combine || # structures while keeping layer information, use cluster() || # || # Arguments: objectlist : list of objects (GDStructure) || # endpoints : dictionary of new endpoints || # endpoint_dims : dictionary of new endpoint sizes || #========================================================================= # Define function to allow for recursive walk through list and pick out all # compound structures def stacker(inlist): outlist = [] for i in inlist: if i.compound: outlist += [i] + stacker(i.compound) else: outlist += [i] return outlist objectlist = stacker(objectlist) ends = copy.deepcopy(endpoints) epsz = copy.deepcopy(endpoint_dims) objs = [] for i in objectlist: objs.append(i.structure) return gtools.classes.GDStructure(gd.fast_boolean(objs, None, 'or', layer = layer), ends, epsz)
number_of_paths=3, distance=0.7, layer=6) path_cell.add(l1path) ## ------------------------------------------------------------------ ## ## POLYGON OPERATIONS ## ------------------------------------------------------------------ ## ## Boolean operations can be executed with either gdspy polygons or ## point lists). The operations are union, intersection, subtraction, ## symmetric subtracion (respectively 'or', 'and', 'not', 'xor'). oper_cell = gdspy.Cell('OPERATIONS') ## Here we subtract the previously created spiral from a rectangle with ## the 'not' operation. oper_cell.add(gdspy.fast_boolean(gdspy.Rectangle((10,-4), (17,4)), path3, 'not', layer=1)) ## 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) oper_cell.add(path4) ## Merge all parts into a single polygon. merged = gdspy.fast_boolean(path4, None, 'or', max_points=0) ## Offset the path shape by 0.5 and add it to the cell. oper_cell.add(gdspy.offset(merged, 1, layer=8))