Esempio n. 1
0
def arc_bezier(radius, start, stop, bezier, DevRec=None):
    from math import sin, cos, pi
    from SiEPIC.utils import points_per_circle
    N = points_per_circle(radius / 1000) / 4
    if DevRec:
        N = int(N / 3)
    else:
        N = int(N)
    if N < 5:
        N = 100
    L = radius  # effective bend radius / Length of the bend
    diff = 1. / (N - 1)  # convert int to float
    xp = [0, (1 - bezier) * L, L, L]
    yp = [0, 0, bezier * L, L]
    xA = xp[3] - 3 * xp[2] + 3 * xp[1] - xp[0]
    xB = 3 * xp[2] - 6 * xp[1] + 3 * xp[0]
    xC = 3 * xp[1] - 3 * xp[0]
    xD = xp[0]
    yA = yp[3] - 3 * yp[2] + 3 * yp[1] - yp[0]
    yB = 3 * yp[2] - 6 * yp[1] + 3 * yp[0]
    yC = 3 * yp[1] - 3 * yp[0]
    yD = yp[0]

    pts = [pya.Point(-L, 0) + pya.Point(xD, yD)]
    for i in range(1, N - 1):
        t = i * diff
        pts.append(
            pya.Point(-L, 0) +
            pya.Point(t**3 * xA + t**2 * xB + t * xC + xD, t**3 * yA +
                      t**2 * yB + t * yC + yD))
    pts.extend([pya.Point(0, L - 1), pya.Point(0, L)])
    return pts
Esempio n. 2
0
def arc_wg_xy(x, y, r, w, theta_start, theta_stop, DevRec=None):
    # function to draw an arc of waveguide
    # x, y: location of the origin
    # r: radius
    # w: waveguide width
    # length units in dbu
    # theta_start, theta_stop: angles for the arc
    # angles in degrees

    from math import pi, cos, sin
    from . import points_per_circle

    circle_fraction = abs(theta_stop - theta_start) / 360.0
    npoints = int(points_per_circle(r / 1000) * circle_fraction)
    if DevRec:
        npoints = int(npoints / 3)
    if npoints == 0:
        npoints = 1
    da = 2 * pi / npoints * circle_fraction  # increment, in radians
    pts = []
    th = theta_start / 360.0 * 2 * pi
    for i in range(0, npoints + 1):
        pts.append(
            pya.Point.from_dpoint(
                pya.DPoint((x + (r + w / 2) * cos(i * da + th)) / 1,
                           (y + (r + w / 2) * sin(i * da + th)) / 1)))
    for i in range(npoints, -1, -1):
        pts.append(
            pya.Point.from_dpoint(
                pya.DPoint((x + (r - w / 2) * cos(i * da + th)) / 1,
                           (y + (r - w / 2) * sin(i * da + th)) / 1)))
    return pya.Polygon(pts)
Esempio n. 3
0
def layout_waveguide_rel(cell, layer, start_point, points, w, radius):
    # create a path, then convert to a polygon waveguide with bends
    # cell: cell into which to place the waveguide
    # layer: layer to draw on
    # start_point: starting vertex for the waveguide
    # points: array of vertices, relative to start_point
    # w: waveguide width

    # example usage:
    # cell = pya.Application.instance().main_window().current_view().active_cellview().cell
    # LayerSi = LayerInfo(1, 0)
    # points = [ [15, 2.75], [30, 2.75] ]  # units of microns.
    # layout_waveguide_rel(cell, LayerSi, [0,0], points, 0.5, 10)

    #print("* layout_waveguide_rel(%s, %s, %s, %s)" % (cell.name, layer, w, radius) )

    ly = cell.layout()
    dbu = cell.layout().dbu

    start_point = [start_point[0] / dbu, start_point[1] / dbu]

    a1 = []
    for p in points:
        a1.append(pya.DPoint(float(p[0]), float(p[1])))

    wg_path = pya.DPath(a1, w)

    npoints = points_per_circle(radius / dbu)
    param = {
        "npoints": npoints,
        "radius": float(radius),
        "path": wg_path,
        "layer": layer
    }

    pcell = ly.create_cell("ROUND_PATH", "Basic", param)

    # Configure the cell location
    trans = Trans(Point(start_point[0], start_point[1]))

    # Place the PCell
    cell.insert(pya.CellInstArray(pcell.cell_index(), trans))
Esempio n. 4
0
def arc(r, theta_start, theta_stop):
    # function to draw an arc of waveguide
    # radius: radius
    # w: waveguide width
    # length units in dbu
    # theta_start, theta_stop: angles for the arc
    # angles in degrees

    from math import pi, cos, sin
    from . import points_per_circle

    circle_fraction = abs(theta_stop - theta_start) / 360.0
    npoints = int(points_per_circle(r/1000) * circle_fraction)
    if npoints == 0:
        npoints = 1
    da = 2 * pi / npoints * circle_fraction  # increment, in radians
    pts = []
    th = theta_start / 360.0 * 2 * pi
    for i in range(0, npoints + 1):
        pts.append(pya.Point.from_dpoint(pya.DPoint(
            (r * cos(i * da + th)) / 1, (r * sin(i * da + th)) / 1)))
    return pts
Esempio n. 5
0
    def produce_impl(self):
        # fetch the parameters
        dbu = self.layout.dbu
        ly = self.layout
        shapes = self.cell.shapes
        TECHNOLOGY = get_technology_by_name('EBeam')

        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
            n_1 = 0
            n_3 = 1.444
            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
            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

                while (nTE_0 < 2 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 < 2 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()
        ne_fiber = 1  # effective index of the mode in the air
        period = self.wavelength / (n_e -
                                    sin(pi / 180 * self.theta_c) * ne_fiber)

        # 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)

        # 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
            seg_points = int(
                points_per_circle(radius / dbu) / 360. *
                self.angle_e)  # number of points grating arc
            theta_up = []
            for m in range(seg_points + 1):
                theta_up = theta_up + [start + m * (stop - start) / seg_points]
            theta_down = theta_up[::-1]

            ##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",
            t)
        shape = shapes(LayerPinRecN).insert(text)
        shape.text_size = 0.4 / dbu
        shape.text_halign = 2  # right alignment

        t = Trans(Trans.R0, 0, 4000)
        text = Text(
            "Wavelength: %s\nIncident Angle: %s\nPolarization: %s\nSilicon thickness: %s\nSilicon etch depth: %s"
            % (self.wavelength, self.theta_c, self.pol, self.Si_thickness,
               self.etch_depth), t)
        shape = shapes(LayerPinRecN).insert(text)
        shape.text_size = 0.4 / 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)
Esempio n. 6
0
def layout_waveguide_sbend(cell, layer, trans, w=500, r=25000, h=2000, length=15000):
    """ Lays out an s-bend

    Args:
        trans: pya.Trans: location and rotation
        w: width of waveguide, float
        r: radius, float
        h: height, float
        length: length, float

    """

    from math import pi, cos, sin, log, sqrt, acos
    from SiEPIC.utils import points_per_circle
    import pya
    
    theta = acos(float(r-abs(h/2))/r)*180/pi
    x = 2*r*sin(theta/180.0*pi)
    straight_l = (length - x)/2

    if (straight_l < 0):
        # Problem: target length is too short. increase
        print('SBend, too short: straight_l = %s' % straight_l)
        length += -straight_l + 1
        straight_l = 1

    waveguide_length = (2*pi*r*(2*theta/360.0)+straight_l*2)
    
    # print('SBend: theta %s, x %s, straight_l %s, r %s, h %s' % (theta, x, straight_l, r, h) )

    # define the cell origin as the left side of the waveguide sbend

    if (straight_l >= 0):
      circle_fraction = abs(theta) / 360.0
      npoints = int(points_per_circle(r) * circle_fraction)
      if npoints == 0:
        npoints = 1
      da = 2 * pi / npoints * circle_fraction # increment, in radians
      x1=straight_l
      x2=length-straight_l

      if h>0:
        y1=r
        theta_start1 = 270
        y2=h-r
        theta_start2 = 90
        pts = []
        th1 = theta_start1 / 360.0 * 2 * pi
        th2 = theta_start2 / 360.0 * 2 * pi
        pts.append(pya.Point.from_dpoint(pya.DPoint(0,w/2)))
        pts.append(pya.Point.from_dpoint(pya.DPoint(0,-w/2)))
        for i in range(0, npoints+1): # lower left
          pts.append(pya.Point.from_dpoint(pya.DPoint((x1+(r+w/2)*cos(i*da+th1))/1, (y1+(r+w/2)*sin(i*da+th1))/1)))
        for i in range(npoints, -1, -1): # lower right
          pts.append(pya.Point.from_dpoint(pya.DPoint((x2+(r-w/2)*cos(i*da+th2))/1, (y2+(r-w/2)*sin(i*da+th2))/1)))
        pts.append(pya.Point.from_dpoint(pya.DPoint(length,h-w/2)))
        pts.append(pya.Point.from_dpoint(pya.DPoint(length,h+w/2)))
        for i in range(0, npoints+1): # upper right
         pts.append(pya.Point.from_dpoint(pya.DPoint((x2+(r+w/2)*cos(i*da+th2))/1, (y2+(r+w/2)*sin(i*da+th2))/1)))
        for i in range(npoints, -1, -1): # upper left
          pts.append(pya.Point.from_dpoint(pya.DPoint((x1+(r-w/2)*cos(i*da+th1))/1, (y1+(r-w/2)*sin(i*da+th1))/1)))
      else:
        y1=-r
        theta_start1 = 90-theta
        y2=r+h
        theta_start2 = 270-theta
        pts = []
        th1 = theta_start1 / 360.0 * 2 * pi
        th2 = theta_start2 / 360.0 * 2 * pi
        pts.append(pya.Point.from_dpoint(pya.DPoint(length,h-w/2)))
        pts.append(pya.Point.from_dpoint(pya.DPoint(length,h+w/2)))
        for i in range(npoints, -1, -1): # upper right
          pts.append(pya.Point.from_dpoint(pya.DPoint((x2+(r-w/2)*cos(i*da+th2))/1, (y2+(r-w/2)*sin(i*da+th2))/1)))
        for i in range(0, npoints+1): # upper left
          pts.append(pya.Point.from_dpoint(pya.DPoint((x1+(r+w/2)*cos(i*da+th1))/1, (y1+(r+w/2)*sin(i*da+th1))/1)))
        pts.append(pya.Point.from_dpoint(pya.DPoint(0,w/2)))
        pts.append(pya.Point.from_dpoint(pya.DPoint(0,-w/2)))
        for i in range(npoints, -1, -1): # lower left
          pts.append(pya.Point.from_dpoint(pya.DPoint((x1+(r-w/2)*cos(i*da+th1))/1, (y1+(r-w/2)*sin(i*da+th1))/1)))
        for i in range(0, npoints+1): # lower right
         pts.append(pya.Point.from_dpoint(pya.DPoint((x2+(r+w/2)*cos(i*da+th2))/1, (y2+(r+w/2)*sin(i*da+th2))/1)))
      cell.shapes(layer).insert(pya.Polygon(pts).transformed(trans))

    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)
        LayerSiSPN = ly.layer(LayerSi)
        LayerPinRecN = ly.layer(self.pinrec)
        LayerDevRecN = ly.layer(self.devrec)

        from math import pi, cos, sin, log, sqrt, tan
        from SiEPIC.utils import points_per_circle

        lambda_0 = self.wavelength  ##um wavelength of light
        #    pin_length =0.0                 ##um extra nub for the waveguide attachment

        # Geometry
        wh = self.period * self.dc  ##thick grating
        wl = self.ff * (self.period - wh)  ## thin grating
        spacing = (self.period - wh - wl) / 2  ##space between thick and thin

        gc_number = int(round(self.grating_length /
                              self.period))  ##number of periods
        e = self.n_t * sin((pi / 180) * self.theta_c) / self.n_e
        N = round(self.taper_length * (1 + e) * self.n_e /
                  lambda_0)  ##allows room for the taper

        start = (pi - (pi / 180) * self.angle_e / 2)
        stop = (pi + (pi / 180) * self.angle_e / 2)

        # Draw coupler grating.
        for j in range(gc_number):

            # number of points in the arcs:
            # calculate such that the vertex & edge placement error is < 0.5 nm.
            #   see "SiEPIC_EBeam_functions - points_per_circle" for more details
            radius = N * lambda_0 / (self.n_e *
                                     (1 - e)) + j * self.period + spacing
            seg_points = int(
                points_per_circle(radius / dbu) / 360. *
                self.angle_e)  # number of points grating arc
            theta_up = []
            for m in range(seg_points + 1):
                theta_up = theta_up + [start + m * (stop - start) / seg_points]
            theta_down = theta_up[::-1]

            ##small one
            r_up = []
            r_down = []
            for k in range(len(theta_up)):
                r_up = r_up + [
                    N * lambda_0 / (self.n_e *
                                    (1 - e * cos(float(theta_up[k])))) +
                    j * self.period + spacing
                ]
            r_down = r_up[::-1]

            xr = []
            yr = []
            for k in range(len(theta_up)):
                xr = xr + [r_up[k] * cos(theta_up[k])]
                yr = yr + [r_up[k] * sin(theta_up[k])]

            xl = []
            yl = []
            for k in range(len(theta_down)):
                xl = xl + [(r_down[k] + wl) * cos(theta_down[k])]
                yl = yl + [(r_down[k] + wl) * sin(theta_down[k])]

            x = xr + xl
            y = yr + yl

            pts = []
            for i in range(len(x)):
                pts.append(Point.from_dpoint(DPoint(x[i] / dbu, y[i] / dbu)))
            #small_one = core.Boundary(points)

            polygon = Polygon(pts)
            if self.ff > 0:
                shapes(LayerSiN).insert(polygon)

            ##big one
            r_up = []
            r_down = []
            for k in range(len(theta_up)):
                r_up = r_up + [
                    N * lambda_0 / (self.n_e *
                                    (1 - e * cos(float(theta_up[k])))) +
                    j * self.period + 2 * spacing + wl
                ]
            r_down = r_up[::-1]

            xr = []
            yr = []
            for k in range(len(theta_up)):
                xr = xr + [r_up[k] * cos(theta_up[k])]
                yr = yr + [r_up[k] * sin(theta_up[k])]

            xl = []
            yl = []
            for k in range(len(theta_down)):
                xl = xl + [(r_down[k] + wh) * cos(theta_down[k])]
                yl = yl + [(r_down[k] + wh) * sin(theta_down[k])]

            x = xr + xl
            y = yr + yl

            pts = []
            for i in range(len(x)):
                pts.append(Point.from_dpoint(DPoint(x[i] / dbu, y[i] / dbu)))

            polygon = Polygon(pts)
            shapes(LayerSiN).insert(polygon)

        # Taper section
        r_up = []
        r_down = []
        for k in range(len(theta_up)):
            r_up = r_up + [
                N * lambda_0 / (self.n_e * (1 - e * cos(float(theta_up[k]))))
            ]
        r_down = r_up[::-1]

        xl = []
        yl = []
        for k in range(len(theta_down)):
            xl = xl + [(r_down[k]) * cos(theta_down[k])]
            yl = yl + [(r_down[k]) * sin(theta_down[k])]

        yr = [self.t / 2., self.t / 2., -self.t / 2., -self.t / 2.]

        yl_abs = []
        for k in range(len(yl)):
            yl_abs = yl_abs + [abs(yl[k])]

        y_max = max(yl_abs)
        iy_max = yl_abs.index(y_max)

        L_o = (y_max - self.t / 2) / tan((pi / 180) * self.angle_e / 2)

        xr = [L_o + xl[iy_max], 0, 0, L_o + xl[iy_max]]

        x = xr + xl
        y = yr + yl

        pts = []
        for i in range(len(x)):
            pts.append(Point.from_dpoint(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

        # Device recognition layer
        yr = sin(start) * (N * lambda_0 / (self.n_e *
                                           (1 - e * cos(float(start)))) +
                           gc_number * self.period + spacing)
        box1 = Box(
            -(self.grating_length + self.taper_length) / dbu - pin_length * 2,
            yr / dbu, 0, -yr / dbu)
        shapes(LayerDevRecN).insert(box1)
Esempio n. 8
0
    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)

        # 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))
        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.")
Esempio n. 9
0
    def produce_impl(self):
        # 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.silayer
        LayerSiN = ly.layer(LayerSi)
        TextLayerN = ly.layer(self.textl)
        LayerPinRecN = ly.layer(self.pinrec)
        LayerDevRecN = ly.layer(self.devrec)

        from SiEPIC.utils import points_per_circle

        # Create the ring resonator:
        layout_Ring(self.cell, LayerSiN, self.r + self.w / 2,
                    self.r + self.g + self.w, self.r, self.w,
                    points_per_circle(self.r))

        w = int(round(self.w / dbu))
        r = int(round(self.r / dbu))
        g = int(round(self.g / dbu))

        #   pcell = ly.create_cell("DirectionalCoupler_HalfRing_Straight", "SiEPIC", { "r": self.r, "w": self.w, "g": self.g, "silayer": LayerSi, "bustype": 0 } )
        #   print ("Cell: pcell: #%s" % pcell.cell_index())
        #   t = Trans(Trans.R0, 0, 0)
        #   instance = self.cell.insert(CellInstArray(pcell.cell_index(), t))
        #   t = Trans(Trans.R180, 0, 2*r+2*g+2*w)
        #   instance = self.cell.insert(CellInstArray(pcell.cell_index(), t))

        # Create the two waveguides
        wg1 = Box(0, -w / 2, w + 2 * r, w / 2)
        shapes(LayerSiN).insert(wg1)
        y_offset = 2 * r + 2 * g + 2 * w
        wg2 = Box(0, y_offset - w / 2, w + 2 * r, y_offset + w / 2)
        shapes(LayerSiN).insert(wg2)

        from SiEPIC._globals import PIN_LENGTH as pin_length
        # Create the pins, as short paths:

        pin = Path([Point(pin_length / 2, 0), Point(-pin_length / 2, 0)], w)
        shapes(LayerPinRecN).insert(pin)
        t = Trans(Trans.R0, 0, 0)
        text = Text("pin1", t)
        shape = shapes(LayerPinRecN).insert(text)
        shape.text_size = 0.4 / dbu

        pin = Path([
            Point(w + 2 * r - pin_length / 2, 0),
            Point(w + 2 * r + pin_length / 2, 0)
        ], w)
        shapes(LayerPinRecN).insert(pin)
        t = Trans(Trans.R0, w + 2 * r, 0)
        text = Text("pin2", t)
        shape = shapes(LayerPinRecN).insert(text)
        shape.text_size = 0.4 / dbu

        pin = Path([
            Point(pin_length / 2, y_offset),
            Point(-pin_length / 2, y_offset)
        ], w)
        shapes(LayerPinRecN).insert(pin)
        t = Trans(Trans.R0, 0, y_offset)
        text = Text("pin3", t)
        shape = shapes(LayerPinRecN).insert(text)
        shape.text_size = 0.4 / dbu

        pin = Path([
            Point(w + 2 * r - pin_length / 2, y_offset),
            Point(w + 2 * r + pin_length / 2, y_offset)
        ], w)
        shapes(LayerPinRecN).insert(pin)
        t = Trans(Trans.R0, w + 2 * r, y_offset)
        text = Text("pin4", t)
        shape = shapes(LayerPinRecN).insert(text)
        shape.text_size = 0.4 / dbu

        # Create the device recognition layer
        dev = Box(0, -w * 3, w + 2 * r, y_offset + w * 3)
        shapes(LayerDevRecN).insert(dev)

        # Add a polygon text description
        if self.textpolygon:
            layout_pgtext(self.cell, self.textl, self.w, self.r + self.w,
                          "%.3f-%g" % (self.r, self.g), 1)

        print("Done drawing the layout for - DoubleBus_Ring: %.3f-%g" %
              (self.r, self.g))
Esempio n. 10
0
    def produce_impl(self):
        # fetch the parameters
        dbu = self.layout.dbu
        ly = self.layout
        shapes = self.cell.shapes

        LayerSi = self.layer
        LayerSiN = ly.layer(self.layer)
        LayerPinRecN = ly.layer(self.pinrec)
        LayerDevRecN = ly.layer(self.devrec)
        LayerTextN = ly.layer(self.textl)

        from math import pi, cos, sin, log, sqrt, tan

        lambda_0 = self.wavelength  ##um wavelength of light
        pin_length = 0.5  ##um extra nub for the waveguid attachment

        # Geometry
        wh = self.period * self.dc  ##thick grating
        wl = self.ff * (self.period - wh)  ## thin grating
        spacing = (self.period - wh - wl) / 2  ##space between thick and thin

        gc_number = int(round(self.grating_length /
                              self.period))  ##number of periods
        gc_number = 3
        e = self.n_t * sin((pi / 180) * self.theta_c) / self.n_e
        N = round(self.taper_length * (1 + e) * self.n_e /
                  lambda_0)  ##allows room for the taper

        start = (pi - (pi / 180) * self.angle_e / 2)
        stop = (pi + (pi / 180) * self.angle_e / 2)

        # Draw coupler grating.
        for j in range(gc_number):

            # number of points in the arcs:
            # calculate such that the vertex & edge placement error is < 0.5 nm.
            #   see "SiEPIC_EBeam_functions - points_per_circle" for more details
            radius = N * lambda_0 / (self.n_e *
                                     (1 - e)) + j * self.period + spacing
            seg_points = int(
                points_per_circle(radius / dbu) / 360. *
                self.angle_e)  # number of points grating arc
            theta_up = []
            for m in range(seg_points + 1):
                theta_up = theta_up + [start + m * (stop - start) / seg_points]
            theta_down = theta_up[::-1]

            ##small one
            r_up = []
            r_down = []
            rng = range(len(theta_up))

            # find the divider to get desired fab error:
            th = min(theta_up)
            div = (2 * sin(th) / self.fab_error) * (N * lambda_0 /
                                                    (self.n_e *
                                                     (1 - e * cos(th))) +
                                                    j * self.period + spacing)
            err = (2 * sin(th) / div) * (N * lambda_0 / (self.n_e *
                                                         (1 - e * cos(th))) +
                                         j * self.period + spacing)
            #        print("div %s, err (double check) %s" % (div, err))

            for k in rng:
                th = theta_up[k]
                #          print("%s, %s, %s" % (th, sin(th), 1+sin(th)/10.) )
                r_up = r_up + [
                    (1 - sin(th) / div) *
                    (N * lambda_0 /
                     (self.n_e *
                      (1 - e * cos(th))) + j * self.period + spacing)
                ]
            for k in rng[::-1]:
                th = theta_up[k]
                #          print("%s, %s, %s" % (th, sin(th), 1+sin(th)/10.) )
                r_down = r_down + [
                    (1 + sin(th) / div) *
                    (N * lambda_0 /
                     (self.n_e *
                      (1 - e * cos(th))) + j * self.period + spacing)
                ]

            xr = []
            yr = []
            for k in range(len(theta_up)):
                xr = xr + [r_up[k] * cos(theta_up[k])]
                yr = yr + [r_up[k] * sin(theta_up[k])]

            xl = []
            yl = []
            for k in range(len(theta_down)):
                xl = xl + [(r_down[k] + wl) * cos(theta_down[k])]
                yl = yl + [(r_down[k] + wl) * sin(theta_down[k])]

            x = xr + xl
            y = yr + yl

            pts = []
            for i in range(len(x)):
                pts.append(
                    Point.from_dpoint(pya.DPoint(x[i] / dbu, y[i] / dbu)))
            #small_one = core.Boundary(points)

            polygon = Polygon(pts)
            shapes(LayerSiN).insert(polygon)

            if j == 1:
                # text label dimensions, for minor grating:
                # top
                shapes(LayerTextN).insert(
                    Text("%0.0f" % ((wl + self.fab_error) * 1000),
                         Trans(Trans.R0, xl[0] / dbu,
                               yl[0] / dbu))).text_size = 0.2 / dbu
                # btm
                shapes(LayerTextN).insert(
                    Text("%0.0f" % ((wl - self.fab_error) * 1000),
                         Trans(Trans.R0, xl[-1] / dbu,
                               yl[-1] / dbu))).text_size = 0.2 / dbu
                # mid
                shapes(LayerTextN).insert(
                    Text(
                        "%0.0f" % ((wl) * 1000),
                        Trans(Trans.R0, xl[int(len(theta_up) / 2)] / dbu,
                              yl[int(len(theta_up) / 2)] /
                              dbu))).text_size = 0.2 / dbu

            ##big one
            r_up = []
            r_down = []

            # find the divider to get desired fab error:
            th = min(theta_up)
            div = (2 * sin(th) / self.fab_error) * (
                N * lambda_0 /
                (self.n_e *
                 (1 - e * cos(th))) + j * self.period + 2 * spacing + wl)
            err = (2 * sin(th) / div) * (N * lambda_0 / (self.n_e *
                                                         (1 - e * cos(th))) +
                                         j * self.period + 2 * spacing + wl)
            #        print("div %s, err (double check) %s" % (div, err))

            rng = range(len(theta_up))
            for k in rng:
                th = theta_up[k]
                r_up = r_up + [
                    (1 - sin(th) / div) *
                    (N * lambda_0 /
                     (self.n_e *
                      (1 - e * cos(th))) + j * self.period + 2 * spacing + wl)
                ]
            for k in rng[::-1]:
                th = theta_up[k]
                r_down = r_down + [
                    (1 + sin(th) / div) *
                    (N * lambda_0 /
                     (self.n_e *
                      (1 - e * cos(th))) + j * self.period + 2 * spacing + wl)
                ]

            xr = []
            yr = []
            for k in range(len(theta_up)):
                xr = xr + [r_up[k] * cos(theta_up[k])]
                yr = yr + [r_up[k] * sin(theta_up[k])]

            xl = []
            yl = []
            for k in range(len(theta_down)):
                xl = xl + [(r_down[k] + wh) * cos(theta_down[k])]
                yl = yl + [(r_down[k] + wh) * sin(theta_down[k])]

            x = xr + xl
            y = yr + yl

            pts = []
            for i in range(len(x)):
                pts.append(
                    Point.from_dpoint(pya.DPoint(x[i] / dbu, y[i] / dbu)))

            polygon = Polygon(pts)
            shapes(LayerSiN).insert(polygon)

            if j == 1:
                # text label dimensions, for major grating:
                # top
                shapes(LayerTextN).insert(
                    Text("%0.0f" % ((wh + self.fab_error) * 1000),
                         Trans(Trans.R0, xl[0] / dbu,
                               yl[0] / dbu))).text_size = 0.2 / dbu
                # btm
                shapes(LayerTextN).insert(
                    Text("%0.0f" % ((wh - self.fab_error) * 1000),
                         Trans(Trans.R0, xl[-1] / dbu,
                               yl[-1] / dbu))).text_size = 0.2 / dbu
                # mid
                shapes(LayerTextN).insert(
                    Text(
                        "%0.0f" % ((wh) * 1000),
                        Trans(Trans.R0, xl[int(len(theta_up) / 2)] / dbu,
                              yl[int(len(theta_up) / 2)] /
                              dbu))).text_size = 0.2 / dbu
Esempio n. 11
0
    def produce_impl(self):
        # fetch the parameters
        dbu = self.layout.dbu
        ly = self.layout
        shapes = self.cell.shapes

        LayerSi = self.layer
        LayerSiN = ly.layer(self.layer)
        LayerPinRecN = ly.layer(self.pinrec)
        LayerDevRecN = ly.layer(self.devrec)
        LayerTextN = ly.layer(self.textl)

        from math import pi, cos, sin, log, sqrt, tan

        lambda_0 = self.wavelength  ##um wavelength of light
        pin_length = 0.5  ##um extra nub for the waveguide attachment

        # Geometry
        wh = self.period * self.dc  ##thick grating
        wl = self.ff * (self.period - wh)  ## thin grating

        # Width scale parameter is a first pass attempt at designing for length contraction
        # at cryogenic temperature. It is applied BEFORE the width error; this is because
        # the order of operations in the reverse is over/under-etch, then cool and contract.
        # So first scale so that target width is reached after contraction, then add
        # fabrication error so that the scaled width is reached.

        wh = (wh * self.w_scale + self.w_err)
        wl = (wl * self.w_scale + self.w_err)

        spacing = (self.period - wh - wl) / 2  ##space between thick and thin

        gc_number = int(round(self.grating_length /
                              self.period))  ##number of periods
        e = self.n_t * sin((pi / 180) * self.theta_c) / self.n_e
        N = round(self.taper_length * (1 + e) * self.n_e /
                  lambda_0)  ##allows room for the taper

        start = (pi - (pi / 180) * self.angle_e / 2)
        stop = (pi + (pi / 180) * self.angle_e / 2)

        # Draw coupler grating.
        for j in range(gc_number):

            # number of points in the arcs:
            # calculate such that the vertex & edge placement error is < 0.5 nm.
            #   see "SiEPIC_EBeam_functions - points_per_circle" for more details
            radius = N * lambda_0 / (self.n_e *
                                     (1 - e)) + j * self.period + spacing
            seg_points = int(
                points_per_circle(radius / dbu) / 360. *
                self.angle_e)  # number of points grating arc
            theta_up = []
            for m in range(seg_points + 1):
                theta_up = theta_up + [start + m * (stop - start) / seg_points]
            theta_down = theta_up[::-1]

            ##small one
            r_up = []
            r_down = []
            for k in range(len(theta_up)):
                r_up = r_up + [
                    N * lambda_0 / (self.n_e *
                                    (1 - e * cos(float(theta_up[k])))) +
                    j * self.period + spacing
                ]
            r_down = r_up[::-1]

            xr = []
            yr = []
            for k in range(len(theta_up)):
                xr = xr + [r_up[k] * cos(theta_up[k])]
                yr = yr + [r_up[k] * sin(theta_up[k])]

            xl = []
            yl = []
            for k in range(len(theta_down)):
                xl = xl + [(r_down[k] + wl) * cos(theta_down[k])]
                yl = yl + [(r_down[k] + wl) * sin(theta_down[k])]

            x = xr + xl
            y = yr + yl

            pts = []
            for i in range(len(x)):
                pts.append(
                    Point.from_dpoint(pya.DPoint(x[i] / dbu, y[i] / dbu)))
            #small_one = core.Boundary(points)

            polygon = Polygon(pts)
            shapes(LayerSiN).insert(polygon)

            ##big one
            r_up = []
            r_down = []
            for k in range(len(theta_up)):
                r_up = r_up + [
                    N * lambda_0 / (self.n_e *
                                    (1 - e * cos(float(theta_up[k])))) +
                    j * self.period + 2 * spacing + wl
                ]
            r_down = r_up[::-1]

            xr = []
            yr = []
            for k in range(len(theta_up)):
                xr = xr + [r_up[k] * cos(theta_up[k])]
                yr = yr + [r_up[k] * sin(theta_up[k])]

            xl = []
            yl = []
            for k in range(len(theta_down)):
                xl = xl + [(r_down[k] + wh) * cos(theta_down[k])]
                yl = yl + [(r_down[k] + wh) * sin(theta_down[k])]

            x = xr + xl
            y = yr + yl

            pts = []
            for i in range(len(x)):
                pts.append(
                    Point.from_dpoint(pya.DPoint(x[i] / dbu, y[i] / dbu)))

            polygon = Polygon(pts)
            shapes(LayerSiN).insert(polygon)

        # Taper section
        r_up = []
        r_down = []
        for k in range(len(theta_up)):
            r_up = r_up + [
                N * lambda_0 / (self.n_e * (1 - e * cos(float(theta_up[k]))))
            ]
        r_down = r_up[::-1]

        xl = []
        yl = []
        for k in range(len(theta_down)):
            xl = xl + [(r_down[k]) * cos(theta_down[k])]
            yl = yl + [(r_down[k]) * sin(theta_down[k])]

        yr = [self.t / 2., self.t / 2., -self.t / 2., -self.t / 2.]

        yl_abs = []
        for k in range(len(yl)):
            yl_abs = yl_abs + [abs(yl[k])]

        y_max = max(yl_abs)
        iy_max = yl_abs.index(y_max)

        L_o = (y_max - self.t / 2) / tan((pi / 180) * self.angle_e / 2)

        xr = [L_o + xl[iy_max], 0, 0, L_o + xl[iy_max]]

        x = xr + xl
        y = yr + yl

        pts = []
        for i in range(len(x)):
            pts.append(Point.from_dpoint(pya.DPoint(x[i] / dbu, y[i] / dbu)))

        polygon = Polygon(pts)
        shapes(LayerSiN).insert(polygon)

        # Pin on the waveguide:
        pin_length = 200
        x = 0
        t = Trans(x, 0)
        pin = pya.Path([Point(-pin_length / 2, 0),
                        Point(pin_length / 2, 0)], self.t / dbu)
        pin_t = pin.transformed(t)
        shapes(LayerPinRecN).insert(pin_t)
        text = Text("pin1", t)
        shape = shapes(LayerPinRecN).insert(text)
        shape.text_size = 0.4 / dbu

        # Device recognition layer
        yr = sin(start) * (N * lambda_0 / (self.n_e *
                                           (1 - e * cos(float(start)))) +
                           gc_number * self.period + spacing)
        box1 = Box(
            -(self.grating_length + self.taper_length) / dbu - pin_length * 2,
            yr / dbu, 0, -yr / dbu)
        shapes(LayerDevRecN).insert(box1)