class Funnel(i3.PCell): """A generic Funnel to connect elements. It is defined by a boundary which are defined by points """ # 1. First we define our 2 Channel Templates with different Width's. channel_1_template = microfluidics.ShortChannelTemplate() channel_2_template = microfluidics.ShortChannelTemplate() # 2. Then we use the LinearWindowChannelTransition class to create a transition between Channels. #####################3 ################### channel_template = microfluidics.ChannelTemplateProperty( default=microfluidics.ShortChannelTemplate(), doc="Channel template of the tee") _name_prefix = "Funnel" # a prefix added to the unique funnel_length = i3.NumberProperty(default=100.0, doc="length of funnel") initial_width = i3.NumberProperty(default=200.0, doc="initial with of funnel") final_width = i3.NumberProperty(default=100.0, doc="final width of funnel") class Layout(i3.LayoutView): def _generate_instances(self, insts): channel_1_template = self.cell.channel_1_template.Layout( channel_width=self.cell.initial_width) channel_2_template = self.cell.channel_2_template.Layout( channel_width=self.cell.final_width) funnel = microfluidics.LinearWindowChannelTransition( start_trace_template=self.cell.channel_1_template, end_trace_template=self.cell.channel_2_template) funnel_layout = funnel.Layout( start_position=(0.0, 0.0), end_position=(self.cell.funnel_length, 0.0)) insts += i3.SRef(reference=funnel_layout, name='funnel', position=(0, 0)) return insts def _generate_ports(self, ports): #port1 ports += microfluidics.FluidicPort( name='in', position=(0, 0), direction=i3.PORT_DIRECTION.IN, angle_deg=180, trace_template=self.cell.channel_template) #port2 ports += microfluidics.FluidicPort( name='out', position=(self.funnel_length, 0), direction=i3.PORT_DIRECTION.OUT, angle_deg=0, trace_template=self.cell.channel_template) return ports
class accessHoleFlat(i3.PCell): """A generic inlet/outlet hole class. It is defined by a circular """ _name_prefix = "ACCESSHOLE_FLAT" # a prefix aded to the unique identifier reduction_ratio = i3.NumberProperty( default=1.0) #transition from BigRes to Trap diameter = i3.NumberProperty(default=300.) class Layout(i3.LayoutView): channel_template = microfluidics.ShortChannelTemplate() cInp = i3.Coord2Property(default=(0.0, 0.0)) def _generate_elements(self, elems): elems += i3.CirclePath(layer=i3.TECH.PPLAYER.CH2.TRENCH, center=(0.0, 0.0), radius=self.diameter * 0.5, line_width=50) point_list = [] point_list.append((0, -self.diameter * 0.5)) point_list.insert(0, (0, self.diameter * 0.5)) point_list.append( (self.diameter * self.cell.reduction_ratio, -self.diameter * 0.5 * self.cell.reduction_ratio)) point_list.insert( 0, (self.diameter * self.cell.reduction_ratio, self.diameter * 0.5 * self.cell.reduction_ratio)) funnel = i3.Shape(point_list, closed=True) bo = i3.Boundary(i3.TECH.PPLAYER.CH2.TRENCH, funnel) elems += bo return elems def _generate_ports(self, ports): '''ports += microfluidics.FluidicPort(name='in', position=(0.0, 0.0), direction=i3.PORT_DIRECTION.IN, trace_template=self.channel_template, angle_deg=0 )''' ports += microfluidics.FluidicPort( name='out', position=(self.diameter * self.cell.reduction_ratio, 0.0), direction=i3.PORT_DIRECTION.OUT, trace_template=self.channel_template, angle_deg=180) return ports
class Layout(i3.LayoutView): channel_template = microfluidics.ShortChannelTemplate() length = i3.NumberProperty(default=60e3) width = i3.NumberProperty(default=20e3) cInp = i3.Coord2Property(default=(0.0, 0.0)) def _generate_elements(self, elems): elems += i3.RoundedRectanglePath(layer=i3.TECH.PPLAYER.CH2.TRENCH, center=(0.0, 0.0), box_size=(self.length, self.width), radius=200., line_width=2.0) return elems
class CircuitModel(i3.CircuitModelView): R = i3.NumberProperty(doc="Responsivity (A/W)", default=0.8) BW = i3.NumberProperty(doc="3 dB bandwidth of the photodetector (GHz)", default=60) # Optical parameters reflection_dB = i3.NonNegativeNumberProperty(doc="Reflection (dB)", default=100.0) # Dark current parameters dark_current = i3.NumberProperty(doc='Dark current (nA)', default=50) def _generate_model(self): from aeponyx.compactmodels import PhotoDetectorCompactModel return PhotoDetectorCompactModel(R=self.R, BW=self.BW, reflection_dB=self.reflection_dB, dark_current=self.dark_current)
class Layout(i3.LayoutView): channel_template = microfluidics.ShortChannelTemplate() size = i3.NumberProperty(default=10e4) cInp = i3.Coord2Property(default=(0.0, 0.0)) def _generate_elements(self, elems): elems += i3.CirclePath(layer=i3.TECH.PPLAYER.CH2.TRENCH, center=(0.0, 0.0), radius=self.size * 0.5, line_width=200) return elems
class Layout(i3.LayoutView): tee_length = i3.NumberProperty(default=10.0, doc="length of each tee branch") def _generate_instances(self, elems): # insts): rectangle = i3.RoundedRectangle(layer=i3.TECH.PPLAYER.CH2.TRENCH, center=(0.0, 0.0), box_size=(self.tee_length, 2 * self.tee_length), radius=1.0) rectangle2 = i3.RoundedRectangle(layer=i3.TECH.PPLAYER.CH2.TRENCH, center=(0.5 * self.tee_length, 0.0), box_size=(self.tee_length, self.tee_length), radius=1.0) #boolean operation add main trap and 1st rectangle b_add = rectangle | rectangle2 s = i3.Structure(elements=b_add) elems += i3.SRef(s) return elems #insts def _generate_ports(self, ports): #port1 ports += microfluidics.FluidicPort( name='in1', position=(0, -self.tee_length), direction=i3.PORT_DIRECTION.IN, angle_deg=270, trace_template=self.cell.channel_template) #port2 ports += microfluidics.FluidicPort( name='in2', position=(0, self.tee_length), direction=i3.PORT_DIRECTION.IN, angle_deg=90, trace_template=self.cell.channel_template) #port3 ports += microfluidics.FluidicPort( name='out1', position=(self.tee_length, 0.0), direction=i3.PORT_DIRECTION.IN, angle_deg=0, trace_template=self.cell.channel_template) return ports
class Layout(i3.LayoutView): tee_length = i3.NumberProperty(default=300.0, doc="length of each tee branch") def _generate_instances(self, insts): channel1 = microfluidics.Channel( trace_template=self.cell.channel_template) channel1_lo = channel1.Layout( shape=[(0, -self.tee_length), (0, self.tee_length)]) insts += i3.SRef(channel1_lo, position=(0, 0)) channel2 = microfluidics.Channel( trace_template=self.cell.channel_template) channel2_lo = channel2.Layout(shape=[(0, 0), (-self.tee_length, 0)]) insts += i3.SRef(channel2_lo, position=(self.tee_length, 0)) return insts def _generate_ports(self, ports): #port1 ports += microfluidics.FluidicPort( name='in1', position=(0, -self.tee_length), direction=i3.PORT_DIRECTION.IN, angle_deg=270, trace_template=self.cell.channel_template) #port2 ports += microfluidics.FluidicPort( name='in2', position=(0, self.tee_length), direction=i3.PORT_DIRECTION.IN, angle_deg=90, trace_template=self.cell.channel_template) #port3 ports += microfluidics.FluidicPort( name='out1', position=(self.tee_length, 0.0), direction=i3.PORT_DIRECTION.OUT, angle_deg=0, trace_template=self.cell.channel_template) return ports
class CircuitModel(i3.CircuitModelView): coupler_length = i3.PositiveNumberProperty( doc= "Length of the straight section of the directional coupler, calculated from the power coupling", locked=True) power_coupling_factor = i3.FractionProperty( default=0.5, doc="Fraction of the field coupling") delta_n_eff = i3.NumberProperty( doc="Difference between even and odd mode of the dc") coupling_at_zero_length = i3.NonNegativeNumberProperty( default=0.03, doc="Field coupling for a zero length coupling", locked=True) center_wavelength = i3.PositiveNumberProperty( doc="Reference wavelength for all parameters", locked=True) def _default_center_wavelength(self): return self.trace_template.center_wavelength def _default_delta_n_eff(self): return 0.04 def _default_coupler_length(self): L = (self.center_wavelength * np.arcsin(self.power_coupling_factor**0.5) / (np.pi * self.delta_n_eff) - self.center_wavelength * np.arcsin(self.coupling_at_zero_length) / (np.pi * self.delta_n_eff)) return L def _generate_model(self): trace_length = self.layout_view.instances[ 'wav_top'].reference.trace_length() non_coupling_length = (trace_length - self.coupler_length) return DirCoupModel( delta_n=self.delta_n_eff, n_avg=self.trace_template.n_eff, coupler_length=self.coupler_length, non_coupling_length=non_coupling_length, coupling_at_zero_length=self.coupling_at_zero_length, center_wavelength=self.center_wavelength)
class accessHole(i3.PCell): """A generic inlet/outlet hole class. It is defined by a circular """ _name_prefix = "ACCESSHOLE" # a prefix aded to the unique identifier diameter = i3.NumberProperty(default=300.) class Layout(i3.LayoutView): channel_template = microfluidics.ShortChannelTemplate() #inlet_diameter = i3.NumberProperty(default =300.) cInp = i3.Coord2Property(default=(0.0, 0.0)) def _generate_elements(self, elems): #solid #circle = i3.ShapeCircle(center=(0.0,0.0), radius=self.inlet_diameter*0.5) #elems += i3.Boundary(layer=i3.TECH.PPLAYER.CH2.TRENCH, shape=circle) #line (faster on MLA) elems += i3.CirclePath(layer=i3.TECH.PPLAYER.CH2.TRENCH, center=(0.0, 0.0), radius=self.diameter * 0.5, line_width=200) return elems def _generate_ports(self, ports): ports += microfluidics.FluidicPort( name='in', position=(0.0, 0.0), direction=i3.PORT_DIRECTION.IN, trace_template=self.channel_template, angle_deg=90) ports += microfluidics.FluidicPort( name='out', position=(0.0, 0.0), direction=i3.PORT_DIRECTION.OUT, trace_template=self.channel_template, angle_deg=90) return ports
class CircuitModel(i3.CircuitModelView): n_eff = i3.PositiveNumberProperty(locked=True) VpiLpi = i3.PositiveNumberProperty( default=1.5, doc="Voltage to get pi-shift for 1 cm of phase modulator [V.cm]") delay = i3.NumberProperty(locked=True) insertion_loss_dB = i3.NonNegativeNumberProperty( default=0., doc='Insertion loss [dB]') def _default_n_eff(self): tt_cm = self.trace_template return tt_cm.n_eff def _default_delay(self): return self.layout_view.delay def _generate_model(self): from aeponyx.compactmodels import MZMLumpedCompactModel return MZMLumpedCompactModel( n_eff=self.n_eff, delay=self.delay, insertion_loss_dB=self.insertion_loss_dB, VpiLpi=self.VpiLpi, length=self.modulator_length)
class pinsRectangleFour(i3.PCell): """A generic inlet/outlet hole class. It is defined by a circular """ _name_prefix = "PINS" # a prefix aded to the unique identifier diameter = i3.NumberProperty(default=1000.) class Layout(i3.LayoutView): channel_template = microfluidics.ShortChannelTemplate() #inlet_diameter = i3.NumberProperty(default =300.) cInp = i3.Coord2Property(default=(0.0, 0.0)) def _generate_elements(self, elems): #solid #circle = i3.ShapeCircle(center=(0.0,0.0), radius=self.inlet_diameter*0.5) #elems += i3.Boundary(layer=i3.TECH.PPLAYER.CH2.TRENCH, shape=circle) #line (faster on MLA) elems += i3.CirclePath(layer=i3.TECH.PPLAYER.CH1.TRENCH, center=(26e3, 9e3), radius=self.diameter * 0.5, line_width=200) elems += i3.CirclePath(layer=i3.TECH.PPLAYER.CH1.TRENCH, center=(-26e3, 9e3), radius=self.diameter * 0.5, line_width=200) elems += i3.CirclePath(layer=i3.TECH.PPLAYER.CH1.TRENCH, center=(-26e3, -9e3), radius=self.diameter * 0.5, line_width=200) elems += i3.CirclePath(layer=i3.TECH.PPLAYER.CH1.TRENCH, center=(26e3, -9e3), radius=self.diameter * 0.5, line_width=200) return elems
class CombinedCircuit(PlaceAndAutoRoute): L1=i3.PositiveNumberProperty(default=100,doc="radius or curvature of circuit") incoupler = i3.ChildCellProperty(doc="coupler used", locked=True) outcoupler = i3.ChildCellProperty(doc="coupler used", locked=True) aligningmark=i3.ChildCellProperty(doc="aligning mark") lens=i3.ChildCellProperty(doc="flowcellbox") flowcellbox=i3.ChildCellProperty(doc="lens") aligningmark_h=i3.ChildCellProperty(doc="aligning mark for heater") coupling_l=i3.PositiveNumberProperty(default=5+2*270, doc="length of coupling WG") coupling_win = i3.PositiveNumberProperty(default=15, doc="width of the coupling WG") coupling_wout = i3.PositiveNumberProperty(default=15, doc="width of the coupling WG") chip_length=i3.PositiveNumberProperty(default=20000,doc="length of the chip") radius=i3.PositiveNumberProperty(default=100,doc="radius or curvature of circuit") core=i3.PositiveNumberProperty(default=3.3, doc="core width") width=i3.PositiveNumberProperty(default=3.3, doc="narrow width of heaters") tipo =i3.NumberProperty(default=2, doc="1=spiral, 2=wide waveguide") wide_wg_l = i3.NumberProperty(default=20, doc="Wide Waveguide") coupler_p=i3.NumberProperty(default=20, doc="position of the grating coupler with respect to the edge") ctipoin=i3.PositiveNumberProperty(default=2, doc="type of coupler used ctipo=1 for grating and ctipo=2 for taper") ctipoout=i3.PositiveNumberProperty(default=1, doc="type of coupler used ctipo=1 for grating and ctipo=2 for taper") transition_length_couplerin=i3.PositiveNumberProperty(default=500, doc="transition length of the coupler") transition_length_couplerout=i3.PositiveNumberProperty(default=500, doc="transition length of the coupler") aligningmark=i3.ChildCellProperty(doc="aligning mark for circuit used") increment=i3.NumberProperty(default=0, doc="core width") periodin=i3.PositiveNumberProperty(default=1.73, doc="core width") dutyin=i3.PositiveNumberProperty(default=0.5, doc="core width") nperiodsin=i3.PositiveNumberProperty(default=8, doc="core width") periodout=i3.PositiveNumberProperty(default=2.566, doc="core width") dutyout=i3.PositiveNumberProperty(default=0.403, doc="core width") nperiodsout=i3.PositiveNumberProperty(default=20, doc="core width") Loc = i3.ListProperty(default=[]) #Non etched part, illuminated during e-beam Lec = i3.ListProperty(default=[]) #Etched part layName_h = i3.StringProperty(default = 'standard', doc = 'options: standard, new') layName_h2 = i3.StringProperty(default = 'standard', doc = 'options: standard, new') layName_c = i3.StringProperty(default = 'standard', doc = 'options: standard, new') layName_f = i3.StringProperty(default = 'standard', doc = 'options: standard, new') separation=i3.NumberProperty(default=100.0, doc="separation between cross and gratings") size=i3.NumberProperty(default=250.0, doc="Size of the cross of aligning marks") print 'core width',core print 'heater width', width def _default_trace_template(self): return self.incoupler.inPort.trace_template def _default_Lec(self): #Loc=[2.175,2.122,2.07,2.016,1.963,1.908,1.854,1.798,1.743,1.687,1.63,1.573, #1.515,1.457,1.398,1.339,1.279,1.219,1.158,1.096] #version A Loc=[2.043,1.643] #1um etch for 2um GOS #Loc=[1,1,1,1,1,1,1] Loc.reverse() return Loc def _default_Loc(self): #Lec=[0.242,0.301,0.361,0.421,0.482,0.543,0.605,0.667,0.73,0.794,0.858,0.922, #0.988,1.054,1.12,1.187,1.255,1.323,1.392,1.462] #version A Lec=[0.511,0.949] #1um etch for 2um GOS #Lec=[0.4,0.5,0.6,0.7,0.8,0.9,1] Lec.reverse() return Lec def _default_child_cells(self): child_cells={ "incoupling1" : self.incoupler, "outcoupling1" : self.outcoupler, # "am1" : self.aligningmark, # "am2" : self.aligningmark, # "am3" : self.aligningmark, # "am4" : self.aligningmark, # "inlens1" : self.lens, # "outlens1" : self.lens, # "chip" : self.flowcellbox } return child_cells def _default_links(self): links = [( "incoupling1:InPort_in","outcoupling1:InPort_in"), ] return links def _default_aligningmark(self): am=AligningMarks(layName='metal2', separation=self.separation, size=self.size) return am def _default_aligningmark_h(self): am=AligningMarks(layName=self.layName_h, separation=self.separation, size=self.size) return am def _default_aligningmark_h2(self): am=AligningMarks(layName=self.layName_h2, separation=self.separation, size=self.size) return am def _default_outcoupler(self): print 'I am in the coupler' coupler = GratingCoupler1Band(coupling_l=self.coupling_l+self.increment, chirp=self.ctipoout, coupling_w=self.coupling_wout,core=self.core, layName = self.layName_c, period=self.periodout, duty=self.dutyout, nperiods=self.nperiodsout, Loc=self.Loc, Lec=self.Lec, transition_length_coupler=self.transition_length_couplerout, ) #layout_coupler=coupler.Layout(transition_length_coupler=self.transition_length_couplerout, #)#.visualize(annotate=True) return coupler def _default_incoupler(self): print 'I am in the coupler' coupler = GratingCoupler1Band(coupling_l=self.coupling_l, chirp=self.ctipoin, coupling_w=self.coupling_win,core=self.core, layName = self.layName_c, period=self.periodin, duty=self.dutyin, nperiods=self.nperiodsin, Loc=self.Loc, Lec=self.Lec, transition_length_coupler=self.transition_length_couplerin, ) ##layout_coupler=coupler.Layout(transition_length_coupler=self.transition_length_couplerin, #)#.visualize(annotate=True) return coupler def _default_lens(self): lens=Lens() return lens def _default_flowcellbox(self): fl=flowcellbox() return fl class Layout(PlaceAndAutoRoute.Layout): def _default_child_transformations(self): incoupler_t =fc_t1 = i3.Rotation(rotation=00.0) #+ i3.Translation(translation=(-(self.chip_length*0.5-self.coupler_p),0.0)) outcoupler_t = i3.Rotation(rotation=180.0) #+ i3.Translation(translation=(self.chip_length*0.5-self.coupler_p,0.0)) L1=self.L1 #s=270 s=0 #z=2500 z=0 #socket_length=2*self.period*int(self.nperiods)+2*10 socket_lengthout=2*(sum(self.Lec)+sum(self.Loc))+2*self.periodout*int(self.nperiodsout)+1*10 socket_lengthin=2*self.periodin*int(self.nperiodsin)+0*10 xin=0.75*self.periodin*int(self.nperiodsin) xout=0.75*(self.periodout*int(self.nperiodsout)+sum(self.Lec)+sum(self.Loc)) print "socket_lengthout: ", socket_lengthout print "socket_lengthin: ", socket_lengthin #print self.child_cells['incoupling1'].FGC.Layout.socket_length child_transformations={ #"incoupling1" : i3.Rotation(rotation=00.0) +i3.Translation(translation=(-z-socket_lengthin#-50.0#-self.transition_length_couplerin #,-s)), #"outcoupling1" : i3.Rotation(rotation=180.0) +i3.Translation(translation=(L1#+50.0#+self.transition_length_couplerout+self.increment-z #,-s)), "incoupling1" : i3.Rotation(rotation=00.0) +i3.Translation(translation=(-z#-socket_lengthin#-50.0#-self.transition_length_couplerin ,-s)), "outcoupling1" : i3.Rotation(rotation=180.0) +i3.Translation(translation=(L1+socket_lengthout-100#+50.0#+self.transition_length_couplerout+self.increment-z ,-s)), # "inlens1" : i3.Rotation(rotation=00.0) +i3.Translation(translation=(-z-self.transition_length_couplerin-socket_lengthin+xin,-s)), #"outlens1" : i3.Rotation(rotation=180.0) +i3.Translation(translation=(L1+self.transition_length_couplerin+self.increment-z-xin,-s)), # "am1" : i3.Translation(translation=(self.chip_length*0.5-1000,0.0)), # "am2" : i3.Translation(translation=(-self.chip_length*0.5+1000,0.0)), # "am3" : i3.Translation(translation=(-self.chip_length*0.5+5000,0.0)), # "am4" : i3.Translation(translation=(self.chip_length*0.5-5000,0.0)) , } return child_transformations def _generate_elements(self, elems): elems += i3.PolygonText(layer= i3.TECH.PPLAYER.WG.TEXT, text='period={}_duty={}_n={}_width={}'.format(self.periodin, self.dutyin, self.nperiodsin, self.core), #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) coordinate=(0.0, -20), alignment=(i3.TEXT_ALIGN_LEFT, i3.TEXT_ALIGN_LEFT), font = 2, height=20.0) return elems def _default_bend_radius(self): return self.radius+self.core*0.5
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
class Layout(i3.LayoutView): # Properties ------- # taper to draw # taper_layout = i3.ViewProperty( default='', doc='taper cell to draw and stuff' ) # connecting length between tapers connect_length = i3.NumberProperty(default=0.0, doc='distance between tapers') # taper cell # taper_cell = i3.PCellProperty( default=i3.PCell(), doc='taper cell to draw') taper_prop_dict = i3.DictProperty( default = {}, doc = 'Dictionary of taper cell properties.' + \ 'Properties = "length", "width1", "width2", "width_etch" ' ) # this is terrible... # actually f**k it hard code in the taper # Methods ------- def _generate_instances(self, insts): # generates taper pair # generate left taper cell taper_layout_left = ParabolicTaper( name=self.name + '_TAPER_L').get_default_view(i3.LayoutView) taper_layout_left.set( length=self.taper_prop_dict['length'], width1=self.taper_prop_dict['width1'], width2=self.taper_prop_dict['width2'], width_etch=self.taper_prop_dict['width_etch']) # generate right taper cell taper_layout_right = ParabolicTaper( name=self.name + '_TAPER_R').get_default_view(i3.LayoutView) taper_layout_right.set( length=self.taper_prop_dict['length'], width1=self.taper_prop_dict['width1'], width2=self.taper_prop_dict['width2'], width_etch=self.taper_prop_dict['width_etch']) # draw taper pairs insts += i3.SRef(name=self.name + '_taper1', reference=taper_layout_left) t = i3.vector_match_transform(taper_layout_left.ports['right'], taper_layout_right.ports['right'], mirrored=True) + i3.Translation( (self.connect_length, 0.0)) insts += i3.SRef(name=self.name + '_taper2', reference=taper_layout_right, transformation=t) # route between tapers if self.connect_length > 0.0: route_tapers = i3.RouteManhattan( input_port=insts[self.name + '_taper1'].ports['right'], output_port=insts[self.name + '_taper2'].ports['right']) # # make my OWN custom waveguide trace template # wg_trace = f_MyIMECWaveguideTemplate( core_width = taper_layout_left.width2, # cladding_width = taper_layout_right.width2 + 2.0*taper_layout_right.width_etch ) # wg_lay = i3.Waveguide(trace_template=StripWgTemplate(), name=wg_name).get_default_view(i3.LayoutView) # make waveguide wg = i3.Waveguide(trace_template=StripWgTemplate(), name=self.name + '_WG') # add wg insts += i3.SRef(name=self.name + 'connect_wg', reference=wg.Layout(shape=route_tapers)) # end if self.connect_length > 0.0 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 + '_taper1'].ports['left'].position, angle=180.0) # right port ports += i3.OpticalPort( name='right', position=self.instances[self.name + '_taper2'].ports['left'].position, angle=0.0) return ports
class Coupon(i3.PCell): _name_prefix = "Coupon" # Center of the structure position = i3.Coord2Property(default=(0.0, 0.0)) # Coupon parameters length = i3.PositiveNumberProperty(default=40.0) width = i3.PositiveNumberProperty(default=8.0) shrink = i3.NumberProperty(default=0.0) # nb = i3.PositiveIntProperty(default=8) space = i3.PositiveNumberProperty(default=1000.0) class Layout(i3.LayoutView): # _name_prefix = "length{}".format(str(self.length)) def _generate_elements(self, elems): # Center of the structure (x0, y0) = self.position # RELEASE release = Release( length=self.length, width=self.width, ) # release = i3.Rectangle(layer=i3.Layer(number=1, name="release"), center=(0, 0), # box_size=(self.length, self.width)) # elems += i3.SRef(reference=release, position=self.position) Error dx = 918.125 # 800nm coupon for i in range(0, 3, 1): elems += i3.ARef(n_o_periods=(3, 7), period=(159.5, 210), reference=release, origin=(640.5 + i * dx, 468.4)) elems += i3.ARef(n_o_periods=(3, 7), period=(159.5, 210), reference=release, origin=(640.5 + i * dx, 538.4)) elems += i3.ARefY(n_o_periods_1d=7, period_1d=210, reference=release, origin=(560.75 + i * dx, 511.6)) dy = 1725 elems += i3.ARef(n_o_periods=(3, 15), period=(159.5, 210), reference=release, origin=(640.5 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(3, 15), period=(159.5, 210), reference=release, origin=(640.5 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=15, period_1d=210, reference=release, origin=(560.75 + i * dx, 511.6 + dy)) dy = 5060 elems += i3.ARef(n_o_periods=(3, 15), period=(159.5, 210), reference=release, origin=(640.5 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(3, 15), period=(159.5, 210), reference=release, origin=(640.5 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=15, period_1d=210, reference=release, origin=(560.75 + i * dx, 511.6 + dy)) dy = 8395 elems += i3.ARef(n_o_periods=(3, 8), period=(159.5, 210), reference=release, origin=(640.5 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(3, 7), period=(159.5, 210), reference=release, origin=(640.5 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=7, period_1d=210, reference=release, origin=(560.75 + i * dx, 511.6 + dy)) dx += 200 # 1000nm coupon for i in range(0, 3, 1): elems += i3.ARef(n_o_periods=(4, 7), period=(167.6, 210), reference=release, origin=(3402.975 + i * dx, 468.4)) elems += i3.ARef(n_o_periods=(4, 7), period=(167.6, 210), reference=release, origin=(3402.975 + i * dx, 538.4)) elems += i3.ARefY(n_o_periods_1d=7, period_1d=210, reference=release, origin=(3319.175 + i * dx, 511.6)) dy = 1725 elems += i3.ARef(n_o_periods=(4, 15), period=(167.6, 210), reference=release, origin=(3402.975 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(4, 15), period=(167.6, 210), reference=release, origin=(3402.975 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=15, period_1d=210, reference=release, origin=(3319.175 + i * dx, 511.6 + dy)) dy = 5060 elems += i3.ARef(n_o_periods=(4, 15), period=(167.6, 210), reference=release, origin=(3402.975 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(4, 15), period=(167.6, 210), reference=release, origin=(3402.975 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=15, period_1d=210, reference=release, origin=(3319.175 + i * dx, 511.6 + dy)) dy = 8395 elems += i3.ARef(n_o_periods=(4, 8), period=(167.6, 210), reference=release, origin=(3402.975 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(4, 7), period=(167.6, 210), reference=release, origin=(3402.975 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=7, period_1d=210, reference=release, origin=(3319.175 + i * dx, 511.6 + dy)) dx += 200 # 1200nm coupon for i in range(0, 3, 1): elems += i3.ARef(n_o_periods=(5, 7), period=(173, 210), reference=release, origin=(6762.75 + i * dx, 468.4)) elems += i3.ARef(n_o_periods=(5, 7), period=(173, 210), reference=release, origin=(6762.75 + i * dx, 538.4)) elems += i3.ARefY(n_o_periods_1d=7, period_1d=210, reference=release, origin=(6676.25 + i * dx, 511.6)) dy = 1725 elems += i3.ARef(n_o_periods=(5, 15), period=(173, 210), reference=release, origin=(6762.75 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(5, 15), period=(173, 210), reference=release, origin=(6762.75 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=15, period_1d=210, reference=release, origin=(6676.25 + i * dx, 511.6 + dy)) dy = 5060 elems += i3.ARef(n_o_periods=(5, 15), period=(173, 210), reference=release, origin=(6762.75 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(5, 15), period=(173, 210), reference=release, origin=(6762.75 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=15, period_1d=210, reference=release, origin=(6676.25 + i * dx, 511.6 + dy)) dy = 8395 elems += i3.ARef(n_o_periods=(5, 8), period=(173, 210), reference=release, origin=(6762.75 + i * dx, 468.4 + dy)) elems += i3.ARef(n_o_periods=(5, 7), period=(173, 210), reference=release, origin=(6762.75 + i * dx, 538.4 + dy)) elems += i3.ARefY(n_o_periods_1d=7, period_1d=210, reference=release, origin=(6676.25 + i * dx, 511.6 + dy)) return elems
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 Coupon(i3.PCell): _name_prefix = "Coupon" # Center of the structure position = i3.Coord2Property(default=(0.0, 0.0)) # Coupon parameters length = i3.PositiveNumberProperty(default=2750.0) width = i3.PositiveNumberProperty(default=85.0) shrink = i3.NumberProperty(default=0.0) nb = i3.PositiveIntProperty(default=8) space = i3.PositiveNumberProperty(default=1000.0) class Layout(i3.LayoutView): # _name_prefix = "length{}".format(str(self.length)) def _generate_elements(self, elems): # Center of the structure (x0, y0) = self.position # RELEASE release = Release( length=self.length, width=self.width, ) # elems += i3.SRef(reference=release, position=self.position) elems += i3.ARef(n_o_periods=(3, self.nb), period=(self.length + 250, 210), reference=release, origin=(x0, y0)) elems += i3.ARef(n_o_periods=(3, self.nb), period=(self.length + 250, 280), reference=release, origin=(x0, y0 + 210 * self.nb - (210 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(3, self.nb), period=(self.length + 250, 350), reference=release, origin=(x0, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(3, self.nb), period=(self.length + 250, 420), reference=release, origin=(x0, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space + 350 * self.nb - (350 - self.width) + self.space)) elems += i3.ARef(n_o_periods=(2, self.nb), period=(self.length + 500, 210), reference=release, origin=(x0 + 9250 - self.shrink * 3, y0)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 500, 280), reference=release, origin=(x0 + 9250 - self.shrink * 3, y0 + 210 * self.nb - (210 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 500, 350), reference=release, origin=(x0 + 9250 - self.shrink * 3, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 500, 420), reference=release, origin=(x0 + 9250 - self.shrink * 3, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space + 350 * self.nb - (350 - self.width) + self.space)) elems += i3.ARef(n_o_periods=(2, self.nb), period=(self.length + 750, 210), reference=release, origin=(x0 + 8000 * 2 - self.shrink * 5, y0)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 750, 280), reference=release, origin=(x0 + 8000 * 2 - self.shrink * 5, y0 + 210 * self.nb - (210 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 750, 350), reference=release, origin=(x0 + 8000 * 2 - self.shrink * 5, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 750, 420), reference=release, origin=(x0 + 8000 * 2 - self.shrink * 5, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space + 350 * self.nb - (350 - self.width) + self.space)) elems += i3.ARef(n_o_periods=(2, self.nb), period=(self.length + 1000, 210), reference=release, origin=(x0 + 23250 - self.shrink * 7, y0)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 1000, 280), reference=release, origin=(x0 + 23250 - self.shrink * 7, y0 + 210 * self.nb - (210 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 1000, 350), reference=release, origin=(x0 + 23250 - self.shrink * 7, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 1000, 420), reference=release, origin=(x0 + 23250 - self.shrink * 7, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space + 350 * self.nb - (350 - self.width) + self.space)) elems += i3.ARef(n_o_periods=(2, self.nb), period=(self.length + 1500, 210), reference=release, origin=(x0 + 29750 + 1500 - self.shrink * 9, y0)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 1500, 280), reference=release, origin=(x0 + 29750 + 1500 - self.shrink * 9, y0 + 210 * self.nb - (210 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 1500, 350), reference=release, origin=(x0 + 29750 + 1500 - self.shrink * 9, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 1500, 420), reference=release, origin=(x0 + 29750 + 1500 - self.shrink * 9, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space + 350 * self.nb - (350 - self.width) + self.space)) elems += i3.ARef(n_o_periods=(2, self.nb), period=(self.length + 2000, 210), reference=release, origin=(x0 + 38250 + 2000 - self.shrink * 11, y0)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 2000, 280), reference=release, origin=(x0 + 38250 + 2000 - self.shrink * 11, y0 + 210 * self.nb - (210 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 2000, 350), reference=release, origin=(x0 + 38250 + 2000 - self.shrink * 11, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 2000, 420), reference=release, origin=(x0 + 38250 + 2000 - self.shrink * 11, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space + 350 * self.nb - (350 - self.width) + self.space)) elems += i3.ARef(n_o_periods=(2, self.nb), period=(self.length + 3000, 210), reference=release, origin=(x0 + 47750 + 3000 - self.shrink * 13, y0)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 3000, 280), reference=release, origin=(x0 + 47750 + 3000 - self.shrink * 13, y0 + 210 * self.nb - (210 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 3000, 350), reference=release, origin=(x0 + 47750 + 3000 - self.shrink * 13, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space)) elems += i3.ARef( n_o_periods=(2, self.nb), period=(self.length + 3000, 420), reference=release, origin=(x0 + 47750 + 3000 - self.shrink * 13, y0 + 210 * self.nb - (210 - self.width) + self.space + 280 * self.nb - (280 - self.width) + self.space + 350 * self.nb - (350 - self.width) + self.space)) # elems += i3.Circle(layer=i3.Layer(number = 5, name = "tether"),radius=50000) return elems
class 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 Layout(PhaseShifterWaveguideTemplate.Layout): core_width = i3.PositiveNumberProperty(doc="width of the core") p_width = i3.PositiveNumberProperty(doc="width of low dose P implant") p_offset = i3.NumberProperty( doc="offset from the center line of low dose P implant") n_width = i3.PositiveNumberProperty(doc="width of low dose N implant") n_offset = i3.NumberProperty( doc="offset from the center line of low dose N implant") high_dose_p_width = i3.PositiveNumberProperty( doc="width of high dose P implant") high_dose_p_offset = i3.NumberProperty( doc="offset from the center line of high dose P implant") high_dose_n_width = i3.PositiveNumberProperty( doc="width of high dose N implant") high_dose_n_offset = i3.NumberProperty( doc="offset from the center line of high dose N implant") con_width = i3.PositiveNumberProperty( doc="width of contacts etch (same on both side of the center line)" ) con_p_offset = i3.NumberProperty( doc="offset from the center line of contacts etch on P implant") con_n_offset = i3.NumberProperty( doc="offset from the center line of contacts etch on N implant") def _default_core_width(self): return 0.45 def _default_p_width(self): return 3. def _default_p_offset(self): return self.p_width / 2. def _default_n_width(self): return self.p_width def _default_n_offset(self): return -self.n_width / 2. def _default_high_dose_p_width(self): return 2. def _default_high_dose_p_offset(self): return self.p_width + self.high_dose_p_width / 2. def _default_high_dose_n_width(self): return self.high_dose_p_width def _default_high_dose_n_offset(self): return -self.high_dose_p_offset def _default_con_width(self): return self.high_dose_p_width / 2. def _default_con_p_offset(self): return self.high_dose_p_offset def _default_con_n_offset(self): return self.high_dose_n_offset def _default_trace_template(self): lv = self.cell.trace_template.get_default_view(self) lv.set(core_width=self.core_width) return lv def _default_trace_template_for_ports(self): port_tt = SiWireWaveguideTemplate( name="{}_wg_port_tt".format(self.name)) port_tt_lv = port_tt.Layout(core_width=self.core_width) return port_tt_lv def _default_implant_windows(self): core_half_width = 0.5 * self.core_width p_half_width = 0.5 * self.p_width n_half_width = 0.5 * self.n_width high_dose_p_half_width = 0.5 * self.high_dose_p_width high_dose_n_half_width = 0.5 * self.high_dose_n_width p_offset = self.p_offset n_offset = self.n_offset high_dose_p_offset = self.high_dose_p_offset high_dose_n_offset = self.high_dose_n_offset windows = [] # core windows.append( i3.PathTraceWindow(layer=i3.TECH.PPLAYER.SI, start_offset=-core_half_width, end_offset=+core_half_width)) # low_dose_p windows.append( i3.PathTraceWindow(layer=i3.TECH.PPLAYER.P, start_offset=p_offset - p_half_width, end_offset=p_offset + p_half_width)) # low_dose_n windows.append( i3.PathTraceWindow(layer=i3.TECH.PPLAYER.N, start_offset=n_offset - n_half_width, end_offset=n_offset + n_half_width)) # high_dose_p windows.append( i3.PathTraceWindow( layer=i3.TECH.PPLAYER.PPLUS, start_offset=high_dose_p_offset - high_dose_p_half_width, end_offset=high_dose_p_offset + high_dose_p_half_width)) # high_dose_n windows.append( i3.PathTraceWindow( layer=i3.TECH.PPLAYER.NPLUS, start_offset=high_dose_n_offset - high_dose_n_half_width, end_offset=high_dose_n_offset + high_dose_n_half_width)) return windows def _default_contact_windows(self): con_half_width = 0.5 * self.con_width con_p_offset = self.con_p_offset con_n_offset = self.con_n_offset windows = [] # Contact on P++ windows.append( i3.ExtendedPathTraceWindow( layer=i3.TECH.PPLAYER.CON, start_offset=con_p_offset - con_half_width, end_offset=con_p_offset + con_half_width, extension=(-0.5, -0.5))) # Contact on N++ windows.append( i3.ExtendedPathTraceWindow( layer=i3.TECH.PPLAYER.CON, start_offset=con_n_offset - con_half_width, end_offset=con_n_offset + con_half_width, extension=(-0.5, -0.5))) return windows def _default_metal_windows(self): windows = [] return windows def _default_windows(self): windows = self.implant_windows + self.contact_windows + self.metal_windows return windows
class Layout(PlaceAndAutoRoute.Layout): delta = i3.PositiveNumberProperty( default=60., doc='Offset of the arm with the 0 (@ 0 delay) [um]') ground_width = i3.PositiveNumberProperty( default=80., doc="Width of exteriors grounds line [um]") signal_width = i3.PositiveNumberProperty( default=10., doc="Width of signal line [um]") metal_spacing = i3.PositiveNumberProperty( default=4., doc="Spacing between 2 metal lines [um]") delay = i3.NumberProperty( default=0., doc= 'Delay between the 2 arms (0< in bottom arm, >0 in top arm) [um]') additional_length = i3.NonNegativeNumberProperty( default=50., doc="Additional length of the metal lines [um]") pad_width = i3.PositiveNumberProperty( default=50., doc='Width of the electrical pads [um]') pad_length = i3.PositiveNumberProperty( default=70., doc='Length of the electrical pads [um]') taper_length = i3.PositiveNumberProperty( default=60., doc='Length of the tapers between the pads and the lines [um]') period_pad = i3.PositiveNumberProperty(default=70., doc='Period of the pads [um]') def _default_modulator(self): lv = self.cell.modulator.get_default_view(self) lv.set(shape=[(0., 0.), (self.modulator_length, 0.)]) return lv def _generate_elements(self, elems): super(PlaceAndAutoRoute.Layout, self)._generate_elements(elems) total_length = self.modulator_length + self.additional_length delta = self.delta delay = self.delay signal_width = self.signal_width metal_spacing = self.metal_spacing ground_width = self.ground_width period_pad = self.period_pad taper_length = self.taper_length pad_length = self.pad_length pad_width = self.pad_width pad_size = (pad_length, pad_width) y_ground = -delta + min( delay / 2., 0.) - signal_width - 1.5 * metal_spacing - ground_width / 2. y_arm1_n = -delta + min( delay / 2., 0.) - signal_width / 2. - metal_spacing / 2. y_arm1_p = delay / 4. - signal_width / 2. - metal_spacing / 2. y_arm2_n = delta + max(delay / 2., 0.) - signal_width / 2. - metal_spacing / 2. y_arm2_p = delta + max(delay / 2., 0.) + ground_width / 2. + metal_spacing / 2. arm2_n_width = 2 * delta + abs( delay / 2.) - signal_width - 2 * metal_spacing # Metal lines lines = [] ground = i3.Rectangle(layer=i3.TECH.PPLAYER.M1, center=(0., y_ground), box_size=(total_length, ground_width)) arm1_n = i3.Rectangle(layer=i3.TECH.PPLAYER.M1, center=(0., y_arm1_n), box_size=(total_length, signal_width)) arm1_p = i3.Rectangle(layer=i3.TECH.PPLAYER.M1, center=(0., y_arm1_p), box_size=(total_length, arm2_n_width)) arm2_n = i3.Rectangle(layer=i3.TECH.PPLAYER.M1, center=(0., y_arm2_n), box_size=(total_length, signal_width)) arm2_p = i3.Rectangle(layer=i3.TECH.PPLAYER.M1, center=(0., y_arm2_p), box_size=(total_length, ground_width)) lines += [ground, arm1_n, arm1_p, arm2_n, arm2_p] # Metal left pads left_pads = [] taper_ground = i3.Boundary( layer=i3.TECH.PPLAYER.M1, shape=[ (-(total_length / 2. + taper_length), y_arm1_p - 2 * period_pad + pad_width / 2.), (-(total_length / 2. + taper_length), y_arm1_p - 2 * period_pad - pad_width / 2.), (-total_length / 2., y_ground - ground_width / 2.), (-total_length / 2., y_ground + ground_width / 2.), ]) taper_arm1_n = i3.Boundary( layer=i3.TECH.PPLAYER.M1, shape=[ (-(total_length / 2. + taper_length), y_arm1_p - period_pad + pad_width / 2.), (-(total_length / 2. + taper_length), y_arm1_p - period_pad - pad_width / 2.), (-total_length / 2., y_arm1_n - signal_width / 2.), (-total_length / 2., y_arm1_n + signal_width / 2.), ]) taper_arm1_p = i3.Wedge( layer=i3.TECH.PPLAYER.M1, begin_coord=(-(total_length / 2. + taper_length), y_arm1_p), end_coord=(-total_length / 2., y_arm1_p), begin_width=pad_width, end_width=arm2_n_width, ) taper_arm2_n = i3.Boundary( layer=i3.TECH.PPLAYER.M1, shape=[ (-(total_length / 2. + taper_length), y_arm1_p + period_pad + pad_width / 2.), (-(total_length / 2. + taper_length), y_arm1_p + period_pad - pad_width / 2.), (-total_length / 2., y_arm2_n - signal_width / 2.), (-total_length / 2., y_arm2_n + signal_width / 2.), ]) taper_arm2_p = i3.Boundary( layer=i3.TECH.PPLAYER.M1, shape=[ (-(total_length / 2. + taper_length), y_arm1_p + 2 * period_pad + pad_width / 2.), (-(total_length / 2. + taper_length), y_arm1_p + 2 * period_pad - pad_width / 2.), (-total_length / 2., y_arm2_p - ground_width / 2.), (-total_length / 2., y_arm2_p + ground_width / 2.), ]) for i in range(-2, 3): left_pads += i3.Rectangle( layer=i3.TECH.PPLAYER.M1, center=( -(total_length / 2. + taper_length + pad_length / 2.), y_arm1_p + i * period_pad), box_size=pad_size) left_pads += [ taper_ground, taper_arm1_n, taper_arm1_p, taper_arm2_n, taper_arm2_p ] # Metal right pads right_pads = [ elt.transform_copy(i3.HMirror(0.)) for elt in left_pads ] elems += lines elems += left_pads elems += right_pads return elems def _generate_ports(self, ports): # Calling super() to inherit ports from non-connected subblocks (i.e., splitter optical input port and combiner optical output port) super(PlaceAndAutoRoute.Layout, self)._generate_ports(ports) modulator_length = self.modulator_length additional_length = self.additional_length pad_length = self.pad_length taper_length = self.taper_length period_pad = self.period_pad y_center_pad = self.delay / 4. - 7. for i in range(5): ports += i3.ElectricalPort( name='elec_left_{}'.format(i + 1), position=( -((modulator_length + additional_length + pad_length) / 2. + taper_length), y_center_pad + (i - 2) * period_pad), layer=i3.TECH.PPLAYER.M1) ports += i3.ElectricalPort( name='elec_right_{}'.format(i + 1), position=( (modulator_length + additional_length + pad_length) / 2. + taper_length, y_center_pad + (i - 2) * period_pad), layer=i3.TECH.PPLAYER.M1) return ports def _default_child_transformations(self): offset_x = self.modulator_length / 2. delta = self.delta delay = self.delay return { 'splitter': i3.Translation((-(50. + offset_x), 0.)), 'arm1': i3.Translation((-offset_x, -delta + (delay / 2. if (delay < 0) else 0.))), 'arm2': i3.Translation((-offset_x, delta + (delay / 2. if (delay >= 0) else 0.))), 'combiner': i3.HMirror(0.) + i3.Translation((50. + offset_x, 0.)) }
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 Layout(i3.LayoutView): wg_path = i3.NumpyArrayProperty( doc="List of coordinates denoting the center path of the waveguide" ) wg_width = i3.NumpyArrayProperty( doc= "List of waveguide widths normal to the path, specified for each point on the path" ) wg_angles = i3.NumpyArrayProperty( doc= "Internal Property!: list of waveguide pointing angles at each path point (rads)" ) start_angle = i3.NumberProperty( doc= "Beginning angle of waveguide for calculating slope (in degrees);" "Note this angle points *into* the waveguide so start_angle=0.0 is equivalent" "to the wg going east") end_angle = i3.NumberProperty( doc="End angle of waveguide for calculating slope (in degrees);" "Note this angle points *out of* the waveguide so end_angle=0.0 is equivalent" "to the waveguide going east") def _default_wg_path(self): sample_wg_path = np.array( [[0.0, 0.0], [5.0, 0.0], [10.0, 0.0], [15.0, 0.0]], np.float_) return sample_wg_path def _default_wg_width(self): width = i3.TECH.WG.CORE_WIDTH size = self.wg_path.shape length = size[0] sample_wg_width = width * np.ones([length], np.float_) return sample_wg_width def _default_wg_angles(self): length, numdim = self.wg_path.shape angle = np.zeros([length - 2], np.float_) for ii in range(length - 2): dy = self.wg_path[ii + 2, 1] - self.wg_path[ii, 1] dx = self.wg_path[ii + 2, 0] - self.wg_path[ii, 0] angle[ii] = np.arctan2(dy, dx) return angle def _default_start_angle(self): start_angle = int(np.round(self.wg_angles[0] / (np.pi / 2))) * 90.0 return start_angle def _default_end_angle(self): size = self.wg_angles.shape length = size[0] end_angle = int(np.round(self.wg_angles[length - 1] / (np.pi / 2))) * 90.0 return end_angle def validate_properties(self): """Check whether the combination of properties is valid.""" return True def _generate_elements(self, elems): # Append start and end angles size = self.wg_width.shape length = size[0] allangles = np.zeros([length], np.float_) allangles[0] = self.start_angle * np.pi / 180.0 allangles[1:(length - 1)] = self.wg_angles allangles[length - 1] = self.end_angle * np.pi / 180.0 # Check block fill layer widths import block_layers as bl block_layers = bl.layers block_widths = bl.widths max_width = 0.0 for ii in range(len(block_widths)): if block_widths[ii] > max_width: max_width = block_widths[ii] # Create coord list for boundaries topcoords = np.zeros([length, 2], np.float_) botcoords = np.zeros([length, 2], np.float_) topbfillcoords = np.zeros([length, 2], np.float_) botbfillcoords = np.zeros([length, 2], np.float_) for ii in range(length): norm = np.array( [-np.sin(allangles[ii]), np.cos([allangles[ii]])], np.float_) topcoords[ ii, :] = self.wg_path[ii, :] + self.wg_width[ii] / 2 * norm botcoords[ ii, :] = self.wg_path[ii, :] - self.wg_width[ii] / 2 * norm topbfillcoords[ii, :] = self.wg_path[ ii, :] + (self.wg_width[ii] / 2 + max_width) * norm botbfillcoords[ii, :] = self.wg_path[ ii, :] - (self.wg_width[ii] / 2 + max_width) * norm allcoords = np.zeros([2 * length, 2], np.float_) allbfillcoords = np.zeros([2 * length, 2], np.float_) allcoords[0:length, :] = topcoords allbfillcoords[:length, :] = topbfillcoords for ii in range(length): allcoords[(length + ii), :] = botcoords[length - (ii + 1), :] allbfillcoords[(length + ii), :] = botbfillcoords[length - (ii + 1), :] wg_coords = [] fill_coords = [] for ii in range(2 * length): wg_coords.append((allcoords[ii, 0], allcoords[ii, 1])) fill_coords.append((allbfillcoords[ii, 0], allbfillcoords[ii, 1])) wg_shape = i3.Boundary(shape=wg_coords, layer=i3.TECH.PPLAYER.WG.COR) # Add wg shape to core layer elems += wg_shape # Add block layers for ii in range(len(block_layers)): fill_shape = i3.Boundary(shape=fill_coords, layer=block_layers[ii]) elems += fill_shape return elems def _generate_ports(self, ports): size = self.wg_width.shape length = size[0] - 1 ports += i3.OpticalPort(name="in", position=(self.wg_path[0, 0], self.wg_path[0, 1]), angle=self.start_angle + 180.0, trace_template=StripWgTemplate().Layout( core_width=self.wg_width[0])) ports += i3.OpticalPort(name="out", position=(self.wg_path[length, 0], self.wg_path[length, 1]), angle=self.end_angle, trace_template=StripWgTemplate().Layout( core_width=self.wg_width[length])) 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=600, doc="Radius of curvature") #spacing =i3.PositiveNumberProperty(default=100, doc="Radius of curvature") spacing = i3.PositiveNumberProperty(default=50, doc="Radius of curvature") n_loops = i3.NumberProperty(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") tlport = i3.PositiveNumberProperty(default=200.0, doc="Transition legth to ports") couplingWG = i3.ChildCellProperty(doc="", locked=True) couplingWG_l = i3.PositiveNumberProperty(default=10000.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) 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 = int(self.n_loops) 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_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 a = 10 b = (self.s_length_vec[-1] - self.s_length_vec[counter]) * 0.1 #b=0 print 'b = ', b #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=( 0.0 - (op[0] - ip[0]) / 2 - self.tlport - a - b, #-self.chip_length/2-self.couplingWG_l, self.n * counter * sx)) d['OutPort' + str(counter)] = i3.Translation(translation=( self.tlport + (op[0] - ip[0]) / 2 + a + b, #self.chip_length/2, 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 total_length = length + self.R * ( 1 + 3.14159) - self.spacing + 4 * self.n_loops * self.spacing print 'total length when inport on top is: ', total_length #print 'child.ports["in"].trace_template.core_width: ', child.ports["in"].trace_template.core_width #i3.TECH.PPLAYER.NONE.LOGOTXT when using isipp50g elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text='Width = {} - Length = {} - R = {}'.format( width, total_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) coordinate=(-(op[0] - ip[0]) / 2 - 1000.0, counter * sx - 100.0), alignment=(i3.TEXT_ALIGN_LEFT, i3.TEXT_ALIGN_LEFT), font=2, height=20.0) return elems
class Layout(i3.LayoutView): cell_trap_length = i3.PositiveNumberProperty(default=300.0, doc="total length") cell_trap_gap = i3.NumberProperty(default=10.0, doc="trap gap") cell_trap_gap_length = i3.NumberProperty(default=60.0, doc="length of trap section") in_angle = i3.NumberProperty(default=85, doc="internal entry angle of the trap ") out_angle = i3.NumberProperty(default=85, doc="internal exit angle of the trap ") radius_fillet = i3.NumberProperty( default=3, doc="radius for rounding internal edges/corners") ''' ----------------------------\ /-------------- in angle \ / outangle \ / |_____| _____ | | / \ in angle / \outangle ____________________________/ \------------------ ''' def _generate_elements(self, elems): cell_trap_width = self.channel_template.channel_width alpha = math.radians(self.in_angle) beta = math.radians(self.out_angle) point_list = [] point_list.append( (-self.cell_trap_length * 0.25, -cell_trap_width * 0.5)) point_list.insert( 0, (-self.cell_trap_length * 0.25, cell_trap_width * 0.5)) point_list.append( (-(cell_trap_width - self.cell_trap_gap) / math.tan(alpha) - self.cell_trap_gap_length * 0.5, -(cell_trap_width * 0.5))) point_list.insert( 0, (-(cell_trap_width - self.cell_trap_gap) / math.tan(alpha) - self.cell_trap_gap_length * 0.5, (cell_trap_width * 0.5))) point_list.append((-self.cell_trap_gap_length * 0.25, -self.cell_trap_gap * 0.5)) # end of array point_list.insert( 0, (-self.cell_trap_gap_length * 0.25, self.cell_trap_gap * 0.5)) # begging of array, position 0 point_list.append( (self.cell_trap_gap_length * 0.25, -self.cell_trap_gap * 0.5)) point_list.insert( 0, (self.cell_trap_gap_length * 0.25, self.cell_trap_gap * 0.5)) point_list.append( ((cell_trap_width - self.cell_trap_gap) / math.tan(beta) + self.cell_trap_gap_length * 0.25, -(cell_trap_width * 0.5))) point_list.insert( 0, ((cell_trap_width - self.cell_trap_gap) / math.tan(beta) + self.cell_trap_gap_length * 0.25, (cell_trap_width * 0.5))) point_list.append( (-self.cell_trap_length * 0.25 + self.cell_trap_length * 0.5, -cell_trap_width * 0.5)) point_list.insert( 0, (-self.cell_trap_length * 0.25 + self.cell_trap_length * 0.5, cell_trap_width * 0.5)) t = i3.Shape(point_list, closed=True) rectang = i3.ShapeRound(original_shape=t, start_face_angle=0, end_face_angle=0, radius=self.radius_fillet) bo = i3.Boundary(self.channel_template.layer, rectang) #insts += bo #comm/uncomm for debugging round stl #creating an inlet rectangle boundary to avoid round corners point_list = [] #w = 3 point_list.append( (-self.cell_trap_length * 0.5, -cell_trap_width * 0.5)) point_list.insert( 0, (-self.cell_trap_length * 0.5, cell_trap_width * 0.5)) point_list.append( (-self.cell_trap_length * 0.25 + self.radius_fillet, -cell_trap_width * 0.5)) point_list.insert(0, (-self.cell_trap_length * 0.25 + self.radius_fillet, cell_trap_width * 0.5)) t = i3.Shape(point_list, closed=True) bo1 = i3.Boundary(self.channel_template.layer, t) #creating an outlet rectangle boundary to avoid round corners point_list = [] point_list.append( (self.cell_trap_length * 0.25 - self.radius_fillet, -cell_trap_width * 0.5)) point_list.insert(0, (self.cell_trap_length * 0.25 - self.radius_fillet, cell_trap_width * 0.5)) point_list.append( (self.cell_trap_length * 0.5, -cell_trap_width * 0.5)) point_list.insert(0, (-self.cell_trap_length * 0.5 + self.cell_trap_length, cell_trap_width * 0.5)) t = i3.Shape(point_list, closed=True) bo2 = i3.Boundary(self.channel_template.layer, t) #boolean operation adding main geometry and inlet rectangle b_add = bo1 | bo #boolean operation adding main geometry and outlet rectangle b_add2 = bo2 | b_add[0] elems += b_add2 return elems def _generate_ports(self, ports): #port1 ports += microfluidics.FluidicPort( name='in1', position=(-self.cell_trap_length * 0.5, 0.0), direction=i3.PORT_DIRECTION.IN, angle_deg=180, trace_template=self.channel_template) ports += microfluidics.FluidicPort( name='out1', position=(self.cell_trap_length * 0.5, 0.0), direction=i3.PORT_DIRECTION.IN, angle_deg=0, trace_template=self.channel_template) return ports
class Layout(CellTrapSimple.Layout): ''' ----------------------------\ /-------------- in angle \ / outangle \ / |_____| |-funnel length-| _____ . . . . . . . . . . _____________________. .__________ ###UNDER CONSTRUCTION ''' funnel_length = i3.NumberProperty(default=100.0, doc="Francisoc: What is this?") def _generate_elements(self, elems): cell_trap_width = self.channel_template.channel_width entry_Lfd = self.cell_trap_length * 0.5 # 1000.0 exit_Lfd = self.cell_trap_length * 0.5 # 500.0 beta = math.radians(self.out_angle) lf = self.funnel_length wi = cell_trap_width wf = self.cell_trap_gap #lfd = 0 taper_samples = 200 a = lf / (wi / wf - 1.0) dx = lf / taper_samples x = 0.0 xl = [] wl = [] point_list = [] x_offset = self.funnel_length + self.cell_trap_gap_length # all expansion will be at 0.0x a = 0.5 b = .35 point_list.append( self.cInp + (-(self.cell_trap_length * b), wi * 0.5)) # end of array point_list.insert(0, self.cInp + (-(self.cell_trap_length * b), -wi * 0.5)) # begging of array, position 0 for i in reversed(range(1, taper_samples + 1)): #xa = lf * math.exp(10.0 * (i / taper_samples - 1.0)) #discretization radius = 0.5 * wi ### this needs to be improved xa = (radius) * math.cos(0.5 * math.pi * i / taper_samples) #w = wi / (1.0 + xa / a) #function value w = (radius) * math.sin(0.5 * math.pi * i / taper_samples) p = (xa - radius - self.cell_trap_gap_length * 0.5, 0.5 * (w + 0.5 * wi)) point_list.append(self.cInp + p) p = (xa - radius - self.cell_trap_gap_length * 0.5, -0.5 * (w + 0.5 * wi)) point_list.insert(0, self.cInp + p) # GAP LENGTH p = ( point_list[-1][0], wf * 0.5 ) #wGet last point, coordX use it for next point with wf as coordY point_list.append(self.cInp + p) # Insert it at the bottom of point_list p = point_list[-1] + (0.0, (-wf)) # Get last point, add -wf point_list.insert(0, self.cInp + p) # Insert it at the bottom of point_list p = point_list[-1] + (self.cell_trap_gap_length, 0.0 ) # Get last point and add length point_list.append(self.cInp + p) # Insert it at [0] in the point_list p = point_list[-1] + (0.0, -wf) # Get last point and add length point_list.insert(0, self.cInp + p) # Insert it at [0] in the point_list # EXIT ANGLE p = point_list[-1] + ( 0.5 * (cell_trap_width - self.cell_trap_gap) / math.tan(beta), -cell_trap_width * 0.5 - wf * .50 ) # Get last point and add length of angle point_list.insert(0, self.cInp + p) # Insert it at [0] in the point_list p = point_list[0] + (0, wi) point_list.append(self.cInp + p) # Insert it at the bottom of point_list # EXIT LENGTH p = (self.cell_trap_length * b, point_list[-1][1] ) # get first point (last point added) and add length point_list.append(self.cInp + p) p = point_list[-1] + (0, -wi) ##get last point and add length point_list.insert(0, self.cInp + p) t = i3.Shape(point_list, closed=True) rectang = i3.ShapeRound(original_shape=t, start_face_angle=0, end_face_angle=0, radius=self.radius_fillet) bo = i3.Boundary(self.channel_template.layer, rectang) #creating an inlet rectangle boundary to avoid round corners point_list = [] point_list.append( (-self.cell_trap_length * a, -cell_trap_width * 0.5)) point_list.insert( 0, (-self.cell_trap_length * a, cell_trap_width * 0.5)) point_list.append((-self.cell_trap_length * b + self.radius_fillet, -cell_trap_width * 0.5)) point_list.insert(0, (-self.cell_trap_length * b + self.radius_fillet, cell_trap_width * 0.5)) t = i3.Shape(point_list, closed=True) bo1 = i3.Boundary(self.channel_template.layer, t) #creating an outlet rectangle boundary to avoid round corners point_list = [] point_list.append((self.cell_trap_length * b - self.radius_fillet, -cell_trap_width * 0.5)) point_list.insert(0, (self.cell_trap_length * b - self.radius_fillet, cell_trap_width * 0.5)) point_list.append( (self.cell_trap_length * a, -cell_trap_width * 0.5)) point_list.insert( 0, (self.cell_trap_length * a, cell_trap_width * 0.5)) t = i3.Shape(point_list, closed=True) bo2 = i3.Boundary(self.channel_template.layer, t) #boolean operation adding main geometry and inlet rectangle b_add = bo | bo1 #boolean operation adding main geometry and outlet rectangle b_add2 = b_add[0] | bo2 #s2 = i3.Structure(elements=b_add2) elems += b_add2 #insts += bo return elems def _generate_ports(self, ports): #port1 ports += microfluidics.FluidicPort( name='in1', position=(-self.cell_trap_length * 0.5, 0.0), direction=i3.PORT_DIRECTION.IN, #angle_deg=0, trace_template=self.channel_template) ports += microfluidics.FluidicPort( name='out1', position=(self.cell_trap_length * 0.5, 0.0), direction=i3.PORT_DIRECTION.IN, #angle_deg=0, trace_template=self.channel_template) return ports
class GratingCoupler1Band(PlaceComponents): core=i3.PositiveNumberProperty(default=3.3, doc="core width") period=i3.PositiveNumberProperty(default=2.566, doc="core width") duty=i3.PositiveNumberProperty(default=0.403, doc="core width") nperiods=i3.PositiveNumberProperty(default=7, doc="core width") wg_coupler= i3.TraceTemplateProperty(doc="Wide trace template used") #FGC= i3.ChildCellProperty(doc="grating used") ctipo=i3.PositiveNumberProperty(default=1, doc="type of coupler used ctipo=1 for grating and ctipo=2 for taper") coupling_l=i3.PositiveNumberProperty(default=5000, doc="length of coupling WG") coupler_template= i3.TraceTemplateProperty(doc="Wide trace template used for coupler tipo2") waveguide_template= i3.TraceTemplateProperty(doc="Wide trace template used on routing") coupling_w = i3.PositiveNumberProperty(default=20, doc="width of the coupling WG") transition_length_coupler=i3.PositiveNumberProperty(default=300, doc="transition length of the coupler") Grating_list = i3.ChildCellListProperty(doc="List containing the non etched parts of gratings") inPort = i3.ChildCellProperty( doc="Used for ports") outPort = i3.ChildCellProperty( doc="Used for ports") incouplingWG = i3.ChildCellProperty( doc="Used for ports") outcouplingWG = i3.ChildCellProperty( doc="Used for ports") chirp=i3.NumberProperty(default=1, doc="1=chirped") socket_length=i3.PositiveNumberProperty(default=2.2, doc="length of coupling WG") layName = i3.StringProperty(default = 'waveguide') layNameg = i3.StringProperty(default = 'metal') print 'the name of the layer is', layName props = i3.DictProperty() def _default_props(self): return AssignProcess(self.layName) propsg = i3.DictProperty() def _default_propsg(self): return AssignProcess(self.layNameg) Lo = i3.ListProperty(default=[]) #Non etched part, illuminated during e-beam Le = i3.ListProperty(default=[]) #Etched part Loc = i3.ListProperty(default=[]) #Non etched part, illuminated during e-beam Lec = i3.ListProperty(default=[]) #Etched part #def _default_transition_length(self): #return self.transition_length_coupler def _default_socket_length(self): a=sum(self.Lo)+sum(self.Le) #if self.chirp==1: #A=a=self.period * int(self.nperiods)+sum(self.Loc)) return a def _default_Loc(self): #Loc=[2.175,2.122,2.07,2.016,1.963,1.908,1.854,1.798,1.743,1.687,1.63,1.573, #1.515,1.457,1.398,1.339,1.279,1.219,1.158,1.096] Loc=self.Loc Loc.reverse() return Loc def _default_Lec(self): #Lec=[0.242,0.301,0.361,0.421,0.482,0.543,0.605,0.667,0.73,0.794,0.858,0.922, #0.988,1.054,1.12,1.187,1.255,1.323,1.392,1.462] Lec=self.Lec Lec.reverse() return Lec def _default_Lo(self): A=[None] * int(self.nperiods) if self.chirp==1: A=[None] * int(self.nperiods+len(self.Loc)) for x in range(0,int(self.nperiods)): A[x]=self.period*self.duty if self.chirp==1: for x in range(0,int(len(self.Loc))): print x print len(self.Loc) A[int(self.nperiods)+x]=self.Loc[x] print 'Lo: ',A return A def _default_Le(self): Le=[None] * int(self.nperiods) if self.chirp==1: Le=[None] * int(self.nperiods+len(self.Loc)) for x in range(0,int(self.nperiods)): Le[x]=self.period*(1-self.duty) if self.chirp==1: for x in range(0,int(len(self.Loc))): Le[int(self.nperiods)+x]=self.Lec[x] print 'Le: ',Le return Le def _default_Grating_list(self): Grating_list = [] for x in range(0,int(len(self.Lo))): #rect=i3.Waveguide(trace_template=self.wg_coupler) rect=i3.Waveguide(trace_template=self.coupler_template) #layout_rect = rect.Layout(shape=[(0.0, 0.0),(self.Lo[x],0.0)])#.visualize(annotate=True) layout_rect = rect.Layout(shape=[(0.0, 0.0),(self.Lo[x],0.0)])#.visualize(annotate=True) Grating_list.append(rect) return Grating_list def _default_incouplingWG(self): rect=i3.Waveguide(trace_template=self.wg_coupler) layout_rect = rect.Layout(shape=[(0.0, 0.0),(self.coupling_l,0.0)] ) return rect def _default_outcouplingWG(self): rect=i3.Waveguide(trace_template=self.wg_coupler) layout_rect = rect.Layout(shape=[(0.0, 0.0),(50+self.socket_length,0.0)] ) return rect def _default_inPort(self): Port=AutoTransitionPorts(contents=self.incouplingWG, port_labels=["in"], trace_template=self.waveguide_template) layout_Port = Port.Layout(transition_length=self.transition_length_coupler)#.visualize(annotate=True) return Port def _default_outPort(self): Port=AutoTransitionPorts(contents=self.outcouplingWG, port_labels=["in"], trace_template=self.wg_coupler) layout_Port = Port.Layout(transition_length=10)#.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, pillar in enumerate(self.Grating_list): child_cells['pillar{}'.format(counter)] = pillar print pillar print 'name of pillar:', pillar.name child_cells['InPort'] = self.inPort child_cells['OutPort']= self.outPort print 'child_cells:', child_cells return child_cells def _default_props(self): return AssignProcess(self.layName) def _default_wg_coupler(self): wg_coupler = WireWaveguideTemplate() wg_coupler.Layout(core_width=self.coupling_w, cladding_width=self.coupling_w+2*8, **self.props) return wg_coupler def _default_waveguide_template(self): wg_t = WireWaveguideTemplate() wg_t_layout=wg_t.Layout(core_width=self.core, cladding_width=self.core+2*8, **self.props) return wg_t def _default_coupler_template(self): wg_t = WireWaveguideTemplate() wg_t_layout=wg_t.Layout(core_width=self.coupling_w, cladding_width=self.coupling_w, **self.propsg) return wg_t def _default_trace_template(self): return self.waveguide_template def _default_contents(self): return self.FGC def _default_port_labels(self): return ["out"] class Layout(PlaceComponents.Layout): #def _default_transition_length(self): #return self.transition_length_coupler def _default_child_transformations(self): d={} position=0 for counter, child in enumerate(self.Grating_list): d['pillar{}'.format(counter)] = i3.Translation(translation=(position, 0.0)) position=position+self.Lo[counter]+self.Le[counter] print 'pillar position: ', position print 'counter= ', counter print 'Lo: ', self.Lo[counter] print 'Le: ', self.Le[counter] #d['InPort'] = i3.HMirror()+ i3.Translation(translation=(position+10,0)) d['InPort'] = i3.HMirror()+ i3.Translation(translation=(self.coupling_l+self.socket_length,0)) d['OutPort'] = i3.Translation(translation=(-50,0.0)) return d
class AligningMarks(PlaceComponents): tt_cross = i3.TraceTemplateProperty(doc="Wide trace template used for the contact pads") hline = i3.ChildCellProperty(doc="Horizontal line", locked=True) vline = i3.ChildCellProperty(doc="Vertical", locked=True) square = i3.ChildCellProperty(doc="Vertical", locked=True) size=i3.PositiveNumberProperty(default=250.0, doc="bigger dimension of aligning mark") width=i3.PositiveNumberProperty(default=20.0, doc="smaller dimension of aligning mark") #Pitch1=i3.PositiveNumberProperty(default=2.0, doc="pitch grating 1") separation=i3.NumberProperty(default=100.0, doc="separation between cross and gratings") #dc=i3.PositiveNumberProperty(default=0.47, doc="duty cycle") layName = i3.StringProperty(default = 'metal2') props = i3.DictProperty() def _default_props(self): return AssignProcess(self.layName) def _default_tt_cross(self): tt_w = WireWaveguideTemplate() tt_w.Layout(core_width=(self.width), cladding_width=(self.width), **self.props ) return tt_w def _default_hline(self): rect=i3.Waveguide(trace_template=self.tt_cross) layout_rect = rect.Layout(shape=[(0.0, self.size/2.0),(self.size,self.size/2.0)]) print 'The trace template of the hline of the cross ', rect.trace_template return rect def _default_vline(self): rect=i3.Waveguide(trace_template=self.tt_cross) layout_rect = rect.Layout(shape=[(self.size/2.0, 0.0),(self.size/2.0,self.size)]) print 'The trace template of the vline of the cross ', rect.trace_template return rect def _default_square(self): #square=Square(size=self.width, layName=self.layName) square=Square(size=self.width, layName='metal') #square.Layout().visualize() return square def _default_child_cells(self): child_cells={"S1" : self.square, "S2" : self.square, "S3" : self.square, "S4" : self.square, "V" : self.vline, "H" : self.hline } return child_cells class Layout(PlaceComponents.Layout): def _default_child_transformations(self): child_transformations={"S1" : i3.Translation(translation=(-(self.width+5.0),(self.width+5.0))), "S2" : i3.Translation(translation=((self.width+5.0),(self.width+5.0))), "S3" : i3.Translation(translation=(-(self.width+5.0),-(self.width+5.0))), "S4" : i3.Translation(translation=((self.width+5.0),-(self.width+5.0))), "V" : i3.Translation(translation=(0.0, 0.0))+ i3.Translation(translation=(-(self.size)/2.0,-(self.size)/2.0)), "H" : i3.Translation(translation=(0.0, 0.0))+ i3.Translation(translation=(-(self.size)/2.0,-(self.size)/2.0)) } return child_transformations
class CircuitOfBlocks(microfluidics.PlaceAndAutoRoute): """Parametric cell with several traps, which are stacked vertically (Parallel) or horizontally (Series) """ #_name_prefix = "circuitOfTraps" type = i3.NumberProperty(default=0) # 0 Paralell 1 Series block_distance = i3.NumberProperty(default=400.) #distance betwn traps block_with_tee = i3.ChildCellListProperty( ) # Generating traps with Tee from Child Cell List Property trace_template = microfluidics.ChannelTemplateProperty( default=microfluidics.ShortChannelTemplate()) n_blocks_x = i3.PositiveIntProperty(default=30) n_blocks_y = i3.PositiveIntProperty(default=30) x_footprint = i3.PositiveIntProperty(default=1000) y_footprint = i3.PositiveIntProperty(default=1000) def _default_child_cells(self): return { "blk_w_tee{}".format(cnt): self.block_with_tee[cnt] for cnt in range(self.n_blocks_x * self.n_blocks_y) } def _default_links(self): links = [] bx = self.n_blocks_x ######################## ############# PARALLEL if self.type == 0: for cntx in range(0, self.n_blocks_x): # self connection at the top -bypass top = self.n_blocks_y * self.n_blocks_x - self.n_blocks_x + cntx links.append(("blk_w_tee{}:in2".format(top), "blk_w_tee{}:out2".format(top))) # connecting bottom to top for cnty in range(0, self.n_blocks_y - 1): links.append( ("blk_w_tee{}:in2".format(cnty * bx + cntx), "blk_w_tee{}:in1".format((cnty + 1) * bx + cntx))) links.append( ("blk_w_tee{}:out2".format(cnty * bx + cntx), "blk_w_tee{}:out1".format((cnty + 1) * bx + cntx))) #interconnecting horizontally if self.n_blocks_x > 1: for cntd in range(0, self.n_blocks_x - 1): links.append(("blk_w_tee{}:out1".format(cntd), "blk_w_tee{}:in1".format(cntd + 1))) ######## END PARALLEL ################# ######################## ############# SERIES if self.type == 1: # series #all by-pass, each object for cnt in range(0, self.n_blocks_y * self.n_blocks_x): #all bypass links.append(("blk_w_tee{}:in2".format(cnt), "blk_w_tee{}:out2".format(cnt))) #all interconnecting horizontally if self.n_blocks_x > 1: for cnty in range(0, self.n_blocks_y): for cntd in range(0, (self.n_blocks_x - 1)): links.append( ("blk_w_tee{}:out1".format(cnty * bx + cntd), "blk_w_tee{}:in1".format(cnty * bx + cntd + 1))) #left connections for cntd in range(1, (self.n_blocks_y - 1), 2): #links.append(("blk_w_tee{}:in1".format(cntd * bx), "blk_w_tee{}:in1".format((cntd+1) * bx))) links.append(("blk_w_tee{}:in1".format( (cntd + 1) * bx), "blk_w_tee{}:in1".format(cntd * bx))) #right connections for cntd in range(1, (self.n_blocks_y), 2): links.append(("blk_w_tee{}:out1".format(cntd * bx - 1), "blk_w_tee{}:out1".format((cntd + 1) * bx - 1))) ######## END SERIES ################# return links def _default_block_with_tee( self): # Generating traps from Child Cell List Property tee1 = TeeSimple() tee1.Layout(tee_length=(200.0)) my_block = CellTrapSimple() my_block.Layout(cell_trap_length=300.0) return [ TrapWithTees( name="blk_w_tee_{}".format(cnt), trap=my_block, tee=tee1, ) for cnt in range(self.n_blocks_x * self.n_blocks_y) ] class Layout(microfluidics.PlaceAndAutoRoute.Layout): def _default_child_transformations(self): # generate grid x = np.linspace(0, self.cell.x_footprint, self.cell.n_blocks_x) y = np.linspace(0, self.cell.y_footprint, self.cell.n_blocks_y) # generate positions from functions.position_coordinates import generate_positions coords = generate_positions(x, y, self.type) return { "blk_w_tee{}".format(cnt): i3.Translation(coords[cnt]) for cnt in range(self.n_blocks_x * self.n_blocks_y) } def _generate_ports(self, ports): # Add ports if self.type == 0: ports += i3.expose_ports( self.instances, { 'blk_w_tee0:in1': 'in1', 'blk_w_tee{}:out1'.format(self.n_blocks_x - 1): 'out1' }) if self.type == 1: if self.n_blocks_y % 2 == 0: ports += i3.expose_ports( self.instances, { 'blk_w_tee0:in1': 'in1', 'blk_w_tee{}:in1'.format(self.n_blocks_x * self.n_blocks_y - self.n_blocks_x): 'out1' # last left }) else: ports += i3.expose_ports( self.instances, { 'blk_w_tee0:in1': 'in1', 'blk_w_tee{}:out1'.format(self.n_blocks_x * self.n_blocks_y - 1): 'out1' # last right }) return ports
class Lspiral(PlaceAndAutoRoute): rectV_list = i3.ChildCellListProperty(default=[]) rectH_list = i3.ChildCellListProperty(default=[]) gap_inc_vec = i3.ListProperty(default=[], doc="Length of MMI") gap_inc_vec2 = i3.ListProperty(default=[], doc="length of 12mmi") WG1 = i3.ChildCellProperty(doc="", locked=True) WG2 = i3.ChildCellProperty() wg_t1 = i3.WaveguideTemplateProperty(doc="board WG") wg_t2 = i3.WaveguideTemplateProperty(doc="change WG") # mmi_trace_template = i3.WaveguideTemplateProperty() # mmi_access_template = i3.WaveguideTemplateProperty() width = i3.PositiveNumberProperty(doc="width of ports", default=15) # cleave = i3.PositiveNumberProperty(doc="tolerance", default=150) offset = i3.NumberProperty(doc="count", default=0) def _default_wg_t1(self): wg_t1 = WireWaveguideTemplate(name="port20") wg_t1.Layout( core_width=20, cladding_width=20 + 2 * 12.0, ) return wg_t1 def _default_wg_t2(self): wg_t2 = WireWaveguideTemplate( name="port_{}_{}".format(str(self.width), str(self.offset))) wg_t2.Layout( core_width=self.width, cladding_width=self.width + 2 * 12.0, ) return wg_t2 def _default_trace_template(self): wg_sm = WireWaveguideTemplate(name="sm_template") wg_sm.Layout(core_width=3.8, cladding_width=3.8 + 2 * 12.0) return wg_sm def _default_WG1(self): WG1 = i3.Waveguide(name="straight{}".format(str(self.width)), trace_template=self.wg_t1) WG1.Layout(shape=[(-200.0, 0.0), (600.0, 0.0)]) return WG1 def _default_WG2(self): Port = AutoTransitionPorts(name="ports{}".format(str(self.width)), contents=self.WG1, port_labels=["out"], trace_template=self.wg_t2) Port.Layout(transition_length=300) # .visualize(annotate=True) return Port # def _default_mmi_trace_template(self): # mmi_trace_template = WireWaveguideTemplate(name="MMI_tt") # mmi_trace_template.Layout(core_width=20.0, cladding_width=20.0 + 2 * 12) # MMI_width # return mmi_trace_template # # def _default_mmi_access_template(self): # mmi_access_template = WireWaveguideTemplate(name="MMI_at") # mmi_access_template.Layout(core_width=9.0, cladding_width=9.0 + 2 * 12) # return mmi_access_template def _default_rectH_list(self): MMI22_list = [] for l in range(0, 5, 1): cell = i3.Waveguide(name="rectH{}_{}".format( str(l), str(self.offset)), trace_template=self.wg_t2) cell.Layout(shape=[(0.0, 0.0), (17000 - 4000 * l, 0.0)]) # cell = i3.Waveguide(name="rectV{}".format(str(l)), # trace_template=self.wg_t2) # cell.Layout(shape=[(18000 - 4000 * l, 0.0), (0.0, 0.0)]) MMI22_list.append(cell) return MMI22_list def _default_rectV_list(self): print '____________ MMI 2x2 ______________' MMI22_list2 = [] for l in range(0, 5, 1): # cell = i3.Waveguide(name="rectH{}".format(str(l)), # trace_template=self.wg_t2) # cell.Layout(shape=[(0.0, 0.0), (18000-4000*l, 0.0)]) cell = i3.Waveguide(name="rectV{}_{}".format( str(l), str(self.offset)), trace_template=self.wg_t2) cell.Layout(shape=[(0.0, 0.0), (0.0, -1000 - 4000 * l)]) MMI22_list2.append(cell) return MMI22_list2 def _default_child_cells(self): child_cells = dict() for counter in range(0, 5, 1): print counter # child_cells['straight' + str(counter)] = self.WG1 child_cells['taperH' + str(counter)] = self.WG2 child_cells['taperV' + str(counter)] = self.WG2 for counter, child in enumerate(self.rectH_list): print 'child number' + str(counter) child_cells['recH' + str(counter)] = child print 'child name ' + str(child.name) for counter, child in enumerate(self.rectV_list): print 'child number' + str(counter) child_cells['recV' + str(counter)] = child print 'child name ' + str(child.name) return child_cells def _default_links(self): links = [ ("recH0:out", "recV4:out"), ("recH1:out", "recV3:out"), ("recH2:out", "recV2:out"), ("recH3:out", "recV1:out"), ("recH4:out", "recV0:out"), ] return links class Layout(PlaceAndAutoRoute.Layout): def _default_child_transformations(self): trans = dict() column = 4000 # trans['ring0'] = (600, 0) # trans['ring1'] = (600, 1 * column) # trans['ring2'] = (600, 2 * column) # # trans['ring3'] = (1300, 8000 + 2 * column ) for i in range(0, 5, 1): trans["taperH{}".format(i)] = (0, column * i + 100 * self.offset) trans["taperV{}".format(i)] = i3.Rotation( rotation=-90) + i3.Translation( (4000 + column * i - 100 * self.offset, 20000)) trans["recH{}".format(i)] = (900, column * i + 100 * self.offset) trans["recV{}".format(i)] = (4000 + column * i - 100 * self.offset, 20000 - 900) # trans["taper0"] = (0, 0) # trans["taper1"] = i3.HMirror(0) + i3.Translation((2500, -1500)) # trans["taper2"] = i3.HMirror(0) + i3.Translation((2500, 1500)) # # trans["taper3"] = (0, 1 * column) # trans["taper4"] = i3.HMirror(0) + i3.Translation((2500, -1500 + 1 * column)) # # trans["taper5"] = i3.HMirror(0) + i3.Translation((2500, 1500 + 1 * column)) # # trans["taper6"] = (0, 2 * column) # trans["taper7"] = i3.HMirror(0) + i3.Translation((2500, -1500 + 2 * column)) # trans["taper8"] = i3.HMirror(0) + i3.Translation((2500, 1500 + 2 * column)) # # trans["taper9"] = (0, 3800 + 2 * column) # # trans["taper10"] = i3.HMirror(0) + i3.Translation((2500, -1500 + 3 * column)) # # trans["taper11"] = i3.HMirror(0) + i3.Translation((3000, 6500+ 2 * column)) # # trans["taper12"] = i3.HMirror(0) + i3.Translation((3000, 9500+ 2 * column)) return trans def _default_bend_radius(self): bend_radius = 500 return bend_radius def _generate_elements(self, elems): for i in range(0, 5, 1): elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text="WITDH={}_length={}*2_R=500".format( str(self.width), 17000 - 4000 * i), coordinate=(900, 55 + 4000 * i + 100 * self.offset), font=2, height=20.0, ) for j in range(0, 13, 1): elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text="{}_{}".format(str(i), str(j)), coordinate=(550, 63 + 4000 * i + 100 * j), font=2, height=30.0, ) elems += i3.PolygonText( layer=i3.TECH.PPLAYER.WG.TEXT, text="{}_{}".format(str(i), str(j)), # coordinate=(), font=2, height=30.0, transformation=i3.Rotation(rotation=-90) + i3.Translation( (20000 - 4000 * i - 100 * j + 63, 20000 - 550)), ) for i in range(0, 6, 1): elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(100, 4000 * i - 1000 + 700), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(300, 4000 * i - 1000 + 700), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(4000 * i + 300, 19700), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(4000 * i + 300, 19900), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(100, 19900), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(-100, 19900), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(500, 19900), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(100, 19500), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(100, 20100), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(-100, -500), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(100, -500), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(300, -500), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(500, -500), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(20500, 19500), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(20500, 19700), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(20500, 19900), box_size=(100, 100)) elems += i3.Rectangle(layer=i3.TECH.PPLAYER.WG.TEXT, center=(20500, 20100), box_size=(100, 100)) return elems