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