Monday, April 9, 2018

Solving circuits with Python

Python is  a scripting languaje that is now quite popular.

It is used in a broad range of applications including performing scientific calculations thanks to the SciPy libraries.

This blog entry links to a pair of Google Colaboratory documents. Colaboratory documents are Python Jupyter Notebooks hosted in a Google Drive account that run the code in virtual machines owned by Google.

That way you don't need to have Python installed to try or modify the proposed code.

Solving circuits with SymPy

SymPy is one of the SciPy libraries. It enables you to perform symbolic calculations. This can come handy to solve electrical circuits like the one below:

In order to solve a circuit, you need to obtain a set of equations whose solution is the magnitude you want to know. In the above circuit, let's say that we want to know the value of Vo as function of Vs, R1, R2 and Is.

You start defining symbols for the circuit. We need symbols for the component values Vs, R1, R2 and Is and for the solution Vo.
As  we will solve the circuit using the nodal method that applies current equations in all nodes except the ground one, we also need symbols for the nodal voltages V1 and V2.
Also, using the full nodal method requires as to add a symbol, like iVs for the current in the source voltages.

# Create the circuit symbols
Vs,iVs,R1,R2,Is,Vo, V1, V2 = sympy.symbols('Vs,iVs,R1,R2,Is,Vo,V1,V2')

Now we define the equations of nodes 1 and 2. To do that, we initizalize an empty list of equations and add the two equations to this list. Using the nodal method, we apply the Kirchhoff KCL law on each node:

# Create an empty list of equations
equations = []

# Nodal equations
equations.append(iVs-(V1-V2)/R1)       # Node 1
equations.append(Is-(V2-V1)/R1-V2/R2)  # Node 2

In SymPy it is assumed that all equations are equal to zero.

In the nodal method we also need to add an equation for each voltage source that relates it to the node voltages. In SymPy, Eq means equal, so, Eq(Vs,V1) means Vs = V1.

# Voltage source equations

The final equation is the one that contains the result we want to obtain for the circuit:

# Measurement equations

Now we have a full set of equations:

iVs - (V1 - V2)/R1
Is - V2/R2 - (-V1 + V2)/R1
Eq(Vs, V1)
Eq(Vo, V2)

Those equations enables us to obtain Vo but a smaller set of equation will also work. In particular, as the first equation contains an unknown  iVs that is not found in any other equation, we can eliminate this equation if the only unknown we care for is Vo. We don't need, however, to know that to solve the circuit.

In these equations, some variables are unknowns, we make a list of them:

unknowns = [V1,V2,iVs,Vo]

Then we can solve the circuit using the solve function of the SymPy library:

# Solve the circuit
solution = sympy.solve(equations,unknowns)

That will yield the solutions for the unknowns:

V1 = Vs
V2 = R2*(Is*R1 + Vs)/(R1 + R2)
iVs = (-Is*R2 + Vs)/(R1 + R2)
Vo = R2*(Is*R1 + Vs)/(R1 + R2)

If you want to see more details, more circuit solutions or you want to test te code yourself, open this colaboratory document.

Automating the solution

As you can see we have followed a recipe for solving the circuit. That means that it can be automated. The circuit Python module, just does that: Solves a circuit using the nodal method recipe.

As we remember the circuit is:

We can describe it just with a list of components and measurements:

# Circuit 1 definiton
c1 = circuit.circuit()  # New circuit
c1.addV('Vs',1,0,5)     # Vs between 1 and GND with 5 V value
c1.addR('R1',1,2,1000)  # R1 between 1 and 2 with 1000 Ohm value
c1.addR('R2',2,0,1000)  # R2 between 2 and GND with 1000 Ohm value
c1.addI('Is',2,0,0.01)  # Is going from GND to 2 with 10 mA value
c1.addVM('Vo',2,0)      # Measurement Vo between 2 and GND

Then we can obtain the symbolic solution for Vo:

# Symbolic solution
print('Vo =',c1.solution['Vo'],'  (Symbolic)')

That yields:

Vo = R2*(Is*R1 + Vs)/(R1 + R2)   (Symbolic)

Note that this is the very same solution we obtained previously without using the SymPy module.

Also, as we have indicated the component values, we can also obtain the numerical solution:

# Numeric solution
print('Vo =',c1.particular['Vo'],'V   (Particular)')

That yields:

Vo = 7.5 V   (Particular)

If you want to see more about the circuit module, see other solutions or use your own code, open this colaboratory document.