SLiCAP pole-zero analysis
Accurate pole-zero analysis is indispensible for investigating the stability of dynamic systems. SLiCAP supports both numeric and symbolic pole-zero analysis. Symbolic pole-zero analysis is supported for lower-order rational functions of the Laplace variable and for functions implemented in sympy. Numeric pole-zero analysis is implemented for Laplace rational functions of any order. SLiCAP uses numeric analysis if no other symbolic variables than the Laplace variable ini.laplace=s
are found in the expression.
SLiCAP has three instructions related to pole-zero analysis:
doPoles() returns the solutions of the Laplace variable of the denominator of a transfer function.
doZeros() returns the solutions of the Laplace variable of the numerator of a transfer function.
doPZ() returns the zero frequecy gain, the poles, and the zeros of a transfer function. Poles and zeros that coincide withing the display accurary
ini.disp
are canceled out.Important
doPZ()
does not return non-observable and non-controllable poles. UsedoPoles()
anddoZeros()
to obtain information about non-observable and non-controllable states.Non-controllable state: the input signal(s) cannot change the state.
Non-observable state: the state cannot be observed at the output(s)
SLiCAP output displayed on this manual page, is generated with the script: laplace.py
, imported by Manual.py
.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4"""
5pz.py: SLiCAP scripts for the HTML help file
6"""
7import SLiCAP as sl
The following circuit is used for this manual page:
9pz_network = sl.makeCircuit("kicad/pzNetwork/pzNetwork.kicad_sch")
With a differential voltage detector between nodes (1) and (2), and the signal source set to V1
, the transfer has four poles and three zeros. Two zeros coincide with two poles:
The pole introduced by C2 is not controllable and thus canceled by a zero.
The pole introduced by \(\tau\) is not observable and thus canceled by a zero.
Obtain all the poles of a transfer
Symbolic poles analysis
11pResult = sl.doPoles(pz_network)
12print(pResult.poles)
This yields:
[-1/(C_b*R_b), -1/tau, (-C_a*R_a - C_a*R_d - sqrt(C_a**2*R_a**2 + 2*C_a**2*R_a*R_d + C_a**2*R_d**2 - 4*C_a*L))/(2*C_a*L), (-C_a*R_a - C_a*R_d + sqrt(C_a**2*R_a**2 + 2*C_a**2*R_a*R_d + C_a**2*R_d**2 - 4*C_a*L))/(2*C_a*L)]
Rendered with the formatter:
# |
\(f\) [Hz] |
---|---|
\(p_{1}\) |
\(- \frac{0.5}{\pi C_{b} R_{b}}\) |
\(p_{2}\) |
\(- \frac{0.5}{\pi \tau}\) |
\(p_{3}\) |
\(\frac{0.25 \left(- C_{a} R_{a} - C_{a} R_{d} - \left(C_{a} \left(C_{a} R_{a}^{2} + 2 C_{a} R_{a} R_{d} + C_{a} R_{d}^{2} - 4 L\right)\right)^{0.5}\right)}{\pi C_{a} L}\) |
\(p_{4}\) |
\(\frac{0.25 \left(- C_{a} R_{a} - C_{a} R_{d} + \left(C_{a} \left(C_{a} R_{a}^{2} + 2 C_{a} R_{a} R_{d} + C_{a} R_{d}^{2} - 4 L\right)\right)^{0.5}\right)}{\pi C_{a} L}\) |
Important
Poles and zeros returned by the instruction are always in [rad/s]. The units of poles and zeros displayed in tables HTML are governed by ini.hz=True
(default).
Numeric poles analysis
14pResult_num = sl.doPoles(pz_network, pardefs="circuit")
15print(pResult_num.poles)
This yields:
[-30000.+10000.j, -30000.-10000.j, -100000.+0.j, -10000000.+0.j]
Rendered with the formatter:
# |
Re [Hz] |
Im [Hz] |
\(f\) [Hz] |
Q |
---|---|---|---|---|
\(p_{1}\) |
\(-4775.0\) |
\(1592.0\) |
\(5033.0\) |
\(0.527\) |
\(p_{2}\) |
\(-4775.0\) |
\(-1592.0\) |
\(5033.0\) |
\(0.527\) |
\(p_{3}\) |
\(-1.592 \cdot 10^{4}\) |
\(0\) |
\(1.592 \cdot 10^{4}\) |
|
\(p_{4}\) |
\(-1.592 \cdot 10^{6}\) |
\(0\) |
\(1.592 \cdot 10^{6}\) |
Obtain all the zeros of a transfer
Symbolic zero analysis
17zResult = sl.doZeros(pz_network)
18print(zResult.zeros)
This yields:
[-1/(C_b*R_b), -1/tau, -1/(C_a*R_d)]
Rendered with the formatter:
# |
\(f\) [Hz] |
---|---|
\(z_{1}\) |
\(- \frac{0.5}{\pi C_{b} R_{b}}\) |
\(z_{2}\) |
\(- \frac{0.5}{\pi \tau}\) |
\(z_{3}\) |
\(- \frac{0.5}{\pi C_{a} R_{d}}\) |
Numeric zero analysis
20zResult_num = sl.doZeros(pz_network, pardefs="circuit")
21print(zResult_num.zeros)
This yields:
[-2.0000e+04, -1.0000e+05, -1.0000e+07]
Rendered with the formatter:
# |
Re [Hz] |
Im [Hz] |
\(f\) [Hz] |
Q |
---|---|---|---|---|
\(z_{1}\) |
\(-3183.0\) |
\(0\) |
\(3183.0\) |
|
\(z_{2}\) |
\(-1.592 \cdot 10^{4}\) |
\(0\) |
\(1.592 \cdot 10^{4}\) |
|
\(z_{3}\) |
\(-1.592 \cdot 10^{6}\) |
\(0\) |
\(1.592 \cdot 10^{6}\) |
Obtain the observable and controllable poles and zeros of a transfer
Symbolic pole-zero analysis
23pzResult = sl.doPZ(pz_network)
24print(pzResult.DCvalue)
25print(pzResult.poles)
26print(pzResult.zeros)
This yields:
1
[(-C_a*R_a - C_a*R_d - sqrt(C_a**2*R_a**2 + 2*C_a**2*R_a*R_d + C_a**2*R_d**2 - 4*C_a*L))/(2*C_a*L), (-C_a*R_a - C_a*R_d + sqrt(C_a**2*R_a**2 + 2*C_a**2*R_a*R_d + C_a**2*R_d**2 - 4*C_a*L))/(2*C_a*L)]
[-1/(C_a*R_d)]
Rendered with the formatter:
DC value of gain: \(1\)
# |
\(f\) [Hz] |
---|---|
\(p_{1}\) |
\(\frac{0.25 \left(- C_{a} R_{a} - C_{a} R_{d} - \left(C_{a} \left(C_{a} R_{a}^{2} + 2 C_{a} R_{a} R_{d} + C_{a} R_{d}^{2} - 4 L\right)\right)^{0.5}\right)}{\pi C_{a} L}\) |
\(p_{2}\) |
\(\frac{0.25 \left(- C_{a} R_{a} - C_{a} R_{d} + \left(C_{a} \left(C_{a} R_{a}^{2} + 2 C_{a} R_{a} R_{d} + C_{a} R_{d}^{2} - 4 L\right)\right)^{0.5}\right)}{\pi C_{a} L}\) |
\(z_{1}\) |
\(- \frac{0.5}{\pi C_{a} R_{d}}\) |
Numeric pole-zero analysis
28pzResult_num = sl.doPZ(pz_network, pardefs="circuit")
29sl.listPZ(pzResult_num)
The listPZ()
function displays a table in the console output:
DC value of gain: 1.00e+00
Poles of gain:
n Real part [Hz] Imag part [Hz] Frequency [Hz] Q [-]
-- -------------- -------------- -------------- --------
0 -4.77e+03 1.59e+03 5.03e+03 5.27e-1
1 -4.77e+03 -1.59e+03 5.03e+03 5.27e-1
Zeros of gain:
n Real part [Hz] Imag part [Hz] Frequency [Hz] Q [-]
-- -------------- -------------- -------------- --------
0 -5.07e+02 0.00e+00 5.07e+02
Rendered with the formatter:
DC value of gain: \(1\)
# |
Re [Hz] |
Im [Hz] |
\(f\) [Hz] |
Q |
---|---|---|---|---|
\(p_{1}\) |
\(-4775.0\) |
\(1592.0\) |
\(5033.0\) |
\(0.527\) |
\(p_{2}\) |
\(-4775.0\) |
\(-1592.0\) |
\(5033.0\) |
\(0.527\) |
\(z_{1}\) |
\(-3183.0\) |
\(0\) |
\(3183.0\) |
Return attributes
Important
Calculation of the poles requires calculation of the denominator of a transfer. Hence, SLiCAP also executes the instruction doDenom()
and returns the Laplace poly of the denominator (= determinant of the MNA matrix = ‘characteristic equation’) in the .denom
attribute of the result. It also places the MNA matrix in the .M
attribute of the returned instruction object.
Calculation of the zeros requires calculation of the numerator of a transfer. Hence, SLiCAP also executes the instruction doNumer()
and returns the Laplace poly of the numerator in the .numer
attribute of the result. It also places the MNA matrix in the .M
in the returned instruction object.
Calculation of the poles and the zeros requires all of the above, and the following attributes are available in the returned object:
31M = pzResult.M
32Iv = pzResult.Iv
33Dv = pzResult.Dv
34denom = pzResult.denom
35numer = pzResult.numer
36transfer = pzResult.laplace
Display equations on HTML pages and in LaTeX documents
The report module Create reports, discusses how HTML snippets and LaTeX snippets can be created for variables, expressions, equations and tables.
As a matter of fact, all equations, tables and figures on this page are created with this module:
38# Generate RST snippets for the Help file
39rst = sl.RSTformatter()
40
41rst.pz(pResult , caption="Poles").save("table-P")
42rst.pz(pResult_num , caption="Poles").save("table-Pnum")
43rst.pz(zResult , caption="Zeros").save("table-Z")
44rst.pz(zResult_num , caption="Zeros").save("table-Znum")
45rst.pz(pzResult , caption="Observable poles and zeros").save("table-PZ")
46rst.pz(pzResult_num, caption="Observable poles and zeros").save("table-PZnum")
Plot pole-zero patterns and root-locus
SLiCAP plot functions are discussed in Create plots