def define_taper(self) : tech = get_technology() if hasattr(tech.PROCESS,'RFC'): # FIXME: dirty modular import hasraised = True else: hasraised = False # Case: same waveguide definitions ... if type(self.end_wg_def)==type(self.start_port.wg_definition) : taper = WgElPortTaperLinear(start_port=self.start_port, end_wg_def=self.end_wg_def, straight_extension=self.straight_extension) # Case: special tapering structures ... elif (hasraised and type(self.start_port.wg_definition) == RaisedWGFCWgElDefinition) and (type(self.end_wg_def) == WGFCWgElDefinition) : taper = RaisedWGFCToWGFCPortTaper(start_port=self.start_port, end_wg_def=self.end_wg_def, straight_extension=self.straight_extension) elif (hasraised and type(self.start_port.wg_definition) == WGFCWgElDefinition) and (type(self.end_wg_def) == RaisedWGFCWgElDefinition) : #we manually create a new port in the opposite direction and flip the straight_extensions, so that we can use the same class 'RaisedWGFCToWGFCPortTaper' new_port = OpticalPort(position=(0.0,0.0), wg_definition=self.end_wg_def, angle=self.start_port.angle+180.0) taper = RaisedWGFCToWGFCPortTaper(start_port=new_port, end_wg_def=self.start_port.wg_definition, straight_extension=(self.straight_extension[1], self.straight_extension[0])) taper = Translation(translation=self.start_position.move_polar_copy(self.length, self.start_port.angle_deg))(taper) elif (hasraised and type(self.start_port.wg_definition) == RaisedFCWgElDefinition) and (type(self.end_wg_def) == WgElDefinition) : return RaisedFCToWgElPortTaper(start_port=self.start_port, end_wg_def=self.end_wg_def, straight_extension=self.straight_extension) elif (hasraised and type(self.start_port.wg_definition) == RaisedWgElDefinition) and (type(self.end_wg_def) == WgElDefinition) : taper = RaisedWgElToWgElPortTaper(start_port=self.start_port, end_wg_def=self.end_wg_def, straight_extension=self.straight_extension) elif hasraised and (type(self.start_port.wg_definition) == WgElDefinition) : if type(self.end_wg_def) == RaisedFCWgElDefinition : #we manually create a new port in the opposite direction and flip the straight_extensions, so that we can use the same class 'RaisedFCToWgElPortTaper' new_port = OpticalPort(position=(0.0,0.0), wg_definition=self.end_wg_def, angle=self.start_port.angle+180.0) taper = RaisedFCToWgElPortTaper(start_port=new_port, end_wg_def=self.start_port.wg_definition, straight_extension=(self.straight_extension[1], self.straight_extension[0])) return Translation(translation=self.start_position.move_polar_copy(self.length, self.start_port.angle_deg))(taper) elif type(self.end_wg_def) == RaisedWgElDefinition : #we manually create a new port in the opposite direction and flip the straight_extensions, so that we can use the same class 'RaisedWgElToWgElPortTaper' new_port = OpticalPort(position=(0.0,0.0), wg_definition=self.end_wg_def, angle=self.start_port.angle+180.0) taper = RaisedWgElToWgElPortTaper(start_port=new_port, end_wg_def=self.start_port.wg_definition, straight_extension=(self.straight_extension[1], self.straight_extension[0])) taper = Translation(translation=self.start_position.move_polar_copy(self.length, self.start_port.angle_deg))(taper) else : raise Exception("No taper could be generated between between waveguide types %s and %s." %(self.start_port.wg_definition,self.end_wg_def)) else : raise Exception("No taper could be generated between between waveguide types %s and %s." %(self.start_port.wg_definition,self.end_wg_def)) if (self.__property_was_externally_set__("length")): taper.length = self.length return taper
def get_numpy_matrix_representation(self): """Make a numpy matrix with for each layer a row that contains: StackID | Layer Height | Layer epsilon | number of layers in stack""" from ipkiss.technology import get_technology TECH = get_technology() number_of_heights = self.get_number_of_layers() nm = numpy.zeros((number_of_heights, 4)) for i in range(number_of_heights): nm[i, 3] = number_of_heights nm[i, 2] = self.materials_heights[i][0].epsilon nm[i, 1] = self.materials_heights[i][1] nm[i, 0] = self.get_unique_id() return nm
# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # i-depot BBIE 7396, 7556, 7748 # # Contact: [email protected] from ipcore.all import * from pysimul.runtime.basic import SimulationVolume2D from pysimul.log import PYSIMUL_LOG as LOG from ipkiss.technology import get_technology TECH=get_technology() class __SimulationVolumeVisualization__(StrongPropertyInitializer): pass class SimulationVolumeVisualization2D(__SimulationVolumeVisualization__): simulation_volume = RestrictedProperty(required = True, restriction = RestrictType(SimulationVolume2D), doc = "The simulation volume that we want to visualize") def visualize(self, aspect_ratio_equal = True): LOG.debug("Preparing the 2D visualization...") from dependencies.matplotlib_wrapper import Figure from dependencies.shapely_wrapper import PolygonPatch geom = self.simulation_volume.geometry
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): from ipkiss.technology import get_technology TECH = get_technology() 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)") wg_width = i3.PositiveNumberProperty( default=TECH.WG.CORE_WIDTH, doc="Width of waveguide core (if grating_amp=0 width of waveguide)" ) length = i3.PositiveNumberProperty(default=100.0, doc="Length of waveguide") # Flag to determine grating type # 'vertical' for vertical (along entire width) grating # 'one_sidewall' for single sidewall grating # 'two_sidewalls' for double sidewall grating grating_type = i3.StringProperty( default='', doc= 'determines grating type. Set to "vertical", "one_sidewall", or "two_sidewalls"' ) # flag to determine nitride top or bottom ('top' or 'bottom') nitride_layer = i3.StringProperty( default='', doc= 'determines which nitride layer to draw. Set to "top" or "bottom"') def validate_properties(self): """Check whether the combination of properties is valid.""" if self.duty_cycle >= self.period: raise i3.PropertyValidationError( self, "Duty cycle is larger than/equal to the grating period", {"duty_cyle": self.duty_cycle}) return True def _generate_elements(self, elems): # Waveguide path wg_path = [(0.0, 0.0), (self.length, 0.0)] # Grating tooth path gt_path = [(0.0, 0.0), (self.duty_cycle, 0.0)] gap_cycle = self.period - self.duty_cycle numperiod = int(np.floor(self.length / self.period)) # determine which layer to use my_layer = { 'top': i3.TECH.PPLAYER.AIM.SNAM, 'bottom': i3.TECH.PPLAYER.AIM.FNAM, }[self.nitride_layer] # Add waveguide core elems += i3.Path(layer=i3.TECH.PPLAYER.WG.COR, shape=wg_path, line_width=self.wg_width) if self.grating_type != 'vertical': elems += i3.Path(layer=my_layer, shape=wg_path, line_width=self.wg_width) # Add grating teeth ytrans = i3.Translation( (0.0, self.wg_width / 2 + self.grating_amp / 2)) for ii in range(numperiod): #Start with gap rather than tooth if self.grating_type == 'vertical': # Draw vertically etched tooth xtrans = i3.Translation( ((gap_cycle + ii * self.period), 0.0)) elems += i3.Path(layer=my_layer, shape=gt_path, line_width=self.wg_width, transformation=(xtrans)) elif self.grating_type == 'one_sidewall': # Draw single sided sidewall grating xtrans = i3.Translation( ((gap_cycle + ii * self.period), 0.0)) elems += i3.Path(layer=my_layer, shape=gt_path, line_width=self.grating_amp, transformation=(xtrans + ytrans)) elif self.grating_type == 'two_sidewalls': # Draw double sided sidewall grating xtrans = i3.Translation( ((gap_cycle + ii * self.period), 0.0)) elems += i3.Path(layer=my_layer, shape=gt_path, line_width=self.grating_amp, transformation=(xtrans + ytrans)) elems += i3.Path(layer=my_layer, shape=gt_path, line_width=self.grating_amp, transformation=(xtrans - ytrans)) # # draw bottom grating if desired # if self.both_sides == True: # elems += i3.Path(layer=i3.TECH.PPLAYER.WG.COR, shape=gt_path, line_width=self.grating_amp, # transformation=(xtrans - ytrans)) # Add block layers import block_layers as bl block_layers = bl.layers block_widths = bl.widths for ii in range(len(block_layers)): elems += i3.Path(layer=block_layers[ii], shape=wg_path, line_width=self.wg_width + 2 * self.grating_amp + 2 * block_widths[ii]) return elems def _generate_ports(self, ports): # ports += i3.OpticalPort(name="in", position=(0.0, 0.0), angle=180.0, # trace_template=StripWgTemplate().Layout(core_width=self.wg_width)) # ports += i3.OpticalPort(name="out", position=(self.length, 0.0), angle=0.0, # trace_template=StripWgTemplate().Layout(core_width=self.wg_width)) ports += i3.OpticalPort(name="in", position=(0.0, 0.0), angle=180.0) ports += i3.OpticalPort(name="out", position=(self.length, 0.0), angle=0.0) 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
class Layout(i3.LayoutView): from ipkiss.technology import get_technology TECH = get_technology() 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)") wg_width = i3.PositiveNumberProperty( default=TECH.WG.CORE_WIDTH, doc="Width of waveguide core (if grating_amp=0 width of waveguide)" ) length = i3.PositiveNumberProperty(default=100.0, doc="Length of waveguide") # Flag, set to True to draw grating on both sides of wg. both_sides = i3.BoolProperty( default=False, doc='set to true to draw grating on both sides') def validate_properties(self): """Check whether the combination of properties is valid.""" if self.duty_cycle >= self.period: raise i3.PropertyValidationError( self, "Duty cycle is larger than/equal to the grating period", {"duty_cyle": self.duty_cycle}) return True def _generate_elements(self, elems): # Waveguide path wg_path = [(0.0, 0.0), (self.length, 0.0)] # Grating tooth path gt_path = [(0.0, 0.0), (self.duty_cycle, 0.0)] gap_cycle = self.period - self.duty_cycle numperiod = int(np.floor(self.length / self.period)) # Add waveguide core elems += i3.Path(layer=i3.TECH.PPLAYER.WG.COR, shape=wg_path, line_width=self.wg_width) # Add grating teeth if self.grating_amp > 0.0: # grating amplitude has to be non-zero ytrans = i3.Translation( (0.0, self.wg_width / 2 + self.grating_amp / 2)) for ii in range(numperiod): #Start with gap rather than tooth xtrans = i3.Translation( ((gap_cycle + ii * self.period), 0.0)) elems += i3.Path(layer=i3.TECH.PPLAYER.WG.COR, shape=gt_path, line_width=self.grating_amp, transformation=(xtrans + ytrans)) # draw bottom grating if desired if self.both_sides == True: elems += i3.Path(layer=i3.TECH.PPLAYER.WG.COR, shape=gt_path, line_width=self.grating_amp, transformation=(xtrans - ytrans)) # Add block layers import block_layers as bl block_layers = bl.layers block_widths = bl.widths for ii in range(len(block_layers)): elems += i3.Path(layer=block_layers[ii], shape=wg_path, line_width=self.wg_width + 2 * self.grating_amp + 2 * block_widths[ii]) return elems def _generate_ports(self, ports): # ports += i3.OpticalPort(name="in", position=(0.0, 0.0), angle=180.0, # trace_template=StripWgTemplate().Layout(core_width=self.wg_width)) # ports += i3.OpticalPort(name="out", position=(self.length, 0.0), angle=0.0, # trace_template=StripWgTemplate().Layout(core_width=self.wg_width)) ports += i3.OpticalPort(name="in", position=(0.0, 0.0), angle=180.0) ports += i3.OpticalPort(name="out", position=(self.length, 0.0), angle=0.0) return ports