def _draw_padded_via(self): viaref=DeviceReference(self.via.draw()) size=float(self.via.size*self.over_via) port=viaref.ports['conn'] trace=pg.rectangle(size=(size,size),layer=self.pad_layers[0]) trace.move(origin=trace.center,\ destination=viaref.center) trace2=pg.copy_layer(trace,layer=self.pad_layers[0],new_layer=self.pad_layers[1]) cell=Device(self.name) cell.absorb(cell<<trace) cell.absorb(cell<<trace2) cell.add(viaref) port.midpoint=(port.midpoint[0],cell.ymax) port.width=size cell.add_port(port) if bottom_conn==False: cell.remove_layers(layers=[self.pad_layers[1]]) return cell
def point_path(points=[(0, 0), (4, 0), (4, 8)], width=1, layer=0): points = np.asarray(points) dxdy = points[1:] - points[:-1] angles = (np.arctan2(dxdy[:, 1], dxdy[:, 0])).tolist() angles = np.array([angles[0]] + angles + [angles[-1]]) diff_angles = (angles[1:] - angles[:-1]) mean_angles = (angles[1:] + angles[:-1]) / 2 dx = width / 2 * np.cos((mean_angles - pi / 2)) / np.cos((diff_angles / 2)) dy = width / 2 * np.sin((mean_angles - pi / 2)) / np.cos((diff_angles / 2)) left_points = points.T - np.array([dx, dy]) right_points = points.T + np.array([dx, dy]) all_points = np.concatenate([left_points.T, right_points.T[::-1]]) D = Device() D.add_polygon(all_points, layer=layer) D.add_port(name=1, midpoint=points[0], width=width, orientation=angles[0] * 180 / pi + 180) D.add_port(name=2, midpoint=points[-1], width=width, orientation=angles[-1] * 180 / pi) return D # quickplot(point_path())
def draw(self): ''' Generates layout cell based on current parameters. 'conn' port is included in the cell. Returns ------- cell : phidl.Device. ''' o = self.origin pad=pg.rectangle(size=self.size.coord,\ layer=self.layer).move(origin=(0,0),\ destination=o.coord) cell = Device(self.name) r1 = cell << pad cell.absorb(r1) r2 = cell << pad r2.move(origin=o.coord,\ destination=(o+self.distance).coord) cell.absorb(r2) cell.add_port(name='conn',\ midpoint=(o+Point(self.size.x/2,self.size.y)).coord,\ width=self.size.x,\ orientation=90) del pad return cell
def _arc(radius=10, width=0.5, theta=45, start_angle=0, angle_resolution=2.5, layer=0): """ Creates an arc of arclength ``theta`` starting at angle ``start_angle`` """ inner_radius = radius - width / 2 outer_radius = radius + width / 2 angle1 = (start_angle) * pi / 180 angle2 = (start_angle + theta) * pi / 180 t = np.linspace(angle1, angle2, np.ceil(abs(theta) / angle_resolution)) inner_points_x = (inner_radius * cos(t)).tolist() inner_points_y = (inner_radius * sin(t)).tolist() outer_points_x = (outer_radius * cos(t)).tolist() outer_points_y = (outer_radius * sin(t)).tolist() xpts = inner_points_x + outer_points_x[::-1] ypts = inner_points_y + outer_points_y[::-1] D = Device('arc') D.add_polygon(points=(xpts, ypts), layer=layer) D.add_port(name=1, midpoint=(radius * cos(angle1), radius * sin(angle1)), width=width, orientation=start_angle - 90 + 180 * (theta < 0)) D.add_port(name=2, midpoint=(radius * cos(angle2), radius * sin(angle2)), width=width, orientation=start_angle + theta + 90 - 180 * (theta < 0)) D.info['length'] = (abs(theta) * pi / 180) * radius return D
def draw(self): cell = Device(name=self.name) lfe_cell = LFERes.draw(self) cell.add_ref(lfe_cell, alias='TopCell') idt_bottom = copy(self.idt) idt_bottom.layer = self.bottom_layer idt_ref = cell.add_ref(idt_bottom.draw(), alias='BottomIDT') p_bott = idt_ref.ports['bottom'] p_bott_coord = Point(p_bott.midpoint) idt_ref.mirror(p1=(p_bott_coord-Point(p_bott.width/2,0)).coord,\ p2=(p_bott_coord+Point(p_bott.width/2,0)).coord) idt_ref.move(origin=(idt_ref.xmin,idt_ref.ymax),\ destination=(idt_ref.xmin,idt_ref.ymax+self.idt.length+self.idt.y_offset)) bus_bottom = copy(self.bus) bus_bottom.layer = self.bottom_layer bus_ref = cell.add_ref(bus_bottom.draw(), alias='BottomBus') bus_ref.move(origin=(0,0),\ destination=(0,-self.bus.size.y)) anchor_bottom = copy(self.anchor) anchor_bottom.layer = self.bottom_layer anchor_bottom.etch_choice = False anchor_ref = cell.add_ref(anchor_bottom.draw(), alias="BottomAnchor_Top") anchor_ref.connect(anchor_ref.ports['conn'],\ destination=idt_ref.ports['top'],\ overlap=-self.bus.size.y) anchor_ref_2 = cell.add_ref(anchor_bottom.draw(), alias="BottomAnchor_Bottom") anchor_ref_2.connect(anchor_ref_2.ports['conn'],\ destination=idt_ref.ports['bottom'],\ overlap=-self.bus.size.y) for p_value in lfe_cell.ports.values(): cell.add_port(p_value) return cell
def draw(self): def mirror_label(str): if 'N_' in str: return str.replace('N_', 'S_') if 'E_' in str: return str.replace('E_', 'W_') if 'S_' in str: return str.replace('S_', 'N_') if 'W_' in str: return str.replace('W_', 'E_') return str cell = Device(name=self.name) probe_cell = super().draw() p1 = cell.add_ref(probe_cell, alias='Port1') p2 = cell.add_ref(probe_cell, alias='Port2') p2.rotate(center=(p1.x, p1.ymax), angle=180) p2.move(destination=self.offset.coord) for n, p in p1.ports.items(): cell.add_port(port=p, name=n + '_1') for n, p in p2.ports.items(): if 'LX' in n: cell.add_port(port=p, name=mirror_label(n + '_2').replace( 'LX', 'RX')) else: if 'RX' in n: cell.add_port(port=p, name=mirror_label(n + '_2').replace( 'RX', 'LX')) else: cell.add_port(port=p, name=mirror_label(n + '_2')) return cell
def draw(self): ''' Generates layout cell based on current parameters. 'top' and 'bottom' ports are included in the cell. Returns ------- cell : phidl.Device. ''' unitcell = self._draw_unit_cell() cell = Device(self.name) cell.name = self.name cell.add_array(unitcell,columns=self.n,rows=1,\ spacing=(self.pitch*2,0)) cell.flatten() totx = self.pitch * (self.n * 2 + 1) - self.pitch * (1 - self.coverage) midx = totx / 2 finger_dist=Point(self.pitch*1,\ self.length+self.y_offset) cell = join(cell) cell.name = self.name cell.add_port(Port(name='bottom',\ midpoint=(self.origin+\ Point(midx,0)).coord,\ width=totx, orientation=-90)) cell.add_port(Port(name='top',\ midpoint=(self.origin+\ Point(midx,self.length+self.y_offset)).coord,\ width=totx, orientation=90)) del unitcell return cell
def attach_taper(cell : Device , port : Port , length : float , \ width2 : float, layer=LayoutDefault.layerTop) : t = pg.taper(length=length, width1=port.width, width2=width2, layer=layer) t_ref = cell.add_ref(t) t_ref.connect(1, destination=port) new_port = t_ref.ports[2] new_port.name = port.name cell.absorb(t_ref) cell.remove(port) cell.add_port(new_port)
def draw(self): ''' Generates layout cell based on current parameters. 'top' and 'bottom' ports is included in the cell. Returns ------- cell : phidl.Device. ''' o = self.origin b_main = Bus() b_main.origin = o b_main.layer = self.layer b_main.size = Point(self.x, self.active_area.y) b_main.distance = Point(self.active_area.x + self.x, 0) main_etch = b_main.draw() etch = Device(self.name) etch.absorb(etch << main_etch) port_up=Port('top',\ midpoint=(o+Point(self.x+self.active_area.x/2,self.active_area.y)).coord,\ width=self.active_area.x,\ orientation=-90) port_down=Port('bottom',\ midpoint=(o+Point(self.x+self.active_area.x/2,0)).coord,\ width=self.active_area.x,\ orientation=90) etch.add_port(port_up) etch.add_port(port_down) del main_etch return etch
def draw(self): cell=Device(name=self.name) super_ref=cell.add_ref(cls.draw(self)) nvias_x,nvias_y=self.n_vias unit_cell=self._draw_padded_via() viacell=join(CellArray(unit_cell,\ columns=nvias_x,rows=nvias_y,\ spacing=(unit_cell.xsize,unit_cell.ysize))) viacell.add_port(Port(name='conn',\ midpoint=(viacell.x,viacell.ymax),\ width=viacell.xsize,\ orientation=90)) for sides in side: for p_name in super_ref.ports.keys(): if re.search(sides,p_name): p=super_ref.ports[p_name] pad=pg.compass(size=(p.width,self.via_distance),layer=self.pad_layers[0]) if sides=='top': self._attach_instance(cell, pad, pad.ports['S'], viacell,p) if sides=='bottom': self._attach_instance(cell, pad, pad.ports['N'], viacell,p) for p_name,p_value in super_ref.ports.items(): cell.add_port(p_value) return cell
def draw(self): cell=Device() cell.name=self.name d_ref=cell.add_ref(cls.draw(self)) for name,port in d_ref.ports.items(): self.pad.port=port pad_ref=cell.add_ref(self.pad.draw()) pad_ref.connect(pad_ref.ports['conn'], destination=port) cell.absorb(pad_ref) cell.add_port(port,name) return cell
def draw(self): name = self.name o = self.origin pad_x = self.size.x if pad_x > self.pitch * 2 / 3: pad_x = self.pitch * 2 / 3 warnings.warn("Pad size too large, capped to pitch*2/3") pad_cell=pg.rectangle(size=(pad_x,self.size.y),\ layer=self.layer) pad_cell.move(origin=(0,0),\ destination=o.coord) cell = Device(self.name) dp = Point(self.pitch, 0) pad_gnd_sx = cell << pad_cell pad_sig = cell << pad_cell pad_sig.move(origin=o.coord,\ destination=(o+dp).coord) cell.add_port(Port(name='gnd_left',\ midpoint=(o+Point(pad_x/2+self.pitch,self.size.y)).coord,\ width=pad_x,\ orientation=90)) cell.add_port(Port(name='sig',\ midpoint=(o+Point(pad_x/2,self.size.y)).coord,\ width=pad_x,\ orientation=90)) return cell
def from_phidl(component: Device, **kwargs) -> Component: """Returns gf.Component from a phidl Device or function""" device = call_if_func(component, **kwargs) component = Component(name=device.name) for ref in device.references: new_ref = ComponentReference( component=ref.parent, origin=ref.origin, rotation=ref.rotation, magnification=ref.magnification, x_reflection=ref.x_reflection, ) new_ref.owner = component component.add(new_ref) for alias_name, alias_ref in device.aliases.items(): if alias_ref == ref: component.aliases[alias_name] = new_ref for p in device.ports.values(): component.add_port( port=Port( name=p.name, midpoint=p.midpoint, width=p.width, orientation=p.orientation, parent=p.parent, ) ) for poly in device.polygons: component.add_polygon(poly) for label in device.labels: component.add_label( text=label.text, position=label.position, layer=(label.layer, label.texttype), ) return component
def add_compass(device: Device) -> Device: ''' add four ports at the bbox of a cell. Parameters ---------- device : phidl.Device Returns ------- device : phidl.Device. ''' bound_cell=pg.compass(size=device.size).move(\ origin=(0,0),destination=device.center) ports = port = bound_cell.get_ports() device.add_port(port=ports[0], name='N') device.add_port(port=ports[1], name='S') device.add_port(port=ports[2], name='E') device.add_port(port=ports[3], name='W') return device
def draw(self): cell = Device() cell.name = self.name supercell = LFERes.draw(self) super_ref = cell.add_ref(supercell) if self.plate_position == 'out, short': plate=pg.rectangle(size=(self.etchpit.active_area.x+8*self.idt.active_area_margin,self.idt.length-self.idt.y_offset/2),\ layer=self.platelayer) plate_ref = cell.add_ref(plate, alias='Plate') transl_rel=Point(self.etchpit.x-4*self.idt.active_area_margin,self.anchor.size.y+2*self.anchor.etch_margin.y+self.bus.size.y\ +self.idt.y_offset*3/4) lr_cell = get_corners(cell)[0] lr_plate = get_corners(plate_ref)[0] plate_ref.move(origin=lr_plate.coord,\ destination=(lr_plate+lr_cell+transl_rel).coord) cell.absorb(plate_ref) del plate elif self.plate_position == 'in, short': plate=pg.rectangle(\ size=(\ self.etchpit.active_area.x-\ 2*self.idt.active_area_margin,\ self.idt.length-self.idt.y_offset/2),\ layer=self.platelayer) plate_ref = cell.add_ref(plate, alias='Plate') transl_rel=Point(self.etchpit.x+\ self.idt.active_area_margin,\ self.anchor.size.y+\ 2*self.anchor.etch_margin.y+\ self.bus.size.y+\ self.idt.y_offset*3/4) lr_cell = get_corners(cell)[0] lr_plate = get_corners(plate_ref)[0] plate_ref.move(origin=lr_plate.coord,\ destination=(lr_plate+lr_cell+transl_rel).coord) cell.absorb(plate_ref) del plate elif self.plate_position == 'out, long': plate=pg.rectangle(\ size=(self.etchpit.active_area.x+\ 8*self.idt.active_area_margin,\ self.idt.length+\ 2*self.bus.size.y+\ self.idt.y_offset),\ layer=self.platelayer) plate_ref = cell.add_ref(plate, alias='Plate') transl_rel=Point(self.etchpit.x-\ 4*self.idt.active_area_margin,\ self.anchor.size.y+2*self.anchor.etch_margin.y) lr_cell = get_corners(cell)[0] lr_plate = get_corners(plate_ref)[0] plate_ref.move(origin=lr_plate.coord,\ destination=(lr_plate+lr_cell+transl_rel).coord) cell.absorb(plate_ref) del plate elif self.plate_position == 'in, long': plate=pg.rectangle(\ size=(\ self.etchpit.active_area.x-\ 2*self.idt.active_area_margin,\ self.idt.length+\ 2*self.bus.size.y+\ self.idt.y_offset), layer=self.platelayer) plate_ref = cell.add_ref(plate, alias='Plate') transl_rel=Point(self.etchpit.x+\ self.idt.active_area_margin,\ self.anchor.size.y+2*self.anchor.etch_margin.y) lr_cell = get_corners(cell)[0] lr_plate = get_corners(plate_ref)[0] plate_ref.move(origin=lr_plate.coord,\ destination=(lr_plate+lr_cell+transl_rel).coord) cell.absorb(plate_ref) del plate for name, port in supercell.ports.items(): cell.add_port(port, name) return cell
from phidl import set_quickplot_options set_quickplot_options(blocking=True) distance = 50 width = 10 spacing = 50 n = 4 d = Device() for i in range(n): d.add_port( Port( midpoint=(spacing * i, 0), # midpoint=(0,spacing*i), width=width, orientation=90, name='top_' + str(i))) connector = connect_ports(d, tag='top', distance=distance) d << connector for p in connector.get_ports(depth=0): d.add_port(connector.ports[p.name]) qp(d)
def draw(self): self._set_relations() idt_cell = self.idt.draw() cell = Device(self.name) idt_ref = cell.add_ref(idt_cell, alias="IDT") idt_top_port = idt_ref.ports['top'] idt_bottom_port = idt_ref.ports['bottom'] bus_cell = self.bus.draw() bus_ref = cell.add_ref(bus_cell, alias="BUS") bus_ref.connect(port=bus_cell.ports['conn'],\ destination=idt_bottom_port) etch_cell = self.etchpit.draw() etch_ref = cell.add_ref(etch_cell, alias='EtchPit') etch_ref.connect(etch_ref.ports['bottom'],\ destination=idt_ref.ports['bottom'],\ overlap=-self.bus.size.y-self.anchor.etch_margin.y) anchor_cell = self.anchor.draw() anchor_bottom = cell.add_ref(anchor_cell, alias='AnchorBottom') anchor_bottom.connect(anchor_bottom.ports['conn'], destination=idt_ref.ports['bottom'], overlap=-self.bus.size.y) if not self._stretch_top_margin: anchor_top = cell.add_ref(anchor_cell, alias='AnchorTop') else: anchor_top_dev = deepcopy(self.anchor) anchor_top_dev.metalized = Point(anchor_top_dev.size.x - 2, anchor_top_dev.metalized.y) anchor_top = cell.add_ref(anchor_top_dev.draw(), alias='AnchorTop') del anchor_top_dev anchor_top.connect(anchor_top.ports['conn'],\ idt_ref.ports['top'],overlap=-self.bus.size.y) outport_top = anchor_top.ports['conn'] outport_bottom = anchor_bottom.ports['conn'] outport_top.name = 'top' outport_top.orientation = 90 outport_bottom.name = 'bottom' outport_bottom.orientation = -90 outport_top.midpoint=(\ outport_top.x,\ outport_top.y+self.anchor.metalized.y) outport_bottom.midpoint=(\ outport_bottom.x,\ outport_bottom.y-self.anchor.metalized.y) cell.add_port(outport_top) cell.add_port(outport_bottom) del idt_cell, bus_cell, etch_cell, anchor_cell return cell
def draw(self): ''' Generates layout cell based on current parameters. 'conn' port is included in the cell. Returns ------- cell : phidl.Device. ''' self._check_anchor() o = self.origin anchor=pg.rectangle(\ size=(self.size-Point(2*self.etch_margin.x,-2*self.etch_margin.y)).coord,\ layer=self.layer) etch_size=Point(\ (self.etch_x-self.size.x)/2,\ self.size.y) offset = Point(self.x_offset, 0) cell = Device(self.name) etch_sx=pg.rectangle(\ size=(etch_size-offset).coord,\ layer=self.etch_layer) etch_dx=pg.rectangle(\ size=(etch_size+offset).coord,\ layer=self.etch_layer) etch_sx_ref=(cell<<etch_sx).move(origin=(0,0),\ destination=(o-Point(0,self.etch_margin.y)).coord) anchor_transl = o + Point(etch_sx.size[0] + self.etch_margin.x, -2 * self.etch_margin.y) anchor_ref=(cell<<anchor).move(origin=(0,0),\ destination=anchor_transl.coord) etchdx_transl = anchor_transl + Point( anchor.size[0] + self.etch_margin.x, +self.etch_margin.y) etch_dx_ref=(cell<<etch_dx).move(origin=(0,0),\ destination=etchdx_transl.coord) cell.add_port(name='conn',\ midpoint=(anchor_transl+Point(self.size.x/2-self.etch_margin.x,self.size.y+2*self.etch_margin.y)).coord,\ width=self.metalized.x,\ orientation=90) if self.etch_choice == True: cell.absorb(etch_sx_ref) cell.absorb(anchor_ref) cell.absorb(etch_dx_ref) else: cell.remove(etch_sx_ref) cell.remove(etch_dx_ref) del anchor, etch_sx, etch_dx return cell
def route_basic(port1, port2, path_type='sine', width_type='straight', width1=None, width2=None, num_path_pts=99, layer=0): # Assuming they're both Ports for now point_a = np.array(port1.midpoint) if width1 is None: width1 = port1.width point_b = np.array(port2.midpoint) if width2 is None: width2 = port2.width if round(abs(mod(port1.orientation - port2.orientation, 360)), 3) != 180: raise ValueError( '[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)' ) orientation = port1.orientation separation = point_b - point_a # Vector drawn from A to B distance = norm(separation) # Magnitude of vector from A to B rotation = np.arctan2( separation[1], separation[0]) * 180 / pi # Rotation of vector from A to B angle = rotation - orientation # If looking out along the normal of ``a``, the angle you would have to look to see ``b`` forward_distance = distance * cos(angle * pi / 180) lateral_distance = distance * sin(angle * pi / 180) # Create a path assuming starting at the origin and setting orientation = 0 # use the "connect" function later to move the path to the correct location xf = forward_distance yf = lateral_distance if path_type == 'straight': curve_fun = lambda t: [xf * t, yf * t] curve_deriv_fun = lambda t: [xf + t * 0, t * 0] if path_type == 'sine': curve_fun = lambda t: [xf * t, yf * (1 - cos(t * pi)) / 2] curve_deriv_fun = lambda t: [xf + t * 0, yf * (sin(t * pi) * pi) / 2] #if path_type == 'semicircle': # def semicircle(t): # t = np.array(t) # x,y = np.zeros(t.shape), np.zeros(t.shape) # ii = (0 <= t) & (t <= 0.5) # jj = (0.5 < t) & (t <= 1) # x[ii] = (cos(-pi/2 + t[ii]*pi/2))*xf # y[ii] = (sin(-pi/2 + t[ii]*pi/2)+1)*yf*2 # x[jj] = (cos(pi*3/2 - t[jj]*pi)+2)*xf/2 # y[jj] = (sin(pi*3/2 - t[jj]*pi)+1)*yf/2 # return x,y # curve_fun = semicircle # curve_deriv_fun = None if width_type == 'straight': width_fun = lambda t: (width2 - width1) * t + width1 if width_type == 'sine': width_fun = lambda t: (width2 - width1) * (1 - cos(t * pi) ) / 2 + width1 route_path = gdspy.Path(width=width1, initial_point=(0, 0)) route_path.parametric(curve_fun, curve_deriv_fun, number_of_evaluations=num_path_pts, max_points=199, final_width=width_fun, final_distance=None) route_path_polygons = route_path.polygons # Make the route path into a Device with ports, and use "connect" to move it # into the proper location D = Device() D.add_polygon(route_path_polygons, layer=layer) p1 = D.add_port(name=1, midpoint=(0, 0), width=width1, orientation=180) p2 = D.add_port(name=2, midpoint=[forward_distance, lateral_distance], width=width2, orientation=0) D.info['length'] = route_path.length D.rotate(angle=180 + port1.orientation - p1.orientation, center=p1.midpoint) D.move(origin=p1, destination=port1) return D
def route_manhattan(port1, port2, bendType='circular', layer=0, radius=20): #route along cardinal directions between any two ports placed diagonally #from each other Total = Device() width = port1.width #first map into uniform plane with normal x,y coords #allows each situation to be put into uniform cases of quadrants for routing. #this is because bends change direction and positioning. if port1.orientation == 0: p2 = [port2.midpoint[0], port2.midpoint[1]] p1 = [port1.midpoint[0], port1.midpoint[1]] if port1.orientation == 90: p2 = [port2.midpoint[1], -port2.midpoint[0]] p1 = [port1.midpoint[1], -port1.midpoint[0]] if port1.orientation == 180: p2 = [-port2.midpoint[0], -port2.midpoint[1]] p1 = [-port1.midpoint[0], -port1.midpoint[1]] if port1.orientation == 270: p2 = [-port2.midpoint[1], port2.midpoint[0]] p1 = [-port1.midpoint[1], port1.midpoint[0]] Total.add_port(name=1, port=port1) Total.add_port(name=2, port=port2) if p2[1] == p1[1] or p2[0] == p1[0]: raise ValueError('Error - ports must be at different x AND y values.') #if it is parallel or anti-parallel, route with 180 option if (np.round(np.abs(np.mod(port1.orientation - port2.orientation, 360)), 3) == 180) or (np.round( np.abs(np.mod(port1.orientation - port2.orientation, 360)), 3) == 0): R1 = route_manhattan180(port1=port1, port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) else: #first quadrant case if (p2[1] > p1[1]) & (p2[0] > p1[0]): #simple 90 degree single-bend case if port2.orientation == port1.orientation - 90 or port2.orientation == port1.orientation + 270: R1 = route_manhattan90(port1=port1, port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) elif port2.orientation == port1.orientation + 90 or port2.orientation == port1.orientation - 270: if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=port1.orientation, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=port1.orientation, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=1, destination=port1) R1 = route_manhattan180(port1=b1.ports[2], port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) #second quadrant case if (p2[1] > p1[1]) & (p2[0] < p1[0]): if np.abs(port1.orientation - port2.orientation) == 90 or np.abs( port1.orientation - port2.orientation) == 270: if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=port1.orientation, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=port1.orientation, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=1, destination=port1) R1 = route_manhattan180(port1=b1.ports[2], port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) #third quadrant case if (p2[1] < p1[1]) & (p2[0] < p1[0]): if np.abs(port1.orientation - port2.orientation) == 90 or np.abs( port1.orientation - port2.orientation) == 270: if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=port1.orientation, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=port1.orientation, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=1, destination=port1) R1 = route_manhattan180(port1=b1.ports[2], port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) #fourth quadrant case if (p2[1] < p1[1]) & (p2[0] > p1[0]): #simple 90 degree single-bend case if port2.orientation == port1.orientation + 90 or port2.orientation == port1.orientation - 270: R1 = route_manhattan90(port1=port1, port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) elif port2.orientation == port1.orientation - 90 or port2.orientation == port1.orientation + 270: if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=port1.orientation, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=port1.orientation, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=1, destination=port1) R1 = route_manhattan180(port1=b1.ports[2], port2=port2, bendType=bendType, layer=layer, radius=radius) r1 = Total.add_ref(R1) return Total
def route_manhattan90(port1, port2, bendType='circular', layer=0, radius=20): #this is a subroutine of route_manhattan() and should not be used by itself. Total = Device() width = port1.width #first map into uniform plane with normal x,y coords #allows each situation to be put into uniform cases of quadrants for routing. #this is because bends change direction and positioning. if port1.orientation == 0: p2 = [port2.midpoint[0], port2.midpoint[1]] p1 = [port1.midpoint[0], port1.midpoint[1]] if port1.orientation == 90: p2 = [port2.midpoint[1], -port2.midpoint[0]] p1 = [port1.midpoint[1], -port1.midpoint[0]] if port1.orientation == 180: p2 = [-port2.midpoint[0], -port2.midpoint[1]] p1 = [-port1.midpoint[0], -port1.midpoint[1]] if port1.orientation == 270: p2 = [-port2.midpoint[1], port2.midpoint[0]] p1 = [-port1.midpoint[1], port1.midpoint[0]] #create placeholder ports based on the imaginary coordinates we created Total.add_port(name='t1', midpoint=[0, 0], orientation=0, width=width) #CHECK THIS #first quadrant target, route upward if (p2[1] > p1[1]) & (p2[0] > p1[0]): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=-90, width=width) if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff, 0]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) R2 = route_basic(port1=b1.ports[2], port2=Total.ports['t2'], layer=layer) r1 = Total.add_ref(R1) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #fourth quadrant target, route downward if (p2[1] < p1[1]) & (p2[0] > p1[0]): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=90, width=width) if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff, 0]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) R2 = route_basic(port1=b1.ports[2], port2=Total.ports['t2'], layer=layer) r1 = Total.add_ref(R1) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) Total.rotate(angle=port1.orientation, center=p1) Total.move(origin=Total.ports['t1'], destination=port1) return Total
def _gradual_bend( radius=20, width=1.0, angular_coverage=15, num_steps=10, angle_resolution=0.1, start_angle=0, direction='ccw', layer=0, ): """ creates a 90-degree bent waveguide the bending radius is gradually increased until it reaches the minimum value of the radius at the "angular coverage" angle. it essentially creates a smooth transition to a bent waveguide mode. user can control number of steps provided. direction determined by start angle and cw or ccw switch ############ with the default 10 "num_steps" and 15 degree coverage, effective radius is about 1.5*radius. """ angular_coverage = np.deg2rad(angular_coverage) D = Device() #determines the increment in radius through its inverse from 0 to 1/r inc_rad = (radius**-1) / (num_steps) angle_step = angular_coverage / num_steps #construct a series of sub-arcs with equal angles but gradually decreasing bend radius arcs = [] for x in xrange(num_steps): A = _arc(radius=1 / ((x + 1) * inc_rad), width=width, theta=np.rad2deg(angle_step), start_angle=x * np.rad2deg(angle_step), angle_resolution=angle_resolution, layer=layer) a = D.add_ref(A) arcs.append(a) if x > 0: a.connect(port=1, destination=prevPort) prevPort = a.ports[2] D.add_port(name=1, port=arcs[0].ports[1]) #now connect a regular bend for the normal curved portion B = _arc(radius=radius, width=width, theta=45 - np.rad2deg(angular_coverage), start_angle=angular_coverage, angle_resolution=angle_resolution, layer=layer) b = D.add_ref(B) b.connect(port=1, destination=prevPort) prevPort = b.ports[2] D.add_port(name=2, port=prevPort) #now create the overall structure Total = Device() #clone the half-curve into two objects and connect for a 90 deg bend. D1 = Total.add_ref(D) D2 = Total.add_ref(D) D2.reflect(p1=[0, 0], p2=[1, 1]) D2.connect(port=2, destination=D1.ports[2]) Total.xmin = 0 Total.ymin = 0 #orient to default settings... Total.reflect(p1=[0, 0], p2=[1, 1]) Total.reflect(p1=[0, 0], p2=[1, 0]) #orient to user-provided settings if direction == 'cw': Total.reflect(p1=[0, 0], p2=[1, 0]) Total.rotate(angle=start_angle, center=Total.center) Total.center = [0, 0] Total.add_port(name=1, port=D1.ports[1]) Total.add_port(name=2, port=D2.ports[1]) return Total
def route_manhattan180(port1, port2, bendType='circular', layer=0, radius=20): #this is a subroutine of route_manhattan() and should not be used by itself. Total = Device() width = port1.width #first map into uniform plane with normal x,y coords #allows each situation to be put into uniform cases of quadrants for routing. #this is because bends change direction and positioning. if port1.orientation == 0: p2 = [port2.midpoint[0], port2.midpoint[1]] p1 = [port1.midpoint[0], port1.midpoint[1]] if port1.orientation == 90: p2 = [port2.midpoint[1], -port2.midpoint[0]] p1 = [port1.midpoint[1], -port1.midpoint[0]] if port1.orientation == 180: p2 = [-port2.midpoint[0], -port2.midpoint[1]] p1 = [-port1.midpoint[0], -port1.midpoint[1]] if port1.orientation == 270: p2 = [-port2.midpoint[1], port2.midpoint[0]] p1 = [-port1.midpoint[1], port1.midpoint[0]] #create placeholder ports based on the imaginary coordinates we created Total.add_port(name='t1', midpoint=[0, 0], orientation=0, width=width) if (port1.orientation != port2.orientation): Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=180, width=width) else: Total.add_port(name='t2', midpoint=list(np.subtract(p2, p1)), orientation=0, width=width) if port1.orientation == port2.orientation: #first quadrant target if (p2[1] > p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0], 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #second quadrant target if (p2[1] > p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b2.ports[2], port2=Total.ports['t2'], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=b1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #third quadrant target if (p2[1] < p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b2.ports[2], port2=Total.ports['t2'], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=b1.ports[1]) Total.add_port(name=2, port=r2.ports[2]) #fourth quadrant target if (p2[1] < p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0], 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #other port orientations are not supported: elif np.round(np.abs(np.mod(port1.orientation - port2.orientation, 360)), 3) != 180: raise ValueError( '[DEVICE] route() error: Ports do not face each other (orientations must be 180 apart)' ) #otherwise, they are 180 degrees apart: else: #first quadrant target if (p2[1] > p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff * 2, 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) #second quadrant target if (p2[1] > p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=90) B3 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=180, theta=-90) B4 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=90, theta=-90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='ccw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='ccw') B3 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=180, direction='cw') B4 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=90, direction='cw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b3 = Total.add_ref(B3) b4 = Total.add_ref(B4) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] - radiusEff * 4]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) b3.connect(port=b3.ports[1], destination=b2.ports[2]) b3.move([p2[0] - p1[0], 0]) R2 = route_basic(port1=b2.ports[2], port2=b3.ports[1], layer=layer) r2 = Total.add_ref(R2) b4.connect(port=b4.ports[1], destination=b3.ports[2]) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b4.ports[2]) #third quadrant target if (p2[1] < p1[1]) & (p2[0] < p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=-90) B3 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-180, theta=90) B4 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='cw') B3 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-180, direction='ccw') B4 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b3 = Total.add_ref(B3) b4 = Total.add_ref(B4) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 4]) R1 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r1 = Total.add_ref(R1) b3.connect(port=b3.ports[1], destination=b2.ports[2]) b3.move([p2[0] - p1[0], 0]) R2 = route_basic(port1=b2.ports[2], port2=b3.ports[1], layer=layer) r2 = Total.add_ref(R2) b4.connect(port=b4.ports[1], destination=b3.ports[2]) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b4.ports[2]) #fourth quadrant target if (p2[1] < p1[1]) & (p2[0] > p1[0]): if bendType == 'circular': B1 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=0, theta=-90) B2 = _arc(radius=radius, width=width, layer=layer, angle_resolution=1, start_angle=-90, theta=90) radiusEff = radius if bendType == 'gradual': B1 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=0, direction='cw') B2 = _gradual_bend(radius=radius, width=width, layer=layer, start_angle=-90, direction='ccw') radiusEff = B1.xsize - width / 2 b1 = Total.add_ref(B1) b2 = Total.add_ref(B2) b1.connect(port=b1.ports[1], destination=Total.ports['t1']) b1.move([p2[0] - p1[0] - radiusEff * 2, 0]) b2.connect(port=b2.ports[1], destination=b1.ports[2]) b2.move([0, p2[1] - p1[1] + radiusEff * 2]) R1 = route_basic(port1=Total.ports['t1'], port2=b1.ports[1], layer=layer) r1 = Total.add_ref(R1) R2 = route_basic(port1=b1.ports[2], port2=b2.ports[1], layer=layer) r2 = Total.add_ref(R2) Total.add_port(name=1, port=r1.ports[1]) Total.add_port(name=2, port=b2.ports[2]) Total.rotate(angle=port1.orientation, center=p1) Total.move(origin=Total.ports['t1'], destination=port1) return Total