Example #1
0
class DropRingAtWavelength(DropRing):
    """
    Drop ring that implements a simple heuristic to set the resonance wavelength.
    """

    res_wavelength = i3.PositiveNumberProperty(default=1.55,
                                               doc="Resonance wavelength")
    order = i3.PositiveIntProperty(default=200, doc="Order of the resonance")
    total_ring_length = i3.LockedProperty()

    def _default_total_ring_length(self):
        cm = self.directional_coupler.trace_template2.get_default_view(
            i3.CapheModelView)
        dneff = -(cm.n_g - cm.n_eff) / cm.center_wavelength
        neff_total = cm.n_eff + (self.res_wavelength -
                                 cm.center_wavelength) * dneff
        return self.order * self.res_wavelength / neff_total
Example #2
0
class SimpleRing(RingRect180DropFilter):

    resonance_wavelength = i3.PositiveNumberProperty(default=1.55)
    length = i3.PositiveNumberProperty()
    order = i3.PositiveIntProperty(default=200)

    def _get_neff_at_res(self):
        tt = self.coupler_trace_templates[0]
        cm = tt.get_default_view(i3.CircuitModelView)
        # Get the n_eff and n_g from the circuit model
        n_eff = cm.n_eff
        n_g = cm.n_g
        cw = cm.center_wavelength
        dneff = -(n_g - n_eff) / cw
        dl = self.resonance_wavelength - cw
        n_res = n_eff + dneff * (dl)
        return n_res

    def _default_length(self):
        length = self.order * self.resonance_wavelength / self._get_neff_at_res(
        )
        return length

    class Layout(RingRect180DropFilter.Layout):
        def _default_straights(self):
            return [0.0, 0.0]

        def _default_bend_radius(self):
            return self.length / np.pi / 2

    class CircuitModel(RingRect180DropFilter.CircuitModel):
        def _default_coupler_parameters(self):
            tau = 0.9**0.5
            kappa = 1j * (1 - tau**2)**0.5
            cp = dict(cross_coupling1=kappa, straight_coupling1=tau)

            return [cp, cp]

        def _default_ring_length(self):

            return self.length
Example #3
0
    class Layout(PlaceAndAutoRoute.Layout):

        period = i3.PositiveNumberProperty(doc="period of grating", default=0.95)
        n_o_periods = i3.PositiveIntProperty(doc="number of periods of grating", default=16)
        line_width = i3.PositiveNumberProperty(doc="width of the grating")
        line_length = i3.PositiveNumberProperty(doc="length of the grating", default=13.0)
        socket_length = i3.PositiveNumberProperty(doc="length of socket")
        # transition_length = i3.PositiveNumberProperty(doc="length of the taper")
        transition_length = i3.PositiveNumberProperty(doc="taper length", default=100.0)
        extension_length = i3.PositiveNumberProperty(doc="extra straight length", default=100.0)

        def _default_line_width(self):
            return self.period * 0.5

        def _default_socket_length(self):
            return self.period * self.n_o_periods + 2.0

        def _default_gc(self):
            gc_lo = self.cell.gc.get_default_view(i3.LayoutView)
            gc_lo.set(period=self.period, n_o_periods=self.n_o_periods, line_width=self.line_width, line_length=self.line_length)
            return gc_lo

        def _default_tapered_gc(self):
            tapered_gc_lo = self.cell.tapered_gc.get_default_view(i3.LayoutView)
            tapered_gc_lo.set(transition_length=self.transition_length)
            return tapered_gc_lo

        def _default_wg(self):
            wg_lo = self.cell.wg.get_default_view(i3.LayoutView)
            wg_lo.set(shape=[(0.0, 0.0), (self.extension_length, 0.0)])
            return wg_lo

        def _default_child_transformations(self):
            trans = dict()
            trans["tapered_gc"] = (0, 0)
            trans["wg"] = i3.Translation(self.tapered_gc.ports["out"].position) + i3.Translation((10, 0))
            return trans
class Coupon(i3.PCell):
    _name_prefix = "Coupon"

    # Center of the structure
    position = i3.Coord2Property(default=(0.0, 0.0))

    # Coupon parameters
    length = i3.PositiveNumberProperty(default=2750.0)
    width = i3.PositiveNumberProperty(default=85.0)
    shrink = i3.NumberProperty(default=0.0)
    nb = i3.PositiveIntProperty(default=8)
    space = i3.PositiveNumberProperty(default=1000.0)

    class Layout(i3.LayoutView):
        # _name_prefix = "length{}".format(str(self.length))
        def _generate_elements(self, elems):
            # Center of the structure
            (x0, y0) = self.position
            # RELEASE
            release = Release(
                length=self.length,
                width=self.width,
            )
            # elems += i3.SRef(reference=release, position=self.position)
            elems += i3.ARef(n_o_periods=(3, self.nb),
                             period=(self.length + 250, 210),
                             reference=release,
                             origin=(x0, y0))
            elems += i3.ARef(n_o_periods=(3, self.nb),
                             period=(self.length + 250, 280),
                             reference=release,
                             origin=(x0, y0 + 210 * self.nb -
                                     (210 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(3, self.nb),
                period=(self.length + 250, 350),
                reference=release,
                origin=(x0,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(3, self.nb),
                period=(self.length + 250, 420),
                reference=release,
                origin=(x0,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space +
                        350 * self.nb - (350 - self.width) + self.space))

            elems += i3.ARef(n_o_periods=(2, self.nb),
                             period=(self.length + 500, 210),
                             reference=release,
                             origin=(x0 + 9250 - self.shrink * 3, y0))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 500, 280),
                reference=release,
                origin=(x0 + 9250 - self.shrink * 3,
                        y0 + 210 * self.nb - (210 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 500, 350),
                reference=release,
                origin=(x0 + 9250 - self.shrink * 3,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 500, 420),
                reference=release,
                origin=(x0 + 9250 - self.shrink * 3,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space +
                        350 * self.nb - (350 - self.width) + self.space))

            elems += i3.ARef(n_o_periods=(2, self.nb),
                             period=(self.length + 750, 210),
                             reference=release,
                             origin=(x0 + 8000 * 2 - self.shrink * 5, y0))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 750, 280),
                reference=release,
                origin=(x0 + 8000 * 2 - self.shrink * 5,
                        y0 + 210 * self.nb - (210 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 750, 350),
                reference=release,
                origin=(x0 + 8000 * 2 - self.shrink * 5,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 750, 420),
                reference=release,
                origin=(x0 + 8000 * 2 - self.shrink * 5,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space +
                        350 * self.nb - (350 - self.width) + self.space))

            elems += i3.ARef(n_o_periods=(2, self.nb),
                             period=(self.length + 1000, 210),
                             reference=release,
                             origin=(x0 + 23250 - self.shrink * 7, y0))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 1000, 280),
                reference=release,
                origin=(x0 + 23250 - self.shrink * 7,
                        y0 + 210 * self.nb - (210 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 1000, 350),
                reference=release,
                origin=(x0 + 23250 - self.shrink * 7,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 1000, 420),
                reference=release,
                origin=(x0 + 23250 - self.shrink * 7,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space +
                        350 * self.nb - (350 - self.width) + self.space))

            elems += i3.ARef(n_o_periods=(2, self.nb),
                             period=(self.length + 1500, 210),
                             reference=release,
                             origin=(x0 + 29750 + 1500 - self.shrink * 9, y0))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 1500, 280),
                reference=release,
                origin=(x0 + 29750 + 1500 - self.shrink * 9,
                        y0 + 210 * self.nb - (210 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 1500, 350),
                reference=release,
                origin=(x0 + 29750 + 1500 - self.shrink * 9,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 1500, 420),
                reference=release,
                origin=(x0 + 29750 + 1500 - self.shrink * 9,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space +
                        350 * self.nb - (350 - self.width) + self.space))

            elems += i3.ARef(n_o_periods=(2, self.nb),
                             period=(self.length + 2000, 210),
                             reference=release,
                             origin=(x0 + 38250 + 2000 - self.shrink * 11, y0))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 2000, 280),
                reference=release,
                origin=(x0 + 38250 + 2000 - self.shrink * 11,
                        y0 + 210 * self.nb - (210 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 2000, 350),
                reference=release,
                origin=(x0 + 38250 + 2000 - self.shrink * 11,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 2000, 420),
                reference=release,
                origin=(x0 + 38250 + 2000 - self.shrink * 11,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space +
                        350 * self.nb - (350 - self.width) + self.space))

            elems += i3.ARef(n_o_periods=(2, self.nb),
                             period=(self.length + 3000, 210),
                             reference=release,
                             origin=(x0 + 47750 + 3000 - self.shrink * 13, y0))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 3000, 280),
                reference=release,
                origin=(x0 + 47750 + 3000 - self.shrink * 13,
                        y0 + 210 * self.nb - (210 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 3000, 350),
                reference=release,
                origin=(x0 + 47750 + 3000 - self.shrink * 13,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space))
            elems += i3.ARef(
                n_o_periods=(2, self.nb),
                period=(self.length + 3000, 420),
                reference=release,
                origin=(x0 + 47750 + 3000 - self.shrink * 13,
                        y0 + 210 * self.nb - (210 - self.width) + self.space +
                        280 * self.nb - (280 - self.width) + self.space +
                        350 * self.nb - (350 - self.width) + self.space))

            # elems += i3.Circle(layer=i3.Layer(number = 5, name = "tether"),radius=50000)
            return elems
Example #5
0
class MMI(i3.PCell):
    """
    MMI with a variable number of inputs and outputs with variable width and length of the taper.
    """
    n_inputs = i3.PositiveIntProperty(default=1, doc="Number of inputs")
    n_outputs = i3.PositiveIntProperty(default=2, doc="Number of outputs")
    trace_template = i3.TraceTemplateProperty(doc="Trace template at the ports")

    def _default_trace_template(self):
        return SiWireWaveguideTemplate(name=self.name + "tt")

    class Layout(i3.LayoutView):

        length = i3.PositiveNumberProperty(default=25.0, doc="Length of the mmi.")
        width = i3.PositiveNumberProperty(default=5.0, doc="Width of core layer at center of the mmi.")
        transistion_length = i3.PositiveNumberProperty(default=5.0, doc="Length of the tapers.")
        wg_spacing_in = i3.NonNegativeNumberProperty(default=2.0,
                                                     doc="Center to center distance between the waveguides at the input.")
        wg_spacing_out = i3.NonNegativeNumberProperty(default=2.0,
                                                      doc="Center to center distance between the waveguides at the output.")
        taper_width = i3.PositiveNumberProperty(default=1.0,
                                                doc="Width of the core of the taper of the access waveguides of the mmi.")

        @i3.cache()
        def _get_input_taper_coords(self):
            # coordinates of the input tapers [(taper1_begin, taper1_end), (taper2_begin, taper2_end), ...]
            base_y = - (self.n_inputs - 1) * self.wg_spacing_in / 2.0
            taper_coords = [
                [(0, base_y + cnt * self.wg_spacing_in), (-self.transistion_length, base_y + cnt * self.wg_spacing_in)]
                for cnt in range(self.n_inputs)]

            return taper_coords

        @i3.cache()
        def _get_output_taper_coords(self):
            # coordinates of the output tapers [(taper1_begin, taper1_end), (taper2_begin, taper2_end), ...]
            base_y = - (self.n_outputs - 1) * self.wg_spacing_out / 2.0
            taper_coords = [[(self.length, base_y + cnt * self.wg_spacing_out),
                             (self.length + self.transistion_length, base_y + cnt * self.wg_spacing_out)]
                            for cnt in range(self.n_outputs)]

            return taper_coords

        def _generate_elements(self, elems):
            layer_core = self.trace_template.core_layer
            # mmi core
            elems += i3.Rectangle(layer=layer_core, center=(+self.length / 2.0, 0.0),
                                  box_size=(self.length, self.width))
            # input wedges
            for bc, ec in self._get_input_taper_coords():
                elems += i3.Wedge(layer_core, begin_coord=bc, end_coord=ec, begin_width=self.taper_width,
                                  end_width=self.trace_template.core_width)
            for bc, ec in self._get_output_taper_coords():
                elems += i3.Wedge(layer_core, begin_coord=bc, end_coord=ec, begin_width=self.taper_width,
                                  end_width=self.trace_template.core_width)
            return elems

        def _generate_ports(self, ports):

            for cnt, coords in enumerate(self._get_input_taper_coords(), 1):
                ports += i3.OpticalPort(name="in{}".format(cnt), position=coords[1], angle=180.0,
                                        trace_template=self.trace_template)

            for cnt, coords in enumerate(self._get_output_taper_coords(), 1):
                ports += i3.OpticalPort(name="out{}".format(cnt), position=coords[1], angle=0.0,
                                        trace_template=self.trace_template)

            return ports

    class Netlist(i3.NetlistFromLayout):
        pass
class CircuitOfBlocks(microfluidics.PlaceAndAutoRoute):
    """Parametric cell with several traps, which are stacked vertically (Parallel) or horizontally (Series)
    """
    #_name_prefix = "circuitOfTraps"
    type = i3.NumberProperty(default=0)  #  0 Paralell 1 Series
    block_distance = i3.NumberProperty(default=400.)  #distance betwn traps
    block_with_tee = i3.ChildCellListProperty(
    )  #  Generating traps with Tee from Child Cell List Property
    trace_template = microfluidics.ChannelTemplateProperty(
        default=microfluidics.ShortChannelTemplate())
    n_blocks_x = i3.PositiveIntProperty(default=30)
    n_blocks_y = i3.PositiveIntProperty(default=30)
    x_footprint = i3.PositiveIntProperty(default=1000)
    y_footprint = i3.PositiveIntProperty(default=1000)

    def _default_child_cells(self):
        return {
            "blk_w_tee{}".format(cnt): self.block_with_tee[cnt]
            for cnt in range(self.n_blocks_x * self.n_blocks_y)
        }

    def _default_links(self):
        links = []
        bx = self.n_blocks_x

        ########################
        #############   PARALLEL
        if self.type == 0:
            for cntx in range(0, self.n_blocks_x):
                # self connection at the top -bypass
                top = self.n_blocks_y * self.n_blocks_x - self.n_blocks_x + cntx
                links.append(("blk_w_tee{}:in2".format(top),
                              "blk_w_tee{}:out2".format(top)))
                # connecting bottom to top
                for cnty in range(0, self.n_blocks_y - 1):
                    links.append(
                        ("blk_w_tee{}:in2".format(cnty * bx + cntx),
                         "blk_w_tee{}:in1".format((cnty + 1) * bx + cntx)))
                    links.append(
                        ("blk_w_tee{}:out2".format(cnty * bx + cntx),
                         "blk_w_tee{}:out1".format((cnty + 1) * bx + cntx)))

            #interconnecting horizontally
            if self.n_blocks_x > 1:
                for cntd in range(0, self.n_blocks_x - 1):
                    links.append(("blk_w_tee{}:out1".format(cntd),
                                  "blk_w_tee{}:in1".format(cntd + 1)))
        ########  END PARALLEL #################

        ########################
        #############   SERIES
        if self.type == 1:  # series
            #all by-pass, each object
            for cnt in range(0,
                             self.n_blocks_y * self.n_blocks_x):  #all bypass
                links.append(("blk_w_tee{}:in2".format(cnt),
                              "blk_w_tee{}:out2".format(cnt)))

            #all interconnecting horizontally
            if self.n_blocks_x > 1:
                for cnty in range(0, self.n_blocks_y):
                    for cntd in range(0, (self.n_blocks_x - 1)):
                        links.append(
                            ("blk_w_tee{}:out1".format(cnty * bx + cntd),
                             "blk_w_tee{}:in1".format(cnty * bx + cntd + 1)))

            #left connections
            for cntd in range(1, (self.n_blocks_y - 1), 2):
                #links.append(("blk_w_tee{}:in1".format(cntd * bx), "blk_w_tee{}:in1".format((cntd+1) * bx)))
                links.append(("blk_w_tee{}:in1".format(
                    (cntd + 1) * bx), "blk_w_tee{}:in1".format(cntd * bx)))

            #right connections
            for cntd in range(1, (self.n_blocks_y), 2):
                links.append(("blk_w_tee{}:out1".format(cntd * bx - 1),
                              "blk_w_tee{}:out1".format((cntd + 1) * bx - 1)))

        ########  END SERIES #################

        return links

    def _default_block_with_tee(
            self):  # Generating traps from Child Cell List Property
        tee1 = TeeSimple()
        tee1.Layout(tee_length=(200.0))
        my_block = CellTrapSimple()
        my_block.Layout(cell_trap_length=300.0)
        return [
            TrapWithTees(
                name="blk_w_tee_{}".format(cnt),
                trap=my_block,
                tee=tee1,
            ) for cnt in range(self.n_blocks_x * self.n_blocks_y)
        ]

    class Layout(microfluidics.PlaceAndAutoRoute.Layout):
        def _default_child_transformations(self):

            # generate grid
            x = np.linspace(0, self.cell.x_footprint, self.cell.n_blocks_x)
            y = np.linspace(0, self.cell.y_footprint, self.cell.n_blocks_y)

            # generate positions
            from functions.position_coordinates import generate_positions
            coords = generate_positions(x, y, self.type)

            return {
                "blk_w_tee{}".format(cnt): i3.Translation(coords[cnt])
                for cnt in range(self.n_blocks_x * self.n_blocks_y)
            }

        def _generate_ports(self, ports):
            # Add ports
            if self.type == 0:
                ports += i3.expose_ports(
                    self.instances, {
                        'blk_w_tee0:in1': 'in1',
                        'blk_w_tee{}:out1'.format(self.n_blocks_x - 1): 'out1'
                    })

            if self.type == 1:
                if self.n_blocks_y % 2 == 0:
                    ports += i3.expose_ports(
                        self.instances,
                        {
                            'blk_w_tee0:in1':
                            'in1',
                            'blk_w_tee{}:in1'.format(self.n_blocks_x * self.n_blocks_y - self.n_blocks_x):
                            'out1'  # last left
                        })
                else:
                    ports += i3.expose_ports(
                        self.instances,
                        {
                            'blk_w_tee0:in1':
                            'in1',
                            'blk_w_tee{}:out1'.format(self.n_blocks_x * self.n_blocks_y - 1):
                            'out1'  # last right
                        })

            return ports
Example #7
0
    class Layout(i3.LayoutView):

        from ipkiss.technology import get_technology
        TECH = get_technology()

        # Sidewall grating waveguide properties
        period = i3.PositiveNumberProperty(default=.3,
                                           doc="Period of sidewall grating")
        duty_cycle = i3.PositiveNumberProperty(
            default=.1,
            doc="Length of grating teeth (along periodic direction)")
        grating_amp = i3.PositiveNumberProperty(
            default=.01,
            doc=
            "Width/amplitude of grating teeth (normal to periodic direction)")
        grat_wg_width = i3.PositiveNumberProperty(
            default=1.0,
            doc=
            "Width of sidewall grating waveguide core (if grating_amp=0 width of waveguide)"
        )

        # Flyback waveguide properites
        flyback_wg_width = i3.PositiveNumberProperty(
            default=0.4, doc="Width of flyback waveguide core")

        # Grating array properties
        pitch = i3.PositiveNumberProperty(
            default=16.0,
            doc=
            "Sidewall grating pitch (center-to-center distance of sidewall grating waveguides)"
        )
        spacing = i3.PositiveNumberProperty(
            doc="Gap between sidewall grating waveguides and flyback waveguides"
        )

        def _default_spacing(self):
            spacing = (self.pitch - self.grat_wg_width -
                       self.flyback_wg_width) / 2
            return spacing

        length = i3.PositiveNumberProperty(
            default=800.0,
            doc="Length of straight (untapered) waveguide sections")
        numrows = i3.PositiveIntProperty(
            default=32,
            doc=
            "Number of sidewall grating/flyback waveguide pairs in the array")

        # Taper properties
        # Properties used for taper_type = "default"
        taper_length = i3.PositiveNumberProperty(default=10.0,
                                                 doc="Taper length")
        # Properties used for taper_type = "manual"
        taper_path_flyback = i3.NumpyArrayProperty(
            doc=
            "List of coordinates denoting the center path of the taper (bend to flyback waveguide)"
        )
        taper_width_flyback = i3.NumpyArrayProperty(
            doc=
            "List of taper widths normal to each point on the path (bend to flyback waveguide)"
        )
        taper_path_swg = i3.NumpyArrayProperty(
            doc=
            "List of coordinates denoting the center path of the taper (bend to sidewall grating waveguide)"
        )
        taper_width_swg = i3.NumpyArrayProperty(
            doc=
            "List of taper widths normal to each point on the path (bend to sidewall grating waveguide)"
        )

        def _default_taper_path_flyback(self):
            #Default is a straight linear taper
            path = np.array([[0.0, 0.0], [self.taper_length, 0.0]], np.float_)
            return path

        def _default_taper_width_flyback(self):
            # Default is a straight linear taper
            widths = np.array([self.bend_width, self.flyback_wg_width],
                              np.float_)
            return widths

        def _default_taper_path_swg(self):
            #Default is a straight linear taper
            path = np.array([[0.0, 0.0], [self.taper_length, 0.0]], np.float_)
            return path

        def _default_taper_width_swg(self):
            # Default is a straight linear taper
            widths = np.array([self.bend_width, self.grat_wg_width], np.float_)
            return widths

        # Bend properties
        # Properties used for bend_type = "default"
        bend_width = i3.PositiveNumberProperty(
            default=TECH.WG.CORE_WIDTH,
            doc="Width of waveguides in bend sections")
        # Properties used for bend_type = "manual"
        arc_path = i3.NumpyArrayProperty(
            doc=
            "List of coordinates denoting the center path of the arc connectors (going from sidewall grating waveguide to flyback waveguide"
        )
        arc_width = i3.NumpyArrayProperty(
            doc=
            "List of arc widths normal to each point on the path (going from sidewall grating waveguide to flyback waveguide"
        )

        def _default_arc_path(self):
            if self.pitch == 16.0:
                #Use pregenerated adiabatic bends (TX)
                pathwidth = np.loadtxt("./bend_data/txbend.txt", np.float_)
                arc_path = pathwidth[:, :2]
            elif self.pitch == 16.516:
                # Use pregenerated adiabatic bends (RX)
                pathwidth = np.loadtxt("./bend_data/rxbend.txt", np.float_)
                arc_path = pathwidth[:, :2]
            else:
                # Default is 180 arc
                arc_path = np.zeros([181, 2], np.float_)
                bend_rad = (self.grat_wg_width / 2 + self.spacing +
                            self.flyback_wg_width / 2) / 2
                for ii in range(181):
                    angle = np.pi / 180.0 * ii
                    arc_path[ii, :] = bend_rad * np.array(
                        [-np.sin(angle), np.cos(angle)], np.float_)

            return arc_path

        def _default_arc_width(self):
            if self.pitch == 16.0:
                #Use pregenerated adiabatic bends (TX)
                pathwidth = np.loadtxt("./bend_data/txbend.txt", np.float_)
                arc_width = pathwidth[:, 2]
            elif self.pitch == 16.516:
                #Use pregenerated adiabatic bends (RX)
                pathwidth = np.loadtxt("./bend_data/rxbend.txt", np.float_)
                arc_width = pathwidth[:, 2]
            else:
                #Default is uniform width with default core_width
                arc_width = np.ones([181], np.float_)
                arc_width = arc_width * self.bend_width
            return arc_width

        def validate_properties(self):
            """Check whether the combination of properties is valid."""
            if (self.grat_wg_width + self.flyback_wg_width +
                    2 * self.spacing) != self.pitch:
                raise i3.PropertyValidationError(
                    self,
                    "Array incorrectly overspecified (pitch=/=wg_widths+spacing",
                    {"pitch": self.pitch})
            return True

        @i3.cache()
        def _get_components(self):
            # Make waveguides
            swg_l = self.cell.swg.get_default_view(i3.LayoutView)
            swg_l.set(period=self.period,
                      duty_cycle=self.duty_cycle,
                      grating_amp=self.grating_amp,
                      wg_width=self.grat_wg_width,
                      length=self.length)
            flyback_l = self.cell.flyback.get_default_view(i3.LayoutView)
            flyback_path = [(0.0, 0.0), (self.length, 0.0)]
            wg_temp_flyback = self.cell.wg_temp.get_default_view(i3.LayoutView)
            wg_temp_flyback.set(core_width=self.flyback_wg_width)
            flyback_l.set(trace_template=wg_temp_flyback, shape=flyback_path)

            # Center-to-center distance between sidewall grating waveguide and flyback waveguide
            flyback_offset = self.grat_wg_width / 2 + self.spacing + self.flyback_wg_width / 2

            # Make bends
            bend_l = self.cell.bend.get_default_view(i3.LayoutView)
            if self.cell.bend_type is "default":
                # Default waveguide 180 arc
                bend_rad = flyback_offset / 2
                arc = i3.ShapeArc(center=(0.0, 0.0),
                                  radius=bend_rad,
                                  start_angle=89.0,
                                  end_angle=270.0)
                wg_temp_bend = self.cell.wg_temp.get_default_view(
                    i3.LayoutView)
                wg_temp_bend.set(core_width=self.bend_width)
                bend_l.set(trace_template=wg_temp_bend, shape=arc)
            else:
                # Custom bend pcell
                bend_l.set(wg_path=self.arc_path,
                           wg_width=self.arc_width,
                           start_angle=180.0,
                           end_angle=0.0)

            # Make tapers
            taper_flyback_l = self.cell.taper_flyback.get_default_view(
                i3.LayoutView)
            taper_swg_l = self.cell.taper_swg.get_default_view(i3.LayoutView)
            if self.cell.taper_type is "default":
                # Linear taper pcell
                if self.cell.bend_type is "default":
                    flyback_bend_width = self.bend_width
                    swg_bend_width = self.bend_width
                else:
                    arcsize = self.arc_width.shape
                    arclength = arcsize[0]
                    flyback_bend_width = self.arc_width[arclength - 1]
                    swg_bend_width = self.arc_width[0]

                taper_flyback_l.set(length=self.taper_length,
                                    wg_width_in=flyback_bend_width,
                                    wg_width_out=self.flyback_wg_width)
                taper_swg_l.set(length=self.taper_length,
                                wg_width_in=swg_bend_width,
                                wg_width_out=self.grat_wg_width)
            else:
                # Custom taper pcell
                taper_flyback_l.set(wg_path=self.taper_path_flyback,
                                    wg_width=self.taper_width_flyback,
                                    start_angle=0.0,
                                    end_angle=0.0)
                taper_swg_l.set(wg_path=self.taper_path_swg,
                                wg_width=self.taper_width_swg,
                                start_angle=0.0,
                                end_angle=0.0)

            return swg_l, flyback_l, bend_l, taper_swg_l, taper_flyback_l

        def _generate_instances(self, insts):
            swg_l, flyback_l, bend_l, taper_swg_l, taper_flyback_l = self._get_components(
            )

            for ii in range(self.numrows):
                # Find component translations (for all numrows)
                t_swg = i3.Translation((0.0, ii * self.pitch))
                t_taper_swg_w = vector_match_transform(
                    taper_swg_l.ports["out"], swg_l.ports['in']) + t_swg
                t_taper_swg_e = vector_match_transform(
                    taper_swg_l.ports["out"],
                    swg_l.ports['out'],
                    mirrored=True) + t_swg
                # Add instances (for all numrows)
                insts += i3.SRef(reference=swg_l,
                                 name="SidewallGratWg" + str(ii),
                                 transformation=t_swg)
                insts += i3.SRef(reference=taper_swg_l,
                                 name="SwgTaper_West" + str(ii),
                                 transformation=t_taper_swg_w)
                insts += i3.SRef(reference=taper_swg_l,
                                 name="SwgTaper_East" + str(ii),
                                 transformation=t_taper_swg_e)
                if ii < (self.numrows - 1):
                    # Find component translations (for numrows-1)
                    flyback_offset = self.grat_wg_width / 2 + self.spacing + self.flyback_wg_width / 2
                    t_flyback = i3.Translation(
                        (0.0, ii * self.pitch + flyback_offset))
                    t_taper_flyback_w = vector_match_transform(
                        taper_flyback_l.ports["out"],
                        flyback_l.ports['in']) + t_flyback
                    t_taper_flyback_e = vector_match_transform(
                        taper_flyback_l.ports["out"],
                        flyback_l.ports['out'],
                        mirrored=True) + t_flyback
                    t_bend_e = i3.VMirror() + vector_match_transform(
                        bend_l.ports['in'],
                        taper_swg_l.ports["in"],
                        mirrored=True) + t_taper_swg_e
                    t_bend_w = i3.VMirror() + vector_match_transform(
                        bend_l.ports['out'], taper_flyback_l.ports["in"]
                    ) + t_taper_flyback_w + i3.Translation((0.0, self.pitch))
                    # Add instances (for numrows-1)
                    insts += i3.SRef(reference=flyback_l,
                                     name="FlybackWg" + str(ii),
                                     transformation=t_flyback)
                    insts += i3.SRef(reference=taper_flyback_l,
                                     name="FlybackTaper_West" + str(ii),
                                     transformation=t_taper_flyback_w)
                    insts += i3.SRef(reference=taper_flyback_l,
                                     name="FlybackTaper_East" + str(ii),
                                     transformation=t_taper_flyback_e)
                    insts += i3.SRef(reference=bend_l,
                                     name="Bend_West" + str(ii),
                                     transformation=t_bend_w)
                    insts += i3.SRef(reference=bend_l,
                                     name="Bend_East" + str(ii),
                                     transformation=t_bend_e)

            return insts

        def _generate_ports(self, ports):
            ports += self.instances["SidewallGratWg0"].ports["in"]
            ports += self.instances["SidewallGratWg" +
                                    str(self.numrows - 1)].ports["out"]
            return ports
Example #8
0
    class Layout(i3.LayoutView):
        """
        List of properties:
        period
        duty_cycle
            absolute length of the grating section, not percentage
        grating_amp
        grat_wg_width
        flyback_wg_width
        pitch
        spacing
        length
        numrows
        taper_length
        taper_path_flyback
        taper_width_flyback
        taper_path_swg
        taper_width_swg
        bend_width
        arc_path
        arc_width

        TO GENERATE CUSTOM BENDS:
            set pitch to either 16.0 or 16.516

        """

        from ipkiss.technology import get_technology
        TECH = get_technology()

        # -----------
        # Properties

        # Sidewall grating waveguide properties
        period = i3.PositiveNumberProperty(default=.3,
                                           doc="Period of sidewall grating")
        duty_cycle = i3.PositiveNumberProperty(
            default=.1,
            doc="Length of grating teeth (along periodic direction)")
        grating_amp = i3.NumberProperty(
            default=.01,
            doc=
            "Width/amplitude of grating teeth (normal to periodic direction)")
        grat_wg_width = i3.PositiveNumberProperty(
            default=1.0,
            doc=
            "Width of sidewall grating waveguide core (if grating_amp=0 width of waveguide)"
        )

        # Flyback waveguide properites
        flyback_wg_width = i3.PositiveNumberProperty(
            default=0.4, doc="Width of flyback waveguide core")

        # Grating array properties
        pitch = i3.PositiveNumberProperty(
            default=16.0,
            doc=
            "Sidewall grating pitch (center-to-center distance of sidewall grating waveguides)"
        )
        spacing = i3.PositiveNumberProperty(
            doc="Gap between sidewall grating waveguides and flyback waveguides"
        )

        def _default_spacing(self):
            spacing = (self.pitch - self.grat_wg_width -
                       self.flyback_wg_width) / 2
            return spacing

        length = i3.PositiveNumberProperty(
            default=800.0,
            doc="Length of straight (untapered) waveguide sections")
        numrows = i3.PositiveIntProperty(
            default=32,
            doc=
            "Number of sidewall grating/flyback waveguide pairs in the array")

        # Taper properties
        # Properties used for taper_type = "default"
        taper_length = i3.PositiveNumberProperty(default=10.0,
                                                 doc="Taper length")
        # Properties used for taper_type = "manual"
        taper_path_flyback = i3.NumpyArrayProperty(
            doc=
            "List of coordinates denoting the center path of the taper (bend to flyback waveguide)"
        )
        taper_width_flyback = i3.NumpyArrayProperty(
            doc=
            "List of taper widths normal to each point on the path (bend to flyback waveguide)"
        )
        taper_path_swg = i3.NumpyArrayProperty(
            doc=
            "List of coordinates denoting the center path of the taper (bend to sidewall grating waveguide)"
        )
        taper_width_swg = i3.NumpyArrayProperty(
            doc=
            "List of taper widths normal to each point on the path (bend to sidewall grating waveguide)"
        )

        # REDEFINING TAPER HERE
        # taper_swg       = i3.ViewProperty( default='', doc='Taper layout that connects grating waveguide to the bends')
        # taper_flyback   = i3.ViewProperty( default='', doc='Taper layout that connects flyback waveguide to the bends')

        # Pick grating type:
        # 'one_sidewall', 'two_sidewalls', 'nitride_vertical_top', 'nitride_vertical_bottom',
        # 'nitride_one_sidewall_top', 'nitride_one_sidewall_bottom',
        grating_type = i3.StringProperty(default='',
                                         doc='Grating type to draw')

        def _default_taper_path_flyback(self):
            #Default is a straight linear taper
            path = np.array([[0.0, 0.0], [self.taper_length, 0.0]], np.float_)
            return path

        def _default_taper_width_flyback(self):
            # Default is a straight linear taper
            widths = np.array([self.bend_width, self.flyback_wg_width],
                              np.float_)
            return widths

        def _default_taper_path_swg(self):
            #Default is a straight linear taper
            path = np.array([[0.0, 0.0], [self.taper_length, 0.0]], np.float_)
            return path

        def _default_taper_width_swg(self):
            # Default is a straight linear taper
            widths = np.array([self.bend_width, self.grat_wg_width], np.float_)
            return widths

        # Bend properties
        # Properties used for bend_type = "default"
        bend_width = i3.PositiveNumberProperty(
            default=TECH.WG.CORE_WIDTH,
            doc="Width of waveguides in bend sections")
        # Properties used for bend_type = "manual"
        arc_path = i3.NumpyArrayProperty(
            doc=
            "List of coordinates denoting the center path of the arc connectors (going from sidewall grating waveguide to flyback waveguide"
        )
        arc_width = i3.NumpyArrayProperty(
            doc=
            "List of arc widths normal to each point on the path (going from sidewall grating waveguide to flyback waveguide"
        )

        def _default_arc_path(self):
            if self.pitch == 16.0:
                #Use pregenerated adiabatic bends (TX)
                pathwidth = np.loadtxt("../nathan/bend_data/txbend.txt",
                                       np.float_)
                arc_path = pathwidth[:, :2]
            elif self.pitch == 16.516:
                # Use pregenerated adiabatic bends (RX)
                pathwidth = np.loadtxt("../nathan/bend_data/rxbend.txt",
                                       np.float_)
                arc_path = pathwidth[:, :2]
            else:
                # Default is 180 arc
                arc_path = np.zeros([181, 2], np.float_)
                bend_rad = (self.grat_wg_width / 2 + self.spacing +
                            self.flyback_wg_width / 2) / 2
                for ii in range(181):
                    angle = np.pi / 180.0 * ii
                    arc_path[ii, :] = bend_rad * np.array(
                        [-np.sin(angle), np.cos(angle)], np.float_)

            return arc_path

        def _default_arc_width(self):
            if self.pitch == 16.0:
                #Use pregenerated adiabatic bends (TX)
                pathwidth = np.loadtxt("../nathan/bend_data/txbend.txt",
                                       np.float_)
                arc_width = pathwidth[:, 2]
            elif self.pitch == 16.516:
                #Use pregenerated adiabatic bends (RX)
                pathwidth = np.loadtxt("../nathan/bend_data/rxbend.txt",
                                       np.float_)
                arc_width = pathwidth[:, 2]
            else:
                #Default is uniform width with default core_width
                arc_width = np.ones([181], np.float_)
                arc_width = arc_width * self.bend_width
            return arc_width

        def validate_properties(self):
            """Check whether the combination of properties is valid."""
            if (self.grat_wg_width + self.flyback_wg_width +
                    2 * self.spacing) != self.pitch:
                raise i3.PropertyValidationError(
                    self,
                    "Array incorrectly overspecified (pitch=/=wg_widths+spacing",
                    {"pitch": self.pitch})
            return True

        @i3.cache()
        def _get_components(self):
            # Make waveguides

            # Pick grating type:
            # 'one_sidewall', 'two_sidewalls', 'nitride_vertical_top', 'nitride_vertical_bottom',
            # 'nitride_one_sidewall_top', 'nitride_one_sidewall_bottom',

            if self.grating_type == 'one_sidewall':
                # single sidewall grating
                swg_l = self.cell.swg.get_default_view(i3.LayoutView)
                swg_l.set(period=self.period,
                          duty_cycle=self.duty_cycle,
                          grating_amp=self.grating_amp,
                          wg_width=self.grat_wg_width,
                          length=self.length,
                          both_sides=False)

            elif self.grating_type == 'two_sidewalls':
                # double sidewall grating
                swg_l = self.cell.swg.get_default_view(i3.LayoutView)
                swg_l.set(period=self.period,
                          duty_cycle=self.duty_cycle,
                          grating_amp=self.grating_amp,
                          wg_width=self.grat_wg_width,
                          length=self.length,
                          both_sides=True)

            elif self.grating_type == 'nitride_vertical_top':
                # nitride vertical grating, top layer
                swg_l = NitrideGratingWg().get_default_view(i3.LayoutView)
                swg_l.set(period=self.period,
                          duty_cycle=self.duty_cycle,
                          grating_amp=self.grating_amp,
                          wg_width=self.grat_wg_width,
                          length=self.length,
                          grating_type='vertical',
                          nitride_layer='top')

            elif self.grating_type == 'nitride_vertical_bottom':
                # nitride vertical grating, top layer
                swg_l = NitrideGratingWg().get_default_view(i3.LayoutView)
                swg_l.set(period=self.period,
                          duty_cycle=self.duty_cycle,
                          grating_amp=self.grating_amp,
                          wg_width=self.grat_wg_width,
                          length=self.length,
                          grating_type='vertical',
                          nitride_layer='bottom')

            elif self.grating_type == 'nitride_one_sidewall_top':
                # nitride vertical grating, top layer
                swg_l = NitrideGratingWg().get_default_view(i3.LayoutView)
                swg_l.set(period=self.period,
                          duty_cycle=self.duty_cycle,
                          grating_amp=self.grating_amp,
                          wg_width=self.grat_wg_width,
                          length=self.length,
                          grating_type='one_sidewall',
                          nitride_layer='top')

            elif self.grating_type == 'nitride_one_sidewall_bottom':
                # nitride vertical grating, top layer
                swg_l = NitrideGratingWg().get_default_view(i3.LayoutView)
                swg_l.set(period=self.period,
                          duty_cycle=self.duty_cycle,
                          grating_amp=self.grating_amp,
                          wg_width=self.grat_wg_width,
                          length=self.length,
                          grating_type='one_sidewall',
                          nitride_layer='bottom')

            # end making grating if statement

            # flyback and stuff
            flyback_l = self.cell.flyback.get_default_view(i3.LayoutView)
            flyback_path = [(0.0, 0.0), (self.length, 0.0)]
            wg_temp_flyback = self.cell.wg_temp.get_default_view(i3.LayoutView)
            wg_temp_flyback.set(core_width=self.flyback_wg_width)
            flyback_l.set(trace_template=wg_temp_flyback, shape=flyback_path)

            # Center-to-center distance between sidewall grating waveguide and flyback waveguide
            flyback_offset = self.grat_wg_width / 2 + self.spacing + self.flyback_wg_width / 2

            # Make bends
            bend_l = self.cell.bend.get_default_view(i3.LayoutView)
            if self.cell.bend_type is "default":
                # Default waveguide 180 arc
                bend_rad = flyback_offset / 2
                arc = i3.ShapeArc(center=(0.0, 0.0),
                                  radius=bend_rad,
                                  start_angle=89.0,
                                  end_angle=270.0)
                wg_temp_bend = self.cell.wg_temp.get_default_view(
                    i3.LayoutView)
                wg_temp_bend.set(core_width=self.bend_width)
                bend_l.set(trace_template=wg_temp_bend, shape=arc)
            else:
                # Custom bend pcell
                bend_l.set(wg_path=self.arc_path,
                           wg_width=self.arc_width,
                           start_angle=180.0,
                           end_angle=0.0)

            # Make tapers
            taper_flyback_l = self.cell.taper_flyback.get_default_view(
                i3.LayoutView)
            taper_swg_l = self.cell.taper_swg.get_default_view(i3.LayoutView)

            if self.cell.taper_type is "default":
                # Linear taper pcell
                if self.cell.bend_type is "default":
                    flyback_bend_width = self.bend_width
                    swg_bend_width = self.bend_width
                else:
                    arcsize = self.arc_width.shape
                    arclength = arcsize[0]
                    flyback_bend_width = self.arc_width[arclength - 1]
                    swg_bend_width = self.arc_width[0]

                taper_flyback_l.set(length=self.taper_length,
                                    wg_width_in=flyback_bend_width,
                                    wg_width_out=self.flyback_wg_width)
                taper_swg_l.set(length=self.taper_length,
                                wg_width_in=swg_bend_width,
                                wg_width_out=self.grat_wg_width)
            else:
                # Custom taper pcell
                taper_flyback_l.set(wg_path=self.taper_path_flyback,
                                    wg_width=self.taper_width_flyback,
                                    start_angle=0.0,
                                    end_angle=0.0)
                taper_swg_l.set(wg_path=self.taper_path_swg,
                                wg_width=self.taper_width_swg,
                                start_angle=0.0,
                                end_angle=0.0)

            return swg_l, flyback_l, bend_l, taper_swg_l, taper_flyback_l

        def _generate_instances(self, insts):
            swg_l, flyback_l, bend_l, taper_swg_l, taper_flyback_l = self._get_components(
            )

            # Hard code the tapers into here: (I hate hardcoding stuff, but no choice here)
            taper_length = 79.0  # 79 is the best according to deniz' sims
            width_etch = 4.0
            wg_width = 0.5
            taper_swg = ParabolicTaper(
                name=self.name + '_TAPER').get_default_view(i3.LayoutView)
            taper_swg.set(length=taper_length,
                          width1=wg_width,
                          width2=self.grat_wg_width,
                          width_etch=width_etch)

            taper_flyback = ParabolicTaper(
                name=self.name + '_OTHERTAPER').get_default_view(i3.LayoutView)
            taper_flyback.set(length=taper_length,
                              width1=wg_width,
                              width2=self.flyback_wg_width,
                              width_etch=width_etch)

            for ii in range(self.numrows):

                # Find component translations (for all numrows)
                t_swg = i3.Translation((0.0, ii * self.pitch))
                # t_taper_swg_w   = vector_match_transform(taper_swg_l.ports["out"], swg_l.ports['in']) + t_swg
                # t_taper_swg_e   = vector_match_transform(taper_swg_l.ports["out"], swg_l.ports['out'], mirrored=True) + t_swg
                t_taper_swg_w = vector_match_transform(
                    taper_swg.ports["right"], swg_l.ports['in']) + t_swg
                t_taper_swg_e = vector_match_transform(
                    taper_swg.ports["right"],
                    swg_l.ports['out'],
                    mirrored=True) + t_swg

                # Add grating rows + grating row tapers
                insts += i3.SRef(reference=swg_l,
                                 name="SidewallGratWg" + str(ii),
                                 transformation=t_swg)
                # insts += i3.SRef(reference=taper_swg_l, name="SwgTaper_West" + str(ii), transformation=t_taper_swg_w)
                # insts += i3.SRef(reference=taper_swg_l, name="SwgTaper_East" + str(ii), transformation=t_taper_swg_e)
                insts += i3.SRef(reference=taper_swg,
                                 name="SwgTaper_West" + str(ii),
                                 transformation=t_taper_swg_w)
                insts += i3.SRef(reference=taper_swg,
                                 name="SwgTaper_East" + str(ii),
                                 transformation=t_taper_swg_e)

                if ii < (self.numrows - 1):

                    # Find component translations (for numrows-1)
                    flyback_offset = self.grat_wg_width / 2 + self.spacing + self.flyback_wg_width / 2
                    t_flyback = i3.Translation(
                        (0.0, ii * self.pitch + flyback_offset))
                    # t_taper_flyback_w   = vector_match_transform(taper_flyback_l.ports["out"], flyback_l.ports['in']) + t_flyback
                    # t_taper_flyback_e   = vector_match_transform(taper_flyback_l.ports["out"], flyback_l.ports['out'],
                    #                                        mirrored=True) + t_flyback
                    t_taper_flyback_w   = vector_match_transform( taper_flyback.ports["right"],
                                                                  flyback_l.ports['in'] ) \
                                          + t_flyback
                    t_taper_flyback_e   = vector_match_transform( taper_flyback.ports["right"],
                                                                  flyback_l.ports['out'],
                                                                  mirrored = True ) \
                                          + t_flyback

                    # t_bend_e = i3.VMirror() + vector_match_transform(bend_l.ports['in'], taper_swg_l.ports["in"],mirrored=True) + t_taper_swg_e
                    t_bend_e = i3.VMirror() \
                               + vector_match_transform( bend_l.ports['in'],
                                                       taper_swg.ports["left"],
                                                       mirrored = True ) \
                               + t_taper_swg_e
                    t_bend_w = i3.VMirror() \
                               + vector_match_transform( bend_l.ports['out'],
                                                         taper_flyback.ports["left"] ) \
                               + t_taper_flyback_w \
                               + i3.Translation( (0.0, self.pitch) )

                    # Add instances (for numrows-1)

                    # flyback waveguide
                    insts += i3.SRef(reference=flyback_l,
                                     name="FlybackWg" + str(ii),
                                     transformation=t_flyback)

                    # flyback tapers
                    # insts += i3.SRef(   reference       = taper_flyback_l,
                    #                     name            = "FlybackTaper_West" + str(ii),
                    #                     transformation  = t_taper_flyback_w )
                    # insts += i3.SRef(   reference       = taper_flyback_l,
                    #                     name            = "FlybackTaper_East" + str(ii),
                    #                     transformation  = t_taper_flyback_e )
                    insts += i3.SRef(reference=taper_flyback,
                                     name="FlybackTaper_West" + str(ii),
                                     transformation=t_taper_flyback_w)
                    insts += i3.SRef(reference=taper_flyback,
                                     name="FlybackTaper_East" + str(ii),
                                     transformation=t_taper_flyback_e)

                    # bends
                    insts += i3.SRef(reference=bend_l,
                                     name="Bend_West" + str(ii),
                                     transformation=t_bend_w)
                    insts += i3.SRef(reference=bend_l,
                                     name="Bend_East" + str(ii),
                                     transformation=t_bend_e)

                # end if
            # end for loop

            return insts

        def _generate_ports(self, ports):
            """
            THIS NEEDS TO BE UPDATED TO REFLECT INPUT OUTPUT TAPERS
            """
            # ports += self.instances["SidewallGratWg0"].ports["in"]
            # ports += self.instances["SidewallGratWg"+str(self.numrows-1)].ports["out"]

            ports += i3.OpticalPort(name='in',
                                    position=self.instances["SwgTaper_West0"].
                                    ports["left"].position,
                                    angle=180.0)
            ports += i3.OpticalPort(
                name='out',
                position=self.instances["SwgTaper_East" +
                                        str(self.numrows -
                                            1)].ports["left"].position,
                angle=0.0)

            return ports