def test_move(): # Test polygon move D = Device() p = D.add_polygon([(8, 6, 7, 9), (6, 8, 9, 5)]) p.move([1.7, 0.8]) h = D.hash_geometry(precision=1e-4) assert (h == '57a86bce5f60f7bc78c7c30473a544b736d2afb3') p.movex(13.9) h = D.hash_geometry(precision=1e-4) assert (h == '8fe6706e05ebe1512ee2efe2582546b949fbc48f') p.movey(19.2) h = D.hash_geometry(precision=1e-4) assert (h == '7df43241eca2dd11f267c25876e650eadaca7d9f') # Test Device move D = Device() D.add_polygon([(8, 6, 7, 9), (6, 8, 9, 5)]) D.add_polygon([(8, 6, 7, 9, 7, 0), (6, 8, 9, 5, 7, 0)]) D.move([1.7, 0.8]) h = D.hash_geometry(precision=1e-4) assert (h == 'c863156dd00a590dc02823e1791554d4142b1ea9') # Test label move D = Device() D.add_polygon([(8, 8, 8, 8), (6, 6, 6, 6)]) l = D.add_label('testing', position=D.center) print(all(l.center == D.center)) D.rotate(45) print(np.allclose(l.center, D.center)) D.move([70000.5, 30000.5]) print(np.allclose(l.center, D.center)) D.rotate(75) print(np.allclose(l.center, D.center)) D.mirror([7, 5]) print(np.allclose(l.center, D.center))
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
# Create two Devices with the same polygons, but different layers D = pg.ellipse(radii=[10, 15], layer=2) D2 = pg.ellipse(radii=[10, 15], layer=77) print(D.hash_geometry()) # 143f7faa8558feb3036487c155083bd53fad4913 print(D2.hash_geometry() ) # cd36f7c5da226f8bb6c29b64acb8c442dcb379c4 <-- Different! # Create two Devices with the same polygons, but added in different orders D = pg.ellipse(radii=[10, 15], layer=2) D << pg.rectangle(size=[7.5, 8.6], layer=99) D2 = pg.rectangle(size=[7.5, 8.6], layer=99) D2 << pg.ellipse(radii=[10, 15], layer=2) print(D.hash_geometry()) # f4d11e73389a1a1578a181c269f79424392482d6 print(D2.hash_geometry() ) # f4d11e73389a1a1578a181c269f79424392482d6 <-- Same! Order ignored # Show manipulation-invariance # WARNING: there is *always* an intrinsic risk of floating-point manipulations # producing rounding errors, but the algorithm should be very robust # (~10^-7 errors/point likelihood measured @ precision of 1e-4) D = pg.ellipse(radii=[10, 15], layer=2) print(D.hash_geometry( precision=1e-4)) # 143f7faa8558feb3036487c155083bd53fad4913 D.move([1.751, 0]).rotate(37.9).rotate(-37.9).move([-1.751, 0]) print(D.hash_geometry( precision=1e-4)) # 143f7faa8558feb3036487c155083bd53fad4913 D.movex(1e-7) # Moving points by << precision should yield the same result print(D.hash_geometry( precision=1e-4)) # 143f7faa8558feb3036487c155083bd53fad4913
e_e_gap = 10 * um #Gap between electrodes (waveguide in the middle) via = elec_w * 0.8 x_pos = 1.5 * mm y_pos = wg_y_off + 3 * V_Groove_Spacing + 40 * um x_base = x_pos y_base = y_pos mi_x = x_pos + im_length #save for later alignment of mirrored path mi_y = y_pos IM = Device('IM') (IM, xl) = li.dcim(im_gap, im_length, coupler_l1, im_r, im_angle, elec_w, e_e_gap, via, wg_width_oc, V_Groove_Spacing) D << IM.move([x_pos, y_pos]) #straight input wg top P = Path() P.append(pp.straight(length=x_pos + xmargin)) P.movex(-xmargin) P.movey(y_pos + V_Groove_Spacing) X = CrossSection() X.add(width=wg_width_oc, offset=0, layer=30) D << P.extrude(X) #straight input wg bottom P = Path() P.append(pp.straight(length=x_pos + xmargin)) P.movex(-xmargin)
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