def test_2_Trans(self): a = pya.Trans() b = pya.Trans(pya.Trans.M135, pya.Point(17, 5)) ma = pya.CplxTrans(a, 0.5) mb = pya.CplxTrans(b, 2.0) u = pya.CplxTrans(a) self.assertEqual(str(ma), "r0 *0.5 0,0") self.assertEqual(str(mb), "m135 *2 17,5") self.assertEqual(ma == mb, False) self.assertEqual(ma == ma, True) self.assertEqual(ma != mb, True) self.assertEqual(ma != ma, False) self.assertEqual(str(mb.inverted()), "m135 *0.5 2.5,8.5") i = mb.dup() i.invert() self.assertEqual(str(i), "m135 *0.5 2.5,8.5") self.assertEqual(i * mb == u, True) self.assertEqual(mb * i == u, True) self.assertEqual(str(mb.trans(pya.Point(1, 0))), "17,3") self.assertEqual(str(mb.ctrans(2)), "4.0") self.assertEqual(str(i.ctrans(2)), "1.0")
def make_pin(cell, name, center, w, pin_length, layer, vertical=0): """Handy method to create a pin on a device to avoid repetitive code. Args: cell (pya cell): cell to create the pin on name (string): name of the pin center (list): center of the pin in dbu. Format: [x, y] w (int): width of the pin in dbu pin_length (int): length of the pin in dbu, change the sign to determine pin direction. Default is left-to-right. layer (pya layer): layer to create the pin on vertical (int, optional): flag to determine if pin is vertical or horizontal. Defaults to 0. """ if vertical == 0: p1 = pya.Point(center[0] + pin_length / 2, center[1]) p2 = pya.Point(center[0] - pin_length / 2, center[1]) elif vertical == 1: p1 = pya.Point(center[0], center[1] + pin_length / 2) p2 = pya.Point(center[0], center[1] - pin_length / 2) pin = pya.Path([p1, p2], w) t = Trans(Trans.R0, center[0], center[1]) text = Text(name, t) shape = cell.shapes(layer).insert(text) shape.text_size = 0.1 cell.shapes(layer).insert(pin) return shape
def transformation_from_shape_impl(self): w = self.shape.bbox().width() h = self.shape.bbox().height() if w > h: return pya.Trans(3, False, pya.Point(self.shape.bbox().center().x+0.1*w, self.shape.bbox().center().y)) else: return pya.Trans(pya.Point(self.shape.bbox().center().x, self.shape.bbox().center().y+0.1*h))
def circle(self, diameter, vertices=128): ''' circle(diameter, vertices) Generates a circle shape Parameters --------- diameter : integer The diameter of a circle vertices : integer (128) Number of vertices in the circle (coerce to multiple of 4) Returns ------ region : [pya.Region] A region containing the circle shape Description ------ The number of vertices is coerced to even numbers to ensure good fracturing ''' r = int(diameter / 2) vertices = int(vertices / 4) * 4 # Create a circle polygon = pya.Polygon([ pya.Point(-r, -r), pya.Point(-r, r), pya.Point(r, r), pya.Point(r, -r) ]) polygon = polygon.round_corners(0, r, vertices) return pya.Region(polygon)
def layout_taper(cell, layer, trans, w1, w2, length, insert=True): """ Lays out a taper Args: trans: pya.Trans: location and rotation w1: width of waveguide, float for DPoint type (microns); int for Point type (nm) w2: width of waveguide, float for DPoint type (microns); int for Point type (nm) length: length, float insert: flag to insert drawn waveguide or return shape, boolean """ import pya if type(w1) == type(float()): pts = [ pya.DPoint(0, -w1 / 2), pya.DPoint(0, w1 / 2), pya.DPoint(length, w2 / 2), pya.DPoint(length, -w2 / 2) ] shape_taper = pya.DPolygon(pts).transformed(trans) else: pts = [ pya.Point(0, -w1 / 2), pya.Point(0, w1 / 2), pya.Point(length, w2 / 2), pya.Point(length, -w2 / 2) ] shape_taper = pya.Polygon(pts).transformed(trans) if insert == True: cell.shapes(layer).insert(shape_taper) else: return shape_taper
def produce_impl(self): # fetch the parameters dbu = self.layout.dbu ly = self.layout shapes = self.cell.shapes LayerSi = self.layer LayerSiN = ly.layer(LayerSi) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) # Determine the period such that the waveguide length is as desired. Slight adjustment to period N_boxes = int(round(self.length / self.target_period - 0.5)) grating_period = self.length / (N_boxes) / dbu # Draw the Bragg grating: box_width = int(round(grating_period * self.duty)) w = self.swg_wg_width / dbu half_w = w / 2 for i in range(0, N_boxes + 1): x = int(round((i * grating_period - box_width / 2))) box1 = Box(x, -half_w, x + box_width, half_w) shapes(LayerSiN).insert(box1) length = self.length / dbu # Strip waveguide: w_strip = self.strip_wg_width / dbu if w_strip > 0: box1 = Box(0, -w_strip / 2, length, w_strip / 2) shapes(LayerSiN).insert(box1) # Create the device recognition layer points = [pya.Point(0, 0), pya.Point(length, 0)] path = pya.Path(points, w) path = pya.Path(points, w * 3) shapes(LayerDevRecN).insert(path.simple_polygon()) # Pins on the waveguide: from SiEPIC._globals import PIN_LENGTH as pin_length make_pin(self.cell, "opt1", [0, 0], w, pin_length, LayerPinRecN) make_pin(self.cell, "opt2", [length, 0], w, -pin_length, LayerPinRecN) # Compact model information t = Trans(Trans.R0, 0, -w * 1.5 + 0.1 / dbu) text = Text('Lumerical_INTERCONNECT_library=Design kits/ebeam', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, 0, -w * 1.5 + 0.2 / dbu) text = Text('Component=NO_MODEL_AVAILABLE', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, 0, -w * 1.5) text = Text \ ('Spice_param:length=%.3fu target_period=%.3fu grating_period=%.3fu swg_wg_width=%.3fu strip_wg_width=%.3fu duty=%.3f ' %\ (self.length, self.target_period, round(grating_period)*dbu, self.swg_wg_width, self.strip_wg_width, self.duty), t ) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu
def vernier(self, width, length, pitch): ''' vernier(width, length, pitch) Generates a vernier scale Parameters --------- width : integer The width of each tick marker length : integer The length of the central tick mark The major tick marks are 3/4 this length The minor tick marks are half this length pitch : integer The distance between each tick mark Returns ------ region : [pya.Region] A region containing the vernier scale Description --------- A pair of vernier scale can be used to measure misalignment by eye. In photolithography the wafer will contain one vernier pattern (width = 4, length = 40, pitch = 8, units = micron) and the mask will contain a second vernier pattern (pitch = 8.2) providing an alignment measurement resolution of 0.2 micron. ''' scaleM = 0.75 scaleS = 0.5 # Create the large tick mark polygons = [] #tick = pya.Polygon([pya.Point(0,0), pya.Point(length,0), pya.Point(length,width), pya.Point(0,width)]) tick = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length,width), pya.Point(length,0)]) tc = pya.ICplxTrans(int(-length/2),int(-width/2)) polygons.append(tc.trans(tick)) # Create the medium tick mark #tickm = pya.Polygon([pya.Point(0,0), pya.Point(length*scaleM,0), pya.Point(length*scaleM,width), pya.Point(0,width)]) tickm = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length*scaleM,width), pya.Point(length*scaleM,0)]) pos = [-2, -1, 1, 2] for i in pos: tt = pya.ICplxTrans(0,int(i*pitch*5)) polygons.append(tc.trans(tt.trans(tickm))) # Create the small tick mark #ticks = pya.Polygon([pya.Point(0,0), pya.Point(length*scaleS,0), pya.Point(length*scaleS,width), pya.Point(0,width)]) ticks = pya.Polygon([pya.Point(0,0), pya.Point(0,width), pya.Point(length*scaleS,width), pya.Point(length*scaleS,0)]) pos = [-9, -8, -7, -6, -4, -3, -2, -1, 1, 2, 3, 4, 6, 7, 8, 9] for i in pos: tt = pya.ICplxTrans(0,int(i*pitch)) polygons.append(tc.trans(tt.trans(ticks))) return pya.Region(polygons)
def siWafer(self, diameter, primaryFlat, secondaryFlat, angle, vertices = 128): ''' siWafer(diameter, secondaryFlatAngle) Generates a Silicon Wafer shape Parameters --------- diameter : integer The diameter of a standard silicon wafer primaryFlat : integer The length of the primary flat secondaryFlat : integer The length of the secondary flat angle : double The location of the secondary flat relative (counterclockwise) to primary flat vertices : integer (coerce to even number) The number of vertices used to generate the circle Returns ------ region : [pya.Region] A region containing the Si Wafer shape Description --------- SEMI Wafer Flat M1-0302 Specification Wafer Size = [2", 3", 100mm, 125mm, 150mm, 200mm, 300mm] Diameter [mm] = [50.8, 76.2, 100, 125, 150, 200, 300] Thickness [um] = [279, 381, 525 or 625, 625, 675 or 625, 725, 775] Primary Flat Length = [15.88, 22.22, 32.5, 42.5, 57.5, Notch, Notch] Secondary Flat Length = [8, 11.18, 18, 27.5, 37.5, NA, NA] ''' dList = [50800, 76200, 10000, 12500, 15000] pFlatLengthList = [15.88, 22.22, 32.5, 42.5, 57.5] sFlatLengthList = [8, 11.18, 18, 27.5, 37.5] r = int(diameter/2) #Height of arc position (https://mathworld.wolfram.com/CircularSegment.html) pH = r- int(np.sqrt(4*np.power(r,2)-np.power(primaryFlat,2))/2) sH = r - int(np.sqrt(4*np.power(r,2)-np.power(secondaryFlat,2))/2) # Create a circle polygon = pya.Polygon([pya.Point(-r,-r), pya.Point(-r,r), pya.Point(r,r), pya.Point(r,-r)]) polygon = polygon.round_corners(0,r,vertices) #Create a rectangle to produce the primary flat pRectangle = pya.Polygon([pya.Point(-r,r-pH), pya.Point(-r,r+pH), pya.Point(r,r+pH), pya.Point(r,r-pH)]) #Create a rectangle to produce the secondary flat sRectangle = pya.Polygon([pya.Point(-r,r-sH), pya.Point(-r,r+sH), pya.Point(r,r+sH), pya.Point(r,r-sH)]) tt = pya.ICplxTrans(1, angle, False, 0, 0) return pya.Region(polygon)-pya.Region(pRectangle)-pya.Region(sRectangle.transform(tt))
def draw_init(self): p1 = pya.Point(self.cd / 2, self.cd / 2 + self.length) p2 = pya.Point(self.cd / 2, self.cd / 2) p3 = pya.Point(self.cd / 2 + self.length, self.cd / 2) p4 = pya.Point(self.cd / 2 + self.length, self.cd / 2 + self.length) self.cell.shapes(self.layer).insert( pya.Path([p1, p2, p3, p4], self.cd, self.cd / 2, self.cd / 2).polygon()) # self.cell.shapes(self.layout.layer(10,0)).insert(pya.Box(self.cd,0,self.cd*2,self.cd)) self.cell.shapes(self.layout.layer(self.via_layer, 0)).insert( pya.Box(0, 0, self.cd * 2, self.cd))
def drawViaArray(layout, cell, origin, cdX, cdY, pX, pY, Nx, Ny, layerSpec): layer = layout.layer(layerSpec[0], layerSpec[1], layerSpec[2]) for i in xrange(Nx): for j in xrange(Ny): l = origin[0] + pX * i b = origin[1] + pY * j r = l + cdX t = b + cdY ll = pya.Point(int(l), int(b)) tr = pya.Point(int(r), int(t)) cell.shapes(layer).insert(pya.Box(ll, tr))
def layout_taper(cell, layer, trans, w1, w2, length): """ Lays out a taper Args: trans: pya.Trans: location and rotation w1: width of waveguide, float w2: width of waveguide, float length: length, float """ import pya pts = [pya.Point(0,-w1/2), pya.Point(0,w1/2), pya.Point(length,w2/2), pya.Point(length,-w2/2)] cell.shapes(layer).insert(pya.Polygon(pts).transformed(trans))
def ring(self, outerDiameter, innerDiameter, vertices=128, fracture=True): ''' circle(outerDiameter, innerDiameter, vertices) Generates a circle shape Parameters --------- outerDiameter : integer The outer diameter of the ring innerDiameter : integer The inner diameter of the ring vertices : integer (128) Number of vertices in the circle (coerce to multiples of 4) fracture : boolean (True) Create the inner polygon with vertices that is optimal for fracturing horizontally Returns ------ region : [pya.Region] A region containing the circle shape ''' ro = int(outerDiameter / 2) ri = int(innerDiameter / 2) vertices = int(vertices / 4) * 4 # Create a circle polygon = pya.Polygon([ pya.Point(-ro, -ro), pya.Point(-ro, ro), pya.Point(ro, ro), pya.Point(ro, -ro) ]) polygonOuter = polygon.round_corners(0, ro, vertices) polygon = pya.Polygon([ pya.Point(-ri, -ri), pya.Point(-ri, ri), pya.Point(ri, ri), pya.Point(ri, -ri) ]) if fracture: points = polygonOuter.each_point_hull() polygonInnerPoints = [] r2 = np.power(ri, 2) for point in points: if (ri > np.absolute(point.y)): x = np.sqrt(r2 - np.power(point.y, 2)) polygonInnerPoints.append( pya.Point(np.sign(point.x) * x, point.y)) polygonInner = pya.Polygon(polygonInnerPoints) else: polygonInner = polygon.round_corners(0, ri, vertices) return pya.Region(polygonOuter) - pya.Region(polygonInner)
def produce_impl(self): # fetch the parameters dbu = self.layout.dbu tg = math.tan(math.radians(self.a / 2)) # compute the bowtie pts = [] pts.append( pya.Point(pya.DPoint((-self.lb / 2) / dbu, (self.wb / 2) / dbu))) pts.append( pya.Point( pya.DPoint((-self.lb / 2 - self.lw) / dbu, (self.wb / 2 + self.lw * tg) / dbu))) pts.append( pya.Point( pya.DPoint((-self.lb / 2 - self.lw) / dbu, (-self.wb / 2 - self.lw * tg) / dbu))) pts.append( pya.Point(pya.DPoint((-self.lb / 2) / dbu, -(self.wb / 2) / dbu))) self.cell.shapes(self.l_layer).insert(pya.Polygon(pts)) pts = [] pts.append( pya.Point(pya.DPoint((self.lb / 2) / dbu, -(self.wb / 2) / dbu))) pts.append( pya.Point( pya.DPoint((self.lb / 2 + self.lw) / dbu, (-self.wb / 2 - self.lw * tg) / dbu))) pts.append( pya.Point( pya.DPoint((self.lb / 2 + self.lw) / dbu, (self.wb / 2 + self.lw * tg) / dbu))) pts.append( pya.Point(pya.DPoint((self.lb / 2) / dbu, (self.wb / 2) / dbu))) self.cell.shapes(self.l_layer).insert(pya.Polygon(pts))
def make_pin(cell, name, center, w, pin_length, layer, vertical=0): if vertical == 0: p1 = pya.Point(center[0] + pin_length / 2, center[1]) p2 = pya.Point(center[0] - pin_length / 2, center[1]) elif vertical == 1: p1 = pya.Point(center[0], center[1] + pin_length / 2) p2 = pya.Point(center[0], center[1] - pin_length / 2) pin = pya.Path([p1, p2], w) t = Trans(Trans.R0, center[0], center[1]) text = Text(name, t) shape = cell.shapes(layer).insert(text) shape.text_size = 0.1 cell.shapes(layer).insert(pin)
def draw_wire_column(cell, layer, cd, min_length, max_length, min_t2t, max_t2t, t2t_grid, max_y, location=[0, 0]): offset_x = location[0] offset_y = location[1] total_x = 0 total_y = 0 while total_y < max_y: wire_left = total_x wire_lower = total_y # print min_length, max_length, max_y try: tmp = min(max_length, max_y - wire_lower) wire_length = rd.randint(min_length, tmp) # print "wire length", wire_length except: # print "escape wire" break wire_upper = wire_lower + wire_length wire_right = wire_left + cd # print(wire_left+offset_x,wire_lower+offset_y,wire_lower,wire_right) wire_ll = pya.Point(wire_left + offset_x, wire_lower + offset_y) wire_ur = pya.Point(wire_right + offset_x, wire_upper + offset_y) wire = pya.Box(wire_ll, wire_ur) cell.shapes(layer).insert(wire) # print wire_ll, wire_ur # quit() try: if max_t2t > min_t2t: tmp = min(max_t2t, max_y - wire_upper) # print min_t2t, tmp, t2t_grid t2t = rd.randrange(min_t2t, tmp, t2t_grid) # print "t2t", t2t else: t2t = max_t2t except: # print "escape t2t" break # print total_x, max_x, max_length, wire_left total_y = total_y + wire_length + t2t
def do_non_opc_fill(fill_top_cell, layer, layer_cfg, outline_area, shapes_area): non_opc_cfg = layer_cfg['non-opc'] sp_non = non_opc_cfg['space_to_non_fill'] sp_fill = non_opc_cfg['space_to_fill'] # Constant across iterations fill_base_area = outline_area - shapes_area.sized(sp_non - half(sp_fill)) for w, h in zip(non_opc_cfg['width'], non_opc_cfg['height']): # Orient the fill in the preferred direction if layer_cfg['dir'] == 'H': w, h = (max(w, h), min(w, h)) else: w, h = (min(w, h), max(w, h)) # The set of non-OPC fill shapes may expand on each iteration so we # recompute it non_opc_fill = pya.Region( fill_top_cell.begin_shapes_rec(non_opc_cfg['klayout'])) fill_area = fill_base_area - non_opc_fill.sized(half(sp_fill)) small_cell, fill_cell_bbox = create_fill_cell(layer + '_non_opc', non_opc_cfg, w, h) while not fill_area.is_empty(): fill_top_cell.fill_region(fill_area, small_cell.cell_index(), fill_cell_bbox, None, fill_area, pya.Point(0, 0), None) if 'datatype2' in non_opc_cfg: e2_cell, _ = create_fill_cell(layer + '_non_opc_e2', non_opc_cfg, w, h, True) double_pattern(fill_top_cell, small_cell, e2_cell)
def test_13(self): n = 100 w = 10000 ly = pya.Layout() l1 = ly.layer(1, 0) top = ly.create_cell("TOP") ix = 0 while ix < n: sys.stdout.write(str(ix) + "/" + str(n) + "\n") sys.stdout.flush() iy = 0 while iy < n: x = ix * w y = iy * w cell = ly.create_cell("X" + str(ix) + "Y" + str(iy)) cell.shapes(l1).insert(pya.Box(0, 0, w, w)) top.insert( pya.CellInstArray(cell.cell_index(), pya.Trans(pya.Point(ix * w, iy * w)))) iy += 1 ix += 1 ly._destroy()
def get_polygons(self, include_pins=True): from .utils import get_layout_variables TECHNOLOGY, lv, ly, cell = get_layout_variables() r = pya.Region() s = self.cell.begin_shapes_rec(ly.layer(TECHNOLOGY['Waveguide'])) while not (s.at_end()): if s.shape().is_polygon() or s.shape().is_box() or s.shape( ).is_path(): r.insert(s.shape().polygon.transformed(s.itrans())) s.next() if include_pins: s = self.cell.begin_shapes_rec(ly.layer(TECHNOLOGY['PinRec'])) import math from .utils import angle_vector while not (s.at_end()): if s.shape().is_path(): p = s.shape().path.transformed(s.itrans()) # extend the pin path by 1 micron for FDTD simulations pts = [pt for pt in p.each_point()] # direction / angle of the optical pin rotation = angle_vector(pts[0] - pts[1]) * math.pi / 180 pts[1] = ( pts[1] - pya.Point(int(math.cos(rotation) * 1000), int(math.sin(rotation) * 1000))).to_p() r.insert(pya.Path(pts, p.width).polygon()) s.next() r.merge() polygons = [p for p in r.each_merged()] return polygons
def checkerboard(self, width, num=5): ''' checkerboard(width, num) Generates a checkerboard pattern Parameters --------- width : integer The width of each square num : integer (5) The number of squares Returns ------ region : pya.Region A region containing the checkerboard Description --------- A checkerboard pattern is used to qualitatively evaluate the resolution of the print. The corners of the checkboard pattern will degrade as resolution gets worse ''' # Create a box square = pya.Polygon([ pya.Point(0, 0), pya.Point(0, width), pya.Point(width, width), pya.Point(width, 0) ]) polygons = [] tc = pya.ICplxTrans(-int(num * width / 2), -int(num * width / 2)) if (num % 2 == 1): for i in range(num * num): if (i % 2 == 0): tt = pya.ICplxTrans(int(i % num * width), int(i / num * width)) polygons.append(tc.trans(tt.trans(square))) else: for i in range(num): for j in range(num): if ((j + i) % 2 == 0): tt = pya.ICplxTrans(int(i * width), int(j * width)) polygons.append(tc.trans(tt.trans(square))) return pya.Region(polygons)
def test_3_Trans(self): c = pya.CplxTrans(5, -7) self.assertEqual(str(c), "r0 *1 5,-7") self.assertEqual(str(pya.CplxTrans.from_s(str(c))), str(c)) c = pya.CplxTrans(pya.CplxTrans.M135) self.assertEqual(str(c), "m135 *1 0,0") self.assertEqual(c.is_unity(), False) self.assertEqual(c.is_ortho(), True) self.assertEqual(c.is_mag(), False) self.assertEqual(c.is_mirror(), True) self.assertEqual(c.rot(), pya.CplxTrans.M135.rot()) self.assertEqual(str(c.s_trans()), "m135 0,0") self.assertAlmostEqual(c.angle, 270) c = pya.CplxTrans.from_dtrans(pya.DCplxTrans.M135) self.assertEqual(str(c), "m135 *1 0,0") c = pya.CplxTrans(1.5) self.assertEqual(str(c), "r0 *1.5 0,0") self.assertEqual(c.is_unity(), False) self.assertEqual(c.is_ortho(), True) self.assertEqual(c.is_mag(), True) self.assertEqual(c.is_mirror(), False) self.assertEqual(c.rot(), pya.CplxTrans.R0.rot()) self.assertEqual(str(c.s_trans()), "r0 0,0") self.assertAlmostEqual(c.angle, 0) c = pya.CplxTrans(0.75, 45, True, 2.5, -12.5) self.assertEqual(str(c), "m22.5 *0.75 2.5,-12.5") self.assertEqual(str(pya.CplxTrans.from_s(str(c))), str(c)) c = pya.CplxTrans(0.75, 45, True, pya.DPoint(2.5, -12.5)) self.assertEqual(str(c), "m22.5 *0.75 2.5,-12.5") self.assertEqual(c.is_unity(), False) self.assertEqual(c.is_ortho(), False) self.assertEqual(c.is_mag(), True) self.assertEqual(c.rot(), pya.CplxTrans.M0.rot()) self.assertEqual(str(c.s_trans()), "m0 3,-13") self.assertAlmostEqual(c.angle, 45) self.assertEqual(str(c.ctrans(5)), "3.75") self.assertEqual(str(c.trans(pya.Point(12, 16))), "17.3492424049,-14.6213203436") self.assertEqual(str(pya.CplxTrans()), "r0 *1 0,0") self.assertEqual(pya.CplxTrans().is_unity(), True) self.assertEqual((c.inverted() * c).is_unity(), True) c.mirror = False self.assertEqual(str(c), "r45 *0.75 2.5,-12.5") c.mag = 1.5 self.assertEqual(str(c), "r45 *1.5 2.5,-12.5") c.disp = pya.DPoint(-1.0, 5.5) self.assertEqual(str(c), "r45 *1.5 -1,5.5") self.assertEqual(c.mag, 1.5) c.angle = 60 self.assertEqual(str(c), "r60 *1.5 -1,5.5") self.assertEqual(("%g" % c.angle), "60")
def pointlist_to_path(pointlist, dbu): # convert [[230.175,169.18],[267.0,169.18],[267.0,252.0],[133.0,252.0],[133.0,221.82],[140.175,221.82]] # to pya.Path points = [] for p in points: points.append(pya.Point(p[0], p[1])) path = pya.Path(points) return path
def main(): # Loop each cut map, & export each GDS as unique name for key in cut_d: filename = "2_" + key # Add prefix to the filename print("\nfilename : ", filename) # Get 1_UNIT (no cuts as a reference) & create SINGLE instance for i in gdsFiles: layout.read(i) for cell in layout.top_cells(): # we don't want to insert the topcell itself if (cell.name != "1_UNIT_CUTS"): print("Adding : " + cell.name) cell_index = cell.cell_index() new_instance = pya.CellInstArray( cell_index, pya.Trans(pya.Point(0, 0))) UNIT.insert(new_instance) # Define imported metal as a region (for boolean) region_metal = pya.Region(layout.top_cell().begin_shapes_rec(l_metal)) # Define cut area & make as region for ind, each_cut in enumerate(cut_d[key]): cut_box_coord = get_cut_coord(each_cut, d) UNIT.shapes(l_cut_box).insert(cut_box_coord) region_cut_box = pya.Region(cut_box_coord) # Do boolean (XOR) # For more than 1 cuts, need to loop and take XOR of previous XOR results if ind == 0: region_xor = region_metal ^ region_cut_box else: region_xor = region_xor ^ region_cut_box # Remove existings metal layer + cut boxes # (!!! SKIP THIS TO CHECK CUT BOXES IN GDS !!!) layout.clear_layer(l_metal) layout.clear_layer(l_cut_box) # INSERT BOOLEAN RESULT AS ORIGINAL METAL LAYER UNIT.shapes(l_metal).insert(region_xor) # Check if filename gds exists -> If so skip "write" if os.path.isfile(root + "/" + filename + ".gds"): print("**** GDS name by : " + filename + ".gds already exists!") print("**** SKIPPING GDS WRITE!!!") else: # Export GDS layout.write(filename + ".gds") # Check if this cell can be used as another at different coordinate dup_l = get_dup_names(filename + ".gds") if dup_l[0] != '': for each in dup_l: print("Create copy as : ", each, " <-----------------------------------------") layout.write(each)
class Top_driver(My_Cell): """docstring for ClassName""" top_vdd = pya.Point(-500, 5350) def __init__(self, cell, out_pin, cell_index): self.out_pin = out_pin self.cell_index = cell_index self.cell = cell
def pieceHolderCassette(self,dbu = 1): ''' pieceHolderCassette() Generates the shape of the Jeol JBX-5500FS piece holder cassette Parameters --------- dbu : double The database unit Returns ------ region : [pya.Region] A region containing the piece holder cassette shape Description ------ The center of this piece holder shape (0,0) is at stage position (62.5mm, 37.5mm) Jeol Stage Y axis is reverse of KLayout Y axis ''' # Create the quarter circle r = 36100/dbu rx = 62500/dbu ry = -37500/dbu polygon = pya.Polygon([pya.Point(-r,-r), pya.Point(-r,r), pya.Point(r,r), pya.Point(r,-r)]) polygon = polygon.round_corners(0,r,128) rectangle = pya.Polygon([pya.Point(-r,0), pya.Point(-r,r), pya.Point(r,r), pya.Point(r,0)]) tt = pya.ICplxTrans(1, 90, False, 0, 0) qCircle = pya.Region(polygon)-pya.Region(rectangle)-pya.Region(rectangle.transform(tt)) # Create the trapezoid trapezoid = pya.Polygon([pya.Point(49500/dbu,-70500/dbu), pya.Point(40500/dbu,-20500/dbu), pya.Point(60500/dbu,-20500/dbu), pya.Point(51500/dbu,-70500/dbu)]) tt = pya.ICplxTrans(-rx,-ry) cassette = qCircle + pya.Region(trapezoid.transform(tt)) return cassette
def draw_contact_pair(layout, tmp_cell, cellname, size, spacing, dest, dbu, put_in_array=True, verbose=True, clip=True, origin=np.array([0, 0])): l_contact = layout.layer(73, 0, "V1") c1ll = pya.Point(origin[0], origin[1]) size = pya.Point(size[0], size[1]) c1ur = c1ll + size spc = pya.Point(spacing[0], spacing[1]) c2ll = c1ur + spc c2ur = c2ll + size tmp_cell.shapes(l_contact).insert(pya.Box(c1ll, c1ur)) tmp_cell.shapes(l_contact).insert(pya.Box(c2ll, c2ur)) if clip: l_bb = layout.layer(1, 0, "bounding_box") # print c1ur.x,c2ll.x center = pya.Point((c1ur.x + c2ll.x) / 2, (c1ur.y + c2ll.y) / 2) bbll = center - pya.Point(0.125 / dbu, 0.125 / dbu) bbur = center + pya.Point(0.125 / dbu, 0.125 / dbu) tmp_cell.shapes(l_bb).insert(pya.Box(bbll, bbur)) if verbose == True: tmp_name = cellname + '.oas' tmp_cell.write(os.path.join(dest, tmp_name))
def arc_bezier(radius, start, stop, bezier, DevRec=None): from math import sin, cos, pi from SiEPIC.utils import points_per_circle N = points_per_circle(radius/1000)/4 bezier=float(bezier) # in case the input was a string if DevRec: N = int(N / 3) else: N = int(N) if N < 5: N = 100 L = radius # effective bend radius / Length of the bend diff = 1. / (N - 1) # convert int to float xp = [0, (1 - bezier) * L, L, L] yp = [0, 0, bezier * L, L] xA = xp[3] - 3 * xp[2] + 3 * xp[1] - xp[0] xB = 3 * xp[2] - 6 * xp[1] + 3 * xp[0] xC = 3 * xp[1] - 3 * xp[0] xD = xp[0] yA = yp[3] - 3 * yp[2] + 3 * yp[1] - yp[0] yB = 3 * yp[2] - 6 * yp[1] + 3 * yp[0] yC = 3 * yp[1] - 3 * yp[0] yD = yp[0] pts = [pya.Point(-L, 0) + pya.Point(xD, yD)] for i in range(1, N - 1): t = i * diff pts.append(pya.Point(-L, 0) + pya.Point(t**3 * xA + t**2 * xB + t * xC + xD, t**3 * yA + t**2 * yB + t * yC + yD)) pts.extend([pya.Point(0, L - 1), pya.Point(0, L)]) return pts
def arc_bezier(radius, start, stop, bezier, DevRec=None): from math import sin, cos, pi N = 100 if DevRec: N = int(N / 10) L = radius # effective bend radius / Length of the bend diff = 1. / (N - 1) # convert int to float xp = [0, (1 - bezier) * L, L, L] yp = [0, 0, bezier * L, L] xA = xp[3] - 3 * xp[2] + 3 * xp[1] - xp[0] xB = 3 * xp[2] - 6 * xp[1] + 3 * xp[0] xC = 3 * xp[1] - 3 * xp[0] xD = xp[0] yA = yp[3] - 3 * yp[2] + 3 * yp[1] - yp[0] yB = 3 * yp[2] - 6 * yp[1] + 3 * yp[0] yC = 3 * yp[1] - 3 * yp[0] yD = yp[0] pts = [pya.Point(-L, 0) + pya.Point(xD, yD)] for i in range(1, N - 1): t = i * diff pts.append( pya.Point(-L, 0) + pya.Point(t**3 * xA + t**2 * xB + t * xC + xD, t**3 * yA + t**2 * yB + t * yC + yD)) pts.extend([pya.Point(0, L - 1), pya.Point(0, L)]) return pts
def main(): # Loop each cut locations, & assign GDS name from key for key in cut_loc: filename = key print("\nfilename : ", filename) # Read 1_UNIT (no cuts as a reference) & create SINGLE instance for each_gds in gds_files: KLAYOUT.read(each_gds) # Read Top Cell for each GDS file for top_cell_read in KLAYOUT.top_cells(): if (top_cell_read.name != "1_UNIT_CUTS" ): # Don't insert TOP_CELL("1_UNIT_CUTS") on itself # print ( "Adding " + top_cell_read.name ) cell_index = top_cell_read.cell_index() new_instance = pya.CellInstArray( cell_index, pya.Trans(pya.Point(0, 0))) # pya.Trans(pya.Point(0,0)) --> defines the LOCATION at which instance should be placed TOP_CELL.insert(new_instance) # Define imported metal as a region (for boolean) region_metal = pya.Region(KLAYOUT.top_cell().begin_shapes_rec(l_metal)) # Define cut area & make as region for ind, each_cut in enumerate(cut_loc[key]): cut_box_coord = get_cut_coord(each_cut, dim) TOP_CELL.shapes(l_cut_box).insert(cut_box_coord) region_cut_box = pya.Region(cut_box_coord) # Do boolean (XOR) # For more than 1 cuts, need to loop and take XOR of previous XOR results if ind == 0: region_xor = region_metal ^ region_cut_box else: region_xor = region_xor ^ region_cut_box # Remove existings metal layer + cut boxes # (!!! SKIP THIS TO CHECK CUT BOXES IN GDS !!!) KLAYOUT.clear_layer(l_metal) KLAYOUT.clear_layer(l_cut_box) # INSERT BOOLEAN RESULT AS ORIGINAL METAL LAYER TOP_CELL.shapes(l_metal).insert(region_xor) # Check if filename gds exists -> If so skip "write" if os.path.isfile(root + "/" + filename + ".gds"): print("**** GDS name by : " + filename + ".gds already exists!") print("**** SKIPPING GDS WRITE!!!") else: # Export GDS KLAYOUT.write(filename + ".gds")
def create(radius, npoints=32): """Create a circular-shaped region. radius: radius of the circular area npoints: the circular area is approximated by a polygon, hence the number of vertices """ angles = np.linspace(0, 2 * np.pi, num=npoints, endpoint=False) points = [] for i, ang in enumerate(angles): points.append(pya.Point(radius * np.cos(ang), radius * np.sin(ang))) circle = pya.Region() circle.insert(pya.SimplePolygon(points)) return circle
def do_opc_fill(fill_top_cell, layer, layer_cfg, outline_area, shapes_area): has_opc = 'opc' in layer_cfg if not has_opc: return opc_cfg = layer_cfg['opc'] sp_non = opc_cfg['space_to_non_fill'] sp_fill = opc_cfg['space_to_fill'] halo = opc_cfg['halo'] non_opc_cfg = layer_cfg['non-opc'] sp_fill_non_opc = non_opc_cfg['space_to_fill'] - half(sp_fill) non_opc_fill = pya.Region( fill_top_cell.begin_shapes_rec(non_opc_cfg['klayout'])) if 'klayout2' in non_opc_cfg: non_opc_fill |= pya.Region( fill_top_cell.begin_shapes_rec(non_opc_cfg['klayout2'])) fill_base_area = outline_area & ( shapes_area.sized(halo) - shapes_area.sized(sp_non - half(sp_fill)) - non_opc_fill.sized(sp_fill_non_opc)) is_h = layer_cfg['dir'] == 'H' for w, h in zip(opc_cfg['width'], opc_cfg['height']): w_space = h_space = half(sp_fill) le = opc_cfg.get('space_line_end', 0) if is_h: w, h = (max(w, h), min(w, h)) w_space += half(le) else: w, h = (min(w, h), max(w, h)) h_space += half(le) opc_fill = pya.Region( fill_top_cell.begin_shapes_rec(opc_cfg['klayout'])) if 'klayout2' in opc_cfg: opc_fill |= pya.Region( fill_top_cell.begin_shapes_rec(opc_cfg['klayout2'])) fill_area = fill_base_area - opc_fill.sized(w_space, h_space, 2) small_cell, fill_cell_bbox = create_fill_cell(layer + '_opc', opc_cfg, w, h) while not fill_area.is_empty(): fill_top_cell.fill_region(fill_area, small_cell.cell_index(), fill_cell_bbox, None, fill_area, pya.Point(0, 0), None) if 'datatype2' in opc_cfg: e2_cell, _ = create_fill_cell(layer + '_opc_e2', opc_cfg, w, h, True) double_pattern(fill_top_cell, small_cell, e2_cell)