def produce_impl(self): from SiEPIC.utils import arc from SiEPIC.extend import to_itype dbu = self.layout.dbu layer = self.layout.layer(self.layer) radius = to_itype(self.radius, dbu) width = to_itype(self.width, dbu) poly = pya.Polygon(arc(radius + width / 2, 0, 360)) hole = pya.Polygon(arc(radius - width / 2, 0, 360)) poly.insert_hole(hole.get_points()) self.cell.shapes(layer).insert(poly)
def coerce_parameters_impl(self): from SiEPIC.extend import to_itype print("GSiP.Waveguide coerce parameters") if 0: TECHNOLOGY = get_technology_by_name('GSiP') dbu = self.layout.dbu wg_width = to_itype(self.width, dbu) for lr in range(0, len(self.layers)): layer = self.layout.layer(TECHNOLOGY[self.layers[lr]]) width = to_itype(self.widths[lr], dbu) # check to make sure that the waveguide with parameters are consistent in both places if self.layout.layer(TECHNOLOGY['Waveguide']) == layer: if width != wg_width: self.widths[lr] = self.width # check to make sure that the DevRec is bigger than the waveguide width if self.layout.layer(TECHNOLOGY['DevRec']) == layer: if width < wg_width: self.widths[lr] = self.width * 2
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 produce_impl(self): from SiEPIC._globals import PIN_LENGTH from SiEPIC.extend import to_itype from pya import DPolygon import math # This is the main part of the implementation: create the layout # 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) #variables pitch = self.pitch w = self.w wc = self.wc ff = self.ff #angle = self.angle gap = self.gap #gap2 = self.gap2 row = self.row Length = self.Length taperL = self.taperL phaseshifted = self.phaseshifted #print(doublebus) if Length <= 3 * pitch: Length = 3 * pitch print( 'invalid length of MultiBox Bragg grating, set length at least 3 times larger than the period of multibox blocks' ) s1 = pitch * ff #silicon s2 = pitch - s1 #gap # Draw the first Multi-box Waveguides #calulate ideal length of bus Bragg_length = Length #bus_length = self.cell.bbox().height()*dbu +pitch*2 constant = math.ceil(Bragg_length / (s1 + s2)) if Bragg_length % (s1 + s2) != 0: Bragg_length = constant * (s1 + s2) for ii in range(0, int(row)): xo = [(ii * (w + gap)), (w + ii * (w + gap)), (w + ii * (w + gap)), (ii * (w + gap))] yo = [0, 0, s1, s1] dpts = [pya.DPoint(xo[i], yo[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) # draw the SWG waveguide from the center one to +/- positions for i in range(0, int(math.ceil((constant) / 2))): yu = [yo[j] + i * pitch for j in range(len(yo))] yd = [yo[j] - i * pitch for j in range(len(yo))] dpts = [pya.DPoint(xo[i], yu[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) dpts = [pya.DPoint(xo[i], yd[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) # draw the tapers from waveguide to SWG if taperL != 0: xtu = [((ii * (w + gap)) + (w - 0.06) / 2), ((ii * (w + gap)) + (w - 0.06) / 2 + 0.06), ((ii * (w + gap)) + w + gap / 2), ((ii * (w + gap)) - gap / 2)] ytu = [(yu[3] - taperL), (yu[3] - taperL), (yu[3]), (yu[3])] dpts = [pya.DPoint(xtu[i], ytu[i]) for i in range(len(xtu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) ytd = [(yd[1] + taperL), (yd[1] + taperL), (yd[1]), (yd[1])] dpts = [pya.DPoint(xtu[i], ytd[i]) for i in range(len(xtu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) # taper connections from multi-tapers to std WG xTu = [(-gap / 2), (-gap / 2 + row * (gap + w)), ((row * (gap + w) - gap) / 2 + 0.25), ((row * (gap + w) - gap) / 2 - 0.25)] yTu = [(yu[3]), (yu[3]), (yu[3] + taperL), (yu[3] + taperL)] dpts = [pya.DPoint(xTu[i], yTu[i]) for i in range(len(xTu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) yTd = [(yd[1]), (yd[1]), (yd[1] - taperL), (yd[1] - taperL)] dpts = [pya.DPoint(xTu[i], yTd[i]) for i in range(len(xTu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) # f is the factor to define the Dev starting point f = 1 if taperL == 0: yTd = yd yTu = yu xTu = [0, 0, 0, 0] f = 0 # draw the corrugations of the Bragg gratings constant_c = math.ceil((Bragg_length - taperL) / (s1 + s2)) for iii in range(0, int(math.ceil(constant_c / 4))): yuc = [yo[j] + 2 * iii * pitch for j in range(len(yo))] ydc = [yo[j] - 2 * iii * pitch for j in range(len(yo))] xlc = [-wc - s2, -s2, -s2, -wc - s2] xrc = [ row * pitch, row * pitch + wc, row * pitch + wc, row * pitch ] dpts = [pya.DPoint(xlc[i], yuc[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) dpts = [pya.DPoint(xrc[i], yuc[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) if phaseshifted is False: for iii in range(0, int(math.ceil(constant_c / 4))): #yuc = [yo[j]+2*iii*pitch for j in range(len(yo))] ydc = [yo[j] - 2 * iii * pitch for j in range(len(yo))] #xlc = [-wc-s2, -s2, -s2,-wc-s2] #xrc = [row*pitch, row*pitch+wc, row*pitch+wc, row*pitch] dpts = [pya.DPoint(xlc[i], ydc[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) dpts = [pya.DPoint(xrc[i], ydc[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) if phaseshifted is True: for iii in range(0, int(math.ceil(constant_c / 4))): #yuc = [yo[j]+2*iii*pitch for j in range(len(yo))] yddc = [ yo[j] - pitch - 2 * iii * pitch for j in range(len(yo)) ] dpts = [pya.DPoint(xlc[i], yddc[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) dpts = [pya.DPoint(xrc[i], yddc[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) #DEV BOX dev = Box(f * (-gap - wc - (2) * pitch) / dbu, yTd[-1] / dbu, (wc + (row + 2) * pitch) / dbu, yTu[-1] / dbu) shapes(LayerDevRecN).insert(dev) dev_width = self.cell.bbox().width() / 2 dev_up = yu[-1] / dbu + taperL / dbu dev_down = yd[-4] / dbu - taperL / dbu # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.w, dbu) gap = to_itype(self.gap, dbu) #Pin1 t = Trans(Trans.R0, xTu[-1] / dbu + 250, dev_up) pin = Path([Point(0, -pin_length / 2), Point(0, pin_length / 2)], 500) #500 is width of std WG pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) t = Trans(Trans.R270, xTu[-1] / dbu + 250, dev_up) text = Text("pin1", t) text.halign = 0 shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu #Pin2 t = Trans(Trans.R0, xTu[-1] / dbu + 250, dev_down) pin = Path([Point(0, pin_length / 2), Point(0, -pin_length / 2)], 500) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) t = Trans(Trans.R90, xTu[-1] / dbu + 250, dev_down) text = Text("pin2", t) text.halign = 0 shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu # Ref t = Trans(Trans.R270, xTu[-1] / dbu + 250 + 0.5 / dbu, dev_up) text = Text("Ref: E. Luan, doi.org/10.1364/BOE.10.004825", t) text.halign = 0 shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu
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) from SiEPIC.extend import to_itype # Draw the Bragg grating: box_width = self.grating_period / 2 / dbu grating_period = self.grating_period / dbu w = to_itype(self.wg_width, dbu) half_w = w / 2 half_corrugation_w = self.corrugation_width / 2 / dbu misalignment = int(self.misalignment / dbu) if self.sinusoidal: npoints_sin = 40 for i in range(0, self.number_of_periods): x = ((i * self.grating_period) / dbu) box1 = Box(x, 0, x + box_width, half_w + half_corrugation_w) pts1 = [Point(x, 0)] pts3 = [Point(x + misalignment, 0)] for i1 in range(0, npoints_sin + 1): x1 = i1 * 2 * math.pi / npoints_sin y1 = half_corrugation_w * math.sin(x1) x1 = x1 / 2 / math.pi * grating_period # print("x: %s, y: %s" % (x1,y1)) pts1.append(Point(x + x1, half_w + y1)) pts3.append(Point(x + misalignment + x1, -half_w - y1)) pts1.append(Point(x + grating_period, 0)) pts3.append(Point(x + grating_period + misalignment, 0)) shapes(LayerSiN).insert(Polygon(pts1)) shapes(LayerSiN).insert(Polygon(pts3)) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, 0, length, half_w) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment, -half_w) shapes(LayerSiN).insert(box3) else: for i in range(0, self.number_of_periods): x = (i * self.grating_period) / dbu box1 = Box(x, 0, x + box_width, half_w + half_corrugation_w) box2 = Box(x + box_width, 0, x + grating_period, half_w - half_corrugation_w) box3 = Box(x + misalignment, 0, x + box_width + misalignment, -half_w - half_corrugation_w) box4 = Box(x + box_width + misalignment, 0, x + grating_period + misalignment, -half_w + half_corrugation_w) shapes(LayerSiN).insert(box1) shapes(LayerSiN).insert(box2) shapes(LayerSiN).insert(box3) shapes(LayerSiN).insert(box4) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, 0, length, half_w) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment, -half_w) shapes(LayerSiN).insert(box3) # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length 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', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, length / 10, 0) text = Text('Component=ebeam_bragg_te1550', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, length / 9, 0) text = Text \ ('Spice_param:number_of_periods=%s grating_period=%.3g corrugation_width=%.3g misalignment=%.3g sinusoidal=%s' %\ (self.number_of_periods, self.grating_period*1e-6, self.corrugation_width*1e-6, self.misalignment*1e-6, int(self.sinusoidal)), 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. t = Trans(Trans.R0, 0, 0) path = Path([Point(0, 0), Point(length, 0)], 3 * w) shapes(LayerDevRecN).insert(path.simple_polygon())
def produce_impl(self): import math # 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) from SiEPIC.extend import to_itype # Draw the Bragg grating (bottom): width_period1 = to_itype(self.grating_period1 / 2, dbu) width_period2 = to_itype(self.grating_period2 / 2, dbu) grating_period1 = to_itype(self.grating_period1, dbu) grating_period2 = to_itype(self.grating_period2, dbu) w1 = to_itype(self.wg1_width, dbu) w2 = to_itype(self.wg2_width, dbu) GaussianIndex = self.index N1 = self.number_of_periods1 vertical_offset1 = to_itype(self.gap / 2 + self.wg1_width / 2, dbu) vertical_offset2 = to_itype(-self.gap / 2 - self.wg2_width / 2, dbu) for i in range(0, self.number_of_periods1): x1 = to_itype(i * self.grating_period1, dbu) x2 = to_itype(i * self.grating_period2, dbu) profileFunction = math.exp(-0.5 * (2 * GaussianIndex * (i - N1 / 2) / (N1))**2) profile_outer = to_itype(self.corrugation_width1outer, dbu) * profileFunction profile_inner = to_itype(self.corrugation_width1inner, dbu) * profileFunction box1outer = Box(x1, vertical_offset1 + w1 / 2 - profile_outer / 2, x1 + width_period1, vertical_offset1) box2outer = Box(x1 + width_period1, vertical_offset1 + w1 / 2 + profile_outer / 2, x1 + 2 * width_period1, vertical_offset1) box1inner = Box(x2, vertical_offset1 - w1 / 2 - profile_inner / 2, x2 + width_period2, vertical_offset1) box2inner = Box(x2 + width_period2, vertical_offset1 - w1 / 2 + profile_inner / 2, x2 + 2 * width_period2, vertical_offset1) shapes(LayerSiN).insert(box1outer) shapes(LayerSiN).insert(box2outer) shapes(LayerSiN).insert(box1inner) shapes(LayerSiN).insert(box2inner) length1 = x1 + grating_period1 length2 = x2 + grating_period2 if grating_period1 > grating_period2: box_inner = Box(length2, vertical_offset1, length1, vertical_offset1 - w1 / 2) shapes(LayerSiN).insert(box_inner) length = x1 + grating_period1 else: box_outer = Box(length1, vertical_offset1, length2, vertical_offset1 + w1 / 2) shapes(LayerSiN).insert(box_outer) length = x2 + grating_period2 # lower waveguide for i in range(0, self.number_of_periods1): x1 = to_itype(i * self.grating_period1, dbu) x2 = to_itype(i * self.grating_period2, dbu) profileFunction = math.exp(-0.5 * (2 * GaussianIndex * (i - N1 / 2) / (N1))**2) profile_outer = to_itype(self.corrugation_width2outer, dbu) * profileFunction profile_inner = to_itype(self.corrugation_width2inner, dbu) * profileFunction box1outer = Box(x2, vertical_offset2 + w2 / 2 + profile_outer / 2, x2 + width_period2, vertical_offset2) box2outer = Box(x2 + width_period2, vertical_offset2 + w2 / 2 - profile_outer / 2, x2 + 2 * width_period2, vertical_offset2) box1inner = Box(x1, vertical_offset2 - w2 / 2 + profile_inner / 2, x1 + width_period1, vertical_offset2) box2inner = Box(x1 + width_period1, vertical_offset2 - w2 / 2 - profile_inner / 2, x1 + 2 * width_period1, vertical_offset2) shapes(LayerSiN).insert(box1outer) shapes(LayerSiN).insert(box2outer) shapes(LayerSiN).insert(box1inner) shapes(LayerSiN).insert(box2inner) length1 = x1 + grating_period1 length2 = x2 + grating_period2 if grating_period1 > grating_period2: box_inner = Box(length2, vertical_offset2, length1, vertical_offset2 + w2 / 2) shapes(LayerSiN).insert(box_inner) length = x1 + grating_period1 else: box_outer = Box(length1, vertical_offset2, length2, vertical_offset2 - w2 / 2) shapes(LayerSiN).insert(box_outer) length = x2 + grating_period2 # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.wg2_width, dbu) t = Trans(Trans.R0, 0, vertical_offset2) 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 w = to_itype(self.wg1_width, dbu) t = Trans(Trans.R0, 0, vertical_offset1) 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("pin3", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu w = to_itype(self.wg1_width, dbu) t = Trans(Trans.R0, length, vertical_offset1) 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 w = to_itype(self.wg2_width, dbu) t = Trans(Trans.R0, length, vertical_offset2) 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("pin4", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu # 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('Component=ebeam_contra_dc', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, length / 9, 0) text = Text \ ('Spice_param:number_of_periods=%s' %\ (self.number_of_periods1), 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. t = Trans(Trans.R0, 0, 0) path = Path([Point(0, 0), Point(length, 0)], 3 * w) shapes(LayerDevRecN).insert(path.simple_polygon())
def produce_impl(self): from SiEPIC.utils.layout import layout_waveguide2 from SiEPIC.utils import angle_vector from math import cos, sin, pi, sqrt import pya from SiEPIC.extend import to_itype # print("GSiP.Waveguide") from SiEPIC.utils import get_technology_by_name TECHNOLOGY = get_technology_by_name('GSiP') 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() pts = path.get_points() # Draw the waveguide geometry, new in SiEPIC-Tools v0.3.64 waveguide_length = layout_waveguide2(TECHNOLOGY, self.layout, self.cell, self.layers, self.widths, self.offsets, pts, self.radius, self.adiab, self.bezier) pts = path.get_points() LayerPinRecN = self.layout.layer(TECHNOLOGY['PinRec']) 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 if angle_vec == 0: # horizontal halign = 2 # right angle = 0 dpt = Point(0, 0.2 * wg_width) if angle_vec == 2: # horizontal halign = 0 # left angle = 0 dpt = Point(0, 0.2 * wg_width) if angle_vec == 1: # vertical halign = 2 # right angle = 1 dpt = Point(0.2 * wg_width, 0) if angle_vec == -1: # vertical halign = 0 # left angle = 1 dpt = Point(0.2 * wg_width, 0) pt2 = pts[0] + dpt pt3 = pts[0] - dpt pt4 = pts[0] - 2 * dpt pt5 = pts[0] + 2 * dpt t = Trans(angle, False, pt5) text = Text('cellName=%s' % self.cellName, t, 0.1 * wg_width, -1) text.halign = halign shape = self.cell.shapes(LayerDevRecN).insert(text) t = Trans(angle, False, pt3) text = Text('Lumerical_INTERCONNECT_library=Design kits/%s' % self.CML, t, 0.1 * wg_width, -1) text.halign = halign shape = self.cell.shapes(LayerDevRecN).insert(text) t = Trans(angle, False, pt2) text = Text('Component=%s' % self.model, t, 0.1 * wg_width, -1) text.halign = halign shape = self.cell.shapes(LayerDevRecN).insert(text) t = Trans(angle, False, pts[0]) pts_txt = str( [[round(p.to_dtype(dbu).x, 3), round(p.to_dtype(dbu).y, 3)] for p in pts]).replace(', ', ',') text = Text ( \ 'Spice_param:wg_length=%.3fu wg_width=%.3fu points="%s" radius=%s' %\ (waveguide_length, self.width, pts_txt,self.radius ), t, 0.1*wg_width, -1 ) text.halign = halign shape = self.cell.shapes(LayerDevRecN).insert(text) t = Trans(angle, False, pt4) text = Text ( \ 'Length=%.3fu' %(waveguide_length), t, 0.5*wg_width, -1 ) text.halign = halign shape = self.cell.shapes(LayerDevRecN).insert(text)
def produce_impl(self): # fetch the parameters dbu = self.layout.dbu ly = self.layout shapes = self.cell.shapes LayerSiN = ly.layer(self.waveguide) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) LayerTextN = ly.layer(self.text) from math import pi, cos, sin, log, sqrt from SiEPIC.utils import arc, arc_to_waveguide, points_per_circle, arc_wg x = 0 y = 0 theta = self.theta # 2*pi*r*(4*theta/360) = length + self.delta_length from SiEPIC.extend import to_itype w = to_itype(self.wg_width,dbu) length = to_itype(self.length,dbu) r = length/4/sin(theta/180.0*pi) waveguide_length = 2*pi*r*(4*theta/360.0) #arc_to_waveguide(pts, width): #arc(radius, start, stop) t = Trans(Trans.R0,x, round(y+r)) self.cell.shapes(LayerSiN).insert(arc_wg(r, w, 270., 270.+theta).transformed(t)) t = Trans(Trans.R0,round(x+length/2), round(y-r+ 2*r*(1-cos(theta/180.0*pi)))) self.cell.shapes(LayerSiN).insert(arc_wg(r, w, 90.-theta, 90.+theta).transformed(t)) t = Trans(Trans.R0,round(x+length), round(y+r)) self.cell.shapes(LayerSiN).insert(arc_wg(r, w, 270.-theta, 270).transformed(t)) # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length x = self.length / dbu t = Trans(Trans.R0, x,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 x = 0 t = Trans(Trans.R0, x,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 # 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, 0, w*2) text = Text ('Component=ebeam_wg_integral_1550', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1/dbu t = Trans(Trans.R0, 0, -w*2) text = Text \ ('Spice_param:wg_length=%.3fu wg_width=%.3fu' %\ (waveguide_length*dbu, self.wg_width), t ) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1/dbu t = Trans(Trans.R0, self.length /6, -w*2) text = Text ('dL = %.4f um' % ((waveguide_length-length)*dbu), t) shape = shapes(LayerTextN).insert(text) shape.text_size = 0.6/dbu # Create the device recognition layer -- make it 1 * wg_width away from the waveguides. box1 = Box(0, -w*3, length, w*3+(2*r*(1-cos(theta/180.0*pi)))) shapes(LayerDevRecN).insert(box1) print("SiEPIC EBeam: Waveguide_bump complete.")
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._globals import PIN_LENGTH from SiEPIC.extend import to_itype import math from pya import DPolygon # This is the main part of the implementation: create the layout # 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) #variables pitch = self.pitch w = self.w r = self.r ff = self.ff angle = self.angle gap = self.gap W_ratio = self.W_ratio doublebus = self.doublebus print(doublebus) # set the deltaL of sT-shaped silicon pillar (deltaL = (Lin-Lout)/4) deltaL = (2 * pitch * ff) * (W_ratio - 1) / ((1 + W_ratio) * (4)) if (r - w / 2 <= 0): r = 5 print('invalid radius, set r to default: 5') #Calculate number of segments s1 = pitch * ff #silicon s2 = pitch - s1 #gap #calculate best radius pi = math.pi const = math.ceil(2 * pi * r / (s1 + s2)) #if doesn't divide evenly, replace r with best possible r if ((2 * pi * r) % (s1 + s2) != 0): r = const * (s1 + s2) / (2 * pi) print('r adjusted to ' + str(r) + 'um to fit periods perfectly.') #theta1 = (s1/r) #theta2 = (s2/r) theta1 = math.atan(s1 / r) # angle of silicon compared to the origin point theta2 = math.atan(s2 / r) # angle of the gap compared to the origin point nSeg = int( math.floor(angle / (math.degrees(theta1) + math.degrees(theta2)))) #how many segments to have si_first = True #for alternating between silicon and gap j = 0 #index of how many silicon thetas jj = 0 #index of how many gap thetas ORDER = True #ordering of the coordinates for polygon drawing #xo = [(r-w/2)*math.cos(0)] #yo = [(r-w/2)*math.sin(0)] #xo.append((r+w/2)*math.cos(0)) #yo.append((r+w/2)*math.sin(0)) xo = [(r - w / 2) * math.cos(0) + deltaL * math.sin(0)] yo = [(r - w / 2) * math.sin(0) - deltaL * math.cos(0)] xo.append((r + w / 2) * math.cos(0) - deltaL * math.sin(0)) yo.append((r + w / 2) * math.sin(0) + deltaL * math.cos(0)) for i in range(0, nSeg * 2): if si_first: j = j + 1 si_first = not (si_first) else: jj = jj + 1 si_first = not (si_first) if ORDER: xo.append((r + w / 2) * math.cos(j * theta1 + jj * theta2) + deltaL * math.sin(j * theta1 + jj * theta2)) yo.append((r + w / 2) * math.sin(j * theta1 + jj * theta2) - deltaL * math.cos(j * theta1 + jj * theta2)) xo.append((r - w / 2) * math.cos(j * theta1 + jj * theta2) - deltaL * math.sin(j * theta1 + jj * theta2)) yo.append((r - w / 2) * math.sin(j * theta1 + jj * theta2) + deltaL * math.cos(j * theta1 + jj * theta2)) ORDER = not (ORDER) else: xo.append((r - w / 2) * math.cos(j * theta1 + jj * theta2) + deltaL * math.sin(j * theta1 + jj * theta2)) yo.append((r - w / 2) * math.sin(j * theta1 + jj * theta2) - deltaL * math.cos(j * theta1 + jj * theta2)) xo.append((r + w / 2) * math.cos(j * theta1 + jj * theta2) - deltaL * math.sin(j * theta1 + jj * theta2)) yo.append((r + w / 2) * math.sin(j * theta1 + jj * theta2) + deltaL * math.cos(j * theta1 + jj * theta2)) ORDER = not (ORDER) if len(xo) == 4: dpts = [pya.DPoint(xo[i], yo[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) xo = [] yo = [] #Draw Bus WG #calulate ideal length of bus bus_length = self.cell.bbox().height() * dbu + pitch * 2 constant = math.ceil(bus_length / (s1 + s2)) if bus_length % (s1 + s2) != 0: bus_length = constant * (s1 + s2) #draw first box at center xo = [(r + w / 2 + gap), (r + w / 2 + gap + w), (r + w / 2 + gap + w), (r + w / 2 + gap)] yo = [0, 0, s1, s1] dpts = [pya.DPoint(xo[i], yo[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) for i in range(0, int(math.ceil((constant) / 2))): yu = [yo[j] + i * pitch for j in range(len(yo))] yd = [yo[j] - i * pitch for j in range(len(yo))] dpts = [pya.DPoint(xo[i], yu[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) dpts = [pya.DPoint(xo[i], yd[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) if doublebus: for i in range(0, int(math.ceil((constant) / 2))): x2 = [xo[j] * -1 for j in range(len(xo))] yu = [yo[j] + i * pitch for j in range(len(yo))] yd = [yo[j] - i * pitch for j in range(len(yo))] dpts = [pya.DPoint(x2[i], yu[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) dpts = [pya.DPoint(x2[i], yd[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) #DEV BOX if doublebus is False: half_l = (self.cell.bbox().width() - w / dbu - gap / dbu) / 2 half_r = self.cell.bbox().width() - half_l dev = Box(-half_l, yd[-4] / dbu, half_r, yu[-1] / dbu) shapes(LayerDevRecN).insert(dev) dev_width = self.cell.bbox().width() / 2 else: half_l = (self.cell.bbox().width()) / 2 half_r = self.cell.bbox().width() - half_l dev = Box(-half_l, yd[-4] / dbu, half_r, yu[-1] / dbu) shapes(LayerDevRecN).insert(dev) dev_width = self.cell.bbox().width() / 2 # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.w, dbu) gap = to_itype(self.gap, dbu) bus_length = to_itype(bus_length / 2, dbu) #Pin1 t = Trans(Trans.R0, half_r - w / 2, yu[-1] / dbu) pin = Path([Point(0, -pin_length / 2), Point(0, pin_length / 2)], 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 #Pin2 t = Trans(Trans.R0, half_r - w / 2, yd[-4] / dbu) pin = Path([Point(0, pin_length / 2), Point(0, -pin_length / 2)], 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 if doublebus is True: half_l = self.cell.bbox().width() / 2 half_r = self.cell.bbox().width() - half_l #pin 3 t = Trans(Trans.R0, -half_l + w / 2, yu[-1] / dbu) pin = Path([Point(0, -pin_length / 2), Point(0, pin_length / 2)], w) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin3", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu #Pin4 t = Trans(Trans.R0, -half_l + w / 2, yd[-4] / dbu) pin = Path([Point(0, pin_length / 2), Point(0, -pin_length / 2)], w) pin_t = pin.transformed(t) shapes(LayerPinRecN).insert(pin_t) text = Text("pin4", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu
def produce(self, layout, layers, parameters, cell): #coerce parameters (make consistent) self._layers = layers self.cell = cell self._param_values = parameters self.layout = layout # cell: layout cell to place the layout # LayerSiN: which layer to use # r: radius # w: waveguide width # length units in dbu from math import pi, cos, sin from SiEPIC.utils import arc_wg, arc_wg_xy from SiEPIC._globals import PIN_LENGTH from SiEPIC.utils.layout import layout_waveguide2 from SiEPIC.extend import to_itype # fetch the parameters TECHNOLOGY = get_technology_by_name('EBeam') dbu = self.layout.dbu ly = self.layout LayerSiN = ly.layer(self.silayer) LayerSlab = ly.layer(self.slayer) # LayerNN = ly.layer(self.nlayer) LayerNPPN = ly.layer(self.npplayer) LayerVCN = ly.layer(self.vclayer) LayerMN = ly.layer(self.mlayer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) TextLayerN = ly.layer(self.textl) ## define parameters for phase shifter # mask overlay accuracy overlay = to_itype(self.overlay, dbu) overlay_ebl = to_itype(self.overlay_ebl, dbu) ## define waveguide related parameters radius, adiab, bezier = 5 / dbu, 1, 0.2 w = to_itype(self.width, dbu) #waveguide width, default 0.5 um l = to_itype(self.length, dbu) #pahse shifter length, default 150 um npp_d = to_itype(self.npp_distance, dbu) #npp to edge of waveguide distance sw = 2 * npp_d + w # twice of npp_d that is defined above in_rib = to_itype( self.input_rib_width, dbu ) #the slab width of the input/output waveguide to the phase shifter in_taper = to_itype( self.in_taper_length, dbu ) #the input waveguide to phase shifter dimension transfer taper length, default 15 edge_slab_width = to_itype( 4, dbu ) # width of the 90 nm slab that is outside of all the folding waveguide, default 2 * 2um npp_width = to_itype(self.npp_width, dbu) #npp width, default 5 um ## define node related parameters contact_size = to_itype(self.vc_w, dbu) # square NPP size, for VC purpose pin_text_size = to_itype(0.4, dbu) # pin text size # define metal contact parameters metal_routing_width = to_itype(self.m_w, dbu) # width of metal ps_sl_w = sw + npp_width * 2 + 2 * overlay # define overall phase shifter 90nm slab width metal_routing_distance_to_node = metal_routing_width + ps_sl_w / 2 vc_si_distance = to_itype(self.vc_si_distance, dbu) vc_to_npp_exclusion = to_itype( 0, dbu) # VC boundary to NPP boundary distance ## define strip to slab transfer taper parameters N = 100 # Number of points for the input/output slab taper order = 3 # input/output slab taper curve wg1 = pya.Box(-l / 2, -w / 2, l / 2, w / 2) wg2 = pya.Box(-l / 2, -ps_sl_w / 2, l / 2, ps_sl_w / 2) wg3 = pya.Box(-l / 2, npp_d + w / 2, l / 2, npp_d + w / 2 + npp_width) wg4 = pya.Box(-l / 2, -npp_d - w / 2, l / 2, -npp_d - w / 2 - npp_width) self.cell.shapes(LayerSiN).insert(wg1) self.cell.shapes(LayerNPPN).insert(wg3) self.cell.shapes(LayerNPPN).insert(wg4) if self.io_wg_type: in_slab = to_itype(self.input_slab_width, dbu) else: in_slab = in_rib if 1: self.cell.shapes(LayerSlab).insert(wg2) # add input strip taper pts = [ Point(-l / 2 - in_taper, -in_rib / 2), Point(-l / 2, -w / 2), Point(-l / 2, w / 2), Point(-l / 2 - in_taper, in_rib / 2) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) # add input slab taper pts = [] for i in range(0, N + 1): pts.append( Point(-l / 2 - in_taper + in_taper / N * i, (in_slab - overlay_ebl * 2) / 2 + ((sw / 2 - (in_slab - overlay_ebl * 2)) / (N**order)) * (i**order))) for i in range(0, N + 1): pts.append( Point( -l / 2 - in_taper + in_taper / N * (N - i), -(in_slab - overlay_ebl * 2) / 2 - ((sw / 2 - (in_slab - overlay_ebl * 2)) / (N**order)) * ((N - i)**order))) self.cell.shapes(LayerSlab).insert(Polygon(pts)) # add output strip taper pts = [ Point(l / 2 + in_taper, -in_rib / 2), Point(l / 2, -w / 2), Point(l / 2, w / 2), Point(l / 2 + in_taper, in_rib / 2) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) # cubic taper pts = [] for i in range(0, N + 1): pts.append( Point(l / 2 + in_taper - in_taper / N * i, (in_slab - overlay_ebl * 2) / 2 + ((sw / 2 - (in_slab - overlay_ebl * 2)) / (N**order)) * (i**order))) for i in range(0, N + 1): pts.append( Point( l / 2 + in_taper / N * i, -(in_slab - overlay_ebl * 2) / 2 - ((sw / 2 - (in_slab - overlay_ebl * 2)) / (N**order)) * ((N - i)**order))) self.cell.shapes(LayerSlab).insert(Polygon(pts)) from SiEPIC._globals import PIN_LENGTH as pin_length # Pin on the left side: p1 = [ Point(-l / 2 - in_taper + pin_length / 2, 0), Point(-l / 2 - in_taper - pin_length / 2, 0) ] p1c = Point(-l / 2 - in_taper, 0) self.set_p1 = p1c self.p1 = p1c pin = Path(p1, in_rib) self.cell.shapes(LayerPinRecN).insert(pin) t = Trans(Trans.R0, -l / 2 - in_taper, 0) text = Text("pin1", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = pin_text_size # Pin on the right side: p2 = [ Point(l / 2 + in_taper - pin_length / 2, 0), Point(l / 2 + in_taper + pin_length / 2, 0) ] p2c = Point(l / 2 + in_taper, 0) self.set_p2 = p2c self.p2 = p2c pin = Path(p2, in_rib) self.cell.shapes(LayerPinRecN).insert(pin) t = Trans(Trans.R0, l / 2 + in_taper, 0) text = Text("pin2", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = pin_text_size for i in range(0, self.segments + 1): vc_temp_upper = pya.Box( (l - contact_size) / self.segments * i - l / 2 + vc_to_npp_exclusion, w / 2 + vc_si_distance, (l - contact_size) / self.segments * i - l / 2 + contact_size - vc_to_npp_exclusion, w / 2 + vc_si_distance + contact_size) self.cell.shapes(LayerVCN).insert(vc_temp_upper) wg_temp_upper = pya.Box( (l - contact_size) / self.segments * i - l / 2 - overlay, sw / 2, (l - contact_size) / self.segments * i - l / 2 + contact_size + overlay, w / 2 + vc_si_distance + contact_size + overlay) self.cell.shapes(LayerNPPN).insert(wg_temp_upper) wg_temp_upper = pya.Box( (l - contact_size) / self.segments * i - l / 2 - 2 * overlay, sw / 2, (l - contact_size) / self.segments * i - l / 2 + contact_size + 2 * overlay, w / 2 + vc_si_distance + contact_size + 2 * overlay) self.cell.shapes(LayerSlab).insert(wg_temp_upper) vc_temp_lower = pya.Box( (l - contact_size) / self.segments * i - l / 2 + vc_to_npp_exclusion, -w / 2 - vc_si_distance, (l - contact_size) / self.segments * i - l / 2 + contact_size - vc_to_npp_exclusion, -w / 2 - vc_si_distance - contact_size) self.cell.shapes(LayerVCN).insert(vc_temp_lower) wg_temp_lower = pya.Box( (l - contact_size) / self.segments * i - l / 2 - overlay, -sw / 2, (l - contact_size) / self.segments * i - l / 2 + contact_size + overlay, -(w / 2 + vc_si_distance + contact_size + overlay)) self.cell.shapes(LayerNPPN).insert(wg_temp_lower) wg_temp_lower = pya.Box( (l - contact_size) / self.segments * i - l / 2 - 2 * overlay, -sw / 2, (l - contact_size) / self.segments * i - l / 2 + contact_size + 2 * overlay, -(w / 2 + vc_si_distance + contact_size + 2 * overlay)) self.cell.shapes(LayerSlab).insert(wg_temp_lower) metal_temp = pya.Box( (l - contact_size) / self.segments * i - l / 2 - overlay, -(w / 2 + vc_si_distance + contact_size + 1 * overlay) - metal_routing_distance_to_node * (i % 2), (l - contact_size) / self.segments * i - l / 2 + contact_size + overlay, w / 2 + vc_si_distance + contact_size + 1 * overlay + metal_routing_distance_to_node * ((i + 1) % 2)) self.cell.shapes(LayerMN).insert(metal_temp) metal_routing = Path([ Point(-l / 2, -sw / 2 - metal_routing_distance_to_node - contact_size), Point(l / 2, -sw / 2 - metal_routing_distance_to_node - contact_size) ], metal_routing_width) self.cell.shapes(LayerMN).insert(metal_routing) metal_routing = Path([ Point(-l / 2, sw / 2 + metal_routing_distance_to_node + contact_size), Point(l / 2, sw / 2 + metal_routing_distance_to_node + contact_size) ], metal_routing_width) self.cell.shapes(LayerMN).insert(metal_routing) dev = Box( -l / 2 - in_taper, -sw / 2 - metal_routing_distance_to_node - contact_size - metal_routing_width, +l / 2 + in_taper, sw / 2 + metal_routing_distance_to_node + contact_size + metal_routing_width) self.cell.shapes(LayerDevRecN).insert(dev)
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 produce_impl(self): import os from SiEPIC.extend import to_itype # 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) TextLayerN = ly.layer(self.textl) LayerPinRec = ly.layer(self.pinrec) file_path = os.path.join(self.forder, self.name + '.' + self.filetype) print('Bragg_Straight_from_file: input file: ' + file_path) if not(os.path.isfile(file_path)): error = 'File not found: ' + file_path text = Text (error, Trans(0,0)) shape = shapes(TextLayerN).insert(text) shape.text_size = 1/dbu box=Box(0,0, len(error)*shape.text_size, shape.text_size) shapes(LayerDevRecN).insert(box) return if 'txt' in self.filetype: # Format: (x1,y1;x2,y2;...), where x and y values are integers text_file = open(file_path, "r") lines = text_file.readlines() else: error = 'File type not available: ' + self.filetype text = Text (error, Trans(0,0)) shape = shapes(TextLayerN).insert(text) shape.text_size = 1/dbu box=Box(0,0, len(error)*shape.text_size, shape.text_size) shapes(LayerDevRecN).insert(box) return p = SimplePolygon().from_s(lines[0]) shapes(LayerSiN).insert(p) # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length bbox = p.bbox() y = (bbox.p1.y + bbox.p2.y) / 2 leftx = bbox.left port_w = to_itype(self.port_w,dbu) pin = Path([Point(pin_length/2, 0), Point(-pin_length/2, 0)], port_w) t = Trans(Trans.R0, bbox.left, y) 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.R180, bbox.right, y) 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. box=Box(bbox.left,y-3/2*port_w,bbox.right,y+3/2*port_w) shapes(LayerDevRecN).insert(box)
def bent_bragg_layout(): # Configure parameter sweep pol = 'te' if pol == 'te': r = 20 w = 0.5 gap = 0.2 wg_bend_radius = 5 # Import functions from SiEPIC-Tools, and get technology details from SiEPIC.utils import select_paths, get_layout_variables TECHNOLOGY, lv, ly, cell = get_layout_variables() dbu = ly.dbu from SiEPIC.extend import to_itype from SiEPIC.scripts import path_to_waveguide # Layer mapping: LayerSiN = ly.layer(TECHNOLOGY['Si']) fpLayerN = cell.layout().layer(TECHNOLOGY['FloorPlan']) TextLayerN = cell.layout().layer(TECHNOLOGY['Text']) # Draw the floor plan cell.shapes(fpLayerN).insert(Box(0, 0, 610 / dbu, 405 / dbu)) #** Create the device under test (directional coupler) top_cell = cell pcell = ly.create_cell("ebeam_dc_halfring_straight", "EBeam", { "r": r, "w": w, "g": gap, "bustype": 0 }) x_pos_device = 100 y_pos_device = 100 t = Trans(Trans.R90, x_pos_device / dbu, to_itype(y_pos_device, dbu)) cell.insert(CellInstArray(pcell.cell_index(), t)) #** input/out GCs GC_array = ly.create_cell("ebeam_gc_te1550", "EBeam").cell_index() GC_pitch = 127 x_pos_GC = 33.1 y_pos_GC = 21.4 / 2 t = Trans(Trans.R0, x_pos_GC / dbu, to_itype(y_pos_GC, dbu)) cell.insert( CellInstArray(GC_array, t, DPoint(0, GC_pitch).to_itype(dbu), DPoint(0, 0).to_itype(dbu), 4, 1)) #** routing pts = [ DPoint(x_pos_GC, y_pos_GC), DPoint(x_pos_device, y_pos_GC), DPoint(x_pos_device, y_pos_device - r - 0.75), ] dpath = DPath(pts, 0.5).transformed(DTrans(DTrans.R0, 0, 0)) cell.shapes(LayerSiN).insert(dpath.to_itype(dbu))
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) from SiEPIC.extend import to_itype N = self.number_of_periods grating_period = int(round(self.grating_period / dbu)) cdc_period = int(round(self.cdc_period / dbu)) misalignment = 0 # Determine the period such that the waveguide length is as desired. Slight adjustment to period N_boxes = N # Draw the Bragg grating: box_width = int(round(grating_period * self.duty)) w1 = self.wg1_width / dbu half_w1 = w1 / 2 w2 = self.wg2_width / dbu half_w2 = w2 / 2 w = self.corrugation_width / dbu half_w = w / 2 gap = int(round(self.gap / dbu)) vertical_offset = int(round(self.wg2_width / 2 / dbu)) + 2 * gap + int( round(self.wg1_width / 2 / dbu)) + int(round(w)) t = Trans(Trans.R0, to_itype(0, dbu), vertical_offset) for i in range(0, N_boxes + 1): if i % 2 == True: x = int(round((i * grating_period - box_width / 2))) box1_a = Box(x, -half_w1, x + box_width, half_w1) shapes(LayerSiN).insert(box1_a) box2_a = Box(x + grating_period, -half_w2, x + grating_period + box_width, half_w2).transformed(t) shapes(LayerSiN).insert(box2_a) else: x = int(round((i * grating_period - box_width / 2))) box1_b = Box(x, -half_w1, x + box_width, half_w1) shapes(LayerSiN).insert(box1_b) box2_b = Box(x + grating_period, -half_w2, x + grating_period + box_width, half_w2).transformed(t) shapes(LayerSiN).insert(box2_b) # compensate length of SWG boxes vs cdc boxes x_cdc = int(round(N_boxes * cdc_period) / 2) xk = int(round(N_boxes * grating_period)) N_cdc_boxes = 2 * int(round((xk - x_cdc) / cdc_period)) print(N_cdc_boxes) for i in range(0, N_boxes + 1 + N_cdc_boxes): if i % 2 == True: x_cdc = int(round((i * cdc_period / 2 - box_width / 2))) boxw_a = Box( x_cdc, -half_w1 - gap, x_cdc + cdc_period / 2, -w - half_w1 - gap, ) shapes(LayerSiN).insert(boxw_a) boxw_a = Box( x_cdc, half_w2 + gap, x_cdc + cdc_period / 2, w + half_w2 + gap, ).transformed(t) shapes(LayerSiN).insert(boxw_a) else: x_cdc = int(round((i * cdc_period / 2 - box_width / 2))) boxw_a = Box( x_cdc, half_w1 + gap, x_cdc + cdc_period / 2, w + half_w1 + gap, ) shapes(LayerSiN).insert(boxw_a) # missing periods due to misalignments box_final = Box(x + grating_period, -half_w1, x + grating_period + box_width, half_w1) shapes(LayerSiN).insert(box_final) box_final = Box(-box_width / 2, -half_w2, box_width / 2, half_w2).transformed(t) shapes(LayerSiN).insert(box_final) # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.wg1_width, dbu) t = Trans(Trans.R0, to_itype(-box_width / 2, dbu * 1000), 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 w = to_itype(self.wg2_width, dbu) t = Trans(Trans.R0, to_itype(-box_width / 2, dbu * 1000), vertical_offset) 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 w = to_itype(self.wg2_width, dbu) t = Trans(Trans.R0, to_itype(x + grating_period + box_width, dbu * 1000), vertical_offset) 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("pin3", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu w = to_itype(self.wg1_width, dbu) t = Trans(Trans.R0, to_itype(x + grating_period + box_width, dbu * 1000), 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("pin4", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu
def layout_waveguide4(cell, dpath, waveguide_type, debug=True): if debug: print('SiEPIC.utils.layout.layout_waveguide4: ') print(' - waveguide_type: %s' % (waveguide_type)) # get the path and clean it up layout = cell.layout() dbu = layout.dbu dpath = dpath.to_itype(dbu) dpath.unique_points() pts = dpath.get_points() dpts = dpath.get_dpoints() # Load the technology and all waveguide types from SiEPIC.utils import load_Waveguides_by_Tech technology_name = layout.technology_name waveguide_types = load_Waveguides_by_Tech(technology_name) if debug: print(' - technology_name: %s' % (technology_name)) print(' - waveguide_types: %s' % (waveguide_types)) # Load parameters for the chosen waveguide type params = [t for t in waveguide_types if t['name'] == waveguide_type] if type(params) == type([]) and len(params) > 0: params = params[0] else: print('error: waveguide type not found in PDK waveguides') raise Exception( 'error: waveguide type (%s) not found in PDK waveguides' % waveguide_type) # compound waveguide types: if 'compound_waveguide' in params: # find the singlemode and multimode waveguides: if 'singlemode' in params['compound_waveguide']: singlemode = params['compound_waveguide']['singlemode'] else: raise Exception( 'error: waveguide type (%s) does not have singlemode defined' % waveguide_type) if 'multimode' in params['compound_waveguide']: multimode = params['compound_waveguide']['multimode'] else: raise Exception( 'error: waveguide type (%s) does not have multimode defined' % waveguide_type) params_singlemode = [ t for t in waveguide_types if t['name'] == singlemode ] params_multimode = [ t for t in waveguide_types if t['name'] == multimode ] if type(params_singlemode) == type([]) and len(params_singlemode) > 0: params_singlemode = params_singlemode[0] else: raise Exception( 'error: waveguide type (%s) not found in PDK waveguides' % singlemode) if type(params_multimode) == type([]) and len(params_multimode) > 0: params_multimode = params_multimode[0] else: raise Exception( 'error: waveguide type (%s) not found in PDK waveguides' % multimode) # find the taper if 'taper_library' in params[ 'compound_waveguide'] and 'taper_cell' in params[ 'compound_waveguide']: taper = layout.create_cell( params['compound_waveguide']['taper_cell'], params['compound_waveguide']['taper_library']) if not taper: raise Exception( 'Cannot import cell %s : %s' % (params['compound_waveguide']['taper_cell'], params['compound_waveguide']['taper_library'])) else: raise Exception( 'error: waveguide type (%s) does not have taper cell and library defined' % waveguide_type) from pya import Trans, CellInstArray ''' find sections of waveguides that are larger than (2 x radius + 2 x taper_length) - insert two tapers - insert multimode straight section - insert singlemode waveguides (including bends) before ''' import math from SiEPIC.extend import to_itype from pya import Point radius = to_itype(params_singlemode['radius'], dbu) taper_length = taper.find_pins()[0].center.distance( taper.find_pins()[1].center) min_length = 2 * radius + 2 * taper_length offset = radius wg_sm_segment_pts = [] wg_last = 0 waveguide_length = 0 for ii in range(1, len(dpts)): start_point = dpts[ii - 1] end_point = dpts[ii] distance_points = end_point.distance(start_point) if distance_points < min_length: # single mode segment, keep track if ii == 1: wg_sm_segment_pts.append(pts[ii - 1]) wg_sm_segment_pts.append(pts[ii]) if ii == len(pts) - 1: subcell = layout.create_cell("Waveguide_sm_%s" % ii) cell.insert(CellInstArray(subcell.cell_index(), Trans())) waveguide_length += layout_waveguide3(subcell, wg_sm_segment_pts, params_singlemode, debug=True) else: # insert two tapers and multimode waveguide angle = math.atan2( (end_point.y - start_point.y), (end_point.x - start_point.x)) / math.pi * 180 if ii == 1: wg_first = offset else: wg_first = 0 if ii == len(pts) - 1: wg_last = offset if round(angle) % 360 == 270.0: t = Trans(Trans.R270, start_point.x, start_point.y - offset + wg_first) t2 = Trans(Trans.R90, end_point.x, end_point.y + offset - wg_last) wg_start_pt = Point( start_point.x, start_point.y - offset - taper_length + wg_first) wg_end_pt = Point( end_point.x, end_point.y + offset + taper_length - wg_last) if round(angle) % 360 == 90.0: t = Trans(Trans.R90, start_point.x, start_point.y + offset - wg_first) t2 = Trans(Trans.R270, end_point.x, end_point.y - offset + wg_last) wg_start_pt = Point( start_point.x, start_point.y + offset + taper_length - wg_first) wg_end_pt = Point( end_point.x, end_point.y - offset - taper_length + wg_last) if round(angle) % 360 == 180.0: t = Trans(Trans.R180, start_point.x - offset + wg_first, start_point.y) t2 = Trans(Trans.R0, end_point.x + offset - wg_last, end_point.y) wg_start_pt = Point( start_point.x - offset - taper_length + wg_first, start_point.y) wg_end_pt = Point( end_point.x + offset + taper_length - wg_last, end_point.y) if round(angle) % 360 == 0.0: t = Trans(Trans.R0, start_point.x + offset - wg_first, start_point.y) t2 = Trans(Trans.R180, end_point.x - offset + wg_last, end_point.y) wg_start_pt = Point( start_point.x + offset + taper_length - wg_first, start_point.y) wg_end_pt = Point( end_point.x - offset - taper_length + wg_last, end_point.y) inst_taper = cell.insert(CellInstArray(taper.cell_index(), t)) inst_taper = cell.insert(CellInstArray(taper.cell_index(), t2)) waveguide_length += taper_length * 2 subcell = layout.create_cell("Waveguide_mm_%s" % ii) cell.insert(CellInstArray(subcell.cell_index(), Trans())) waveguide_length += layout_waveguide3(subcell, [wg_start_pt, wg_end_pt], params_multimode, debug=True) # compound segment if ii > 1: wg_sm_segment_pts.append(t.disp.to_p()) subcell = layout.create_cell("Waveguide_sm_%s" % ii) cell.insert(CellInstArray(subcell.cell_index(), Trans())) waveguide_length += layout_waveguide3(subcell, wg_sm_segment_pts, params_singlemode, debug=True) wg_sm_segment_pts = [t2.disp.to_p(), pts[ii]] else: wg_sm_segment_pts = [t2.disp.to_p(), pts[ii]] else: # primitive waveguide type waveguide_length = layout_waveguide3(cell, pts, params, debug=True) 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 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 y_splitter_tree(cell, tree_depth=4, y_splitter_cell="y_splitter_1310", library="SiEPICfab_Shuksan_PDK", wg_type='Strip TE 1310 nm, w=350 nm', draw_waveguides=True): ''' Create a tree of splitters - cell: layout cell to create the structures in - tree_depth: Tree depth (2^N outputs) - y_splitter_cell: name of the y-splitter cell - library: the library containing the y_splitter_cell - wg_type: waveguide type from WAVEGUIDES.XML - draw_waveguides: True draws the waveguides, False is faster for debugging Returns - inst_in: instance of the input cell - inst_out[]: array of instances of the output cells - cell_tree: new cell created This is useful for subsequent routing Limitations: - the design uses regular 90 degree bends, rather than S-bends. hence it could be made more compact ''' from SiEPIC.scripts import connect_pins_with_waveguide from SiEPIC.extend import to_itype from math import floor # create a new sub-cell where the tree will go ly = cell.layout() tech = ly.technology().name cell_tree = ly.create_cell("y_splitter_tree") # load the y-splitter from the library y_splitter = ly.create_cell(y_splitter_cell, library) if not y_splitter: raise Exception('Cannot import cell %s:%s' % (library, y_splitter_cell)) # Load waveguide information from SiEPIC.utils import load_Waveguides_by_Tech waveguides = load_Waveguides_by_Tech(tech) wg = [w for w in waveguides if wg_type in w['name']][0] if not wg: raise Exception("Waveguide type not defined in WAVEGUIDES.XML: %s" % wg_type) return wg_width = to_itype(float(wg['width']), ly.dbu) wg_radius = to_itype(float(wg['radius']), ly.dbu) # build the tree, using measurements from the cell and waveguide parameters x = 0 dx = y_splitter.bbox().width() + wg_radius * 2 # calculate the spacing for the y-splitters based on waveguide radius and 90 degree bends y_wg_offset = (y_splitter.pinPoint("opt2").y - y_splitter.pinPoint("opt3").y) dy = max(y_splitter.bbox().height(), wg_radius * 4 + y_wg_offset) # intialize loop inst_out = [] y0 = 0 for i in range(0, tree_depth): inst = [] y = y0 for j in range(0, int(2**(tree_depth - i - 1))): t = pya.Trans(pya.Trans.R0, x, y) inst.append( cell_tree.insert(pya.CellInstArray(y_splitter.cell_index(), t))) # perform waveguide routing if (i > 0) and draw_waveguides: connect_pins_with_waveguide(inst[j], 'opt2', inst_higher[j * 2 + 1], 'opt1', waveguide_type=wg_type) connect_pins_with_waveguide(inst[j], 'opt3', inst_higher[j * 2], 'opt1', waveguide_type=wg_type) y += dy inst_higher = inst if i == 0: inst_out = inst if i == tree_depth - 1: inst_in = inst[0] x += -dx y0 = y0 + dy / 2 dy = dy * 2 return inst_in, inst_out, cell_tree
def produce_impl(self): import math # 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) from SiEPIC.extend import to_itype # Draw the Bragg grating (bottom): # create chirped period array grating_period_start = int(round(self.grating_period_start/dbu)) grating_period_end = int(round(self.grating_period_end/dbu)) length = self.number_of_periods step = ((grating_period_start-grating_period_end) * 1.0 / length) grating_period = [] box_width = [] misalignment = [] for i in range(length): grating_period.append(grating_period_end+i*step) grating_period[i] = round(grating_period_end+i*step) box_width.append(int(round(grating_period[i]/2))) misalignment.append( int(round(grating_period[i]/2))) w = to_itype(self.wg1_width,dbu) GaussianIndex = self.index half_w = w/2 half_corrugation_w = to_itype(self.corrugation_width1/2,dbu) N = self.number_of_periods if self.sinusoidal: x = 0 npoints_sin = 40 for i in range(0,self.number_of_periods): if i != 0: x = x + grating_period[i] profileFunction = math.exp( -0.5*(2*GaussianIndex*(i-N/2)/(N))**2 ) profile = int(round(self.corrugation_width1/2/dbu))*profileFunction; box1 = Box(x, 0, x + box_width[i], half_w+profile) pts1 = [Point(x,0)] pts3 = [Point(x + misalignment[i],0)] for i1 in range(0,npoints_sin+1): x1 = i1 * 2* math.pi / npoints_sin y1 = round(profile*math.sin(x1)) x1 = round(x1/2/math.pi*grating_period[i]) pts1.append( Point(x + x1,half_w+y1 ) ) pts3.append( Point(x + misalignment[i] + x1,-half_w-y1 ) ) pts1.append( Point(x + grating_period[i], 0) ) pts3.append( Point(x + grating_period[i] + misalignment[i], 0) ) shapes(LayerSiN).insert(Polygon(pts1)) shapes(LayerSiN).insert(Polygon(pts3)) length = x + grating_period[i] + misalignment[i] # extra piece at the end: box2 = Box(x + grating_period[i], 0, length, half_w) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment[0], -half_w) shapes(LayerSiN).insert(box3) else: x = 0 for i in range(0,self.number_of_periods): if i != 0: x = x + grating_period[i] profileFunction = math.exp( -0.5*(2*GaussianIndex*(i-N/2)/(N))**2 ) profile = int(round(self.corrugation_width1/2/dbu))*profileFunction; box1 = Box(x, 0, x + box_width[i], to_itype(half_w+profile,dbu*1000)) box2 = Box(x + box_width[i], 0, x + grating_period[i], to_itype(half_w-profile,dbu*1000)) box3 = Box(x + misalignment[i], 0, x + box_width[i] + misalignment[i], to_itype(-half_w-profile,dbu*1000)) box4 = Box(x + box_width[i] + misalignment[i], 0, x + grating_period[i] + misalignment[i], to_itype(-half_w+profile,dbu*1000)) shapes(LayerSiN).insert(box1) shapes(LayerSiN).insert(box2) shapes(LayerSiN).insert(box3) shapes(LayerSiN).insert(box4) length = x + grating_period[i] + misalignment[i] # extra piece at the end: box2 = Box(x + grating_period[i], 0, length, half_w) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment[0], -half_w) shapes(LayerSiN).insert(box3) vertical_offset = int(round(self.wg2_width/2/dbu))+int(round(self.gap/dbu))+int(round(self.wg1_width/2/dbu)) t = Trans(Trans.R0, 0,vertical_offset) # Draw the Bragg grating (top): w = to_itype(self.wg2_width,dbu) GaussianIndex = self.index half_w = w/2 half_corrugation_w = int(round(self.corrugation_width2/2/dbu)) N = self.number_of_periods if self.sinusoidal: npoints_sin = 40 x = 0 for i in range(0,self.number_of_periods): if i != 0: x = x + grating_period[i] profileFunction = math.exp( -0.5*(2*GaussianIndex*(i-N/2)/(N))**2 ) profile = int(round(self.corrugation_width2/2/dbu))*profileFunction; box1 = Box(x, 0, x + box_width[i], -half_w+profile).transformed(t) pts1 = [Point(x,0)] pts3 = [Point(x + misalignment[i],0)] for i1 in range(0,npoints_sin+1): x1 = i1 * 2* math.pi / npoints_sin y1 = round(profile*math.sin(x1)) x1 = round(x1/2/math.pi*grating_period[i]) # print("x: %s, y: %s" % (x1,y1)) pts1.append( Point(x + x1,-half_w-y1 ) ) pts3.append( Point(x + misalignment[i] + x1,+half_w+y1 ) ) pts1.append( Point(x + grating_period[i], 0) ) pts3.append( Point(x + grating_period[i] + misalignment[i], 0) ) shapes(LayerSiN).insert(Polygon(pts1).transformed(t)) shapes(LayerSiN).insert(Polygon(pts3).transformed(t)) length = x + grating_period[i] + misalignment[i] # extra piece at the end: box2 = Box(x + grating_period[i], 0, length, -half_w).transformed(t) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment[0], half_w).transformed(t) shapes(LayerSiN).insert(box3) else: x = 0 for i in range(0,self.number_of_periods): if i != 0: x = x + grating_period[i] profileFunction = math.exp( -0.5*(2*GaussianIndex*(i-N/2)/(N))**2 ) profile = int(round(self.corrugation_width2/2/dbu))*profileFunction; box1 = Box(x, 0, x + box_width[i], -half_w-profile).transformed(t) box2 = Box(x + box_width[i], 0, x + grating_period[i], -half_w+profile).transformed(t) box3 = Box(x + misalignment[i], 0, x + box_width[i] + misalignment[i], half_w+profile).transformed(t) box4 = Box(x + box_width[i] + misalignment[i], 0, x + grating_period[i] + misalignment[i], half_w-profile).transformed(t) shapes(LayerSiN).insert(box1) shapes(LayerSiN).insert(box2) shapes(LayerSiN).insert(box3) shapes(LayerSiN).insert(box4) length = x + grating_period[i] + misalignment[i] box2 = Box(x + grating_period[i], 0, length, -half_w).transformed(t) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment[0], half_w).transformed(t) shapes(LayerSiN).insert(box3) # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.wg1_width,dbu) 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 w = to_itype(self.wg2_width,dbu) t = Trans(Trans.R0, 0,vertical_offset) 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 ("pin3", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4/dbu w = to_itype(self.wg1_width,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 w = to_itype(self.wg2_width,dbu) t = Trans(Trans.R0, length,vertical_offset) 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 ("pin4", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4/dbu # 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 ('Component=ebeam_contra_dc_chirped', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1/dbu t = Trans(Trans.R0, length/9, 0) 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. t = Trans(Trans.R0, 0,0) path = Path([Point(0, vertical_offset/2), Point(length, vertical_offset/2)], 3*w) shapes(LayerDevRecN).insert(path.simple_polygon())
def produce_impl(self): # Layout and layers: dbu = self.layout.dbu ly = self.layout shapes = self.cell.shapes LayerSi = self.layer LayerSiN = ly.layer(LayerSi) LayerSiSPN = ly.layer(LayerSi) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) # fetch the PCell parameters, and calculate a few things from SiEPIC.extend import to_itype wg_width_swg = to_itype(self.wg_width_swg, dbu) wg_width_strip = to_itype(self.wg_width_strip, dbu) length = to_itype(self.length, dbu) w1 = to_itype(self.wg_width_taper, dbu) w2 = to_itype(self.wg_width_strip, dbu) # Determine the period such that the waveguide length is as desired. Slight adjustment to period N_boxes = int( round(self.length / (self.period_swg + self.period_strip) * 2.0) - 0.5) grating_period = self.length / (N_boxes) / dbu print("N boxes: %s, grating_period: %s" % (N_boxes, grating_period)) # taper length, minus last SWG block # taper_length = int(round(self.taper_fraction * self.length / dbu)) taper_length = (N_boxes - 1) * grating_period # Find out what is the delta width required between # a) the SWG end and b) the SWG with nanotaper # Done by simulating the neff versus SWG waveguide width, with an without a 60 nm w = wg_width_swg if self.fishbone: dw = 0 elif self.wavelength == 1310: dw = -3.3736e-5 * w * w + 0.0618752 * w + 3.88577 else: dw = 0 wg_width_swg_taperend = wg_width_swg - dw # Draw the SWG waveguide: x = -self.period_swg * self.duty_swg / 2 / dbu for i in range(0, N_boxes): local_duty = 1.0 * ( N_boxes - i ) / N_boxes * self.duty_swg + 1.0 * i / N_boxes * self.duty_strip local_period = to_itype( (1.0 * (N_boxes - i) / N_boxes * self.period_swg + 1.0 * i / N_boxes * self.period_strip), dbu) local_wg_width = (1.0 * (N_boxes - i) / N_boxes * wg_width_swg_taperend + 1.0 * i / N_boxes * wg_width_strip) if (i == 0) | (i == N_boxes): print("local_duty: %s, local_period: %s, local_wg_width: %s" % (local_duty, local_period, local_wg_width)) local_box_width = int(round(local_period * local_duty)) # x = int(round((i * local_period - local_box_width/2))) if i != 0: box1 = Box(x, -local_wg_width / 2, x + local_box_width, local_wg_width / 2) else: # Last SWG, that is a bit larger than the one connected to the taper box1 = Box(x, -wg_width_swg / 2, x + local_box_width, wg_width_swg / 2) shapes(LayerSiN).insert(box1) x = x + int(round((local_period))) # Taper if self.fishbone: taper_length = length pts = [ Point(length - taper_length, -w1 / 2), Point(length - taper_length, w1 / 2), Point(length, w2 / 2), Point(length, -w2 / 2) ] shapes(LayerSiN).insert(Polygon(pts)) # Pins on the waveguide: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.wg_width_swg, dbu) 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 w = to_itype(max(self.wg_width_strip, self.wg_width_taper), 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, -w*2) text = Text \ ('Spice_param:length=%.3fu period_swg=%.3fu period_strip=%.3fu wg_width_swg=%.3fu wg_width_strip=%.3fu duty_swg=%.3f duty_strip=%.3f ' %\ (self.length, self.period_swg, (self.period_strip), self.wg_width_swg, self.wg_width_strip, self.duty_swg, self.duty_strip), t ) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1/dbu blabla ''' # Create the device recognition layer points = [pya.Point(0, 0), pya.Point(length, 0)] path = pya.Path(points, (self.wg_width_strip + self.clad_width * 2) / dbu) shapes(LayerDevRecN).insert(path.simple_polygon()) # waveguide cladding points = [pya.Point(0, 0), pya.Point(length, 0)] path = pya.Path(points, (self.wg_width_strip + self.clad_width * 2) / dbu) shapes(ly.layer(self.layer_clad)).insert(path.simple_polygon()) print(' - done: Waveguide_SWG_to_Strip')
def layout_waveguide3(cell, pts, params, debug=True): if debug: print('SiEPIC.utils.layout.layout_waveguide3: ') layout = cell.layout() dbu = layout.dbu technology_name = layout.technology_name from SiEPIC.utils import get_technology_by_name TECHNOLOGY = get_technology_by_name(technology_name) from SiEPIC.extend import to_itype wg_width = to_itype(params['width'], dbu) radius = float(params['radius']) model = params['model'] cellName = 'Waveguide2' CML = params['CML'] if debug: print(' - waveguide params: %s' % (params)) if 'compound_waveguide' in params: print('error: this function cannot handle compound waveguides') raise Exception( 'error: this function cannot handle compound waveguides (%s)' % waveguide_type) # draw the waveguide waveguide_length = layout_waveguide2( TECHNOLOGY, layout, cell, [wg['layer'] for wg in params['component']], [wg['width'] for wg in params['component']], [wg['offset'] for wg in params['component']], pts, radius, params['adiabatic'], params['bezier']) # Draw the marking layers from SiEPIC.utils import angle_vector LayerPinRecN = layout.layer(TECHNOLOGY['PinRec']) make_pin(cell, 'opt1', pts[0], wg_width, LayerPinRecN, angle_vector(pts[0] - pts[1]) % 360) make_pin(cell, 'opt2', pts[-1], wg_width, LayerPinRecN, angle_vector(pts[-1] - pts[-2]) % 360) from pya import Trans, Text, Path, Point ''' t1 = Trans(angle_vector(pts[0]-pts[1])/90, False, pts[0]) cell.shapes(LayerPinRecN).insert(Path([Point(-10, 0), Point(10, 0)], wg_width).transformed(t1)) cell.shapes(LayerPinRecN).insert(Text("opt1", t1, 0.3/dbu, -1)) t = Trans(angle_vector(pts[-1]-pts[-2])/90, False, pts[-1]) cell.shapes(LayerPinRecN).insert(Path([Point(-10, 0), Point(10, 0)], wg_width).transformed(t)) cell.shapes(LayerPinRecN).insert(Text("opt2", t, 0.3/dbu, -1)) ''' LayerDevRecN = layout.layer(TECHNOLOGY['DevRec']) # Compact model information angle_vec = angle_vector(pts[0] - pts[1]) / 90 halign = 0 # left angle = 0 dpt = Point(0, 0) if angle_vec == 0: # horizontal halign = 2 # right angle = 0 dpt = Point(0, 0.2 * wg_width) if angle_vec == 2: # horizontal halign = 0 # left angle = 0 dpt = Point(0, 0.2 * wg_width) if angle_vec == 1: # vertical halign = 2 # right angle = 1 dpt = Point(0.2 * wg_width, 0) if angle_vec == -1: # vertical halign = 0 # left angle = 1 dpt = Point(0.2 * wg_width, 0) pt2 = pts[0] + dpt pt3 = pts[0] - dpt pt4 = pts[0] - 6 * dpt pt5 = pts[0] + 2 * dpt t = Trans(angle, False, pt3) text = Text('Lumerical_INTERCONNECT_library=Design kits/%s' % CML, t, 0.1 * wg_width, -1) text.halign = halign shape = cell.shapes(LayerDevRecN).insert(text) t = Trans(angle, False, pt2) text = Text('Component=%s' % model, t, 0.1 * wg_width, -1) text.halign = halign shape = cell.shapes(LayerDevRecN).insert(text) t = Trans(angle, False, pt5) text = Text('cellName=%s' % cellName, t, 0.1 * wg_width, -1) text.halign = halign shape = cell.shapes(LayerDevRecN).insert(text) t = Trans(angle, False, pts[0]) pts_txt = str([[round(p.to_dtype(dbu).x, 3), round(p.to_dtype(dbu).y, 3)] for p in pts]).replace(', ', ',') text = Text( 'Spice_param:wg_length=%.9f wg_width=%.3g points="%s" radius=%.3g' % (waveguide_length * 1e-6, wg_width * 1e-9, pts_txt, radius * 1e-6), t, 0.1 * wg_width, -1) text.halign = halign shape = cell.shapes(LayerDevRecN).insert(text) t = Trans(angle, False, pt4) text = Text('Length=%.3f (microns)' % (waveguide_length), t, 0.5 * wg_width, -1) text.halign = halign shape = cell.shapes(LayerDevRecN).insert(text) return waveguide_length
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) from SiEPIC.extend import to_itype from math import pi, cos, sin, acos N = int(self.N) #draw the encoded bragg grating: dx = 0.01 corrugations = self.corrugation_widths if N != len(self.binary): pya.MessageBox.warning("Array length mismatch!", "Number of bits (N) does NOT equal the bits array length!", pya.MessageBox.Ok) return elif N != len(self.corrugation_widths): pya.MessageBox.warning("Array length mismatch!", "Number of bits (N) does NOT equal the corrugation widths array length!", pya.MessageBox.Ok) return dlambda = (self.stop_period - self.start_period)/(N-1) npoints = int(self.length/dx) w = to_itype(self.wg_width,dbu) l = to_itype(self.length,dbu) f = self.sum_format E = [] wavelengths = [] for i in range(0,N): E.append(int(self.binary[i])) wavelengths.append(self.start_period + i*dlambda) round(wavelengths[i],3) x = 0 y1 = 0 y2 = 0 pts1 = [Point(x,0)] pts3 = [Point(x,0)] for i in range(0, npoints + 1): x1 = i*dx #summation method if f == 1: for j in range(0, N): corrugation = float(self.corrugation_widths[j])/10 y1 = y1 + corrugation*E[j]*sin((x1*2*pi)/wavelengths[j]) y2 = y1 elif f == 2: #half half method (11110000 style): for j in range(0, int(N/2)): corrugation1 = float(self.corrugation_widths[j])/10 corrugation2 = float(self.corrugation_widths[j+int(N/2)])/10 y1 = y1 + corrugation1*E[j]*sin((x1*2*pi)/wavelengths[j]) y2 = y2 + corrugation2*E[j+int(N/2)]*sin((x1*2*pi)/wavelengths[j+int(N/2)]) elif f == 3: #half half method (10101010 style): for j in range(0, int(N/2)): idx1 = int(j*2) idx2 = int((j*2+1)) corrugation1 = float(self.corrugation_widths[idx1])/10 corrugation2 = float(self.corrugation_widths[idx2])/10 y1 = y1 + corrugation1*E[idx1]*sin((x1*2*pi)/wavelengths[idx1]) y2 = y2 + corrugation2*E[idx2]*sin((x1*2*pi)/wavelengths[idx2]) if self.N == 2: dw1 = float(self.corrugation_widths[0]) dw2 = float(self.corrugation_widths[1]) else: dw1 = 0 dw2 = 0 pts1.append( Point((x + x1)/dbu, ((self.wg_width-dw1)/2 + y1)/dbu)) pts3.append( Point((x + x1)/dbu, ((-self.wg_width+dw2)/2 - y2)/dbu)) pts1.append( Point((x + x1 + 20*dx)/dbu, self.wg_width/2/dbu)) pts3.append( Point((x + x1 + 20*dx)/dbu, -self.wg_width/2/dbu)) pts1.append( Point((x + x1 + 20*dx)/dbu, 0)) pts3.append( Point((x + x1 + 20*dx)/dbu, 0)) shapes(LayerSiN).insert(Polygon(pts1)) shapes(LayerSiN).insert(Polygon(pts3)) #create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length 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,(self.length+dx*20)/dbu,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
def produce_impl(self): import math # 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) from SiEPIC.extend import to_itype N = self.number_of_periods # create chirped period array grating_period_start = to_itype(self.grating_period_start, dbu) grating_period_end = to_itype(self.grating_period_end, dbu) length = self.number_of_periods step = (float(grating_period_start - grating_period_end) / length) grating_period = [] box_width = [] misalignment = [] for i in range(length): grating_period.append(grating_period_end + i * step) #grating_period[i] = round(grating_period_end+i*step) box_width.append(grating_period[i] / 2) misalignment = 0 # Determine the period such that the waveguide length is as desired. Slight adjustment to period N_boxes = N # Draw the Bragg grating: # box_width = int(round(grating_period*self.duty)) w1 = self.wg1_width / dbu half_w1 = w1 / 2 w2 = self.wg2_width / dbu half_w2 = w2 / 2 deltaW1_max = to_itype(self.corrugation_width1, dbu) deltaW2_max = to_itype(self.corrugation_width2, dbu) for i in range(0, N_boxes): # apply apodization profileFunction = math.exp(-0.5 * (2 * self.a * (i - N / 2) / (N))**2) deltaW1 = deltaW1_max * profileFunction deltaW2 = deltaW2_max * profileFunction vertical_offset = int(round(self.wg2_width / 2 / dbu)) + int( round(self.gap / dbu)) + int( round(self.wg1_width / 2 / dbu)) #+(-int(round(deltaW1))+int(round(deltaW2)))/2 t = Trans(Trans.R0, 0, vertical_offset) if i % 2 == True: x = i * grating_period[i] - box_width[i] / 2 box1_a = Box(x, -half_w1 - deltaW1, x + box_width[i], half_w1 - deltaW1) shapes(LayerSiN).insert(box1_a) box2_a = Box(x + grating_period[i], -half_w2 - deltaW2, x + grating_period[i] + box_width[i], half_w2 - deltaW2).transformed(t) shapes(LayerSiN).insert(box2_a) else: x = (i * grating_period[i] - box_width[i] / 2) box1_b = Box(x, -half_w1, x + box_width[i], half_w1) shapes(LayerSiN).insert(box1_b) box2_b = Box(x + grating_period[i], -half_w2, x + grating_period[i] + box_width[i], half_w2).transformed(t) shapes(LayerSiN).insert(box2_b) # missing periods due to misalignments box_final = Box(x + grating_period[i - 1], -half_w1, x + grating_period[i - 1] + box_width[i - 1], half_w1) shapes(LayerSiN).insert(box_final) box_final = Box(-box_width[i - 1] / 2, -half_w2 - deltaW2, box_width[i - 1] / 2, half_w2 - deltaW2).transformed(t) shapes(LayerSiN).insert(box_final) # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.wg1_width, dbu) t = Trans(Trans.R0, to_itype(-box_width[0] / 2, dbu * 1000), -deltaW1 / 2) 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 w = to_itype(self.wg2_width, dbu) t = Trans(Trans.R0, to_itype(-box_width[0] / 2, dbu * 1000), vertical_offset - deltaW2 / 2) 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 w = to_itype(self.wg2_width, dbu) t = Trans(Trans.R0, to_itype(x + grating_period[0] + box_width[0], dbu * 1000), vertical_offset - deltaW2 / 2) 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("pin3", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu w = to_itype(self.wg1_width, dbu) t = Trans(Trans.R0, to_itype(x + grating_period[0] + box_width[0], dbu * 1000), -deltaW1 / 2) 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("pin4", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4 / dbu
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 LayerSi = self.silayer LayerSi3 = ly.layer(self.si3layer) LayerSiN = ly.layer(LayerSi) 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 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 - to_itype(2.1, dbu) w_via = to_itype(5.0, dbu) h_via = to_itype(5.0, dbu) # Variables for the SiEtch2 layer (Slab) w_Si3 = w_m1_out + 2 * (r_m1_out) 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, (self.r + self.w / 2) / dbu, (self.r + self.g + self.w) / dbu) pcell = ly.create_cell("Ring", "GSiP", { "layer": LayerSi, "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(LayerSi3).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(LayerSi3).insert(pya.Polygon(pin1pts)) shapes(LayerSi3).insert(pya.Polygon(pin2pts)) shapes(LayerSi3).insert(pya.Polygon(pin3pts)) shapes(LayerSi3).insert(pya.Polygon(pin4pts)) from SiEPIC.utils import arc # 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 #Create the VL Layer, as well as the electrical PinRec geometries # 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 poly = pya.Path(arc(self.r / dbu, angle_min_MH, angle_max_MH), w_mh).transformed(t).simple_polygon() self.cell.shapes(LayermhN).insert(poly) 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_Filter_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)
def produce_impl(self): import math try: from SiEPIC.utils.layout import layout_waveguide_sbend, layout_taper except: from siepic_tools.utils.layout import layout_waveguide_sbend, layout_taper # 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) from SiEPIC.extend import to_itype # Draw the Bragg grating (bottom): box_width = int(round(self.grating_period / 2 / dbu)) grating_period = int(round(self.grating_period / dbu)) w = to_itype(self.wg1_width, dbu) GaussianIndex = self.apodization_index half_w = w / 2 half_corrugation_w = to_itype(self.corrugation_width1 / 2, dbu) y_offset_top = -w / 2 - to_itype(self.gap / 2, dbu) if self.AR: misalignment = grating_period / 2 else: misalignment = 0 N = self.number_of_periods if self.sinusoidal: npoints_sin = 40 for i in range(0, self.number_of_periods): x = (round((i * self.grating_period) / dbu)) profileFunction = math.exp(-0.5 * (2 * GaussianIndex * (i - N / 2) / (N))**2) profile = int(round( self.corrugation_width1 / 2 / dbu)) * profileFunction box1 = Box(x, y_offset_top, x + box_width, y_offset_top + half_w + profile) pts1 = [Point(x, y_offset_top)] pts3 = [Point(x + misalignment, y_offset_top)] for i1 in range(0, npoints_sin + 1): x1 = i1 * 2 * math.pi / npoints_sin y1 = round(profile * math.sin(x1)) x1 = round(x1 / 2 / math.pi * grating_period) pts1.append(Point(x + x1, y_offset_top + half_w + y1)) pts3.append( Point(x + misalignment + x1, y_offset_top - half_w - y1)) pts1.append(Point(x + grating_period, y_offset_top)) pts3.append( Point(x + grating_period + misalignment, y_offset_top)) shapes(LayerSiN).insert(Polygon(pts1)) shapes(LayerSiN).insert(Polygon(pts3)) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, y_offset_top, length, y_offset_top + half_w) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, y_offset_top, misalignment, y_offset_top - half_w) shapes(LayerSiN).insert(box3) else: for i in range(0, self.number_of_periods): x = int(round((i * self.grating_period) / dbu)) profileFunction = math.exp(-0.5 * (2 * GaussianIndex * (i - N / 2) / (N))**2) profile = int(round( self.corrugation_width1 / 2 / dbu)) * profileFunction box1 = Box( x, y_offset_top, x + box_width, y_offset_top + to_itype(half_w + profile, dbu * 1000)) box2 = Box( x + box_width, y_offset_top, x + grating_period, y_offset_top + to_itype(half_w - profile, dbu * 1000)) box3 = Box( x + misalignment, y_offset_top, x + box_width + misalignment, y_offset_top + to_itype(-half_w - profile, dbu * 1000)) box4 = Box( x + box_width + misalignment, y_offset_top, x + grating_period + misalignment, y_offset_top + to_itype(-half_w + profile, dbu * 1000)) shapes(LayerSiN).insert(box1) shapes(LayerSiN).insert(box2) shapes(LayerSiN).insert(box3) shapes(LayerSiN).insert(box4) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, y_offset_top, length, y_offset_top + half_w) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, y_offset_top, misalignment, y_offset_top - half_w) shapes(LayerSiN).insert(box3) vertical_offset = int(round(self.wg2_width / 2 / dbu)) + int( round(self.gap / 2 / dbu)) if misalignment > 0: t = Trans(Trans.R0, 0, vertical_offset) else: t = Trans(Trans.R0, 0, vertical_offset) # Draw the Bragg grating (top): box_width = int(round(self.grating_period / 2 / dbu)) grating_period = int(round(self.grating_period / dbu)) w = to_itype(self.wg2_width, dbu) half_w = w / 2 half_corrugation_w = int(round(self.corrugation_width2 / 2 / dbu)) N = self.number_of_periods if self.sinusoidal: npoints_sin = 40 for i in range(0, self.number_of_periods): x = (round((i * self.grating_period) / dbu)) profileFunction = math.exp(-0.5 * (2 * GaussianIndex * (i - N / 2) / (N))**2) profile = int(round( self.corrugation_width2 / 2 / dbu)) * profileFunction box1 = Box(x, 0, x + box_width, -half_w + profile).transformed(t) pts1 = [Point(x, 0)] pts3 = [Point(x + misalignment, 0)] for i1 in range(0, npoints_sin + 1): x1 = i1 * 2 * math.pi / npoints_sin y1 = round(profile * math.sin(x1)) x1 = round(x1 / 2 / math.pi * grating_period) # print("x: %s, y: %s" % (x1,y1)) pts1.append(Point(x + x1, -half_w - y1)) pts3.append(Point(x + misalignment + x1, +half_w + y1)) pts1.append(Point(x + grating_period, 0)) pts3.append(Point(x + grating_period + misalignment, 0)) shapes(LayerSiN).insert(Polygon(pts1).transformed(t)) shapes(LayerSiN).insert(Polygon(pts3).transformed(t)) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, 0, length, -half_w).transformed(t) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment, half_w).transformed(t) shapes(LayerSiN).insert(box3) else: for i in range(0, self.number_of_periods): x = int(round((i * self.grating_period) / dbu)) profileFunction = math.exp(-0.5 * (2 * GaussianIndex * (i - N / 2) / (N))**2) profile = int(round( self.corrugation_width2 / 2 / dbu)) * profileFunction box1 = Box(x, 0, x + box_width, -half_w - profile).transformed(t) box2 = Box(x + box_width, 0, x + grating_period, -half_w + profile).transformed(t) box3 = Box(x + misalignment, 0, x + box_width + misalignment, half_w + profile).transformed(t) box4 = Box(x + box_width + misalignment, 0, x + grating_period + misalignment, half_w - profile).transformed(t) shapes(LayerSiN).insert(box1) shapes(LayerSiN).insert(box2) shapes(LayerSiN).insert(box3) shapes(LayerSiN).insert(box4) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, 0, length, -half_w).transformed(t) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment, half_w).transformed(t) shapes(LayerSiN).insert(box3) # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w1 = to_itype(self.wg1_width, dbu) w2 = to_itype(self.wg2_width, dbu) if self.sbend: port_w = to_itype(self.port_w, dbu) sbend_r = 25000 sbend_length = 15000 sbend_offset = 2 * port_w + port_w - (w1 + w2) / 2 - int( round(self.gap / dbu)) taper_length = 20 * max(abs(w1 - port_w), abs(w2 - port_w)) t = Trans(Trans.R180, 0, y_offset_top) layout_waveguide_sbend(self.cell, LayerSiN, t, w1, sbend_r, sbend_offset, sbend_length) t = Trans(Trans.R0, -sbend_length - taper_length, y_offset_top - sbend_offset) layout_taper(self.cell, LayerSiN, t, port_w, w1, taper_length) x = -sbend_length - taper_length y = y_offset_top - sbend_offset make_pin(self.cell, "opt1", [x, y], port_w, pin_length, LayerPinRecN, vertical=0) t = Trans(Trans.R180, 0, vertical_offset) layout_taper(self.cell, LayerSiN, t, w2, w2, sbend_length / 2) t = Trans(Trans.R180, -sbend_length / 2, vertical_offset) layout_taper(self.cell, LayerSiN, t, w2, port_w, taper_length + sbend_length / 2) x = -taper_length - sbend_length y = vertical_offset make_pin(self.cell, "opt2", [x, y], port_w, pin_length, LayerPinRecN, vertical=0) t = Trans(Trans.R0, length, y_offset_top) layout_waveguide_sbend(self.cell, LayerSiN, t, w1, sbend_r, -sbend_offset, sbend_length) t = Trans(Trans.R0, length + sbend_length, y_offset_top - sbend_offset) layout_taper(self.cell, LayerSiN, t, w1, port_w, taper_length) x = length + sbend_length + taper_length y = y_offset_top - sbend_offset make_pin(self.cell, "opt3", [x, y], port_w, -pin_length, LayerPinRecN, vertical=0) t = Trans(Trans.R0, length, vertical_offset) layout_taper(self.cell, LayerSiN, t, w2, w2, sbend_length / 2) t = Trans(Trans.R0, length + sbend_length / 2, vertical_offset) layout_taper(self.cell, LayerSiN, t, w2, port_w, taper_length + sbend_length / 2) x = length + taper_length + sbend_length y = vertical_offset make_pin(self.cell, "opt4", [x, y], port_w, -pin_length, LayerPinRecN, vertical=0) else: x = 0 y = y_offset_top make_pin(self.cell, "opt1", [x, y], w1, pin_length, LayerPinRecN, vertical=0) y = vertical_offset make_pin(self.cell, "opt2", [x, y], w2, pin_length, LayerPinRecN, vertical=0) x = length y = y_offset_top make_pin(self.cell, "opt3", [x, y], w1, -pin_length, LayerPinRecN, vertical=0) x = length y = vertical_offset make_pin(self.cell, "opt4", [x, y], w2, -pin_length, LayerPinRecN, vertical=0) # Compact model information t = Trans(Trans.R0, 10, 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, 10, 500) text = Text('Component=contra_directional_coupler', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1 / dbu t = Trans(Trans.R0, 10, -500) text = Text \ ('Spice_param:number_of_periods=%s grating_period=%.3fu wg1_width=%.3fu wg2_width=%.3fu corrugation_width1=%.3fu corrugation_width2=%.3fu gap=%.3fu apodization_index=%.3f AR=%s sinusoidal=%s accuracy=%s' %\ (self.number_of_periods, self.grating_period, self.wg1_width, self.wg2_width, self.corrugation_width1, self.corrugation_width2, self.gap, self.apodization_index, int(self.AR), int(self.sinusoidal), int(self.accuracy)), 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. if self.sbend: box = pya.Box( pya.Point(-taper_length - sbend_length, vertical_offset + 3 / 2 * port_w), pya.Point(length + taper_length + sbend_length, y_offset_top - sbend_offset - 3 / 2 * port_w)) else: box = pya.Box( pya.Point(0, vertical_offset + 3 / 2 * (w1 + w2) / 2), pya.Point(length, y_offset_top - 3 / 2 * (w1 + w2) / 2)) shapes(LayerDevRecN).insert(box)
def produce_impl(self): import math from SiEPIC.extend import to_itype # 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) # Draw the Bragg grating (bottom): box_width = int(round(self.grating_period/2/dbu)) grating_period = int(round(self.grating_period/dbu)) w = to_itype(self.wg1_width,dbu) GaussianIndex = self.index half_w = w/2 half_corrugation_w = int(round(self.corrugation_width1/2/dbu)) if self.AR: misalignment = grating_period/2 else: misalignment = 0 N = self.number_of_periods if self.sinusoidal: npoints_sin = 40 for i in range(0,self.number_of_periods): x = (round((i * self.grating_period)/dbu)) deltaW1 = int(round(self.corrugation_width1/2/dbu)) box1 = Box(x, 0, x + box_width, half_w+deltaW1) pts1 = [Point(x,0)] pts3 = [Point(x + misalignment,0)] for i1 in range(0,npoints_sin+1): x1 = i1 * 2* math.pi / npoints_sin y1 = round(deltaW1*math.sin(x1)) x1 = round(x1/2/math.pi*grating_period) # print("x: %s, y: %s" % (x1,y1)) pts1.append( Point(x + x1,half_w+y1 ) ) pts3.append( Point(x + misalignment + x1,-half_w-y1 ) ) pts1.append( Point(x + grating_period, 0) ) pts3.append( Point(x + grating_period + misalignment, 0) ) shapes(LayerSiN).insert(Polygon(pts1)) shapes(LayerSiN).insert(Polygon(pts3)) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, 0, length, half_w) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment, -half_w) shapes(LayerSiN).insert(box3) else: for i in range(0,self.number_of_periods): x = int(round((i * self.grating_period)/dbu)) deltaW1 = int(round(self.corrugation_width1/2/dbu)) box1 = Box(x, 0, x + box_width, half_w+deltaW1) box2 = Box(x + box_width, 0, x + grating_period, half_w-deltaW1) box3 = Box(x + misalignment, 0, x + box_width + misalignment, -half_w-deltaW1) box4 = Box(x + box_width + misalignment, 0, x + grating_period + misalignment, -half_w+deltaW1) shapes(LayerSiN).insert(box1) shapes(LayerSiN).insert(box2) shapes(LayerSiN).insert(box3) shapes(LayerSiN).insert(box4) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, 0, length, half_w) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment, -half_w) shapes(LayerSiN).insert(box3) vertical_offset = int(round(self.wg2_width/2/dbu))+int(round(self.gap/dbu))+int(round(self.wg1_width/2/dbu)) if misalignment > 0: t = Trans(Trans.R0, 0,vertical_offset) else: t = Trans(Trans.R0, 0,vertical_offset) # Draw the Bragg grating (top): box_width = int(round(self.grating_period/2/dbu)) grating_period = int(round(self.grating_period/dbu)) w = to_itype(self.wg2_width,dbu) GaussianIndex = self.index half_w = w/2 half_corrugation_w = int(round(self.corrugation_width2/2/dbu)) N = self.number_of_periods if self.sinusoidal: npoints_sin = 40 for i in range(0,self.number_of_periods): periodGap = int(round(self.gap/dbu)) vertical_offset = int(round(self.wg2_width/2/dbu))+periodGap+int(round(self.wg1_width/2/dbu)) if misalignment > 0: t = Trans(Trans.R0, 0,vertical_offset) else: t = Trans(Trans.R0, 0,vertical_offset) x = (round((i * self.grating_period)/dbu)) deltaW2 = int(round(self.corrugation_width2/2/dbu)); box1 = Box(x, 0, x + box_width, -half_w+deltaW2).transformed(t) pts1 = [Point(x,0)] pts3 = [Point(x + misalignment,0)] for i1 in range(0,npoints_sin+1): x1 = i1 * 2* math.pi / npoints_sin y1 = round(deltaW2*math.sin(x1)) x1 = round(x1/2/math.pi*grating_period) # print("x: %s, y: %s" % (x1,y1)) pts1.append( Point(x + x1,-half_w-y1 ) ) pts3.append( Point(x + misalignment + x1,+half_w+y1 ) ) pts1.append( Point(x + grating_period, 0) ) pts3.append( Point(x + grating_period + misalignment, 0) ) shapes(LayerSiN).insert(Polygon(pts1).transformed(t)) shapes(LayerSiN).insert(Polygon(pts3).transformed(t)) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, 0, length, -half_w).transformed(t) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment, half_w).transformed(t) shapes(LayerSiN).insert(box3) else: for i in range(0,self.number_of_periods): x = int(round((i * self.grating_period)/dbu)) periodGap = int(round(self.gap/dbu)) + 2*int(round(self.H/dbu)) *(1-math.exp( (-self.index*(i-0.5*N)**2)/(N**2) )) vertical_offset = int(round(self.wg2_width/2/dbu))+periodGap+int(round(self.wg1_width/2/dbu)) if misalignment > 0: t = Trans(Trans.R0, 0,vertical_offset) else: t = Trans(Trans.R0, 0,vertical_offset) deltaW2 = int(round(self.corrugation_width2/2/dbu)); box1 = Box(x, 0, x + box_width, -half_w-deltaW2).transformed(t) box2 = Box(x + box_width, 0, x + grating_period, -half_w+deltaW2).transformed(t) box3 = Box(x + misalignment, 0, x + box_width + misalignment, half_w+deltaW2).transformed(t) box4 = Box(x + box_width + misalignment, 0, x + grating_period + misalignment, half_w-deltaW2).transformed(t) shapes(LayerSiN).insert(box1) shapes(LayerSiN).insert(box2) shapes(LayerSiN).insert(box3) shapes(LayerSiN).insert(box4) length = x + grating_period + misalignment if misalignment > 0: # extra piece at the end: box2 = Box(x + grating_period, 0, length, -half_w).transformed(t) shapes(LayerSiN).insert(box2) # extra piece at the beginning: box3 = Box(0, 0, misalignment, half_w).transformed(t) shapes(LayerSiN).insert(box3) # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.wg1_width,dbu) 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 w = to_itype(self.wg2_width,dbu) t = Trans(Trans.R0, 0,vertical_offset) 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 ("pin3", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4/dbu w = to_itype(self.wg1_width,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 w = to_itype(self.wg2_width,dbu) t = Trans(Trans.R0, length,vertical_offset) 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 ("pin4", t) shape = shapes(LayerPinRecN).insert(text) shape.text_size = 0.4/dbu # 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 ('Component=ebeam_contra_dc', t) shape = shapes(LayerDevRecN).insert(text) shape.text_size = 0.1/dbu t = Trans(Trans.R0, length/9, 0) text = Text \ ('Spice_param:number_of_periods=%s grating_period=%.3fu corrugation_width=%.3fu misalignment=%.3fu sinusoidal=%s' %\ (self.number_of_periods, self.grating_period, self.corrugation_width1, misalignment, int(self.sinusoidal)), 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. t = Trans(Trans.R0, 0,0) path = Path([Point(0, vertical_offset/2), Point(length, vertical_offset/2)], 3*w) shapes(LayerDevRecN).insert(path.simple_polygon())
def produce(self, layout, layers, parameters, cell): # coerce parameters (make consistent) self._layers = layers self.cell = cell self._param_values = parameters self.layout = layout # cell: layout cell to place the layout # LayerSiN: which layer to use # r: radius # w: waveguide width # length units in dbu from math import pi, cos, sin from SiEPIC.utils import arc_wg, arc_wg_xy from SiEPIC._globals import PIN_LENGTH from SiEPIC.utils.layout import layout_waveguide2 from SiEPIC.extend import to_itype # fetch the parameters TECHNOLOGY = get_technology_by_name('EBeam') dbu = self.layout.dbu ly = self.layout LayerSiN = ly.layer(self.silayer) LayerSlab = ly.layer(self.slayer) # LayerNN = ly.layer(self.nlayer) LayerNPPN = ly.layer(self.npplayer) LayerVCN = ly.layer(self.vclayer) LayerMN = ly.layer(self.mlayer) LayerPinRecN = ly.layer(self.pinrec) LayerDevRecN = ly.layer(self.devrec) TextLayerN = ly.layer(self.textl) ## define parameters for phase shifter overlay = to_itype(self.overlay, dbu) overlay_ebl = to_itype(self.overlay_ebl, dbu) ## define waveguide related parameters radius_um, adiab, bezier = 5, 1, 0.2 w = to_itype(self.width, dbu) #waveguide width, default 0.5 um l = to_itype(self.length, dbu) #pahse shifter length, default 150 um npp_d = to_itype(self.npp_distance, dbu) #npp to center waveguide distance sw = (npp_d + w / 2) * 2 #twice of npp_d that is defined above in_rib = to_itype( self.input_rib_width, dbu ) #the slab width of the input/output waveguide to the phase shifter in_taper = to_itype( self.in_taper_length, dbu ) #the input waveguide to phase shifter dimension transfer taper length, default 15 edge_slab_width = to_itype( 4, dbu ) #width of the 90 nm slab that is outside of all the folding waveguide, default 2 * 2um npp_width = to_itype(self.npp_width, dbu) #npp width, default 5 um ## define waveguide folding parameters folding_n = int(round((self.fold_number - 1) / 2)) # folding waveguide number on one side folding_w1 = w + 50 # one of the folding waveguide width folding_w2 = w - 50 # the other one of the folding waveguide width folding_gap = to_itype(2, dbu) # the gap between two folding waveguide routing_step = to_itype( radius_um, dbu) # define how large the U shape should be, default 20 ## define node related parameters contact_size = to_itype(self.vc_w, dbu) # square NPP size, for VC purpose pin_text_size = to_itype(0.4, dbu) # pin text size metal_routing_distance_to_node = to_itype( 30, dbu) # *** calculate from PCell parameters *** metal_routing_width = to_itype(self.m_w, dbu) # width of metal vc_to_npp_exclusion = to_itype( self.overlay, dbu) # VC boundary to NPP boundary distance ps_sl_w = sw + 2 * contact_size + folding_n * 2 * ( w + folding_gap + overlay_ebl ) # overall pahse shifter 90nm slab width # measure the total length of the waveguides total_waveguide_length = 0 ## define strip to slab transfer taper parameters N = 100 # Number of points for the input/output slab taper order = 3 # input/output slab taper curve pts = [Point(-l / 2, 0), Point(l / 2, 0)] total_waveguide_length += layout_waveguide2(TECHNOLOGY, self.layout, self.cell, ['Si'], [w * dbu], [0], pts, radius_um, adiab, bezier) wg2 = pya.Box(-l / 2, -ps_sl_w / 2, l / 2, ps_sl_w / 2) wg3 = pya.Box(-l / 2, npp_d + w / 2, l / 2, npp_d + w / 2 + npp_width) wg4 = pya.Box(-l / 2, -npp_d - w / 2, l / 2, -npp_d - w / 2 - npp_width) self.cell.shapes(LayerNPPN).insert(wg3) self.cell.shapes(LayerNPPN).insert(wg4) if self.io_wg_type: in_slab = to_itype(self.input_slab_width, dbu) else: in_slab = in_rib if folding_n > 0: ps_sl_w = contact_size * 2 + sw * 2 + ( folding_w1 + folding_w2 + folding_gap * 2) * folding_n + edge_slab_width wg2 = pya.Box(-l / 2, -ps_sl_w / 2, l / 2, ps_sl_w / 2) self.cell.shapes(LayerSlab).insert(wg2) pts = [ Point(-l / 2 - in_taper, -w / 2), Point(-l / 2, -w / 2), Point(-l / 2, w / 2), Point(-l / 2 - in_taper, w / 2) ] # fix waveguide width mismatch for left center input taper self.cell.shapes(LayerSiN).insert(Polygon(pts)) # add input slab taper pts = [] for i in range(0, N + 1): pts.append( Point( -l / 2 - in_taper + in_taper / N * i, w / 2 + ((sw / 2 + contact_size - w) / (N**order)) * (i**order))) for i in range(0, N + 1): pts.append( Point( -l / 2 - in_taper + in_taper / N * (N - i), -w / 2 - ((sw / 2 + contact_size - w) / (N**order)) * ((N - i)**order))) self.cell.shapes(LayerSlab).insert(Polygon(pts)) # add output strip taper pts = [ Point(l / 2 + in_taper, -w / 2), Point(l / 2, -w / 2), Point(l / 2, w / 2), Point(l / 2 + in_taper, w / 2) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) total_waveguide_length += in_taper * dbu # add input slab taper pts = [] for i in range(0, N + 1): pts.append( Point(l / 2 + in_taper - in_taper / N * i, (w - overlay_ebl) / 2 + ((sw / 2 + contact_size - (w - overlay_ebl)) / (N**order)) * (i**order))) for i in range(0, N + 1): pts.append( Point( l / 2 + in_taper / N * i, -(w - overlay_ebl) / 2 - ((sw / 2 + contact_size - (w - overlay_ebl)) / (N**order)) * ((N - i)**order))) self.cell.shapes(LayerSlab).insert(Polygon(pts)) for i in range(0, folding_n): y_coordinate = contact_size + sw + 2 * vc_to_npp_exclusion # move up/down the position according to vc_to_npp_exclusion y_move = i * (folding_w1 + folding_w2 + folding_gap * 2) / 2 + y_coordinate if i % 2: folding_w = folding_w2 else: folding_w = folding_w1 # waveguide in the heated region pts = [Point(-l / 2, y_move), Point(l / 2, y_move)] total_waveguide_length += layout_waveguide2( TECHNOLOGY, self.layout, self.cell, ['Si'], [folding_w * dbu], [0], pts, radius_um, adiab, bezier) pts = [Point(-l / 2, -y_move), Point(l / 2, -y_move)] total_waveguide_length += layout_waveguide2( TECHNOLOGY, self.layout, self.cell, ['Si'], [folding_w * dbu], [0], pts, radius_um, adiab, bezier) # add upper left input taper pts = [ Point(-l / 2 - in_taper, -w / 2 + y_move), Point(-l / 2, -folding_w / 2 + y_move), Point(-l / 2, folding_w / 2 + y_move), Point(-l / 2 - in_taper, w / 2 + y_move) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) total_waveguide_length += in_taper * dbu # slab cubic taper pts = [] for ii in range(0, N + 1): pts.append( Point(-l / 2 - in_taper + in_taper / N * ii, (w - overlay_ebl) / 2 + ((2 / dbu - (w - overlay_ebl)) / (N**order)) * (ii**order) + y_move)) for ii in range(0, N + 1): pts.append( Point( -l / 2 - in_taper + in_taper / N * (N - ii), -(w - overlay_ebl) / 2 - ((2 / dbu - (w - overlay_ebl)) / (N**order)) * ((N - ii)**order) + y_move)) self.cell.shapes(LayerSlab).insert(Polygon(pts)) if (i != folding_n - 1): # add below left taper y_move = -y_move pts = [ Point(-l / 2 - in_taper, -w / 2 + y_move), Point(-l / 2, -folding_w / 2 + y_move), Point(-l / 2, folding_w / 2 + y_move), Point(-l / 2 - in_taper, w / 2 + y_move) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) total_waveguide_length += in_taper * dbu # slab cubic taper pts = [] for ii in range(0, N + 1): pts.append( Point(-l / 2 - in_taper + in_taper / N * ii, (w - overlay_ebl) / 2 + ((2 / dbu - (w - overlay_ebl)) / (N**order)) * (ii**order) + y_move)) for ii in range(0, N + 1): pts.append( Point( -l / 2 - in_taper + in_taper / N * (N - ii), -(w - overlay_ebl) / 2 - ((2 / dbu - (w - overlay_ebl)) / (N**order)) * ((N - ii)**order) + y_move)) self.cell.shapes(LayerSlab).insert(Polygon(pts)) # add upper right taper y_move = -y_move pts = [ Point(l / 2 + in_taper, -w / 2 + y_move), Point(l / 2, -folding_w / 2 + y_move), Point(l / 2, folding_w / 2 + y_move), Point(l / 2 + in_taper, w / 2 + y_move) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) total_waveguide_length += in_taper * dbu # slab cubic taper pts = [] for ii in range(0, N + 1): pts.append( Point(l / 2 + in_taper - in_taper / N * ii, (w - overlay_ebl) / 2 + ((2 / dbu - (w - overlay_ebl)) / (N**order)) * (ii**order) + y_move)) for ii in range(0, N + 1): pts.append( Point( l / 2 + in_taper / N * ii, -(w - overlay_ebl) / 2 - ((2 / dbu - (w - overlay_ebl)) / (N**order)) * ((N - ii)**order) + y_move)) self.cell.shapes(LayerSlab).insert(Polygon(pts)) else: y_move = -y_move # add input strip taper pts = [ Point(-l / 2 - in_taper, -w / 2 + y_move), Point(-l / 2, -folding_w / 2 + y_move), Point(-l / 2, folding_w / 2 + y_move), Point(-l / 2 - in_taper, w / 2 + y_move) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) total_waveguide_length += in_taper * dbu # add input slab taper pts = [] for i in range(0, N + 1): pts.append( Point( -l / 2 - in_taper + in_taper / N * i, in_slab / 2 + ((edge_slab_width - in_slab) / (N**order)) * (i**order) + y_move)) for i in range(0, N + 1): pts.append( Point( -l / 2 - in_taper + in_taper / N * (N - i), -in_slab / 2 - ((edge_slab_width - in_slab) / (N**order)) * ((N - i)**order) + y_move)) self.cell.shapes(LayerSlab).insert(Polygon(pts)) # add output strip taper y_move = -y_move pts = [ Point(l / 2 + in_taper, -w / 2 + y_move), Point(l / 2, -folding_w / 2 + y_move), Point(l / 2, folding_w / 2 + y_move), Point(l / 2 + in_taper, w / 2 + y_move) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) total_waveguide_length += in_taper * dbu # add input slab taper pts = [] for i in range(0, N + 1): pts.append( Point( l / 2 + in_taper - in_taper / N * i, in_slab / 2 + ((edge_slab_width - in_slab) / (N**order)) * (i**order) + y_move)) for i in range(0, N + 1): pts.append( Point( l / 2 + in_taper / N * i, -in_slab / 2 - ((edge_slab_width - in_slab) / (N**order)) * ((N - i)**order) + y_move)) self.cell.shapes(LayerSlab).insert(Polygon(pts)) # Pin on the left side: wg_start = -(l / 2 + in_taper + routing_step * folding_n * 3 + w) p1 = [ Point(wg_start + PIN_LENGTH / 2, -y_move), Point(wg_start - PIN_LENGTH / 2, -y_move) ] p1c = Point(wg_start, -y_move) self.set_p1 = p1c self.p1 = p1c pin = Path(p1, w) self.cell.shapes(LayerPinRecN).insert(pin) t = Trans(Trans.R0, wg_start, -y_move) text = Text("pin1", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = pin_text_size # Waveguide to route to port, new in SiEPIC-Tools v0.3.64 pts = [ Point(-l / 2 - in_taper, -y_move), Point(wg_start, -y_move) ] if self.io_wg_type: waveguide_length = layout_waveguide2( TECHNOLOGY, self.layout, self.cell, ['Si', 'Si - 90 nm rib'], [w * dbu, in_slab * dbu], [0, 0], pts, radius_um, adiab, bezier) else: waveguide_length = layout_waveguide2( TECHNOLOGY, self.layout, self.cell, ['Si'], [w * dbu], [0], pts, radius_um, adiab, bezier) # Pin & Waveguide on the right side: wg_end = l / 2 + in_taper + routing_step * folding_n * 3 + w p2 = [ Point(wg_end - PIN_LENGTH / 2, y_move), Point(wg_end + PIN_LENGTH / 2, y_move) ] p2c = Point(wg_end, y_move) self.set_p2 = p2c self.p2 = p2c pin = Path(p2, w) self.cell.shapes(LayerPinRecN).insert(pin) t = Trans(Trans.R0, wg_end, y_move) text = Text("pin2", t) shape = self.cell.shapes(LayerPinRecN).insert(text) shape.text_size = pin_text_size # Waveguide to route to port, new in SiEPIC-Tools v0.3.64 pts = [ Point(l / 2 + in_taper, y_move), Point(wg_end, y_move) ] if self.io_wg_type: total_waveguide_length += layout_waveguide2( TECHNOLOGY, self.layout, self.cell, ['Si', 'Si - 90 nm rib'], [w * dbu, in_slab * dbu], [0, 0], pts, radius_um, adiab, bezier) else: total_waveguide_length += layout_waveguide2( TECHNOLOGY, self.layout, self.cell, ['Si'], [w * dbu], [0], pts, radius_um, adiab, bezier) # add below right taper y_move = -y_move pts = [ Point(l / 2 + in_taper, -w / 2 + y_move), Point(l / 2, -folding_w / 2 + y_move), Point(l / 2, folding_w / 2 + y_move), Point(l / 2 + in_taper, w / 2 + y_move) ] #wg2 = pya.Box(-l/2,-4/2/dbu,l/2,4/2/dbu) self.cell.shapes(LayerSiN).insert(Polygon(pts)) total_waveguide_length += in_taper * dbu pts = [] for ii in range(0, N + 1): pts.append( Point(l / 2 + in_taper - in_taper / N * ii, (w - overlay_ebl) / 2 + ((2 / dbu - (w - overlay_ebl)) / (N**order)) * (ii**order) + y_move)) for ii in range(0, N + 1): pts.append( Point( l / 2 + in_taper / N * ii, -(w - overlay_ebl) / 2 - ((2 / dbu - (w - overlay_ebl)) / (N**order)) * ((N - ii)**order) + y_move)) self.cell.shapes(LayerSlab).insert(Polygon(pts)) # add metal contact and folded waveguides for i in range(-folding_n, folding_n): y_coordinate = contact_size + sw + 2 * vc_to_npp_exclusion # move up/down the position according to vc_to_npp_exclusion if i < 0: y_origin = (i + 1) * (folding_w1 + folding_w2 + folding_gap * 2) / 2 - y_coordinate if i == 0: y_origin = 0 if i > 0: y_origin = (i - 1) * (folding_w1 + folding_w2 + folding_gap * 2) / 2 + y_coordinate if (i + 1) < 0: y_end = (i + 2) * (folding_w1 + folding_w2 + folding_gap * 2) / 2 - y_coordinate if (i + 1) == 0: y_end = 0 if (i + 1) > 0: y_end = i * (folding_w1 + folding_w2 + folding_gap * 2) / 2 + y_coordinate # Calculate the Folded waveguide vertices if (i + folding_n) % 2: # Waveguides on the left side if y_origin + routing_step * 2 - y_end < 2 * radius_um: y_routing = max(routing_step * 2, y_end - y_origin) else: y_routing = y_end + routing_step * 2 path = Path([ Point(-l / 2 - in_taper, y_origin), Point( -l / 2 - in_taper - routing_step * (1.5 * folding_n - 1.5 * i + 1.5), y_origin), Point( -l / 2 - in_taper - routing_step * (1.5 * folding_n - 1.5 * i + 1.5), y_routing), Point( -l / 2 - in_taper - routing_step * (1.5 * folding_n - 1.5 * i - 0.5), y_routing), Point( -l / 2 - in_taper - routing_step * (1.5 * folding_n - 1.5 * i - 0.5), y_end), Point(-l / 2 - in_taper, y_end) ], 0) else: # Waveguides on the right side if y_origin - y_end + routing_step * 2 < 2 * radius_um: y_routing = -max(routing_step * 2, y_end - y_origin) else: y_routing = y_origin - routing_step * 2 path = Path([ Point(l / 2 + in_taper, y_origin), Point( l / 2 + in_taper + routing_step * (1.5 * folding_n + 1.5 * i + 1), y_origin), Point( l / 2 + in_taper + routing_step * (1.5 * folding_n + 1.5 * i + 1), y_routing), Point( l / 2 + in_taper + routing_step * (1.5 * folding_n + 1.5 * i + 3), y_routing), Point( l / 2 + in_taper + routing_step * (1.5 * folding_n + 1.5 * i + 3), y_end), Point(l / 2 + in_taper, y_end) ], 0) # Draw the waveguide geometry, new in SiEPIC-Tools v0.3.64 path.unique_points() pts = path.get_points() total_waveguide_length += layout_waveguide2( TECHNOLOGY, self.layout, self.cell, ['Si'], [w * dbu], [0], pts, radius_um, adiab, bezier) for i in range(0, self.segments + 1): wg_temp_upper = pya.Box( (l - contact_size) / self.segments * i - l / 2 - vc_to_npp_exclusion, sw / 2, (l - contact_size) / self.segments * i - l / 2 + contact_size + vc_to_npp_exclusion, sw / 2 + contact_size + 2 * vc_to_npp_exclusion) #self.cell.shapes(LayerNN).insert(wg_temp_upper) self.cell.shapes(LayerNPPN).insert(wg_temp_upper) # self.cell.shapes(LayerNN).insert(wg_temp_upper) wg_temp_upper = pya.Box( (l - contact_size) / self.segments * i - l / 2 - 2 * vc_to_npp_exclusion, sw / 2, (l - contact_size) / self.segments * i - l / 2 + contact_size + 2 * vc_to_npp_exclusion, sw / 2 + contact_size + 3 * vc_to_npp_exclusion) self.cell.shapes(LayerSlab).insert(wg_temp_upper) vc_temp_upper = pya.Box( (l - contact_size) / self.segments * i - l / 2, sw / 2 + vc_to_npp_exclusion, (l - contact_size) / self.segments * i - l / 2 + contact_size, sw / 2 + contact_size + vc_to_npp_exclusion) self.cell.shapes(LayerVCN).insert(vc_temp_upper) wg_temp_lower = pya.Box( (l - contact_size) / self.segments * i - l / 2 - vc_to_npp_exclusion, -sw / 2, (l - contact_size) / self.segments * i - l / 2 + contact_size + vc_to_npp_exclusion, -sw / 2 - contact_size - 2 * vc_to_npp_exclusion) #self.cell.shapes(LayerNN).insert(wg_temp_lower) self.cell.shapes(LayerNPPN).insert(wg_temp_lower) wg_temp_lower = pya.Box( (l - contact_size) / self.segments * i - l / 2 - 2 * vc_to_npp_exclusion, -sw / 2, (l - contact_size) / self.segments * i - l / 2 + contact_size + 2 * vc_to_npp_exclusion, -sw / 2 - contact_size - 3 * vc_to_npp_exclusion) self.cell.shapes(LayerSlab).insert(wg_temp_lower) # self.cell.shapes(LayerNN).insert(wg_temp_lower) vc_temp_lower = pya.Box( (l - contact_size) / self.segments * i - l / 2, -sw / 2 - vc_to_npp_exclusion, (l - contact_size) / self.segments * i - l / 2 + contact_size, -sw / 2 - contact_size - vc_to_npp_exclusion) self.cell.shapes(LayerVCN).insert(vc_temp_lower) metal_temp = pya.Box( (l - contact_size) / self.segments * i - l / 2 - vc_to_npp_exclusion, -sw / 2 - contact_size - metal_routing_distance_to_node * (i % 2) - 2 * vc_to_npp_exclusion, (l - contact_size) / self.segments * i - l / 2 + contact_size + vc_to_npp_exclusion, sw / 2 + contact_size + metal_routing_distance_to_node * ((i + 1) % 2) + 2 * vc_to_npp_exclusion) self.cell.shapes(LayerMN).insert(metal_temp) metal_routing = Path([ Point(-l / 2, -sw / 2 - metal_routing_distance_to_node - contact_size), Point(l / 2, -sw / 2 - metal_routing_distance_to_node - contact_size) ], metal_routing_width) self.cell.shapes(LayerMN).insert(metal_routing) metal_routing = Path([ Point(-l / 2, sw / 2 + metal_routing_distance_to_node + contact_size), Point(l / 2, sw / 2 + metal_routing_distance_to_node + contact_size) ], metal_routing_width) self.cell.shapes(LayerMN).insert(metal_routing) if folding_n > 0: dev = Box( wg_start, -sw / 2 - metal_routing_distance_to_node - contact_size - metal_routing_width, wg_end, sw / 2 + metal_routing_distance_to_node + contact_size + metal_routing_width) self.cell.shapes(LayerDevRecN).insert(dev) t = Trans(0, False, Point(0, 0)) text = Text ( \ 'Waveguide Length=%.3fu' %(total_waveguide_length), t, 3*w, -1) shape = self.cell.shapes(LayerDevRecN).insert(text)
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 produce_impl(self): from SiEPIC._globals import PIN_LENGTH from SiEPIC.extend import to_itype import math from pya import DPolygon pi = math.pi # This is the main part of the implementation: create the layout # 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) #variables pitch = self.pitch w = self.w r = self.r ff = self.ff angle = self.angle gap = self.gap gap2 = self.gap2 row = self.row busL = self.busL taperL = self.taperL #doublebus = self.doublebus #print(doublebus) if (r - w / 2 <= 0): r = 5 print('invalid radius, set r to default: 5') if row <= 1: row = 1 print('invalid number of rows, set row to default: 1') if busL <= 10: busL = taperL * 2 + 10 print( 'invalid length of SWG bus waveguide, set length at least 2 times larger than taper length and pluse 10 um' ) #Calculate number of segments s1 = pitch * ff #silicon s2 = pitch - s1 #gap # Draw the Multi-box Ring for i in range(0, int(row)): # draw different radius SWG rings #calculate best radius #const = math.ceil(2*pi*r/(s1+s2)) const = math.floor(2 * pi * r / (s1 + s2)) #if doesn't divide evenly, replace r with best possible r if ((2 * pi * r) % (s1 + s2) != 0): r = const * (s1 + s2) / (2 * pi) print('r adjusted to ' + str(r) + 'um to fit periods perfectly.') theta1 = math.atan(s1 / r) theta2 = math.atan(s2 / r) nSeg = int( math.floor(angle / (math.degrees(theta1) + math.degrees(theta2)))) #how many segments to have si_first = True #for alternating between silicon and gap j = 0 #index of how many silicon thetas jj = 0 #index of how many gap thetas ORDER = True #ordering of the coordinates for polygon drawing xo = [(r - w / 2) * math.cos(0)] yo = [(r - w / 2) * math.sin(0)] xo.append((r + w / 2) * math.cos(0)) yo.append((r + w / 2) * math.sin(0)) for i in range(0, nSeg * 2): if si_first: j = j + 1 si_first = not (si_first) else: jj = jj + 1 si_first = not (si_first) if ORDER: xo.append((r + w / 2) * math.cos(j * theta1 + jj * theta2)) yo.append((r + w / 2) * math.sin(j * theta1 + jj * theta2)) xo.append((r - w / 2) * math.cos(j * theta1 + jj * theta2)) yo.append((r - w / 2) * math.sin(j * theta1 + jj * theta2)) ORDER = not (ORDER) else: xo.append((r - w / 2) * math.cos(j * theta1 + jj * theta2)) yo.append((r - w / 2) * math.sin(j * theta1 + jj * theta2)) xo.append((r + w / 2) * math.cos(j * theta1 + jj * theta2)) yo.append((r + w / 2) * math.sin(j * theta1 + jj * theta2)) ORDER = not (ORDER) if len(xo) == 4: dpts = [pya.DPoint(xo[i], yo[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) xo = [] yo = [] r = r - (w + gap) # Draw the Bus Waveguides #Draw Bus WG # to go back to the initial point for Multi-box drawing, ger the first adjusted r value r = self.r const = math.floor(2 * pi * r / (s1 + s2)) #if doesn't divide evenly, replace r with best possible r if ((2 * pi * r) % (s1 + s2) != 0): r = const * (s1 + s2) / (2 * pi) #calulate ideal length of bus bus_length = busL #bus_length = self.cell.bbox().height()*dbu +pitch*2 constant = math.ceil(bus_length / (s1 + s2)) if bus_length % (s1 + s2) != 0: bus_length = constant * (s1 + s2) for ii in range(0, int(row)): xo = [(r + w / 2 + gap2 + ii * (w + gap)), (r + w / 2 + gap2 + w + ii * (w + gap)), (r + w / 2 + gap2 + w + ii * (w + gap)), (r + w / 2 + gap2 + ii * (w + gap))] yo = [0, 0, s1, s1] dpts = [pya.DPoint(xo[i], yo[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) # draw the bus waveguide for i in range(0, int(math.ceil((constant) / 2))): yu = [yo[j] + i * pitch for j in range(len(yo))] yd = [yo[j] - i * pitch for j in range(len(yo))] dpts = [pya.DPoint(xo[i], yu[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) dpts = [pya.DPoint(xo[i], yd[i]) for i in range(len(xo))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) # draw the tapers from waveguide to SWG xtu = [ ((r + w / 2 + gap2 + ii * (w + gap)) + (w - 0.06) / 2), ((r + w / 2 + gap2 + ii * (w + gap)) + (w - 0.06) / 2 + 0.06), ((r + w / 2 + gap2 + ii * (w + gap)) + w + gap / 2), ((r + w / 2 + gap2 + ii * (w + gap)) - gap / 2) ] ytu = [(yu[3] - taperL), (yu[3] - taperL), (yu[3]), (yu[3])] dpts = [pya.DPoint(xtu[i], ytu[i]) for i in range(len(xtu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) ytd = [(yd[1] + taperL), (yd[1] + taperL), (yd[1]), (yd[1])] dpts = [pya.DPoint(xtu[i], ytd[i]) for i in range(len(xtu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) # taper connections from multi-tapers to std WG xTu = [(r + w / 2 + gap2 - gap / 2), (r + w / 2 + gap2 - gap / 2 + row * (gap + w)), (r + w / 2 + gap2 + row * (gap + w) / 2 + 0.25), (r + w / 2 + gap2 + row * (gap + w) / 2 - 0.25)] yTu = [(yu[3]), (yu[3]), (yu[3] + taperL), (yu[3] + taperL)] dpts = [pya.DPoint(xTu[i], yTu[i]) for i in range(len(xTu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) yTd = [(yd[1]), (yd[1]), (yd[1] - taperL), (yd[1] - taperL)] dpts = [pya.DPoint(xTu[i], yTd[i]) for i in range(len(xTu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) # add std WG if 2 * r >= busL + 2 * taperL: xstdu = [(r + w / 2 + gap2 + row * (gap + w) / 2 - 0.25), (r + w / 2 + gap2 + row * (gap + w) / 2 + 0.25), (r + w / 2 + gap2 + row * (gap + w) / 2 + 0.25), (r + w / 2 + gap2 + row * (gap + w) / 2 - 0.25)] ystdu = [(yu[3] + taperL), (yu[3] + taperL), (yu[3] + r - busL / 2 + 1), (yu[3] + r - busL / 2 + 1)] dpts = [pya.DPoint(xstdu[i], ystdu[i]) for i in range(len(xstdu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) ystdd = [(yd[1] - taperL), (yd[1] - taperL), (yd[1] - r + busL / 2 - 1), (yd[1] - r + busL / 2 - 1)] dpts = [pya.DPoint(xstdu[i], ystdd[i]) for i in range(len(xstdu))] dpolygon = DPolygon(dpts) element = Polygon.from_dpoly(dpolygon * (1.0 / dbu)) shapes(LayerSiN).insert(element) else: xstdu = xTu ystdu = yTu ystdd = yTd #DEV BOX half_l = (self.cell.bbox().width() - row * (gap + w) / dbu - gap2 / dbu + (gap / 2) / dbu) / 2 half_r = self.cell.bbox().width() - half_l dev = Box(-half_l, ystdd[-1] / dbu - 1, half_r, ystdu[-1] / dbu + 1) shapes(LayerDevRecN).insert(dev) dev_width = self.cell.bbox().width() / 2 if 2 * r >= busL + 2 * taperL: dev_up = yu[-1] / dbu + taperL / dbu + (yu[3] + r - busL / 2 + 1 - (yu[3] + taperL)) / dbu dev_down = yd[-4] / dbu - taperL / dbu - (yu[3] + r - busL / 2 + 1 - (yu[3] + taperL)) / dbu else: dev_up = yu[-1] / dbu + taperL / dbu dev_down = yd[-4] / dbu - taperL / dbu # Create the pins on the waveguides, as short paths: from SiEPIC._globals import PIN_LENGTH as pin_length w = to_itype(self.w, dbu) gap = to_itype(self.gap, dbu) bus_length = to_itype(bus_length / 2, dbu) #Pin1 #t = Trans(Trans.R0, dev_width-w/2,dev_up) t = Trans(Trans.R0, xstdu[-1] / dbu + 251, dev_up + 1) pin = Path([Point(0, -pin_length / 2), Point(0, pin_length / 2)], 500) #500 is width of std WG 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 print(dev_up) print(dev_up * dbu) #Pin2 t = Trans(Trans.R0, xstdu[-1] / dbu + 251, dev_down - 1) pin = Path([Point(0, pin_length / 2), Point(0, -pin_length / 2)], 500) 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