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 test_1_Trans(self): a = pya.Trans() b = pya.Trans( pya.Trans.M135, pya.Point( 17, 5 )) c = pya.Trans( 3, True, pya.Point( 17, 5 )) d = pya.Trans( pya.Point( 17, 5 )) e = pya.Trans( pya.Trans.M135 ) e2 = pya.Trans.from_dtrans( pya.DTrans.M135 ) f = pya.Trans( pya.DTrans( pya.DTrans.M135, pya.DPoint( 17, 5 )) ) self.assertEqual( str(a), "r0 0,0" ) self.assertEqual( str(pya.Trans.from_s(str(a))), str(a) ) self.assertEqual( str(b), "m135 17,5" ) self.assertEqual( str(c), "m135 17,5" ) self.assertEqual( str(d), "r0 17,5" ) self.assertEqual( str(e), "m135 0,0" ) self.assertEqual( str(e2), "m135 0,0" ) self.assertEqual( str(f), "m135 17,5" ) self.assertEqual( str(pya.Trans.from_s(str(f))), str(f) ) self.assertEqual( str(b.trans( pya.Point( 1, 0 ))), "17,4" ) self.assertEqual( a == b, False ) self.assertEqual( a == a, True ) self.assertEqual( a != b, True ) self.assertEqual( a != a, False ) self.assertEqual( (d * e) == b, True ) self.assertEqual( (e * d) == b, False ) i = c.inverted() self.assertEqual( str(i), "m135 5,17" ) self.assertEqual( (i * b) == a, True ) self.assertEqual( (b * i) == a, True ) c = pya.Trans( 3, True, pya.Point( 17, 5 )) self.assertEqual( str(c), "m135 17,5" ) c.disp = pya.Point(1, 7) self.assertEqual( str(c), "m135 1,7" ) c.angle = 1 self.assertEqual( str(c), "m45 1,7" ) c.rot = 3 self.assertEqual( str(c), "r270 1,7" ) c.mirror = True self.assertEqual( str(c), "m135 1,7" ) self.assertEqual( str(e.trans( pya.Edge(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(( e * pya.Edge(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(e.trans( pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(( e * pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(e.trans( pya.Text("text", pya.Vector(0, 1)) )), "('text',m135 -1,0)" ) self.assertEqual( str(( e * pya.Text("text", pya.Vector(0, 1)) )), "('text',m135 -1,0)" ) self.assertEqual( str(e.trans( pya.Polygon( [ pya.Point(0, 1), pya.Point(2, -3), pya.Point(4, 5) ] ) )), "(-5,-4;-1,0;3,-2)" ) self.assertEqual( str(( e * pya.Polygon( [ pya.Point(0, 1), pya.Point(2, -3), pya.Point(4, 5) ] ) )), "(-5,-4;-1,0;3,-2)" ) self.assertEqual( str(e.trans( pya.Path( [ pya.Point(0, 1), pya.Point(2, 3) ], 10 ) )), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) self.assertEqual( str(( e * pya.Path( [ pya.Point(0, 1), pya.Point(2, 3) ], 10 ) )), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" )
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 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 next_level(self): self.level += 1 print("Drawing Hilbert Level %g" % self.level) newcell = self.layout.create_cell(self.cell_prefix + "level_" + str(self.level)) transll = pya.Trans(0, 0) translr = pya.Trans(self.box_size + self.length, 0) transul = pya.Trans(1, False, self.box_size + self.cd, self.box_size + self.length) transur = pya.Trans(3, False, self.box_size + self.length, self.box_size * 2 + self.length + self.cd) print(" Placing Cells...") ll_inst = pya.CellInstArray(self.cell.cell_index(), transll) lr_inst = pya.CellInstArray(self.cell.cell_index(), translr) ul_inst = pya.CellInstArray(self.cell.cell_index(), transul) ur_inst = pya.CellInstArray(self.cell.cell_index(), transur) newcell.insert(ll_inst) newcell.insert(lr_inst) newcell.insert(ur_inst) newcell.insert(ul_inst) print(" Connecting Quadrants...") p11 = pya.Point(self.box_size + self.cd / 2, self.box_size + self.cd / 2) p12 = pya.Point(self.box_size + self.cd / 2 + self.length, self.box_size + self.cd / 2) p21 = pya.Point(self.cd / 2, self.cd / 2 + self.box_size) p22 = pya.Point(self.cd / 2, self.cd / 2 + self.box_size + self.length) p31 = pya.Point(self.box_size * 2 + self.length + self.cd / 2, self.cd / 2 + self.box_size) p32 = pya.Point(self.box_size * 2 + self.length + self.cd / 2, self.cd / 2 + self.box_size + self.length) path1 = pya.Path([p11, p12], self.cd, self.cd / 2, self.cd / 2).polygon() path2 = pya.Path([p21, p22], self.cd, self.cd / 2, self.cd / 2).polygon() path3 = pya.Path([p31, p32], self.cd, self.cd / 2, self.cd / 2).polygon() newcell.shapes(self.layer).insert(path1) newcell.shapes(self.layer).insert(path2) newcell.shapes(self.layer).insert(path3) print(" Updating Box Size...") self.box_size = self.box_size * 2 + self.length self.cell = newcell print("Done!")
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 draw_init(self, type): p1 = pya.Point(self.cd / 2, self.cd / 2) p2 = pya.Point(self.cd / 2 + self.length * 2, self.cd / 2) p31 = pya.Point(self.cd / 2 + self.length, self.cd / 2 + self.length * 2) p32 = pya.Point(self.cd / 2 + self.length * 2, self.cd / 2 + self.length) p41 = pya.Point(self.cd / 2 + self.length, self.cd / 2) p42 = pya.Point(self.cd / 2, self.cd / 2 + self.length) p5 = pya.Point(self.cd / 2, self.cd / 2 + self.length * 2) p6 = pya.Point(self.cd / 2 + self.length * 2, self.cd / 2 + self.length * 2) if type == 1: self.cell.shapes(self.layer).insert( pya.Path([p1, p5, p31, p41, p2, p6], self.cd, self.cd / 2, self.cd / 2).polygon()) elif type == 2: self.cell.shapes(self.layer).insert( pya.Path([p1, p2, p32, p42, p5, p6], self.cd, self.cd / 2, self.cd / 2).polygon())
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 snap(self, pins): # Import functionality from SiEPIC-Tools: from .utils import angle_vector, get_technology from . import _globals TECHNOLOGY = get_technology() # Search for pins within this distance to the path endpoints, e.g., 10 microns d_min = _globals.PATH_SNAP_PIN_MAXDIST/TECHNOLOGY['dbu']; if not len(pins): return # array of path vertices: pts = self.get_points() # angles of all segments: ang = angle_vector(pts[0]-pts[1]) # sort all the pins based on distance to the Path endpoint # only consider pins that are facing each other, 180 degrees pins_sorted = sorted([pin for pin in pins if round((ang - pin.rotation)%360) == 180 and pin.type == _globals.PIN_TYPES.OPTICAL], key=lambda x: x.center.distance(pts[0])) if len(pins_sorted): # pins_sorted[0] is the closest one dpt = pins_sorted[0].center - pts[0] # check if the pin is close enough to the path endpoint if dpt.abs() <= d_min: # snap the endpoint to the pin pts[0] += dpt # move the first corner if(round(ang % 180) == 0): pts[1].y += dpt.y else: pts[1].x += dpt.x # do the same thing on the other end: ang = angle_vector(pts[-1]-pts[-2]) pins_sorted = sorted([pin for pin in pins if round((ang - pin.rotation)%360) == 180 and pin.type == _globals.PIN_TYPES.OPTICAL], key=lambda x: x.center.distance(pts[-1])) if len(pins_sorted): dpt = pins_sorted[0].center - pts[-1] if dpt.abs() <= d_min: pts[-1] += dpt if(round(ang % 180) == 0): pts[-2].y += dpt.y else: pts[-2].x += dpt.x # check that the path has non-zero length after the snapping operation test_path = pya.Path() test_path.points = pts if test_path.length() > 0: self.points = 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 create_path(self, points: list, width: float, layer: 'pya.LayerInfo'): """Creates a pya.Path object and inserts it into the Library-PCell. :param points: The points describing the path [[x1,y1],[x2,y2],...] in microns :param width: Path width :param layer: layer on which the path should be made """ pts = [] for p in points: pts.append( pya.Point( pya.DPoint(p[0] / self.layout.dbu, p[1] / self.layout.dbu))) w = int(width / self.layout.dbu) self.cell.shapes(self.layout.find_layer(layer)).insert(pya.Path( pts, w))
def translate_from_center(self, offset): from math import pi, cos, sin, acos, sqrt from .utils import angle_vector pts = [pt for pt in self.get_dpoints()] tpts = [pt for pt in self.get_dpoints()] for i in range(0, len(pts)): if i == 0: u = pts[i] - pts[i + 1] v = -u elif i == (len(pts) - 1): u = pts[i - 1] - pts[i] v = -u else: u = pts[i - 1] - pts[i] v = pts[i + 1] - pts[i] if offset < 0: o1 = pya.DPoint(abs(offset) * cos(angle_vector(u) * pi / 180 - pi / 2), abs(offset) * sin(angle_vector(u) * pi / 180 - pi / 2)) o2 = pya.DPoint(abs(offset) * cos(angle_vector(v) * pi / 180 + pi / 2), abs(offset) * sin(angle_vector(v) * pi / 180 + pi / 2)) else: o1 = pya.DPoint(abs(offset) * cos(angle_vector(u) * pi / 180 + pi / 2), abs(offset) * sin(angle_vector(u) * pi / 180 + pi / 2)) o2 = pya.DPoint(abs(offset) * cos(angle_vector(v) * pi / 180 - pi / 2), abs(offset) * sin(angle_vector(v) * pi / 180 - pi / 2)) p1 = u + o1 p2 = o1 p3 = v + o2 p4 = o2 d = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x) if round(d, 10) == 0: tpts[i] += p2 else: tpts[i] += pya.DPoint(((p1.x * p2.y - p1.y * p2.x) * (p3.x - p4.x) - (p1.x - p2.x) * (p3.x * p4.y - p3.y * p4.x)) / d, ((p1.x * p2.y - p1.y * p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x * p4.y - p3.y * p4.x)) / d) if self.__class__ == pya.Path: return pya.Path([pya.Point(pt.x, pt.y) for pt in tpts], self.width) elif self.__class__ == pya.DPath: return pya.DPath(tpts, self.width)
def produce_impl(self): # This is the main part of the implementation: create the layout from math import pi, cos, sin from SiEPIC.extend import to_itype # fetch the parameters # TECHNOLOGY = get_technology_by_name('GSiP') dbu = self.layout.dbu ly = self.layout shapes = self.cell.shapes LayerSi3N = ly.layer(self.si3layer) LayerSiN = ly.layer(self.silayer) LayernN = ly.layer(self.nlayer) LayerpN = ly.layer(self.player) LayernpN = ly.layer(self.nplayer) LayerppN = ly.layer(self.pplayer) LayernppN = ly.layer(self.npplayer) LayerpppN = ly.layer(self.ppplayer) LayervcN = ly.layer(self.vclayer) Layerm1N = ly.layer(self.m1layer) LayervlN = ly.layer(self.vllayer) LayermlN = ly.layer(self.mllayer) LayermhN = ly.layer(self.mhlayer) TextLayerN = ly.layer(self.textl) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) # Define variables for the Modulator # Variables for the Si waveguide w = to_itype(self.w,dbu) r = to_itype(self.r,dbu) g = to_itype(self.g,dbu) gmon = to_itype(self.gmon,dbu) #Variables for the N layer w_1 = 2.0/dbu #same for N, P, N+, P+ layer r_n = to_itype(self.r - 1.0,dbu) #Variables for the P layer r_p = to_itype(self.r + 1.0, dbu) #Variables for the N+layer r_np = to_itype(self.r - 1.5,dbu) #Variables for the P+layer r_pp = to_itype(self.r + 1.5,dbu) #Variables for the N++ layer w_2 = to_itype(5.5,dbu) #same for N++, P++ layer r_npp = to_itype(self.r - 3.75,dbu) #Variables for the P+layer r_ppp = to_itype(self.r + 3.75,dbu) #Variables for the VC layer w_vc = to_itype(4.0,dbu) r_vc1 = to_itype(self.r - 3.75,dbu) r_vc2 = to_itype(self.r + 3.75,dbu) #Variables for the M1 layer w_m1_in = r_vc1 + w_vc/2.0 + to_itype(0.5,dbu) r_m1_in = r_vc1 + w_vc/2.0 + to_itype(0.5,dbu) /2.0 w_m1_out = to_itype(6.0,dbu) r_m1_out = to_itype(self.r + 4.25,dbu) #Variables for the VL layer #r_vl = w_m1_in/2.0 - 2.1/dbu r_vl = r_vc1 - w_vc/2.0 - to_itype(2.01,dbu) if r_vl < to_itype(1.42,dbu): r_vl = to_itype(1.42,dbu) w_vc = r - to_itype(1.75,dbu) - (r_vl + 2.01) r_vc1 = r - to_itype(1.75,dbu) - w_vc/2.0 r_vc2 = r + to_itype(1.75,dbu) + w_vc/2.0 w_2 = (r-w/2.0 - to_itype(0.75,dbu)) - (r_vc1 - w_vc/2.0 - 0.75) # same for N++, P++ layer r_npp = ((r-w/2.0 - to_itype(0.75,dbu)) + (r_vc1 - w_vc/2.0 - 0.75))/2.0 r_ppp = 2*r - r_npp w_via = to_itype(5.0,dbu) h_via = to_itype(5.0,dbu) # Variables for the SiEtch2 layer (Slab) w_Si3 = round(w_m1_out + 2*(r_m1_out)+ 0/dbu) h_Si3 = w_Si3 taper_bigend = to_itype(2,dbu) taper_smallend = to_itype(0.3,dbu) taper_length = to_itype(5,dbu) #Variables for the MH layer w_mh = to_itype(2.0,dbu) r_mh = r r_mh_in = r_mh - w_mh/2.0 #Define Ring centre x0 = r + w/2 y0 = r + g + w ###################### # Generate the layout: # Create the ring resonator t = pya.Trans(pya.Trans.R0,x0, y0) pcell = ly.create_cell("Ring", "GSiP", { "layer": self.silayer, "radius": self.r, "width": self.w } ) self.cell.insert(pya.CellInstArray(pcell.cell_index(), t)) # Create the two waveguides wg1 = pya.Box(x0 - (w_Si3 / 2 + taper_length), -w/2, x0 + (w_Si3 / 2 + taper_length), w/2) shapes(LayerSiN).insert(wg1) y_offset = 2*r + g + gmon + 2*w wg2 = pya.Box(x0 - (w_Si3 / 2 + taper_length), y_offset-w/2, x0 + (w_Si3 / 2 + taper_length), y_offset+w/2) shapes(LayerSiN).insert(wg2) #Create the SiEtch2 (Slab) layer boxSi3 = pya.Box(x0-w_Si3/2.0, y0 - h_Si3/2.0, x0+w_Si3/2.0, y0 + h_Si3/2.0) shapes(LayerSi3N).insert(boxSi3) pin1pts = [pya.Point(x0-w_Si3/2.0, -taper_bigend/2.0), pya.Point(x0-w_Si3/2.0-taper_length,-taper_smallend/2.0), pya.Point(x0-w_Si3/2.0-taper_length,taper_smallend/2.0), pya.Point(x0-w_Si3/2.0, taper_bigend/2.0)] pin2pts = [pya.Point(x0+w_Si3/2.0,-taper_bigend/2.0), pya.Point(x0+w_Si3/2.0+taper_length,-taper_smallend/2.0), pya.Point(x0+w_Si3/2.0+taper_length,taper_smallend/2.0), pya.Point(x0+w_Si3/2.0,+taper_bigend/2.0)] pin3pts = [pya.Point(x0-w_Si3/2.0,y_offset-taper_bigend/2.0), pya.Point(x0-w_Si3/2.0-taper_length,y_offset-taper_smallend/2.0), pya.Point(x0-w_Si3/2.0-taper_length,y_offset+taper_smallend/2.0), pya.Point(x0-w_Si3/2.0,y_offset+ taper_bigend/2.0)] pin4pts = [pya.Point(x0+w_Si3/2.0,y_offset-taper_bigend/2.0), pya.Point(x0+w_Si3/2.0+taper_length,y_offset-taper_smallend/2.0), pya.Point(x0+w_Si3/2.0+taper_length,y_offset+taper_smallend/2.0), pya.Point(x0+w_Si3/2.0,y_offset+taper_bigend/2.0)] shapes(LayerSi3N).insert(pya.Polygon(pin1pts)) shapes(LayerSi3N).insert(pya.Polygon(pin2pts)) shapes(LayerSi3N).insert(pya.Polygon(pin3pts)) shapes(LayerSi3N).insert(pya.Polygon(pin4pts)) # arc angles # doping: angle_min_doping = -35 angle_max_doping = 215 # VC contact: angle_min_VC = angle_min_doping + 8 angle_max_VC = angle_max_doping - 8 # M1: angle_min_M1 = angle_min_VC - 4 angle_max_M1 = angle_max_VC + 4 # MH: angle_min_MH = -75.0 angle_max_MH = 255 from SiEPIC.utils import arc #Create the N Layer self.cell.shapes(LayernN).insert(pya.Path(arc(r_n, angle_min_doping, angle_max_doping), w_1).transformed(t).simple_polygon()) #Create the P Layer self.cell.shapes(LayerpN).insert(pya.Path(arc(r_p, angle_min_doping, angle_max_doping), w_1).transformed(t).simple_polygon()) #Create the N+ Layer self.cell.shapes(LayernpN).insert(pya.Path(arc(r_np, angle_min_doping, angle_max_doping), w_1).transformed(t).simple_polygon()) #Create the P+ Layer self.cell.shapes(LayerppN).insert(pya.Path(arc(r_pp, angle_min_doping, angle_max_doping), w_1).transformed(t).simple_polygon()) #Create the N++ Layer self.cell.shapes(LayernppN).insert(pya.Path(arc(r_npp, angle_min_doping, angle_max_doping), w_2).transformed(t).simple_polygon()) #Create the P+ +Layer poly = pya.Path(arc(r_ppp, angle_min_doping, angle_max_doping), w_2).transformed(t).simple_polygon() self.cell.shapes(LayerpppN).insert(pya.Region(poly) - pya.Region(pya.Box(x0-r_ppp-w_2/2, y_offset-w/2 - 0.75/dbu, x0+r_ppp+w/2, y_offset+w/2 + 0.75/dbu))) #Create the VC Layer self.cell.shapes(LayervcN).insert(pya.Path(arc(r_vc1, angle_min_VC, angle_max_VC), w_vc).transformed(t).simple_polygon()) poly = pya.Path(arc(r_vc2, angle_min_VC, angle_max_VC), w_vc).transformed(t).simple_polygon() self.cell.shapes(LayervcN).insert(pya.Region(poly) - pya.Region(pya.Box(x0-r_vc2-w_vc/2, y_offset-w/2 - 1.5/dbu, x0+r_vc2+w_vc/2, y_offset+w/2 + 1.5/dbu))) #Create the M1 Layer self.cell.shapes(Layerm1N).insert(pya.Polygon(arc(w_m1_in, angle_min_doping, angle_max_doping) + [pya.Point(0, 0)]).transformed(t)) self.cell.shapes(Layerm1N).insert(pya.Polygon(arc(w_m1_in/2.0, 0, 360)).transformed(t)) self.cell.shapes(Layerm1N).insert(pya.Path(arc(r_m1_out, angle_min_M1, angle_max_M1), w_m1_out).transformed(t).simple_polygon()) boxM11 = pya.Box(x0-w_via, y0 + r_m1_out + w_m1_out-h_via, x0+w_via, y0 + r_m1_out + w_m1_out+h_via) shapes(Layerm1N).insert(boxM11) #Create the ML Layer self.cell.shapes(LayermlN).insert(pya.Polygon(arc(w_m1_in/2.0, 0, 360)).transformed(t)) #Create the VL Layer, as well as the electrical PinRec geometries # centre contact (P, anode): self.cell.shapes(LayervlN).insert(pya.Polygon(arc(r_vl, 0, 360)).transformed(t)) self.cell.shapes(LayerPinRecN).insert(pya.Polygon(arc(r_vl, 0, 360)).transformed(t)) shapes(LayerPinRecN).insert(pya.Text ("elec1a", pya.Trans(pya.Trans.R0,x0,y0))).text_size = 0.5/dbu shapes(LayerPinRecN).insert(pya.Box(x0-w_via/2, y0-w_via/2, x0+w_via/2, y0+w_via/2)) # top contact (N, cathode): boxVL1 = pya.Box(x0-w_via/2, y0 + r_vc2 + w_vc/2 + 2.0/dbu, x0+w_via/2, y0 + r_vc2 + w_vc/2 + 2.0/dbu+ h_via) shapes(LayervlN).insert(boxVL1) shapes(LayerPinRecN).insert(boxVL1) shapes(LayerPinRecN).insert(pya.Text ("elec1c", pya.Trans(pya.Trans.R0,x0,y0 + r_vc2 + w_vc/2 + 2.0/dbu+ h_via/2))).text_size = 0.5/dbu # heater contacts boxVL3 = pya.Box(x0+(r_mh_in)*cos(angle_min_MH/180*pi) + 2.5/dbu, -w/2.0 - 10/dbu, x0 + (r_mh_in)*cos(angle_min_MH/180*pi) + 7.5/dbu, -w/2.0 - 5/dbu) shapes(LayervlN).insert(boxVL3) shapes(LayerPinRecN).insert(boxVL3) shapes(LayerPinRecN).insert(pya.Text ("elec2h2", pya.Trans(pya.Trans.R0,x0+(r_mh_in)*cos(angle_min_MH/180*pi) + 5.0/dbu,-w/2.0 - 7.5/dbu))).text_size = 0.5/dbu boxVL4 = pya.Box(x0-(r_mh_in)*cos(angle_min_MH/180*pi)- 7.5/dbu, -w/2.0 - 10/dbu, x0 - (r_mh_in)*cos(angle_min_MH/180*pi) - 2.5/dbu, -w/2.0 - 5/dbu) shapes(LayervlN).insert(boxVL4) shapes(LayerPinRecN).insert(boxVL4) shapes(LayerPinRecN).insert(pya.Text ("elec2h1", pya.Trans(pya.Trans.R0,x0-(r_mh_in)*cos(angle_min_MH/180*pi) - 5.0/dbu,-w/2.0 - 7.5/dbu))).text_size = 0.5/dbu #Create the MH Layer self.cell.shapes(LayermhN).insert(pya.Path(arc(r_mh, angle_min_MH, angle_max_MH), w_mh).transformed(t).simple_polygon()) boxMH1 = pya.Box(x0+(r_mh_in)*cos(angle_min_MH/180*pi), -w/2.0 - 2.5/dbu, x0 + (r_mh_in)*cos(angle_min_MH/180*pi) + w_mh, y0 +(r_mh_in)*sin(angle_min_MH/180*pi)) shapes(LayermhN).insert(boxMH1) boxMH2 = pya.Box(x0-(r_mh_in)*cos(angle_min_MH/180*pi) - w_mh, -w/2.0 - 2.5/dbu, x0 - (r_mh_in)*cos(angle_min_MH/180*pi), y0 +(r_mh_in)*sin(angle_min_MH/180*pi)) shapes(LayermhN).insert(boxMH2) boxMH3 = pya.Box(x0+(r_mh_in)*cos(angle_min_MH/180*pi), -w/2.0 - 12.5/dbu, x0 + (r_mh_in)*cos(angle_min_MH/180*pi) + 10/dbu, -w/2.0 - 2.5/dbu) shapes(LayermhN).insert(boxMH3) boxMH4 = pya.Box(x0-(r_mh_in)*cos(angle_min_MH/180*pi)- 10/dbu, -w/2.0 - 12.5/dbu, x0 - (r_mh_in)*cos(angle_min_MH/180*pi), -w/2.0 - 2.5/dbu) shapes(LayermhN).insert(boxMH4) # Create the pins, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length shapes(LayerPinRecN).insert(pya.Path([pya.Point(x0 - (w_Si3 / 2. + taper_length) + pin_length/2., 0), pya.Point(x0 - (w_Si3 / 2. + taper_length) - pin_length/2., 0)], w)) shapes(LayerPinRecN).insert(pya.Text("opt1", pya.Trans(pya.Trans.R0,x0 - (w_Si3 / 2. + taper_length), 0))).text_size = 0.5/dbu shapes(LayerPinRecN).insert(pya.Path([pya.Point(x0 + (w_Si3 / 2. + taper_length) - pin_length/2., 0), pya.Point(x0 + (w_Si3 / 2. + taper_length) + pin_length/2., 0)], w)) shapes(LayerPinRecN).insert(pya.Text("opt2", pya.Trans(pya.Trans.R0,x0 + (w_Si3 / 2. + taper_length), 0))).text_size = 0.5/dbu shapes(LayerPinRecN).insert(pya.Path([pya.Point(x0 - (w_Si3 / 2. + taper_length) + pin_length/2., y_offset), pya.Point(x0 - (w_Si3 / 2. + taper_length) - pin_length/2., y_offset)], w)) shapes(LayerPinRecN).insert(pya.Text("opt3", pya.Trans(pya.Trans.R0,x0 - (w_Si3 / 2. + taper_length), y_offset))).text_size = 0.5/dbu shapes(LayerPinRecN).insert(pya.Path([pya.Point(x0 + (w_Si3 / 2. + taper_length) - pin_length/2., y_offset), pya.Point(x0 + (w_Si3 / 2. + taper_length) + pin_length/2., y_offset)], w)) shapes(LayerPinRecN).insert(pya.Text("opt4", pya.Trans(pya.Trans.R0,x0 + (w_Si3 / 2. + taper_length), y_offset))).text_size = 0.5/dbu # Create the device recognition layer shapes(LayerDevRecN).insert(pya.Box(x0 - (w_Si3 / 2 + taper_length), -w/2.0 - 12.5/dbu, x0 + (w_Si3 / 2 + taper_length), y0 + r_m1_out + w_m1_out+h_via )) # Compact model information shape = shapes(LayerDevRecN).insert(pya.Text('Lumerical_INTERCONNECT_library=Design kits/GSiP', \ pya.Trans(pya.Trans.R0,0, 0))).text_size = 0.3/dbu shapes(LayerDevRecN).insert(pya.Text('Component=Ring_Modulator_DB', \ pya.Trans(pya.Trans.R0,0, w*2))).text_size = 0.3/dbu shapes(LayerDevRecN).insert(pya.Text('Component_ID=%s' % self.component_ID, \ pya.Trans(pya.Trans.R0,0, w*4))).text_size = 0.3/dbu shapes(LayerDevRecN).insert(pya.Text \ ('Spice_param:radius=%.3fu wg_width=%.3fu gap=%.3fu gap_monitor=%.3fu' %\ (self.r, self.w, self.g, self.gmon), \ pya.Trans(pya.Trans.R0,0, -w*2) ) ).text_size = 0.3/dbu # Add a polygon text description from SiEPIC.utils import layout_pgtext if self.textpolygon : layout_pgtext(self.cell, self.textl, self.w, self.r+self.w, "%.3f-%g" % ( self.r, self.g), 1) # Reference publication: shapes(TextLayerN).insert(pya.Text ("Ref: Raphael Dube-Demers, JLT, 2015", pya.Trans(pya.Trans.R0,x0 - (w_Si3 / 2 + taper_length), -w/2.0 - 12.5/dbu+4.0/dbu))).text_size = 0.7/dbu shapes(TextLayerN).insert(pya.Text ("http://dx.doi.org/10.1109/JLT.2015.2462804", pya.Trans(pya.Trans.R0,x0 - (w_Si3 / 2 + taper_length), -w/2.0 - 12.5/dbu+1.0/dbu))).text_size = 0.7/dbu
def layout_waveguide2(TECHNOLOGY, layout, cell, layers, widths, offsets, pts, radius, adiab, bezier): from SiEPIC.utils import arc_xy, arc_bezier, angle_vector, angle_b_vectors, inner_angle_b_vectors, translate_from_normal from SiEPIC.extend import to_itype from pya import Path, Polygon, Trans dbu = layout.dbu width = widths[0] turn = 0 waveguide_length = 0 for lr in range(0, len(layers)): wg_pts = [pts[0]] layer = layout.layer(TECHNOLOGY[layers[lr]]) width = to_itype(widths[lr], dbu) offset = to_itype(offsets[lr], dbu) for i in range(1, len(pts) - 1): turn = ( (angle_b_vectors(pts[i] - pts[i - 1], pts[i + 1] - pts[i]) + 90) % 360 - 90) / 90 dis1 = pts[i].distance(pts[i - 1]) dis2 = pts[i].distance(pts[i + 1]) angle = angle_vector(pts[i] - pts[i - 1]) / 90 pt_radius = to_itype(radius, dbu) # determine the radius, based on how much space is available if len(pts) == 3: pt_radius = min(dis1, dis2, pt_radius) else: if i == 1: if dis1 <= pt_radius: pt_radius = dis1 elif dis1 < 2 * pt_radius: pt_radius = dis1 / 2 if i == len(pts) - 2: if dis2 <= pt_radius: pt_radius = dis2 elif dis2 < 2 * pt_radius: pt_radius = dis2 / 2 # waveguide bends: if abs(turn) == 1: if (adiab): wg_pts += Path( arc_bezier( pt_radius, 270, 270 + inner_angle_b_vectors( pts[i - 1] - pts[i], pts[i + 1] - pts[i]), float(bezier), DevRec='DevRec' in layers[lr]), 0).transformed(Trans(angle, turn < 0, pts[i])).get_points() else: wg_pts += Path( arc_xy(-pt_radius, pt_radius, pt_radius, 270, 270 + inner_angle_b_vectors( pts[i - 1] - pts[i], pts[i + 1] - pts[i]), DevRec='DevRec' in layers[lr]), 0).transformed(Trans(angle, turn < 0, pts[i])).get_points() wg_pts += [pts[-1]] wg_pts = pya.Path(wg_pts, 0).unique_points().get_points() wg_polygon = Polygon( translate_from_normal( wg_pts, width / 2 + (offset if turn > 0 else -offset)) + translate_from_normal( wg_pts, -width / 2 + (offset if turn > 0 else -offset))[::-1]) cell.shapes(layer).insert(wg_polygon) if layout.layer(TECHNOLOGY['Waveguide']) == layer: waveguide_length = wg_polygon.area() / width * dbu return waveguide_length
def make_pin(cell, name, center, w, layer, direction): ''' Makes a pin that SiEPIC-Tools will recognize cell: which cell to draw it in name: text label for the pin center: location, int [x,y] w: pin width layer: layout.layer() type direction = 0: right 90: up 180: left 270: down Units: intput can be float for microns, or int for nm ''' from SiEPIC.extend import to_itype import numpy dbu = cell.layout().dbu if type(w) == type(float()): w = to_itype(w, dbu) print('SiEPIC.utils.layout.make_pin: w converted to %s' % w) else: print('SiEPIC.utils.layout.make_pin: w %s' % w) # print(type(center[0])) if type(center[0]) == type(float()) or type(center[0]) == type( numpy.float64()): center[0] = to_itype(center[0], dbu) center[1] = to_itype(center[1], dbu) print('SiEPIC.utils.layout.make_pin: center converted to %s' % (center)) else: print('SiEPIC.utils.layout.make_pin: center %s' % (center)) from SiEPIC._globals import PIN_LENGTH as pin_length if direction not in [0, 90, 180, 270]: raise ('error in make_pin: direction must be one of [0, 90, 180, 270]') # text label t = pya.Trans(pya.Trans.R0, center[0], center[1]) text = pya.Text(name, t) shape = cell.shapes(layer).insert(text) shape.text_dsize = float(w * dbu) shape.text_valign = 1 if direction == 0: p1 = pya.Point(center[0] - pin_length / 2, center[1]) p2 = pya.Point(center[0] + pin_length / 2, center[1]) shape.text_halign = 2 if direction == 90: p1 = pya.Point(center[0], center[1] - pin_length / 2) p2 = pya.Point(center[0], center[1] + pin_length / 2) shape.text_halign = 2 shape.text_rot = 1 if direction == 180: p1 = pya.Point(center[0] + pin_length / 2, center[1]) p2 = pya.Point(center[0] - pin_length / 2, center[1]) shape.text_halign = 3 if direction == 270: p1 = pya.Point(center[0], center[1] + pin_length / 2) p2 = pya.Point(center[0], center[1] - pin_length / 2) shape.text_halign = 3 shape.text_rot = 1 pin = pya.Path([p1, p2], w) cell.shapes(layer).insert(pin)
def to_itype(self, dbu): path = pya.Path([pt.to_itype(dbu) for pt in self.each_point()], round(self.width/dbu)) path.width = round(self.width / dbu) return path
def produce_impl(self): # fetch the parameters dbu = self.layout.dbu ly = self.layout shapes = self.cell.shapes LayerSi = self.layer LayerSiN = ly.layer(self.layer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) LayerTextN = ly.layer(self.textl) from math import pi, cos, sin, log, sqrt, tan lambda_0 = self.wavelength ##um wavelength of light pin_length = 0.5 ##um extra nub for the waveguide attachment # Geometry wh = self.period * self.dc ##thick grating wl = self.ff * (self.period - wh) ## thin grating # Width scale parameter is a first pass attempt at designing for length contraction # at cryogenic temperature. It is applied BEFORE the width error; this is because # the order of operations in the reverse is over/under-etch, then cool and contract. # So first scale so that target width is reached after contraction, then add # fabrication error so that the scaled width is reached. wh = (wh * self.w_scale + self.w_err) wl = (wl * self.w_scale + self.w_err) spacing = (self.period - wh - wl) / 2 ##space between thick and thin gc_number = int(round(self.grating_length / self.period)) ##number of periods e = self.n_t * sin((pi / 180) * self.theta_c) / self.n_e N = round(self.taper_length * (1 + e) * self.n_e / lambda_0) ##allows room for the taper start = (pi - (pi / 180) * self.angle_e / 2) stop = (pi + (pi / 180) * self.angle_e / 2) # Draw coupler grating. for j in range(gc_number): # number of points in the arcs: # calculate such that the vertex & edge placement error is < 0.5 nm. # see "SiEPIC_EBeam_functions - points_per_circle" for more details radius = N * lambda_0 / (self.n_e * (1 - e)) + j * self.period + spacing seg_points = int( points_per_circle(radius / dbu) / 360. * self.angle_e) # number of points grating arc theta_up = [] for m in range(seg_points + 1): theta_up = theta_up + [start + m * (stop - start) / seg_points] theta_down = theta_up[::-1] ##small one r_up = [] r_down = [] for k in range(len(theta_up)): r_up = r_up + [ N * lambda_0 / (self.n_e * (1 - e * cos(float(theta_up[k])))) + j * self.period + spacing ] r_down = r_up[::-1] xr = [] yr = [] for k in range(len(theta_up)): xr = xr + [r_up[k] * cos(theta_up[k])] yr = yr + [r_up[k] * sin(theta_up[k])] xl = [] yl = [] for k in range(len(theta_down)): xl = xl + [(r_down[k] + wl) * cos(theta_down[k])] yl = yl + [(r_down[k] + wl) * sin(theta_down[k])] x = xr + xl y = yr + yl pts = [] for i in range(len(x)): pts.append( Point.from_dpoint(pya.DPoint(x[i] / dbu, y[i] / dbu))) #small_one = core.Boundary(points) polygon = Polygon(pts) shapes(LayerSiN).insert(polygon) ##big one r_up = [] r_down = [] for k in range(len(theta_up)): r_up = r_up + [ N * lambda_0 / (self.n_e * (1 - e * cos(float(theta_up[k])))) + j * self.period + 2 * spacing + wl ] r_down = r_up[::-1] xr = [] yr = [] for k in range(len(theta_up)): xr = xr + [r_up[k] * cos(theta_up[k])] yr = yr + [r_up[k] * sin(theta_up[k])] xl = [] yl = [] for k in range(len(theta_down)): xl = xl + [(r_down[k] + wh) * cos(theta_down[k])] yl = yl + [(r_down[k] + wh) * sin(theta_down[k])] x = xr + xl y = yr + yl pts = [] for i in range(len(x)): pts.append( Point.from_dpoint(pya.DPoint(x[i] / dbu, y[i] / dbu))) polygon = Polygon(pts) shapes(LayerSiN).insert(polygon) # Taper section r_up = [] r_down = [] for k in range(len(theta_up)): r_up = r_up + [ N * lambda_0 / (self.n_e * (1 - e * cos(float(theta_up[k])))) ] r_down = r_up[::-1] xl = [] yl = [] for k in range(len(theta_down)): xl = xl + [(r_down[k]) * cos(theta_down[k])] yl = yl + [(r_down[k]) * sin(theta_down[k])] yr = [self.t / 2., self.t / 2., -self.t / 2., -self.t / 2.] yl_abs = [] for k in range(len(yl)): yl_abs = yl_abs + [abs(yl[k])] y_max = max(yl_abs) iy_max = yl_abs.index(y_max) L_o = (y_max - self.t / 2) / tan((pi / 180) * self.angle_e / 2) xr = [L_o + xl[iy_max], 0, 0, L_o + xl[iy_max]] x = xr + xl y = yr + yl pts = [] for i in range(len(x)): pts.append(Point.from_dpoint(pya.DPoint(x[i] / dbu, y[i] / dbu))) polygon = Polygon(pts) shapes(LayerSiN).insert(polygon) # Pin on the waveguide: pin_length = 200 x = 0 t = Trans(x, 0) pin = pya.Path([Point(-pin_length / 2, 0), Point(pin_length / 2, 0)], self.t / dbu) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin1", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu # Device recognition layer yr = sin(start) * (N * lambda_0 / (self.n_e * (1 - e * cos(float(start)))) + gc_number * self.period + spacing) box1 = Box( -(self.grating_length + self.taper_length) / dbu - pin_length * 2, yr / dbu, 0, -yr / dbu) shapes(LayerDevRecN).insert(box1)
def produce_impl(self): # fetch the parameters dbu = self.layout.dbu ly = self.layout bus_n = self.bus_number LayerSi = self.layer LayerSiN = ly.layer(self.layer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) LayerTextN = ly.layer(self.textl) a = self.a / dbu r = self.r / dbu wg_dis = self.wg_dis + 1 n = int(math.ceil(self.n / 2)) Sx = [self.S1x, self.S2x, self.S3x, self.S4x, self.S5x] Sy = [self.S1y, 0, self.S2y] if wg_dis % 2 == 0: length_slab_x = 2 * n * a else: length_slab_x = (2 * n + 1) * a length_slab_y = 2 * (n - 2) * a if bus_n == 2: k = -1 else: k = 1 #function to creat polygon pts for right half of a hole in a hexagon unit cell def hexagon_hole_half(a, r): npts = 10 theta_div = math.pi / 3 theta_div_hole = math.pi / npts triangle_length = a / math.sqrt(3) pts = [] for i in range(0, 4): pts.append( Point.from_dpoint( pya.DPoint( triangle_length * math.cos(i * theta_div - math.pi / 2), triangle_length * math.sin(i * theta_div - math.pi / 2)))) for i in range(0, npts + 1): pts.append( Point.from_dpoint( pya.DPoint( r * math.cos(math.pi / 2 - i * theta_div_hole), r * math.sin(math.pi / 2 - i * theta_div_hole)))) return pts def hexagon_shifthole_half(a, r): npts = 10 theta_div = math.pi / 3 theta_div_hole = math.pi / npts triangle_length = a * 1.235 / math.sqrt(3) pts = [] for i in range(0, 4): pts.append( Point.from_dpoint( pya.DPoint( triangle_length * math.cos(i * theta_div - math.pi / 2), triangle_length * math.sin(i * theta_div - math.pi / 2)))) for i in range(0, npts + 1): pts.append( Point.from_dpoint( pya.DPoint( r * math.cos(math.pi / 2 - i * theta_div_hole), r * math.sin(math.pi / 2 - i * theta_div_hole)))) return pts #function to creat polygon pts for right half of a hexagon unit cell def hexagon_half(a): theta_div = math.pi / 3 triangle_length = a / math.sqrt(3) pts = [] for i in range(0, 4): pts.append( Point.from_dpoint( pya.DPoint( triangle_length * math.cos(i * theta_div - math.pi / 2), triangle_length * math.sin(i * theta_div - math.pi / 2)))) return pts #create the right and left half of the hole and hexagon cells #hole_cell = pya.Region() #hexagon_cell = pya.Region() hole = pya.Region() hole_cell_pts = hexagon_hole_half(a, r) hexagon_pts = hexagon_half(a) hole_shiftcell_pts = hexagon_shifthole_half(a, r) hole_cell_poly_0 = pya.Polygon(hole_cell_pts) hexagon_cell_poly_0 = pya.Polygon(hexagon_pts) hole_shiftcell_poly_0 = pya.Polygon(hole_shiftcell_pts) hole_trans = pya.Trans(pya.Trans.R180) hole_cell_poly_1 = hole_cell_poly_0.transformed(hole_trans) hexagon_cell_poly_1 = hexagon_cell_poly_0.transformed(hole_trans) hole_shiftcell_poly_1 = hole_shiftcell_poly_0.transformed(hole_trans) #create the photonic crystal with shifts and waveguides for j in range(-n + 1, n): if j % 2 == 0: for i in range(-n, n + 1): #waveguide if (j == k * wg_dis and i > 3) or (j == wg_dis and i != 0): hole_x = abs(i) / i * (abs(i) - 0.5) * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t_0 = hexagon_cell_poly_0.transformed(hole_trans) hole_t_1 = hexagon_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) #filling the edges with half cell elif i in (-n, n) and wg_dis % 2 == 1: hole_x = abs(i) / i * (abs(i) + 0.5) * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) if i == -n: hole_t = hole_cell_poly_0.transformed(hole_trans) else: hole_t = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t) hole_x = abs(i) / i * (abs(i) - 0.5) * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t_0 = hole_cell_poly_0.transformed(hole_trans) hole_t_1 = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) #x shifts elif j == 0 and i in (1, -1, 2, -2, 3, -3, 4, -4, 5, -5): hole_x = abs(i) / i * (abs(i) - 0.5 + Sx[abs(i) - 1]) * a hole_y = 0 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t_0 = hole_shiftcell_poly_0.transformed( hole_trans) hole_t_1 = hole_shiftcell_poly_1.transformed( hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) elif i != 0: hole_x = abs(i) / i * (abs(i) - 0.5) * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t_0 = hole_cell_poly_0.transformed(hole_trans) hole_t_1 = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) elif j % 2 == 1: for i in range(-n, n + 1): #waveguide if (j == k * wg_dis and i > 3) or j == wg_dis: hole_x = i * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t_0 = hexagon_cell_poly_0.transformed(hole_trans) hole_t_1 = hexagon_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) #filling the edges with half cell elif wg_dis % 2 == 0 and i in (-n, n): hole_x = i * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) if i == -n: hole_t = hole_cell_poly_0.transformed(hole_trans) else: hole_t = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t) #y shifts elif i == 0 and j in (1, -1, 3, -3): hole_x = 0 hole_y = j * a * (math.sqrt(3) / 2) + abs(j) / j * a * Sy[abs(j) - 1] hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t_0 = hole_shiftcell_poly_0.transformed( hole_trans) hole_t_1 = hole_shiftcell_poly_1.transformed( hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) else: hole_x = i * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t_0 = hole_cell_poly_0.transformed(hole_trans) hole_t_1 = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) #print(hole_t_0) box_l = a / 2 hole.insert(pya.Box(-box_l, -box_l, box_l, box_l)) cover_box = pya.Box(-length_slab_x / 2, -a / 2, length_slab_x / 2, a / 2) box_y = n * a * math.sqrt(3) / 2 cover_box_trans_0 = pya.Trans(Trans.R0, 0, box_y) cover_box_trans_1 = pya.Trans(Trans.R0, 0, -box_y) cover_box_t_0 = cover_box.transformed(cover_box_trans_0) cover_box_t_1 = cover_box.transformed(cover_box_trans_1) #hole.insert(pya.Box()) self.cell.shapes(LayerSiN).insert(hole) self.cell.shapes(LayerSiN).insert(cover_box_t_0) self.cell.shapes(LayerSiN).insert(cover_box_t_1) # Pins on the waveguide: pin_length = 200 pin_w = a wg_pos = a * math.sqrt(3) / 2 * wg_dis t = pya.Trans(Trans.R0, -length_slab_x / 2, wg_pos) pin = pya.Path( [pya.Point(-pin_length / 2, 0), pya.Point(pin_length / 2, 0)], pin_w) pin_t = pin.transformed(t) self.cell.shapes(LayerPinRecN).insert(pin_t) text = pya.Text("pin1", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu t = pya.Trans(Trans.R0, length_slab_x / 2, wg_pos) pin_t = pin.transformed(t) self.cell.shapes(LayerPinRecN).insert(pin_t) text = pya.Text("pin2", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu #pin for drop waveguide t = pya.Trans(Trans.R0, length_slab_x / 2, -wg_pos) pin_t = pin.transformed(t) self.cell.shapes(LayerPinRecN).insert(pin_t) text = pya.Text("pin3", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu # Create the device recognition layer -- make it 1 * wg_width away from the waveguides. points = [[-length_slab_x / 2, 0], [length_slab_x / 2, 0]] points = [Point(each[0], each[1]) for each in points] path = Path(points, length_slab_y) self.cell.shapes(LayerDevRecN).insert(path.simple_polygon())
grating_reps = 120 grid_nx = len(grating_period) grid_ny = len(grating_dc) grid_dx = 50 / dbu grid_dy = 50 / dbu gp = grating_period[0] gd = grating_dc[0] for i, gr in enumerate(grating_rt): for j, gd2 in enumerate(grating_dc2): x = grid_dx * i y = grid_dy * j boundary = pya.Region() outer_grating = pya.Region() inner_grating = pya.Region() boundary.insert(pya.Path([pya.Point(x, y + grating_length / 2)], gr * 2, gr, gr, True)) for k in range(grating_reps): outer_grating.insert(pya.Box(x + gp * (k - grating_reps / 2), y, x + gp * (k - grating_reps / 2 + (1 - gd)), y + grating_length)) inner_grating.insert(pya.Box(x + gp * (k - grating_reps / 2), y, x + gp * (k - grating_reps / 2 + (1 - gd - gd2)), y + grating_length)) inner_grating.__iand__(boundary) outer_grating.__isub__( boundary) cell.shapes(li).insert(inner_grating) cell.shapes(li).insert(outer_grating) lv.add_missing_layers() lv.zoom_fit() lv.commit()
def produce(self, layout, layers, parameters, cell): """ coerce parameters (make consistent) """ self._layers = layers self.cell = cell self._param_values = parameters self.layout = layout shapes = self.cell.shapes # cell: layout cell to place the layout # LayerSiN: which layer to use # w: waveguide width # length units in dbu # fetch the parameters dbu = self.layout.dbu ly = self.layout LayerSi = self.silayer LayerSiN = ly.layer(self.silayer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) LayerTextN = ly.layer(self.textl) base = int(round(self.tri_base / dbu)) height = int(round(self.tri_height / dbu)) l = int(round(self.taper_wg_length / dbu)) w = int(round(self.wg_width / dbu)) pts = [ Point(-l, w / 2), Point(-base, w / 2), Point(0, w / 2 + height), Point(0, -(w / 2 + height)), Point(-base, -w / 2), Point(-l, -w / 2) ] shapes(LayerSiN).insert(Polygon(pts)) # Pins on the bus waveguide side: pin_length = 200 if l < pin_length + 1: pin_length = int(l / 3) pin_length = math.ceil(pin_length / 2.) * 2 if pin_length == 0: pin_length = 2 t = Trans(Trans.R0, -l, 0) pin = pya.Path([Point(-pin_length / 2, 0), Point(pin_length / 2, 0)], w) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin1", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu t = Trans(Trans.R0, 0, 0) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin2", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu # Create the device recognition layer -- make it 1 * wg_width away from the waveguides. #box1 = Box(w/2+height, -(w/2+height), -l, -1) #shapes(LayerDevRecN).insert(box1) return "wg_triangle_taper"
def layout_waveguide2(TECHNOLOGY, layout, cell, layers, widths, offsets, pts, radius, adiab, bezier): from SiEPIC.utils import arc_xy, arc_bezier, angle_vector, angle_b_vectors, inner_angle_b_vectors, translate_from_normal from SiEPIC.extend import to_itype from pya import Path, Polygon, Trans dbu = layout.dbu if 'Errors' in TECHNOLOGY: error_layer = layout.layer(TECHNOLOGY['Errors']) else: error_layer = None width = widths[0] turn = 0 waveguide_length = 0 for lr in range(0, len(layers)): wg_pts = [pts[0]] layer = layout.layer(TECHNOLOGY[layers[lr]]) width = to_itype(widths[lr], dbu) offset = to_itype(offsets[lr], dbu) for i in range(1, len(pts) - 1): turn = ( (angle_b_vectors(pts[i] - pts[i - 1], pts[i + 1] - pts[i]) + 90) % 360 - 90) / 90 dis1 = pts[i].distance(pts[i - 1]) dis2 = pts[i].distance(pts[i + 1]) angle = angle_vector(pts[i] - pts[i - 1]) / 90 pt_radius = to_itype(radius, dbu) error_seg1 = False error_seg2 = False # determine the radius, based on how much space is available if len(pts) == 3: # simple corner, limit radius by the two edges if dis1 < pt_radius: error_seg1 = True if dis2 < pt_radius: error_seg2 = True pt_radius = min(dis1, dis2, pt_radius) else: if i == 1: # first corner, limit radius by first edge, or 1/2 of second one if dis1 < pt_radius: error_seg1 = True if dis2 / 2 < pt_radius: error_seg2 = True pt_radius = min(dis1, dis2 / 2, pt_radius) elif i == len(pts) - 2: # last corner, limit radius by second edge, or 1/2 of first one if dis1 / 2 < pt_radius: error_seg1 = True if dis2 < pt_radius: error_seg2 = True pt_radius = min(dis1 / 2, dis2, pt_radius) else: if dis1 / 2 < pt_radius: error_seg1 = True if dis2 / 2 < pt_radius: error_seg2 = True pt_radius = min(dis1 / 2, dis2 / 2, pt_radius) if error_seg1 or error_seg2: if not error_layer: # we have an error, but no Error layer print('- SiEPIC:layout_waveguide2: missing Error layer') # and pt_radius < to_itype(radius,dbu): elif layer == layout.layer(TECHNOLOGY['Waveguide']): # add an error polygon to flag the incorrect bend if error_seg1: error_pts = pya.Path([pts[i - 1], pts[i]], width) cell.shapes(error_layer).insert(error_pts) if error_seg2: error_pts = pya.Path([pts[i], pts[i + 1]], width) cell.shapes(error_layer).insert(error_pts) # error_pts = pya.Path([pts[i-1], pts[i], pts[i+1]], width) # cell.shapes(error_layer).insert(error_pts) # waveguide bends: if abs(turn) == 1: if (adiab): wg_pts += Path( arc_bezier( pt_radius, 270, 270 + inner_angle_b_vectors( pts[i - 1] - pts[i], pts[i + 1] - pts[i]), float(bezier), DevRec='DevRec' in layers[lr]), 0).transformed(Trans(angle, turn < 0, pts[i])).get_points() else: wg_pts += Path( arc_xy(-pt_radius, pt_radius, pt_radius, 270, 270 + inner_angle_b_vectors( pts[i - 1] - pts[i], pts[i + 1] - pts[i]), DevRec='DevRec' in layers[lr]), 0).transformed(Trans(angle, turn < 0, pts[i])).get_points() wg_pts += [pts[-1]] wg_pts = pya.Path(wg_pts, 0).unique_points().get_points() wg_polygon = Polygon( translate_from_normal( wg_pts, width / 2 + (offset if turn > 0 else -offset)) + translate_from_normal( wg_pts, -width / 2 + (offset if turn > 0 else -offset))[::-1]) cell.shapes(layer).insert(wg_polygon) if layout.layer(TECHNOLOGY['Waveguide']) == layer: waveguide_length = wg_polygon.area() / width * dbu return waveguide_length
def produce_impl(self): from SiEPIC.utils import arc_xy, arc_bezier, angle_vector, angle_b_vectors, inner_angle_b_vectors, translate_from_normal from math import cos, sin, pi, sqrt import pya from SiEPIC.extend import to_itype print("GSiP.Wireguide") TECHNOLOGY = get_technology_by_name( 'GSiP') if op_tag == "GUI" else Tech.load_from_xml( lyp_filepath).layers dbu = self.layout.dbu wg_width = to_itype(self.width, dbu) path = self.path.to_itype(dbu) if not (len(self.layers) == len(self.widths) and len(self.layers) == len(self.offsets) and len(self.offsets) == len(self.widths)): raise Exception( "There must be an equal number of layers, widths and offsets") path.unique_points() turn = 0 for lr in range(0, len(self.layers)): layer = self.layout.layer(TECHNOLOGY[self.layers[lr]]) width = to_itype(self.widths[lr], dbu) offset = to_itype(self.offsets[lr], dbu) pts = path.get_points() wg_pts = [pts[0]] for i in range(1, len(pts) - 1): turn = ((angle_b_vectors(pts[i] - pts[i - 1], pts[i + 1] - pts[i]) + 90) % 360 - 90) / 90 dis1 = pts[i].distance(pts[i - 1]) dis2 = pts[i].distance(pts[i + 1]) angle = angle_vector(pts[i] - pts[i - 1]) / 90 pt_radius = self.radius # determine the radius, based on how much space is available if len(pts) == 3: pt_radius = min(dis1, dis2, pt_radius) else: if i == 1: if dis1 <= pt_radius: pt_radius = dis1 elif dis1 < 2 * pt_radius: pt_radius = dis1 / 2 if i == len(pts) - 2: if dis2 <= pt_radius: pt_radius = dis2 elif dis2 < 2 * pt_radius: pt_radius = dis2 / 2 # wireguide bends: if (self.adiab): wg_pts += Path( arc_bezier( pt_radius, 270, 270 + inner_angle_b_vectors( pts[i - 1] - pts[i], pts[i + 1] - pts[i]), self.bezier, DevRec='DevRec' in self.layers[lr]), 0).transformed(Trans(angle, turn < 0, pts[i])).get_points() else: wg_pts += Path( arc_xy(-pt_radius, pt_radius, pt_radius, 270, 270 + inner_angle_b_vectors( pts[i - 1] - pts[i], pts[i + 1] - pts[i]), DevRec='DevRec' in self.layers[lr]), 0).transformed(Trans(angle, turn < 0, pts[i])).get_points() wg_pts += [pts[-1]] wg_pts = pya.Path(wg_pts, 0).unique_points().get_points() wg_polygon = Path(wg_pts, wg_width) self.cell.shapes(layer).insert(wg_polygon) # insert the wireguide #if self.layout.layer(TECHNOLOGY['Wireguide']) == layer: waveguide_length = wg_polygon.area() / self.width * dbu**2 pts = path.get_points() LayerPinRecN = self.layout.layer(TECHNOLOGY['PinRecM']) # insert pins to wireguide t1 = Trans(angle_vector(pts[0] - pts[1]) / 90, False, pts[0]) self.cell.shapes(LayerPinRecN).insert( Path([Point(-50, 0), Point(50, 0)], wg_width).transformed(t1)) self.cell.shapes(LayerPinRecN).insert(Text("pin1", t1, 0.3 / dbu, -1)) t = Trans(angle_vector(pts[-1] - pts[-2]) / 90, False, pts[-1]) self.cell.shapes(LayerPinRecN).insert( Path([Point(-50, 0), Point(50, 0)], wg_width).transformed(t)) self.cell.shapes(LayerPinRecN).insert(Text("pin2", t, 0.3 / dbu, -1)) LayerDevRecN = self.layout.layer(TECHNOLOGY['DevRec']) # Compact model information angle_vec = angle_vector(pts[0] - pts[1]) / 90 halign = 0 # left angle = 0 pt2 = pts[0] pt3 = pts[0] if angle_vec == 0: # horizontal halign = 2 # right angle = 0 pt2 = pts[0] + Point(0, wg_width) pt3 = pts[0] + Point(0, -wg_width) if angle_vec == 2: # horizontal halign = 0 # left angle = 0 pt2 = pts[0] + Point(0, wg_width) pt3 = pts[0] + Point(0, -wg_width) if angle_vec == 1: # vertical halign = 2 # right angle = 1 pt2 = pts[0] + Point(wg_width, 0) pt3 = pts[0] + Point(-wg_width, 0) if angle_vec == -1: # vertical halign = 0 # left angle = 1 pt2 = pts[0] + Point(wg_width, 0) pt3 = pts[0] + Point(-wg_width, 0)
def make_pattern(self, l, w, xpos, ypos, num): a1 = [ pya.Point(0, 0), pya.Point(100, 0), pya.Point(100, l + w), pya.Point(200, l + w), pya.Point(200, 0), pya.Point(300, 0) ] y1 = l + w / 2 - 20 x1 = 150 d1 = [ pya.Point(x1, y1), pya.Point(x1, -w / 2) ] y2 = w / 2 + 20 x2 = 50 e1 = [ pya.Point(x2, y2), pya.Point(x2, l + w + w / 2) ] unit_cell = self.layout.create_cell("1") unit_cell.shapes(self.layer_index).insert(pya.Path(a1, w)) unit_cell.shapes(self.layer_index).insert(pya.Path(d1, w)) unit_cell.shapes(self.layer_index).insert(pya.Path(e1, w)) unit_cell.shapes(self.layer_index).insert(pya.Box(x1 - w / 2 - 2, y1 - 2, x1 - w / 2 + 2, y1 + 2)) unit_cell.shapes(self.layer_index).insert(pya.Box(x1 + w / 2 + 2, y1 - 2, x1 + w / 2 + 2, y1 + 2)) unit_cell.shapes(self.layer_index).insert(pya.Box(x2 - w / 2 - 2, y2 - 2, x2 - w / 2 + 2, y2 + 2)) unit_cell.shapes(self.layer_index).insert(pya.Box(x2 + w / 2 + 2, y2 - 2, x2 + w / 2 + 2, y2 + 2)) nx = 45 dx = 200 ny = 28 dy = 320 ox = xpos oy = ypos trans = pya.Trans(pya.Point(ox, oy)) ax = pya.Point(dx, 0) ay = pya.Point(0, dy) self.top.insert(pya.CellInstArray(unit_cell.cell_index(), trans, ax, ay, nx, ny)) # Note: this is the translation of named parameters to the PCell specific parameter vector # pv. Since the parameter vector is built the same way always, we could simply exchange the # text parameter, given we have the index. param = { "text": "%02d-%06d" % ( l, num ), "layer": pya.LayerInfo(15,99), "mag": 1 } pv = [] for p in self.pcell_decl.get_parameters(): if p.name in param: pv.append(param[p.name]) else: pv.append(p.default) # "fake PCell code" - see other thread ("Creating letters") text_cell = self.layout.create_cell("1T") self.pcell_decl.produce(self.layout, [ self.layer_index ], pv, text_cell) t = pya.Trans(pya.Trans.R0, -1000 + xpos, -1000 + ypos) self.top.insert(pya.CellInstArray(text_cell.cell_index(), t))
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) #LayerSiSPN = ly.layer(LayerSiSP) 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 print("N boxes: %s, grating_period: %s" % (N_boxes, grating_period)) # Draw the Bragg grating: box_width = int(round(grating_period * self.duty)) w = self.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) # i = i + 1 # x = int(round((i * grating_period))) # box1 = Box(x, -half_w, x + box_width, half_w) # shapes(LayerSiN).insert(box1) length = self.length / dbu # Pins on the waveguide: from SiEPIC._globals import PIN_LENGTH as pin_length # pin_length = box_width t = Trans(Trans.R0, 0, 0) pin = Path([Point(pin_length / 2, 0), Point(-pin_length / 2, 0)], w) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin1", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu t = Trans(Trans.R0, length, 0) pin = Path([Point(-pin_length / 2, 0), Point(pin_length / 2, 0)], w) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin2", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu shape.text_halign = 2 # Compact model information t = Trans(Trans.R0, 0, 0) text = Text('Lumerical_INTERCONNECT_library=Design kits/ebeam_v1.2', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, length / 10, 0) text = Text('Component=NO_MODEL_AVAILABLE', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, length / 9, -box_width * 2) text = Text \ ('Spice_param:length=%.3fu target_period=%.3fu grating_period=%.3fu wg_width=%.3fu duty=%.3f ' %\ (self.length, self.target_period, round(grating_period)*dbu, self.wg_width, self.duty), t ) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu # Create the device recognition layer -- make it 1 * wg_width away from the waveguides. 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())
def layout_waveguide2(TECHNOLOGY, layout, cell, layers, widths, offsets, pts, radius, adiab, bezier): ''' Create a waveguide, in a specific technology inputs - TECHNOLOGY, layout, cell: from SiEPIC.utils import get_layout_variables TECHNOLOGY, lv, layout, cell = get_layout_variables() - layers: list of text names, e.g., ['Waveguide'] - widths: list of floats in units Microns, e.g., [0.50] - offsets: list of floats in units Microns, e.g., [0] - pts: a list of pya.Points, e.g. L=15/dbu pts = [Point(0,0), Point(L,0), Point(L,L)] - radius: in Microns, e.g., 5 - adiab: 1 = Bezier curve, 0 = radial bend (arc) - bezier: the bezier parameter, between 0 and 0.45 (almost a radial bend) Note: bezier parameters need to be simulated and optimized, and will depend on wavelength, polarization, width, etc. TM and rib waveguides don't benefit from bezier curves most useful for TE ''' from SiEPIC.utils import arc_xy, arc_bezier, angle_vector, angle_b_vectors, inner_angle_b_vectors, translate_from_normal from SiEPIC.extend import to_itype from pya import Path, Polygon, Trans dbu = layout.dbu width=widths[0] turn=0 waveguide_length = 0 for lr in range(0, len(layers)): wg_pts = [pts[0]] layer = layout.layer(TECHNOLOGY[layers[lr]]) width = to_itype(widths[lr],dbu) offset = to_itype(offsets[lr],dbu) for i in range(1,len(pts)-1): turn = ((angle_b_vectors(pts[i]-pts[i-1],pts[i+1]-pts[i])+90)%360-90)/90 dis1 = pts[i].distance(pts[i-1]) dis2 = pts[i].distance(pts[i+1]) angle = angle_vector(pts[i]-pts[i-1])/90 pt_radius = to_itype(radius,dbu) # determine the radius, based on how much space is available if len(pts)==3: pt_radius = min (dis1, dis2, pt_radius) else: if i==1: if dis1 <= pt_radius: pt_radius = dis1 elif dis1 < 2*pt_radius: pt_radius = dis1/2 if i==len(pts)-2: if dis2 <= pt_radius: pt_radius = dis2 elif dis2 < 2*pt_radius: pt_radius = dis2/2 # waveguide bends: if abs(turn)==1: if(adiab): wg_pts += Path(arc_bezier(pt_radius, 270, 270 + inner_angle_b_vectors(pts[i-1]-pts[i], pts[i+1]-pts[i]), bezier, DevRec='DevRec' in layers[lr]), 0).transformed(Trans(angle, turn < 0, pts[i])).get_points() else: wg_pts += Path(arc_xy(-pt_radius, pt_radius, pt_radius, 270, 270 + inner_angle_b_vectors(pts[i-1]-pts[i], pts[i+1]-pts[i]),DevRec='DevRec' in layers[lr]), 0).transformed(Trans(angle, turn < 0, pts[i])).get_points() wg_pts += [pts[-1]] wg_pts = pya.Path(wg_pts, 0).unique_points().get_points() wg_polygon = Polygon(translate_from_normal(wg_pts, width/2 + (offset if turn > 0 else - offset))+translate_from_normal(wg_pts, -width/2 + (offset if turn > 0 else - offset))[::-1]) cell.shapes(layer).insert(wg_polygon) if layout.layer(TECHNOLOGY['Waveguide']) == layer: waveguide_length = wg_polygon.area() / width * dbu return waveguide_length
def wireguide_to_path(cell=None): from . import _globals from .utils import get_layout_variables if cell is None: TECHNOLOGY, lv, ly, cell = get_layout_variables() else: TECHNOLOGY, lv, _, _ = get_layout_variables() ly = cell.layout() lv.transaction("wireguide to path") # record objects to delete: to_delete = [] wireguides = select_wireguides(cell) selection = [] for obj in wireguides: # path from wireguide guiding shape wireguide = obj.inst() layer_list = wireguide.cell.pcell_parameter( 'layers' ) # get the list of layers in the wireguide pcell (should only be one) original_layer = ly.layer( TECHNOLOGY[layer_list[0]] ) # convert layer to understandable type for future functions from ._globals import KLAYOUT_VERSION if KLAYOUT_VERSION > 24: path = wireguide.cell.pcell_parameters_by_name()['path'] else: # wireguide path and width from Wireguide PCell path1 = wireguide.cell.pcell_parameters_by_name()['path'] path = pya.Path() path.width = wireguide.cell.pcell_parameters_by_name( )['width'] / TECHNOLOGY['dbu'] pts = [] for pt in [pt1 for pt1 in (path1).each_point()]: if type(pt) == pya.Point: # for instantiated PCell pts.append(pya.Point()) else: # for wireguide from path pts.append(pya.Point().from_dpoint( pt * (1 / TECHNOLOGY['dbu']))) path.points = pts selection.append(pya.ObjectInstPath()) selection[-1].layer = original_layer # DPath.transformed requires DTrans. wireguide.trans is a Trans object if KLAYOUT_VERSION > 24: selection[-1].shape = cell.shapes(original_layer).insert( path.transformed(wireguide.trans.to_dtype(TECHNOLOGY['dbu']))) else: selection[-1].shape = cell.shapes(original_layer).insert( path.transformed( pya.Trans(wireguide.trans.disp.x, wireguide.trans.disp.y))) selection[-1].top = obj.top selection[-1].cv_index = obj.cv_index # deleting the instance was ok, but would leave the cell which ends up as # an uninstantiated top cell to_delete.append(obj.inst()) # deleting instance or cell should be done outside of the for loop, # otherwise each deletion changes the instance pointers in KLayout's # internal structure [t.delete() for t in to_delete] # Clear the layout view selection, since we deleted some objects (but # others may still be selected): lv.clear_object_selection() # Select the newly added objects lv.object_selection = selection # Record a transaction, to enable "undo" lv.commit()
def produce_impl(self): ly = self.layout LayerSiN = ly.layer(self.layer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) # 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) # print("Waveguide_Straight:") w = self.wg_width length = self.wg_length points = [[-length / 2, 0], [length / 2, 0]] path = Path([Point(-length / 2, 0), Point(length / 2, 0)], w) # print(path) shapes(LayerSiN).insert(path.simple_polygon()) from SiEPIC._globals import PIN_LENGTH # Pins on the bus waveguide side: pin_length = PIN_LENGTH if length < pin_length + 1: pin_length = int(length / 3) pin_length = math.ceil(pin_length / 2.) * 2 if pin_length == 0: pin_length = 2 t = Trans(Trans.R0, -length / 2, 0) pin = pya.Path([Point(pin_length / 2, 0), Point(-pin_length / 2, 0)], w) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin1", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu t = Trans(Trans.R0, length / 2, 0) pin = pya.Path([Point(-pin_length / 2, 0), Point(pin_length / 2, 0)], w) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin2", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu shape.text_halign = 2 # Compact model information t = Trans(Trans.R0, 0, 0) text = Text('Lumerical_INTERCONNECT_library=Design kits/EBeam', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, length / 10, 0) text = Text('Lumerical_INTERCONNECT_component=ebeam_wg_integral_1550', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, length / 9, 0) text = Text( 'Spice_param:wg_width=%.3fu wg_length=%.3fu' % (self.wg_width * dbu, self.wg_length * dbu), t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu # Create the device recognition layer -- make it 1 * wg_width away from the waveguides. path = Path([Point(-length / 2, 0), Point(length / 2, 0)], w * 3) shapes(LayerDevRecN).insert(path.simple_polygon())
def produce_impl(self): # fetch the parameters dbu = self.layout.dbu ly = self.layout LayerSi = self.layer LayerSiN = ly.layer(self.layer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) LayerTextN = ly.layer(self.textl) LayerInvert=ly.layer(self.invert) n_vertices = int(self.vertices) a = self.a/dbu r = self.r/dbu n_x = int(math.ceil(self.x/2)) n_y = int(math.ceil(self.y/2)) positive=bool(self.positive) minimum_feature=self.feature_size apodized=bool(self.apodized) length_slab_x = 2*n_x*a length_slab_y = 2*(n_y-2)*a k = 2 #function to creat polygon pts for right half of a hole in a hexagon unit cell def circle(x,y,r): npts = n_vertices theta = 2 * math.pi / npts # increment, in radians pts = [] for i in range(0, npts): pts.append(Point.from_dpoint(pya.DPoint((x+r*math.cos(i*theta))/1, (y+r*math.sin(i*theta))/1))) return pts def hexagon_hole_half(a,r): npts = 10 theta_div = math.pi/3 theta_div_hole = math.pi/npts triangle_length = a/math.sqrt(3) pts = [] for i in range(0,4): pts.append(Point.from_dpoint(pya.DPoint(triangle_length*math.cos(i*theta_div-math.pi/2), triangle_length*math.sin(i*theta_div-math.pi/2)))) for i in range(0, npts+1): pts.append(Point.from_dpoint(pya.DPoint(r*math.cos(math.pi/2-i*theta_div_hole), r*math.sin(math.pi/2-i*theta_div_hole)))) return pts def hexagon_shifthole_half(a,r): npts = 10 theta_div = math.pi/3 theta_div_hole = math.pi/npts triangle_length = a*1.235/math.sqrt(3) pts = [] for i in range(0,4): pts.append(Point.from_dpoint(pya.DPoint(triangle_length*math.cos(i*theta_div-math.pi/2), triangle_length*math.sin(i*theta_div-math.pi/2)))) for i in range(0, npts+1): pts.append(Point.from_dpoint(pya.DPoint(r*math.cos(math.pi/2-i*theta_div_hole), r*math.sin(math.pi/2-i*theta_div_hole)))) return pts #function to creat polygon pts for right half of a hexagon unit cell def hexagon_half(a): theta_div = math.pi/3 triangle_length = a/math.sqrt(3) pts = [] for i in range(0,4): pts.append(Point.from_dpoint(pya.DPoint(triangle_length*math.cos(i*theta_div-math.pi/2), triangle_length*math.sin(i*theta_div-math.pi/2)))) return pts #create the right and left half of the hole and hexagon cells #hole_cell = pya.Region() #hexagon_cell = pya.Region() # Define Si slab and hole region for future subtraction Si_slab = pya.Region() Si_slab.insert(pya.Box(-length_slab_x/2+a*2, -length_slab_y/2, length_slab_x/2-a, length_slab_y/2)) hole = pya.Region() hole_r = r # function to generate points to create a circle def circle(x,y,r): npts = n_vertices theta = 2 * math.pi / npts # increment, in radians pts = [] for i in range(0, npts): pts.append(Point.from_dpoint(pya.DPoint((x+r*math.cos(i*theta))/1, (y+r*math.sin(i*theta))/1))) return pts import numpy as np def gaussian(x, mu, sig): return 1./(np.sqrt(2.*np.pi)*sig)*np.exp(-np.power((x - mu)/sig, 2.)/2) # raster through all holes with shifts and waveguide for j in range(-n_y,n_y+1): if j%2 == 0: skip=0 apodization=0 for i in range(-n_x,n_x+1): if i==0: continue if skip==0: skip=1 continue elif skip==1: skip=2 continue elif skip==2: skip=3 apodization=apodization+1 continue elif skip==3: skip=1 #radius=r/gaussian(float(i)/(2*(n_x+1)),2*(n_x+1),10) location=float(i) location=abs(location) #radius=r/gaussian(location/(2*(n_x+1)),2*(n_x+1),0.02) #radius=(((2*n_x)-abs(i+n_x+3)*r)/(2*n_x)) radius=(float(apodization)/((n_x*2/3)-1))*r if radius<minimum_feature*500: radius=minimum_feature*500 if apodized==False: radius=r #radius=minimum_feature hole_cell = circle(0,0,radius) hole_poly = pya.Polygon(hole_cell) print("x1 "+str(apodization)) hole_x = abs(i)/i*(abs(i)-0.5)*a hole_y = j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) hole_t = hole_poly.transformed(hole_trans) hole.insert(hole_t) elif j%2 == 1: skipodd=0 apodization=0 for i in range(-n_x,n_x+1): if i==-n_x: continue if i==n_x: continue if skipodd==0: skipodd=1 continue elif skipodd==1: skipodd=2 apodization=apodization+1 continue elif skipodd==2: skipodd=3 elif skipodd==3: skipodd=1 #radius=(((2*n_x)-abs(i+n_x+3)*r)/(2*n_x)) radius=(float(apodization)/((n_x*2/3)-1))*r if radius<minimum_feature*500: radius=minimum_feature*500 if apodized==False: radius=r #radius=minimum_feature #radius=r*(float(abs(i))/(2*(n_x+1))) hole_cell = circle(0,0,radius) hole_poly = pya.Polygon(hole_cell) print("x2 "+str(apodization)) print("p "+str(n_x)) hole_x = i*a hole_y = j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) hole_t = hole_poly.transformed(hole_trans) hole.insert(hole_t) if positive==True: phc = hole else: phc = Si_slab - hole self.cell.shapes(LayerSiN).insert(phc) #print(hole_t_0) box_l = a/2 # Pins on the waveguide: pin_length = 200 pin_w = a t = pya.Trans(Trans.R0, -length_slab_x/2+a*2,0) pin = pya.Path([pya.Point(-pin_length/2, 0), pya.Point(pin_length/2, 0)], pin_w) pin_t = pin.transformed(t) self.cell.shapes(LayerPinRecN).insert(pin_t) text = pya.Text ("pin1", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = 0.4/dbu # Create the device recognition layer -- make it 1 * wg_width away from the waveguides. points = [[-length_slab_x/2+a*2,0], [length_slab_x/2-a, 0]] points = [Point(each[0], each[1]) for each in points] path = Path(points, length_slab_y) self.cell.shapes(LayerDevRecN).insert(path.simple_polygon()) if positive==True: self.cell.shapes(LayerInvert).insert(path.simple_polygon()) return hole = pya.Region() hole_cell_pts = hexagon_hole_half(a,r) hexagon_pts = hexagon_half(a) hole_shiftcell_pts = hexagon_shifthole_half(a,r) hole_cell_poly_0 = pya.Polygon(hole_cell_pts) hexagon_cell_poly_0 = pya.Polygon(hexagon_pts) hole_shiftcell_poly_0 = pya.Polygon(hole_shiftcell_pts) hole_trans = pya.Trans(pya.Trans.R180) hole_cell_poly_1 = hole_cell_poly_0.transformed(hole_trans) hexagon_cell_poly_1 = hexagon_cell_poly_0.transformed(hole_trans) hole_shiftcell_poly_1 = hole_shiftcell_poly_0.transformed(hole_trans) skip=1 skip2=0 #create the photonic crystal with shifts and waveguides for j in range(-y+1,y): if j%2 == 0: for i in range(-x,x+1): #waveguide if (j == k and i > 3): hole_x = abs(i)/i*(abs(i)-0.5)*a hole_y = j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) hole_t_0 = hole_cell_poly_0.transformed(hole_trans) hole_t_1 = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) #filling the edges with half cell elif i!=0: hole_x = abs(i)/i*(abs(i)-0.5)*a hole_y = j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) hole_t_0 = hole_cell_poly_0.transformed(hole_trans) hole_t_1 = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) elif j%2 == 1: for i in range(-x,x+1): if skip==0 and i%3==0: skip=1 continue if skip==1: skip=2 continue if skip==2: skip=0 if i== -x: hole_x = abs(i)/i*(abs(i)-0.5)*a hole_y = j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) hole_t_1 = hexagon_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_1) if i== x: hole_x = abs(i)/i*(abs(i)-0.5)*a hole_y = j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) hole_t_0 = hexagon_cell_poly_0.transformed(hole_trans) hole.insert(hole_t_0) #waveguide if (j == k and i > 3): hole_x =i*a hole_y =j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) hole_t_0 = hexagon_cell_poly_0.transformed(hole_trans) hole_t_1 = hexagon_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) #filling the edges with half cell elif i in (-x,x): hole_x =i*a hole_y =j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) if i == -x: hole_t = hole_cell_poly_0.transformed(hole_trans) else: hole_t = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t) else: hole_x = i*a hole_y = j*a*math.sqrt(3)/2 hole_trans = pya.Trans(Trans.R0, hole_x,hole_y) hole_t_0 = hole_cell_poly_0.transformed(hole_trans) hole_t_1 = hole_cell_poly_1.transformed(hole_trans) hole.insert(hole_t_0) hole.insert(hole_t_1) #print(hole_t_0) box_l = a/2 cover_box = pya.Box(-length_slab_x/2, -a/2, length_slab_x/2, a/2) box_y = y*a*math.sqrt(3)/2 cover_box_trans_0 = pya.Trans(Trans.R0, 0,box_y) cover_box_trans_1 = pya.Trans(Trans.R0, 0,-box_y) cover_box_t_0 = cover_box.transformed(cover_box_trans_0) cover_box_t_1 = cover_box.transformed(cover_box_trans_1) #hole.insert(pya.Box()) self.cell.shapes(LayerSiN).insert(hole) self.cell.shapes(LayerSiN).insert(cover_box_t_0) self.cell.shapes(LayerSiN).insert(cover_box_t_1) # Pins on the waveguide: pin_length = 200 pin_w = a t = pya.Trans(Trans.R0, -length_slab_x/2,0) pin = pya.Path([pya.Point(-pin_length/2, 0), pya.Point(pin_length/2, 0)], pin_w) pin_t = pin.transformed(t) self.cell.shapes(LayerPinRecN).insert(pin_t) text = pya.Text ("pin1", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = 0.4/dbu # Create the device recognition layer -- make it 1 * wg_width away from the waveguides. points = [[-length_slab_x/2,0], [length_slab_x/2, 0]] points = [Point(each[0], each[1]) for each in points] path = Path(points, length_slab_y) self.cell.shapes(LayerDevRecN).insert(path.simple_polygon())
def produce_impl(self): # fetch the parameters dbu = self.layout.dbu ly = self.layout LayerSi = self.layer LayerSiN = ly.layer(self.layer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) LayerTextN = ly.layer(self.textl) # Fetch all the parameters: a = self.a / dbu r = self.r / dbu wg_dis = self.wg_dis + 1 n_vertices = self.n_vertices n_bus = self.n_bus n = int(math.ceil(self.n / 2)) Sx = [self.S1x, self.S2x, self.S3x, self.S4x, self.S5x] Sy = [self.S1y, 0, self.S2y] if n_bus == 1: Sx = [0, 0, 0, 0, 0] Sy = [0, 0, 0] if wg_dis % 2 == 0: length_slab_x = (2 * n - 1) * a else: length_slab_x = 2 * n * a length_slab_y = 2 * (wg_dis + 15) * a * math.sqrt(3) / 2 n_x = n n_y = wg_dis + 10 # Define Si slab and hole region for future subtraction Si_slab = pya.Region() Si_slab.insert( pya.Box(-length_slab_x / 2, -length_slab_y / 2, length_slab_x / 2, length_slab_y / 2)) hole = pya.Region() hole_r = r # function to generate points to create a circle def circle(x, y, r): npts = n_vertices theta = 2 * math.pi / npts # increment, in radians pts = [] for i in range(0, npts): pts.append( Point.from_dpoint( pya.DPoint((x + r * math.cos(i * theta)) / 1, (y + r * math.sin(i * theta)) / 1))) return pts # raster through all holes with shifts and waveguide hole_cell = circle(0, 0, hole_r) hole_poly = pya.Polygon(hole_cell) for j in range(-n_y, n_y + 1): if j % 2 == 0 and j != wg_dis: for i in range(-n_x, n_x + 1): if j == -wg_dis and i > 3 and n_bus == 2: None elif j == 0 and i in (1, -1, 2, -2, 3, -3, 4, -4, 5, -5): hole_x = abs(i) / i * (abs(i) - 0.5 + Sx[abs(i) - 1]) * a hole_y = 0 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t = hole_poly.transformed(hole_trans) hole.insert(hole_t) elif i != 0: hole_x = abs(i) / i * (abs(i) - 0.5) * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t = hole_poly.transformed(hole_trans) hole.insert(hole_t) elif j % 2 == 1 and j != wg_dis: for i in range(-n_x, n_x + 1): if j == -wg_dis and i > 3 and n_bus == 2: None elif i == 0 and j in (1, -1, 3, -3): hole_x = 0 hole_y = j * a * (math.sqrt(3) / 2) + abs(j) / j * a * Sy[abs(j) - 1] hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t = hole_poly.transformed(hole_trans) hole.insert(hole_t) else: hole_x = i * a hole_y = j * a * math.sqrt(3) / 2 hole_trans = pya.Trans(Trans.R0, hole_x, hole_y) hole_t = hole_poly.transformed(hole_trans) hole.insert(hole_t) phc = Si_slab - hole self.cell.shapes(LayerSiN).insert(phc) # Pins on the waveguide: pin_length = 200 pin_w = a wg_pos = a * math.sqrt(3) / 2 * wg_dis t = pya.Trans(Trans.R0, -length_slab_x / 2, wg_pos) pin = pya.Path( [pya.Point(pin_length / 2, 0), pya.Point(-pin_length / 2, 0)], pin_w) pin_t = pin.transformed(t) self.cell.shapes(LayerPinRecN).insert(pin_t) text = pya.Text("pin1", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu t = pya.Trans(Trans.R0, length_slab_x / 2, wg_pos) pin = pya.Path( [pya.Point(-pin_length / 2, 0), pya.Point(pin_length / 2, 0)], pin_w) pin_t = pin.transformed(t) self.cell.shapes(LayerPinRecN).insert(pin_t) text = pya.Text("pin2", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu #pin for drop waveguide if n_bus == 2: t = pya.Trans(Trans.R0, length_slab_x / 2, -wg_pos) pin_t = pin.transformed(t) self.cell.shapes(LayerPinRecN).insert(pin_t) text = pya.Text("pin3", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu # Create the device recognition layer -- make it 1 * wg_width away from the waveguides. points = [[-length_slab_x / 2, 0], [length_slab_x / 2, 0]] points = [Point(each[0], each[1]) for each in points] path = Path(points, length_slab_y) self.cell.shapes(LayerDevRecN).insert(path.simple_polygon())
def test_3_DTrans(self): c = pya.DCplxTrans( 5.0, -7.0 ) self.assertEqual( str(c), "r0 *1 5,-7" ) c = pya.DCplxTrans( pya.DCplxTrans.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.DCplxTrans.M135.rot() ) self.assertEqual( str(c.s_trans()), "m135 0,0" ) self.assertAlmostEqual( c.angle, 270 ) self.assertEqual( str(c.trans( pya.Edge(0, 1, 2, 3) )), "(-1,0;-3,-2)" ) self.assertEqual( str(( c * pya.Edge(0, 1, 2, 3) )), "(-1,0;-3,-2)" ) self.assertEqual( str(c.trans( pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(( c * pya.Box(0, 1, 2, 3) )), "(-3,-2;-1,0)" ) self.assertEqual( str(c.trans( pya.Text("text", pya.Vector(0, 1)) )), "('text',m135 -1,0)" ) self.assertEqual( str(( c * pya.Text("text", pya.Vector(0, 1)) )), "('text',m135 -1,0)" ) self.assertEqual( str(c.trans( pya.Polygon( [ pya.Point(0, 1), pya.Point(2, -3), pya.Point(4, 5) ] ) )), "(-5,-4;-1,0;3,-2)" ) self.assertEqual( str(( c * pya.Polygon( [ pya.Point(0, 1), pya.Point(2, -3), pya.Point(4, 5) ] ) )), "(-5,-4;-1,0;3,-2)" ) self.assertEqual( str(c.trans( pya.Path( [ pya.Point(0, 1), pya.Point(2, 3) ], 10 ) )), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) self.assertEqual( str(( c * pya.Path( [ pya.Point(0, 1), pya.Point(2, 3) ], 10 ) )), "(-1,0;-3,-2) w=10 bx=0 ex=0 r=false" ) c = pya.DCplxTrans.from_itrans( pya.CplxTrans.M135 ) self.assertEqual( str(c), "m135 *1 0,0" ) c = pya.DCplxTrans( 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.DCplxTrans.R0.rot() ) self.assertEqual( str(c.s_trans()), "r0 0,0" ) self.assertAlmostEqual( c.angle, 0 ) c = pya.DCplxTrans( 0.75, 45, True, 2.5, -12.5 ) self.assertEqual( str(c), "m22.5 *0.75 2.5,-12.5" ) c = pya.DCplxTrans( 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.DCplxTrans.M0.rot() ) self.assertEqual( str(c.s_trans()), "m0 2.5,-12.5" ) self.assertAlmostEqual( c.angle, 45 ) self.assertEqual( str(c.ctrans( 5 )), "3.75" ) self.assertEqual( str(c.trans( pya.DPoint( 12, 16 ) )), "17.3492424049,-14.6213203436" ) self.assertEqual( str(pya.DCplxTrans()), "r0 *1 0,0" ) self.assertEqual( pya.DCplxTrans().is_unity(), True ) self.assertEqual( (c * c.inverted()).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" ) # Constructor variations self.assertEqual( str(pya.ICplxTrans()), "r0 *1 0,0" ) self.assertEqual( str(pya.ICplxTrans(1.5)), "r0 *1.5 0,0" ) self.assertEqual( str(pya.ICplxTrans(pya.Trans(1, False, 10, 20), 1.5)), "r90 *1.5 10,20" ) self.assertEqual( str(pya.ICplxTrans(pya.Trans(1, False, 10, 20))), "r90 *1 10,20" ) self.assertEqual( str(pya.ICplxTrans(1.5, 80, True, pya.Vector(100, 200))), "m40 *1.5 100,200" ) self.assertEqual( str(pya.ICplxTrans(1.5, 80, True, 100, 200)), "m40 *1.5 100,200" ) self.assertEqual( str(pya.ICplxTrans(pya.Vector(100, 200))), "r0 *1 100,200" ) self.assertEqual( str(pya.ICplxTrans(100, 200)), "r0 *1 100,200" ) self.assertEqual( str(pya.ICplxTrans(pya.ICplxTrans(100, 200))), "r0 *1 100,200" ) self.assertEqual( str(pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5)), "r0 *1.5 150,300" ) self.assertEqual( str(pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5, pya.Vector(10, 20))), "r0 *1.5 160,320" ) self.assertEqual( str(pya.ICplxTrans(pya.ICplxTrans(100, 200), 1.5, 10, 20)), "r0 *1.5 160,320" ) self.assertEqual( str(pya.DCplxTrans()), "r0 *1 0,0" ) self.assertEqual( str(pya.DCplxTrans(1.5)), "r0 *1.5 0,0" ) self.assertEqual( str(pya.DCplxTrans(pya.DTrans(1, False, 0.01, 0.02), 1.5)), "r90 *1.5 0.01,0.02" ) self.assertEqual( str(pya.DCplxTrans(pya.DTrans(1, False, 0.01, 0.02))), "r90 *1 0.01,0.02" ) self.assertEqual( str(pya.DCplxTrans(1.5, 80, True, pya.DVector(0.1, 0.2))), "m40 *1.5 0.1,0.2" ) self.assertEqual( str(pya.DCplxTrans(1.5, 80, True, 0.1, 0.2)), "m40 *1.5 0.1,0.2" ) self.assertEqual( str(pya.DCplxTrans(pya.DVector(0.1, 0.2))), "r0 *1 0.1,0.2" ) self.assertEqual( str(pya.DCplxTrans(0.1, 0.2)), "r0 *1 0.1,0.2" ) self.assertEqual( str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2))), "r0 *1 0.1,0.2" ) self.assertEqual( str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5)), "r0 *1.5 0.15,0.3" ) self.assertEqual( str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5, pya.DVector(0.01, 0.02))), "r0 *1.5 0.16,0.32" ) self.assertEqual( str(pya.DCplxTrans(pya.DCplxTrans(0.1, 0.2), 1.5, 0.01, 0.02)), "r0 *1.5 0.16,0.32" )