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 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 rfpm(wg_width, length, middle_e_width, e_e_gap): side_electrode_width = middle_e_width * 2 P = Path() P.append(pp.straight(length=length)) X = CrossSection() X.add(width=wg_width, offset=0, layer=30) RFPM = Device() RFPM << P.extrude(X) Rt = pg.rectangle(size=(length, side_electrode_width), layer=40) Rm = pg.rectangle(size=(length, middle_e_width), layer=40) Rb = pg.rectangle(size=(length, side_electrode_width), layer=40) RFPM << Rt.move([0, e_e_gap / 2]) RFPM << Rm.move([0, -middle_e_width - e_e_gap / 2]) RFPM << Rb.move( [0, -middle_e_width - side_electrode_width - e_e_gap - e_e_gap / 2]) square = middle_e_width * 0.9 side_height = side_electrode_width * 0.9 square_rec_offset = (side_electrode_width - side_height) / 2 square_rec_offset_m = (middle_e_width - square) / 2 e_left = 0 e_right = e_left + length - square #side_e_width R = pg.rectangle(size=(length, middle_e_width), layer=40) R2 = pg.rectangle(size=(length, side_electrode_width), layer=40) S = pg.rectangle(size=(square, square), layer=50) S2 = pg.rectangle(size=(square, side_height), layer=50) #top electrode h_top = e_e_gap / 2 RFPM.add_ref(S2).move([e_left, h_top + square_rec_offset]) RFPM.add_ref(S2).move([e_right, h_top + square_rec_offset]) #middle electrode h_mid = -middle_e_width - e_e_gap / 2 RFPM.add_ref(S).move([e_left, h_mid + square_rec_offset_m]) RFPM.add_ref(S).move([e_right, h_mid + square_rec_offset_m]) #bottom electrode h_bot = -middle_e_width - side_electrode_width - e_e_gap - e_e_gap / 2 RFPM.add_ref(S2).move([e_left, h_bot + square_rec_offset]) RFPM.add_ref(S2).move([e_right, h_bot + square_rec_offset]) return RFPM
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 straight(length=5, num_pts=100): """ Creates a straight Path Parameters ---------- length : int or float Total length of straight path num_pts : int Number of points along Path Returns ------- Path A Path object with the specified straight section """ x = np.linspace(0, length, num_pts) y = x * 0 points = np.array((x, y)).T P = Path() P.append(points) return P
def eom_sym(wg_width, length, middle_e_width, e_e_gap, chip_width, offset, radius): euler_y = mod_euler(radius=radius, angle=-45)[1][1] euler_x = mod_euler(radius=radius, angle=-45)[1][0] wg_wg_sep = (middle_e_width + e_e_gap) / 2 - 2 * euler_y straight = wg_wg_sep * np.sqrt(2) if wg_wg_sep < 0: raise Exception( "middle_e_width is set too small with respect to Euler radius") left = chip_width / 2 - length / 2 - 2 * euler_x - wg_wg_sep + offset right = left P1 = Path() P1.append(pp.straight(length=left)) P1.append(mod_euler(radius=radius, angle=-45)[0]) P1.append(pp.straight(length=straight)) P1.append(mod_euler(radius=radius, angle=45)[0]) P1.append(pp.straight(length=length)) P1.append(mod_euler(radius=radius, angle=45)[0]) P1.append(pp.straight(length=straight)) P1.append(mod_euler(radius=radius, angle=-45)[0]) P1.append(pp.straight(length=right)) X = CrossSection() X.add(width=wg_width, offset=0, layer=1) waveguide_device1 = P1.extrude(X) E = Device('EOM_GHz') b1 = E.add_ref(waveguide_device1) b2 = E.add_ref(waveguide_device1) b2.mirror((0, 0), (1, 0)) square = middle_e_width * 0.6 square_rec_offset = (middle_e_width - square) / 2 e_left = left + 2 * euler_x + wg_wg_sep e_right = left + 2 * euler_x + wg_wg_sep + length - square #side_e_width R = pg.rectangle(size=(length, middle_e_width), layer=10) S = pg.rectangle(size=(square, square), layer=2) #top electrode h_top = middle_e_width / 2 + e_e_gap E.add_ref(R).move([e_left, h_top]) E.add_ref(S).move([e_left, h_top + square_rec_offset]) E.add_ref(S).move([e_right, h_top + square_rec_offset]) #middle electrode E.add_ref(R).move([e_left, -middle_e_width / 2]) E.add_ref(S).move([e_left, -square / 2]) E.add_ref(S).move([e_right, -square / 2]) #bottom electrode h_bot = -3 * middle_e_width / 2 - e_e_gap E.add_ref(R).move([e_left, h_bot]) E.add_ref(S).move([e_left, h_bot + square_rec_offset]) E.add_ref(S).move([e_right, h_bot + square_rec_offset]) #E << R return E
def dcim(im_gap, im_length, coupler_l, im_r, im_angle, elec_w, e_e_gap, via, wg_width, V_Groove_Spacing): P = Path() euler_y = mod_euler(radius=im_r, angle=im_angle)[1][1] euler_x = mod_euler(radius=im_r, angle=im_angle)[1][0] l_bend = ((V_Groove_Spacing - im_gap - 4 * euler_y - wg_width) / 2) / np.sin(np.pi * im_angle / 180) P.append(pp.euler(radius=im_r, angle=-im_angle)) P.append(pp.straight(length=l_bend)) P.append(pp.euler(radius=im_r, angle=im_angle)) P.append(pp.straight(length=coupler_l)) P.append(pp.euler(radius=im_r, angle=im_angle)) P.append(pp.euler(radius=im_r, angle=-im_angle)) P.append(pp.straight(length=im_length)) P.append(pp.euler(radius=im_r, angle=-im_angle)) P.append(pp.euler(radius=im_r, angle=im_angle)) P.append(pp.straight(length=coupler_l)) P.append(pp.euler(radius=im_r, angle=im_angle)) P.append(pp.straight(length=l_bend)) P.append(pp.euler(radius=im_r, angle=-im_angle)) P.movey(V_Groove_Spacing) X = CrossSection() X.add(width=wg_width, offset=0, layer=30) IM = Device('IM') IM << P.extrude(X) IM << P.extrude(X).mirror(p1=[1, V_Groove_Spacing / 2], p2=[2, V_Groove_Spacing / 2]) R1 = pg.rectangle(size=(im_length, elec_w), layer=40) R2 = pg.rectangle(size=(im_length, elec_w), layer=40) R3 = pg.rectangle(size=(im_length, elec_w), layer=40) R4 = pg.rectangle(size=(im_length, elec_w), layer=40) movex = euler_x * 4 + coupler_l + l_bend * np.cos(np.pi * im_angle / 180) movey = euler_y * 2 + im_gap / 2 + wg_width IM << R1.move( [movex, V_Groove_Spacing / 2 + movey + e_e_gap / 2 - wg_width / 2]) IM << R2.move([ movex, V_Groove_Spacing / 2 + movey - e_e_gap / 2 - elec_w - wg_width / 2 ]) IM << R3.move( [movex, V_Groove_Spacing / 2 - movey + e_e_gap / 2 + wg_width / 2]) IM << R4.move([ movex, V_Groove_Spacing / 2 - movey - e_e_gap / 2 - elec_w + wg_width / 2 ]) return IM, movex
def smooth( points = [(20,0), (40,0), (80,40), (80,10), (100,10),], radius = 4, corner_fun = euler, **kwargs ): """ Create a smooth path from a series of waypoints. Corners will be rounded using `corner_fun` and any additional key word arguments (for example, `use_eff = True` when `corner_fun = pp.euler`) Parameters ---------- points : array-like[N][2] List of waypoints for the path to follow radius : int or float Radius of curvature, this argument will be passed to `corner_fun` corner_fun : function The function that controls how the corners are rounded. Typically either `arc()` or `euler()` **kwargs : dict Extra keyword arguments that will be passed to `corner_fun` Returns ------- Path A Path object with the specified smoothed path. """ points = np.asfarray(points) normals = np.diff(points, axis = 0) normals = (normals.T/np.linalg.norm(normals, axis = 1)).T # normals_rev = normals*np.array([1,-1]) dx = np.diff(points[:,0]) dy = np.diff(points[:,1]) ds = np.sqrt(dx**2 + dy**2) theta = np.degrees(np.arctan2(dy,dx)) dtheta = np.diff(theta) dtheta = dtheta - 360*np.floor((dtheta + 180)/360) # FIXME add caching # Create arcs paths = [] radii = [] for dt in dtheta: P = corner_fun(radius = radius, angle = dt, **kwargs) chord = np.linalg.norm(P.points[-1,:] - P.points[0,:]) r = (chord/2)/np.sin(np.radians(dt/2)) r = np.abs(r) radii.append(r) paths.append(P) d = np.abs(np.array(radii)/np.tan(np.radians(180-dtheta)/2)) encroachment = np.concatenate([[0],d]) + np.concatenate([d,[0]]) if np.any(encroachment > ds): raise ValueError('[PHIDL] smooth(): Not enough distance between points to to fit curves. Try reducing the radius or spacing the points out farther') p1 = points[1:-1,:] - normals[:-1,:]*d[:,np.newaxis] # Move arcs into position new_points = [] new_points.append( [points[0,:]] ) for n,dt in enumerate(dtheta): P = paths[n] P.rotate(theta[n] - 0) P.move(p1[n]) new_points.append(P.points) new_points.append( [points[-1,:]] ) new_points = np.concatenate(new_points) P = Path() P.rotate(theta[0]) P.append(new_points) P.move(points[0,:]) return P
def smooth(points=[ (20, 0), (40, 0), (80, 40), (80, 10), (100, 10), ], radius=4, corner_fun=euler, **kwargs): """ Create a smooth path from a series of waypoints. Corners will be rounded using `corner_fun` and any additional key word arguments (for example, `use_eff = True` when `corner_fun = pp.euler`) Parameters ---------- points : array-like[N][2] List of waypoints for the path to follow radius : int or float Radius of curvature, this argument will be passed to `corner_fun` corner_fun : function The function that controls how the corners are rounded. Typically either `arc()` or `euler()` **kwargs : dict Extra keyword arguments that will be passed to `corner_fun` Returns ------- Path A Path object with the specified smoothed path. """ points, normals, ds, theta, dtheta = _compute_segments(points) colinear_elements = np.concatenate([[False], np.abs(dtheta) < 1e-6, [False]]) if np.any(colinear_elements): new_points = points[~colinear_elements, :] points, normals, ds, theta, dtheta = _compute_segments(new_points) if np.any(np.abs(np.abs(dtheta) - 180) < 1e-6): raise ValueError( '[PHIDL] smooth() received points which double-back on themselves' + '--turns cannot be computed when going forwards then exactly backwards.' ) # FIXME add caching # Create arcs paths = [] radii = [] for dt in dtheta: P = corner_fun(radius=radius, angle=dt, **kwargs) chord = np.linalg.norm(P.points[-1, :] - P.points[0, :]) r = (chord / 2) / np.sin(np.radians(dt / 2)) r = np.abs(r) radii.append(r) paths.append(P) d = np.abs(np.array(radii) / np.tan(np.radians(180 - dtheta) / 2)) encroachment = np.concatenate([[0], d]) + np.concatenate([d, [0]]) if np.any(encroachment > ds): raise ValueError( '[PHIDL] smooth(): Not enough distance between points to to fit curves. Try reducing the radius or spacing the points out farther' ) p1 = points[1:-1, :] - normals[:-1, :] * d[:, np.newaxis] # Move arcs into position new_points = [] new_points.append([points[0, :]]) for n, dt in enumerate(dtheta): P = paths[n] P.rotate(theta[n] - 0) P.move(p1[n]) new_points.append(P.points) new_points.append([points[-1, :]]) new_points = np.concatenate(new_points) P = Path() P.rotate(theta[0]) P.append(new_points) P.move(points[0, :]) return P