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 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 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_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 complicated_waveguide(width=10, height=1, x=10, y=25, rotation=15): C = Device('complicated_waveguide') C.add_polygon([(0, 0), (width, 0), (width, height), (0, height)]) C.add_port(name=1, midpoint=[0, height / 2], width=height, orientation=180) C.add_port(name=2, midpoint=[width, height / 2], width=height, orientation=0) C.rotate(angle=rotation, center=(0, 0)) C.move((x, y)) return C
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 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 htron(nanowire_width = 0.15, nanowire_spacing = 0.1, meander_num_squares = 5000, heater_num_squares = 1): # Create blank device D = Device(name = 'htron') # Basic calculations extra_meander_width = 2 extra_meander_height = 1 area_per_meander_sq = (nanowire_width+nanowire_spacing)*nanowire_width meander_area = area_per_meander_sq*meander_num_squares meander_total_width = np.sqrt(meander_area/heater_num_squares) meander_total_height = heater_num_squares*meander_total_width meander_size = np.array([meander_total_width, meander_total_height]) heater_size = meander_size meander_size = meander_size + [extra_meander_width,extra_meander_height] # meander_size = heater_size + np.array([meander_extra_width,0]) meander_pitch = nanowire_width + nanowire_spacing # heater_standoff_y = 1 # Create components Meander = pg.snspd_expanded(wire_width = nanowire_width, wire_pitch = meander_pitch, size = meander_size, terminals_same_side = False, connector_width = nanowire_width*4, layer = lys['m2_nw']) # heater_size_actual = heater_size + np.array([0, heater_standoff_y]) Heater = pg.compass(size = heater_size, layer = lys['m3_res']) # Add references to components m = D.add_ref(Meander) h = D.add_ref(Heater) h.center = m.center # Record meta-information heater_area = heater_size[0]*heater_size[1] D.info['nanowire_width'] = nanowire_width D.info['nanowire_pitch'] = nanowire_width + nanowire_spacing D.info['meander_num_squares'] = np.round(m.info['num_squares'],2) D.info['meander_size'] = np.round((m.xsize, m.ysize),2).tolist() D.info['heater_size'] = np.round(heater_size,2).tolist() D.info['heater_area'] = np.round(heater_size[0]*heater_size[1],2) D.info['heater_num_squares'] = np.round(heater_num_squares,2) D.info['overlap_area'] = np.round(m.ysize*heater_size[0],1) D.info['overlap_num_squares'] = np.round(heater_area/area_per_meander_sq,1) D.add_port(name = 1, port = h.ports['N']) D.add_port(name = 2, port = h.ports['S']) D.add_port(name = 3, port = m.ports[1]) D.add_port(name = 4, port = m.ports[2]) return D
# negative number, separate the ports). wg1.connect(port='wgport1', destination=wg2.ports['wgport2']) wg3.connect(port='wgport2', destination=wg2.ports['wgport1'], overlap=-1) qp(D) # quickplot it! #============================================================================== # Adding ports #============================================================================== # Although our waveguides wg1/wg2/wg3 have ports, they're only references # of the device ``D`` we're working in, and D itself does not -- it only draws # the subports (ports of wg1, wg2, wg3) as a convenience. We need to add ports # that we specifically want in our new device ``D``. add_port() can take a # port argument which allows you to pass it an underlying reference port to # copy. You can also rename the port if you desire: p1 = D.add_port(port=wg1.ports['wgport2'], name=1) p2 = D.add_port(port=wg3.ports['wgport1'], name=2) # Optionally, let's assign some information to these ports. Every Port has # a Port.info dictionary which can be used to store information about that port p1.info['is_useful'] = True p2.info['is_useful'] = False qp(D) # quickplot it! #============================================================================== # Taking things a level higher #============================================================================== # Now that we have our device ``D`` which is a multi-waveguide device, we # can add references to that device in a new blank canvas we'll call ``D2``. # We'll add two copies of ``D`` to D2, and shift one so we can see them both
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