from sympy import *
import numpy as np
from tabulate import tabulate
import pandas as pd
from scipy import signal
import matplotlib.pyplot as plt
import SymMNA
from IPython.display import display, Markdown, Math, Latex
init_printing()
29 Bridge-T
29.1 Introduction
A question posted on electronics stackexchange by Lees (2023) asked:
I’m trying to figure out how to formulate and solve some differential equations for this circuit. … I run into an issue with the voltage/capacitor loop comprising Vin VC1 VC2 VC3 as I cannot arbitrarily assign an initial value to the voltage across C3.
Figure 29.1 shows that the independent source V1 is connected to ground by the series connection of C1, C2 and C3. Lees (2023) asked if the initial voltage on the capacitors, C1, C2 and C3 could violate Kirchhoff’s circuit laws by leading to a situation where the initial voltage on the capacitors do not sum to a value equal to V1. For example, if V1 is equal to 1 volt and the initial voltage on the capacitors are each equal to one volt, the sum of the three initial voltages on the capacitors would be 3 volts and not sum to zero around the loop. Lees (2023) provided some analysis in his question and discussed the voltage across C3 and stated that he needed to add a small series resistor to V1.
There were four answers provided by the comunity. The answer provided by Franc (2023) is long and detailed and in part 3 of his answer he adress the non zero initial condition and the transfer function at node 2.
29.2 Circuit description
The circuit shown in Figure 29.1 has six branches and three nodes. The circuit has a three capacitor path from the independent source, V1 to ground. The circuit posted by Lees (2023) didn’t have any values for the components, so I’m going use values that are all set to one. The initial voltages on the capacitor can be set to any abritrairy value or left defaulted to zero volts. As described below, the circuit needs to be modified for the MNA.
29.3 Circuit analysis
Lees (2023) asked for differential equations, but since this analysis is about using MNA, a frequency domain analysis will be presented. So I’m not going to derive the differential equations for the circuit.
- transfer function at nodes 2 and 3
- Solve the equations for initial conditions
- compare to LTSpice results
Voltage sources, V2, v3 and V4 were added to the circuit shown in Figure 29.2 so that these sources can hold the capacitor’s initial voltage value at time equal to 0+. Frequency domain equivilent of C with an initial voltage condition is a series voltage source v(0+)/s
The net list for the circuit is:
V1 1 0 1
C2 4 1 1
C3 5 2 1
C1 6 2 1
L1 1 2 1
R1 3 0 1
V2 4 3 1
V3 6 0 1
V4 3 5 1
The following Python modules are used.
= symbols('t',positive=True) # t > 0 t
29.3.1 Load the netlist
The netlist for Figure 29.1 is pasted into the cell below and some edits were made to remove the inductor series resistance.
= '''
net_list V1 1 0 1
C2 3 1 1
C3 3 2 1
C1 0 2 1
L 1 2 1
R2 3 0 1
'''
Generate the network equations.
= SymMNA.smna(net_list)
report, network_df, df2, A, X, Z
# Put matricies into SymPy
= Matrix(X)
X = Matrix(Z)
Z
= Eq(A*X,Z) NE_sym
Generate markdown text to display the network equations.
= ''
temp for i in range(len(X)):
+= '${:s}$<br>'.format(latex(Eq((A*X)[i:i+1][0],Z[i])))
temp
Markdown(temp)
\(C_{2} s v_{1} - C_{2} s v_{3} + I_{L} + I_{V1} = 0\)
\(- C_{3} s v_{3} - I_{L} + v_{2} \left(C_{1} s + C_{3} s\right) = 0\)
\(- C_{2} s v_{1} - C_{3} s v_{2} + v_{3} \left(C_{2} s + C_{3} s + \frac{1}{R_{2}}\right) = 0\)
\(v_{1} = V_{1}\)
\(- I_{L} L s + v_{1} - v_{2} = 0\)
As shown above MNA generated many equations and these would be difficult to solve by hand and a symbolic soultion would take a lot of computing time. The equations are displace in matrix notation.
NE_sym
\(\displaystyle \left[\begin{matrix}C_{2} s v_{1} - C_{2} s v_{3} + I_{L} + I_{V1}\\- C_{3} s v_{3} - I_{L} + v_{2} \left(C_{1} s + C_{3} s\right)\\- C_{2} s v_{1} - C_{3} s v_{2} + v_{3} \left(C_{2} s + C_{3} s + \frac{1}{R_{2}}\right)\\v_{1}\\- I_{L} L s + v_{1} - v_{2}\end{matrix}\right] = \left[\begin{matrix}0\\0\\0\\V_{1}\\0\end{matrix}\right]\)
The symbols generated by the Python code are extraced by the SymPy function free_symbols and then declared as SymPy variables.
# turn the free symbols into SymPy variables
str(NE_sym.free_symbols).replace('{','').replace('}','')) var(
\(\displaystyle \left( v_{2}, \ s, \ C_{3}, \ R_{2}, \ C_{1}, \ C_{2}, \ L, \ I_{L}, \ v_{1}, \ I_{V1}, \ V_{1}, \ v_{3}\right)\)
29.3.2 Transfer function
Solving for the transfer function at node 2. Transfer functions are normally presented in the frequency domain and are steady state responses.
= solve(NE_sym,X)
U_sym
= U_sym[v2]/U_sym[v1]
H2_sym H2_sym
\(\displaystyle \frac{C_{2} C_{3} L R_{2} V_{1} s^{3} + C_{2} R_{2} V_{1} s + C_{3} R_{2} V_{1} s + V_{1}}{V_{1} \left(C_{1} C_{2} L R_{2} s^{3} + C_{1} C_{3} L R_{2} s^{3} + C_{1} L s^{2} + C_{2} C_{3} L R_{2} s^{3} + C_{2} R_{2} s + C_{3} L s^{2} + C_{3} R_{2} s + 1\right)}\)
Collecting the s terms:
cancel(H2_sym,s)
\(\displaystyle \frac{C_{2} C_{3} L R_{2} s^{3} + s \left(C_{2} R_{2} + C_{3} R_{2}\right) + 1}{s^{3} \left(C_{1} C_{2} L R_{2} + C_{1} C_{3} L R_{2} + C_{2} C_{3} L R_{2}\right) + s^{2} \left(C_{1} L + C_{3} L\right) + s \left(C_{2} R_{2} + C_{3} R_{2}\right) + 1}\)
My transfer function equation does not agree with the answer provided by Franc (2023).
Solving for the transfer function at node 3.
= U_sym[v3]/U_sym[v1]
H3_sym H3_sym
\(\displaystyle \frac{C_{1} C_{2} L R_{2} V_{1} s^{3} + C_{2} C_{3} L R_{2} V_{1} s^{3} + C_{2} R_{2} V_{1} s + C_{3} R_{2} V_{1} s}{V_{1} \left(C_{1} C_{2} L R_{2} s^{3} + C_{1} C_{3} L R_{2} s^{3} + C_{1} L s^{2} + C_{2} C_{3} L R_{2} s^{3} + C_{2} R_{2} s + C_{3} L s^{2} + C_{3} R_{2} s + 1\right)}\)
Collecting the s terms:
cancel(H3_sym,s)
\(\displaystyle \frac{s^{3} \left(C_{1} C_{2} L R_{2} + C_{2} C_{3} L R_{2}\right) + s \left(C_{2} R_{2} + C_{3} R_{2}\right)}{s^{3} \left(C_{1} C_{2} L R_{2} + C_{1} C_{3} L R_{2} + C_{2} C_{3} L R_{2}\right) + s^{2} \left(C_{1} L + C_{3} L\right) + s \left(C_{2} R_{2} + C_{3} R_{2}\right) + 1}\)
My answer for the node 3 transfer function does not agree with the 1st answer’s node 3 transfer function. SE answer: numerator
*((C3+C2)*R2+(C1+C3)*L*R2*C2*s**2) s
\(\displaystyle s \left(C_{2} L R_{2} s^{2} \left(C_{1} + C_{3}\right) + R_{2} \left(C_{2} + C_{3}\right)\right)\)
*((C3+C2)*R2+(C1+C3)*L*R2*C2*s**2),s) cancel(s
\(\displaystyle s^{3} \left(C_{1} C_{2} L R_{2} + C_{2} C_{3} L R_{2}\right) + s \left(C_{2} R_{2} + C_{3} R_{2}\right)\)
numerator agrees
denominator
1+(C1+C3)*L*s**2)*(1+R2*C2*s) (
\(\displaystyle \left(C_{2} R_{2} s + 1\right) \left(L s^{2} \left(C_{1} + C_{3}\right) + 1\right)\)
1+(C1+C3)*L*s**2)*(1+R2*C2*s),s) cancel((
\(\displaystyle C_{2} R_{2} s + s^{3} \left(C_{1} C_{2} L R_{2} + C_{2} C_{3} L R_{2}\right) + s^{2} \left(C_{1} L + C_{3} L\right) + 1\)
Denominator does not agree
= (s*((C3+C2)*R2+(C1+C3)*L*R2*C2*s**2))/((1+(C1+C3)*L*s**2)*(1+R2*C2*s))
SE1 SE1
\(\displaystyle \frac{s \left(C_{2} L R_{2} s^{2} \left(C_{1} + C_{3}\right) + R_{2} \left(C_{2} + C_{3}\right)\right)}{\left(C_{2} R_{2} s + 1\right) \left(L s^{2} \left(C_{1} + C_{3}\right) + 1\right)}\)
cancel(SE1,s)
\(\displaystyle \frac{s^{3} \left(C_{1} C_{2} L R_{2} + C_{2} C_{3} L R_{2}\right) + s \left(C_{2} R_{2} + C_{3} R_{2}\right)}{C_{2} R_{2} s + s^{3} \left(C_{1} C_{2} L R_{2} + C_{2} C_{3} L R_{2}\right) + s^{2} \left(C_{1} L + C_{3} L\right) + 1}\)
29.3.3 Include the initial conditions
Set initial conditions for C1, C2 and C3 by including independent voltage sources, V2, V3 and V4, and appling SymPy Heaveside function. The independent voltage source, V1 is a also a step function. Since V2, V3 and V4 are in series with capacitors, the steady state value of these sources is blocked and only the transient response is present. The netlist for Figure 29.2 is pasted into the cell below and some edits were made to remove the inductor series resistance and the independent sources are set to their DC values.
= '''
net_list V1 1 0 1
C2 4 1 1
C3 5 2 1
C1 6 2 1
L 1 2 1
R2 3 0 1
V2 4 3 1
V3 6 0 1
V4 3 5 1
'''
Generate the network equations.
= SymMNA.smna(net_list)
report, network_df, df2, A, X, Z
# Put matricies into SymPy
= Matrix(X)
X = Matrix(Z)
Z
= Eq(A*X,Z) NE_ic_sym
Generate markdown text to display the network equations.
= ''
temp for i in range(len(X)):
+= '${:s}$<br>'.format(latex(Eq((A*X)[i:i+1][0],Z[i])))
temp
Markdown(temp)
\(C_{2} s v_{1} - C_{2} s v_{4} + I_{L} + I_{V1} = 0\)
\(- C_{1} s v_{6} - C_{3} s v_{5} - I_{L} + v_{2} \left(C_{1} s + C_{3} s\right) = 0\)
\(- I_{V2} + I_{V4} + \frac{v_{3}}{R_{2}} = 0\)
\(- C_{2} s v_{1} + C_{2} s v_{4} + I_{V2} = 0\)
\(- C_{3} s v_{2} + C_{3} s v_{5} - I_{V4} = 0\)
\(- C_{1} s v_{2} + C_{1} s v_{6} + I_{V3} = 0\)
\(v_{1} = V_{1}\)
\(- v_{3} + v_{4} = V_{2}\)
\(v_{6} = V_{3}\)
\(v_{3} - v_{5} = V_{4}\)
\(- I_{L} L s + v_{1} - v_{2} = 0\)
As shown above MNA generated many equations and these would be difficult to solve by hand and a symbolic soultion would take a lot of computing time. The equations are displace in matrix notation.
NE_ic_sym
\(\displaystyle \left[\begin{matrix}C_{2} s v_{1} - C_{2} s v_{4} + I_{L} + I_{V1}\\- C_{1} s v_{6} - C_{3} s v_{5} - I_{L} + v_{2} \left(C_{1} s + C_{3} s\right)\\- I_{V2} + I_{V4} + \frac{v_{3}}{R_{2}}\\- C_{2} s v_{1} + C_{2} s v_{4} + I_{V2}\\- C_{3} s v_{2} + C_{3} s v_{5} - I_{V4}\\- C_{1} s v_{2} + C_{1} s v_{6} + I_{V3}\\v_{1}\\- v_{3} + v_{4}\\v_{6}\\v_{3} - v_{5}\\- I_{L} L s + v_{1} - v_{2}\end{matrix}\right] = \left[\begin{matrix}0\\0\\0\\0\\0\\0\\V_{1}\\V_{2}\\V_{3}\\V_{4}\\0\end{matrix}\right]\)
The symbols generated by the Python code are extraced by the SymPy function free_symbols and then declared as SymPy variables.
# turn the free symbols into SymPy variables
str(NE_ic_sym.free_symbols).replace('{','').replace('}','')) var(
\(\displaystyle \left( v_{2}, \ C_{3}, \ R_{2}, \ v_{5}, \ v_{6}, \ C_{2}, \ v_{1}, \ I_{V1}, \ V_{4}, \ V_{1}, \ V_{3}, \ I_{V2}, \ s, \ C_{1}, \ L, \ I_{L}, \ v_{4}, \ I_{V3}, \ V_{2}, \ I_{V4}, \ v_{3}\right)\)
The SymPy Heaviside function is used to define the initial current and voltage on the inductor and capacitor. The equivalent circuit in s domain has a capacitor C with impedance 1/(sC) and a voltage source v(0)/s in series.
= NE_ic_sym.subs({V1:laplace_transform(V1*Heaviside(t), t, s)[0], V2:laplace_transform(V2*Heaviside(t), t, s)[0], V3:laplace_transform(V3*Heaviside(t), t, s)[0], V4:laplace_transform(V4*Heaviside(t), t, s)[0]})
NE_ic_sym NE_ic_sym
\(\displaystyle \left[\begin{matrix}C_{2} s v_{1} - C_{2} s v_{4} + I_{L} + I_{V1}\\- C_{1} s v_{6} - C_{3} s v_{5} - I_{L} + v_{2} \left(C_{1} s + C_{3} s\right)\\- I_{V2} + I_{V4} + \frac{v_{3}}{R_{2}}\\- C_{2} s v_{1} + C_{2} s v_{4} + I_{V2}\\- C_{3} s v_{2} + C_{3} s v_{5} - I_{V4}\\- C_{1} s v_{2} + C_{1} s v_{6} + I_{V3}\\v_{1}\\- v_{3} + v_{4}\\v_{6}\\v_{3} - v_{5}\\- I_{L} L s + v_{1} - v_{2}\end{matrix}\right] = \left[\begin{matrix}0\\0\\0\\0\\0\\0\\\frac{V_{1}}{s}\\\frac{V_{2}}{s}\\\frac{V_{3}}{s}\\\frac{V_{4}}{s}\\0\end{matrix}\right]\)
Generate and display the symbolic solution.
= solve(NE_ic_sym,X)
U_ic_sym
= ''
temp for i in U_ic_sym.keys():
+= '${:s} = {:s}$<br>'.format(latex(i),latex(U_ic_sym[i]))
temp
Markdown(temp)
\(v_{1} = \frac{V_{1}}{s}\)
\(v_{2} = \frac{C_{1} C_{2} L R_{2} V_{3} s^{3} + C_{1} C_{3} L R_{2} V_{3} s^{3} + C_{1} L V_{3} s^{2} + C_{2} C_{3} L R_{2} V_{1} s^{3} - C_{2} C_{3} L R_{2} V_{2} s^{3} - C_{2} C_{3} L R_{2} V_{4} s^{3} + C_{2} R_{2} V_{1} s - C_{3} L V_{4} s^{2} + C_{3} R_{2} V_{1} s + V_{1}}{C_{1} C_{2} L R_{2} s^{4} + C_{1} C_{3} L R_{2} s^{4} + C_{1} L s^{3} + C_{2} C_{3} L R_{2} s^{4} + C_{2} R_{2} s^{2} + C_{3} L s^{3} + C_{3} R_{2} s^{2} + s}\)
\(v_{3} = \frac{C_{1} C_{2} L R_{2} V_{1} s^{2} - C_{1} C_{2} L R_{2} V_{2} s^{2} + C_{1} C_{3} L R_{2} V_{3} s^{2} + C_{1} C_{3} L R_{2} V_{4} s^{2} + C_{2} C_{3} L R_{2} V_{1} s^{2} - C_{2} C_{3} L R_{2} V_{2} s^{2} + C_{2} R_{2} V_{1} - C_{2} R_{2} V_{2} + C_{3} R_{2} V_{1} + C_{3} R_{2} V_{4}}{C_{1} C_{2} L R_{2} s^{3} + C_{1} C_{3} L R_{2} s^{3} + C_{1} L s^{2} + C_{2} C_{3} L R_{2} s^{3} + C_{2} R_{2} s + C_{3} L s^{2} + C_{3} R_{2} s + 1}\)
\(v_{4} = \frac{C_{1} C_{2} L R_{2} V_{1} s^{3} + C_{1} C_{3} L R_{2} V_{2} s^{3} + C_{1} C_{3} L R_{2} V_{3} s^{3} + C_{1} C_{3} L R_{2} V_{4} s^{3} + C_{1} L V_{2} s^{2} + C_{2} C_{3} L R_{2} V_{1} s^{3} + C_{2} R_{2} V_{1} s + C_{3} L V_{2} s^{2} + C_{3} R_{2} V_{1} s + C_{3} R_{2} V_{2} s + C_{3} R_{2} V_{4} s + V_{2}}{C_{1} C_{2} L R_{2} s^{4} + C_{1} C_{3} L R_{2} s^{4} + C_{1} L s^{3} + C_{2} C_{3} L R_{2} s^{4} + C_{2} R_{2} s^{2} + C_{3} L s^{3} + C_{3} R_{2} s^{2} + s}\)
\(v_{5} = \frac{C_{1} C_{2} L R_{2} V_{1} s^{3} - C_{1} C_{2} L R_{2} V_{2} s^{3} - C_{1} C_{2} L R_{2} V_{4} s^{3} + C_{1} C_{3} L R_{2} V_{3} s^{3} - C_{1} L V_{4} s^{2} + C_{2} C_{3} L R_{2} V_{1} s^{3} - C_{2} C_{3} L R_{2} V_{2} s^{3} - C_{2} C_{3} L R_{2} V_{4} s^{3} + C_{2} R_{2} V_{1} s - C_{2} R_{2} V_{2} s - C_{2} R_{2} V_{4} s - C_{3} L V_{4} s^{2} + C_{3} R_{2} V_{1} s - V_{4}}{C_{1} C_{2} L R_{2} s^{4} + C_{1} C_{3} L R_{2} s^{4} + C_{1} L s^{3} + C_{2} C_{3} L R_{2} s^{4} + C_{2} R_{2} s^{2} + C_{3} L s^{3} + C_{3} R_{2} s^{2} + s}\)
\(v_{6} = \frac{V_{3}}{s}\)
\(I_{V1} = \frac{- C_{1} C_{2} C_{3} L R_{2} V_{1} s^{3} + C_{1} C_{2} C_{3} L R_{2} V_{2} s^{3} + C_{1} C_{2} C_{3} L R_{2} V_{3} s^{3} + C_{1} C_{2} C_{3} L R_{2} V_{4} s^{3} - C_{1} C_{2} L V_{1} s^{2} + C_{1} C_{2} L V_{2} s^{2} - C_{1} C_{2} R_{2} V_{1} s + C_{1} C_{2} R_{2} V_{3} s - C_{1} C_{3} R_{2} V_{1} s + C_{1} C_{3} R_{2} V_{3} s - C_{1} V_{1} + C_{1} V_{3} - C_{2} C_{3} L V_{1} s^{2} + C_{2} C_{3} L V_{2} s^{2} - C_{2} V_{1} + C_{2} V_{2} - C_{3} V_{1} - C_{3} V_{4}}{C_{1} C_{2} L R_{2} s^{3} + C_{1} C_{3} L R_{2} s^{3} + C_{1} L s^{2} + C_{2} C_{3} L R_{2} s^{3} + C_{2} R_{2} s + C_{3} L s^{2} + C_{3} R_{2} s + 1}\)
\(I_{V2} = \frac{C_{1} C_{2} C_{3} L R_{2} V_{1} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{2} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{3} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{4} s^{3} + C_{1} C_{2} L V_{1} s^{2} - C_{1} C_{2} L V_{2} s^{2} + C_{2} C_{3} L V_{1} s^{2} - C_{2} C_{3} L V_{2} s^{2} - C_{2} C_{3} R_{2} V_{2} s - C_{2} C_{3} R_{2} V_{4} s + C_{2} V_{1} - C_{2} V_{2}}{C_{1} C_{2} L R_{2} s^{3} + C_{1} C_{3} L R_{2} s^{3} + C_{1} L s^{2} + C_{2} C_{3} L R_{2} s^{3} + C_{2} R_{2} s + C_{3} L s^{2} + C_{3} R_{2} s + 1}\)
\(I_{V3} = \frac{C_{1} C_{2} C_{3} L R_{2} V_{1} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{2} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{3} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{4} s^{3} + C_{1} C_{2} R_{2} V_{1} s - C_{1} C_{2} R_{2} V_{3} s - C_{1} C_{3} L V_{3} s^{2} - C_{1} C_{3} L V_{4} s^{2} + C_{1} C_{3} R_{2} V_{1} s - C_{1} C_{3} R_{2} V_{3} s + C_{1} V_{1} - C_{1} V_{3}}{C_{1} C_{2} L R_{2} s^{3} + C_{1} C_{3} L R_{2} s^{3} + C_{1} L s^{2} + C_{2} C_{3} L R_{2} s^{3} + C_{2} R_{2} s + C_{3} L s^{2} + C_{3} R_{2} s + 1}\)
\(I_{V4} = \frac{C_{1} C_{2} C_{3} L R_{2} V_{1} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{2} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{3} s^{3} - C_{1} C_{2} C_{3} L R_{2} V_{4} s^{3} - C_{1} C_{3} L V_{3} s^{2} - C_{1} C_{3} L V_{4} s^{2} - C_{2} C_{3} R_{2} V_{2} s - C_{2} C_{3} R_{2} V_{4} s - C_{3} V_{1} - C_{3} V_{4}}{C_{1} C_{2} L R_{2} s^{3} + C_{1} C_{3} L R_{2} s^{3} + C_{1} L s^{2} + C_{2} C_{3} L R_{2} s^{3} + C_{2} R_{2} s + C_{3} L s^{2} + C_{3} R_{2} s + 1}\)
\(I_{L} = \frac{C_{1} C_{2} R_{2} V_{1} s - C_{1} C_{2} R_{2} V_{3} s + C_{1} C_{3} R_{2} V_{1} s - C_{1} C_{3} R_{2} V_{3} s + C_{1} V_{1} - C_{1} V_{3} + C_{2} C_{3} R_{2} V_{2} s + C_{2} C_{3} R_{2} V_{4} s + C_{3} V_{1} + C_{3} V_{4}}{C_{1} C_{2} L R_{2} s^{3} + C_{1} C_{3} L R_{2} s^{3} + C_{1} L s^{2} + C_{2} C_{3} L R_{2} s^{3} + C_{2} R_{2} s + C_{3} L s^{2} + C_{3} R_{2} s + 1}\)
Network equations and solution in symbolic form are seaily obtained. These equations are in the frequency domain and if transformed back to the time domain, they would be differential equations. But not necessarily in the form of state equations. In this case the equations are a little too long to provide any insight into the operation of the circuit.
29.4 Initial conditions with component values
Get element values from network dataframe. As describe above, the orginal question didn’t have values associated with the components, so I’m using a value of one for all the values.
= SymMNA.get_part_values(network_df)
element_values element_values
\(\displaystyle \left\{ C_{1} : 1.0, \ C_{2} : 1.0, \ C_{3} : 1.0, \ L : 1.0, \ R_{2} : 1.0, \ V_{1} : 1.0, \ V_{2} : 1.0, \ V_{3} : 1.0, \ V_{4} : 1.0\right\}\)
= NE_ic_sym.subs(element_values)
NE_ic NE_ic
\(\displaystyle \left[\begin{matrix}I_{L} + I_{V1} + 1.0 s v_{1} - 1.0 s v_{4}\\- I_{L} + 2.0 s v_{2} - 1.0 s v_{5} - 1.0 s v_{6}\\- I_{V2} + I_{V4} + 1.0 v_{3}\\I_{V2} - 1.0 s v_{1} + 1.0 s v_{4}\\- I_{V4} - 1.0 s v_{2} + 1.0 s v_{5}\\I_{V3} - 1.0 s v_{2} + 1.0 s v_{6}\\v_{1}\\- v_{3} + v_{4}\\v_{6}\\v_{3} - v_{5}\\- 1.0 I_{L} s + v_{1} - v_{2}\end{matrix}\right] = \left[\begin{matrix}0\\0\\0\\0\\0\\0\\\frac{1.0}{s}\\\frac{1.0}{s}\\\frac{1.0}{s}\\\frac{1.0}{s}\\0\end{matrix}\right]\)
Solve the network equations and display the results.
= solve(NE_ic,X)
U_ic
= ''
temp for i in U_ic.keys():
+= '${:s} = {:s}$<br>'.format(latex(i),latex(U_ic[i]))
temp
Markdown(temp)
\(v_{1} = \frac{1}{s}\)
\(v_{2} = \frac{s^{3} + 2.0 s + 1.0}{3.0 s^{4} + 2.0 s^{3} + 2.0 s^{2} + s}\)
\(v_{3} = \frac{2.0 s^{2} + 2.0}{3.0 s^{3} + 2.0 s^{2} + 2.0 s + 1.0}\)
\(v_{4} = \frac{5.0 s^{3} + 2.0 s^{2} + 4.0 s + 1.0}{3.0 s^{4} + 2.0 s^{3} + 2.0 s^{2} + s}\)
\(v_{5} = \frac{- s^{3} - 2.0 s^{2} - 1.0}{3.0 s^{4} + 2.0 s^{3} + 2.0 s^{2} + s}\)
\(v_{6} = \frac{1}{s}\)
\(I_{V1} = \frac{2.0 s^{3} - 2.0}{3.0 s^{3} + 2.0 s^{2} + 2.0 s + 1.0}\)
\(I_{V2} = \frac{- 2.0 s^{3} - 2.0 s}{3.0 s^{3} + 2.0 s^{2} + 2.0 s + 1.0}\)
\(I_{V3} = \frac{- 2.0 s^{3} - 2.0 s^{2}}{3.0 s^{3} + 2.0 s^{2} + 2.0 s + 1.0}\)
\(I_{V4} = \frac{- 2.0 s^{3} - 2.0 s^{2} - 2.0 s - 2.0}{3.0 s^{3} + 2.0 s^{2} + 2.0 s + 1.0}\)
\(I_{L} = \frac{2.0 s + 2.0}{3.0 s^{3} + 2.0 s^{2} + 2.0 s + 1.0}\)
29.5 Node 2 voltage
= U_ic[v2].nsimplify().simplify().expand().together()
node2 node2
\(\displaystyle \frac{s^{3} + 2 s + 1}{s \left(3 s^{3} + 2 s^{2} + 2 s + 1\right)}\)
The inverse Laplace was taking too long, so the lines of code were commented out
#node_2_voltage = inverse_laplace_transform(temp, s, t)
#node_2_voltage
Using NumPy to obtain the partial fraction expansion, convert back to the s domain and then take the inverse Laplace transform on each term.
Extract the numerator and denominator and display.
= fraction(node2)
n, d = n.expand()
n 'numerator: ${:s}$<br>denominator: ${:s}$'.format(latex(n),latex(d))) Markdown(
numerator: \(s^{3} + 2 s + 1\)
denominator: \(s \left(3 s^{3} + 2 s^{2} + 2 s + 1\right)\)
Each of the numerator terms can be put over the common denominator.
= [a / d for a in n.args]
terms display(terms)
\(\displaystyle \left[ \frac{1}{s \left(3 s^{3} + 2 s^{2} + 2 s + 1\right)}, \ \frac{s^{2}}{3 s^{3} + 2 s^{2} + 2 s + 1}, \ \frac{2}{3 s^{3} + 2 s^{2} + 2 s + 1}\right]\)
The following code processes each of the terms obtained above.
- the SciPy function residue is used to get the residues and poles of the partial-fraction expansion
- build the partial expansion terms and find the inverse Laplace of each term and save
Returns:
- r: Residues corresponding to the poles. For repeated poles, the residues are ordered to correspond to ascending by power fractions.
- p: Poles ordered by magnitude in ascending order.
- k: Coefficients of the direct polynomial term.
When computing the inverse Laplace transform, the Coefficients (k) are ignored since these transform to a Dirac delta function, \(\delta (t)\) and don’t need to be plotted.
= []
N
for p in terms:
# use the SciPy residue function to get the partial-fraction expansion residues and poles
= fraction(p)
n, d = Poly(n, s).all_coeffs()
cn = Poly(d, s).all_coeffs()
cd = signal.residue(cn, cd, tol=0.001, rtype='avg')
r, p, k
# build a symbolic expression for each of the residues and find the inverse Laplace of each one and save
= 0
z for i in range(len(r)):
= (r[i]/(s-p[i]))
m += inverse_laplace_transform(m, s, t)
z
N.append(z)
Each of these terms came be converted to a function using SymPy’s lambdify function.
Define the values for the x-axis of the plot and put each one into an array for plotting.
= np.linspace(0, 50, 2000, endpoint=True)
x = np.zeros(len(x),dtype = complex)
V_node2 for p in N:
+= lambdify(t, p)(x) V_node2
Plot the final combined result.
'Filter output voltage vs time')
plt.title(
='v2(t)')
plt.plot(x, np.real(V_node2),label#plt.plot(x, np.real(func_V1_t(x)),label='V1(t)')
'v(t), volts')
plt.ylabel('time, sec')
plt.xlabel(
plt.legend()
plt.grid() plt.show()
29.6 Node 3 voltage
The voltage on node 3 is symplified with the chain of operators applied to the expression; nsimplify(), simplify(), expand() and together(). This helps SymPy solve the inverse Laplace transform.
= U_ic[v3].nsimplify().simplify().expand().together()
node3 node3
\(\displaystyle \frac{2 \left(s^{2} + 1\right)}{3 s^{3} + 2 s^{2} + 2 s + 1}\)
The inverse Laplace was taking too long, so the lines of code were commented out
#node_2_voltage = inverse_laplace_transform(temp, s, t)
#node_2_voltage
Using NumPy
Extract the numerator and denominator and display.
= fraction(node3)
n, d = n.expand()
n 'numerator: ${:s}$<br>denominator: ${:s}$'.format(latex(n),latex(d))) Markdown(
numerator: \(2 s^{2} + 2\)
denominator: \(3 s^{3} + 2 s^{2} + 2 s + 1\)
Each of the numerator terms can be put over the common denominator.
= [a / d for a in n.args]
terms display(terms)
\(\displaystyle \left[ \frac{2}{3 s^{3} + 2 s^{2} + 2 s + 1}, \ \frac{2 s^{2}}{3 s^{3} + 2 s^{2} + 2 s + 1}\right]\)
Process term as decribed above.
= []
N
for p in terms:
# use the SciPy residue function to get the partial-fraction expansion residues and poles
= fraction(p)
n, d = Poly(n, s).all_coeffs()
cn = Poly(d, s).all_coeffs()
cd = signal.residue(cn, cd, tol=0.001, rtype='avg')
r, p, k
# build a symbolic expression for each of the residues and find the inverse Laplace of each one and save
= 0
z for i in range(len(r)):
= (r[i]/(s-p[i]))
m += inverse_laplace_transform(m, s, t)
z
N.append(z)
Sum all the terms.
= np.linspace(0, 50, 2000, endpoint=True)
x = np.zeros(len(x),dtype = complex)
V_node3 for p in N:
+= lambdify(t, p)(x) V_node3
Plot the final combined result.
'Filter output voltage vs time')
plt.title(
='v3(t)')
plt.plot(x, np.real(V_node3),label#plt.plot(x, np.real(func_V1_t(x)),label='V1(t)')
'v(t), volts')
plt.ylabel('time, sec')
plt.xlabel(
plt.legend()
plt.grid() plt.show()
29.7 LTSpice Solution
To chech the Python results the circuit was simulated in LTSpice. The following schematice was used and the LTSpice results are also shown below.
Load the csv file LTSpice results and plot along with the results obtained from Python.
= 'Bridge-T-LTSpice.csv' # data from LTSpice
fn = np.genfromtxt(fn, delimiter=',',skip_header=1) LTSpice_data
Copy the data from the csv file into NumPy arrays.
# initaliase some empty arrays
= np.zeros(len(LTSpice_data))
time = np.zeros(len(LTSpice_data))
voltage1 = np.zeros(len(LTSpice_data))
voltage2 = np.zeros(len(LTSpice_data))
voltage3
# convert the csv data to complez numbers and store in the array
for i in range(len(LTSpice_data)):
= LTSpice_data[i][0]
time[i] = LTSpice_data[i][1]
voltage1[i] = LTSpice_data[i][2]
voltage2[i] = LTSpice_data[i][3] voltage3[i]
Plot the LTSpice data and the Python data to see if they agree.
'LTSpice')
plt.title(
='LTSpice v1(t)')
plt.plot(time, voltage1,label='LTSpice v2(t)')
plt.plot(time, voltage2,label='LTSpice v3(t)')
plt.plot(time, voltage3,label
='V node 2(t)')
plt.plot(x, np.real(V_node2),label='V node 3(t)')
plt.plot(x, np.real(V_node3),label#plt.plot(x, np.real(func_V1_t(x)),label='V1(t)')
'v(t), volts')
plt.ylabel('time, sec')
plt.xlabel(
plt.legend()
plt.grid() plt.show()
As shown in the plot above, the curves superimpose.
29.8 Summary
In this notebook, newtork equations were solved which had initial conditions. The independent voltage source, V1 was modeled as a step function. The initial voltages on each of the capacitors were also modeled as step functions equal to the initial voltage. The network equations were generated with MNA and SymPy was used to solve the system of equations. The results were compared to results from LTSpice and the curves superimpose.
The MNA procedure and LTSpice didn’t have any issues with initial conditions. At t=0, all the voltages and currents in the circuit comply with Kirchhoff’s circuit laws (KCL) since the Heavyside function at t=0 is zero. At t=0+, where the + indicates the next infensisible instant in time, the voltages and currents would comply with KCL and result in large current flows to accomidate the solution for the voltages. Both Python and LTSpice step through time in discreate amounts, and at each step time KCL is satisified. I don’t think this was explained in any of the answers provided.
The answers provide by Franc (2023) are different from the results I obtained and he did not provide a time domain solution and plots of the node voltages.