class Layout(i3.LayoutView): """ This section is to draw a Vernier scale """ # Basic properties of the scale spacing = i3.PositiveNumberProperty(default=4, doc="spacing between bars") number_of_bars = i3.IntProperty(default=13, doc="the number of bars") # Detailed properties of the scale bar_length = i3.PositiveNumberProperty( default=10, doc="length of the shortest bars on the scale") bar_extra_length = i3.PositiveNumberProperty( default=10, doc="extra length of the central bar") bar_width = i3.PositiveNumberProperty(default=2, doc="width of a single bar") process = i3.ProcessProperty( default=i3.TECH.PPLAYER.CH2.TRENCH ) #i3.TECH.PROCESS.WG, doc="Process Layer on which the cross is drawn") # Purpose property cannot be set from outside purpose = i3.PurposeProperty( locked=True, default=i3.TECH.PPLAYER.CH2.TRENCH ) #i3.TECH.PURPOSE.DF.LINE, doc="Process Purpose of the cross") def validate_properties(self): # The scale is symmetric with respect to its central bar so the number of bars is an odd number if self.number_of_bars % 2 == 0: raise i3.PropertyValidationError( self, "The number of bars should be an odd number", {"number_of_bars": self.number_of_bars}) return True def _generate_elements(self, elems): # Draw the central bar, which is longer than the others elems += i3.Rectangle( layer=i3.TECH.PPLAYER.CH1. TRENCH, #i3.PPLayer(self.process, self.purpose), center=(0, (self.bar_length + self.bar_extra_length) * 0.5), box_size=(self.bar_width, self.bar_length + self.bar_extra_length)) # Draw the other bars for i in range((self.number_of_bars - 1) / 2): elems += i3.Rectangle( layer=i3.TECH.PPLAYER.CH1. TRENCH, #i3.PPLayer(self.process, self.purpose), center=(-(i + 1) * self.spacing, self.bar_length * 0.5), box_size=(self.bar_width, self.bar_length)) for j in range((self.number_of_bars - 1) / 2): elems += i3.Rectangle( layer=i3.TECH.PPLAYER.CH1. TRENCH, #i3.PPLayer(self.process, self.purpose), center=((j + 1) * self.spacing, self.bar_length * 0.5), box_size=(self.bar_width, self.bar_length)) return elems
class Layout(i3.LayoutView): # Properties ------- # taper to draw taper_prop_dict = i3.DictProperty( default = {}, doc = 'Dictionary of taper cell properties.' + \ 'Properties = "length", "width1", "width2", "width_etch" ' ) # number of rows n_rows = i3.IntProperty(default=3, doc='number of taper clip rows') # number of taper pairs per row n_taper_pairs_per_row = i3.IntProperty( default=2, doc='number of taper clip pairs per row') # spacing between rows row_spacing = i3.PositiveNumberProperty( default=0.0, doc='spacing between rows (midpoint to midpoint)') # Taper pair properties connect_length = i3.NumberProperty(default=0.0, doc='distance between tapers') pair_connect_length = i3.NumberProperty( default=0.0, doc='distance between taper pairs') # input/output connection length bot_gc_connect_length = i3.NumberProperty( default=0.0, doc='distance between grating and bottom taper') top_gc_connect_length = i3.NumberProperty( default=0.0, doc='distance between grating and top taper') # radius of each arc bend_radius = i3.PositiveNumberProperty( default=5.0, doc='spacing between rows (midpoint to midpoint)') # Methods ------- def _generate_instances(self, insts): # Generates taper clip # make my OWN custom waveguide trace template # wg_trace = f_MyIMECWaveguideTemplate(core_width=self.taper_prop_dict['width1'], # cladding_width=self.taper_prop_dict['width1'] + 2.0 * self.taper_prop_dict['width_etch']) # make waveguide wg = i3.Waveguide(trace_template=StripWgTemplate(), name=self.name + '_WG') wg_round = i3.RoundedWaveguide(trace_template=StripWgTemplate(), name=self.name + '_WG_ROUND') # how much to translate bends left/right # t_left = i3.Translation((self.bend_radius + (float(self.n_rows)/2.0) )) t_left = i3.Translation((-2.5 * self.bend_radius, 0.0)) t_right = i3.Translation((2.5 * self.bend_radius, 0.0)) # draw taper pair rows for ii in range(self.n_rows): # add rows tp_rows_layout = TaperPairRow(name=self.name + '_TProw' + str(ii)).get_default_view( i3.LayoutView) tp_rows_layout.set( taper_prop_dict=self.taper_prop_dict, connect_length=self.connect_length, pair_connect_length=self.pair_connect_length, n_pairs=self.n_taper_pairs_per_row) # set translation t = i3.Translation((0.0, float(ii) * self.row_spacing)) # place taper pair row tp_row_name = self.name + '_TP_ROW' + str(ii) insts += i3.SRef(name=tp_row_name, reference=tp_rows_layout, transformation=t) # draw connecting arcs if ii > 0: if (ii % 2) == 1: # bend on the right # make shape bend row_name = self.name + '_TP_ROW' + str(ii - 1) shape_bend = i3.ShapeBend(start_point=insts[row_name]. ports['right'].position, radius=self.bend_radius, start_angle=-90.05, end_angle=90.05, angle_step=0.1) # add 180 deg bend wg_copy = i3.Waveguide( trace_template=StripWgTemplate(), name=self.name + '_arc_r' + str(ii)) arc_name = self.name + '_arc' + str(ii) insts += i3.SRef( name=arc_name, reference=wg_copy.Layout(shape=shape_bend), transformation=t_right) # connect bottom wgs # get coords in_port_coords = insts[arc_name].ports['in'].position out_port_coords = insts[row_name].ports[ 'right'].position # draw bezier curve bez = BezierCurve( N=100, P0=(in_port_coords[0] + 0.01, in_port_coords[1]), P1=(in_port_coords[0] - self.bend_radius / 2.0, in_port_coords[1]), P2=(out_port_coords[0] + self.bend_radius / 2.0, out_port_coords[1]), P3=(out_port_coords[0] - 0.01, out_port_coords[1]), R=(-self.bend_radius, +self.bend_radius), dy_dx=(0.0, -0.0)) bez_coords = bez.bezier_coords() # make ipkiss shape s = i3.Shape(bez_coords) # add bottom wg connector wg_copy = i3.Waveguide( trace_template=StripWgTemplate(), name=self.name + '_arc_r_con' + str(ii)) insts += i3.SRef(name=self.name + '_con_wg_r_b_' + str(ii), reference=wg_copy.Layout(shape=s)) # connect top wgs next_row_name = self.name + '_TP_ROW' + str(ii) in_port_coords = insts[arc_name].ports['out'].position out_port_coords = insts[next_row_name].ports[ 'right'].position # draw bezier curve bez = BezierCurve( N=500, P0=(in_port_coords[0] + 0.01, in_port_coords[1]), P1=(in_port_coords[0] - self.bend_radius / 2.0, in_port_coords[1]), P2=(out_port_coords[0] + self.bend_radius / 2.0, out_port_coords[1]), P3=(out_port_coords[0] - 0.01, out_port_coords[1]), R=(self.bend_radius, -self.bend_radius), dy_dx=(0.0, -0.0)) bez_coords = bez.bezier_coords() # make ipkiss shape s = i3.Shape(bez_coords) # add wg bend wg_copy = i3.Waveguide( trace_template=StripWgTemplate(), name=self.name + '_bez_r' + str(ii)) insts += i3.SRef(name=self.name + '_con_wg_r_t_' + str(ii), reference=wg_copy.Layout(shape=s)) else: # bend on the left # make shape bend row_name = self.name + '_TP_ROW' + str(ii - 1) shape_bend = i3.ShapeBend(start_point=( insts[row_name].ports['left'].position), radius=self.bend_radius, start_angle=90.05, end_angle=-90.05, angle_step=0.1, clockwise=False) # add 180 deg bend wg_copy = i3.Waveguide( trace_template=StripWgTemplate(), name=self.name + '_arc_l' + str(ii)) arc_name = self.name + '_arc' + str(ii) insts += i3.SRef( name=arc_name, reference=wg_copy.Layout(shape=shape_bend), transformation=t_left) # connect bottom wgs # get coords in_port_coords = insts[arc_name].ports['out'].position out_port_coords = insts[row_name].ports[ 'left'].position # draw bezier curve bez = BezierCurve( N=100, P0=(in_port_coords[0] - 0.01, in_port_coords[1]), P1=(in_port_coords[0] + self.bend_radius / 2.0, in_port_coords[1]), P2=(out_port_coords[0] - self.bend_radius / 2.0, out_port_coords[1]), P3=(out_port_coords[0] + 0.01, out_port_coords[1]), R=(-self.bend_radius, +self.bend_radius), dy_dx=(0.0, -0.0)) bez_coords = bez.bezier_coords() # make ipkiss shape s = i3.Shape(bez_coords) # add bottom wg connector wg_copy = i3.Waveguide( trace_template=StripWgTemplate(), name=self.name + '_arc_l_con' + str(ii)) insts += i3.SRef(name=self.name + '_con_wg_l_b_' + str(ii), reference=wg_copy.Layout(shape=s)) # connect top wgs next_row_name = self.name + '_TP_ROW' + str(ii) in_port_coords = insts[arc_name].ports['in'].position out_port_coords = insts[next_row_name].ports[ 'left'].position # draw bezier curve bez = BezierCurve( N=500, P0=(in_port_coords[0] - 0.01, in_port_coords[1]), P1=(in_port_coords[0] + self.bend_radius / 2.0, in_port_coords[1]), P2=(out_port_coords[0] - self.bend_radius / 2.0, out_port_coords[1]), P3=(out_port_coords[0] + 0.01, out_port_coords[1]), R=(-self.bend_radius, +self.bend_radius), dy_dx=(0.0, -0.0)) bez_coords = bez.bezier_coords() # make ipkiss shape s = i3.Shape(bez_coords) # add wg bend wg_copy = i3.Waveguide( trace_template=StripWgTemplate(), name=self.name + '_bez_l' + str(ii)) insts += i3.SRef(name=self.name + '_con_wg_l_t_' + str(ii), reference=wg_copy.Layout(shape=s)) # end if bend # end drawing connecting arcs # end for ii in range(self.rows) # # connect the input grating # # pick grating layout to return # grating_layout = { # 'FGCCTE_FC1DC_625_313': FGCCTE_FC1DC_625_313().Layout(), # 'FGCCTE_FCWFC1DC_630_378': FGCCTE_FCWFC1DC_630_378().Layout(), # 'FGCCTM_FC1DC_984_492': FGCCTM_FC1DC_984_492().Layout(), # }[self.grating_name] # # # # # place bottom grating # # always assuming bottom grating starts on the left # bot_grating_name = self.name+'_bot_grating' # t = i3.vector_match_transform( grating_layout.ports['waveguide'], # insts[self.name + '_TP_ROW0'].ports['left'] ) + \ # i3.Translation( ( -self.bot_gc_connect_length, 0.0 ) ) # # insts += i3.SRef( name = bot_grating_name, # reference = grating_layout, # transformation = t ) # # # connect bottom grating to taper # route_wg_bot = i3.RouteManhattan( input_port = insts[bot_grating_name].ports['waveguide'], # output_port = insts[self.name + '_TP_ROW0'].ports['left'] ) # # # add wg # wg_bot = i3.Waveguide( trace_template = StripWgTemplate(), name = self.name + '_WG_BOT') # insts += i3.SRef(name=self.name + '_connect_wg_bot', reference=wg_bot.Layout(shape=route_wg_bot)) # # # # # place top grating # top_grating_name = self.name + '_top_grating' # if (self.n_rows % 2) == 1: # # even # of rows, output is to the right # t = i3.vector_match_transform( grating_layout.ports['waveguide'], # insts[self.name + '_TP_ROW' + str(self.n_rows-1)].ports['right'], # mirrored = True ) + \ # i3.Translation((self.top_gc_connect_length, 0.0)) # # insts += i3.SRef( name = top_grating_name, # reference = grating_layout, # transformation = t) # # # connect top grating to taper # route_wg_top = i3.RouteManhattan( input_port = insts[top_grating_name].ports['waveguide'], # output_port = insts[self.name + '_TP_ROW' + str(self.n_rows-1)].ports['right']) # # # add wg # wg_top = i3.Waveguide( trace_template = StripWgTemplate(), name = self.name + '_WG_TOP') # insts += i3.SRef(name=self.name + '_connect_wg_top', reference=wg_top.Layout(shape=route_wg_top)) # # else: # # odd # of rows, output is to the left # t = i3.vector_match_transform( grating_layout.ports['waveguide'], # insts[self.name + '_TP_ROW' + str(self.n_rows-1)].ports['left'], # mirrored = False ) + \ # i3.Translation((-self.top_gc_connect_length, 0.0)) # # insts += i3.SRef( name = top_grating_name, # reference = grating_layout, # transformation = t) # # # connect top grating to taper # route_wg_top = i3.RouteManhattan( input_port = insts[top_grating_name].ports['waveguide'], # output_port = insts[self.name + '_TP_ROW' + str(self.n_rows-1)].ports['left']) # # # add wg # wg_top = i3.Waveguide( trace_template = StripWgTemplate(), name = self.name + '_WG_TOP') # insts += i3.SRef(name=self.name + '_connect_wg_top', reference=wg_top.Layout(shape=route_wg_top)) return insts
class Spirals(PlaceAndAutoRoute): _name_prefix = 'LSpiral' tipo = i3.PositiveNumberProperty(doc="Number loops", default=1) waveguide_template = i3.DefinitionProperty(doc="Trace template used") R = i3.PositiveNumberProperty(default=200, doc="Radius of curvature") spacing = i3.PositiveNumberProperty(default=100, doc="Radius of curvature") n_loops = i3.IntProperty(doc="Number loops", default=2) n_loops_vec = i3.ListProperty(default=[4, 8]) s_length_vec = i3.ListProperty(default=[0.0]) Spiral_list = i3.ChildCellListProperty( doc="List containing the 90 degree angle child cells") #chip_length = i3.PositiveNumberProperty(default=2500.0, doc="Radius of curvature") chip_length = i3.PositiveNumberProperty(default=3000.0, doc="Radius of curvature") Port = i3.ChildCellProperty(doc="Used for ports") Port2 = i3.ChildCellProperty(doc="Used for ports") tlport = i3.PositiveNumberProperty(default=1000.0, doc="Transition legth to ports") couplingWG = i3.ChildCellProperty(doc="", locked=True) couplingWG_l = i3.PositiveNumberProperty(default=5000.0, doc="Length of the coupling WG ") tt_port = i3.TraceTemplateProperty( doc="Wide trace template used for the contacts") tt_port2 = i3.TraceTemplateProperty( doc="Wide trace template used for the contacts") #width_vec = i3.ListProperty(default=[1]) n = i3.PositiveNumberProperty(default=1, doc="") width = i3.PositiveNumberProperty(default=1, doc="") lengths = i3.PositiveNumberProperty(default=1, doc="") def _default_lengths(self): for counter, cell in enumerate(self.s_length_vec): numero = counter + 1 return numero #template for Autorute def _default_trace_template(self): return self.waveguide_template def _default_tt(self): return self.waveguide_template def _default_tt_port(self): tt_port = WireWaveguideTemplate() tt_port_layout = tt_port.Layout(core_width=10.0, cladding_width=10.0) return tt_port def _default_tt_port2(self): tt_port = WireWaveguideTemplate() tt_port_layout = tt_port.Layout(core_width=10.0, cladding_width=10.0) return tt_port def _default_Spiral_list(self): Spiral_list = [] # empty list print ' I am in _Spiral_list' for l, length in enumerate(self.s_length_vec): loops = 1 print length cell = FixedLengthSpiralRounded( trace_template=self.waveguide_template, #total_length=length-self.chip_length, total_length=length, n_o_loops=loops, name=self.name + '_Spiral_' + str(l)) cell.Layout( incoupling_length=0, bend_radius=self.R, spacing=self.spacing, stub_direction="H", growth_direction="H", ) #.visualize(annotate=True) print 'The legth of the spiral is: ', cell.total_length print 'Cell: ', cell.name Spiral_list.append(cell) return Spiral_list def _default_couplingWG(self): rect = i3.Waveguide(trace_template=self.tt_port) layout_rect = rect.Layout(shape=[(0.0, 0.0), (self.couplingWG_l, 0.0)]) return rect def _default_Port(self): Port = AutoTransitionPorts(contents=self.couplingWG, port_labels=["in"], trace_template=self.waveguide_template) layout_Port = Port.Layout( transition_length=self.tlport) #.visualize(annotate=True) return Port def _default_Port2(self): Port = AutoTransitionPorts(contents=self.couplingWG, port_labels=["in"], trace_template=self.waveguide_template) layout_Port = Port.Layout( transition_length=self.tlport) #.visualize(annotate=True) return Port def _default_child_cells(self): child_cells = { } # First we define the property "child_cells" as an empty dictionary for counter, spiral in enumerate( self.Spiral_list ): # the iteration starts in the first element of the list and follows element by element to the last element. child_cells['Spiral{}'.format(counter)] = spiral print spiral print 'name of spiral:', spiral.name child_cells['InPort' + str(counter)] = self.Port child_cells['OutPort' + str(counter)] = self.Port print 'child_cells:', child_cells return child_cells def _default_links(self): links = [] for counter, spiral in enumerate(self.Spiral_list): print counter in_port = "Spiral{}:in".format(counter) out_port = "InPort{}:in".format(counter) links.append((in_port, out_port)) in_port = "Spiral{}:out".format(counter) out_port = "OutPort{}:in".format(counter) links.append((in_port, out_port)) return links class Layout(PlaceAndAutoRoute.Layout): #tipo=1 def _default_bend_radius(self): return self.R def _default_child_transformations(self): d = {} for counter, child in enumerate(self.Spiral_list): ip = child.ports["in"].position #print self.child_cells['InPort' + str(counter)].ports["out"].position #print self.child_cells['OutPort' + str(counter)].ports.position print '----------------' print 'spiral length:', child.total_length print 'counter: ', counter #print ip op = child.ports["out"].position #print op print 'The lateral size of the spiral is', op[0] - ip[0] print 'The type of mask is: ', self.tipo print 'The number of widths is: ', self.n print 'The number of lengths is: ', self.lengths print 'The width number is: ', self.width print '----------------' iz = child.inner_size sx = iz[1] + 200 #sx=1200 if self.tipo == 1: d['Spiral' + str(counter)] = i3.Translation( translation=(-(op[0] - ip[0]) / 2, self.n * counter * sx)) d['InPort' + str(counter)] = i3.HMirror() + i3.Translation( translation=(-self.chip_length / 2.0 - self.couplingWG_l, self.n * counter * sx)) d['OutPort' + str(counter)] = i3.Translation( translation=(self.chip_length / 2.0 + self.couplingWG_l, self.n * counter * sx)) if self.tipo == 2: d['Spiral' + str(counter)] = i3.Translation( translation=(-(op[0] - ip[0]) / 2, -(self.n + 0.5) * counter * sx)) #d['InPort' + str(counter)] = i3.HMirror()+ i3.Translation(translation=(-self.chip_length*(3/4)-self.couplingWG_l, -(self.n+0.5)*counter*sx)) #d['OutPort' + str(counter)] = i3.Rotation(rotation=90) + i3.Translation(translation=((op[0]-ip[0])/2+2*self.R+(((self.n+0.5)*counter+self.width)*sx/4), self.chip_length*(3/4)+(self.width+counter-(((counter+1)-1.0)%self.lengths))*sx)) d['InPort' + str(counter)] = i3.HMirror() + i3.Translation( translation=(-self.chip_length * (1 / 2) - 2000, -(self.n + 0.5) * counter * sx)) d['OutPort' + str(counter)] = i3.Rotation( rotation=90) + i3.Translation(translation=( (op[0] - ip[0]) / 2 + 2 * self.R + (((self.n + 0.5) * counter + self.width) * sx / 4), 3000 + self.chip_length * (3 / 4) + (self.width + counter - (((counter + 1) - 1.0) % self.lengths)) * sx)) #For awg's #if self.tipo==2: #d['Spiral' + str(counter)] = i3.Translation(translation=(-(op[0]-ip[0])/2, -(self.n+0.5)*counter*sx)) #d['InPort' + str(counter)] = i3.HMirror()+ i3.Translation(translation=(-self.chip_length*(3/4.0), -(self.n+0.5)*counter*sx)) #d['OutPort' + str(counter)] = i3.Rotation(rotation=90) + i3.Translation(translation=((op[0]-ip[0])/2+2*self.R #+(((self.n+0.5)*counter+self.width)*sx/100.0) #, self.chip_length*(2/4.0)+ #(self.width+counter-(((counter+1)-1.0)%self.lengths))*sx)) return d # Fabio's addition def _generate_elements(self, elems): # We calculate the lengths of the 2 spirals in this pcell. # Note that we assume that there are exactly 2 spirals in this list. #assert len(self.Spiral_list) == 2 lengths = get_lengths(self) iz = self.Spiral_list[0].inner_size sx = iz[1] + 200 for counter, (child, length) in enumerate(zip(self.Spiral_list, lengths)): ip = child.ports["in"].position op = child.ports["out"].position width = child.ports["in"].trace_template.core_width #print 'child.ports["in"].trace_template.core_width: ', child.ports["in"].trace_template.core_width #i3.TECH.PPLAYER.NONE.LOGOTXT when using isipp50g if self.tipo == 2: elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text='Width={}_Length={}_R={}'.format( width, length, self.R), coordinate=((op[0] - ip[0]) / 2 - 1000.0, (self.n + 0.5) * counter * sx - 50.0), alignment=(i3.TEXT_ALIGN_LEFT, i3.TEXT_ALIGN_LEFT), font=2, height=20.0) if self.tipo == 1: elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text='Width={}_Length={}_R={}'.format( width, length, self.R), coordinate=(-(op[0] - ip[0]) / 2 - 1000.0, -(self.n + 0.5) * counter * sx - 50.0), alignment=(i3.TEXT_ALIGN_LEFT, i3.TEXT_ALIGN_LEFT), font=2, height=20.0) return elems
class Layout(i3.LayoutView): # specified parameters used for layout, lengths of various waveguides # using some default values if standard ring shape is used bend_radius_ring = i3.PositiveNumberProperty(default=10., doc="bend radius of ring") ring_x_straight = i3.PositiveNumberProperty( default=15., doc="straight between bends in x ring") ring_y_straight = i3.PositiveNumberProperty( default=25., doc="straight between bends in y ring") external_straights = i3.PositiveNumberProperty( default=10., doc="extra straight for outside structure") external_gap = i3.PositiveNumberProperty( default=1., doc="gap between outside waveguides and resonator") # external_radius = i3.PositiveNumberProperty(default=bend_radius_ring, doc="radius of outside coupler") rounding_algorithm = i3.DefinitionProperty( default=SplineRoundingAlgorithm(), doc="secondary rounding algorithm") # extra layouting for the CROW num_rings = i3.IntProperty(default=3, doc="number of rings") ring_gap = i3.PositiveNumberProperty(default=0.5, doc="gap between internal rings") use_gap_list = i3.BoolProperty(default=False, doc="use non default bending algorithm") ring_gap_list = i3.ListProperty( default=[], doc="list of gaps for manual swapping, default empty!") # define the layout of the internal coupler which we SRef below def _default_resonator(self): res_layout = self.cell.resonator.get_default_view( i3.LayoutView) # Retrieve layout view following example # make the shape of the layout from the previous values. Assume (0, 0) is bottom middle!) # will do each corner for clarity # bottom_left = (-self.bend_radius_ring - self.ring_x_straight/2., 0.) # top_left = (-self.bend_radius_ring - self.ring_x_straight/2., # self.bend_radius_ring*2. + self.ring_y_straight) # top_right = (self.bend_radius_ring + self.ring_x_straight/2., # self.bend_radius_ring*2. + self.ring_y_straight) # bottom_right = (self.bend_radius_ring + self.ring_x_straight/2., 0.) # ring_shape = [bottom_left, top_left, top_right, bottom_right, bottom_left] # print ring_shape # tried to use generic round ring, but failed :P. Using ring rect instead # set the layout of the resonator. Stuck a bool for non default rounding algorithm res_layout.set(bend_radius=self.bend_radius_ring, straights=(self.ring_x_straight, self.ring_y_straight), rounding_algorithm=self.rounding_algorithm) return res_layout def _dummy_resonator(self): dummy_res = i3.SRef(name="just_a_dummy", reference=self.resonator) return dummy_res # make a function for determining the distance between core size and def _resonator_size_core_to_core(self): # calls the get components function and then does math on the pulled in layout resonator = self._dummy_resonator() wg_ring_template = self.wg_ring_template # grabbing the position of the resonator to layout the rest of the coupler properly resonator_west_side = resonator.size_info().west resonator_east_side = resonator.size_info().east resonator_core_width = wg_ring_template.core_width resonator_clad_width = wg_ring_template.cladding_width resonator_x_dim = (resonator_east_side - resonator_west_side) - resonator_clad_width return resonator_x_dim # setting the output shape of the access waveguides using a shape defined by ports from MMI (hopefully..) def _default_wgs(self): # bring in parts from rest of PCell Layout, used dummy resonator to grab positions resonator = self._dummy_resonator() wg_in_cell, wg_pass_cell = self.cell.wgs wg_template = self.wg_coupler_template wg_ring_template = self.wg_ring_template # using the ring radius for the external radius external_rad = self.bend_radius_ring external_str = self.external_straights # grabbing the position of the resonator to layout the rest of the coupler properly resonator_west_side = resonator.size_info().west resonator_south_side = resonator.size_info().south resonator_core_width = wg_ring_template.core_width resonator_clad_width = wg_ring_template.cladding_width coupler_core_width = wg_template.core_width # calculate the x position for center of input coupling waveguide when coupling, and make shape x_coup_spot = resonator_west_side + resonator_clad_width/2. - resonator_core_width/2. - self.external_gap \ - coupler_core_width/2. # get bottom using the south and cladding information again bottom_left = (x_coup_spot - external_str - external_rad, resonator_south_side + resonator_clad_width / 2.) bottom_right = (x_coup_spot, resonator_south_side + resonator_clad_width / 2.) top_right = (x_coup_spot, bottom_right[1] + 2. * external_rad + self.ring_y_straight) top_left = (bottom_left[0], top_right[1]) wg_shape = [bottom_left, bottom_right, top_right, top_left] # now make the instance using this shape info wg_in_layout = wg_in_cell.get_default_view(i3.LayoutView) wg_in_layout.set(trace_template=wg_template, shape=wg_shape, bend_radius=external_rad, rounding_algorithm=self.rounding_algorithm) # other waveguide for reference, can put in shape or mirror later wg_pass_layout = wg_pass_cell.get_default_view(i3.LayoutView) # wg_in_layout.set() return wg_in_layout, wg_pass_layout # wg_ring_layout # A few functions for grabbing waveguide parameters to determine lengths for FSR checking # def wg_lengths(self): # # grab the lengths of internal waveguides to use for calculations later # wg_in_layout, wg_pass_layout, wg_ring_layout = self.wgs # # straights_and_bends = wg_ring_layout.trace_length() # return straights_and_bends # now we take the resonator and perform multiple translations for the CROW def _get_components(self): res_x_dim = self._resonator_size_core_to_core() ring_gap = self.ring_gap ring_core_width = self.wg_ring_template.core_width ring_gap_list = self.ring_gap_list shifting_list = [0.] + ring_gap_list all_components = [] # and now crank an SRef for each Ring in a loop for ring in range(self.num_rings): # will translate the original ring over to the correct position, and iterate for number of rings # use an if statement for external gap list or not. Need an error if self.use_gap_list is False: this_transform = i3.Translation( ((res_x_dim + ring_gap + ring_core_width) * ring, 0.)) this_resonator = i3.SRef(name="R_" + str(ring), reference=self.resonator, transformation=this_transform) all_components.append(this_resonator) else: # sum previous elements of the shifting list for correct relative translation total_shift = sum(shifting_list[:(ring + 1)]) this_transform = i3.Translation( ((res_x_dim + ring_core_width) * ring + total_shift, 0.)) this_resonator = i3.SRef(name="R_" + str(ring), reference=self.resonator, transformation=this_transform) all_components.append(this_resonator) return all_components def _generate_instances(self, insts): # includes the get components and the waveguides the_rings = self._get_components() insts += the_rings wg_in_layout, wg_pass_layout = self.wgs # wg_pass_layout, wg_ring_layout insts += i3.SRef(reference=wg_in_layout, name="wg_in") # ok so now I grab the last ring from the rings and use it to determine its position last_ring = the_rings[-1] east_side_ring = last_ring.size_info().east # and I get the waveguide properties for ring and coupler, to give correct outside gap ring_core_width = self.wg_ring_template.core_width ring_clad_width = self.wg_ring_template.cladding_width bus_wg_core_width = self.wg_coupler_template.core_width bus_wg_clad_width = self.wg_coupler_template.cladding_width final_x_spot = (east_side_ring - ring_clad_width/2.) + ring_core_width/2. \ + self.external_gap + bus_wg_core_width/2. # rather than making a new waveguide we can mirror the previous structure into the final position # thus we need to determine the difference in the core position of the original structure # with the *negative* position of the final x position, and then the mirror will flip it around bus_core_pos = wg_in_layout.size_info( ).east - bus_wg_clad_width / 2. # now we translate the original structure to the desired negative position, and horizontally mirror around 0 output_transformation = i3.HMirror() + i3.Translation( (-1. * (-final_x_spot - bus_core_pos), 0.)) # finally we perform the SRef on the previous layout and transform it with a new name insts += i3.SRef(reference=wg_in_layout, name="wg_out", transformation=output_transformation) return insts def _generate_ports(self, prts): # try to reuse the output waveguides following the example and change the names, looks good instances = self.instances prts += instances["wg_in"].ports["in"].modified_copy(name="in1") prts += instances["wg_in"].ports["out"].modified_copy(name="in2") prts += instances["wg_out"].ports["in"].modified_copy(name="out1") prts += instances["wg_out"].ports["out"].modified_copy(name="out2") return prts
class Layout(i3.LayoutView): # Properties ------- # taper to draw # taper_prop_dict = i3.DictProperty( default = {}, doc = 'Dictionary of taper cell properties.' + \ # 'Properties = "length", "width1", "width2", "width_etch" ' ) # connecting length between tapers # connect_length = i3.NumberProperty( default=0.0, doc='distance between tapers' ) # connecting length between taper PAIRS # pair_connect_length = i3.NumberProperty( default=0.0, doc='distance between taper pairs' ) # number of taper pairs n_pairs = i3.IntProperty(default=1, doc='number of taper pairs') # Methods ------- def _generate_instances(self, insts): # Generates taper pairs tp_name_list = [] # temporary? pick taper properties taper_prop_dict = { 'length': 79.0, 'width1': 0.50, 'width2': 6.50, 'width_etch': 2.0 } # generate a huge taper row tp_rows_layout = TaperPairRow().Layout( taper_prop_dict=taper_prop_dict, connect_length=0.0, pair_connect_length=10.0, n_pairs=self.n_pairs) # load the aim gds just to get its positions and stuff # main chip GDS fname = os.path.dirname( os.path.realpath(__file__)) + '/gds/ap_suny_v20a_chipframe.gds' main_chip_gds_cell = i3.GDSCell(filename=fname) # grab layout size info main_chip_gds_lay = main_chip_gds_cell.Layout() main_chip_gds_lay_size_info = main_chip_gds_lay.size_info() # grab relevant positions chip_edge_east = main_chip_gds_lay_size_info.east chip_edge_west = main_chip_gds_lay_size_info.west # make edge coupler and add to layout # edge coupler edge_coupler_gds_lay = EdgeCoupler(name=self.name + 'edge_coupler_si').Layout() # add and route input/west edgecoupler # position edge coupler on west side of chip chip_port_west = i3.OpticalPort(position=(chip_edge_west, 0.0), angle_deg=0.0) edge_coupler_west_port = edge_coupler_gds_lay.ports['out'] t = i3.vector_match_transform(edge_coupler_west_port, chip_port_west) edge_coupler_west_name = self.name + '_EDGE_COUPLER_WEST' west_edge_coupler = i3.SRef(name=edge_coupler_west_name, reference=edge_coupler_gds_lay, transformation=t, flatten=False) # add a small linear taper to go from 0.4 to 0.5um wg lin_taper_lay = LinearTaper().get_default_view(i3.LayoutView) lin_taper_lay.set(wg_width_in=0.4, wg_width_out=0.5, length=10.0) t = i3.vector_match_transform(lin_taper_lay.ports['in'], west_edge_coupler.ports['in']) lin_taper_lay_name = self.name + '_EDGETAPER_WEST' insts += i3.SRef(name=lin_taper_lay_name, reference=lin_taper_lay, transformation=t, flatten=True) # add taper rows taper_row_name = self.name + '_TAPERSSSSSSSS' t = i3.vector_match_transform( tp_rows_layout.ports['left'], insts[lin_taper_lay_name].ports['out']) insts += i3.SRef(name=taper_row_name, reference=tp_rows_layout, transformation=t, flatten=True) # add east coupler chip_port_east = i3.OpticalPort(position=(chip_edge_east, 0.0), angle_deg=180.0) edge_coupler_east_port = edge_coupler_gds_lay.ports['out'] t = i3.vector_match_transform(edge_coupler_east_port, chip_port_east, mirrored=True) edge_coupler_east_name = self.name + '_EDGE_COUPLER_EAST' east_edge_coupler = i3.SRef(name=edge_coupler_east_name, reference=edge_coupler_gds_lay, transformation=t, flatten=False) # add a small linear taper to go from 0.4 to 0.5um wg lin_taper_lay = LinearTaper().get_default_view(i3.LayoutView) lin_taper_lay.set(wg_width_in=0.4, wg_width_out=0.5, length=10.0) t = i3.vector_match_transform(lin_taper_lay.ports['in'], east_edge_coupler.ports['in'], mirrored=True) lin_taper_lay_name = self.name + '_EDGETAPER_EAST' insts += i3.SRef(name=lin_taper_lay_name, reference=lin_taper_lay, transformation=t, flatten=True) # route the east coupler to the east edge of the taper pairs route_wg_row_taper = i3.Shape([ insts[taper_row_name].ports['right'].position, insts[lin_taper_lay_name].ports['out'].position ]) wg_name = self.name + '_WG' wg_lay = i3.Waveguide(trace_template=StripWgTemplate(), name=wg_name).get_default_view(i3.LayoutView) wg_lay.set(shape=route_wg_row_taper) insts += i3.SRef(name=wg_name, reference=wg_lay, flatten=True) return insts # end _generate_instances() def _generate_ports(self, ports): # add ports 'left' and 'right' # left port ports += i3.OpticalPort( name='left', position=self.instances[ self.name + '_EDGETAPER_WEST'].ports['in'].position, angle=180.0) # right port ports += i3.OpticalPort( name='right', position=self.instances[ self.name + '_EDGETAPER_EAST'].ports['out'].position, angle=0.0) return ports
class Layout(i3.LayoutView): # Properties ------- # taper to draw taper_prop_dict = i3.DictProperty( default = {}, doc = 'Dictionary of taper cell properties.' + \ 'Properties = "length", "width1", "width2", "width_etch" ' ) # connecting length between tapers connect_length = i3.NumberProperty(default=0.0, doc='distance between tapers') # connecting length between taper PAIRS pair_connect_length = i3.NumberProperty( default=0.0, doc='distance between taper pairs') # number of taper pairs n_pairs = i3.IntProperty(default=1, doc='number of taper pairs') # Methods ------- def _generate_instances(self, insts): # Generates taper pairs tp_name_list = [] for ii in range(self.n_pairs): # for each pair # draw taper pair layout tp_lay = TaperPair(name=self.name + '_tp' + str(ii)).get_default_view(i3.LayoutView) tp_lay.set(taper_prop_dict=self.taper_prop_dict, connect_length=self.connect_length) # set name tp_name = 'tp' + str(ii) tp_name_list.append(tp_name) # set transformation if ii > 0: # set transform t = i3.vector_match_transform( tp_lay.ports['left'], insts[ tp_name_list[ii - 1] ].ports['right']) \ + i3.Translation( ( self.pair_connect_length, 0.0 ) ) # print t # draw next taper pair insts += i3.SRef(name=tp_name, reference=tp_lay, transformation=t) # route wg route_wg = i3.RouteManhattan( input_port=insts[tp_name_list[ii - 1]].ports['right'], output_port=insts[tp_name_list[ii]].ports['left']) # # make my OWN custom waveguide trace template # wg_trace = f_MyIMECWaveguideTemplate( core_width = self.taper_prop_dict['width1'], # cladding_width = self.taper_prop_dict['width1'] + 2.0*self.taper_prop_dict['width_etch'] ) # make waveguide wg = i3.Waveguide(trace_template=StripWgTemplate(), name=self.name + '_WG' + str(ii)) # add wg insts += i3.SRef(name=self.name + 'connect_wg' + str(ii), reference=wg.Layout(shape=route_wg)) else: # draw first taper pair insts += i3.SRef(name=tp_name, reference=tp_lay) # DEBUG # print insts # end if else return insts # end _generate_instances() def _generate_ports(self, ports): # add ports 'left' and 'right' # left port ports += i3.OpticalPort( name='left', position=self.instances['tp0'].ports['left'].position, angle=180.0) # right port ports += i3.OpticalPort( name='right', position=self.instances['tp' + str(self.n_pairs - 1)].ports['right'].position, angle=0.0) return ports
class Spirals(PlaceAndAutoRoute): _name_prefix = 'LSpiral' tipo = i3.PositiveNumberProperty(doc="Number loops", default=1) waveguide_template = i3.DefinitionProperty(doc="Trace template used") R = i3.PositiveNumberProperty(default=500, doc="Radius of curvature") spacing = i3.PositiveNumberProperty(default=100, doc="Radius of curvature") n_loops = i3.IntProperty(doc="Number loops", default=2) n_loops_vec = i3.ListProperty(default=[4, 8]) s_length_vec = i3.ListProperty(default=[0.0]) #Spiral_list = i3.ChildCellListProperty(doc="List containing the 90 degree angle child cells") #chip_length = i3.PositiveNumberProperty(default=12000.0, doc="") chip_length = i3.PositiveNumberProperty(default=13000.0, doc="") Port = i3.ChildCellProperty(doc="Used for ports") tlport = i3.PositiveNumberProperty(default=2000.0, doc="Transition legth to ports") couplingWG = i3.ChildCellProperty(doc="", locked=True) couplingWG_l = i3.PositiveNumberProperty(default=5000.0, doc="Length of the coupling WG ") tt_port = i3.TraceTemplateProperty( doc="Wide trace template used for the contacts") #width_vec = i3.ListProperty(default=[1]) n = i3.PositiveNumberProperty(default=1, doc="") width = i3.PositiveNumberProperty(default=1, doc="") lengths = i3.PositiveNumberProperty(default=1, doc="") def _default_lengths(self): for counter, cell in enumerate(self.s_length_vec): numero = counter + 1 return numero #template for Autorute def _default_trace_template(self): return self.waveguide_template def _default_tt(self): return self.waveguide_template def _default_tt_port(self): tt_port = WireWaveguideTemplate() tt_port_layout = tt_port.Layout(core_width=15.0, cladding_width=15.0 + 2 * 8.0) return tt_port def _default_couplingWG(self): rect = i3.Waveguide(trace_template=self.tt_port) layout_rect = rect.Layout(shape=[(0.0, 0.0), (self.couplingWG_l, 0.0)]) return rect def _default_Port(self): Port = AutoTransitionPorts(contents=self.couplingWG, port_labels=["in"], trace_template=self.waveguide_template) layout_Port = Port.Layout( transition_length=self.tlport) #.visualize(annotate=True) return Port def _default_child_cells(self): child_cells = { } # First we define the property "child_cells" as an empty dictionary for counter, length in enumerate( self.s_length_vec ): # the iteration starts in the first element of the list and follows element by element to the last element. #child_cells['Spiral{}'.format(counter)] = spiral #print spiral #print 'name of spiral:', spiral.name child_cells['InPort' + str(counter)] = self.Port child_cells['OutPort' + str(counter)] = self.Port print 'child_cells:', child_cells return child_cells def _default_links(self): links = [] for counter, spiral in enumerate(self.s_length_vec): print counter #in_port = "Spiral{}:in".format(counter) in_port = "InPort{}:in".format(counter) #links.append((in_port, out_port)) #in_port = "Spiral{}:out".format(counter) out_port = "OutPort{}:in".format(counter) links.append((in_port, out_port)) return links class Layout(PlaceAndAutoRoute.Layout): #tipo=1 def _default_bend_radius(self): return self.R def _default_child_transformations(self): d = {} for counter, child in enumerate(self.s_length_vec): #ip= child.ports["in"].position #print self.child_cells['InPort' + str(counter)].ports["out"].position #print self.child_cells['OutPort' + str(counter)].ports.position print '----------------' #print 'spiral length:', child.total_length print 'counter: ', counter print 'self.n = ', self.n print 'self.width: ', self.width #print 'sx: ', sx if self.tipo == 1: sx = 70 a = 0.5 print 2 * (22362 * 0.5 - self.couplingWG_l) print 'tipo = ', self.tipo #d['Spiral' + str(counter)] = i3.Translation(translation=(-(op[0]-ip[0])/2, self.n*counter*sx)) #d['InPort' + str(counter)] = i3.HMirror()+ i3.Translation(translation=(-self.chip_length*0.5, (self.n+a)*counter*sx)) #d['OutPort' + str(counter)] = i3.Translation(translation=(self.chip_length*0.5, (self.n+a)*counter*sx)) d['InPort' + str(counter)] = i3.HMirror() + i3.Translation( translation=(-22362 * 0.5 + self.couplingWG_l, (self.n + a) * counter * sx)) d['OutPort' + str(counter)] = i3.Translation( translation=(22362 * 0.5 - self.couplingWG_l, (self.n + a) * counter * sx)) #if self.tipo==2: #d['Spiral' + str(counter)] = i3.Translation(translation=(-(op[0]-ip[0])/2, -(self.n+0.5)*counter*sx)) #d['InPort' + str(counter)] = i3.HMirror()+ i3.Translation(translation=(-self.chip_length*(3/4)-self.couplingWG_l, -(self.n+0.5)*counter*sx)) #d['OutPort' + str(counter)] = i3.Rotation(rotation=90) + i3.Translation(translation=((op[0]-ip[0])/2+2*self.R+(((self.n+0.5)*counter+self.width)*sx/4), self.chip_length*(3/4)+(self.width+counter-(((counter+1)-1.0)%self.lengths))*sx)) if self.tipo == 2: sx = 100 #d['Spiral' + str(counter)] = i3.Translation(translation=(-(op[0]-ip[0])/2, -(self.n+1)*counter*sx)) a = 7.0 print 'increment of length between waveguides of same width: ', ( self.n + a) * 1 * sx + ((self.n + a) * 1 + 0) * sx print 'increment of length between waveguides of same length group: ', ( self.n + a) * 0 * sx + ( (self.n + a) * 0 + self.width) * sx d['InPort' + str(counter)] = i3.HMirror() + i3.Translation( translation=(0.0 - self.chip_length * 0.5, -(self.n + a) * counter * sx)) d['OutPort' + str(counter)] = i3.Rotation( rotation=90) + i3.Translation(translation=( (((self.n + a) * counter + self.width) * sx), self.chip_length * 0.5 + (self.width + counter - (((counter + 1) - 1.0) % self.lengths)) * sx)) return d # Fabio's addition def _generate_elements(self, elems): # We calculate the lengths of the 2 spirals in this pcell. # Note that we assume that there are exactly 2 spirals in this list. #assert len(self.Spiral_list) == 2 lengths = get_lengths(self)[0] print lengths Link = get_lengths(self)[1] print Link if self.tipo == 1: sx = 70 for counter, (child, length) in enumerate( zip(self.s_length_vec, lengths)): #ip= child.ports["in"].position #op= child.ports["out"].position width = Link.trace_template.core_width #print 'child.ports["in"].trace_template.core_width: ', child.ports["in"].trace_template.core_width a = 0.5 #i3.TECH.PPLAYER.NONE.LOGOTXT when using isipp50g elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text='Width={}'.format(width, ), coordinate=(-self.chip_length * 0.5 + 2 * self.tlport, (self.n + a) * counter * sx - 15.0), alignment=(i3.TEXT_ALIGN_LEFT, i3.TEXT_ALIGN_LEFT), font=2, height=20.0) elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text='Width={}'.format(width, ), coordinate=(0.0, (self.n + a) * counter * sx - 15.0), alignment=(i3.TEXT_ALIGN_LEFT, i3.TEXT_ALIGN_LEFT), font=2, height=20.0) elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text='Width={}'.format(width, ), coordinate=(self.chip_length * 0.5 - 2 * self.tlport, (self.n + a) * counter * sx - 15.0), alignment=(i3.TEXT_ALIGN_LEFT, i3.TEXT_ALIGN_LEFT), font=2, height=20.0) if self.tipo == 2: sx = 100 for counter, (child, length) in enumerate( zip(self.s_length_vec, lengths)): #ip= child.ports["in"].position #op= child.ports["out"].position width = Link.trace_template.core_width #print 'child.ports["in"].trace_template.core_width: ', child.ports["in"].trace_template.core_width a = 7.0 #i3.TECH.PPLAYER.NONE.LOGOTXT when using isipp50g elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text='Width={}_Length={}_R={}'.format( width, length, self.R), coordinate=(-1500, -(self.n + a) * counter * sx - 55.0), alignment=(i3.TEXT_ALIGN_LEFT, i3.TEXT_ALIGN_LEFT), font=2, height=20.0) return elems
class Layout(i3.LayoutView): # Properties ------- # number of bend pairs n_pairs = i3.IntProperty(default=1, doc='number of bend pairs') # Methods ------- def _generate_instances(self, insts): # Generates bend pairs tp_name_list = [] # arc path fname = '../nathan/bend_data/txbend.txt' path_width = np.loadtxt(fname, np.float_) arc_path = path_width[:, :2] # make a bend woo bend_wg_lay = i3.Waveguide( name=self.name + "_Bend", trace_template=StripWgTemplate()).get_default_view( i3.LayoutView) bend_wg_lay.set(shape=arc_path) # add to insts insts += i3.SRef(name=self.name + '_BEND', reference=bend_wg_lay) # list of bend names bend_name_A_list = [] bend_name_B_list = [] # draw a bunch of bends for ii in range(self.n_pairs): # make bend A if ii == 0: # place first bend pair # make a bend woo bend_name_A = self.name + '_BEND_A' + str(ii) bend_wg_lay_A = i3.Waveguide( trace_template=StripWgTemplate()).get_default_view( i3.LayoutView) bend_wg_lay_A.set(shape=arc_path) # add to insts insts += i3.SRef(name=bend_name_A, reference=bend_wg_lay_A, flatten=True) else: # make a bend woo bend_name_A = self.name + '_BEND_A' + str(ii) bend_wg_lay_A = i3.Waveguide( trace_template=StripWgTemplate()).get_default_view( i3.LayoutView) bend_wg_lay_A.set(shape=arc_path) # add to insts t = i3.vector_match_transform( bend_wg_lay_A.ports['in'], insts[bend_name_B_list[ii - 1]].ports['out'], mirrored=False) insts += i3.SRef(name=bend_name_A, reference=bend_wg_lay_A, transformation=t, flatten=True) # make another bend woo bend_name_B = self.name + '_BEND_B' + str(ii) bend_wg_lay_B = i3.Waveguide( trace_template=StripWgTemplate()).get_default_view( i3.LayoutView) bend_wg_lay_B.set(shape=arc_path) # add to insts t = i3.vector_match_transform(bend_wg_lay_B.ports['in'], insts[bend_name_A].ports['out'], mirrored=True) insts += i3.SRef(name=bend_name_B, reference=bend_wg_lay_B, transformation=t, flatten=True) # end if else # append bend names bend_name_A_list.append(bend_name_A) bend_name_B_list.append(bend_name_B) # end for loop return insts # end _generate_instances() def _generate_ports(self, ports): # add ports 'left' and 'right' # left port ports += i3.OpticalPort( name='in', position=self.instances[self.name + '_BEND_A0'].ports['in'].position, angle=0.0) # right port ports += i3.OpticalPort( name='out', position=self.instances[self.name + '_BEND_B' + str(self.n_pairs - 1)].ports['out'].position, angle=180.0) return ports
class SpiralWgLossEbeam(PlaceAndAutoRoute): ## generate segmented FBMS line spiral = i3.ChildCellProperty() taper = i3.ChildCellProperty() wg = i3.ChildCellProperty() wg_right = i3.ChildCellProperty() expanded_wg = i3.ChildCellProperty() trace_template = i3.WaveguideTemplateProperty() expanded_wg_template = i3.WaveguideTemplateProperty() wg_width = i3.PositiveNumberProperty(default=2.0) trench_width = i3.PositiveNumberProperty(default=3.0) _wg_width_indesign = i3.PositiveNumberProperty() expanded_wg_width = i3.PositiveNumberProperty(default=3.0) _expanded_wg_width_indesign = i3.PositiveNumberProperty() spiral_length = i3.PositiveNumberProperty(default=10000.0) n_o_loops = i3.IntProperty(default=2) def _default__wg_width_indesign(self): return self.wg_width def _default__expanded_wg_width_indesign(self): return self.expanded_wg_width def _default_trace_template(self): wstart1 = PathTraceWindow(layer=i3.TECH.PPLAYER.WG.CORE, start_offset=-self.wg_width * 0.5, end_offset=self.wg_width * 0.5) wstart2 = PathTraceWindow( layer=i3.TECH.PPLAYER.WG.CLADDING, start_offset=-(self.wg_width + self.trench_width) * 0.5, end_offset=-(self.wg_width + self.trench_width) * 0.5, line_width=0) wstart3 = PathTraceWindow( layer=i3.TECH.PPLAYER.WG.CLADDING, start_offset=(self.wg_width + self.trench_width) * 0.5, end_offset=(self.wg_width + self.trench_width) * 0.5, line_width=0) wg_t = i3.WindowWaveguideTemplate() wg_t.Layout(windows=[wstart1, wstart2, wstart3]) return wg_t def _default_expanded_wg_template(self): wstart1 = PathTraceWindow(layer=i3.TECH.PPLAYER.WG.CORE, start_offset=-self.expanded_wg_width * 0.5, end_offset=self.expanded_wg_width * 0.5) wstart2 = PathTraceWindow( layer=i3.TECH.PPLAYER.WG.CLADDING, start_offset=-(self.expanded_wg_width + self.trench_width) * 0.5, end_offset=-(self.expanded_wg_width + self.trench_width) * 0.5, line_width=0) wstart3 = PathTraceWindow( layer=i3.TECH.PPLAYER.WG.CLADDING, start_offset=(self.expanded_wg_width + self.trench_width) * 0.5, end_offset=(self.expanded_wg_width + self.trench_width) * 0.5, line_width=0) expanded_wg_template = i3.WindowWaveguideTemplate() expanded_wg_template.Layout(windows=[wstart1, wstart2, wstart3]) return expanded_wg_template def _default_wg(self): wg = i3.RoundedWaveguide(trace_template=self.trace_template) return wg def _default_wg_right(self): wg_right = i3.RoundedWaveguide(trace_template=self.trace_template) return wg_right def _default_spiral(self): spiral = FixedLengthSpiralRounded(total_length=self.spiral_length, n_o_loops=self.n_o_loops, trace_template=self.trace_template) return spiral def _default_expanded_wg(self): expanded_wg = i3.RoundedWaveguide( trace_template=self.expanded_wg_template) return expanded_wg def _default_taper(self): taper = LinearWindowWaveguideTransition( start_trace_template=self.trace_template, end_trace_template=self.expanded_wg_template) return taper def _default_child_cells(self): child_cells = dict() child_cells["spiral"] = self.spiral child_cells["taper_left"] = self.taper child_cells["taper_right"] = self.taper child_cells["wg_left"] = self.wg child_cells["wg_right"] = self.wg_right child_cells["expanded_wg_left"] = self.expanded_wg child_cells["expanded_wg_right"] = self.expanded_wg return child_cells def _default_external_port_names(self): ports = dict() ports["expanded_wg_right:out"] = "in" ports["expanded_wg_left:out"] = "out" return ports class Layout(PlaceAndAutoRoute.Layout): spiral_width = i3.PositiveNumberProperty( doc="total width of spiral structure", default=6000.0) right_wg_length = i3.PositiveNumberProperty( doc="length of narrow wg on the right side of spiral", default=500.0) expanded_wg_length = i3.PositiveNumberProperty(default=5000.0) taper_length = i3.PositiveNumberProperty(default=300.0) bend_radius = i3.PositiveNumberProperty(default=100.0) gap = i3.PositiveNumberProperty(doc="distance between two trenches", default=3.0) _spacing_indesign = i3.PositiveNumberProperty() def _default__spacing_indesign(self): return self.gap + self.trench_width * 2 + self.wg_width def _default_spiral(self): spiral_lo = self.cell.spiral.get_default_view(i3.LayoutView) spiral_lo.set(incoupling_length=0, bend_radius=self.bend_radius, spacing=self._spacing_indesign, growth_direction="H", stub_direction="H") return spiral_lo def _default_wg(self): wg_lo = self.cell.wg.get_default_view(i3.LayoutView) wg_lo.set(shape=[( 0.0, 0.0), (self.spiral_width - self._spiral_in_out_distance(), 0)]) return wg_lo def _spiral_in_out_distance(self): return self.spiral.ports["out"].position.x - self.spiral.ports[ "in"].position.x def _default_wg_right(self): wg_right_lo = self.cell.wg_right.get_default_view(i3.LayoutView) wg_right_lo.set(shape=[(0.0, 0.0), (self.right_wg_length, 0)]) return wg_right_lo def _default_taper(self): taper_lo = self.cell.taper.get_default_view(i3.LayoutView) taper_lo.set(start_position=(0.0, 0.0), end_position=(self.taper_length, 0.0)) return taper_lo def _default_expanded_wg(self): expanded_wg_lo = self.cell.expanded_wg.get_default_view( i3.LayoutView) expanded_wg_lo.set(shape=[(0.0, 0.0), (self.expanded_wg_length, 0.0)]) return expanded_wg_lo def _default_child_transformations(self): trans = dict() trans["spiral"] = (-self._spiral_in_out_distance(), 0.0) trans["wg_right"] = (0.0, 0.0) trans["taper_right"] = i3.Translation((self.right_wg_length, 0)) trans["expanded_wg_right"] = i3.Translation( (self.right_wg_length + self.taper_length, 0)) trans["wg_left"] = i3.HMirror(mirror_plane_x=0.0) + i3.Translation( (-self._spiral_in_out_distance(), 0)) trans["taper_left"] = i3.HMirror( mirror_plane_x=0.0) + i3.Translation((-self.spiral_width, 0)) trans["expanded_wg_left"] = i3.HMirror( mirror_plane_x=0.0) + i3.Translation( (-self.spiral_width - self.taper_length, 0)) return trans def get_true_length(self): return self.spiral_width - self._spiral_in_out_distance( ) + self.right_wg_length + self.spiral_length
class Layout(i3.LayoutView): # Properties ------- # number of taper pairs n_pairs = i3.IntProperty(default=1, doc='number of taper pairs') # Methods ------- def _generate_instances(self, insts): # Generates taper pairs w edge couplers # load the aim gds just to get its positions and stuff # main chip GDS fname = '../PDK_Library_Layout_GDS/ap_suny_v20a_chipframe.gds' main_chip_gds_cell = i3.GDSCell(filename=fname) # grab layout size info main_chip_gds_lay = main_chip_gds_cell.Layout() main_chip_gds_lay_size_info = main_chip_gds_lay.size_info() # grab relevant positions chip_edge_east = main_chip_gds_lay_size_info.east chip_edge_west = main_chip_gds_lay_size_info.west # make edge coupler edge_coupler_gds_lay = EdgeCoupler(name=self.name + 'edge_coupler_sffdfi').Layout() # add and route input/west edgecoupler # position edge coupler on west side of chip chip_port_west = i3.OpticalPort(position=(chip_edge_west, 0.0), angle_deg=0.0) edge_coupler_west_port = edge_coupler_gds_lay.ports['out'] t = i3.vector_match_transform(edge_coupler_west_port, chip_port_west) edge_coupler_west_name = self.name + '_EDGE_COUPLER_WEST' west_edge_coupler = i3.SRef(name=edge_coupler_west_name, reference=edge_coupler_gds_lay, transformation=t, flatten=False) # add a small linear taper to go from 0.4 to 0.5um wg lin_taper_lay = LinearTaper().get_default_view(i3.LayoutView) lin_taper_lay.set(wg_width_in=0.4, wg_width_out=0.5, length=10.0) t = i3.vector_match_transform(lin_taper_lay.ports['in'], west_edge_coupler.ports['in']) lin_taper_lay_name = self.name + '_EDGETAPER_WEST' insts += i3.SRef(name=lin_taper_lay_name, reference=lin_taper_lay, transformation=t, flatten=True) # route wg to wg with arc bend_radius = 10.0 arc_center_1 = ( insts[lin_taper_lay_name].ports['out'].position[0], insts[lin_taper_lay_name].ports['out'].position[1] + bend_radius) route_wg_shape_arc1 = i3.ShapeArc(radius=bend_radius, angle_step=1.0, center=arc_center_1, start_angle=269.5, end_angle=0.5, closed=False, clockwise=False) wg_name_arc1 = self.name + '_ARC1' wg_lay_arc1 = i3.Waveguide(trace_template=StripWgTemplate(), name=wg_name_arc1).get_default_view( i3.LayoutView) wg_lay_arc1.set(shape=route_wg_shape_arc1) insts += i3.SRef(name=wg_name_arc1, reference=wg_lay_arc1, flatten=True) # add the bends bend_clip_lay = BendClip( name=self.name + '_BEND_CLIP').get_default_view(i3.LayoutView) bend_clip_lay.set(n_pairs=self.n_pairs) # add to insts bend_clip_lay_name = self.name + '_BEND_CLIP' t = i3.vector_match_transform(bend_clip_lay.ports['in'], insts[wg_name_arc1].ports['out']) insts += i3.SRef(name=bend_clip_lay_name, reference=bend_clip_lay, transformation=t, flatten=True) # add output bend arc_center_2 = ( insts[bend_clip_lay_name].ports['out'].position[0] + bend_radius, insts[bend_clip_lay_name].ports['out'].position[1]) route_wg_shape_arc2 = i3.ShapeArc(radius=bend_radius, angle_step=1.0, center=arc_center_2, start_angle=180.5, end_angle=89.5, closed=False, clockwise=True) wg_name_arc2 = self.name + '_ARC2' wg_lay_arc2 = i3.Waveguide(trace_template=StripWgTemplate(), name=wg_name_arc2).get_default_view( i3.LayoutView) wg_lay_arc2.set(shape=route_wg_shape_arc2) insts += i3.SRef(name=wg_name_arc2, reference=wg_lay_arc2, flatten=True) # add east coupler chip_port_east = i3.OpticalPort( position=(chip_edge_east, insts[wg_name_arc2].ports['out'].position[1]), angle_deg=180.0) edge_coupler_east_port = edge_coupler_gds_lay.ports['out'] t = i3.vector_match_transform(edge_coupler_east_port, chip_port_east, mirrored=True) edge_coupler_east_name = self.name + '_EDGE_COUPLER_EAST' east_edge_coupler = i3.SRef(name=edge_coupler_east_name, reference=edge_coupler_gds_lay, transformation=t, flatten=False) # add a small linear taper to go from 0.4 to 0.5um wg lin_taper_lay = LinearTaper().get_default_view(i3.LayoutView) lin_taper_lay.set(wg_width_in=0.4, wg_width_out=0.5, length=10.0) t = i3.vector_match_transform(lin_taper_lay.ports['in'], east_edge_coupler.ports['in'], mirrored=True) lin_taper_lay_name = self.name + '_EDGETAPER_EAST' insts += i3.SRef(name=lin_taper_lay_name, reference=lin_taper_lay, transformation=t, flatten=True) # route arc to arc with straight section route_wg_shape_out = i3.Shape([ insts[wg_name_arc2].ports['out'].position, insts[lin_taper_lay_name].ports['out'].position ]) wg_name_out = self.name + '_WG_CON_OUT' wg_lay_out = i3.Waveguide(trace_template=StripWgTemplate(), name=wg_name_out).get_default_view( i3.LayoutView) wg_lay_out.set(shape=route_wg_shape_out) insts += i3.SRef(name=wg_name_out, reference=wg_lay_out, flatten=True) return insts # end _generate_instances() def _generate_ports(self, ports): # add ports 'left' and 'right' # left port ports += i3.OpticalPort( name='in', position=self.instances[ self.name + '_EDGETAPER_WEST'].ports['in'].position, angle=180.0) # right port ports += i3.OpticalPort( name='out', position=self.instances[ self.name + '_EDGETAPER_EAST'].ports['out'].position, angle=0.0) return ports
class Layout(i3.LayoutView): """ Alignment mark layout view. """ # Specify two layers on which markers are drawn process1 = i3.ProcessProperty(default=i3.TECH.PPLAYER.CH2.TRENCH)#i3.TECH.PROCESS.CHANNEL_1, doc="Process Layer 1") #was i3.TECH.PROCESS.WG process2 = i3.ProcessProperty(default=i3.TECH.PPLAYER.CH1.TRENCH)#i3.TECH.PROCESS.CHANNEL_2, doc="Process Layer 2") # Properties of crosses dark_cross_bar_width = i3.PositiveNumberProperty(default=30, doc="width of the dark cross") open_cross_bar_width = i3.PositiveNumberProperty(default=40, doc="width of the open cross") cross_boundary_width = i3.PositiveNumberProperty(default=150, doc="width of the cross boundary box") # Basic properties of Vernier scales vern_spacing_short = i3.PositiveNumberProperty(default=18, doc="spacing between bars of shorter vernier scale") vern_spacing_long = i3.PositiveNumberProperty(default=18.5, doc="spacing between bars of longer vernier scale") vern_number_of_bar = i3.IntProperty(default=13, doc="the number of vernier bars") # Detailed properties of Vernier scales vern_bar_length = i3.PositiveNumberProperty(default=30, doc="length of the shortest bars on the scale") vern_bar_extra_length = i3.PositiveNumberProperty(default=10, doc="extra length of the central bar") vern_bar_width = i3.PositiveNumberProperty(default=5, doc="width of a single bar") # Separation between Vernier scales and Crosses section vern_cross_spacing = i3.PositiveNumberProperty(default=30, doc="Distance between cross box and closest edge of scales") # Separation between 2 scales of 2 layers vern_layer_gap = i3.NonNegativeNumberProperty(default=0.0, doc="gap between 2 scales of 2 layers on alignment") def _default_verniers(self): vern_1 = self.cell.verniers[0].Layout(spacing=self.vern_spacing_long, number_of_bars=self.vern_number_of_bar, bar_length=self.vern_bar_length, bar_extra_length=self.vern_bar_extra_length, bar_width=self.vern_bar_width)#, #process=self.process1) vern_2 = self.cell.verniers[1].Layout(spacing=self.vern_spacing_short, number_of_bars=self.vern_number_of_bar, bar_length=self.vern_bar_length, bar_extra_length=self.vern_bar_extra_length, bar_width=self.vern_bar_width)#, #process=self.process2) return [vern_1, vern_2] def _default_cross_marks(self): # Dark Cross on layer 1 dark_cross = self.cell.cross_marks[0].Layout(inversion=False, cross_bar_width=self.dark_cross_bar_width, cross_boundary_width=self.cross_boundary_width)#, #process=self.process1) open_cross = self.cell.cross_marks[1].Layout(inversion=True, cross_bar_width=self.open_cross_bar_width, cross_boundary_width=self.cross_boundary_width)#, #process=self.process2) return [dark_cross, open_cross] def _generate_instances(self, insts): insts += i3.SRef(reference=self.cross_marks[0]) insts += i3.SRef(reference=self.cross_marks[1]) vern_1_horz_trans = i3.VMirror() + \ i3.Translation((0, -self.cross_boundary_width * 0.5 - self.vern_cross_spacing - (self.vern_bar_length + self.vern_bar_extra_length) - self.vern_layer_gap)) insts += i3.SRef(reference=self.verniers[0], transformation=vern_1_horz_trans) vern_2_horz_trans = i3.Translation((0, -self.cross_boundary_width * 0.5 - self.vern_cross_spacing - (self.vern_bar_length + self.vern_bar_extra_length))) insts += i3.SRef(reference=self.verniers[1], transformation=vern_2_horz_trans) vern_1_vert_trans = i3.Rotation(rotation=90) + \ i3.Translation((-self.cross_boundary_width*0.5 - self.vern_cross_spacing - (self.vern_bar_length + self.vern_bar_extra_length) - self.vern_layer_gap, 0)) insts += i3.SRef(reference=self.verniers[0], transformation=vern_1_vert_trans) vern_2_vert_trans = i3.Rotation(rotation=270) + \ i3.Translation((-self.cross_boundary_width*0.5 - (self.vern_bar_length + self.vern_bar_extra_length) - self.vern_cross_spacing, 0)) insts += i3.SRef(reference=self.verniers[1], transformation=vern_2_vert_trans) return insts
class SplitterTree(APAC): splitter = i3.ChildCellProperty(doc="splitter used") levels = i3.IntProperty(default=3, doc="Number of levels") spacing_x = i3.PositiveNumberProperty(default=100.0) spacing_y = i3.PositiveNumberProperty(default=50.0) bend_radius = i3.PositiveNumberProperty() def _default_bend_radius(self): return 20.0 def _default_splitter(self): return MMI_12(name=self.name + "_MMI") def _default_child_cells(self): childs = dict() for l in range(self.levels): for sp in range(int(2**l)): childs["sp_{}_{}".format(l, sp)] = self.splitter return childs def _default_connectors(self): conn = [] for l in range(1, self.levels): for sp in range(int(2**l)): if sp % 2 == 0: in_port = "sp_{}_{}:out1".format(l - 1, int(sp / 2.0)) else: in_port = "sp_{}_{}:out2".format(l - 1, int(sp / 2.0)) out_port = "sp_{}_{}:in1".format(l, sp) conn.append((in_port, out_port, sbend, { "bend_radius": self.bend_radius })) return conn def _default_external_port_names(self): epn = dict() cnt = 1 l = self.levels - 1 for sp in range(int(2**l)): epn["sp_{}_{}:out1".format(l, sp)] = "out{}".format(cnt) cnt = cnt + 1 epn["sp_{}_{}:out2".format(l, sp)] = "out{}".format(cnt) cnt = cnt + 1 epn["sp_{}_{}:in1".format(0, 0)] = "in" return epn class Layout(APAC.Layout): def _default_child_transformations(self): trans = dict() for l in range(self.levels): for sp in range(int(2**l)): sp_y = self.spacing_y * 2**(self.levels - l - 1) trans["sp_{}_{}".format(l, sp)] = i3.Translation( translation=(l * self.spacing_x, -0.5 * (2**l - 1) * sp_y + sp * sp_y)) return trans