def test_group(): # Test all types D = Device() E1 = pg.ellipse(radii=(10, 5), angle_resolution=2.5, layer=0) E2 = pg.rectangle(size=(4, 2), layer=0).movex(15) e1 = D << E1 e2 = D << E2 e3 = D << E2 e4 = D.add_label('hello', position=(1.5, -1.5)) e5 = pg.snspd() e6 = D.add_polygon([(8, 6, 7, 9, 7, 0), (6, 8, 9, 5, 7, 0)]) e7 = D.add_array(pg.cross()) e2verify = D << E2 # Test creation and addition G = Group() G.add(e1) G.add(e2) G.add([e3, e4, e5]) G += (e6, e7) assert np.allclose(G.bbox.flatten(), np.array([-10., -8.5, 105., 105.])) # Test movement G.move((2, 7)) e2verify.move((2, 7)) assert np.allclose(G.bbox.flatten(), np.array([-8., -1.5, 107., 112.])) assert all(e2.center == e2verify.center) assert e2.rotation == e2verify.rotation # Test rotation G.rotate(90, center=(5, 5)) e2verify.rotate(90, center=(5, 5)) assert np.allclose(G.bbox.flatten(), np.array([-102., -8., 11.5, 107.])) assert all(e2.center == e2verify.center) assert e2.rotation == e2verify.rotation # Test mirroring G.mirror(p1=(1, 1), p2=(-1, 1)) e2verify.mirror(p1=(1, 1), p2=(-1, 1)) assert np.allclose(G.bbox.flatten(), np.array([-102., -105., 11.5, 10.])) assert all(e2.center == e2verify.center) assert e2.rotation == e2verify.rotation h = D.hash_geometry(precision=1e-4) assert (h == '3964acb3971771c6e70ceb587c2ae8b37f2ed112')
a.bbox.tolist() == [[0.0, 0.0], [216.0, 104.0]] qp(D) #============================================================================== # Adding premade geometry with phidl.geometry #============================================================================== # Usually at the beginning of a phidl file we import the phidl.geometry module # as ``pg``, like this: import phidl.geometry as pg # The ``pg`` module contains dozens of premade shapes and structures, ranging # from simple ones like ellipses to complex photonic structures. Let's create # a few simple structures and plot them D = Device() G1 = pg.ellipse(radii=(10, 5), angle_resolution=2.5, layer=1) G2 = pg.snspd(wire_width=0.2, wire_pitch=0.6, size=(10, 8), layer=2) G3 = pg.rectangle(size=(10, 5), layer=3) g1 = D.add_ref(G1) g2 = D.add_ref(G2) g3 = D.add_ref(G3) g1.xmin = g2.xmax + 5 g3.xmin = g1.xmax + 5 qp(D) # There are dozens of these types of structures. See the /phidl/geometry.py # file for a full geometry list. Note some of the more complex shapes are # experimental and may change with time. # Let's save this file so we can practice importing it in the next step D.write_gds('MyNewGDS.gds')
def do_something_interesting(): x_section = Device() def insert_wx(layname, width, x): # insert a rectangle with a given width and center x. Height doesn't matter. shape = x_section << pg.rectangle((width, 2), layer=lys[layname]) shape.x = x shape.y = 50 return shape def insert(layname, xmin, xmax): # insert a rectangle spanning these x coordinates shape = x_section << pg.rectangle((xmax - xmin, 2), layer=lys[layname]) shape.xmin = xmin shape.y = 50 return shape # LED ppdopant_x = .65 outer_wgx = ppdopant_x + .17 insert_wx('wg_deep', 2 * outer_wgx, 0) insert('wg_shallow', .15, outer_wgx) insert('wg_shallow', -outer_wgx, -.15) insert_wx('dp_e', .1, 0) insert('dp_n', .05, outer_wgx) insert('dp_p', -outer_wgx, -.05) insert_wx('dp_n+', .3, ppdopant_x) insert_wx('dp_p+', .3, -ppdopant_x) for side in [-1, 1]: insert_wx('m4_ledpad', .28, side * ppdopant_x) insert_wx('v3', .25, side * ppdopant_x) insert_wx('m5_wiring', .3, side * ppdopant_x) insert_wx('m5_wiring', .6, side * (ppdopant_x + .3)) insert_wx('v5', side * .4, side * (ppdopant_x + .4)) # WG wg = insert_wx('wg_deep', .2, 2) # SNSPD nw = insert_wx('m2_nw', .3, wg.xmax + 1) pad = insert('m1_nwpad', nw.xmax - .1, nw.xmax + .4) via = insert('v3', pad.xmax - .3, pad.xmax - .1) wire = insert('m5_wiring', pad.x, pad.x + 1) insert_wx('v5', .3, wire.xmax - .2) # htron snspd = x_section << pg.snspd( wire_width=0.05, wire_pitch=0.3, size=(2, 2), layer=lys['m2_nw']) snspd.rotate(90) snspd.y = wire.y snspd.xmin = wire.xmax + 1 heater = x_section << pg.rectangle((2.4, 2), layer=lys['m3_res']) heater.xmin = snspd.xmin heater.y = snspd.y pad = insert('m1_nwpad', snspd.xmin - .5, snspd.xmin + .02) via = insert('v5', pad.xmin, pad.xmax - .2) wire = insert_wx('m5_wiring', .4, via.x) insert_wx('v3', .3, wire.x - .05) # pad = insert('m1_nwpad', heater.xmax - .2, heater.xmax-.05) via = insert('v5', heater.xmax - .2, heater.xmax - .05) wire = insert_wx('m5_wiring', .4, via.x) insert_wx('v3', .3, wire.x - .05) # resistor res = insert_wx('m3_res', 2, wire.xmax + 1.5) via1 = insert('v5', res.xmin + .1, res.xmin + .5) wire1 = insert_wx('m5_wiring', .5, via1.x) insert_wx('v3', .4, wire1.x) via2 = insert('v5', res.xmax - .5, res.xmax - .1) wire2 = insert_wx('m5_wiring', .5, via2.x) insert_wx('v3', .4, wire2.x) insert('wg_deep', wire1.xmin - .1, wire2.xmax + .1) return x_section
create_image(D, 'optimal_step') # example-optimal_90deg import phidl.geometry as pg from phidl import quickplot as qp D = pg.optimal_90deg(width = 100.0, num_pts = 15, length_adjust = 1, layer = 0) qp(D) # quickplot the geometry create_image(D, 'optimal_90deg') # example-snspd import phidl.geometry as pg from phidl import quickplot as qp D = pg.snspd(wire_width = 0.2, wire_pitch = 0.6, size = (10,8), num_squares = None, turn_ratio = 4, terminals_same_side = False, layer = 0) qp(D) # quickplot the geometry create_image(D, 'snspd') # example-snspd_expanded import phidl.geometry as pg from phidl import quickplot as qp D = pg.snspd_expanded(wire_width = 0.3, wire_pitch = 0.6, size = (10,8), num_squares = None, connector_width = 1, connector_symmetric = False, turn_ratio = 4, terminals_same_side = False, layer = 0) qp(D) # quickplot the geometry create_image(D, 'snspd_expanded')
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 if __name__ == "__main__": import phidl.geometry as pg import gdsfactory as gf c = pg.rectangle() c = pg.snspd() c2 = from_phidl(component=c) print(c2.ports) gf.show(c2)
def wg_to_snspd(wgnw_width=0.1, wgnw_length=100, wgnw_gap=0.15, num_squares=5000.0, meander_width=0.4, meander_fill_factor=0.5, wg_width=0.75): ''' Waveguide coupled to SNSPD with inductor (meander). The length and width of the meander are chosen so that it is approximately square Args: meander_width (float): nanowire width within meander inductor num_squares (float): total squares in meander and out-and-back wgnw_width (float): width of out-and-back nanowire wgnw_length (float): length of out-and-back wgnw_gap (float): spacing between the out-and-back wires wg_width (float): waveguide width Ports: el_1: wiring port el_gnd: wiring port wg_in: input optical port de_edge: edge of explicit waveguide on the SNSPD side ''' D = Device('wg_to_snspd') # Calculations and checks numsquares_wgnw = 2 * wgnw_length / wgnw_width numsquares_meander = num_squares - numsquares_wgnw if numsquares_meander < 1000: print( 'Warning: Not enough squares in SNSPD meander. Clipped to 1000 from {:.1f}' .format(numsquares_meander)) numsquares_meander = 1000 meander_pitch = meander_width / meander_fill_factor meander_length = np.sqrt(numsquares_meander * meander_width * meander_pitch) wgnw_pitch = wgnw_width + wgnw_gap wgnw_distance_to_edge = wg_width / 2 - wgnw_width - wgnw_gap / 2 if wgnw_distance_to_edge < 0: print('Warning: nanowire will overhang side of waveguide by {:.3f} um'. format(-wgnw_distance_to_edge)) numsquares_per_taper = 3 # approximate D.info[ 'num_squares'] = numsquares_meander + numsquares_wgnw - meander_length / meander_width + 3 * numsquares_per_taper # D.info['expected_resistance'] = D.info['num_squares']*EXPECTED_RSQ_WSI D.info['wire_width'] = wgnw_width D.info['length'] = wgnw_length # Geometry meander = D << pg.snspd(wire_width=meander_width, wire_pitch=meander_pitch, terminals_same_side=False, size=(meander_length, None), num_squares=numsquares_meander, layer=lys['m2_nw']) meander.reflect(p1=(0, 0), p2=(1, 0)) Taper = pg.optimal_step(start_width=wgnw_width, end_width=meander_width, num_pts=50, width_tol=1e-3, anticrowding_factor=1.2, layer=lys['m2_nw']) taper1 = D << Taper taper1.connect(2, meander.ports[1]) wgnw = D << pg.optimal_hairpin(width=wgnw_width, pitch=wgnw_pitch, length=wgnw_length, layer=lys['m2_nw']) wgnw.connect(2, taper1.ports[1]) taper2 = D << Taper taper2.reflect() taper2.connect(1, wgnw.ports[1]) # Electrical ports exit_bend = D << pg.optimal_90deg( width=meander_width, num_pts=15, length_adjust=1, layer=lys['m2_nw']) exit_bend.connect(port=2, destination=taper2.ports[2]) D.add_port('el_gnd', port=exit_bend.ports[1]) exit_taper = D << pg.optimal_step(start_width=meander_width, end_width=meander_width * 4, num_pts=50, width_tol=1e-3, anticrowding_factor=1.2, layer=lys['m2_nw']) exit_taper.connect(1, meander.ports[2]) D.add_port('el_1', port=exit_taper.ports[2]) # Waveguide and optical ports wg = D << pg.compass(size=[wgnw_length + wgnw_distance_to_edge, wg_width], layer=lys['wg_deep']) wg.xmax = wgnw.xmax wg.y = wgnw.y D.add_port('de_edge', port=wg.ports['E']) D.add_port('wg_in', port=wg.ports['W']) D.ports['de_edge'].info['is_waveguide_edge'] = True pos = D.ports['wg_in'].midpoint D.move(-1 * pos) return D