Thursday, 6 August 2020

Matchable Loads from S-Parameters Defined Matching Networks

This post compares two different ways for finding the matchable loads \(Z_L^{'}\) that provide the desired impedance \(Z_G^{*}\) to the generator.

I will show the difference between two approaches, one rigorous and the other approximate, by using a set of S-parameters obtained by a black box network. The only requirement is that the black-box is linear, as the S-parameters requires linearity. For the curious, they are obtained from a binary weighted sweep of a lossy \(LL\) network composed of 1024 states in total from SimSmith.

Note that \(Z_G\) does NOT generally represent the output impedance of switching amplifiers, or more generally of non-linear amplifiers. It is not possible to define an operating point for a small signal (linear) analysis from the output, as the conditions change during the analysis and therefore a Thevenin equivalent cannot be defined. For example, an ideal class E amplifier has a particular particular optimum load \(Z_G^{*}\) where the efficiency is 100%, which would not be possible if there was an internal impedance to conjugate match (it would be 50%).


The first method uses the de-embedding matrix, or the matrix that if cascaded with itself will create a transparent block. It is a rigorous and mathematically correct method.

That matrix is obtained by converting the S matrix to a T matrix, or transmission matrix (similar to ABCD or more generally A matrix), inverted, and then converted back to an S matrix. Notice that, by multiplying those two S matrices will not provide an identity matrix as it would by multiplying \(\mathrm{T T^{-1} = I}\), because the cascade of S matrices is not done by multiplying them together due to their definition. For a 2x2 network, it will however provide the anti-diagonal identity matrix, where the ones are on the anti-diagonal. Indeed, this will result in a perfectly matched and lossless block, and therefore transparent. There are some works that extend the concept of T matrices (or ABCD) to higher order matrices (2nx2n), they are interesting in some cases, but this will focus on the traditional 2x2 case.

Given \(\mathbf{b} = \left[b_1, b_2 \right]^{T}\), \(\mathbf{a} = \left[a_1, a_2 \right]^{T}\) and \(\mathbf{b^{'}} = \left[b_2, b_1 \right]^{T}\) and \(\mathbf{a^{'}} = \left[a_2, a_1 \right]^{T}\), by definition of S and T parameters:

\[ \left[ \begin{matrix} b_1 \\ b_2 \end{matrix} \right] = \underline{{S}} \left[ \begin{matrix} a_1\\ a_2 \end{matrix} \right] \]

\[ \left[ \begin{matrix} b_1 \\ a_1 \end{matrix} \right] = \underline{T} \left[ \begin{matrix} a_2\\ b_2 \end{matrix} \right] \] so, the inverse of the T matrix is (or de-embedding matrix with T parameters): \[ \left[ \begin{matrix} a_2 \\ b_2 \end{matrix} \right] = \underline{T}^{\,-1} \left[ \begin{matrix} b_1\\ a_1 \end{matrix} \right] \] which results in (use the definitions of S and T with the swapped terms): \[ \left[ \begin{matrix} a_2\\ a_1 \end{matrix} \right] = \underline{\tilde{S}} \left[ \begin{matrix} b_2 \\ b_1 \end{matrix} \right] \]

Therefore, it can be said that: \[ \mathbf{a^{'}} = \mathrm{t2s}\left(\left(\mathrm{s2t}\left(\underline{S}\right)\right)^{-1}\right) \; \mathbf{b^{'}} = \underline{\tilde{S}} \; \mathbf{b^{'}} \] where \(\mathrm{t2s}\) and \(\mathrm{s2t}\) are the operators to covert to the T-parameters and back to the S-parameters, respectively. The mirrored \(\mathbf{b^{'}}\) and \(\mathbf{a^{'}}\) come from the fact that input and output are reversed by the operation done above.

Now, considering for simplicity that \(Z_G^{*} = Z_0\), so that \(b_1 = 0\), and using the above expression, the load \(Z_L^{'}\) can be defined as \(\underline{\tilde{S}}_{11}\). Indeed, \(b_2\) is the wave going into \(Z_L^{'}\), or out of the S block, and \(a_2\) is the wave reflected out of \(Z_L^{'}\), or into the S block, so: \[ \Gamma_L^{'} = \frac{a_2}{b_2} = \underline{\tilde{S}}_{11} \]

The efficiency will be defined as: \[ \mathrm{eff_{inv}} = \frac{\mathrm{P_{out}}}{\mathrm{P_{in}}} = \frac{\left| b_2 \right|^{2} - \left| a_2 \right|^{2}}{\left| a_1 \right|^{2}} = \frac{1 - \left| \tilde{S}_{11} \right|^{\,2}}{\left| \tilde{S}_{21} \right|^{\,2}} \]

The expression above might seem unexpected, as it allows to compute the power delivered on the load without apparently knowing the load, but which is actually known and is \(Z_L^{'}\).

It might be easier to see it the other way around. By using the de-embedding matrix, the source of power is now the load (\(Z_L^{'}\)), which delivers \(\mathrm{P_{out}}\), and the load is \(Z_G^{*}\) (\(Z_0\), so no reflection to be included) and receives \(\mathrm{P_{in}}\). The result is an efficiency over than one when going from load to generator. Again it might be a strange result, but if one thinks that it is a matrix that de-embedded the original, it must also de-embed the losses, so the efficiency must be the inverse of the one of the original. Using the symbols for the power as above, with the load that generates power (\(\mathrm{P_{out}}\)), does the inversion and therefore the efficiency does not need to be inverted again.

In other words, as the de-embedding will compensate the original losses in order to properly de-embed the original matrix, the ratio of the powers is inverted.


The second is an approximate method, which is correct only when the matching network is lossless, but it is a good approximation when the losses are small.

It uses the fact that a conjugate match on a section of the network will provide a conjugate match in all the sections. Indeed, without losses, a conjugate match provides the maximum power available from the generator, which means that it must be true in every section, as there are no losses between the sections. It can be proved using the Thevenin theorem. It is also called Everitt’s Theorem. In case that there are losses, this is not mathematically true, as erroneously stated by Walter Maxwell.


The analysis is done with python and scikit-rf

%matplotlib inline
#%config InlineBackend.figure_format = 'svg'
import numpy as np
import skrf as rf
import matplotlib.pylab as plt
import glob as glob
from smithplot import SmithAxes
plt.rcParams.update({'figure.dpi': 300})
plt.close('all')
hot = plt.get_cmap('hot')
params = {'legend.fontsize': 10,
          'axes.labelsize': 10,
          'axes.titlesize': 10,
          'xtick.labelsize': 9,
          'ytick.labelsize': 9,
          'mathtext.fontset': 'stix',
          # 'mathtext.fontset': 'cm',
          'font.family': 'Times New Roman',
          'mathtext.rm': 'serif',
          'mathtext.bf': 'serif:bold',
          'mathtext.it': 'serif:italic',
          'mathtext.sf': 'sans\\-serif',
          'grid.color': 'k',
          'grid.linestyle': ':',
          'grid.linewidth': 0.5,
          'axes.xmargin': 0,
          # 'axes.ymargin': 0,
          'axes.axisbelow': False,
          'lines.linewidth': 0.5,
          'legend.frameon': False,
          # 'pgf.rcfonts': False, # http://matplotlib.org/users/pgf.html
          # 'pgf.texsystem': 'pdflatex',
          'figure.figsize': [6, 4.8],
          # When there are twin axis figures: set them using figsize=(3.6, 2.4)
          # when creating the figure
          # 'figure.figsize': [3.6, 2.4],
          }
plt.rcParams.update(params)

The following block loads all the networks in a list and for simplicity uses a single frequency of 6.78Mhz, which is an ISM band.

names = sorted(glob.glob("./S2P/*.s2p"))
N_names = [rf.Network(x)['6.78MHz'] for x in names]
def eff_inv(S):
    return (1 - np.abs(S.inv.s[0,0,0])**2)/(np.abs(S.inv.s[0,1,0])**2)

eff_max = np.argmax([eff_inv(x) for x in N_names])
print(f"Max efficiency: {max([eff_inv(x) for x in N_names])} @ {eff_max}")
print(f"{N_names[eff_max]}, Match. Load: {N_names[eff_max].inv.s11.z[0,0,0]}")

eff_min = np.argmin([eff_inv(x) for x in N_names])
print(f"min efficiency: {min([eff_inv(x) for x in N_names])} @ {eff_min}")
print(f"{N_names[eff_min]}, Match. Load: {N_names[eff_min].inv.s11.z[0,0,0]}")
Max efficiency: 0.9610989764358663 @ 16
2-Port Network: '0_0_11_0',  6780000.0-6780000.0 Hz, 1 pts, z0=[50.+0.j 50.+0.j],
Match. Load: (21.95363882455083+19.32579329596367j)

min efficiency: -1.3983108977772765 @ 951
2-Port Network: '0_7_15_7',  6780000.0-6780000.0 Hz, 1 pts, z0=[50.+0.j 50.+0.j],
Match. Load: (-0.38480054383560236+7.076547636573117j)

Where:

  • 0_0_11_0.s2p
    !Created with skrf (http://scikit-rf.org).
    # Hz S RI R 50.0 
    !freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22
    6780000.0 -0.390350341796875 0.202789306640625 0.16522216796875 -0.85809326171875 0.16552734375 -0.857086181640625 -0.286376953125 -0.3515625
    
  • 0_7_15_7.s2p
    !Created with skrf (http://scikit-rf.org).
    # Hz S RI R 50.0 
    !freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22
    6780000.0 -0.87652587890625 -0.42828369140625 -0.05519866943359375 0.13173675537109375 -0.055084228515625 0.1311492919921875 -0.92669677734375 -0.26641845703125
    

eff_inv is the efficiency calculated using the de-embedding network.

The minimum of the resulting efficiency is negative and this might seem a bizarre result. However, it means that the matching network in that matching state would be able to match a negative resistance, which is effectively a generator. It technically dissipates all the power delivered by the two generators in the matching network, but that would mathematically still provide a match, albeit a very lossy one. Unless it is a very exotic application, those cases can be safely ignored as they will end up outside the Smith Chart.

By using a RF circuit simulator, like ADS or even SimSmith, it is immediate to verify that with both of the above networks and the respective matched load, the impedance at the generator is perfectly matched. The efficiencies also match.


def eff(S):
    return np.abs(S.s[0,0,1])**2 / (1 - np.abs(S.s[0,1,1])**2)

eff_max = np.argmax([eff(x) for x in N_names])
print(f"Max efficiency: {max([eff(x) for x in N_names])} @ {eff_max}")
print(f"{N_names[eff_max]}, Match. Load: {np.conj(N_names[eff_max].s22.z[0,0,0])}")

eff_min = np.argmin([eff(x) for x in N_names])
print(f"min efficiency: {min([eff(x) for x in N_names])} @ {eff_min}")
print(f"{N_names[eff_min]}, Match. Load: {np.conj(N_names[eff_min].s22.z[0,0,0])}")
Max efficiency: 0.9597088223702781 @ 120
2-Port Network: '0_0_9_0',  6780000.0-6780000.0 Hz, 1 pts, z0=[50.+0.j 50.+0.j],
Match. Load: (20.7203287068673+18.174720956555028j)

min efficiency: 0.2880167093064851 @ 951
2-Port Network: '0_7_15_7',  6780000.0-6780000.0 Hz, 1 pts, z0=[50.+0.j 50.+0.j],
Match. Load: (0.9285184032718548+7.042258777529751j)

Where:

  • 0_0_9_0.s2p
    !Created with skrf (http://scikit-rf.org).
    # Hz S RI R 50.0 
    !freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22
    6780000.0 -0.447967529296875 0.110107421875 0.227874755859375 -0.83343505859375 0.22833251953125 -0.83294677734375 -0.326416015625 -0.34088134765625
    
  • 0_7_15_7.s2p
    !Created with skrf (http://scikit-rf.org).
    # Hz S RI R 50.0 
    !freq ReS11 ImS11 ReS21 ImS21 ReS12 ImS12 ReS22 ImS22
    6780000.0 -0.87652587890625 -0.42828369140625 -0.05519866943359375 0.13173675537109375 -0.055084228515625 0.1311492919921875 -0.92669677734375 -0.26641845703125
    

eff(s) is defined as the efficiency by looking from load to the generator. This is different from the previous case, as this effectively as powering from the receiver to the transmitter, where the generator is replaced by the desired load \(Z_G^{*}\). Here the desired load is again \(Z_0\), for simplicity.

This is not the correct way to calculate the efficiency, but only an approximation when the losses are small. It will be same when there are no losses of course, as the efficiency is 1. Notice that there is no negative efficiency this time, and this is a wrong result because of the very lossy match in those situations.

I used this method to calculate the efficiency for the single sided conjugate match, only to show the difference between the two methods.

Repeating the same exercise on those two extreme cases, it is easy to verify that the match is not perfect and that the efficiency is different. In the case with low efficiency, the result is completely wrong.

For reference, using the non correct (W. Maxwell) method:

  • 0_0_9_0.s2p with a load of 20.72 + 18.17j, the load at the generator is 48.8 - 1.16j (SWR 1.034), which is a sufficiently good match.
  • 0_7_15_7.s2p with a load of 0.9285 + 7.042j, the load at the generator is 9.33 - 10.6j (SWR 5.610), therefore very different from a matched load.

The following plot shows the entire 1024 different tuner’s configurations:

fig_S = plt.figure('S', figsize=(5, 10))

ax_S_inv = fig_S.add_subplot(2, 1, 1, projection='smith')
ax_S_inv.set_title("Matchable loads using the de-embedding matrix")
[ax_S_inv.plot(
    x.inv.s[0,0,0], datatype=SmithAxes.S_PARAMETER,
    marker='.', markersize=5, linewidth=0, color=hot(1- eff_inv(x))
    )
    for x in N_names];

ax_S = fig_S.add_subplot(2, 1, 2, projection='smith')
ax_S.set_title("Matchable loads using S22*")
[ax_S.plot(
    np.conj(x.s[0,1,1]), datatype=SmithAxes.S_PARAMETER,
    marker='.', markersize=5, linewidth=0, color=hot(1- eff(x))
    )
    for x in N_names];

im = ax_S_inv.imshow(np.array([[0,1]]), cmap="hot_r")
im.set_visible(False)
cb_ax = fig_S.add_axes([0.2, 0.52, 0.65, 0.01])
cbar = fig_S.colorbar(im, cax=cb_ax, orientation="horizontal")

The plot on top is obtained with the connection of the desired generator load to the de-embedding matrix. The one on the bottom is obtained with \(S_{22}^*\).

As you can see from the following above, the error is more relevant near the extremes of the Smithchart, where the VSWR is high and therefore the losses are also higher. Comparing the two, it is possible to see that high efficiency matches (lower temperature color, black) are almost identical, while low efficiency matches (higher temperature color, up to yellow), are different.

Given the results and the not big additional computational complexity to compute the matchable loads using the correct equations, I believe it is better to always use the de-embedding matrix unless there are good reasons not to do so.


If you like it and feel the desire to cite it, you can use the BibTex below:
@misc{APacini_MatchableLoads,
    author = {Alex Pacini},
    title = {{Matchable Loads from S-parameters}},
    howpublished = "\url{https://pacinispace.blogspot.com/2020/08/matchable-loads-from-S-parameters.html}",
    year = 2020,
}

No comments:

Post a Comment