# Networks¶

## Introduction¶

This tutorial gives an overview of the microwave network analysis features of skrf.

For this tutorial, and the rest of the scikit-rf documentation, it is assumed that skrf has been imported as rf. Whether or not you follow this convention in your own code is up to you.

In [3]: import skrf as rf

If this produces an import error, please see Installation.

## Creating Networks¶

skrf provides an object for a N-port microwave Network. A Network can be created in a number of ways. One way is from data stored in a touchstone file. There are some examples touchstone files shipped with skrf in the data module.

In [4]: ring_slot = rf.Network(rf.data.pwd+'/ring slot.s2p')

A short description of the network will be printed out if entered onto the command line

In [5]: ring_slot
Out[5]: 2-Port Network: 'ring slot',  75-110 GHz, 201 pts, z0=[ 50.+0.j  50.+0.j]

Networks can also be created by directly passing values for the frequency, s-paramters and z0.

In [6]: f = rf.Frequency(1,10,101,'ghz')

In [7]: custom_ntwk = rf.Network(frequency = f, s= [-1, 1j, 0], z0=50)

## Network Basics¶

The basic attributes of a microwave Network are provided by the following properties :

All of the network parameters are represented internally as complex numpy.ndarray ‘s of shape FxNxN, where F is the number of frequency points and N is the number of ports.

In [8]: shape(ring_slot.s)
Out[8]: (201, 2, 2)

Note that the indexing starts at 0, so the first 10 values of can be accessed with slicing

In [9]: ring_slot.s[:10,0,0]
Out[9]:
array([-0.50372318+0.4578448j , -0.49581904+0.45707698j,
-0.48782538+0.4561578j , -0.47974451+0.45508186j,
-0.47157898+0.45384372j, -0.46333160+0.45243787j,
-0.45500548+0.45085878j, -0.44660400+0.44910088j,
-0.43813086+0.4471586j , -0.42959005+0.44502637j])

The Network object has numerous other properties and methods which can found in the Network docstring. If you are using IPython, then these properties and methods can be ‘tabbed’ out on the command line.

In [10]: short.s<TAB>
rf.data.line.s              rf.data.line.s_arcl         rf.data.line.s_im
rf.data.line.s11            rf.data.line.s_arcl_unwrap  rf.data.line.s_mag
...

Note

Although this tutorial focuses on s-parametes, other network representations such as Impedance (Network.z) and Admittance Parameters (Network.y) are available as well, see Impedance and Admittance Parameters .

Amongst other things, the methods of the Network class provide convenient ways to plot components of the network parameters,

If you would like to use skrf’s plot styling,

In [11]: rf.stylely()

To plot all four s-parameters of the ring_slot on the Smith Chart.

In [12]: ring_slot.plot_s_smith();

Or plot a pair of s-parameters individually, in log magnitude

In [13]: figure();

In [14]: ring_slot.plot_s_db(m=1, n=0); # s21

In [15]: ring_slot.plot_s_db(m=0, n=0); # s11

For more detailed information about plotting see Plotting.

## Network Operators¶

### Linear Operations¶

Element-wise mathematical operations on the scattering parameter matrices are accessible through overloaded operators. To illustrate their usage, load a couple Networks stored in the data module.

In [16]: short = rf.data.wr2p2_short

In [17]: delayshort = rf.data.wr2p2_delayshort

In [18]: short - delayshort
Out[18]: 1-Port Network: 'wr2p2,short',  330-500 GHz, 201 pts, z0=[ 50.+0.j]

In [19]: short + delayshort
Out[19]: 1-Port Network: 'wr2p2,short',  330-500 GHz, 201 pts, z0=[ 50.+0.j]

In [20]: short * delayshort
Out[20]: 1-Port Network: 'wr2p2,short',  330-500 GHz, 201 pts, z0=[ 50.+0.j]

In [21]: short / delayshort
Out[21]: 1-Port Network: 'wr2p2,short',  330-500 GHz, 201 pts, z0=[ 50.+0.j]

In [22]: short / delayshort
Out[22]: 1-Port Network: 'wr2p2,short',  330-500 GHz, 201 pts, z0=[ 50.+0.j]

All of these operations return Network types. For example, to plot the complex difference between short and delay_short,

In [23]: figure();

In [24]: short
Out[24]: 1-Port Network: 'wr2p2,short',  330-500 GHz, 201 pts, z0=[ 50.+0.j]

In [25]: delayshort
Out[25]: 1-Port Network: 'wr2p2,delayshort',  330-500 GHz, 201 pts, z0=[ 50.+0.j]

In [26]: difference = (short- delayshort)

In [27]: difference.plot_s_mag()

Another common application is calculating the phase difference using the division operator,

In [28]: figure();

In [29]: (delayshort/short).plot_s_deg()

Linear operators can also be used with scalars or an numpy.ndarray that ais the same length as the Network.

In [30]: open = (short*-1)

In [31]: open.s[:3,...]
Out[31]:
array([[[ 1.-0.j]],

[[ 1.-0.j]],

[[ 1.-0.j]]])

In [32]: rando =  open *rand(len(open))

In [33]: rando.s[:3,...]
Out[33]:
array([[[ 0.29345787+0.j]],

[[ 0.36465957+0.j]],

[[ 0.99912116+0.j]]])

Note that if you multiply a Network by an numpy.ndarray be sure to place the array on right side.

Cascading and de-embeding 2-port Networks can also be done though operators. The cascade() function can be called through the power operator, **. To calculate a new network which is the cascaded connection of the two individual Networks line and short,

In [34]: short = rf.data.wr2p2_short

In [35]: line = rf.data.wr2p2_line

In [36]: delayshort = line ** short

De-embedding can be accomplished by cascading the inverse of a network. The inverse of a network is accessed through the property Network.inv. To de-embed the short from delay_short,

In [37]: short = line.inv ** delayshort

## Connecting Multi-ports¶

skrf supports the connection of arbitrary ports of N-port networks. It accomplishes this using an algorithm called sub-network growth [1], available through the function connect(). Terminating one port of an ideal 3-way splitter can be done like so,

In [38]: tee = rf.data.tee

To connect port 1 of the tee, to port 0 of the delay short,

In [39]: terminated_tee = rf.connect(tee,1,delayshort,0)

Note that this function takes into account port impedances. If two connected ports have different port impedances, an appropriate impedance mismatch is inserted.

## Interpolation and Stitching¶

A common need is to change the number of frequency points of a Network. For instance, to use the operators and cascading functions the networks involved must have matching frequencies. If two networks have different frequency information, then an error will be raised,

In [40]: line = rf.data.wr2p2_line.copy()

In [41]: line1 = rf.data.wr2p2_line1.copy()

In [42]: line1
Out[42]: 2-Port Network: 'wr2p2,line1',  330-500 GHz, 101 pts, z0=[ 50.+0.j  50.+0.j]

In [43]: line
Out[43]: 2-Port Network: 'wr2p2,line',  330-500 GHz, 201 pts, z0=[ 50.+0.j  50.+0.j]

In [44]: line1+line
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-44-82040f7eab08> in <module>()
----> 1 line1+line

473
474         if isinstance(other, Network):
--> 475             self.__compatable_for_scalar_operation_test(other)
476             result.s = self.s + other.s
477         else:

615         '''
616         if other.frequency  != self.frequency:
--> 617             raise IndexError('Networks must have same frequency. See `Network.interpolate`')
618
619         if other.s.shape != self.s.shape:

IndexError: Networks must have same frequency. See `Network.interpolate`

This problem can be solved by interpolating one of Networks, using Network.resample().

In [45]: line1
Out[45]: 2-Port Network: 'wr2p2,line1',  330-500 GHz, 101 pts, z0=[ 50.+0.j  50.+0.j]

In [46]: line1.resample(201)

In [47]: line1
Out[47]: 2-Port Network: 'wr2p2,line1',  330-500 GHz, 201 pts, z0=[ 50.+0.j  50.+0.j]

In [48]: line1+line
Out[48]: 2-Port Network: 'wr2p2,line1',  330-500 GHz, 201 pts, z0=[ 50.+0.j  50.+0.j]

A related application is the need to combine Networks which cover different frequency ranges. Two Netwoks can be stitched together using stitch(), which concatenates their s-parameter matrices along their frequency axis. To combine a WR-2.2 Network with a WR-1.5 Network,

In [49]: from skrf.data import wr2p2_line, wr1p5_line

In [50]: line = rf.stitch(wr2p2_line, wr1p5_line)

In [51]: line
Out[51]: 2-Port Network: 'wr2p2,line',  330-750 GHz, 402 pts, z0=[ 50.+0.j  50.+0.j]

For long term data storage, skrf has support for reading and partial support for writing touchstone file format . Reading is accomplished with the Network initializer as shown above and writing with the method Network.write_touchstone().

For temporary data storage, skrf object can be pickled with the functions read() and write().

In [52]: line = rf.Network(rf.data.pwd+'/line.s2p')

In [53]: rf.write(line) # write out Network using pickline IO
line.ntwk

In [54]: rf.Network('line.ntwk') # read Network using native IO

Warning

Pickling methods cant support long term data storage because they require the structure of the object being written to remain unchanged. something that cannot be guarnteed in future versions of skrf. (see http://docs.python.org/2/library/pickle.html)

Frequently there is an entire directory of files that need to be analyzed. The function read_all() is used to create objects from all files in a directory quickly. Given a directory of skrf-readable files, read_all() returns a dict with keys equal to the filenames, and values equal to objects. To load all skrf files in the `skrf/data/` directory which contain the string ‘wr2p2’.

In [55]: dict_o_ntwks = rf.read_all(rf.data.pwd, contains = 'wr2p2')

In [56]: dict_o_ntwks
Out[56]:
{'wr2p2,delayshort': 1-Port Network: 'wr2p2,delayshort',  330-500 GHz, 201 pts, z0=[ 50.+0.j],
'wr2p2,line': 2-Port Network: 'wr2p2,line',  330-500 GHz, 201 pts, z0=[ 50.+0.j  50.+0.j],
'wr2p2,line1': 2-Port Network: 'wr2p2,line1',  330-500 GHz, 101 pts, z0=[ 50.+0.j  50.+0.j],
'wr2p2,short': 1-Port Network: 'wr2p2,short',  330-500 GHz, 201 pts, z0=[ 50.+0.j]}

read_all() has a companion function, write_all() which takes a dictionary of skrf objects, and writes each object to an individual file.

In [57]: rf.write_all(dict_o_ntwks, dir = '.')

In [58]: ls
wr2p2,delayshort.ntwk   wr2p2,line.ntwk         wr2p2,short.ntwk

It is also possible to write a dictionary of objects to a single file, by using write(),

In [59]: rf.write('dict_o_ntwk.p', dict_o_ntwks)

In [60]: ls
dict_o_ntwk.p

A similar function save_sesh(), can be used to save all skrf objects in the current namespace.

This tutorial focuses on s-parameters, but other network represenations are available as well. Impedance and Admittance Parameters can be accessed through the parameters Network.z and Network.y, respectively. Scalar components of complex parameters, such as Network.z_re, Network.z_im and plotting methods like Network.plot_z_mag() are available as well.

In [61]: ring_slot.z[:3,...]
Out[61]:
array([[[ 0.88442687+28.15350224j,  0.94703504+30.46757222j],
[ 0.94703504+30.46757222j,  1.04344170+43.45766805j]],

[[ 0.91624901+28.72415928j,  0.98188607+31.09594438j],
[ 0.98188607+31.09594438j,  1.08168411+44.17642274j]],

[[ 0.94991736+29.31694632j,  1.01876516+31.74874257j],
[ 1.01876516+31.74874257j,  1.12215451+44.92215712j]]])

In [62]: figure();

In [63]: ring_slot.plot_z_im(m=1,n=0)

## Creating Networks ‘From Scratch’¶

A Network can be created from scratch by passing values of relevant properties as keyword arguments to the constructor,

In [64]: frequency = rf.Frequency(75,110,101,'ghz')

In [65]: s = -1*ones(101)

In [66]: wr10_short = rf.Network(frequency = frequency, s = s, z0 = 50 )

For information on creating Networks representing transmission line and lumped components, see the media module.

## Sub-Networks¶

Frequently, the one-port s-parameters of a multiport network’s are of interest. These can be accessed by the sub-network properties, which return one-port Network objects,

In [68]: port1_return = line.s11

In [69]: port1_return
Out[69]: 1-Port Network: 'line',  75-110 GHz, 201 pts, z0=[ 50.+0.j]

## References¶

 [1] Compton, R.C.; , “Perspectives in microwave circuit analysis,” Circuits and Systems, 1989., Proceedings of the 32nd Midwest Symposium on , vol., no., pp.716-718 vol.2, 14-16 Aug 1989. URL: http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=101955&isnumber=3167