From 88b18a16ca61223be7f36d28932a92ea482aa2cf Mon Sep 17 00:00:00 2001 From: Hang-Bobby-Zou <45744171+Hang-Bobby-Zou@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:04:25 -0700 Subject: [PATCH] Added support for GC, and a spiral for SiN components in the beta Library --- .../AA_EBeam_GC_SiN_1310_12deg.py | 319 ++++++++++++++++++ .../pymacros/pcells_EBeam_Beta/spiral_SiN.py | 220 ++++++++++++ 2 files changed, 539 insertions(+) create mode 100644 klayout/EBeam/pymacros/pcells_EBeam_Beta/AA_EBeam_GC_SiN_1310_12deg.py create mode 100644 klayout/EBeam/pymacros/pcells_EBeam_Beta/spiral_SiN.py diff --git a/klayout/EBeam/pymacros/pcells_EBeam_Beta/AA_EBeam_GC_SiN_1310_12deg.py b/klayout/EBeam/pymacros/pcells_EBeam_Beta/AA_EBeam_GC_SiN_1310_12deg.py new file mode 100644 index 00000000..4ce8c070 --- /dev/null +++ b/klayout/EBeam/pymacros/pcells_EBeam_Beta/AA_EBeam_GC_SiN_1310_12deg.py @@ -0,0 +1,319 @@ +from pya import * +import pya + +def linspace_without_numpy(low, high, length): + step = ((high-low) * 1.0 / length) + return [low+i*step for i in range(length)] + +class AA_EBeam_GC_SiN_1310_12deg(pya.PCellDeclarationHelper): + """ + Universal Grating Coupler PCell implementation. + Analytical design based on "Grating Coupler Design Based on Silicon-On-Insulator", Yun Wang (2013). Master's Thesis, University of British Columbia, Canada + Some PCell implementation adapted from the SiEPIC_EBeam library by Dr. Lukas Chrostowski, University of British Columbia, Canada + Orignal script written by Timothy Richards (Simon Fraser University, BC, Canada) and Adam DeAbreu (Simon Fraser University, BC, Canada) + Modified to use UGC method presented in : Yun Wang, Jonas Flueckiger, Charlie Lin, Lukas Chrostowski, "Universal grating coupler design," Proc. SPIE 8915, Photonics North 2013, 89150Y (11 October 2013), + Transfered from Mentor Graphics to KLayout by Connor Mosquera (University of British Columbia, Canada) + + Adapted by: Hang (Bobby) Zou + Date Edited: 2024-02-06 + + """ + + def __init__(self): + + # Important: initialize the super class + super(AA_EBeam_GC_SiN_1310_12deg, self).__init__() + from SiEPIC.utils import get_technology_by_name, load_Waveguides_by_Tech + TECHNOLOGY = get_technology_by_name('EBeam') + self.TECHNOLOGY = TECHNOLOGY + + # declare the parameters + self.param("wavelength", self.TypeDouble, "Design Wavelength (micron)", default = 1.31) + self.param("Si_thickness", self.TypeDouble, "SiN Thickness (micron)", default = 0.4) + self.param("etch_depth", self.TypeDouble, "Etch Depth (micron)", default = 0.4) + self.param("pol", self.TypeString, "Polarization", default = "TE") + self.param("n_t", self.TypeDouble, "Cladding Index", default = 1.4718) + self.param("angle_e", self.TypeDouble, "Taper Angle (deg)", default = 36.0) + self.param("grating_length", self.TypeDouble, "Grating Length (micron)", default = 30.0) + self.param("taper_length", self.TypeDouble, "Taper Length (micron)", default = 20) + self.param("dc", self.TypeDouble, "Duty Cylce", default = 0.474) + self.param('pitch', self.TypeDouble, "Pitch (micron)", default = 0.956) + self.param("t", self.TypeDouble, "Waveguide Width (micron)", default = 0.75) + self.param("theta_c", self.TypeDouble, "Insertion Angle (deg)", default = 8.121) + + # Layer parameters + self.param("layer", self.TypeLayer, "Layer", default = TECHNOLOGY['SiN']) + self.param("pinrec", self.TypeLayer, "PinRec Layer", default = TECHNOLOGY['PinRec']) + self.param("devrec", self.TypeLayer, "DevRec Layer", default = TECHNOLOGY['DevRec']) + + def display_text_impl(self): + # Provide a descriptive text for the cell + # return "EBeam_GC_SiN_1310_8deg_%.1f-%.2f-%.2f-%.2f-%.2f-%.2f" % \ + # (self.wavelength, self.theta_c, self.dc, self.angle_e, self.taper_length, self.t) + return "AA_EBeam_GC_SiN_1310_12deg" + + def coerce_parameters_impl(self): + pass + + def can_create_from_shape(self, layout, shape, layer): + return False + + def produce_impl(self): + # fetch the parameters + dbu = self.layout.dbu + ly = self.layout + shapes = self.cell.shapes + TECHNOLOGY = self.TECHNOLOGY + + LayerSi = self.layer + LayerSiN = ly.layer(LayerSi) + LayerSiSPN = ly.layer(LayerSi) + LayerPinRecN = ly.layer(self.pinrec) + LayerDevRecN = ly.layer(self.devrec) + LayerTextN = TECHNOLOGY['Text'] + + ######## effective index function ########## + + def effective_index(wl = self.wavelength, etch_depth = self.etch_depth, Si_thickness = self.Si_thickness, n_t = self.n_t, pol = self.pol, dc = self.dc): + + from math import pi, cos, sin, log, sqrt, tan + from SiEPIC.utils import points_per_circle + + point =1001 + n_0 = n_t # Top cladding + n_1 = 0 + n_3 = 1.4718 # Bottom Cladding + # if 0: # Silicon + # n_2 = sqrt(7.9874+(3.68*pow(3.9328,2)*pow(10,30))/((pow(3.9328,2)*pow(10,30)-pow(2*3.14*3*pow(10,8)/(wl*pow(10,-6)),2)))) # Silicon wavelength-dependant index of refraction + # if 1: # Silicon Nitride + n_2 = 2.001667 # approximate + delta = n_0 - n_3 + t = Si_thickness + t_slot = t - etch_depth + k_0 = 2*pi/wl + + b_0 = linspace_without_numpy(0, 0, point-1) + te_0 = linspace_without_numpy(0, 0, point-1) + te_1 = linspace_without_numpy(0, 0, point-1) + tm_0 = linspace_without_numpy(0, 0, point-1) + tm_1 = linspace_without_numpy(0, 0, point-1) + h_0 = linspace_without_numpy(0, 0, point-1) + q_0 = linspace_without_numpy(0, 0, point-1) + p_0 = linspace_without_numpy(0, 0, point-1) + qbar_0 = linspace_without_numpy(0, 0, point-1) + pbar_0 = linspace_without_numpy(0, 0, point-1) + + # calculating neff for the silicon layer + if delta<0: + n_1 = n_3 + else: + n_1 = n_0 + + for ii in range(0,point-1): + b_0[ii] = n_1*k_0 + (n_2 - n_1)*k_0/(point-1)*ii # copied from .ample UGC: should this be point-1? + + h_0[ii] = sqrt( abs(pow(n_2*k_0,2) - pow(b_0[ii],2))); + q_0[ii] = sqrt( abs(pow(b_0[ii],2) - pow(n_0*k_0,2))); + p_0[ii] = sqrt( abs(pow(b_0[ii],2) - pow(n_3*k_0,2))); + + pbar_0[ii] = pow(n_2/n_3, 2)*p_0[ii] + qbar_0[ii] = pow(n_2/n_0, 2)*q_0[ii] + + # calculating neff for TE mode + if pol == "TE": + for ii in range(0, point-1): + te_0[ii] = tan( h_0[ii]*t )-(p_0[ii]+q_0[ii])/h_0[ii]/(1-p_0[ii]*q_0[ii]/pow(h_0[ii],2)) + te_1[ii] = tan( h_0[ii]*t_slot )-(p_0[ii]+q_0[ii])/h_0[ii]/(1-p_0[ii]*q_0[ii]/pow(h_0[ii],2)) + + abs_te_0 = [abs(x) for x in te_0] + abs_te_1 = [abs(x) for x in te_1] + index_TE_0 = abs_te_0.index(min(abs_te_0)) + index_TE_1 = abs_te_1.index(min(abs_te_1)) + nTE_0 = b_0[index_TE_0]/k_0 + nTE_1 = b_0[index_TE_1]/k_0 + nTE_1 = n_3 + + while (nTE_0<1.5 or nTE_0>3): + abs_te_0[index_TE_0] = 100 + index_TE_0 = abs_te_0.index(min(abs_te_0)) + nTE_0 = b_0[index_TE_0]/k_0 + + ''' + while (nTE_1<1.5 or nTE_1>3): + abs_te_1[index_TE_1] = 100 + index_TE_1 = abs_te_1.index(min(abs_te_1)) + nTE_1 = b_0[index_TE_1]/k_0 + ''' + + ne=dc*nTE_0+(1-dc)*nTE_1 + + # calculating neff for TE mode + elif pol == "TM": + for ii in range(0, point-1): + tm_0[ii] = tan( h_0[ii]*t )-(pbar_0[ii]+qbar_0[ii])/h_0[ii]/(1-pbar_0[ii]*qbar_0[ii]/pow(h_0[ii],2)) + tm_1[ii] = tan( h_0[ii]*t_slot )-(pbar_0[ii]+qbar_0[ii])/h_0[ii]/(1-pbar_0[ii]*qbar_0[ii]/pow(h_0[ii],2)) + + abs_tm_0 = [abs(x) for x in tm_0] + abs_tm_1 = [abs(x) for x in tm_1] + index_TM_0 = abs_tm_0.index(min(abs_tm_0)) + index_TM_1 = abs_tm_1.index(min(abs_tm_1)) + nTM_0 = b_0[index_TM_0]/k_0 + nTM_1 = b_0[index_TM_1]/k_0 + + while (nTM_0<1.5 or nTM_0>3): + abs_tm_0[index_TM_0] = 100 + index_TM_0 = abs_tm_0.index(min(abs_tm_0)) + nTM_0 = b_0[index_TM_0]/k_0 + + while (nTM_1<1.5 or nTM_1>3): + abs_tm_1[index_TM_1] = 100 + index_TM_1 = abs_tm_1.index(min(abs_tm_1)) + nTM_1 = b_0[index_TM_1]/k_0 + + ne=dc*nTM_0+(1-dc)*nTM_1 + + else: + print('Please type TE or TM for polarization') + + return ne + ##################################### + + from math import pi, cos, sin, log, sqrt, tan + from SiEPIC.utils import points_per_circle + + lambda_0 = self.wavelength ##um wavelength of light + + n_e = effective_index() + #n_e = 1.6975992 + ne_fiber = 1 # effective index of the mode in the air + period = self.wavelength/(n_e - sin(pi/180*self.theta_c)*ne_fiber) + period = self.pitch + + + # Geometry + wh = period*self.dc ##thick grating + + gc_number = int(round(self.grating_length/period)) ##number of periods + e = self.n_t*sin((pi/180)*self.theta_c)/n_e + N = round(self.taper_length*(1+e)*n_e/lambda_0) ##allows room for the taper + + start = (pi - (pi/180)*self.angle_e/2) + stop = (pi + (pi/180)*self.angle_e/2) + + radius_1 = 999 # random number so we know there is an error if radius_1 is not overwritten + radius_2 = 999 + # Draw coupler grating. + for j in range(gc_number): + + # number of points in the arcs: + # calculate such that the vertex & edge placement error is < 0.5 nm. + # see "SiEPIC_EBeam_functions - points_per_circle" for more details + radius = N*lambda_0 / (n_e*( 1 - e )) + j*period + + if j == 0: + radius_1 = radius + + if j == 1: + radius_2 = radius + + seg_points = int(points_per_circle(radius/dbu, dbu=dbu)/360.*self.angle_e) # number of points grating arc + theta_up = [] + for m in range(seg_points+1): + theta_up = theta_up + [start + m*(stop-start)/seg_points] + theta_down = theta_up[::-1] + + ##big one + r_up = [] + r_down = [] + for k in range(len(theta_up)): + r_up = r_up + [N*lambda_0 / (n_e*( 1 - e*cos(float(theta_up[k])) )) + j*period+period*(1-self.dc)] + r_down = r_up[::-1] + + xr = [] + yr = [] + for k in range(len(theta_up)): + xr = xr + [r_up[k]*cos(theta_up[k])] + yr = yr + [r_up[k]*sin(theta_up[k])] + + xl = [] + yl = [] + for k in range(len(theta_down)): + xl = xl + [(r_down[k] + wh)*cos(theta_down[k])] + yl = yl + [(r_down[k] + wh)*sin(theta_down[k])] + + x = xr + xl + y = yr + yl + + pts = [] + for i in range(len(x)): + pts.append(Point.from_dpoint(DPoint(x[i]/dbu, y[i]/dbu))) + + polygon = Polygon(pts) + shapes(LayerSiN).insert(polygon) + + # Taper section + r_up = [] + r_down = [] + for k in range(len(theta_up)): + r_up = r_up + [N*lambda_0 / (n_e*( 1 - e*cos(float(theta_up[k])) ))] + r_down = r_up[::-1] + + xl = [] + yl = [] + for k in range(len(theta_down)): + xl = xl + [(r_down[k])*cos(theta_down[k])] + yl = yl + [(r_down[k])*sin(theta_down[k])] + + yr = [self.t/2., -self.t/2.] + + yl_abs = [] + for k in range(len(yl)): + yl_abs = yl_abs + [abs(yl[k])] + + y_max = max(yl_abs) + iy_max = yl_abs.index(y_max) + + L_o = (y_max - self.t/2)/tan((pi/180)*self.angle_e/2) + + xr = [0, 0] + + x = xr + xl + y = yr + yl + + pts = [] + for i in range(len(x)): + pts.append(Point.from_dpoint(DPoint(x[i]/dbu, y[i]/dbu))) + + polygon = Polygon(pts) + shapes(LayerSiN).insert(polygon) + + + # Pin on the waveguide: + from SiEPIC._globals import PIN_LENGTH as pin_length + x = 0 + t = Trans(Trans.R0, x,0) + pin = Path([Point(-pin_length/2,0), Point(pin_length/2,0)], self.t/dbu) + pin_t = pin.transformed(t) + shapes(LayerPinRecN).insert(pin_t) + text = Text ("pin1", t) + shape = shapes(LayerPinRecN).insert(text) + shape.text_size = 0.4/dbu + + # Reference information + t = Trans(Trans.R0, 0,-4000) + text = Text ("Ref: 'Universal grating coupler design'\nhttps://doi.org/10.1117/12.2042185\nPCell implementation by: Yun Wang, Timothy Richards, Adam DeAbreu,\nJonas Flueckiger, Charlie Lin, Lukas Chrostowski, Connor Mosquera\n Adapted by Hang Bobby Zou", t) + shape = shapes(LayerPinRecN).insert(text) + shape.text_size = 0.5/dbu + shape.text_halign=2 # right alignment + + t = Trans(Trans.R0, 0,4000) + text = Text ("Wavelength: %s\nIncident Angle: %s\nPolarization: %s\nSiN thickness: %s\nSiN etch depth: %s\nDuty Cycle: %s\nPitch: %s\nRadius_1: %s\nRadius_2: %s\nn_eff: %s\ne: %s\nN: %s\n" % (self.wavelength, self.theta_c, self.pol, self.Si_thickness, self.etch_depth, self.dc, self.pitch, radius_1, radius_2,n_e,e,N), t) + shape = shapes(LayerPinRecN).insert(text) + shape.text_size = 0.5/dbu + shape.text_halign=2 # right alignment + shape.text_valign=2 # bottom alignment + + # Device recognition layer + yr = sin(start) * (N*lambda_0 / ( n_e*( 1 - e*cos(float(start)) )) + gc_number*period ) + box1 = Box(-(self.grating_length+self.taper_length)/dbu-pin_length*2, yr/dbu, 0, -yr/dbu) + shapes(LayerDevRecN).insert(box1) diff --git a/klayout/EBeam/pymacros/pcells_EBeam_Beta/spiral_SiN.py b/klayout/EBeam/pymacros/pcells_EBeam_Beta/spiral_SiN.py new file mode 100644 index 00000000..74b092c3 --- /dev/null +++ b/klayout/EBeam/pymacros/pcells_EBeam_Beta/spiral_SiN.py @@ -0,0 +1,220 @@ +import pya +from SiEPIC.utils import get_technology_by_name +from pya import * + +class spiral_SiN(pya.PCellDeclarationHelper): + """ + Input: + """ + + def __init__(self): + + # Important: initialize the super class + super(spiral_SiN, self).__init__() + TECHNOLOGY = get_technology_by_name('EBeam') + + # declare the parameters + self.param("length", self.TypeDouble, "Target Waveguide length", default = 500) + self.param("wg_width", self.TypeDouble, "Waveguide width (microns)", default = 0.75) + self.param("min_radius", self.TypeDouble, "Minimum radius (microns)", default = 60) + self.param("wg_spacing", self.TypeDouble, "Waveguide spacing (microns)", default = 1) + self.param("spiral_ports", self.TypeInt, "Ports on the same side? 0/1", default = 0) + self.param("layer", self.TypeLayer, "Layer", default = TECHNOLOGY['SiN']) + self.param("pinrec", self.TypeLayer, "PinRec Layer", default = TECHNOLOGY['PinRec']) + self.param("devrec", self.TypeLayer, "DevRec Layer", default = TECHNOLOGY['DevRec']) + + def display_text_impl(self): + # Provide a descriptive text for the cell + return "spiral_SiN_%s-%.3f-%.3f-%.3f" % \ + (self.length, self.wg_width, self.min_radius, self.wg_spacing) + + def coerce_parameters_impl(self): + pass + + def can_create_from_shape(self, layout, shape, layer): + return False + + def produce_impl(self): + debug = False + # 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 spiral + from math import pi, cos, sin, log, sqrt + + # Archimedes spiral + # r = b + a * theta + b = self.min_radius + spacing = self.wg_spacing+self.wg_width + a = 2*spacing/(2*pi) + + # area, length, turn tracking for spiral + area = 0 + spiral_length = 0 + turn = -1 + + from SiEPIC.utils import points_per_circle, arc_wg_xy + + while spiral_length < self.length: + turn +=1 + + # Spiral #1 + pts = [] + # local radius: + r = 2*b + a * turn * 2 * pi - self.wg_width/2 + # number of points per circle: + npoints = int(points_per_circle(r)) + # increment, in radians, for each point: + da = 2 * pi / npoints + # draw the inside edge of spiral + for i in range(0, npoints+1): + t = i*da + xa = (a*t + r) * cos(t) + ya = (a*t + r) * sin(t) + pts.append(Point.from_dpoint(DPoint(xa/dbu, ya/dbu))) + # draw the outside edge of spiral + r = 2*b + a * turn * 2 * pi + self.wg_width/2 + npoints = int(points_per_circle(r)) + da = 2 * pi / npoints + for i in range(npoints, -1, -1): + t = i*da + xa = (a*t + r) * cos(t) + ya = (a*t + r) * sin(t) + pts.append(Point.from_dpoint(DPoint(xa/dbu, ya/dbu))) + polygon = Polygon(pts) + area += polygon.area() + shapes(LayerSiN).insert(polygon) + + # Spiral #2 + pts = [] + # local radius: + r = 2*b + a * turn * 2 * pi - self.wg_width/2 - spacing + # number of points per circle: + npoints = int(points_per_circle(r)) + # increment, in radians, for each point: + da = 2 * pi / npoints + # draw the inside edge of spiral + for i in range(0, npoints+1): + t = i*da + pi + xa = (a*t + r) * cos(t) + ya = (a*t + r) * sin(t) + pts.append(Point.from_dpoint(DPoint(xa/dbu, ya/dbu))) + # draw the outside edge of spiral + r = 2*b + a * turn * 2 * pi + self.wg_width/2 - spacing + npoints = int(points_per_circle(r)) + da = 2 * pi / npoints + for i in range(npoints, -1, -1): + t = i*da + pi + xa = (a*t + r) * cos(t) + ya = (a*t + r) * sin(t) + pts.append(Point.from_dpoint(DPoint(xa/dbu, ya/dbu))) + polygon = Polygon(pts) + area += polygon.area() + shapes(LayerSiN).insert(polygon) + + # waveguide length: + spiral_length = area / self.wg_width * dbu*dbu + 2 * pi * self.min_radius + + if self.spiral_ports: + # Spiral #1 extra 1/2 arm + turn = turn + 1 + pts = [] + # local radius: + r = 2*b + a * turn * 2 * pi - self.wg_width/2 + # number of points per circle: + npoints = int(points_per_circle(r)) + # increment, in radians, for each point: + da = pi / npoints + # draw the inside edge of spiral + for i in range(0, npoints+1): + t = i*da + xa = (a*t + r) * cos(t) + ya = (a*t + r) * sin(t) + pts.append(Point.from_dpoint(DPoint(xa/dbu, ya/dbu))) + # draw the outside edge of spiral + r = 2*b + a * turn * 2 * pi + self.wg_width/2 + npoints = int(points_per_circle(r)) + da = pi / npoints + for i in range(npoints, -1, -1): + t = i*da + xa = (a*t + r) * cos(t) + ya = (a*t + r) * sin(t) + pts.append(Point.from_dpoint(DPoint(xa/dbu, ya/dbu))) + polygon = Polygon(pts) + area += polygon.area() + shapes(LayerSiN).insert(polygon) + turn = turn - 1 + # waveguide length: + spiral_length = area / self.wg_width * dbu*dbu + 2 * pi * self.min_radius + + # Centre S-shape connecting waveguide + #layout_arc_wg_dbu(self.cell, LayerSiN, -b/dbu, 0, b/dbu, self.wg_width/dbu, 0, 180) + self.cell.shapes(LayerSiN).insert(arc_wg_xy(-b/dbu, 0, b/dbu, self.wg_width/dbu, 0, 180)) + #layout_arc_wg_dbu(self.cell, LayerSiN, b/dbu, 0, b/dbu, self.wg_width/dbu, 180, 0) + self.cell.shapes(LayerSiN).insert(arc_wg_xy(b/dbu, 0, b/dbu, self.wg_width/dbu, 180, 0)) + if debug: + print("spiral length: %s microns" % spiral_length) + + # Pins on the waveguide: + from SiEPIC._globals import PIN_LENGTH as pin_length + + x = -(2*b + a * (turn+1) * 2 * pi)/dbu + w = self.wg_width / dbu + t = Trans(Trans.R0, x,0) + 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 self.spiral_ports: + x = -(2*b + a * (turn+1.5) * 2 * pi)/dbu + else: + x = (2*b + a * (turn+1) * 2 * pi)/dbu + t = Trans(Trans.R0, x,0) + 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 + + # Compact model information + t = Trans(Trans.R0, -abs(x), 0) + text = Text ('Length=%.3fu' % spiral_length, t) + shape = shapes(LayerDevRecN).insert(text) + shape.text_size = abs(x)/8 + 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, 0, w*2) + text = Text ('Component=ebeam_wg_strip_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 min_radius=%.3fu wg_spacing=%.3fu' %\ + (spiral_length, self.wg_width, (self.min_radius), self.wg_spacing), 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. + x = abs(x) + npoints = int(points_per_circle(x) /10 ) + da = 2 * pi / npoints # increment, in radians + r=x + 2 * self.wg_width/dbu + pts = [] + for i in range(0, npoints+1): + pts.append(Point.from_dpoint(DPoint(r*cos(i*da), r*sin(i*da)))) + shapes(LayerDevRecN).insert(Polygon(pts)) + + # print("spiral done.")