scikit-rf: S-parameter calculation incorrect for 3-port circuit
Bug S-parameter calculation is incorrect for a 3-port circuit
Procedure to reproduce the bug Take the Wilkinson circuit available on scikit-rf website: https://scikit-rf.readthedocs.io/en/latest/examples/circuit/Wilkinson Power Splitter.html
Copy the code and remove the resistor, which gives a Tee circuit with impedance matching:
# standard imports
import numpy as np
import matplotlib.pyplot as plt
import skrf as rf
rf.stylely()
# frequency band
freq = rf.Frequency(start=0, stop=2, npoints=501, unit='GHz')
# characteristic impedance of the ports
z0_ports = 50
# resistor
R = 100
line_resistor = rf.media.DefinedGammaZ0(frequency=freq, z0=R)
resistor = line_resistor.resistor(R, name='resistor')
# branches
z0_branches = np.sqrt(2)*z0_ports
beta = freq.w/rf.c
line_branches = rf.media.DefinedGammaZ0(frequency=freq, z0=z0_branches, gamma=0+beta*1j)
d = line_branches.theta_2_d(90, deg=True) # @ 90°(lambda/4)@ 1 GHz is ~ 75 mm
branch1 = line_branches.line(d, unit='m', name='branch1')
branch2 = line_branches.line(d, unit='m', name='branch2')
# ports
port1 = rf.Circuit.Port(freq, name='port1', z0=50)
port2 = rf.Circuit.Port(freq, name='port2', z0=50)
port3 = rf.Circuit.Port(freq, name='port3', z0=50)
# Connection setup
#┬Note that the order of appearance of the port in the setup is important
connections = [
[(port1, 0), (branch1, 0), (branch2, 0)],
[(port2, 0), (branch1, 1)],
[(port3, 0), (branch2, 1)]
]
# Building the circuit
C = rf.Circuit(connections)
fig, (ax1,ax2) = plt.subplots(2, 1, sharex=True)
C.network.plot_s_db(ax=ax1, m=0, n=0, lw=2) # S11
C.network.plot_s_db(ax=ax1, m=1, n=1, lw=2) # S22
ax1.set_ylim(-90, 0)
C.network.plot_s_db(ax=ax2, m=1, n=0, lw=2) # S21
C.network.plot_s_db(ax=ax2, m=2, n=0, ls='--', lw=2) # S31
ax2.set_ylim(-4, 0)
fig.suptitle('Ideal Wilkinson Divider @ 1 GHz')
Simply execute the code and plot the S-parameters
Expected behavior S21 and S31 should be at -3dB at 1 GHz (middle of the pass-band) because the circuit is now a simple tee, so the power from port 1 is equally split between ports 2 and 3. This behavior was confirmed with a simulation on ADS.
Observed behavior S21 and S31 are -1.5 dB at 1 GHz.
System
- OS: Windows
- scikit-rf version: 0.31.0
Additional context I am trying to design Wilkinsons with different port impedances. I obtained various strange/wrong S-parameter results with scikit-rf (sometimes S21 and S31 are at -0.8 dB, sometimes at -4.1 dB, for designs where ADS gives -3 dB). The procedure given here is the simplest I found to show the problem.
About this issue
- Original URL
- State: closed
- Created 5 months ago
- Comments: 46 (9 by maintainers)
For $S_{ij}$ it should be remembered that it is a power divider. The remaining power that go through after the reflection is split according to the port’s impedances (e.g. if they are equal the power is split in equal parts).
$$ 1 = S_{ii}^2 + \sum_{j=1…n} S_{ij}^2 $$
The power that remains to be split is
$$ \sum_{j=1…n} S_{ij}^2 = 1 - S_{ii}^2 $$
Which is satisfied by
$$ S_{ij} = \frac{2\sqrt{Y_i \cdot Y_j}}{\sum_{k=1…n} Y_{k}} $$
The case of $S_{ii}$ is quite trivial, the port has a mismatch with the equivalent impedance of the other port in parallel, which is more easily computed in terms of admittance.
$$ S_{ii} = \frac{Y_i - \sum_{j\neq i} Y_j}{Y_i + \sum_{j\neq i} Y_j} = \frac{2Y_i}{\sum_{j=1…n} Y_j} - 1 $$
Which is coherent with
rf.Circuit
# formula (1)Great! So it confirms that the equation (3) is incorrect in the generalized case of different impedances.
I was working in parallel on this too 😉 I made a new implementation of
renormalize_s
which reduces the numerical noise when renormalizing the S-param of a N-port junction. But it only supports power waves.Non-efficient code:
Let’s take the 50 ohm 2-port splitter. It is lossless, reciprocal and matched. As a matter of fact, it is just a zero-length thru. The scattering parameters are:
$$ S = \begin{bmatrix} 0 & 1\ 1 & 0\ \end{bmatrix} $$
Which are normalized to the port impedance:
$$Z_1 = 50, Z_2 = 50$$
The impedance parameters do not need to normalize to a port impedance, we can convert to them like
skrf.network.s2z
. Behind the hood, when usingrenormalize_s
, scikit-rf just doz2s(s2z())
withz_old
andz_new
.$$ Z = \begin{bmatrix} \frac{1}{2\sqrt{|\Re(Z_1)|}} & 0 \ 0 & \frac{1}{2\sqrt{|\Re(Z_2)|}} \end{bmatrix}^{-1} \left(1 - S \right)^{-1} \left(S \begin{bmatrix}Z_1 & 0 \ 0 & Z_2 \end{bmatrix} + \begin{bmatrix}Z_1 & 0 \ 0 & Z_2 \end{bmatrix}^{*} \right) \begin{bmatrix} \frac{1}{2\sqrt{|\Re(Z_1)|}} & 0 \ 0 & \frac{1}{2\sqrt{|\Re(Z_2)|}} \end{bmatrix} $$
Which gives
$$ Z = \left(\left(\begin{bmatrix} 1 & 0 \ 0 & 1 \end{bmatrix} - \begin{bmatrix} 0 & 1 \ 1 & 0 \end{bmatrix} \right) \begin{bmatrix} 2\sqrt{50} & 0 \ 0 & 2\sqrt{50} \end{bmatrix}\right)^{-1} \left(\begin{bmatrix} 0 & 1 \ 1 & 0 \end{bmatrix} \begin{bmatrix}50 & 0 \ 0 & 50 \end{bmatrix} + \begin{bmatrix}50 & 0 \ 0 & 50 \end{bmatrix} \right) \begin{bmatrix} \frac{1}{2\sqrt{50}} & 0 \ 0 & \frac{1}{2\sqrt{50}} \end{bmatrix} $$
$$ Z = \begin{bmatrix} 2\sqrt{50} & -2\sqrt{50} \ -2\sqrt{50} & 2\sqrt{50} \end{bmatrix}^{-1} \begin{bmatrix} \frac{\sqrt{50}}{2} & \frac{\sqrt{50}}{2} \ \frac{\sqrt{50}}{2} &\frac{\sqrt{50}}{2} \end{bmatrix} $$
And here tadaaa, the left matrix is singular and cannot be inverted !
s2z
will still return a result because this matrix is added a small offset bynudge
to avoid singularity and gives a matrix with big numbers. Hence probably the loss of precision when usingz2s
on this result.I think the
z0_port=None
provided a kind of backward compatibility. In #651 , a group of users who did electromagnetic simulations were interested in raw impedance. Most users however probably think in terms of 50-ohm measurements.What is strange to me in the above example is that the transmission line networks are not renormalized when connected to the
rf.Circuit.Port
impedances. In ADS, the “port” component explicitly solves the impedance problem in S-Parameter simulations.The issue that we mostly face in scikit-rf is that we want to plot a line network as if has been measured, without cascading explicitly with ports. This is not the case in this example with
rf.Circuit
.There is two open questions: what should
z0_port
default be and why the above example does not renormalize whenz0_port=None
.