# solve() fails for 3 simple linear equations

I am doing basic circuit analysis, which involves solving multiple algebraic equations. solve() fails for a simple linear circuit (image does not work -- see circuit description below).

Vi, Vo, Vm, Vx, R1, R2, R3, C, A, s = var('Vi,Vo,Vm,Vx,R1,R2,R3,C,A,s')
# Three Kirchoff equations at nodes Vm, Vx, Vo
eq_at_Vm = (Vi-Vm)/R1 + (Vx-Vm)/R2 == 0
eq_at_Vx = (Vm-Vx)/R2 + (Vo-Vx)/R3 + (0-Vx)/(1/(C*s)) == 0
eq_at_Vo = Vo == -A*Vm
# solving for Vo FAILS
solve([eq_at_Vm,eq_at_Vx,eq_at_Vo],Vo)


The output is just [].

Am I doing something wrong, or is this a limitation of solve()?

I did find a workaround, solving each equation individually, which in this case is easy, but in general is not:

Vi, Vo, Vm, Vx, R1, R2, R3, C, A, s = var('Vi,Vo,Vm,Vx,R1,R2,R3,C,A,s')
# Three Kirchoff equations at nodes Vm, Vx, Vo
eq_at_Vm = (Vi-Vm)/R1 + (Vx-Vm)/R2 == 0
eq_at_Vx = (Vm-Vx)/R2 + (Vo-Vx)/R3 + (0-Vx)/(1/(C*s)) == 0
eq_at_Vo = Vo == -A*Vm
# eliminate Vm
Vm_eq = solve(eq_at_Vo,Vm)[0]
eq_at_Vm = eq_at_Vm.substitute(Vm_eq)
eq_at_Vx = eq_at_Vx.substitute(Vm_eq)
# eliminate Vx
Vx_eq = solve(eq_at_Vm,Vx)[0]
eq_at_Vx = eq_at_Vx.substitute(Vx_eq)
# final transfer function
print((solve(eq_at_Vx,Vo)[0]/Vi).simplify_full())


The output is: Vo/Vi == -(A*C*R2*R3*s + A*R2 + A*R3)/((C*R1 + C*R2)*R3*s + (A + 1)*R1 + R2 + R3)

Circuit Description: This is an inverting OpAmp with a R2-C-R3 Tee network as feedback. The OpAmp has gain=A with in+ grounded, so Vo=-A*in-. Nodes are labeled Vi, Vm, Vx, Vo: Vi is the input, Vo is the OpAmp output, Vm is the OpAmp in-, and Vx is inside the Tee. R1 is Vi to Vm, R2 is Vm to Vx, R3 is Vx to Vo, and C is Vx to ground.

edit retag close merge delete

Note that many asterisks got omitted in the output Vo/Vi, even after editing to indicate it is preformatted.

( 2021-10-14 20:22:50 +0100 )edit

Sort by » oldest newest most voted

By solving against Vo only, you do as if every other symbols involved in the equations were constants. However, Vi, Vm, Vx are also unknown.

Imagine that the solver would return [Vo == -A*Vm] as a solution, i bet you will not be very happy.

So, what you have to do is to put all unknowns, and let only the constants outside:

sage: solve([eq_at_Vm, eq_at_Vx, eq_at_Vo],[Vo, Vi, Vm, Vx])
[[Vo == (A*R3*r1 + (A*C*R3*r1*s + A*r1)*R2)/(A*R2 - R3), Vi == -((C*R3*r1*s + A*r1 + r1)*R1 + (C*R3*r1*s + r1)*R2 + R3*r1)/(A*R2 - R3), Vm == -((C*R3*r1*s + r1)*R2 + R3*r1)/(A*R2 - R3), Vx == r1]]


Now, as you can see, there is one degree of freedom : Vx can take any value, so Sage added a free parameter r1 and gave you all the solutions using the constants and this free parameter r1.

Now, i see in your workaround that you want to solve for Vo/Vi, so let me assume that you want Vi being the free parameter, not Vx.

Hence, you can solve as if Vi is also a constant:

sage: solve([eq_at_Vm, eq_at_Vx, eq_at_Vo],[Vo, Vm, Vx])
[[Vo == -((A*C*R3*s + A)*R2 + A*R3)*Vi/((C*R3*s + A + 1)*R1 + (C*R3*s + 1)*R2 + R3), Vm == ((C*R3*s + 1)*R2 + R3)*Vi/((C*R3*s + A + 1)*R1 + (C*R3*s + 1)*R2 + R3), Vx == -(A*R2 - R3)*Vi/((C*R3*s + A + 1)*R1 + (C*R3*s + 1)*R2 + R3)]]

more

Thanks. This makes sense.

( 2021-10-22 17:17:26 +0100 )edit