def mmi1x2(wg_width=0.35, length_port=0.2, length_mmi=2.8, width_mmi=1.55, gap_mmi=0.4): D = Device() Port_wg = pg.taper(length=length_port, width1=wg_width, width2=wg_width, layer=lys['wg_deep']) port_in = D.add_ref(Port_wg) MMI = pg.taper(length=length_mmi, width1=width_mmi, width2=width_mmi, layer=lys['wg_deep']) mmi = D.add_ref(MMI) mmi.connect(port=1, destination=port_in.ports[2]) port_up = D.add_ref(Port_wg) port_up.connect(port=1, destination=mmi.ports[2]) port_up.movey(gap_mmi) port_down = D.add_ref(Port_wg) port_down.connect(port=1, destination=mmi.ports[2]) port_down.movey(-gap_mmi) D.add_port(name=1, port=port_in.ports[1]) D.add_port(name=2, port=port_up.ports[2]) D.add_port(name=3, port=port_down.ports[2]) return D
def test_port_reference_manipulate(): D = Device() D.add_port(name='test123', midpoint=(5.7, 9.2), orientation=37) d = D.add_ref(D).move([1, 1]).rotate(45) assert (np.allclose(d.ports['test123'].midpoint, (-2.474873734152916, 11.950104602052654))) assert (d.ports['test123'].orientation == 37 + 45)
def poling_region(length=4000, period=5, dutycycle=0.4, gap=25, Lfinger=50, layer=10, pad_width=50): #Calculations Wfinger = period * dutycycle Nfinger = int(length / period) + 1 length = Nfinger * period - (1 - dutycycle) * period P = Device('Poling Electrodes') #Positive side R = pg.rectangle([length, pad_width], layer=layer) F = pg.rectangle([Wfinger, Lfinger], layer=layer) P << R a = P.add_array(F, columns=Nfinger, rows=1, spacing=(period, 0)) a.move([0, pad_width]) #Negative side R2 = pg.rectangle([length, pad_width], layer=layer) r2 = P.add_ref(R2) r2.move([0, pad_width + Lfinger + gap]) return P
def fang(wg_width, length, orientation): F = Device() w1 = wg_width X1 = CrossSection() X1.add(width=w1, offset=0, layer=30, ports=('in', 'out')) P = Path() P.append(pp.euler(radius=50, angle=45)) # Euler bend (aka "racetrack" curve) fang = P.extrude(X1) fang = F.add_ref(fang) D = pg.taper(length=length, width1=w1, width2=0.000001, port=None, layer=30) taper = F.add_ref(D) taper.connect(port=1, destination=fang.ports['out']) #Defualt is RU, right up if orientation == 'RD': F.mirror(p1=[0, 0], p2=[1, 0]) elif orientation == 'LU': F.mirror(p1=[0, 0], p2=[0, 1]) elif orientation == 'LD': F.rotate(180, center=[0, 0]) return F
def fibonacci(n, width = 0.008, angle_resolution = 1.5, layer = 0): # --------------------------------------- # Template that draws a golden spiral # n : number of points on the spiral # width: width of the spiral # --------------------------------------- pi = 3.14 F = Device("spiral") start_angle = 0 dtheta = 360/n theta = dtheta for i in range(n): angle1 = (start_angle)*pi/180 angle2 = (start_angle + dtheta)*pi/180 r1 = 0.0053*start_angle r2 = 0.0053*(start_angle + dtheta) x1 = r1*math.cos(angle1) y1 = r1*math.sin(angle1) x2 = (r1+width)*math.cos(angle1) y2 = (r1+width)*math.sin(angle1) x3 = (r2+width)*math.cos(angle2) y3 = (r2+width)*math.sin(angle2) x4 = (r2)*math.cos(angle2) y4 = (r2)*math.sin(angle2) F.add_polygon([(x1,y1),(x2,y2),(x3,y3),(x4,y4)], layer = layer) start_angle += dtheta return F
def give_loopmirror(gap=.5): # just an example of augmenting a normal phidl device (loop_mirror_terminator) # giving it as a phidl Device as well as port, bounding box, and source things used by MEEP D = Device('loopmirror') cell = D << pg.rectangle([31, 15], layer=lys['FLOORPLAN']) cell.center = (0, 0) access = D << pg.compass([8, .35], layer=lys['wg_deep']) access.y = cell.y access.xmin = cell.xmin mmi = mmi1x2(gap_mmi=.5) loop = D << loop_mirror_terminator(y_splitter=mmi) loop.connect('wg_in_1', access.ports['E']) medium_map = get_layer_mapping(lys) port = D << pg.rectangle([.1, 1], layer=1) source = D << pg.rectangle([.1, 1], layer=2) port.y = 0 source.y = 0 port.x = loop.xmin - 6 source.x = loop.xmin - 7 D.flatten() return D
def xor_polygons_phidl(A, B, hash_geom=True): """ Given two devices A and B, performs a layer-by-layer XOR diff between A and B, and returns polygons representing the differences between A and B. """ from phidl import Device import gdspy # first do a geometry hash to vastly speed up if they are equal if hash_geom and (A.hash_geometry() == B.hash_geometry()): return Device() D = Device() A_polys = A.get_polygons(by_spec=True) B_polys = B.get_polygons(by_spec=True) A_layers = A_polys.keys() B_layers = B_polys.keys() all_layers = set() all_layers.update(A_layers) all_layers.update(B_layers) for layer in all_layers: if (layer in A_layers) and (layer in B_layers): p = gdspy.fast_boolean(A_polys[layer], B_polys[layer], operation='xor', precision=0.001, max_points=4000, layer=layer[0], datatype=layer[1]) elif (layer in A_layers): p = A_polys[layer] elif (layer in B_layers): p = B_polys[layer] if p is not None: D.add_polygon(p, layer=layer) return D
def invert(self, layer, padding=None): """Inverts a pattern from positive to negative or vice versa. Parameters ---------- layer: string Layer of new, inverted device. padding: np.array Array containing padding of bounding box used to substract device [left, right, top, bottom] """ bounding_box = Device('interim_bounding_box') if padding is None: padding = [0, 0, 0, 0] bounding_box.add_polygon( [(self.xmin - padding[0], self.ymin - padding[3]), (self.xmin - padding[0], self.ymax + padding[2]), (self.xmax + padding[1], self.ymax + padding[2]), (self.xmax + padding[1], self.ymin - padding[3])], layer=layer) # perform substraction for positive e-beam resist inverse = pg.boolean(A=bounding_box, B=self, operation='A-B', layer=layer) return inverse
def test_copy_deepcopy(): D = Device() A = pg.ellipse(radii=(10, 5), angle_resolution=2.5, layer=1) B = pg.ellipse(radii=(10, 5), angle_resolution=2.5, layer=1) a = D << A b1 = D << B b2 = D << B Dcopy = pg.copy(D) Ddeepcopy = pg.deepcopy(D) h = D.hash_geometry(precision=1e-4) assert (h == '0313cd7e58aa265b44dd1ea10265d1088a2f1c6d') h = Dcopy.hash_geometry(precision=1e-4) assert (h == '0313cd7e58aa265b44dd1ea10265d1088a2f1c6d') h = Ddeepcopy.hash_geometry(precision=1e-4) assert (h == '0313cd7e58aa265b44dd1ea10265d1088a2f1c6d') D << pg.ellipse(radii=(12, 5), angle_resolution=2.5, layer=2) h = D.hash_geometry(precision=1e-4) assert (h == '856cedcbbb53312ff839b9fe016996357e658d33') h = Dcopy.hash_geometry(precision=1e-4) assert (h == '0313cd7e58aa265b44dd1ea10265d1088a2f1c6d') h = Ddeepcopy.hash_geometry(precision=1e-4) assert (h == '0313cd7e58aa265b44dd1ea10265d1088a2f1c6d') A.add_ref(pg.ellipse(radii=(12, 5), angle_resolution=2.5, layer=2)) B.add_polygon([[3, 4, 5], [6.7, 8.9, 10.15]], layer=0) h = D.hash_geometry(precision=1e-4) assert (h == 'c007b674e8053c11c877860f0552fff18676b68e') h = Dcopy.hash_geometry(precision=1e-4) assert (h == '2590bd786348ab684616eecdfdbcc9735b156e18') h = Ddeepcopy.hash_geometry(precision=1e-4) assert (h == '0313cd7e58aa265b44dd1ea10265d1088a2f1c6d')
def test_port_add(): D = Device() D.add_port(name='test123', midpoint=(5.7, 9.2), orientation=37) D.add_port(name='test456', midpoint=(1.5, 6.7), orientation=99) assert (len(D.ports) == 2) assert (np.allclose(D.ports['test123'].midpoint, (5.7, 9.2))) assert (np.allclose(D.ports['test456'].midpoint, (1.5, 6.7))) assert (D.ports['test123'].orientation == 37) assert (D.ports['test456'].orientation == 99)
def makeCross(x0,y0,width,lw, layer): # Make cross with # x0,y0 : center # width: width of the bounding box # lw: linewidth cross = Device("cross") cross.add_polygon([(x0-width/2,y0-lw/2),(x0-width/2,y0+lw/2), (x0+width/2,y0+lw/2), (x0+width/2,y0-lw/2) ], layer = layer) cross.add_polygon([(x0-lw/2,y0-width/2),(x0-lw/2,y0+width/2), (x0+lw/2,y0+width/2), (x0+lw/2,y0-width/2) ], layer = layer) return cross
def test_polygon_simplify(): D = Device() t = np.linspace(0, np.pi, 1000) x = np.cos(t) y = np.sin(t) poly = D.add_polygon([x, y]) h = D.hash_geometry(precision=1e-4) assert (h == '0c3b1465c8b6ffd911c41b02114b9a06f606ad91') # qp(D) poly.simplify(tolerance=1e-1) h = D.hash_geometry(precision=1e-4) assert (h == '7d9ebcb231fb0107cbbf618353adeb583782ca11')
def wb_pad(x, y, pad, name): R = pg.rectangle(size=(pad, pad - 0.5), layer=70) D = Device() rect1 = D << R rect1.move([x, y]) text_size = 20 L = pg.text(text=name, size=text_size, layer=97, justify='center') D.add_ref(L).move((x + pad / 3, y)) return D
def test_rotate(): # Test polygon rotation D = Device() p = D.add_polygon([(8, 6, 7, 9), (6, 8, 9, 5)]) p.rotate(37.5) h = D.hash_geometry(precision=1e-4) assert (h == '2e4815072eabe053c3029d9e29a5b3ed59fe9bb7') # Test Device rotation D = Device() p = D.add_polygon([(8, 6, 7, 9), (6, 8, 9, 5)]) D.rotate(37.5) h = D.hash_geometry(precision=1e-4) assert (h == '2e4815072eabe053c3029d9e29a5b3ed59fe9bb7')
def test_reflect(): # Test polygon reflection D = Device() p = D.add_polygon([(8, 6, 7, 9), (6, 8, 9, 5)]) p.mirror(p1=(1.7, 2.5), p2=(4.5, 9.1)) h = D.hash_geometry(precision=1e-4) assert (h == 'bc6ae5308c2240e425cd503e0cdda30007bbfc4d') # Test Device reflection D = Device() p = D.add_polygon([(8, 6, 7, 9), (6, 8, 9, 5)]) D.mirror(p1=(1.7, 2.5), p2=(4.5, 9.1)) h = D.hash_geometry(precision=1e-4) assert (h == 'bc6ae5308c2240e425cd503e0cdda30007bbfc4d')
def global_markers(layer_marker=10, layer_mask=20): D = Device('Global Markers') R = pg.rectangle(size=(20, 20), layer=1) a = D.add_array(R, columns=6, rows=6, spacing=(100, 100)) a.move([-260, -260]) #Center of the array R = pg.rectangle(size=(20, 20), layer=1) a = D.add_array(R, columns=6, rows=6, spacing=(100, 100)) a.move([-260, -260]) #Center of the array #Add marker cover cover = pg.bbox(bbox=a.bbox, layer=layer_mask) D << pg.offset(cover, distance=100, layer=layer_mask) return D
def waveguide(width=1, length=10, layer=1): ''' Parameters ---------- width : FLOAT, optional WIDTH OF THE WAVEGUIDE. The default is 1. length : FLOAT, optional LENGTH OF THE WAVEGUIDE. The default is 10. layer : INT, optional LAYER. The default is 1. Returns ------- WG : DEVICE (PHIDL) WAVEGUIDE OBJECT ''' WG = Device('Waveguide') WG.add_polygon([(0, 0), (length, 0), (length, width), (0, width)], layer=layer) WG.add_port(name=1, midpoint=[0, width / 2], width=width, orientation=180) WG.add_port(name=2, midpoint=[length, width / 2], width=width, orientation=0) return WG
def phidl_port_translation(): # Conversion between object and geometric representation of ports try: pg.with_geometric_ports except AttributeError: pass # skipping def geom_equal(A, B): h1 = A.hash_geometry(precision=1e-4) h2 = B.hash_geometry(precision=1e-4) return h1 == h2 init_D = pg.compass(layer=1) geom_D = pg.with_geometric_ports(init_D, layer=2) end_D = pg.with_object_ports(geom_D, layer=2) assert geom_equal(init_D, end_D) assert len(geom_D.ports) == 0 geom_D.remove_layers([2], include_labels=True) assert geom_equal(init_D, geom_D) assert geom_equal(init_D, end_D) for pnam, port in init_D.ports.items(): assert np.all(end_D.ports[pnam].midpoint == port.midpoint) # now through the filesystem end2_D = anyCell_to_anyCell(init_D, Device()) for pnam, port in init_D.ports.items(): assert np.all(end_D.ports[pnam].midpoint == port.midpoint) assert geom_equal(init_D, end2_D) assert init_D.name == end2.name
def mzi(length, radius, angle, wg_width, Y_mmi): P1 = Path() P1.append(pp.euler(radius=radius, angle=-angle)) P1.append(pp.euler(radius=radius, angle=angle)) P1.append(pp.straight(length=length)) P1.append(pp.euler(radius=radius, angle=angle)) P1.append(pp.euler(radius=radius, angle=-angle)) X = CrossSection() X.add(width=wg_width, offset=0, layer=30) waveguide_device1 = P1.extrude(X) E = Device('EOM_GHz') b1 = E.add_ref(waveguide_device1).move([0, -Y_mmi / 2]) b2 = E.add_ref(waveguide_device1).move([0, -Y_mmi / 2]) b2.mirror((0, 0), (1, 0)) return E
def test_add_polygon3(): D = Device() D.add_polygon([(8, 6), (6, 8), (7, 9), (9, 5)], layer=7) D.add_polygon([(8, 0), (6, 8), (7, 9), (9, 5)], layer=(8, 0)) D.add_polygon([(8, 1), (6, 8), (7, 9), (9, 5)], layer=(9, 1)) h = D.hash_geometry(precision=1e-4) assert (h == '96abc3c9e30f3bbb32c5a39aeea2ba0fa3b13ebe')
def bonding_pads(margin, pad, chip_width, chip_height): num = 18 sep = (chip_width - 2 * margin) / num P = pg.rectangle(size=(pad, pad), layer=3).move([margin, margin]) P << pg.rectangle(size=(pad, pad), layer=3).move( [margin, chip_height - margin - pad]) for i in range(1, int(num / 2)): P << pg.rectangle(size=(pad, pad), layer=3).move( [margin + i * sep, margin]) P << pg.rectangle(size=(pad, pad), layer=3).move( [margin + i * sep, chip_height - margin - pad]) E = Device('bonding_pads') b1 = E.add_ref(P) b2 = E.add_ref(P) b2.mirror((chip_width / 2, chip_height), (chip_width / 2, 0)) return E, sep
def waveguide(width=10, height=1): WG = Device('waveguide') WG.add_polygon([(0, 0), (width, 0), (width, height), (0, height)]) WG.add_port(name='wgport1', midpoint=[0, height / 2], width=height, orientation=180) WG.add_port(name='wgport2', midpoint=[width, height / 2], width=height, orientation=0) return WG
def dcimthin(wg_width, off_chip, W, L_tp, W_tp, L_mmi, W_mmi, Y_mmi, length, radius, x_pos, y_pos, middle_e_width, e_e_gap): if e_e_gap == 9: angle = 12.5196846104787 elif e_e_gap == 7: angle = 11.83919343835311 elif e_e_gap == 11: angle = 13.165741242693 # Parameters mmi_length = L_tp * 2 + L_mmi side_electrode_width = middle_e_width * 2 eulerX = mod_euler(radius=radius, angle=angle)[1][0] racetrack_length = eulerX * 4 + length #mzi_length = racetrack_length + mmi_length*2 side = x_pos #side = (chip_width - mzi_length)/2 # Devices M = mzi(length, radius, angle, wg_width, Y_mmi) mmiL = mmi(W, L_tp, W_tp, L_mmi, W_mmi, Y_mmi) mmiR = mmi(W, L_tp, W_tp, L_mmi, W_mmi, Y_mmi) mmiR.mirror().move([mmi_length, 0]) E = Device('EOM') #E << pg.rectangle(size=(side+off_chip,wg_width), layer=1).move([-off_chip,y_pos-wg_width/2]) E << mmiL.move([side, y_pos]) E << M.move([side + mmi_length, y_pos]) E << mmiR.move([side + mmi_length + racetrack_length, y_pos]) #E << pg.rectangle(size=(side+off_chip,wg_width), layer=1).move([side+mzi_length,y_pos-wg_width/2]) e_left = side + mmi_length + 2 * eulerX #side_e_width R = pg.rectangle(size=(length, middle_e_width), layer=40) R2 = pg.rectangle(size=(length, side_electrode_width), layer=40) #top electrode h_top = middle_e_width / 2 + e_e_gap + y_pos E.add_ref(R2).move([e_left, h_top]) #middle electrode E.add_ref(R).move([e_left, y_pos - middle_e_width / 2]) #bottom electrode h_bot = -middle_e_width / 2 - side_electrode_width - e_e_gap + y_pos E.add_ref(R2).move([e_left, h_bot]) return E, e_left
def back_and_forth(): # from phidl to pya and back init_device = some_device(10, 20) pya_layout = pya.Layout() pya_cell = pya_layout.create_cell('newname') anyCell_to_anyCell(init_device, pya_cell) final_device = Device() anyCell_to_anyCell(pya_cell, final_device) return init_device, pya_layout, final_device
def makeLineSpace(x0,y0,width, height,pitch,ymax, layer): #---------------------------------------------# # Creates a line space pattern in y-direction # x0: x coordinate of the lower left line # y0: y coordinate of the lower left line # width: width of each line # height: height of each line # pitch: pitch of each line # pitch > height #---------------------------------------------# if abs(pitch) < abs(height): print("pitch must be greater then height") return LS = Device('linespace') if pitch > 0: while y0+height <= ymax: Li = makeLine(x0,y0,width,height,layer) LS.add_ref(Li) y0 += pitch elif pitch < 0: while y0+height >= -ymax: Li = makeLine(x0,y0,width,height,layer) LS.add_ref(Li) y0 += pitch return y0, LS
def save_or_visualize(device_name=None, out_file=None): ''' Handles a conditional write to file or send over lyipc connection. The context manager yields a new empty Device. The context block then modifies that device by adding references to it. It does not need to return anything. Back to the context manager, the Device is saved if out_file is not None, or it is sent over ipc Example:: with save_or_visualize(out_file='my_box.gds') as D: r = D << phidl.geometry.rectangle(size=(10, 10), layer=1) r.movex(20) will write the device with a rectangle to a file called 'my_box.gds' and do nothing with lyipc. By changing out_file to None, it will send an ipc load command instead of writing to a permanent file, (Although ipc does write a file to be loaded by klayout, it's name or persistence is not guaranteed.) ''' if device_name is None: CELL = Device() else: CELL = Device(device_name) yield CELL if out_file is None: klayout_quickplot(CELL, 'debugging.gds', fresh=True) else: CELL.write_gds(out_file)
def test_port_remove(): D = Device() D.add_port(name='test123', midpoint=(5.7, 9.2), orientation=37) D.add_port(name='test456', midpoint=(1.5, 6.7), orientation=99) E = Device() d = E << D D.remove(D.ports['test123']) assert (len(D.ports) == 1) assert (len(d.ports) == 1) assert (D.ports['test456']) assert (d.ports['test456'])
def dcpm(L, elec_w, e_e_gap, via, wg_width): P = Path() P.append(pp.straight(length=L)) X = CrossSection() X.add(width=wg_width, offset=0, layer=30) DCPM = Device() DCPM << P.extrude(X) R1 = pg.rectangle(size=(L, elec_w), layer=40) R2 = pg.rectangle(size=(L, elec_w), layer=40) DCPM << R1.move([0, e_e_gap / 2]) DCPM << R2.move([0, -elec_w - e_e_gap / 2]) return DCPM
def connection_UR(x1, y1, x2, y2, elec_m, elec_s, e_e_gap, Rt): #right down D = Device() x1t = x1 + elec_m / 2 + elec_s / 2 + e_e_gap x1b = x1 - elec_m / 2 - elec_s / 2 - e_e_gap y2t = y2 - elec_m / 2 - elec_s / 2 - e_e_gap y2b = y2 + elec_m / 2 + elec_s / 2 + e_e_gap points = np.array([(x1, y1), (x1, y2), (x2, y2)]) points_t = np.array([(x1t, y1), (x1t, y2t), (x2, y2t)]) points_b = np.array([(x1b, y1), (x1b, y2b), (x2, y2b)]) M = pp.smooth( points=points, radius=Rt + elec_m / 2 + elec_s / 2 + e_e_gap, corner_fun=pp.arc, ) M.rotate(90, center=(x1, y1)) Mt = pp.smooth( points=points_t, radius=Rt, corner_fun=pp.arc, ) Mt.rotate(90, center=(x1t, y1)) Mb = pp.smooth( points=points_b, radius=Rt + 2 * (elec_m / 2 + elec_s / 2 + e_e_gap), corner_fun=pp.arc, ) Mb.rotate(90, center=(x1b, y1)) X = CrossSection() X.add(width=elec_m, offset=0, layer=70) Xt = CrossSection() Xt.add(width=elec_s, offset=0, layer=70) Xb = CrossSection() Xb.add(width=elec_s, offset=0, layer=70) L = M.extrude(X) #Left Trace Lt = Mt.extrude(Xt) #Left Trace Lb = Mb.extrude(Xb) #Left Trace D << L D << Lt D << Lb return D
def connectPad(middle_e_width, chip_width, chip_height, radius, length, e_e_gap, setpos, side_electrode_width, y_pos): T = Device('connections') x2 = (chip_width - length) / 2 + middle_e_width h_bot = -middle_e_width / 2 - side_electrode_width / 2 - e_e_gap + y_pos T << connect(x2, h_bot, middle_e_width, chip_width, chip_height, 'b', radius, length, e_e_gap, setpos) h_mid = y_pos T << connect(x2, h_mid, middle_e_width, chip_width, chip_height, 'm', radius, length, e_e_gap, setpos) h_top = middle_e_width / 2 + e_e_gap + y_pos + side_electrode_width / 2 T << connect(x2, h_top, middle_e_width, chip_width, chip_height, 't', radius, length, e_e_gap, setpos) return T