459 KiB
459 KiB
In [1]:
import numpy as np import skrf from skrf.media import DistributedCircuit import skrf.frequency as freq import skrf.network as net import skrf.util import matplotlib.pyplot as plt %matplotlib inline plt.rcParams['figure.figsize'] = [10, 10] f = freq.Frequency(0.4, 2, 1001) tem = DistributedCircuit(f, z0=50) bjt = net.Network('BFU520_Spar_NF_400MHz-2GHz/BFU520_05V0_005mA_NF_SP.s2p').interpolate(f) bjt
Out[1]:
2-Port Network: 'BFU520_05V0_005mA_NF_SP', 0.4-2.0 GHz, 1001 pts, z0=[ 50.+0.j 50.+0.j]
In [2]:
bjt.plot_s_smith()
In [3]:
# let's add an inductor to the emitter # to do this we need a function that lets you combine two port networks in series # so we can model it from scipy.interpolate import interp1d def series(a, b): newz = a.z + b.z ret = net.Network.from_z(newz, frequency=a.frequency) noiseless = not a.noisy and not b.noisy if noiseless: return ret if a.noisy: an = a.n af = a.f_noise else: an = np.zeros(b.n.shape) af = b.f_noise if b.noisy: bn = b.n bf = b.f_noise else: bn = np.zeros(a.n.shape) bf = a.f_noise if bf != af: # TODO interpolate noise values for b into a frequency ranges # bn = ??? raise NotImplementedError a_real = interp1d(a.frequency.f, a.a.real, axis=0, kind=net.Network.noise_interp_kind) a_imag = interp1d(a.frequency.f, a.a.imag, axis=0, kind=net.Network.noise_interp_kind) a_abcd = a_real(af.f) + 1.j * a_imag(af.f) b_real = interp1d(b.frequency.f, b.a.real, axis=0, kind=net.Network.noise_interp_kind) b_imag = interp1d(b.frequency.f, b.a.imag, axis=0, kind=net.Network.noise_interp_kind) b_abcd = b_real(bf.f) + 1.j * b_imag(bf.f) # calculate noise # based on https://onlinelibrary.wiley.com/doi/pdf/10.1002/9781119073093.app3 a1 = 1 b1 = (b_abcd[:,0,0] - a_abcd[:,0,0])/(a_abcd[:,1,0] + b_abcd[:,1,0]) c1 = 0 d1 = b_abcd[:,1,0]/(a_abcd[:,1,0] + b_abcd[:,1,0]) a2 = 1 b2 = (a_abcd[:,0,0] - b_abcd[:,0,0])/(a_abcd[:,1,0] + b_abcd[:,1,0]) c2 = 0 d2 = a_abcd[:,1,0]/(a_abcd[:,1,0] + b_abcd[:,1,0]) ee = (a1*np.conj(a1)*an[:,0,0] + b1*np.conj(b1)*bn[:,1,1] + a2*np.conj(a2)*bn[:,1,1] + b2*np.conj(b2)*bn[:,1,1] + a1*np.conj(b1)*an[:,0,1] + b1*np.conj(a1)*an[:,1,0] + a2*np.conj(b2)*bn[:,0,1] + b2*np.conj(a2)*bn[:,1,0]) ii = (c1*np.conj(c1)*an[:,0,0] + d1*np.conj(d1)*an[:,1,1] + c2*np.conj(c2)*bn[:,0,0] + d2*np.conj(d2)*bn[:,1,1] + c1*np.conj(d1)*an[:,0,1] + d1*np.conj(c1)*an[:,1,0] + c2*np.conj(d2)*bn[:,0,1] + d2*np.conj(c2)*bn[:,1,0]) ei = (a1*np.conj(c1)*an[:,0,0] + b1*np.conj(d1)*an[:,1,1] + a2*np.conj(c2)*bn[:,0,0] + b2*np.conj(d2)*bn[:,1,1] + a1*np.conj(d1)*an[:,0,1] + b1*np.conj(c1)*an[:,1,0] + a2*np.conj(d2)*bn[:,0,1] + b2*np.conj(c2)*bn[:,1,0]) ie = np.conj(ei) ret.noise = np.moveaxis(np.array([[ee, ei], [ie, ii]]), 2, 0) ret.noise_freq = af return ret
In [4]:
bjt_degen = series(bjt, tem.shunt_inductor(1e-9)) bjt_degen.plot_s_smith() idx_900mhz = skrf.util.find_nearest_index(bjt.f, 915.e+6) bjt_degen.z_opt[idx_900mhz]
Out[4]:
(58.285348012831371+12.185071598304109j)
In [5]:
# calculate the stability circles for the source and load impedances idx_900mhz = skrf.util.find_nearest_index(bjt_degen.f, 915.e+6) sqabs = lambda x: np.square(np.absolute(x)) delta = bjt_degen.s11.s*bjt_degen.s22.s - bjt_degen.s12.s*bjt_degen.s21.s rl = np.absolute((bjt_degen.s12.s * bjt_degen.s21.s)/(sqabs(bjt_degen.s22.s) - sqabs(delta))) cl = np.conj(bjt_degen.s22.s - delta*np.conj(bjt_degen.s11.s))/(sqabs(bjt_degen.s22.s) - sqabs(delta)) rl_900mhz = rl[idx_900mhz][0, 0] cl_900mhz = cl[idx_900mhz][0, 0] rl_900mhz, cl_900mhz
Out[5]:
(3.048555602935056, (3.001540263823371+2.6459699316881777j))
In [6]:
def calc_circle(c, r): theta = np.linspace(0, 2*np.pi, 1000) return c + r*np.exp(1.0j*theta) def plot_smith(pts): n = net.Network(s=pts) n.plot_s_smith() cl_points = calc_circle(cl_900mhz, rl_900mhz) plot_smith(cl_points)
In [7]:
rs = np.absolute((bjt_degen.s12.s * bjt_degen.s21.s)/(sqabs(bjt_degen.s11.s) - sqabs(delta))) cs = np.conj(bjt_degen.s11.s - delta*np.conj(bjt_degen.s22.s))/(sqabs(bjt_degen.s11.s) - sqabs(delta)) rs_900mhz = rs[idx_900mhz][0, 0] cs_900mhz = cs[idx_900mhz][0, 0] rs_900mhz, cs_900mhz
Out[7]:
(3.267143687859595, (1.6731769925739743-1.6577383735222198j))
In [8]:
cs_points = calc_circle(cs_900mhz, rs_900mhz) plot_smith(cs_points)
In [9]:
# let's plot all of them # output stability first for i, f in enumerate(bjt.f): # decimate it a little if i % 100 != 0: continue n = net.Network(name=str(f/1.e+9), s=calc_circle(cl[i][0, 0], rl[i][0, 0])) n.plot_s_smith()
In [10]:
# input stability for i, f in enumerate(bjt.f): if i % 100 != 0: continue n = net.Network(name=str(f/1.e+9), s=calc_circle(cs[i][0, 0], rs[i][0, 0])) n.plot_s_smith()
In [11]:
# let's draw some constant noise circles # first we grab the noise parameters for our target frequency from the network model idx_915mhz = skrf.util.find_nearest_index(bjt_degen.f, 915.e+6) # we need the normalized equivalent noise and optimum source coefficient to calculate the constant noise circles rn = bjt_degen.rn[idx_915mhz]/50 gamma_opt = bjt_degen.g_opt[idx_915mhz] fmin = bjt_degen.nfmin[idx_915mhz] for nf_added in [0, 0.05, 0.1, 0.2, 0.3]: nf = 10**(nf_added/10) * fmin N = (nf - fmin)*abs(1+gamma_opt)**2/(4*rn) c_n = gamma_opt/(1+N) r_n = 1/(1-N)*np.sqrt(N**2 + N*(1-abs(gamma_opt)**2)) n = net.Network(name=str(nf_added), s=calc_circle(c_n, r_n)) n.plot_s_smith() print("the optimum source reflection coefficient is ", gamma_opt)
the optimum source reflection coefficient is (0.0880613782526+0.102618106726j)
In [12]:
gamma_s = bjt_degen.g_opt[idx_900mhz] gamma_s
Out[12]:
(0.088061378252587419+0.10261810672607581j)
In [13]:
# so I need to calculate the load reflection coefficient to get a conjugate match when the input sees 50 ohms gamma_l = np.conj(bjt_degen.s22.s - bjt_degen.s21.s*gamma_s*bjt_degen.s12.s/(1-bjt_degen.s11.s*gamma_s)) is_gamma_l_stable = np.absolute(gamma_l[idx_900mhz, 0, 0] - cl_900mhz) > rl_900mhz gamma_l = gamma_l[idx_900mhz, 0, 0] gamma_l, is_gamma_l_stable
Out[13]:
((0.53933106007665532+0.26530128140907783j), True)
In [14]:
def calc_matching_network_vals(z1, z2): flipped = ((abs(np.imag(z2)) < 1e-6 and np.real(z1) < np.real(z2)) or (abs(np.imag(z2)) > 1e-6 and np.real(z1) < np.real(1/(1/z2-1/(1.j*np.imag(z2)))))) if flipped: z2, z1 = z1, z2 # cancel out the imaginary parts of both input and output impedances z1_par = 1e+10 if abs(np.imag(z1)) > 1e-6: # parallel something to cancel out the imaginary part of # z1's impedance z1_par = 1/(-1j*np.imag(1/z1)) z1 = 1/(1./z1 + 1/z1_par) z2_ser = 0.0 if abs(np.imag(z2)) > 1e-6: z2_ser = -1j*np.imag(z2) z2 = z2 + z2_ser Q = np.sqrt((np.real(z1) - np.real(z2))/np.real(z2)) x1 = -1.j * np.real(z1)/Q x2 = 1.j * np.real(z2)*Q x1_tot = 1/(1/z1_par + 1/x1) x2_tot = z2_ser + x2 if flipped: return x2_tot, x1_tot else: return x1_tot, x2_tot z_l = net.s2z(np.array([[[gamma_l]]]))[0,0,0] # note that we're matching against the conjugate; # this is because we want to see z_l from the BJT side # if we plugged in z the matching network would make # the 50 ohms look like np.conj(z) to match against it, so # we use np.conj(z_l) so that it'll look like z_l from the BJT's side z_par, z_ser = calc_matching_network_vals(np.conj(z_l), 50) z_l, z_par, z_ser
Out[14]:
((113.01057791499088+93.878513301282439j), -225.08350870216864j, 83.96308091204551j)
In [15]:
# let's calculate what the component values are c_par = np.real(1/(2j*np.pi*915e+6*z_par)) l_ser = np.real(z_ser/(2j*np.pi*915e+6)) c_par, l_ser
Out[15]:
(7.727790877202402e-13, 1.4604523895493778e-08)
In [16]:
# the capacitance is kind of low but the inductance seems reasonable # let's test it out output_network = tem.shunt_capacitor(c_par) ** tem.inductor(l_ser) amplifier = tem.inductor(0.9e-9) ** bjt_degen ** output_network amplifier.plot_s_smith()
In [17]:
amplifier.s11.plot_s_db() amplifier.s22.plot_s_db() 10*np.log10(amplifier.nf(50.)[idx_900mhz])
Out[17]:
0.7680949467294812
In [ ]:
amplifier.s21.plot_s_db() 20*np.log10(np.abs(amplifier.s21.s[idx_900mhz,0,0]))
In [38]:
# that's pretty good but let's try to optimize the inductor value so we can get conjugate matching # at the same time as minimum noise figure nf_min = 10e+9 lval = 0.0 gamma_s_opt = None gamma_l_opt = None best_zs_opt = None for l in np.linspace(0.1e-9, 5e-9, 50): bjt_degen = series(bjt, tem.shunt_inductor(l)) ** tem.shunt(tem.resistor(130.) ** tem.short()) #bjt_degen.plot_s_smith() idx_900mhz = skrf.util.find_nearest_index(bjt.f, 915.e+6) zs_opt = bjt_degen.z_opt[idx_900mhz] bjt_s11 = bjt_degen.s11.s[idx_900mhz,0,0] bjt_s12 = bjt_degen.s12.s[idx_900mhz,0,0] bjt_s21 = bjt_degen.s21.s[idx_900mhz,0,0] bjt_s22 = bjt_degen.s22.s[idx_900mhz,0,0] delta2 = bjt_s11*bjt_s22 - bjt_s12*bjt_s21 B1 = 1 + sqabs(bjt_s11) - sqabs(bjt_s22) - sqabs(delta2) B2 = 1 + sqabs(bjt_s22) - sqabs(bjt_s11) - sqabs(delta2) C1 = bjt_s11 - delta2*np.conj(bjt_s22) C2 = bjt_s22 - delta2*np.conj(bjt_s11) gamma_s = (B1 - np.sqrt(np.square(B1) - 4*sqabs(C1) + 0j))/(2*C1) gamma_l = (B2 - np.sqrt(np.square(B2) - 4*sqabs(C2) + 0j))/(2*C2) zs_loop = net.s2z(np.array([[[gamma_s]]]))[0,0,0] nf_loop = bjt_degen.nf(zs_loop)[idx_900mhz] print(l, nf_loop, zs_loop, gamma_s, gamma_l) if nf_loop < nf_min: nf_min = nf_loop lval = l gamma_s_opt = gamma_s gamma_l_opt = gamma_l best_zs_opt = zs_opt (lval, best_zs_opt, gamma_s_opt, gamma_l_opt, nf_min)
1e-10 1.49493880303 (11.7493768308+28.7628652849j) (-0.330722950484+0.619850870093j) (0.0992765929965+0.588224830645j) 2e-10 1.36993262557 (16.4849320445+28.8369107953j) (-0.265942338047+0.549084809921j) (0.106645799953+0.494338082016j) 3e-10 1.31158328261 (20.5372365221+28.9407622533j) (-0.213424973782+0.497856811663j) (0.113213957865+0.434300899415j) 4e-10 1.27715866691 (24.2517614958+29.0745264872j) (-0.167727993988+0.457243542875j) (0.118798063319+0.391141893907j) 5e-10 1.25455232795 (27.7646674452+29.2383011619j) (-0.126661387814+0.423607096212j) (0.123533544153+0.358113582887j) 6e-10 1.23881883594 (31.1443109939+29.4321746645j) (-0.0890900524197+0.395028169634j) (0.127572097207+0.331803787522j) 7e-10 1.22750491079 (34.4298531137+29.656225997j) (-0.0543330857894+0.370337494562j) (0.131040897828+0.310243034486j) 8e-10 1.21923430805 (37.6457005122+29.9105246759j) (-0.021939457623+0.348753506286j) (0.134040927599+0.292193173834j) 9e-10 1.21316610812 (40.8079788043+30.195130638j) (0.00841214642297+0.329719097067j) (0.136651673704+0.276827376785j) 1e-09 1.20875307515 (43.927804695+30.5100941547j) (0.0369636873677+0.312818218931j) (0.138935990476+0.263567950696j) 1.1e-09 1.20562142147 (47.0130896524+30.8554557525j) (0.0639053833195+0.297729163442j) (0.140944021371+0.251996768165j) 1.2e-09 1.20350583869 (50.0696016237+31.2312461418j) (0.0893918921655+0.284196454199j) (0.142716172479+0.241802468644j) 1.3e-09 1.20221204798 (53.1016231945+31.6374861529j) (0.113552324243+0.272012944104j) (0.144285331553+0.232747697228j) 1.4e-09 1.20159405843 (56.1123770534+32.0741866797j) (0.13649672645+0.261007866975j) (0.145678524436+0.224647913624j) 1.5e-09 1.20153975078 (59.1043107539+32.5413486319j) (0.158320450549+0.251038547109j) (0.146918159745+0.217357205001j) 1.6e-09 1.20196140366 (62.0792929755+33.0389628941j) (0.179107200831+0.241984455931j) (0.148022972727+0.210758508128j) 1.7e-09 1.20278927373 (65.0387522288+33.5670102944j) (0.198931228181+0.233742831778j) (0.149008748181+0.20475670095j) 1.8e-09 1.20396712902 (67.983777045+34.1254615797j) (0.217858955053+0.226225374774j) (0.149888879761+0.199273615361j) 1.9e-09 1.20544906883 (70.915189744+34.7142773999j) (0.235950210217+0.219355702175j) (0.150674806927+0.194244368424j) 2e-09 1.20719721342 (73.8336016775+35.333408301j) (0.253259188968+0.213067355014j) (0.151376359528+0.189614618183j) 2.1e-09 1.2091799953 (76.7394552341+35.982794725j) (0.269835215488+0.207302213095j) (0.152002031926+0.185338480394j) 2.2e-09 1.21137087518 (79.6330562184+36.6623670194j) (0.285723359461+0.202009218272j) (0.1525592029+0.181376925817j) 2.3e-09 1.21374736308 (82.514599123+37.372045455j) (0.300964943034+0.197143334368j) (0.153054313431+0.177696532336j) 2.4e-09 1.21629026245 (85.3841870809+38.1117402509j) (0.315597963686+0.19266469148j) (0.15349301147+0.174268502699j) 2.5e-09 1.21898307957 (88.2418477827+38.8813516094j) (0.32965745145+0.188537875809j) (0.153880270656+0.17106788363j) 2.6e-09 1.22181155715 (91.087546301+39.6807697581j) (0.343175773985+0.184731335737j) (0.154220488278+0.168072939352j) 2.7e-09 1.22476330244 (93.9211955153+40.5098750009j) (0.356182899652+0.181216881677j) (0.154517566606+0.165264644754j) 2.8e-09 1.22782748798 (96.7426646605+41.3685377768j) (0.368706626237+0.177969262322j) (0.154774980804+0.162626272188j) 2.9e-09 1.23099460883 (99.5517863924+42.2566187275j) (0.380772781254+0.174965803615j) (0.154995835929+0.160143052169j) 3e-09 1.23425628416 (102.348362671+43.1739687725j) (0.392405398466+0.172186099627j) (0.155182915028+0.157801892898j) 3.1e-09 1.2376050938 (105.132169698+44.1204291926j) (0.403626874289+0.169611746658j) (0.155338719903+0.155591146982j) 3.2e-09 1.24103444289 (107.902962084+45.095831721j) (0.414458107037+0.167226113571j) (0.155465505822+0.153500416264j) 3.3e-09 1.24453844898 (110.660476392+46.0999986428j) (0.424918621419+0.165014142666j) (0.155565311211+0.151520387667j) 3.4e-09 1.24811184731 (113.404434172+47.1327429017j) (0.435026680243+0.162962176402j) (0.155639983133+0.149642694408j) 3.5e-09 1.25174991105 (116.134544566+48.1938682141j) (0.444799384999+0.161057806139j) (0.155691199266+0.147859798097j)
Out[38]:
(1.0204081632653062e-10, (58.067887220276525+17.108296550276762j), (-0.1404128330067739+0.99009304427766398j), (0.90582823692607706+0.42364514063942088j), 0.71654949035834925)
In [25]:
bjt.nf(50.)
Out[25]:
array([ 1.19937002, 1.19751604, 1.19566207, ..., 1.24864715, 1.24835019, 1.24805323])
In [28]:
series(bjt, tem.shunt_inductor(1e-9)).nf(50.)
Out[28]:
array([ 1.19753659, 1.19568388, 1.19383126, ..., 1.2216579 , 1.22134603, 1.22103419])
In [ ]:
# that's probably good enough for the first stage # for the second stage let's stabilize the transistor by adding some parallel resistance # and then simultaneous conjugate matching so that both the input and output are matched to 50 ohms # let's calculate the stability factor to see where it's unstable sqabs = lambda x: np.square(np.absolute(x)) delta = bjt.s11.s*bjt.s22.s - bjt.s12.s*bjt.s21.s K = ((1 - sqabs(bjt.s11.s) - sqabs(bjt.s22.s) + sqabs(delta))/(2*np.absolute(bjt.s12.s*bjt.s21.s)))[:,0,0] plt.plot(bjt.f, K)
In [ ]:
#so it's basically always unstable #let's add a 250 ohm shunt to the output and see how much that improves it bjt_comp = bjt ** tem.shunt(tem.resistor(75.) ** tem.short()) bjt_comp.plot_s_smith()
In [ ]:
# let's calculate K again delta2 = bjt_comp.s11.s*bjt_comp.s22.s - bjt_comp.s12.s*bjt_comp.s21.s K2 = ((1 - sqabs(bjt_comp.s11.s) - sqabs(bjt_comp.s22.s) + sqabs(delta2))/(2*np.absolute(bjt_comp.s12.s*bjt_comp.s21.s)))[:,0,0] plt.plot(bjt_comp.f, K2)
In [ ]:
# that's a pretty nice improvement! let's check the delta to be sure, and # find the new source stability circles to see if we need to add some series resistance there # K > 1 and |delta| < 1 for stability plt.plot(bjt_comp.f, np.absolute(delta2[:,0,0]))
In [ ]:
rs2 = np.absolute((bjt_comp.s12.s * bjt_comp.s21.s)/(sqabs(bjt_comp.s11.s) - sqabs(delta2))) cs2 = np.conj(bjt_comp.s11.s - delta2*np.conj(bjt_comp.s22.s))/(sqabs(bjt_comp.s11.s) - sqabs(delta2)) # input stability for i, f in enumerate(bjt_comp.f): if i % 100 != 0: continue n = net.Network(name=str(f/1.e+9), s=calc_circle(cs2[i][0, 0], rs2[i][0, 0])) n.plot_s_smith()
In [ ]:
# that doesn't look too bad, so let's move forward and try to conjugate match delta2 = bjt_comp.s11.s*bjt_comp.s22.s - bjt_comp.s12.s*bjt_comp.s21.s B1 = 1 + sqabs(bjt_comp.s11.s) - sqabs(bjt_comp.s22.s) - sqabs(delta2) B2 = 1 + sqabs(bjt_comp.s22.s) - sqabs(bjt_comp.s11.s) - sqabs(delta2) C1 = bjt_comp.s11.s - delta2*np.conj(bjt_comp.s22.s) C2 = bjt_comp.s22.s - delta2*np.conj(bjt_comp.s11.s) gamma_s_all = (B1 - np.sqrt(np.square(B1) - 4*sqabs(C1) + 0j))/(2*C1) gamma_l_all = (B2 - np.sqrt(np.square(B2) - 4*sqabs(C2) + 0j))/(2*C2) gamma_s = gamma_s_all[idx_900mhz, 0, 0] gamma_l = gamma_l_all[idx_900mhz, 0, 0] z_s = net.s2z(np.array([[[gamma_s]]]))[0,0,0] z_l = net.s2z(np.array([[[gamma_l]]]))[0,0,0] # this section broke when I switched to a lower bias current so just ignore this z_s, z_l
In [ ]:
x_s_1, x_s_2 = calc_matching_network_vals(np.conj(z_s), 50) x_l_1, x_l_2 = calc_matching_network_vals(np.conj(z_l), 50) x_s_1, x_s_2, x_l_1, x_l_2
In [ ]:
c_s_shunt = np.real(1/(2j*np.pi*915e+6*x_s_1)) c_s_ser = np.real(1/(2j*np.pi*915e+6*x_s_2)) l_s_ser = np.real(x_s_2/(2j*np.pi*915e+6)) c_l_shunt = np.real(1/(2j*np.pi*915e+6*x_l_1)) l_l_ser = np.real(x_l_2/(2j*np.pi*915e+6)) c_s_shunt2 = np.real(1/(2j*np.pi*915e+6*x_s_2)) l_s_ser2 = np.real(x_s_1/(2j*np.pi*915e+6)) c_l_shunt2 = np.real(1/(2j*np.pi*915e+6*x_l_2)) l_l_ser2 = np.real(x_l_1/(2j*np.pi*915e+6)) #c_s_shunt, c_s_ser, c_l_shunt, l_l_ser c_s_shunt2, l_s_ser2, c_l_shunt2, l_l_ser2
In [ ]:
#input_network2 = tem.capacitor(c_s_ser) ** tem.shunt_capacitor(c_s_shunt) #input_network2 = tem.inductor(l_s_ser) ** tem.shunt_capacitor(c_s_shunt) input_network2 = tem.shunt_capacitor(c_s_shunt2) ** tem.inductor(l_s_ser2) #output_network2 = tem.shunt_capacitor(c_l_shunt) ** tem.inductor(l_l_ser) output_network2 = tem.inductor(l_l_ser2) ** tem.shunt_capacitor(c_l_shunt2) amplifier2 = input_network2 ** bjt_comp ** output_network2 #amplifier2 = input_network2 ** bjt #** output_network2 amplifier2.plot_s_smith()
In [ ]:
amplifier2.s21.plot_s_db()
In [ ]:
amplifier2.s11.plot_s_db()
In [ ]:
amplifier2.s22.plot_s_db()
In [ ]:
10*np.log10(amplifier2.nf(50.)[idx_900mhz])
In [ ]:
# check stability again delta2 = amplifier2.s11.s*amplifier2.s22.s - amplifier2.s12.s*amplifier2.s21.s K2 = ((1 - sqabs(amplifier2.s11.s) - sqabs(amplifier2.s22.s) + sqabs(delta2))/(2*np.absolute(amplifier2.s12.s*amplifier2.s21.s)))[:,0,0] plt.plot(amplifier2.f, K2)