#import os
from sympy import *
import numpy as np
from tabulate import tabulate
from scipy import signal
import matplotlib.pyplot as plt
import pandas as pd
import SymMNA
from IPython.display import display, Markdown, Math, Latex
init_printing()
15 Test 11
Figure 15.1 is from Johnson, Hilburn, and Johnson (1978) (page 444, problem 16.15). The circuit was drawn in LTSpice and the circuit nodes are labeled. Find the network function, H(s)=V2(s)/V3(s). I made some changes to the values, phases of the windings and coupling constants.
29 Nov 2023:
Problem - When the D matrix is built, independent voltage sources are processed in the data frame order when building the D matrix. If the voltage source followed element L, H, F, K types in the netlist, a row was inserted that put the voltage source in a different row in relation to it’s position in the Ev matrix. This would cause the node attached to the terminal of the voltage source to be zero volts. Solution - added code to move voltage source types to the beginning of the net list data frame before any calculations are performed.
Need to verify during testing that independednt current sources, type I, do not also need this fix.
The netlist generated by LTSpice:
L1 1 0 10 Rser=0
L2 0 2 20 Rser=0
L3 4 0 30 Rser=0
R2 2 0 5
R3 4 0 10
R1 1 3 2
V1 3 0 AC 10
K1 L1 L2 0.2
K2 L1 L3 0.8
K3 L2 L3 0.5
See notes at the end for debugging steps.
15.1 Load the net list
= '''
net_list L1 1 0 10
L2 0 2 20
L3 4 0 30
R2 2 0 5
R3 4 0 10
R1 1 3 2
V1 3 0 10
K1 L1 L2 0.2
K2 L1 L3 0.8
K3 L2 L3 0.5
'''
15.2 Call the symbolic modified nodal analysis function
= SymMNA.smna(net_list) report, network_df, i_unk_df, A, X, Z
Display the equations
# reform X and Z into Matrix type for printing
= Matrix(X)
Xp = Matrix(Z)
Zp = ''
temp for i in range(len(X)):
+= '${:s}$<br>'.format(latex(Eq((A*Xp)[i:i+1][0],Zp[i])))
temp
Markdown(temp)
\(I_{L1} + \frac{v_{1}}{R_{1}} - \frac{v_{3}}{R_{1}} = 0\)
\(- I_{L2} + \frac{v_{2}}{R_{2}} = 0\)
\(I_{V1} - \frac{v_{1}}{R_{1}} + \frac{v_{3}}{R_{1}} = 0\)
\(I_{L3} + \frac{v_{4}}{R_{3}} = 0\)
\(v_{3} = V_{1}\)
\(- I_{L1} L_{1} s - I_{L2} M_{1} s - I_{L3} M_{2} s + v_{1} = 0\)
\(- I_{L1} M_{1} s - I_{L2} L_{2} s - I_{L3} M_{3} s - v_{2} = 0\)
\(- I_{L1} M_{2} s - I_{L2} M_{3} s - I_{L3} L_{3} s + v_{4} = 0\)
15.2.1 Netlist statistics
print(report)
Net list report
number of lines in netlist: 10
number of branches: 7
number of nodes: 4
number of unknown currents: 4
number of RLC (passive components): 6
number of inductors: 3
number of independent voltage sources: 1
number of independent current sources: 0
number of Op Amps: 0
number of E - VCVS: 0
number of G - VCCS: 0
number of F - CCCS: 0
number of H - CCVS: 0
number of K - Coupled inductors: 3
15.2.2 Connectivity Matrix
A
\(\displaystyle \left[\begin{matrix}\frac{1}{R_{1}} & 0 & - \frac{1}{R_{1}} & 0 & 0 & 1 & 0 & 0\\0 & \frac{1}{R_{2}} & 0 & 0 & 0 & 0 & -1 & 0\\- \frac{1}{R_{1}} & 0 & \frac{1}{R_{1}} & 0 & 1 & 0 & 0 & 0\\0 & 0 & 0 & \frac{1}{R_{3}} & 0 & 0 & 0 & 1\\0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\1 & 0 & 0 & 0 & 0 & - L_{1} s & - M_{1} s & - M_{2} s\\0 & -1 & 0 & 0 & 0 & - M_{1} s & - L_{2} s & - M_{3} s\\0 & 0 & 0 & 1 & 0 & - M_{2} s & - M_{3} s & - L_{3} s\end{matrix}\right]\)
15.2.3 Unknown voltages and currents
X
\(\displaystyle \left[ v_{1}, \ v_{2}, \ v_{3}, \ v_{4}, \ I_{V1}, \ I_{L1}, \ I_{L2}, \ I_{L3}\right]\)
15.2.4 Known voltages and currents
Z
\(\displaystyle \left[ 0, \ 0, \ 0, \ 0, \ V_{1}, \ 0, \ 0, \ 0\right]\)
15.2.5 Network dataframe
network_df
element | p node | n node | cp node | cn node | Vout | value | Vname | Lname1 | Lname2 | |
---|---|---|---|---|---|---|---|---|---|---|
0 | V1 | 3 | 0 | NaN | NaN | NaN | 10.0 | NaN | NaN | NaN |
1 | L1 | 1 | 0 | NaN | NaN | NaN | 10.0 | NaN | NaN | NaN |
2 | L2 | 0 | 2 | NaN | NaN | NaN | 20.0 | NaN | NaN | NaN |
3 | L3 | 4 | 0 | NaN | NaN | NaN | 30.0 | NaN | NaN | NaN |
4 | R2 | 2 | 0 | NaN | NaN | NaN | 5.0 | NaN | NaN | NaN |
5 | R3 | 4 | 0 | NaN | NaN | NaN | 10.0 | NaN | NaN | NaN |
6 | R1 | 1 | 3 | NaN | NaN | NaN | 2.0 | NaN | NaN | NaN |
7 | K1 | NaN | NaN | NaN | NaN | NaN | 0.2 | NaN | L1 | L2 |
8 | K2 | NaN | NaN | NaN | NaN | NaN | 0.8 | NaN | L1 | L3 |
9 | K3 | NaN | NaN | NaN | NaN | NaN | 0.5 | NaN | L2 | L3 |
15.2.6 Unknown current dataframe
i_unk_df
element | p node | n node | |
---|---|---|---|
0 | V1 | 3 | 0 |
1 | L1 | 1 | 0 |
2 | L2 | 0 | 2 |
3 | L3 | 4 | 0 |
15.2.7 Build the network equations
# Put matrices into SymPy
= Matrix(X)
X = Matrix(Z)
Z
= Eq(A*X,Z) NE_sym
Turn the free symbols into SymPy variables.
str(NE_sym.free_symbols).replace('{','').replace('}','')) var(
\(\displaystyle \left( I_{V1}, \ R_{1}, \ R_{2}, \ v_{2}, \ M_{1}, \ v_{4}, \ I_{L3}, \ M_{2}, \ I_{L2}, \ M_{3}, \ v_{1}, \ R_{3}, \ s, \ v_{3}, \ L_{1}, \ I_{L1}, \ L_{2}, \ V_{1}, \ L_{3}\right)\)
15.3 Symbolic solution
= solve(NE_sym,X) U_sym
Display the symbolic solution
= ''
temp for i in U_sym.keys():
+= '${:s} = {:s}$<br>'.format(latex(i),latex(U_sym[i]))
temp
Markdown(temp)
\(v_{1} = \frac{L_{1} L_{2} L_{3} V_{1} s^{3} + L_{1} L_{2} R_{3} V_{1} s^{2} + L_{1} L_{3} R_{2} V_{1} s^{2} - L_{1} M_{3}^{2} V_{1} s^{3} + L_{1} R_{2} R_{3} V_{1} s - L_{2} M_{2}^{2} V_{1} s^{3} - L_{3} M_{1}^{2} V_{1} s^{3} - M_{1}^{2} R_{3} V_{1} s^{2} + 2 M_{1} M_{2} M_{3} V_{1} s^{3} - M_{2}^{2} R_{2} V_{1} s^{2}}{L_{1} L_{2} L_{3} s^{3} + L_{1} L_{2} R_{3} s^{2} + L_{1} L_{3} R_{2} s^{2} - L_{1} M_{3}^{2} s^{3} + L_{1} R_{2} R_{3} s + L_{2} L_{3} R_{1} s^{2} - L_{2} M_{2}^{2} s^{3} + L_{2} R_{1} R_{3} s - L_{3} M_{1}^{2} s^{3} + L_{3} R_{1} R_{2} s - M_{1}^{2} R_{3} s^{2} + 2 M_{1} M_{2} M_{3} s^{3} - M_{2}^{2} R_{2} s^{2} - M_{3}^{2} R_{1} s^{2} + R_{1} R_{2} R_{3}}\)
\(v_{2} = \frac{- L_{3} M_{1} R_{2} V_{1} s^{2} - M_{1} R_{2} R_{3} V_{1} s + M_{2} M_{3} R_{2} V_{1} s^{2}}{L_{1} L_{2} L_{3} s^{3} + L_{1} L_{2} R_{3} s^{2} + L_{1} L_{3} R_{2} s^{2} - L_{1} M_{3}^{2} s^{3} + L_{1} R_{2} R_{3} s + L_{2} L_{3} R_{1} s^{2} - L_{2} M_{2}^{2} s^{3} + L_{2} R_{1} R_{3} s - L_{3} M_{1}^{2} s^{3} + L_{3} R_{1} R_{2} s - M_{1}^{2} R_{3} s^{2} + 2 M_{1} M_{2} M_{3} s^{3} - M_{2}^{2} R_{2} s^{2} - M_{3}^{2} R_{1} s^{2} + R_{1} R_{2} R_{3}}\)
\(v_{3} = V_{1}\)
\(v_{4} = \frac{L_{2} M_{2} R_{3} V_{1} s^{2} - M_{1} M_{3} R_{3} V_{1} s^{2} + M_{2} R_{2} R_{3} V_{1} s}{L_{1} L_{2} L_{3} s^{3} + L_{1} L_{2} R_{3} s^{2} + L_{1} L_{3} R_{2} s^{2} - L_{1} M_{3}^{2} s^{3} + L_{1} R_{2} R_{3} s + L_{2} L_{3} R_{1} s^{2} - L_{2} M_{2}^{2} s^{3} + L_{2} R_{1} R_{3} s - L_{3} M_{1}^{2} s^{3} + L_{3} R_{1} R_{2} s - M_{1}^{2} R_{3} s^{2} + 2 M_{1} M_{2} M_{3} s^{3} - M_{2}^{2} R_{2} s^{2} - M_{3}^{2} R_{1} s^{2} + R_{1} R_{2} R_{3}}\)
\(I_{V1} = \frac{- L_{2} L_{3} V_{1} s^{2} - L_{2} R_{3} V_{1} s - L_{3} R_{2} V_{1} s + M_{3}^{2} V_{1} s^{2} - R_{2} R_{3} V_{1}}{L_{1} L_{2} L_{3} s^{3} + L_{1} L_{2} R_{3} s^{2} + L_{1} L_{3} R_{2} s^{2} - L_{1} M_{3}^{2} s^{3} + L_{1} R_{2} R_{3} s + L_{2} L_{3} R_{1} s^{2} - L_{2} M_{2}^{2} s^{3} + L_{2} R_{1} R_{3} s - L_{3} M_{1}^{2} s^{3} + L_{3} R_{1} R_{2} s - M_{1}^{2} R_{3} s^{2} + 2 M_{1} M_{2} M_{3} s^{3} - M_{2}^{2} R_{2} s^{2} - M_{3}^{2} R_{1} s^{2} + R_{1} R_{2} R_{3}}\)
\(I_{L1} = \frac{L_{2} L_{3} V_{1} s^{2} + L_{2} R_{3} V_{1} s + L_{3} R_{2} V_{1} s - M_{3}^{2} V_{1} s^{2} + R_{2} R_{3} V_{1}}{L_{1} L_{2} L_{3} s^{3} + L_{1} L_{2} R_{3} s^{2} + L_{1} L_{3} R_{2} s^{2} - L_{1} M_{3}^{2} s^{3} + L_{1} R_{2} R_{3} s + L_{2} L_{3} R_{1} s^{2} - L_{2} M_{2}^{2} s^{3} + L_{2} R_{1} R_{3} s - L_{3} M_{1}^{2} s^{3} + L_{3} R_{1} R_{2} s - M_{1}^{2} R_{3} s^{2} + 2 M_{1} M_{2} M_{3} s^{3} - M_{2}^{2} R_{2} s^{2} - M_{3}^{2} R_{1} s^{2} + R_{1} R_{2} R_{3}}\)
\(I_{L2} = \frac{- L_{3} M_{1} V_{1} s^{2} - M_{1} R_{3} V_{1} s + M_{2} M_{3} V_{1} s^{2}}{L_{1} L_{2} L_{3} s^{3} + L_{1} L_{2} R_{3} s^{2} + L_{1} L_{3} R_{2} s^{2} - L_{1} M_{3}^{2} s^{3} + L_{1} R_{2} R_{3} s + L_{2} L_{3} R_{1} s^{2} - L_{2} M_{2}^{2} s^{3} + L_{2} R_{1} R_{3} s - L_{3} M_{1}^{2} s^{3} + L_{3} R_{1} R_{2} s - M_{1}^{2} R_{3} s^{2} + 2 M_{1} M_{2} M_{3} s^{3} - M_{2}^{2} R_{2} s^{2} - M_{3}^{2} R_{1} s^{2} + R_{1} R_{2} R_{3}}\)
\(I_{L3} = \frac{- L_{2} M_{2} V_{1} s^{2} + M_{1} M_{3} V_{1} s^{2} - M_{2} R_{2} V_{1} s}{L_{1} L_{2} L_{3} s^{3} + L_{1} L_{2} R_{3} s^{2} + L_{1} L_{3} R_{2} s^{2} - L_{1} M_{3}^{2} s^{3} + L_{1} R_{2} R_{3} s + L_{2} L_{3} R_{1} s^{2} - L_{2} M_{2}^{2} s^{3} + L_{2} R_{1} R_{3} s - L_{3} M_{1}^{2} s^{3} + L_{3} R_{1} R_{2} s - M_{1}^{2} R_{3} s^{2} + 2 M_{1} M_{2} M_{3} s^{3} - M_{2}^{2} R_{2} s^{2} - M_{3}^{2} R_{1} s^{2} + R_{1} R_{2} R_{3}}\)
15.4 Construct a dictionary of element values
= SymMNA.get_part_values(network_df)
element_values
# display the component values
for k,v in element_values.items():
print('{:s} = {:s}'.format(str(k), str(v)))
V1 = 10.0
L1 = 10.0
L2 = 20.0
L3 = 30.0
R2 = 5.0
R3 = 10.0
R1 = 2.0
K1 = 0.2
K2 = 0.8
K3 = 0.5
15.4.1 Mutual inductance
In the netlist, the line below specifies that L3 and L4 are connected by a magnetic circuit.
K1 L1 L2 0.2
K2 L1 L3 0.8
K3 L2 L3 0.5
K1 identifies the mutual inductance between in two inductors, L3 and L4. k is the coefficient of coupling.
A coupled inductor has two or more windings that are connected by a magnetic circuit. Coupled inductors transfer energy from one winding to a different winding usually through a commonly used core. The efficiency of the magnetic coupling between both the windings is defined by the coupling factor k or by mutual inductance.
The coupling constant and the mutual inductance are related by the equation:
\(M_1 = K1 \times {\sqrt{L_1 \times L_2}}\)
\(M_2 = K2 \times {\sqrt{L_1 \times L_3}}\)
\(M_3 = K3 \times {\sqrt{L_2 \times L_3}}\)
Where k is the coupling coefficient and in spice the value of k can be from -1 to +1 to account for a a negative phase relation. Phase dots are drawn on the schematic to indicate the relative direction of the windings. In LTspice the phase dots are associated with the negative terminal of the winding.
= symbols('K1 K2 K3')
K1, K2, K3
# calculate the coupling constant from the mutual inductance
= element_values[K1]*np.sqrt(element_values[L1] * element_values[L2])
element_values[M1] print('mutual inductance, M1 = {:.9f}'.format(element_values[M1]))
= element_values[K2]*np.sqrt(element_values[L1] * element_values[L3])
element_values[M2] print('mutual inductance, M2 = {:.9f}'.format(element_values[M2]))
= element_values[K3]*np.sqrt(element_values[L2] * element_values[L3])
element_values[M3] print('mutual inductance, M3 = {:.9f}'.format(element_values[M3]))
mutual inductance, M1 = 2.828427125
mutual inductance, M2 = 13.856406461
mutual inductance, M3 = 12.247448714
15.5 DC operating point
= NE_sym.subs(element_values)
NE = NE.subs({s:0}) NE_dc
Display the equations with numeric values.
= ''
temp for i in range(shape(NE_dc.lhs)[0]):
+= '${:s} = {:s}$<br>'.format(latex(NE_dc.rhs[i]),latex(NE_dc.lhs[i]))
temp
Markdown(temp)
\(0 = I_{L1} + 0.5 v_{1} - 0.5 v_{3}\)
\(0 = - I_{L2} + 0.2 v_{2}\)
\(0 = I_{V1} - 0.5 v_{1} + 0.5 v_{3}\)
\(0 = I_{L3} + 0.1 v_{4}\)
\(10.0 = v_{3}\)
\(0 = v_{1}\)
\(0 = - v_{2}\)
\(0 = v_{4}\)
Solve for voltages and currents.
= solve(NE_dc,X) U_dc
Display the numerical solution
Six significant digits are displayed so that results can be compared to LTSpice.
= ['unknown', 'mag']
table_header = []
table_row
for name, value in U_dc.items():
str(name),float(value)])
table_row.append([
print(tabulate(table_row, headers=table_header,colalign = ('left','decimal'),tablefmt="simple",floatfmt=('5s','.6f')))
unknown mag
--------- ---------
v1 0.000000
v3 10.000000
I_V1 -5.000000
I_L1 5.000000
v2 0.000000
I_L2 0.000000
v4 0.000000
I_L3 0.000000
The node voltages and current through the sources are solved for. The Sympy generated solution matches the LTSpice results:
--- Operating Point ---
V(1): 0 voltage
V(2): 0 voltage
V(4): 0 voltage
V(3): 10 voltage
I(L1): 5 device_current
I(L2): 0 device_current
I(L3): 0 device_current
I(R2): 0 device_current
I(R3): 0 device_current
I(R1): -5 device_current
I(V1): -5 device_current
The results from LTSpice are slightly different in some cases starting at the 2nd decimal place.
15.5.1 AC analysis
Solve equations for \(\omega\) equal to 1 radian per second, s = 1j. V1 is the AC source, magnitude of 10
= NE_sym.subs(element_values)
NE = NE.subs({s:1j}) NE_w1
Display the equations with numeric values.
= ''
temp for i in range(shape(NE_w1.lhs)[0]):
+= '${:s} = {:s}$<br>'.format(latex(NE_w1.rhs[i]),latex(NE_w1.lhs[i]))
temp
Markdown(temp)
\(0 = I_{L1} + 0.5 v_{1} - 0.5 v_{3}\)
\(0 = - I_{L2} + 0.2 v_{2}\)
\(0 = I_{V1} - 0.5 v_{1} + 0.5 v_{3}\)
\(0 = I_{L3} + 0.1 v_{4}\)
\(10.0 = v_{3}\)
\(0 = - 10.0 i I_{L1} - 2.82842712474619 i I_{L2} - 13.856406460551 i I_{L3} + v_{1}\)
\(0 = - 2.82842712474619 i I_{L1} - 20.0 i I_{L2} - 12.2474487139159 i I_{L3} - v_{2}\)
\(0 = - 13.856406460551 i I_{L1} - 12.2474487139159 i I_{L2} - 30.0 i I_{L3} + v_{4}\)
Solve for voltages and currents.
= solve(NE_w1,X) U_w1
Display the numerical solution
Six significant digits are displayed so that results can be compared to LTSpice.
= ['unknown', 'mag','phase, deg']
table_header = []
table_row
for name, value in U_w1.items():
str(name),float(abs(value)),float(arg(value)*180/np.pi)])
table_row.append([
print(tabulate(table_row, headers=table_header,colalign = ('left','decimal','decimal'),tablefmt="simple",floatfmt=('5s','.6f','.6f')))
unknown mag phase, deg
--------- --------- ------------
v1 8.002110 16.522966
v2 1.369735 15.274587
v3 10.000000 0.000000
v4 7.724137 -19.105758
I_V1 1.627911 135.653713
I_L1 1.627911 -44.346287
I_L2 0.273947 15.274587
I_L3 0.772414 160.894242
--- AC Analysis ---
frequency: 0.159155 Hz
V(1): mag: 8.00211 phase: 16.523° voltage
V(2): mag: 1.36973 phase: 15.2746° voltage
V(4): mag: 7.72414 phase: -19.1058° voltage
V(3): mag: 10 phase: 0° voltage
I(L1): mag: 1.62791 phase: -44.3463° device_current
I(L2): mag: 0.273947 phase: 15.2746° device_current
I(L3): mag: 0.772414 phase: 160.894° device_current
I(R2): mag: 0.273947 phase: 15.2746° device_current
I(R3): mag: 0.772414 phase: -19.1058° device_current
I(R1): mag: 1.62791 phase: 135.654° device_current
I(V1): mag: 1.62791 phase: 135.654° device_current
15.5.2 AC Sweep
Looking at node 21 voltage and comparing the results with those obtained from LTSpice. The frequency sweep is from 0.01 Hz to 1 Hz.
= NE_sym.subs(element_values) NE
Display the equations with numeric values.
= ''
temp for i in range(shape(NE.lhs)[0]):
+= '${:s} = {:s}$<br>'.format(latex(NE.rhs[i]),latex(NE.lhs[i]))
temp
Markdown(temp)
\(0 = I_{L1} + 0.5 v_{1} - 0.5 v_{3}\)
\(0 = - I_{L2} + 0.2 v_{2}\)
\(0 = I_{V1} - 0.5 v_{1} + 0.5 v_{3}\)
\(0 = I_{L3} + 0.1 v_{4}\)
\(10.0 = v_{3}\)
\(0 = - 10.0 I_{L1} s - 2.82842712474619 I_{L2} s - 13.856406460551 I_{L3} s + v_{1}\)
\(0 = - 2.82842712474619 I_{L1} s - 20.0 I_{L2} s - 12.2474487139159 I_{L3} s - v_{2}\)
\(0 = - 13.856406460551 I_{L1} s - 12.2474487139159 I_{L2} s - 30.0 I_{L3} s + v_{4}\)
Solve for voltages and currents.
= solve(NE,X) U_ac
15.5.3 Plot the voltage at node 2
= U_ac[v2] H
= fraction(H) #returns numerator and denominator
num, denom
# convert symbolic to numpy polynomial
= np.array(Poly(num, s).all_coeffs(), dtype=float)
a = np.array(Poly(denom, s).all_coeffs(), dtype=float)
b = (a, b) system
#x = np.linspace(0.01*2*np.pi, 10*2*np.pi, 2000, endpoint=True)
= np.logspace(-2, 1, 300, endpoint=False)*2*np.pi
x = signal.bode(system, w=x) # returns: rad/s, mag in dB, phase in deg w, mag, phase
Load the csv file of node 10 voltage over the sweep range and plot along with the results obtained from SymPy.
= 'test_11.csv' # data from LTSpice
fn = np.genfromtxt(fn, delimiter=',') LTSpice_data
# initaliaze some empty arrays
= np.zeros(len(LTSpice_data))
frequency = np.zeros(len(LTSpice_data)).astype(complex)
voltage
# convert the csv data to complez numbers and store in the array
for i in range(len(LTSpice_data)):
= LTSpice_data[i][0]
frequency[i] = LTSpice_data[i][1] + LTSpice_data[i][2]*1j voltage[i]
Plot the results.
Using
np.unwrap(2 * phase) / 2)
to keep the pahse plots the same.
= plt.subplots()
fig, ax1 'magnitude, dB')
ax1.set_ylabel('frequency, Hz')
ax1.set_xlabel(
20*np.log10(np.abs(voltage)),'-r') # Bode magnitude plot
plt.semilogx(frequency, /(2*np.pi), mag,'-b') # Bode magnitude plot
plt.semilogx(w
='y')
ax1.tick_params(axis#ax1.set_ylim((-30,20))
plt.grid()
# instantiate a second y-axes that shares the same x-axis
= ax1.twinx()
ax2 = 'tab:blue'
color
2*np.angle(voltage)/2) *180/np.pi,':',color=color) # Bode phase plot
plt.semilogx(frequency, np.unwrap(/(2*np.pi), phase,':',color='tab:red') # Bode phase plot
plt.semilogx(w
'phase, deg',color=color)
ax2.set_ylabel(='y', labelcolor=color)
ax2.tick_params(axis#ax2.set_ylim((-5,25))
'Magnitude and phase response')
plt.title( plt.show()
= plt.subplots()
fig, ax1 'magnitude difference')
ax1.set_ylabel('frequency, Hz')
ax1.set_xlabel(
0:-1], np.abs(voltage[0:-1])-10**(mag/20),'-r') # Bode magnitude plot
plt.semilogx(frequency[#plt.semilogx(w/(2*np.pi), mag,'-b') # Bode magnitude plot
='y')
ax1.tick_params(axis#ax1.set_ylim((-30,20))
plt.grid()
# instantiate a second y-axes that shares the same x-axis
= ax1.twinx()
ax2 = 'tab:blue'
color
0:-1], np.unwrap(2*np.angle(voltage[0:-1])/2) *180/np.pi - phase,':',color=color) # Bode phase plot
plt.semilogx(frequency[#plt.semilogx(w/(2*np.pi), phase,':',color='tab:red') # Bode phase plot
'phase difference, deg',color=color)
ax2.set_ylabel(='y', labelcolor=color)
ax2.tick_params(axis#ax2.set_ylim((-5,25))
'Magnitude and phase response')
plt.title( plt.show()
The SymPy and LTSpice results overlay each other. The scale for the magnitude is \(10^{-14}\) and \(10^{-13}\) for the phase indicating the numerical difference is very small.