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()
32 Super node
A question posted on Electronics Stackexchange by Malek (2020) asked:
I have a circuit that I would like to convert into a conductance matrix for the node analysis by inspection method. I have, however, run into a problem. I have this 36V ideal voltage source with no resistance on its branch, which means that I cannot convert it into a current source with the Norton/Thenevin equivalent circuit theorem (or can I?). I have thought of making the third node connected to the voltage source dependent on the first node connected to the voltage source, i.e. v1 = v1 + 36. I have tried this method, but I do not get the correct values. Am I messing up? Is there no way to handle ideal voltage sources with the node analysis by inspection method?
The MNA solution provided below can work directly with the circuit netlist and the concept of a super node is not required.
The net list for Figure 5.1 was generated by LTSpice and show below:
V1 1 3 36
I1 0 1 12
I2 2 0 18
I3 0 3 24
R1 1 0 4
R4 3 0 6
R2 2 1 12
R3 3 2 15
The following Python modules are used in this notebook.
32.1 Load the net list
The netlist for the circuit is pasted into the code cell below. In Python a triple-quoted string includes whitespace, tabs and newlines. The newlines characters are needed to mark the end of each SPICE statement in the netlist.
= '''
net_list V1 1 3 36
I1 0 1 12
I2 2 0 18
I3 0 3 24
R1 1 0 4
R4 3 0 6
R2 2 1 12
R3 3 2 15
'''
32.2 Call the symbolic modified nodal analysis function
= SymMNA.smna(net_list) report, network_df, i_unk_df, A, X, Z
The network equations for the circuit can be obtained from the A, X and Z values returned from the smna function. The A, X and Z are formuloated into equations and displayed below. Markdown is an IPython function and latex is a SymPy printing function.
# 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_{V1} + v_{1} \cdot \left(\frac{1}{R_{2}} + \frac{1}{R_{1}}\right) - \frac{v_{2}}{R_{2}} = I_{1}\)
\(v_{2} \cdot \left(\frac{1}{R_{3}} + \frac{1}{R_{2}}\right) - \frac{v_{3}}{R_{3}} - \frac{v_{1}}{R_{2}} = - I_{2}\)
\(- I_{V1} + v_{3} \cdot \left(\frac{1}{R_{4}} + \frac{1}{R_{3}}\right) - \frac{v_{2}}{R_{3}} = I_{3}\)
\(v_{1} - v_{3} = V_{1}\)
32.2.1 Build the network equation matrix
# Put matrices into SymPy
= Matrix(X)
X = Matrix(Z)
Z
= Eq(A*X,Z)
NE_sym NE_sym
\(\displaystyle \left[\begin{matrix}I_{V1} + v_{1} \cdot \left(\frac{1}{R_{2}} + \frac{1}{R_{1}}\right) - \frac{v_{2}}{R_{2}}\\v_{2} \cdot \left(\frac{1}{R_{3}} + \frac{1}{R_{2}}\right) - \frac{v_{3}}{R_{3}} - \frac{v_{1}}{R_{2}}\\- I_{V1} + v_{3} \cdot \left(\frac{1}{R_{4}} + \frac{1}{R_{3}}\right) - \frac{v_{2}}{R_{3}}\\v_{1} - v_{3}\end{matrix}\right] = \left[\begin{matrix}I_{1}\\- I_{2}\\I_{3}\\V_{1}\end{matrix}\right]\)
# turn the free symbols into SymPy variables
str(NE_sym.free_symbols).replace('{','').replace('}','')) var(
\(\displaystyle \left( I_{V1}, \ R_{3}, \ v_{2}, \ I_{2}, \ I_{1}, \ R_{2}, \ R_{4}, \ V_{1}, \ I_{3}, \ v_{3}, \ v_{1}, \ R_{1}\right)\)
32.3 Symbolic solution
The newtork equations can be solved symbolically.
= 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{I_{1} R_{1} R_{4} - I_{2} R_{1} R_{4} + I_{3} R_{1} R_{4} + R_{1} V_{1}}{R_{1} + R_{4}}\)
\(v_{2} = \frac{I_{1} R_{1} R_{2} R_{4} + I_{1} R_{1} R_{3} R_{4} - I_{2} R_{1} R_{2} R_{3} - I_{2} R_{1} R_{2} R_{4} - I_{2} R_{1} R_{3} R_{4} - I_{2} R_{2} R_{3} R_{4} + I_{3} R_{1} R_{2} R_{4} + I_{3} R_{1} R_{3} R_{4} + R_{1} R_{3} V_{1} - R_{2} R_{4} V_{1}}{R_{1} R_{2} + R_{1} R_{3} + R_{2} R_{4} + R_{3} R_{4}}\)
\(v_{3} = \frac{I_{1} R_{1} R_{4} - I_{2} R_{1} R_{4} + I_{3} R_{1} R_{4} - R_{4} V_{1}}{R_{1} + R_{4}}\)
\(I_{V1} = \frac{I_{1} R_{1} R_{2} + I_{1} R_{1} R_{3} - I_{2} R_{1} R_{3} + I_{2} R_{2} R_{4} - I_{3} R_{2} R_{4} - I_{3} R_{3} R_{4} - R_{1} V_{1} - R_{2} V_{1} - R_{3} V_{1} - R_{4} V_{1}}{R_{1} R_{2} + R_{1} R_{3} + R_{2} R_{4} + R_{3} R_{4}}\)
32.4 Construct a dictionary of element values
= SymMNA.get_part_values(network_df)
element_values element_values
\(\displaystyle \left\{ I_{1} : 12.0, \ I_{2} : 18.0, \ I_{3} : 24.0, \ R_{1} : 4.0, \ R_{2} : 12.0, \ R_{3} : 15.0, \ R_{4} : 6.0, \ V_{1} : 36.0\right\}\)
32.5 Numerical solution
= NE_sym.subs(element_values)
NE NE
\(\displaystyle \left[\begin{matrix}I_{V1} + 0.333333333333333 v_{1} - 0.0833333333333333 v_{2}\\- 0.0833333333333333 v_{1} + 0.15 v_{2} - 0.0666666666666667 v_{3}\\- I_{V1} - 0.0666666666666667 v_{2} + 0.233333333333333 v_{3}\\v_{1} - v_{3}\end{matrix}\right] = \left[\begin{matrix}12.0\\-18.0\\24.0\\36.0\end{matrix}\right]\)
= solve(NE,X) U
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.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 57.600000
v2 -78.400000
v3 21.600000
I_V1 -13.733333
The node voltages and current through the sources are solved for. The Sympy generated solution matches the LTSpice results:
--- Operating Point ---
V(1): 2 voltage
V(2): 4 voltage
V(4): -1.14286 voltage
V(3): 6.28571 voltage
V(5): 0 voltage
I(F1): -4 device_current
I(I1): 9 device_current
I(R2): -2 device_current
I(R3): -7.42857 device_current
I(R4): 2 device_current
I(R1): 1.57143 device_current
I(E1): -11.4286 device_current
I(V1): 0.428571 device_current
I(V2): -2 device_current
The results from LTSpice agree with the SymPy results.