def transition(cross_section1, cross_section2, width_type='sine'): """ Creates a CrossSection that smoothly transitions between two input CrossSections. Only cross-sectional elements that have the `name` (as in X.add(..., name = 'wg') ) parameter specified in both input CrosSections will be created. Port names will be cloned from the input CrossSections in reverse. Parameters ---------- cross_section1 : CrossSection First input CrossSection cross_section2 : CrossSection Second input CrossSection width_type : {'sine', 'linear'} Sets the type of width transition used if any widths are different between the two input CrossSections. Returns ------- CrossSection A smoothly-transitioning CrossSection """ X1 = cross_section1 X2 = cross_section2 Xtrans = CrossSection() for alias in X1.aliases.keys(): if alias in X2.aliases: offset1 = X1[alias]['offset'] offset2 = X2[alias]['offset'] width1 = X1[alias]['width'] width2 = X2[alias]['width'] if callable(offset1): offset1 = offset1(1) if callable(offset2): offset2 = offset2(0) if callable(width1): width1 = width1(1) if callable(width2): width2 = width2(0) offset_fun = _sinusoidal_transition(offset1, offset2) if width_type == 'sine': width_fun = _sinusoidal_transition(width1, width2) elif width_type == 'linear': width_fun = _linear_transition(width1, width2) else: raise ValueError("[PHIDL] transition() width_type " + "argument must be one of {'sine','linear'}") Xtrans.add(width=width_fun, offset=offset_fun, layer=X1[alias]['layer'], ports=(X2[alias]['ports'][0], X1[alias]['ports'][1]), name=alias) return Xtrans
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 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 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 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 mmi(W, L_tp, W_tp, L_mmi, W_mmi, Y_mmi): mm = 10**3 um = 1 # Create CrossSections X1 = CrossSection() X2 = CrossSection() X1.add(width=W, offset=0, layer=30, name='wg') X2.add(width=W_tp, offset=0, layer=30, name='wg') # create Devices by extruding them P1 = pp.straight(length=10 * um) P2 = pp.straight(length=10 * um) WG1 = P1.extrude(X1) WG2 = P2.extrude(X2) # Place both cross-section Devices and quickplot them D = Device() wg1 = D << WG1 wg2 = D << WG2 wg2.movex(L_tp) # Create the transitional CrossSection Xtrans = pp.transition(cross_section1=X1, cross_section2=X2, width_type='linear') # Create a Path for the transitional CrossSection to follow P3 = pp.straight(length=L_tp) # Use the transitional CrossSection to create a Device WG_in = P3.extrude(Xtrans) WG_out1 = P3.extrude(Xtrans) WG_out2 = P3.extrude(Xtrans) WG_out1.mirror((0, 1), (0, 0)) WG_out2.mirror((0, 1), (0, 0)) WG_in << pg.rectangle(size=(L_mmi, W_mmi), layer=30).move( [L_tp, -W_mmi / 2]) WG_in << WG_out1.move([L_mmi + 2 * L_tp, Y_mmi / 2]) WG_in << WG_out2.move([L_mmi + 2 * L_tp, -Y_mmi / 2]) return WG_in
def connection_UR(x1, y1, x2, y2, elec_m, elec_s, e_e_gap, Rt): #right down D = Device() x1t = x1 + elec_m / 2 + elec_s / 2 + e_e_gap x1b = x1 - elec_m / 2 - elec_s / 2 - e_e_gap y2t = y2 - elec_m / 2 - elec_s / 2 - e_e_gap y2b = y2 + elec_m / 2 + elec_s / 2 + e_e_gap points = np.array([(x1, y1), (x1, y2), (x2, y2)]) points_t = np.array([(x1t, y1), (x1t, y2t), (x2, y2t)]) points_b = np.array([(x1b, y1), (x1b, y2b), (x2, y2b)]) M = pp.smooth( points=points, radius=Rt + elec_m / 2 + elec_s / 2 + e_e_gap, corner_fun=pp.arc, ) M.rotate(90, center=(x1, y1)) Mt = pp.smooth( points=points_t, radius=Rt, corner_fun=pp.arc, ) Mt.rotate(90, center=(x1t, y1)) Mb = pp.smooth( points=points_b, radius=Rt + 2 * (elec_m / 2 + elec_s / 2 + e_e_gap), corner_fun=pp.arc, ) Mb.rotate(90, center=(x1b, y1)) X = CrossSection() X.add(width=elec_m, offset=0, layer=70) Xt = CrossSection() Xt.add(width=elec_s, offset=0, layer=70) Xb = CrossSection() Xb.add(width=elec_s, offset=0, layer=70) L = M.extrude(X) #Left Trace Lt = Mt.extrude(Xt) #Left Trace Lb = Mb.extrude(Xb) #Left Trace D << L D << Lt D << Lb return D
def connect(x2, y2, middle_e_width, chip_width, chip_height, pos, radius, length, e_e_gap, setpos): mm = 10**3 um = 1 M = Path() # Dimensions margin = 0.5 * mm to_pad_term = 0.1 * mm pad = 50 * um pitch = 100 * um side_e_width = middle_e_width * 2 rad_gap = e_e_gap + middle_e_width / 2 + side_e_width / 2 if pos == 't': Rt = radius + rad_gap * 2 elif pos == 'm': Rt = radius + rad_gap else: Rt = radius if setpos == 't': set_bias = 0.6 * mm elif setpos == 'm': set_bias = 0.3 * mm else: set_bias = 0 x1 = (chip_width - length) / 2 - Rt - set_bias y1 = margin + to_pad_term points = np.array([(x1, y1), (x1, y2), (x2, y2)]) points = rotate90(points) M = pp.smooth( points=points, radius=Rt, corner_fun=pp.arc, ) M.rotate(90) X = CrossSection() if pos == 'm': X.add(width=middle_e_width, offset=0, layer=3) else: X.add(width=side_e_width, offset=0, layer=3) L = M.extrude(X) #Left Trace if pos == 'm': # adding pads S = pg.rectangle(size=(pad, pad), layer=3) L.add_ref(S).move([x1 - pad / 2, margin]) L.add_ref(S).move([x1 - pad / 2 - pitch, margin]) L.add_ref(S).move([x1 - pad / 2 + pitch, margin]) xm1 = x1 - middle_e_width / 2 xm2 = x1 + middle_e_width / 2 xm3 = x1 + pad / 2 xm4 = x1 - pad / 2 xpts = (xm1, xm2, xm3, xm4) ypts = (y1, y1, margin + pad, margin + pad) L.add_polygon([xpts, ypts], layer=3) xt1 = xm1 + middle_e_width + e_e_gap xt2 = xt1 + side_e_width xt3 = xm3 + pitch xt4 = xm4 + pitch xpts = (xt1, xt2, xt3, xt4) ypts = (y1, y1, margin + pad, margin + pad) L.add_polygon([xpts, ypts], layer=3) xb1 = xm1 - side_e_width - e_e_gap xb2 = xm1 - e_e_gap xb3 = xm3 - pitch xb4 = xm4 - pitch xpts = (xb1, xb2, xb3, xb4) ypts = (y1, y1, margin + pad, margin + pad) L.add_polygon([xpts, ypts], layer=3) R = pg.copy(L) # Right Trace R.mirror((chip_width / 2, chip_height), (chip_width / 2, 0)) D = Device('trace') D << L D << R return D