Example #1
0
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
Example #4
0
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)
Example #5
0
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