# Circuits

## Introduction

The Circuit class represents a circuit of arbitrary topology, consisting of an arbitrary number of N-ports Networks connected together. Like in an electronic circuit simulator, the circuit must have one (or more) `Port`

connected to the circuit. The `Circuit`

object allows one retrieving the M-ports `Network`

(and thus its network parameters: \(S\), \(Z\), etc.), where M is the number of ports defined. Moreover, the `Circuit`

object also allows calculating the scattering matrix \(S\) of the entire circuit, that is the “internal” scattering matrices for the various intersections in the circuit. The calculation algorithm is based on ref [1].

The figure below illustrates a network with 2 ports, `Network`

elements \(N_i\) and intersections:

one must must define the connection list (“netlist”) of the circuit. This connexion list is defined as a List of List of interconnected Tuples `(network, port_number)`

:

```
connexions = [
[(network1, network1_port_nb), (network2, network2_port_nb), (network2, network2_port_nb), ...],
...
]
```

For example, the connexion list to construct the above circuit could be:

```
connexions = [
[(port1, 0), (network1, 0), (network4, 0)],
[(network1, 1), (network2, 0), (network5, 0)],
[(network1, 2), (network3, 0)],
[(network2, 1), (network3, 1)],
[(network2, 2), (port2, 0)],
[(network5, 1), (ground1, 0)],
[(network5, 2), (open1, 0)]
]
```

where we have assumed that `port1`

, `port2`

, `ground1`

, `open1`

and all the `network1`

to `network5`

are scikit-rf Networks objects with same `Frequency`

. Networks can have different (real) characteristic impedances: mismatch are taken into account. Convenience methods are provided to create ports and grounded connexions:

**Warnings**

Circuit requires distinct `Network`

names for each `Network`

. So, in case you would like to use multiple times the same `Network`

, you have to duplicate this `Network`

(for example using the `.copy()`

method) and to specify a distinct `.name`

property for each copy, like:

```
# Assuming network1 is the Network you would like to duplicate:
network2 = network1.copy()
network1.name = 'My first Network'
network2.name = 'My second network'
```

In addition, a set `(network, port_number)`

should appear only once in the connection list. In case you would like to connect multiple networks to a single ones (like grounding multiple networks), there is two solutions: * connecting the N networks to a single `Ground`

object once like in:

```
# in the connection list:
...
[(network1,1),(network2,1),(network3,1),(gnd,0)]
...
```

Or create as many distinct

`Ground`

objects (also with unique`name`

properties as mentionned above) as necessary.

```
# in the connection list:
...
[(network1,1), (gnd1,0)],
[(network2,1), (gnd2,0)],
[(network3,1), (gnd3,0)],
...
```

Once the connexion list is defined, the `Circuit`

with:

```
resulting_circuit = rf.Circuit(connexions)
```

`resulting_circuit`

is a Circuit object.

The resulting 2-ports `Network`

is obtained with the Circuit.network parameter:

```
resulting_network = resulting_circuit.network
```

Note that it is also possible to create manually a circuit of multiple `Network`

objects using the connecting methods of `scikit-rf`

. Although the `Circuit`

approach to build a multiple `Network`

may appear to be more verbose than the ‘classic’ way for building a circuit, as the circuit complexity increases, in particular when components are connected in parallel, the `Circuit`

approach is interesting as it increases the readability of the
code. Moreover, `Circuit`

circuit topology can be plotted using its `plot_graph`

method, which is usefull to rapidly control if the circuit is built as expected.

## Examples

### Loaded transmission line

Assume that a \(50\Omega\) lossless transmission line is loaded with a \(Z_L=75\Omega\) impedance.

If the transmission line electric length is \(\theta=0\), then one would thus expect the reflection coefficient to be:

```
[1]:
```

```
import skrf as rf
rf.stylely()
```

```
[2]:
```

```
Z_0 = 50
Z_L = 75
theta = 0
# the necessary Frequency description
freq = rf.Frequency(start=1, stop=2, unit='GHz', npoints=3)
# The combination of a transmission line + a load can be created
# using the convenience delay_load method
# important: all the Network must have the parameter "name" defined
tline_media = rf.DefinedGammaZ0(freq, z0=Z_0)
delay_load = tline_media.delay_load(rf.zl_2_Gamma0(Z_0, Z_L), theta, unit='deg', name='delay_load')
# the input port of the circuit is defined with the Circuit.Port method
port1 = rf.Circuit.Port(freq, 'port1', z0=Z_0)
# connexion list
cnx = [
[(port1, 0), (delay_load, 0)]
]
# building the circuit
cir = rf.Circuit(cnx)
# getting the resulting Network from the 'network' parameter:
ntw = cir.network
print(ntw)
```

```
1-Port Network: '', 1.0-2.0 GHz, 3 pts, z0=[50.+0.j]
```

```
[3]:
```

```
# as expected the reflection coefficient is:
print(ntw.s[0])
```

```
[[0.2+0.j]]
```

It is also possible to build the above circuit using a series impedance Network, then shorted:

To do so, one would need to use the `Ground()`

method to generate the required `Network`

object.

```
[4]:
```

```
port1 = rf.Circuit.Port(freq, 'port1', z0=Z_0)
# piece of transmission line and series impedance
trans_line = tline_media.line(theta, unit='deg', name='trans_line')
load = tline_media.resistor(Z_L, name='delay_load')
# ground network (short)
ground = rf.Circuit.Ground(freq, name='ground')
# connexion list
cnx = [
[(port1, 0), (trans_line, 0)],
[(trans_line, 1), (load, 0)],
[(load, 1), (ground, 0)]
]
# building the circuit
cir = rf.Circuit(cnx)
# the result if the same :
print(cir.network.s[0])
```

```
[[0.2+0.j]]
```

### LC Filter

Here we model a low-pass LC filter, with example values taken from rf-tools.com :

```
[5]:
```

```
freq = rf.Frequency(start=0.1, stop=10, unit='GHz', npoints=1001)
tl_media = rf.DefinedGammaZ0(freq, z0=50, gamma=1j*freq.w/rf.c)
C1 = tl_media.capacitor(3.222e-12, name='C1')
C2 = tl_media.capacitor(82.25e-15, name='C2')
C3 = tl_media.capacitor(3.222e-12, name='C3')
L2 = tl_media.inductor(8.893e-9, name='L2')
RL = tl_media.resistor(50, name='RL')
gnd = rf.Circuit.Ground(freq, name='gnd')
port1 = rf.Circuit.Port(freq, name='port1', z0=50)
port2 = rf.Circuit.Port(freq, name='port2', z0=50)
cnx = [
[(port1, 0), (C1, 0), (L2, 0), (C2, 0)],
[(L2, 1), (C2, 1), (C3, 0), (port2, 0)],
[(gnd, 0), (C1, 1), (C3, 1)],
]
cir = rf.Circuit(cnx)
ntw = cir.network
```

```
[6]:
```

```
ntw.plot_s_db(m=0, n=0, lw=2, logx=True)
ntw.plot_s_db(m=1, n=0, lw=2, logx=True)
```

When building a `Circuit`

made of few networks, it can be usefull to represent the connexion graphically, in order to check for possible errors. This is possible using the Circuit.plot_graph() method. Ports are indicated by triangles, Network with squares and interconnections with circles. It is possible to display the network names as well as their associated ports (and characteristic impedances):

```
[7]:
```

```
cir.plot_graph(network_labels=True, network_fontsize=15,
port_labels=True, port_fontsize=15,
edge_labels=True, edge_fontsize=10)
```

## References

[1] Hallbjörner, P., 2003. Method for calculating the scattering matrix of arbitrary microwave networks giving both internal and external scattering. Microw. Opt. Technol. Lett. 38, 99–102. https://doi.org/10/d27t7m

```
[ ]:
```

```
```